diff --git a/docsrc/manual/project-files.rst b/docsrc/manual/project-files.rst index aef44032..92e6984f 100644 --- a/docsrc/manual/project-files.rst +++ b/docsrc/manual/project-files.rst @@ -44,7 +44,6 @@ The filepath that Porymap expects for each file can be overridden under the ``Pr include/constants/global.h, yes, no, ``constants_global``, reads ``OBJECT_EVENT_TEMPLATES_COUNT`` include/constants/map_groups.h, no, yes, ``constants_map_groups``, include/constants/items.h, yes, no, ``constants_items``, - include/constants/opponents.h, yes, no, ``constants_opponents``, reads max trainers constant include/constants/flags.h, yes, no, ``constants_flags``, include/constants/vars.h, yes, no, ``constants_vars``, include/constants/weather.h, yes, no, ``constants_weather``, diff --git a/include/config.h b/include/config.h index 835ee47d..8a0cbbed 100644 --- a/include/config.h +++ b/include/config.h @@ -196,7 +196,6 @@ enum ProjectFilePath { constants_global, constants_map_groups, constants_items, - constants_opponents, constants_flags, constants_vars, constants_weather, diff --git a/include/core/parseutil.h b/include/core/parseutil.h index 198f4c95..7a8c3df1 100644 --- a/include/core/parseutil.h +++ b/include/core/parseutil.h @@ -56,7 +56,8 @@ public: QMap readCIncbinMulti(const QString &filepath); QStringList readCIncbinArray(const QString &filename, const QString &label); QMap readCDefines(const QString &filename, const QStringList &prefixes, QMap = { }); - QStringList readCDefinesSorted(const QString&, const QStringList&, const QMap& = { }); + QStringList readCDefineNames(const QString&, const QStringList&); + QStringList readCDefineNamesByValue(const QString&, const QStringList&, const QMap& = { }); QMap> readCStructs(const QString &, const QString & = "", const QHash = { }); QList getLabelMacros(const QList&, const QString&); QStringList getLabelValues(const QList&, const QString&); @@ -89,6 +90,7 @@ private: QString file; QString curDefine; QHash errorMap; + QString readCDefinesFile(const QString &filename); QList tokenizeExpression(QString expression, const QMap &knownIdentifiers); QList generatePostfix(const QList &tokens); int evaluatePostfix(const QList &postfix); diff --git a/src/config.cpp b/src/config.cpp index 1c393f3f..6307db02 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -42,7 +42,6 @@ const QMap> ProjectConfig::defaultP {ProjectFilePath::constants_global, { "constants_global", "include/constants/global.h"}}, {ProjectFilePath::constants_map_groups, { "constants_map_groups", "include/constants/map_groups.h"}}, {ProjectFilePath::constants_items, { "constants_items", "include/constants/items.h"}}, - {ProjectFilePath::constants_opponents, { "constants_opponents", "include/constants/opponents.h"}}, {ProjectFilePath::constants_flags, { "constants_flags", "include/constants/flags.h"}}, {ProjectFilePath::constants_vars, { "constants_vars", "include/constants/vars.h"}}, {ProjectFilePath::constants_weather, { "constants_weather", "include/constants/weather.h"}}, diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp index a421a087..7d2d81ca 100644 --- a/src/core/parseutil.cpp +++ b/src/core/parseutil.cpp @@ -334,16 +334,12 @@ QStringList ParseUtil::readCIncbinArray(const QString &filename, const QString & return paths; } -QMap ParseUtil::readCDefines(const QString &filename, - const QStringList &prefixes, - QMap allDefines) +QString ParseUtil::readCDefinesFile(const QString &filename) { - QMap filteredDefines; - this->file = filename; if (this->file.isEmpty()) { - return filteredDefines; + return QString(); } QString filepath = this->root + "/" + this->file; @@ -351,13 +347,27 @@ QMap ParseUtil::readCDefines(const QString &filename, if (this->text.isNull()) { logError(QString("Failed to read C defines file: '%1'").arg(filepath)); - return filteredDefines; + return QString(); } static const QRegularExpression re_extraChars("(//.*)|(\\/+\\*+[^*]*\\*+\\/+)"); this->text.replace(re_extraChars, ""); static const QRegularExpression re_extraSpaces("(\\\\\\s+)"); this->text.replace(re_extraSpaces, ""); + return this->text; +} + +QMap ParseUtil::readCDefines(const QString &filename, + const QStringList &prefixes, + QMap allDefines) +{ + QMap filteredDefines; + + this->text = this->readCDefinesFile(filename); + if (this->text.isEmpty()) { + return filteredDefines; + } + allDefines.insert("FALSE", 0); allDefines.insert("TRUE", 1); @@ -383,13 +393,37 @@ QMap ParseUtil::readCDefines(const QString &filename, return filteredDefines; } -QStringList ParseUtil::readCDefinesSorted(const QString &filename, - const QStringList &prefixes, - const QMap &knownDefines) +// Similar to readCDefines, but for cases where we only need to show a list of define names. +// We can skip evaluating each define (and by extension skip reporting any errors from this process). +QStringList ParseUtil::readCDefineNames(const QString &filename, const QStringList &prefixes) { + QStringList filteredDefines; + + this->text = this->readCDefinesFile(filename); + if (this->text.isEmpty()) { + return filteredDefines; + } + + static const QRegularExpression re("#define\\s+(?\\w+)[^\\S\\n]+"); + QRegularExpressionMatchIterator iter = re.globalMatch(this->text); + while (iter.hasNext()) { + QRegularExpressionMatch match = iter.next(); + QString name = match.captured("defineName"); + for (QString prefix : prefixes) { + if (name.startsWith(prefix) || QRegularExpression(prefix).match(name).hasMatch()) { + filteredDefines.append(name); + } + } + } + return filteredDefines; +} + +QStringList ParseUtil::readCDefineNamesByValue(const QString &filename, + const QStringList &prefixes, + const QMap &knownDefines) { QMap defines = readCDefines(filename, prefixes, knownDefines); - // The defines should be sorted by their underlying value, not alphabetically. + // The defines should be sorted by their underlying value, not alphabetically or in parse order. // Reverse the map and read out the resulting keys in order. QMultiMap definesInverse; for (QString defineName : defines.keys()) { diff --git a/src/project.cpp b/src/project.cpp index 69778540..9533789a 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -2069,7 +2069,7 @@ bool Project::readItemNames() { QStringList prefixes("\\bITEM_(?!(B_)?USE_)"); // Exclude ITEM_USE_ and ITEM_B_USE_ constants QString filename = projectConfig.getFilePath(ProjectFilePath::constants_items); fileWatcher.addPath(root + "/" + filename); - itemNames = parser.readCDefinesSorted(filename, prefixes); + itemNames = parser.readCDefineNames(filename, prefixes); if (itemNames.isEmpty()) { logError(QString("Failed to read item constants from %1").arg(filename)); return false; @@ -2078,18 +2078,12 @@ bool Project::readItemNames() { } bool Project::readFlagNames() { - // First read MAX_TRAINERS_COUNT, used to skip over trainer flags - // If this fails flags may simply be out of order, no need to check for success - QString opponentsFilename = projectConfig.getFilePath(ProjectFilePath::constants_opponents); - fileWatcher.addPath(root + "/" + opponentsFilename); - QMap maxTrainers = parser.readCDefines(opponentsFilename, QStringList() << "\\bMAX_"); - // Parse flags QStringList prefixes("\\bFLAG_"); - QString flagsFilename = projectConfig.getFilePath(ProjectFilePath::constants_flags); - fileWatcher.addPath(root + "/" + flagsFilename); - flagNames = parser.readCDefinesSorted(flagsFilename, prefixes, maxTrainers); + QString filename = projectConfig.getFilePath(ProjectFilePath::constants_flags); + fileWatcher.addPath(root + "/" + filename); + flagNames = parser.readCDefineNames(filename, prefixes); if (flagNames.isEmpty()) { - logError(QString("Failed to read flag constants from %1").arg(flagsFilename)); + logError(QString("Failed to read flag constants from %1").arg(filename)); return false; } return true; @@ -2099,7 +2093,7 @@ bool Project::readVarNames() { QStringList prefixes("\\bVAR_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_vars); fileWatcher.addPath(root + "/" + filename); - varNames = parser.readCDefinesSorted(filename, prefixes); + varNames = parser.readCDefineNames(filename, prefixes); if (varNames.isEmpty()) { logError(QString("Failed to read var constants from %1").arg(filename)); return false; @@ -2111,7 +2105,7 @@ bool Project::readMovementTypes() { QStringList prefixes("\\bMOVEMENT_TYPE_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_obj_event_movement); fileWatcher.addPath(root + "/" + filename); - movementTypes = parser.readCDefinesSorted(filename, prefixes); + movementTypes = parser.readCDefineNames(filename, prefixes); if (movementTypes.isEmpty()) { logError(QString("Failed to read movement type constants from %1").arg(filename)); return false; @@ -2134,7 +2128,7 @@ bool Project::readMapTypes() { QStringList prefixes("\\bMAP_TYPE_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types); fileWatcher.addPath(root + "/" + filename); - mapTypes = parser.readCDefinesSorted(filename, prefixes); + mapTypes = parser.readCDefineNames(filename, prefixes); if (mapTypes.isEmpty()) { logError(QString("Failed to read map type constants from %1").arg(filename)); return false; @@ -2146,7 +2140,7 @@ bool Project::readMapBattleScenes() { QStringList prefixes("\\bMAP_BATTLE_SCENE_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_map_types); fileWatcher.addPath(root + "/" + filename); - mapBattleScenes = parser.readCDefinesSorted(filename, prefixes); + mapBattleScenes = parser.readCDefineNames(filename, prefixes); if (mapBattleScenes.isEmpty()) { logError(QString("Failed to read map battle scene constants from %1").arg(filename)); return false; @@ -2158,7 +2152,7 @@ bool Project::readWeatherNames() { QStringList prefixes("\\bWEATHER_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather); fileWatcher.addPath(root + "/" + filename); - weatherNames = parser.readCDefinesSorted(filename, prefixes); + weatherNames = parser.readCDefineNames(filename, prefixes); if (weatherNames.isEmpty()) { logError(QString("Failed to read weather constants from %1").arg(filename)); return false; @@ -2173,7 +2167,7 @@ bool Project::readCoordEventWeatherNames() { QStringList prefixes("\\bCOORD_EVENT_WEATHER_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_weather); fileWatcher.addPath(root + "/" + filename); - coordEventWeatherNames = parser.readCDefinesSorted(filename, prefixes); + coordEventWeatherNames = parser.readCDefineNames(filename, prefixes); if (coordEventWeatherNames.isEmpty()) { logWarn(QString("Failed to read coord event weather constants from %1. Disabling Weather Trigger events.").arg(filename)); projectConfig.setEventWeatherTriggerEnabled(false); @@ -2188,7 +2182,7 @@ bool Project::readSecretBaseIds() { QStringList prefixes("\\bSECRET_BASE_[A-Za-z0-9_]*_[0-9]+"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_secret_bases); fileWatcher.addPath(root + "/" + filename); - secretBaseIds = parser.readCDefinesSorted(filename, prefixes); + secretBaseIds = parser.readCDefineNames(filename, prefixes); if (secretBaseIds.isEmpty()) { logWarn(QString("Failed to read secret base id constants from '%1'. Disabling Secret Base events.").arg(filename)); projectConfig.setEventSecretBaseEnabled(false); @@ -2200,7 +2194,7 @@ bool Project::readBgEventFacingDirections() { QStringList prefixes("\\bBG_EVENT_PLAYER_FACING_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_event_bg); fileWatcher.addPath(root + "/" + filename); - bgEventFacingDirections = parser.readCDefinesSorted(filename, prefixes); + bgEventFacingDirections = parser.readCDefineNames(filename, prefixes); if (bgEventFacingDirections.isEmpty()) { logError(QString("Failed to read bg event facing direction constants from %1").arg(filename)); return false; @@ -2212,7 +2206,7 @@ bool Project::readTrainerTypes() { QStringList prefixes("\\bTRAINER_TYPE_"); QString filename = projectConfig.getFilePath(ProjectFilePath::constants_trainer_types); fileWatcher.addPath(root + "/" + filename); - trainerTypes = parser.readCDefinesSorted(filename, prefixes); + trainerTypes = parser.readCDefineNames(filename, prefixes); if (trainerTypes.isEmpty()) { logError(QString("Failed to read trainer type constants from %1").arg(filename)); return false; @@ -2243,13 +2237,14 @@ bool Project::readSongNames() { QStringList songDefinePrefixes{ "\\bSE_", "\\bMUS_" }; QString filename = projectConfig.getFilePath(ProjectFilePath::constants_songs); fileWatcher.addPath(root + "/" + filename); - QMap songDefines = parser.readCDefines(filename, songDefinePrefixes); - this->songNames = songDefines.keys(); + this->songNames = parser.readCDefineNames(filename, songDefinePrefixes); this->defaultSong = this->songNames.value(0, "MUS_DUMMY"); if (this->songNames.isEmpty()) { logError(QString("Failed to read song names from %1.").arg(filename)); return false; } + // Song names don't have a very useful order (esp. if we include SE_* values), so sort them alphabetically. + this->songNames.sort(); return true; } @@ -2484,8 +2479,9 @@ bool Project::readSpeciesIconPaths() { static const QStringList prefixes("\\bSPECIES_"); const QString constantsFilename = projectConfig.getFilePath(ProjectFilePath::constants_species); fileWatcher.addPath(root + "/" + constantsFilename); - const QMap defines = parser.readCDefines(constantsFilename, prefixes); // TODO: Suppress errors - const QStringList speciesNames = defines.isEmpty() ? monIconNames.keys() : defines.keys(); + QStringList speciesNames = parser.readCDefineNames(constantsFilename, prefixes); + if (speciesNames.isEmpty()) + speciesNames = monIconNames.keys(); bool missingIcons = false; for (auto species : speciesNames) { diff --git a/src/ui/newmappopup.cpp b/src/ui/newmappopup.cpp index ba315975..41c339ec 100644 --- a/src/ui/newmappopup.cpp +++ b/src/ui/newmappopup.cpp @@ -175,7 +175,7 @@ void NewMapPopup::setDefaultSettings(Project *project) { settings.secondaryTilesetLabel = project->getDefaultSecondaryTilesetLabel(); settings.type = project->mapTypes.at(0); settings.location = project->mapSectionValueToName.values().at(0); - settings.song = project->songNames.at(0); + settings.song = project->defaultSong; settings.canFlyTo = false; settings.showLocationName = true; settings.allowRunning = false;