diff --git a/include/config.h b/include/config.h index 30306f80..142d2bf1 100644 --- a/include/config.h +++ b/include/config.h @@ -29,11 +29,21 @@ class KeyValueConfigBase { public: bool save(); - void load(); - virtual ~KeyValueConfigBase(); + bool load(const QString &dir = QString()); + + void setRoot(const QString &dir); + QString root() const { return m_root; } + QString filepath() const { return m_filepath; } + QString filename() const { return m_filename; } + + explicit KeyValueConfigBase(const QString &filename) + : m_root(QString()), + m_filename(filename), + m_filepath(filename) + { }; + virtual ~KeyValueConfigBase() {}; virtual void reset() = 0; protected: - virtual QString getConfigFilepath() = 0; virtual void parseConfigKeyValue(QString key, QString value) = 0; virtual QMap getKeyValueMap() = 0; virtual void init() = 0; @@ -43,14 +53,16 @@ protected: static int getConfigInteger(const QString &key, const QString &value, int min = INT_MIN, int max = INT_MAX, int defaultValue = 0); static uint32_t getConfigUint32(const QString &key, const QString &value, uint32_t min = 0, uint32_t max = UINT_MAX, uint32_t defaultValue = 0); static QColor getConfigColor(const QString &key, const QString &value, const QColor &defaultValue = Qt::black); + + QString m_root; + QString m_filename; + QString m_filepath; }; class PorymapConfig: public KeyValueConfigBase { public: - PorymapConfig() { - reset(); - } + PorymapConfig(); virtual void reset() override { this->recentProjects.clear(); this->projectManuallyClosed = false; @@ -167,7 +179,6 @@ public: std::set statusBarLogTypes; protected: - virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; virtual QMap getKeyValueMap() override; virtual void init() override {}; @@ -318,9 +329,7 @@ enum ProjectFilePath { class ProjectConfig: public KeyValueConfigBase { public: - ProjectConfig() { - reset(); - } + ProjectConfig(); virtual void reset() override { this->baseGameVersion = BaseGameVersion::pokeemerald; // Reset non-version-specific settings @@ -365,6 +374,7 @@ public: static QString getPlayerIconPath(BaseGameVersion baseGameVersion, int character); static QIcon getPlayerIcon(BaseGameVersion baseGameVersion, int character); + QString projectDir() const { return m_root; } // Alias for root() void reset(BaseGameVersion baseGameVersion); void setFilePath(ProjectFilePath pathId, const QString &path); void setFilePath(const QString &pathId, const QString &path); @@ -387,7 +397,6 @@ public: QMap getPokemonIconPaths(); BaseGameVersion baseGameVersion; - QString projectDir; bool usePoryScript; bool useCustomBorderSize; bool eventWeatherTriggerEnabled; @@ -435,7 +444,6 @@ public: QMap globalConstants; protected: - virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; virtual QMap getKeyValueMap() override; virtual void init() override; @@ -454,27 +462,25 @@ extern ProjectConfig projectConfig; class UserConfig: public KeyValueConfigBase { public: - UserConfig() { - reset(); - } + UserConfig(); virtual void reset() override { this->recentMapOrLayout = QString(); this->useEncounterJson = true; this->customScripts.clear(); this->readKeys.clear(); } + + QString projectDir() const { return m_root; } // Alias for root() void parseCustomScripts(QString input); QString outputCustomScripts(); void setCustomScripts(QStringList scripts, QList enabled); QStringList getCustomScriptPaths(); QList getCustomScriptsEnabled(); - QString projectDir; QString recentMapOrLayout; bool useEncounterJson; protected: - virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; virtual QMap getKeyValueMap() override; virtual void init() override; @@ -496,10 +502,7 @@ class Shortcut; class ShortcutsConfig : public KeyValueConfigBase { public: - ShortcutsConfig() : - user_shortcuts({ }), - default_shortcuts({ }) - { } + ShortcutsConfig(); virtual void reset() override { user_shortcuts.clear(); } @@ -512,7 +515,6 @@ public: QList userShortcuts(const QObject *object) const; protected: - virtual QString getConfigFilepath() override; virtual void parseConfigKeyValue(QString key, QString value) override; virtual QMap getKeyValueMap() override; virtual void init() override { }; diff --git a/include/mainwindow.h b/include/mainwindow.h index 73408817..3b3003aa 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -386,7 +386,6 @@ private: void scrollMapListToCurrentLayout(MapTree *list); void scrollCurrentMapListToItem(const QString &itemName); void showFileWatcherWarning(); - QString getExistingDirectory(QString); bool openProject(QString dir, bool initial = false); bool closeProject(); void showRecentError(const QString &baseMessage); diff --git a/src/config.cpp b/src/config.cpp index ffc46f0a..766a20e4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -203,17 +203,26 @@ ProjectFilePath reverseDefaultPaths(QString str) { return static_cast(-1); } -KeyValueConfigBase::~KeyValueConfigBase() { - +void KeyValueConfigBase::setRoot(const QString &root) { + m_root = root; + QDir dir(m_root); + if (!m_root.isEmpty() && !dir.exists()) { + dir.mkpath(m_root); + } + m_filepath = dir.absoluteFilePath(m_filename); } -void KeyValueConfigBase::load() { +bool KeyValueConfigBase::load(const QString &root) { + if (!root.isEmpty()) { + setRoot(root); + } reset(); - QFile file(this->getConfigFilepath()); - if (!file.exists()) { + QFile file(this->filepath()); + if (file.exists() && !file.open(QIODevice::ReadOnly)) { + logError(QString("Could not open config file '%1': ").arg(this->filepath()) + file.errorString()); + return false; + } else if (file.size() == 0) { this->init(); - } else if (!file.open(QIODevice::ReadOnly)) { - logError(QString("Could not open config file '%1': ").arg(this->getConfigFilepath()) + file.errorString()); } QTextStream in(&file); @@ -231,7 +240,7 @@ void KeyValueConfigBase::load() { QRegularExpressionMatch match = re.match(line); if (!match.hasMatch()) { - logWarn(QString("Invalid config line in %1: '%2'").arg(this->getConfigFilepath()).arg(line)); + logWarn(QString("Invalid config line in %1: '%2'").arg(this->filepath()).arg(line)); continue; } @@ -240,6 +249,7 @@ void KeyValueConfigBase::load() { this->setUnreadKeys(); file.close(); + return true; } bool KeyValueConfigBase::save() { @@ -249,9 +259,9 @@ bool KeyValueConfigBase::save() { text += QString("%1=%2\n").arg(it.key()).arg(it.value()); } - QFile file(this->getConfigFilepath()); + QFile file(this->filepath()); if (!file.open(QIODevice::WriteOnly)) { - logError(QString("Could not open config file '%1' for writing: ").arg(this->getConfigFilepath()) + file.errorString()); + logError(QString("Could not open config file '%1' for writing: ").arg(this->filepath()) + file.errorString()); return false; } @@ -300,16 +310,9 @@ QColor KeyValueConfigBase::getConfigColor(const QString &key, const QString &val PorymapConfig porymapConfig; -QString PorymapConfig::getConfigFilepath() { - // porymap config file is in the same directory as porymap itself. - QString settingsPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QDir dir(settingsPath); - if (!dir.exists()) - dir.mkpath(settingsPath); - - QString configPath = dir.absoluteFilePath("porymap.cfg"); - - return configPath; +PorymapConfig::PorymapConfig() : KeyValueConfigBase(QStringLiteral("porymap.cfg")) { + reset(); + setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); } void PorymapConfig::parseConfigKeyValue(QString key, QString value) { @@ -330,7 +333,7 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { bool ok; int tab = key.mid(QStringLiteral("map_list_hide_empty_enabled/").length()).toInt(&ok, 0); if (!ok) { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); return; } this->mapListHideEmptyEnabled.insert(tab, getConfigBool(key, value)); @@ -485,7 +488,7 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) { this->statusBarLogTypes.insert(type); } } else { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } } @@ -773,9 +776,8 @@ QIcon ProjectConfig::getPlayerIcon(BaseGameVersion baseGameVersion, int characte ProjectConfig projectConfig; -QString ProjectConfig::getConfigFilepath() { - // porymap config file is in the same directory as porymap itself. - return QDir(this->projectDir).filePath("porymap.project.cfg"); +ProjectConfig::ProjectConfig() : KeyValueConfigBase(QStringLiteral("porymap.project.cfg")) { + reset(); } void ProjectConfig::parseConfigKeyValue(QString key, QString value) { @@ -871,14 +873,14 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { if (k != static_cast(-1)) { this->setFilePath(k, value); } else { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } } else if (key.startsWith("ident/")) { auto identifierId = reverseDefaultIdentifier(key.mid(QStringLiteral("ident/").length())); if (identifierId != static_cast(-1)) { this->setIdentifier(identifierId, value); } else { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } } else if (key.startsWith("global_constant/")) { this->globalConstants.insert(key.mid(QStringLiteral("global_constant/").length()), value); @@ -935,7 +937,7 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) { } else if (key == "forced_major_version") { this->forcedMajorVersion = getConfigInteger(key, value); } else { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } readKeys.append(key); } @@ -1048,7 +1050,7 @@ QMap ProjectConfig::getKeyValueMap() { } void ProjectConfig::init() { - QString dirName = QDir(this->projectDir).dirName().toLower(); + QString dirName = QDir(this->projectDir()).dirName().toLower(); BaseGameVersion version = stringToBaseGameVersion(dirName); if (version != BaseGameVersion::none) { @@ -1107,7 +1109,7 @@ QString ProjectConfig::getFilePath(ProjectFilePath pathId) { QString customPath = this->getCustomFilePath(pathId); if (!customPath.isEmpty()) { // A custom filepath has been specified. If the file/folder exists, use that. - const QString baseDir = this->projectDir + "/"; + const QString baseDir = this->projectDir() + "/"; if (customPath.startsWith(baseDir)) { customPath.remove(0, baseDir.length()); } @@ -1201,9 +1203,8 @@ QMap ProjectConfig::getPokemonIconPaths() { UserConfig userConfig; -QString UserConfig::getConfigFilepath() { - // porymap config file is in the same directory as porymap itself. - return QDir(this->projectDir).filePath("porymap.user.cfg"); +UserConfig::UserConfig() : KeyValueConfigBase(QStringLiteral("porymap.user.cfg")) { + reset(); } void UserConfig::parseConfigKeyValue(QString key, QString value) { @@ -1214,7 +1215,7 @@ void UserConfig::parseConfigKeyValue(QString key, QString value) { } else if (key == "custom_scripts") { this->parseCustomScripts(value); } else { - logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->getConfigFilepath()).arg(key)); + logWarn(QString("Invalid config key found in config file %1: '%2'").arg(this->filepath()).arg(key)); } readKeys.append(key); } @@ -1283,15 +1284,10 @@ QList UserConfig::getCustomScriptsEnabled() { ShortcutsConfig shortcutsConfig; -QString ShortcutsConfig::getConfigFilepath() { - QString settingsPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - QDir dir(settingsPath); - if (!dir.exists()) - dir.mkpath(settingsPath); - - QString configPath = dir.absoluteFilePath("porymap.shortcuts.cfg"); - - return configPath; +ShortcutsConfig::ShortcutsConfig() : KeyValueConfigBase(QStringLiteral("porymap.shortcuts.cfg")), + user_shortcuts({ }), + default_shortcuts({ }) { + setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); } void ShortcutsConfig::parseConfigKeyValue(QString key, QString value) { diff --git a/src/core/map.cpp b/src/core/map.cpp index 7754948f..e69068f3 100644 --- a/src/core/map.cpp +++ b/src/core/map.cpp @@ -176,7 +176,7 @@ QStringList Map::getScriptLabels(Event::Group group) { QString Map::getScriptsFilepath() const { const bool usePoryscript = projectConfig.usePoryScript; auto path = QDir::cleanPath(QString("%1/%2/%3/scripts") - .arg(projectConfig.projectDir) + .arg(projectConfig.projectDir()) .arg(projectConfig.getFilePath(ProjectFilePath::data_map_folders)) .arg(!m_sharedScriptsMap.isEmpty() ? m_sharedScriptsMap : m_name)); auto extension = Project::getScriptFileExtension(usePoryscript); @@ -188,7 +188,7 @@ QString Map::getScriptsFilepath() const { QString Map::getJsonFilepath(const QString &mapName) { return QDir::cleanPath(QString("%1/%2/%3/map.json") - .arg(projectConfig.projectDir) + .arg(projectConfig.projectDir()) .arg(projectConfig.getFilePath(ProjectFilePath::data_map_folders)) .arg(mapName)); } diff --git a/src/editor.cpp b/src/editor.cpp index 6de4895f..3a12838e 100644 --- a/src/editor.cpp +++ b/src/editor.cpp @@ -2339,7 +2339,7 @@ void Editor::openMapJson(const QString &mapName) const { } void Editor::openLayoutJson(const QString &layoutId) const { - QString path = QDir::cleanPath(QString("%1/%2").arg(projectConfig.projectDir).arg(projectConfig.getFilePath(ProjectFilePath::json_layouts))); + QString path = QDir::cleanPath(QString("%1/%2").arg(projectConfig.projectDir()).arg(projectConfig.getFilePath(ProjectFilePath::json_layouts))); QString idField = QString("\"id\": \"%1\",").arg(layoutId); openInTextEditor(path, ParseUtil::getJsonLineNumber(path, idField)); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 027602c5..8d2cef6f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -728,10 +728,11 @@ bool MainWindow::openProject(QString dir, bool initial) { porysplash->start(); porysplash->showLoadingMessage("config"); - userConfig.projectDir = dir; - userConfig.load(); - projectConfig.projectDir = dir; - projectConfig.load(); + if (!projectConfig.load(dir) || !userConfig.load(dir)) { + showProjectOpenFailure(); + porysplash->stop(); + return false; + } porysplash->showLoadingMessage("custom scripts"); Scripting::init(this); @@ -992,13 +993,8 @@ void MainWindow::showFileWatcherWarning() { this->fileWatcherWarning->exec(); } -QString MainWindow::getExistingDirectory(QString dir) { - return FileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly); -} - -void MainWindow::on_action_Open_Project_triggered() -{ - QString dir = getExistingDirectory(!projectConfig.projectDir.isEmpty() ? userConfig.projectDir : "."); +void MainWindow::on_action_Open_Project_triggered() { + QString dir = FileDialog::getExistingDirectory(this, QStringLiteral("Choose Project Folder")); if (!dir.isEmpty()) openProject(dir); } @@ -3010,7 +3006,7 @@ void MainWindow::reloadScriptEngine() { Scripting::init(this); Scripting::populateGlobalObject(this); // Lying to the scripts here, simulating a project reload - Scripting::cb_ProjectOpened(projectConfig.projectDir); + Scripting::cb_ProjectOpened(projectConfig.projectDir()); if (this->editor) { if (this->editor->layout) Scripting::cb_LayoutOpened(this->editor->layout->name); diff --git a/src/project.cpp b/src/project.cpp index 28634dde..eada0202 100644 --- a/src/project.cpp +++ b/src/project.cpp @@ -3375,7 +3375,7 @@ QString Project::getExistingFilepath(QString filepath) { if (filepath.isEmpty() || QFile::exists(filepath)) return filepath; - filepath = QDir::cleanPath(projectConfig.projectDir + QDir::separator() + filepath); + filepath = QDir::cleanPath(projectConfig.projectDir() + QDir::separator() + filepath); if (QFile::exists(filepath)) return filepath; diff --git a/src/ui/customscriptseditor.cpp b/src/ui/customscriptseditor.cpp index 7cc89a14..43becad9 100644 --- a/src/ui/customscriptseditor.cpp +++ b/src/ui/customscriptseditor.cpp @@ -11,7 +11,7 @@ CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) : QMainWindow(parent), ui(new Ui::CustomScriptsEditor), - baseDir(userConfig.projectDir + "/") + baseDir(userConfig.projectDir() + "/") { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); diff --git a/src/ui/prefab.cpp b/src/ui/prefab.cpp index b1f39679..32a6c86a 100644 --- a/src/ui/prefab.cpp +++ b/src/ui/prefab.cpp @@ -94,7 +94,7 @@ void Prefab::savePrefabs() { QFileInfo info(filepath); if (info.isRelative()) { - filepath = QDir::cleanPath(projectConfig.projectDir + QDir::separator() + filepath); + filepath = QDir::cleanPath(projectConfig.projectDir() + QDir::separator() + filepath); } QFile prefabsFile(filepath); if (!prefabsFile.open(QIODevice::WriteOnly)) { @@ -297,7 +297,7 @@ bool Prefab::tryImportDefaultPrefabs(QWidget * parent, BaseGameVersion version, if (fileInfo.suffix().isEmpty()) filepath += ".json"; if (fileInfo.isRelative()) { - absFilepath = QDir::cleanPath(projectConfig.projectDir + QDir::separator() + filepath); + absFilepath = QDir::cleanPath(projectConfig.projectDir() + QDir::separator() + filepath); } else { absFilepath = filepath; } diff --git a/src/ui/projectsettingseditor.cpp b/src/ui/projectsettingseditor.cpp index fa2d3ee2..ce91b2b3 100644 --- a/src/ui/projectsettingseditor.cpp +++ b/src/ui/projectsettingseditor.cpp @@ -21,7 +21,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(QWidget *parent, Project *project) QMainWindow(parent), ui(new Ui::ProjectSettingsEditor), project(project), - baseDir(projectConfig.projectDir + "/") + baseDir(projectConfig.projectDir() + "/") { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose);