diff --git a/forms/newdefinedialog.ui b/forms/newdefinedialog.ui
new file mode 100644
index 00000000..c816c6e3
--- /dev/null
+++ b/forms/newdefinedialog.ui
@@ -0,0 +1,89 @@
+
+
+ NewDefineDialog
+
+
+
+ 0
+ 0
+ 252
+ 124
+
+
+
+ true
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+ color: rgb(255, 0, 0)
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Value
+
+
+
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+ QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok
+
+
+ false
+
+
+
+
+
+
+
+
diff --git a/forms/projectsettingseditor.ui b/forms/projectsettingseditor.ui
index 4cf06515..05598b8f 100644
--- a/forms/projectsettingseditor.ui
+++ b/forms/projectsettingseditor.ui
@@ -1582,19 +1582,21 @@
499
-
- -
-
-
- ...
+
+
-
+
+
+ Qt::Orientation::Horizontal
-
-
- :/icons/help.ico:/icons/help.ico
+
+
+ 40
+ 20
+
-
+
- -
+
-
@@ -1626,7 +1628,7 @@
0
0
544
- 437
+ 338
@@ -1646,6 +1648,34 @@
+ -
+
+
+ <html><head/><body><p>Add additional C files containing #defines or enums. These will be used to resolve unknown symbols during project launch.</p></body></html>
+
+
+ Add Global Constants File...
+
+
+
+ :/icons/add.ico:/icons/add.ico
+
+
+
+ -
+
+
+ ...
+
+
+
+ :/icons/help.ico:/icons/help.ico
+
+
+
+ -
+
+
@@ -1671,8 +1701,8 @@
499
-
- -
+
+
-
...
@@ -1683,7 +1713,7 @@
- -
+
-
@@ -1715,7 +1745,7 @@
0
0
544
- 437
+ 421
@@ -1735,6 +1765,36 @@
+ -
+
+
+ <html><head/><body><p>Add an additional #define name and expression. This may be used to evaluate other #defines during project launch.</p></body></html>
+
+
+ Add Global Constant...
+
+
+
+ :/icons/add.ico:/icons/add.ico
+
+
+
+ -
+
+
+ Qt::Orientation::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
diff --git a/include/config.h b/include/config.h
index 67a8b507..26d2a353 100644
--- a/include/config.h
+++ b/include/config.h
@@ -345,6 +345,8 @@ public:
this->unusedTileCovered = 0x0000;
this->unusedTileSplit = 0x0000;
this->maxEventsPerGroup = 255;
+ this->globalConstantsFilepaths.clear();
+ this->globalConstants.clear();
this->identifiers.clear();
this->readKeys.clear();
}
@@ -417,6 +419,8 @@ public:
QMargins playerViewDistance;
QList warpBehaviors;
int maxEventsPerGroup;
+ QStringList globalConstantsFilepaths;
+ QMap globalConstants;
protected:
virtual QString getConfigFilepath() override;
diff --git a/include/core/parseutil.h b/include/core/parseutil.h
index e02d0504..aae86a88 100644
--- a/include/core/parseutil.h
+++ b/include/core/parseutil.h
@@ -43,7 +43,7 @@ class ParseUtil
{
public:
ParseUtil();
- void set_root(const QString &dir);
+ void setRoot(const QString &dir) { this->root = dir; }
static QString readTextFile(const QString &path, QString *error = nullptr);
bool cacheFile(const QString &path, QString *error = nullptr);
void clearFileCache() { this->fileCache.clear(); }
@@ -55,9 +55,13 @@ public:
QString readCIncbin(const QString &text, const QString &label);
QMap readCIncbinMulti(const QString &filepath);
QStringList readCIncbinArray(const QString &filename, const QString &label);
- QMap readCDefinesByRegex(const QString &filename, const QSet ®exList, QString *error = nullptr);
- QMap readCDefinesByName(const QString &filename, const QSet &names, QString *error = nullptr);
+ QHash readCDefinesByRegex(const QString &filename, const QSet ®exList, QString *error = nullptr);
+ QHash readCDefinesByName(const QString &filename, const QSet &names, QString *error = nullptr);
QStringList readCDefineNames(const QString &filename, const QSet ®exList, QString *error = nullptr);
+ void loadGlobalCDefinesFromFile(const QString &filename, QString *error = nullptr);
+ void loadGlobalCDefines(const QMap &defines);
+ void loadGlobalCDefines(const QHash &defines);
+ void resetCDefines();
OrderedMap> readCStructs(const QString &, const QString & = "", const QHash& = {});
QList getLabelMacros(const QList&, const QString&);
QStringList getLabelValues(const QList&, const QString&);
@@ -90,8 +94,20 @@ private:
QString curDefine;
QHash fileCache;
QHash errorMap;
- int evaluateDefine(const QString&, const QString &, QMap*, QMap*);
- QList tokenizeExpression(QString, QMap*, QMap*);
+
+ // The maps of define names to values/expressions that are available while parsing C defines.
+ // As the parser reads and evaluates more defines it will update these maps accordingly.
+ QHash knownDefineValues;
+ QHash knownDefineExpressions;
+
+ // Maps of special define names to values/expressions that take precedence over defines encountered while parsing.
+ // Some (like 'TRUE'/'FALSE') are always present in these maps, others may be specified by the user with 'loadGlobalCDefines' / 'loadGlobalCDefinesFromFile'.
+ QHash globalDefineValues;
+ QHash globalDefineExpressions;
+
+ int evaluateDefine(const QString &identifier, bool *ok = nullptr);
+ int evaluateExpression(const QString &expression);
+ QList tokenizeExpression(QString expression);
QList generatePostfix(const QList &tokens);
int evaluatePostfix(const QList &postfix);
void recordError(const QString &message);
@@ -100,11 +116,11 @@ private:
QString createErrorMessage(const QString &message, const QString &expression);
struct ParsedDefines {
- QMap expressions; // Map of all define names encountered to their expressions
+ QHash expressions; // Map of all define names encountered to their expressions
QStringList filteredNames; // List of define names that matched the search text, in the order that they were encountered
};
ParsedDefines readCDefines(const QString &filename, const QSet &filterList, bool useRegex, QString *error);
- QMap evaluateCDefines(const QString &filename, const QSet &filterList, bool useRegex, QString *error);
+ QHash evaluateCDefines(const QString &filename, const QSet &filterList, bool useRegex, QString *error);
bool defineNameMatchesFilter(const QString &name, const QSet &filterList) const;
bool defineNameMatchesFilter(const QString &name, const QSet &filterList) const;
QString loadTextFile(const QString &path, QString *error = nullptr);
diff --git a/include/project.h b/include/project.h
index 1031640b..2d2b5fd1 100644
--- a/include/project.h
+++ b/include/project.h
@@ -76,7 +76,7 @@ public:
int maxEncounterRate;
bool wildEncountersLoaded;
- void set_root(QString);
+ void setRoot(const QString&);
void clearMaps();
void clearTilesetCache();
@@ -203,6 +203,7 @@ public:
bool readEventGraphics();
bool readFieldmapProperties();
bool readFieldmapMasks();
+ bool readGlobalConstants();
QMap> readObjEventGfxInfo();
QPixmap getEventPixmap(const QString &gfxName, const QString &movementName);
diff --git a/include/ui/newdefinedialog.h b/include/ui/newdefinedialog.h
new file mode 100644
index 00000000..2107dcec
--- /dev/null
+++ b/include/ui/newdefinedialog.h
@@ -0,0 +1,32 @@
+#ifndef NEWDEFINEDIALOG_H
+#define NEWDEFINEDIALOG_H
+
+#include
+#include
+
+namespace Ui {
+class NewDefineDialog;
+}
+
+class NewDefineDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit NewDefineDialog(QWidget *parent = nullptr);
+ ~NewDefineDialog();
+
+ virtual void accept() override;
+
+signals:
+ void createdDefine(const QString &name, const QString &expression);
+
+private:
+ Ui::NewDefineDialog *ui;
+
+ bool validateName(bool allowEmpty = false);
+ void onNameChanged(const QString &name);
+ void dialogButtonClicked(QAbstractButton *button);
+};
+
+#endif // NEWDEFINEDIALOG_H
diff --git a/include/ui/projectsettingseditor.h b/include/ui/projectsettingseditor.h
index e4a6ae94..579aec21 100644
--- a/include/ui/projectsettingseditor.h
+++ b/include/ui/projectsettingseditor.h
@@ -67,6 +67,12 @@ private:
void setWarpBehaviorsList(QStringList list);
void openFilesHelp();
void openIdentifiersHelp();
+ void addNewGlobalConstantsFilepath();
+ void addGlobalConstantsFilepath(const QString &filepath);
+ QStringList getGlobalConstantsFilepaths();
+ void addNewGlobalConstant();
+ void addGlobalConstant(const QString &name, const QString &expression);
+ QMap getGlobalConstants();
private slots:
void dialogButtonClicked(QAbstractButton *button);
diff --git a/porymap.pro b/porymap.pro
index 263bc61b..ff894f39 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -104,6 +104,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/metatileselector.cpp \
src/ui/movablerect.cpp \
src/ui/movementpermissionsselector.cpp \
+ src/ui/newdefinedialog.cpp \
src/ui/neweventtoolbutton.cpp \
src/ui/newlayoutdialog.cpp \
src/ui/newlayoutform.cpp \
@@ -217,6 +218,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/metatileselector.h \
include/ui/movablerect.h \
include/ui/movementpermissionsselector.h \
+ include/ui/newdefinedialog.h \
include/ui/neweventtoolbutton.h \
include/ui/newlayoutdialog.h \
include/ui/newlayoutform.h \
@@ -272,6 +274,7 @@ FORMS += forms/mainwindow.ui \
forms/loadingscreen.ui \
forms/mapheaderform.ui \
forms/maplisttoolbar.ui \
+ forms/newdefinedialog.ui \
forms/newlayoutdialog.ui \
forms/newlayoutform.ui \
forms/newlocationdialog.ui \
diff --git a/src/config.cpp b/src/config.cpp
index 7e7422c7..0e22840e 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -234,7 +234,7 @@ void KeyValueConfigBase::load() {
continue;
}
- this->parseConfigKeyValue(match.captured("key").trimmed().toLower(), match.captured("value").trimmed());
+ this->parseConfigKeyValue(match.captured("key").trimmed(), match.captured("value").trimmed());
}
this->setUnreadKeys();
@@ -840,6 +840,10 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else {
logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key));
}
+ } else if (key.startsWith("global_constant/")) {
+ this->globalConstants.insert(key.mid(QStringLiteral("global_constant/").length()), value);
+ } else if (key == "global_constants_filepaths") {
+ this->globalConstantsFilepaths = value.split(",", Qt::SkipEmptyParts);
} else if (key == "prefabs_filepath") {
this->prefabFilepath = value;
} else if (key == "prefabs_import_prompted") {
@@ -863,7 +867,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else if (key == "event_icon_path_heal") {
this->eventIconPaths[Event::Group::Heal] = value;
} else if (key.startsWith("pokemon_icon_path/")) {
- this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()).toUpper(), value);
+ this->pokemonIconPaths.insert(key.mid(QStringLiteral("pokemon_icon_path/").length()), value);
} else if (key == "collision_sheet_path") {
this->collisionSheetPath = value;
} else if (key == "collision_sheet_width") {
@@ -970,12 +974,16 @@ QMap ProjectConfig::getKeyValueMap() {
map.insert("event_icon_path_coord", this->eventIconPaths[Event::Group::Coord]);
map.insert("event_icon_path_bg", this->eventIconPaths[Event::Group::Bg]);
map.insert("event_icon_path_heal", this->eventIconPaths[Event::Group::Heal]);
- for (auto i = this->pokemonIconPaths.cbegin(), end = this->pokemonIconPaths.cend(); i != end; i++){
- const QString path = i.value();
- if (!path.isEmpty()) map.insert("pokemon_icon_path/" + i.key(), path);
+ for (auto it = this->pokemonIconPaths.constBegin(); it != this->pokemonIconPaths.constEnd(); it++) {
+ const QString path = it.value();
+ if (!path.isEmpty()) map.insert("pokemon_icon_path/" + it.key(), path);
}
- for (auto i = this->identifiers.cbegin(), end = this->identifiers.cend(); i != end; i++) {
- map.insert("ident/"+defaultIdentifiers.value(i.key()).first, i.value());
+ for (auto it = this->globalConstants.constBegin(); it != this->globalConstants.constEnd(); it++) {
+ map.insert("global_constant/" + it.key(), it.value());
+ }
+ map.insert("global_constants_filepaths", this->globalConstantsFilepaths.join(","));
+ for (auto it = this->identifiers.constBegin(); it != this->identifiers.constEnd(); it++) {
+ map.insert("ident/"+defaultIdentifiers.value(it.key()).first, it.value());
}
map.insert("collision_sheet_path", this->collisionSheetPath);
map.insert("collision_sheet_width", QString::number(this->collisionSheetSize.width()));
diff --git a/src/core/parseutil.cpp b/src/core/parseutil.cpp
index 4e048140..cba8c223 100644
--- a/src/core/parseutil.cpp
+++ b/src/core/parseutil.cpp
@@ -16,26 +16,8 @@ const QRegularExpression ParseUtil::re_poryScriptLabel("\\b(script)(\\((global|l
const QRegularExpression ParseUtil::re_globalPoryScriptLabel("\\b(script)(\\((global)\\))?\\s*\\b(?