From 357bf3db61ca4dcb44569b00560bc956579b9aab Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 25 Feb 2024 14:01:20 +0000 Subject: [PATCH 01/36] Load all config files --- apps/launcher/maindialog.cpp | 10 ++-------- components/files/qtconfigpath.hpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 5d558ef38f..f9d07d54a5 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -349,17 +349,11 @@ bool Launcher::MainDialog::setupGameSettings() if (!loadFile(Files::getUserConfigPathQString(mCfgMgr), &Config::GameSettings::readUserFile)) return false; - // Now the rest - priority: user > local > global - if (auto result = loadFile(Files::getLocalConfigPathQString(mCfgMgr), &Config::GameSettings::readFile, true)) + for (const auto& path : Files::getActiveConfigPathsQString(mCfgMgr)) { - // Load global if local wasn't found - if (!*result && !loadFile(Files::getGlobalConfigPathQString(mCfgMgr), &Config::GameSettings::readFile, true)) + if (!loadFile(path, &Config::GameSettings::readFile)) return false; } - else - return false; - if (!loadFile(Files::getUserConfigPathQString(mCfgMgr), &Config::GameSettings::readFile)) - return false; return true; } diff --git a/components/files/qtconfigpath.hpp b/components/files/qtconfigpath.hpp index e6d7b4202c..16e0499cd5 100644 --- a/components/files/qtconfigpath.hpp +++ b/components/files/qtconfigpath.hpp @@ -22,6 +22,16 @@ namespace Files { return Files::pathToQString(cfgMgr.getGlobalPath() / openmwCfgFile); } + + inline QStringList getActiveConfigPathsQString(const Files::ConfigurationManager& cfgMgr) + { + const auto& activePaths = cfgMgr.getActiveConfigPaths(); + QStringList result; + result.reserve(static_cast(activePaths.size())); + for (const auto& path : activePaths) + result.append(Files::pathToQString(path / openmwCfgFile)); + return result; + } } #endif // OPENMW_COMPONENTS_FILES_QTCONFIGPATH_H From 626f438dcc4de7a2581f630356dce91bd5717cba Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 27 Feb 2024 01:09:46 +0000 Subject: [PATCH 02/36] Make builtin.omwscripts actually mandatory Previously it was quasi-mandatory - lots of things would add it, e.g. when running openmw through the CS, but it could technically be disabled. Now it's treated like the resources/vfs directory and implicitly added by the engine etc. --- apps/bulletobjecttool/main.cpp | 4 +++- apps/navmeshtool/main.cpp | 4 +++- apps/opencs/model/doc/runner.cpp | 1 - apps/openmw/main.cpp | 3 ++- components/contentselector/model/contentmodel.cpp | 4 ---- files/openmw.cfg | 1 - files/openmw.cfg.local | 1 - 7 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/bulletobjecttool/main.cpp b/apps/bulletobjecttool/main.cpp index 884c196e53..b27c8135d6 100644 --- a/apps/bulletobjecttool/main.cpp +++ b/apps/bulletobjecttool/main.cpp @@ -146,7 +146,9 @@ namespace dataDirs.insert(dataDirs.begin(), resDir / "vfs"); const Files::Collections fileCollections(dataDirs); const auto& archives = variables["fallback-archive"].as(); - const auto& contentFiles = variables["content"].as(); + StringsVector contentFiles{ "builtin.omwscripts" }; + const auto& configContentFiles = variables["content"].as(); + contentFiles.insert(contentFiles.end(), configContentFiles.begin(), configContentFiles.end()); Fallback::Map::init(variables["fallback"].as().mMap); diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index 3ec34114af..94ab7ef082 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -165,7 +165,9 @@ namespace NavMeshTool dataDirs.insert(dataDirs.begin(), resDir / "vfs"); const Files::Collections fileCollections(dataDirs); const auto& archives = variables["fallback-archive"].as(); - const auto& contentFiles = variables["content"].as(); + StringsVector contentFiles{ "builtin.omwscripts" }; + const auto& configContentFiles = variables["content"].as(); + contentFiles.insert(contentFiles.end(), configContentFiles.begin(), configContentFiles.end()); const std::size_t threadsNumber = variables["threads"].as(); if (threadsNumber < 1) diff --git a/apps/opencs/model/doc/runner.cpp b/apps/opencs/model/doc/runner.cpp index 0099cb2f94..d647d6b498 100644 --- a/apps/opencs/model/doc/runner.cpp +++ b/apps/opencs/model/doc/runner.cpp @@ -93,7 +93,6 @@ void CSMDoc::Runner::start(bool delayed) arguments << "--data=\"" + Files::pathToQString(mProjectPath.parent_path()) + "\""; arguments << "--replace=content"; - arguments << "--content=builtin.omwscripts"; for (const auto& mContentFile : mContentFiles) { diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 5bbc0211c1..beaa452f19 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -108,7 +108,8 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati Log(Debug::Error) << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..."; return false; } - std::set contentDedupe; + engine.addContentFile("builtin.omwscripts"); + std::set contentDedupe{ "builtin.omwscripts" }; for (const auto& contentFile : content) { if (!contentDedupe.insert(contentFile).second) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index d800112712..377edc3b1c 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -434,10 +434,6 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf { QFileInfo info(dir.absoluteFilePath(path2)); - // Enabled by default in system openmw.cfg; shouldn't be shown in content list. - if (info.fileName().compare("builtin.omwscripts", Qt::CaseInsensitive) == 0) - continue; - EsmFile* file = const_cast(item(info.fileName())); bool add = file == nullptr; std::unique_ptr newFile; diff --git a/files/openmw.cfg b/files/openmw.cfg index 37ecda3b1d..20e11323cf 100644 --- a/files/openmw.cfg +++ b/files/openmw.cfg @@ -2,7 +2,6 @@ # Modifications should be done on the user openmw.cfg file instead # (see: https://openmw.readthedocs.io/en/master/reference/modding/paths.html) -content=builtin.omwscripts data-local="?userdata?data" user-data="?userdata?" config="?userconfig?" diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index c9949f2447..65f8b31136 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -2,7 +2,6 @@ # Modifications should be done on the user openmw.cfg file instead # (see: https://openmw.readthedocs.io/en/master/reference/modding/paths.html) -content=builtin.omwscripts data-local="?userdata?data" user-data="?userdata?" config="?userconfig?" From 90966ecc477bd14661a862e2bd9915f94bbf59dd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 27 Feb 2024 01:39:49 +0000 Subject: [PATCH 03/36] Handle replace= lines properly in the launcher --- components/config/gamesettings.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index ad7c73d3d9..339acd01fe 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -98,8 +98,32 @@ bool Config::GameSettings::readUserFile(QTextStream& stream, bool ignoreContent) bool Config::GameSettings::readFile(QTextStream& stream, QMultiMap& settings, bool ignoreContent) { QMultiMap cache; + QRegularExpression replaceRe("^\\s*replace\\s*=\\s*(.+)$"); QRegularExpression keyRe("^([^=]+)\\s*=\\s*(.+)$"); + auto initialPos = stream.pos(); + + while (!stream.atEnd()) + { + QString line = stream.readLine(); + + if (line.isEmpty() || line.startsWith("#")) + continue; + + QRegularExpressionMatch match = keyRe.match(line); + if (match.hasMatch()) + { + QString key = match.captured(1).trimmed(); + // Replace composing entries with a replace= line + if (key == QLatin1String("data") || key == QLatin1String("fallback-archive") + || key == QLatin1String("content") || key == QLatin1String("groundcover") + || key == QLatin1String("script-blacklist")) + settings.remove(key); + } + } + + stream.seek(initialPos); + while (!stream.atEnd()) { QString line = stream.readLine(); From dbdecfe94b814a85a16aac0760d7dce6789c29cc Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 27 Feb 2024 01:41:12 +0000 Subject: [PATCH 04/36] Use approved safety comment for path escaping explanation I thought I'd got this one already --- components/config/gamesettings.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 339acd01fe..cf0f7e382d 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -147,8 +147,11 @@ bool Config::GameSettings::readFile(QTextStream& stream, QMultiMap Date: Tue, 27 Feb 2024 02:14:31 +0000 Subject: [PATCH 05/36] data-local is already unquoted when it's read --- components/config/gamesettings.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index cf0f7e382d..53924c4313 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -47,11 +47,6 @@ void Config::GameSettings::validatePaths() // Do the same for data-local QString local = mSettings.value(QString("data-local")); - if (local.length() && local.at(0) == QChar('\"')) - { - local.remove(0, 1); - local.chop(1); - } if (local.isEmpty()) return; From f476301670d208690f660a054a3463d35b80142a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 27 Feb 2024 14:11:48 +0000 Subject: [PATCH 06/36] There's no such thing as the global data directory That's what resources/vfs is for. --- apps/launcher/datafilespage.cpp | 10 +++++----- components/config/gamesettings.cpp | 10 ++++------ components/config/gamesettings.hpp | 3 ++- components/config/launchersettings.cpp | 6 +++--- components/files/configurationmanager.cpp | 5 ----- components/files/configurationmanager.hpp | 1 - 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 92b86e9cec..a0287afd87 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -279,9 +279,9 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) if (!mDataLocal.isEmpty()) directories.insert(0, mDataLocal); - const auto& globalDataDir = mGameSettings.getGlobalDataDir(); - if (!globalDataDir.empty()) - directories.insert(0, Files::pathToQString(globalDataDir)); + const auto& resourcesVfs = mGameSettings.getResourcesVfs(); + if (!resourcesVfs.isEmpty()) + directories.insert(0, resourcesVfs); std::unordered_set visitedDirectories; for (const QString& currentDir : directories) @@ -315,8 +315,8 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) item->setFont(font); } - // deactivate data-local and global data directory: they are always included - if (currentDir == mDataLocal || Files::pathFromQString(currentDir) == globalDataDir) + // deactivate data-local and resources/vfs: they are always included + if (currentDir == mDataLocal || currentDir == resourcesVfs) { auto flags = item->flags(); item->setFlags(flags & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled)); diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 53924c4313..f8ff3d362c 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -64,13 +64,11 @@ void Config::GameSettings::validatePaths() } } -std::filesystem::path Config::GameSettings::getGlobalDataDir() const +QString Config::GameSettings::getResourcesVfs() const { - // global data dir may not exists if OpenMW is not installed (ie if run from build directory) - const auto& path = mCfgMgr.getGlobalDataPath(); - if (std::filesystem::exists(path)) - return std::filesystem::canonical(path); - return {}; + QString resources = mSettings.value(QString("resources"), QString("./resources")); + resources += "/vfs"; + return QFileInfo(resources).canonicalFilePath(); } QStringList Config::GameSettings::values(const QString& key, const QStringList& defaultValues) const diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 34e3dc73ea..bef108e2c7 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -53,7 +53,8 @@ namespace Config } QStringList getDataDirs() const; - std::filesystem::path getGlobalDataDir() const; + + QString getResourcesVfs() const; inline void removeDataDir(const QString& dir) { diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index 9d12535619..2f4decb762 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -233,10 +233,10 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) return; } - // global and local data directories are not part of any profile - const auto globalDataDir = Files::pathToQString(gameSettings.getGlobalDataDir()); + // local data directory and resources/vfs are not part of any profile + const auto resourcesVfs = gameSettings.getResourcesVfs(); const auto dataLocal = gameSettings.getDataLocal(); - dirs.removeAll(globalDataDir); + dirs.removeAll(resourcesVfs); dirs.removeAll(dataLocal); // if any existing profile in launcher matches the content list, make that profile the default diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 210261cdf4..10e10375bb 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -411,11 +411,6 @@ namespace Files return mFixedPath.getLocalPath(); } - const std::filesystem::path& ConfigurationManager::getGlobalDataPath() const - { - return mFixedPath.getGlobalDataPath(); - } - const std::filesystem::path& ConfigurationManager::getCachePath() const { return mFixedPath.getCachePath(); diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 8a5bca0869..aec7799fea 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -45,7 +45,6 @@ namespace Files const std::filesystem::path& getGlobalPath() const; const std::filesystem::path& getLocalPath() const; - const std::filesystem::path& getGlobalDataPath() const; const std::filesystem::path& getUserConfigPath() const; const std::filesystem::path& getUserDataPath() const; const std::filesystem::path& getLocalDataPath() const; From 322a378907b034abfd722a861c7ab04f929ea0fb Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 28 Feb 2024 00:49:15 +0000 Subject: [PATCH 07/36] Load correct config files in the wizard --- apps/wizard/main.cpp | 2 +- apps/wizard/mainwizard.cpp | 8 +++----- apps/wizard/mainwizard.hpp | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/wizard/main.cpp b/apps/wizard/main.cpp index b62428f946..e740f06015 100644 --- a/apps/wizard/main.cpp +++ b/apps/wizard/main.cpp @@ -47,7 +47,7 @@ int main(int argc, char* argv[]) l10n::installQtTranslations(app, "wizard", resourcesPath); - Wizard::MainWizard wizard; + Wizard::MainWizard wizard(std::move(configurationManager)); wizard.show(); return app.exec(); diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index 2f1f373cfd..e8bd6f7007 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -26,9 +26,10 @@ using namespace Process; -Wizard::MainWizard::MainWizard(QWidget* parent) +Wizard::MainWizard::MainWizard(Files::ConfigurationManager&& cfgMgr, QWidget* parent) : QWizard(parent) , mInstallations() + , mCfgMgr(cfgMgr) , mError(false) , mGameSettings(mCfgMgr) { @@ -172,10 +173,7 @@ void Wizard::MainWizard::setupGameSettings() file.close(); // Now the rest - QStringList paths; - paths.append(Files::getUserConfigPathQString(mCfgMgr)); - paths.append(QLatin1String("openmw.cfg")); - paths.append(Files::getGlobalConfigPathQString(mCfgMgr)); + QStringList paths = Files::getActiveConfigPathsQString(mCfgMgr); for (const QString& path2 : paths) { diff --git a/apps/wizard/mainwizard.hpp b/apps/wizard/mainwizard.hpp index 60f46fa1a5..2f120f1955 100644 --- a/apps/wizard/mainwizard.hpp +++ b/apps/wizard/mainwizard.hpp @@ -45,7 +45,7 @@ namespace Wizard Page_Conclusion }; - MainWizard(QWidget* parent = nullptr); + MainWizard(Files::ConfigurationManager&& cfgMgr, QWidget* parent = nullptr); ~MainWizard() override; bool findFiles(const QString& name, const QString& path); From d111b4bbd9bd8570ab8524a72dab044585eea911 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 28 Feb 2024 00:58:30 +0000 Subject: [PATCH 08/36] Handle built-in content files in content model There's also handling for files declared as originating from a lower-priority openmw.cfg, e.g. anything in the local config or any intermediate ones, as they can't be disabled or reordered. There's no way to mark such files yet, but the logic's the same as built-in files, so everything will be fine once that's set up. --- .../contentselector/model/contentmodel.cpp | 38 +++++++++++++++---- components/contentselector/model/esmfile.cpp | 27 +++++++++++++ components/contentselector/model/esmfile.hpp | 19 +++++++++- 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 377edc3b1c..003f2ee241 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -109,6 +109,9 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index if (!file) return Qt::NoItemFlags; + if (file->builtIn() || file->fromAnotherConfigFile()) + return Qt::NoItemFlags; + // game files can always be checked if (file == mGameFile) return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; @@ -130,7 +133,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index continue; noGameFiles = false; - if (mCheckedFiles.contains(depFile)) + if (depFile->builtIn() || depFile->fromAnotherConfigFile() || mCheckedFiles.contains(depFile)) { gamefileChecked = true; break; @@ -217,14 +220,14 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int if (file == mGameFile) return QVariant(); - return mCheckedFiles.contains(file) ? Qt::Checked : Qt::Unchecked; + return (file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file)) ? Qt::Checked : Qt::Unchecked; } case Qt::UserRole: { if (file == mGameFile) return ContentType_GameFile; - else if (flags(index)) + else return ContentType_Addon; break; @@ -279,7 +282,12 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const { int checkValue = value.toInt(); bool setState = false; - if (checkValue == Qt::Checked && !mCheckedFiles.contains(file)) + if (file->builtIn() || file->fromAnotherConfigFile()) + { + setState = false; + success = false; + } + else if (checkValue == Qt::Checked && !mCheckedFiles.contains(file)) { setState = true; success = true; @@ -374,6 +382,13 @@ bool ContentSelectorModel::ContentModel::dropMimeData( else if (parent.isValid()) beginRow = parent.row(); + int firstModifiable = 0; + while (item(firstModifiable)->builtIn() || item(firstModifiable)->fromAnotherConfigFile()) + ++firstModifiable; + + if (beginRow < firstModifiable) + return false; + QByteArray encodedData = data->data(mMimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); @@ -449,6 +464,9 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf file->setGameFiles({}); } + if (info.fileName().compare("builtin.omwscripts", Qt::CaseInsensitive) == 0) + file->setBuiltIn(true); + if (info.fileName().endsWith(".omwscripts", Qt::CaseInsensitive)) { file->setDate(info.lastModified()); @@ -579,15 +597,20 @@ void ContentSelectorModel::ContentModel::setCurrentGameFile(const EsmFile* file) void ContentSelectorModel::ContentModel::sortFiles() { emit layoutAboutToBeChanged(); + + int firstModifiable = 0; + while (mFiles.at(firstModifiable)->builtIn() || mFiles.at(firstModifiable)->fromAnotherConfigFile()) + ++firstModifiable; + // Dependency sort std::unordered_set moved; - for (int i = mFiles.size() - 1; i > 0;) + for (int i = mFiles.size() - 1; i > firstModifiable;) { const auto file = mFiles.at(i); if (moved.find(file) == moved.end()) { int index = -1; - for (int j = 0; j < i; ++j) + for (int j = firstModifiable; j < i; ++j) { const QStringList& gameFiles = mFiles.at(j)->gameFiles(); // All addon files are implicitly dependent on the game file @@ -650,6 +673,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL { if (setCheckState(filepath, true)) { + // setCheckState already gracefully handles builtIn and fromAnotherConfigFile // as necessary, move plug-ins in visible list to match sequence of supplied filelist const EsmFile* file = item(filepath); int filePosition = indexFromItem(file).row(); @@ -747,7 +771,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString& filepath, const EsmFile* file = item(filepath); - if (!file) + if (!file || file->builtIn() || file->fromAnotherConfigFile()) return false; if (checkState) diff --git a/components/contentselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp index e4280baef7..02ac0efa70 100644 --- a/components/contentselector/model/esmfile.cpp +++ b/components/contentselector/model/esmfile.cpp @@ -41,6 +41,16 @@ void ContentSelectorModel::EsmFile::setDescription(const QString& description) mDescription = description; } +void ContentSelectorModel::EsmFile::setBuiltIn(bool builtIn) +{ + mBuiltIn = builtIn; +} + +void ContentSelectorModel::EsmFile::setFromAnotherConfigFile(bool fromAnotherConfigFile) +{ + mFromAnotherConfigFile = fromAnotherConfigFile; +} + bool ContentSelectorModel::EsmFile::isGameFile() const { return (mGameFiles.size() == 0) @@ -76,6 +86,14 @@ QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) co return mDescription; break; + case FileProperty_BuiltIn: + return mBuiltIn; + break; + + case FileProperty_FromAnotherConfigFile: + return mFromAnotherConfigFile; + break; + case FileProperty_GameFile: return mGameFiles; break; @@ -113,6 +131,15 @@ void ContentSelectorModel::EsmFile::setFileProperty(const FileProperty prop, con mDescription = value; break; + // todo: check these work + case FileProperty_BuiltIn: + mBuiltIn = value == "true"; + break; + + case FileProperty_FromAnotherConfigFile: + mFromAnotherConfigFile = value == "true"; + break; + case FileProperty_GameFile: mGameFiles << value; break; diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index 606cc3d319..42b6cfeff6 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -26,7 +26,9 @@ namespace ContentSelectorModel FileProperty_DateModified = 3, FileProperty_FilePath = 4, FileProperty_Description = 5, - FileProperty_GameFile = 6 + FileProperty_BuiltIn = 6, + FileProperty_FromAnotherConfigFile = 7, + FileProperty_GameFile = 8, }; EsmFile(const QString& fileName = QString(), ModelItem* parent = nullptr); @@ -40,6 +42,8 @@ namespace ContentSelectorModel void setFilePath(const QString& path); void setGameFiles(const QStringList& gameFiles); void setDescription(const QString& description); + void setBuiltIn(bool builtIn); + void setFromAnotherConfigFile(bool fromAnotherConfigFile); void addGameFile(const QString& name) { mGameFiles.append(name); } QVariant fileProperty(const FileProperty prop) const; @@ -49,18 +53,27 @@ namespace ContentSelectorModel QDateTime modified() const { return mModified; } QString formatVersion() const { return mVersion; } QString filePath() const { return mPath; } + bool builtIn() const { return mBuiltIn; } + bool fromAnotherConfigFile() const { return mFromAnotherConfigFile; } /// @note Contains file names, not paths. const QStringList& gameFiles() const { return mGameFiles; } QString description() const { return mDescription; } QString toolTip() const { - return mTooltipTemlate.arg(mAuthor) + QString tooltip = mTooltipTemlate.arg(mAuthor) .arg(mVersion) .arg(mModified.toString(Qt::ISODate)) .arg(mPath) .arg(mDescription) .arg(mGameFiles.join(", ")); + + if (mBuiltIn) + tooltip += tr("
This content file cannot be disabled because it is part of OpenMW.
"); + else if (mFromAnotherConfigFile) + tooltip += tr("
This content file cannot be disabled because it is enabled in a config file other than the user one.
"); + + return tooltip; } bool isGameFile() const; @@ -82,6 +95,8 @@ namespace ContentSelectorModel QStringList mGameFiles; QString mDescription; QString mToolTip; + bool mBuiltIn = false; + bool mFromAnotherConfigFile = false; }; } From 9e1334cc097b6765f42e7943d98fe1f0a69540df Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 28 Feb 2024 23:49:55 +0000 Subject: [PATCH 09/36] Resync composing and path openmw.cfg settings with options.cpp --- components/config/gamesettings.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index f8ff3d362c..8ddf72325f 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -108,9 +108,10 @@ bool Config::GameSettings::readFile(QTextStream& stream, QMultiMap Date: Thu, 29 Feb 2024 00:48:03 +0000 Subject: [PATCH 11/36] Tooltips for data-local and resources/vfs --- apps/launcher/datafilespage.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index a0287afd87..de72aa2577 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -295,7 +295,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) // add new achives files presents in current directory addArchivesFromDir(currentDir); - QString tooltip; + QStringList tooltip; // add content files presents in current directory mSelector->addFiles(currentDir, mNewDataDirs.contains(canonicalDirPath)); @@ -308,7 +308,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) // Display new content with custom formatting if (mNewDataDirs.contains(canonicalDirPath)) { - tooltip += tr("Will be added to the current profile"); + tooltip << tr("Will be added to the current profile"); QFont font = item->font(); font.setBold(true); font.setItalic(true); @@ -321,15 +321,17 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) auto flags = item->flags(); item->setFlags(flags & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled)); } + if (currentDir == mDataLocal) + tooltip << tr("This is the data-local directory and cannot be disabled"); + else if (currentDir == resourcesVfs) + tooltip << tr("This directory is part of OpenMW and cannot be disabled"); // Add a "data file" icon if the directory contains a content file if (mSelector->containsDataFiles(currentDir)) { item->setIcon(QIcon(":/images/openmw-plugin.png")); - if (!tooltip.isEmpty()) - tooltip += "\n"; - tooltip += tr("Contains content file(s)"); + tooltip << tr("Contains content file(s)"); } else { @@ -339,7 +341,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) auto emptyIcon = QIcon(pixmap); item->setIcon(emptyIcon); } - item->setToolTip(tooltip); + item->setToolTip(tooltip.join('\n')); } mSelector->sortFiles(); From a130ca57a49d56c6c6125a4cfbc1b969dcccfb4c Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 00:36:13 +0000 Subject: [PATCH 12/36] Track source of settings This one's a biggie. The basic idea's that GameSettings should know: * what the interpreted value of a setting is, so it can actually be used. * what the original value the user put in their config was, so it can be put back when the config's saved. * which path it's processing the openmw.cfg from so relative paths can be resolved correctly. * whether a setting's a user setting that can be modified, or from one of the other openmw.cfg files that can't necessarily be modified. This had fairly wide-reaching implications. The first is that paths are resolved properly in cases where they previously wouldn't have been. Without this commit, if the launcher saw a relative path in an openmw.cfg, it'd be resolved relative to the process' working directory (which we always set to the binary directory for reasons I won't get into). That's not what the engine does, so is bad. It's also not something a user's likely to suspect. This mess is no longer a problem as paths are resolved correctly when they're loaded instead of on demand when they're used by whatever uses them. Another problem was that if paths used slugs like ?userconfig? would be written back to openmw.cfg with the slugs replaced, which defeats the object of using the slugs. This is also fixed. Tracking which settings are user settings and which are in a non-editable openmw.cfg allows the launcher to grey out rows so they can't be edited (which is sensible as they can't be edited on-disk) while still being aware of content files that are provided by non-user data directories etc. This is done in a pretty straightforward way for the data directories and fallback-archives, as those bits of UI are basic, but it's more complicated for content files as that uses a nmodel/view approach and has a lot more moving parts. Thankfully, I'd already implemented that when dealing with builtin.omwscripts, so it just needed wiring up. One more thing of note is that I made the SettingValue struct storable as a QVariant so it could be attached to the UI widgets as userdata, and then I could just grab the original representation and use it instead of needing any complicated mapping from display value to on-disk value. --- apps/launcher/datafilespage.cpp | 150 +++++++++++++----- apps/launcher/datafilespage.hpp | 6 +- apps/launcher/importpage.cpp | 6 +- apps/launcher/maindialog.cpp | 14 +- apps/launcher/settingspage.cpp | 18 +-- apps/wizard/mainwizard.cpp | 28 ++-- components/config/gamesettings.cpp | 143 ++++++++++------- components/config/gamesettings.hpp | 82 +++++++--- components/config/launchersettings.cpp | 46 +++++- .../contentselector/model/contentmodel.cpp | 27 +++- .../contentselector/model/contentmodel.hpp | 3 +- .../contentselector/view/contentselector.cpp | 7 +- .../contentselector/view/contentselector.hpp | 1 + 13 files changed, 367 insertions(+), 164 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index de72aa2577..d87073df02 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -142,7 +142,7 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C ui.setupUi(this); setObjectName("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector(ui.contentSelectorWidget, /*showOMWScripts=*/true); - const QString encoding = mGameSettings.value("encoding", "win1252"); + const QString encoding = mGameSettings.value("encoding", { "win1252" }).value; mSelector->setEncoding(encoding); QVector> languages = { { "English", tr("English") }, { "French", tr("French") }, @@ -163,11 +163,11 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C connect(ui.directoryInsertButton, &QPushButton::released, this, [this]() { this->addSubdirectories(false); }); connect(ui.directoryUpButton, &QPushButton::released, this, [this]() { this->moveDirectory(-1); }); connect(ui.directoryDownButton, &QPushButton::released, this, [this]() { this->moveDirectory(1); }); - connect(ui.directoryRemoveButton, &QPushButton::released, this, [this]() { this->removeDirectory(); }); + connect(ui.directoryRemoveButton, &QPushButton::released, this, &DataFilesPage::removeDirectory); connect(ui.archiveUpButton, &QPushButton::released, this, [this]() { this->moveArchives(-1); }); connect(ui.archiveDownButton, &QPushButton::released, this, [this]() { this->moveArchives(1); }); - connect( - ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, [this]() { this->sortDirectories(); }); + connect(ui.directoryListWidget->model(), &QAbstractItemModel::rowsMoved, this, &DataFilesPage::sortDirectories); + connect(ui.archiveListWidget->model(), &QAbstractItemModel::rowsMoved, this, &DataFilesPage::sortArchives); buildView(); loadSettings(); @@ -271,39 +271,50 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) ui.archiveListWidget->clear(); ui.directoryListWidget->clear(); - QStringList directories = mLauncherSettings.getDataDirectoryList(contentModelName); - if (directories.isEmpty()) - directories = mGameSettings.getDataDirs(); + QList directories = mGameSettings.getDataDirs(); + QStringList contentModelDirectories = mLauncherSettings.getDataDirectoryList(contentModelName); + if (!contentModelDirectories.isEmpty()) + { + directories.erase(std::remove_if(directories.begin(), directories.end(), + [&](const Config::SettingValue& dir) { return mGameSettings.isUserSetting(dir); }), + directories.end()); + for (const auto& dir : contentModelDirectories) + directories.push_back({ dir }); + } mDataLocal = mGameSettings.getDataLocal(); if (!mDataLocal.isEmpty()) - directories.insert(0, mDataLocal); + directories.insert(0, { mDataLocal }); const auto& resourcesVfs = mGameSettings.getResourcesVfs(); if (!resourcesVfs.isEmpty()) - directories.insert(0, resourcesVfs); + directories.insert(0, { resourcesVfs }); std::unordered_set visitedDirectories; - for (const QString& currentDir : directories) + for (const Config::SettingValue& currentDir : directories) { - // normalize user supplied directories: resolve symlink, convert to native separator, make absolute - const QString canonicalDirPath = QDir(QDir::cleanPath(currentDir)).canonicalPath(); + // normalize user supplied directories: resolve symlink, convert to native separator + const QString canonicalDirPath = QDir(QDir::cleanPath(currentDir.value)).canonicalPath(); if (!visitedDirectories.insert(canonicalDirPath).second) continue; // add new achives files presents in current directory - addArchivesFromDir(currentDir); + addArchivesFromDir(currentDir.value); QStringList tooltip; // add content files presents in current directory - mSelector->addFiles(currentDir, mNewDataDirs.contains(canonicalDirPath)); + mSelector->addFiles(currentDir.value, mNewDataDirs.contains(canonicalDirPath)); // add current directory to list - ui.directoryListWidget->addItem(currentDir); + ui.directoryListWidget->addItem(currentDir.originalRepresentation); auto row = ui.directoryListWidget->count() - 1; auto* item = ui.directoryListWidget->item(row); + item->setData(Qt::UserRole, QVariant::fromValue(currentDir)); + + if (currentDir.value != currentDir.originalRepresentation) + tooltip << tr("Resolved as %1").arg(currentDir.value); // Display new content with custom formatting if (mNewDataDirs.contains(canonicalDirPath)) @@ -316,18 +327,22 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) } // deactivate data-local and resources/vfs: they are always included - if (currentDir == mDataLocal || currentDir == resourcesVfs) + // same for ones from non-user config files + if (currentDir.value == mDataLocal || currentDir.value == resourcesVfs + || !mGameSettings.isUserSetting(currentDir)) { auto flags = item->flags(); item->setFlags(flags & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled)); + if (currentDir.value == mDataLocal) + tooltip << tr("This is the data-local directory and cannot be disabled"); + else if (currentDir.value == resourcesVfs) + tooltip << tr("This directory is part of OpenMW and cannot be disabled"); + else + tooltip << tr("This directory is enabled in an openmw.cfg other than the user one"); } - if (currentDir == mDataLocal) - tooltip << tr("This is the data-local directory and cannot be disabled"); - else if (currentDir == resourcesVfs) - tooltip << tr("This directory is part of OpenMW and cannot be disabled"); // Add a "data file" icon if the directory contains a content file - if (mSelector->containsDataFiles(currentDir)) + if (mSelector->containsDataFiles(currentDir.value)) { item->setIcon(QIcon(":/images/openmw-plugin.png")); @@ -345,15 +360,22 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) } mSelector->sortFiles(); - QStringList selectedArchives = mLauncherSettings.getArchiveList(contentModelName); - if (selectedArchives.isEmpty()) - selectedArchives = mGameSettings.getArchiveList(); + QList selectedArchives = mGameSettings.getArchiveList(); + QStringList contentModelSelectedArchives = mLauncherSettings.getArchiveList(contentModelName); + if (contentModelSelectedArchives.isEmpty()) + { + selectedArchives.erase(std::remove_if(selectedArchives.begin(), selectedArchives.end(), + [&](const Config::SettingValue& dir) { return mGameSettings.isUserSetting(dir); }), + selectedArchives.end()); + for (const auto& dir : contentModelSelectedArchives) + selectedArchives.push_back({ dir }); + } // sort and tick BSA according to profile int row = 0; for (const auto& archive : selectedArchives) { - const auto match = ui.archiveListWidget->findItems(archive, Qt::MatchExactly); + const auto match = ui.archiveListWidget->findItems(archive.value, Qt::MatchExactly); if (match.isEmpty()) continue; const auto name = match[0]->text(); @@ -361,9 +383,25 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) ui.archiveListWidget->takeItem(oldrow); ui.archiveListWidget->insertItem(row, name); ui.archiveListWidget->item(row)->setCheckState(Qt::Checked); + ui.archiveListWidget->item(row)->setData(Qt::UserRole, QVariant::fromValue(archive)); + if (!mGameSettings.isUserSetting(archive)) + { + auto flags = ui.archiveListWidget->item(row)->flags(); + ui.archiveListWidget->item(row)->setFlags( + flags & ~(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled)); + ui.archiveListWidget->item(row)->setToolTip( + tr("This archive is enabled in an openmw.cfg other than the user one")); + } row++; } + QStringList nonUserContent; + for (const auto& content : mGameSettings.getContentList()) + { + if (!mGameSettings.isUserSetting(content)) + nonUserContent.push_back(content.value); + } + mSelector->setNonUserContent(nonUserContent); mSelector->setProfileContent(mLauncherSettings.getContentListFiles(contentModelName)); } @@ -391,7 +429,19 @@ void Launcher::DataFilesPage::saveSettings(const QString& profile) { fileNames.append(item->fileName()); } - mLauncherSettings.setContentList(profileName, dirList, selectedArchivePaths(), fileNames); + QStringList dirNames; + for (const auto& dir : dirList) + { + if (mGameSettings.isUserSetting(dir)) + dirNames.push_back(dir.originalRepresentation); + } + QStringList archiveNames; + for (const auto& archive : selectedArchivePaths()) + { + if (mGameSettings.isUserSetting(archive)) + archiveNames.push_back(archive.originalRepresentation); + } + mLauncherSettings.setContentList(profileName, dirNames, archiveNames, fileNames); mGameSettings.setContentList(dirList, selectedArchivePaths(), fileNames); QString language(mSelector->languageBox()->currentData().toString()); @@ -400,38 +450,38 @@ void Launcher::DataFilesPage::saveSettings(const QString& profile) if (language == QLatin1String("Polish")) { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1250" }); } else if (language == QLatin1String("Russian")) { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1251" }); } else { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1252" }); } } -QStringList Launcher::DataFilesPage::selectedDirectoriesPaths() const +QList Launcher::DataFilesPage::selectedDirectoriesPaths() const { - QStringList dirList; + QList dirList; for (int i = 0; i < ui.directoryListWidget->count(); ++i) { const QListWidgetItem* item = ui.directoryListWidget->item(i); if (item->flags() & Qt::ItemIsEnabled) - dirList.append(item->text()); + dirList.append(qvariant_cast(item->data(Qt::UserRole))); } return dirList; } -QStringList Launcher::DataFilesPage::selectedArchivePaths() const +QList Launcher::DataFilesPage::selectedArchivePaths() const { - QStringList archiveList; + QList archiveList; for (int i = 0; i < ui.archiveListWidget->count(); ++i) { const QListWidgetItem* item = ui.archiveListWidget->item(i); if (item->checkState() == Qt::Checked) - archiveList.append(item->text()); + archiveList.append(qvariant_cast(item->data(Qt::UserRole))); } return archiveList; } @@ -585,7 +635,20 @@ void Launcher::DataFilesPage::on_cloneProfileAction_triggered() if (profile.isEmpty()) return; - mLauncherSettings.setContentList(profile, selectedDirectoriesPaths(), selectedArchivePaths(), selectedFilePaths()); + const auto& dirList = selectedDirectoriesPaths(); + QStringList dirNames; + for (const auto& dir : dirList) + { + if (mGameSettings.isUserSetting(dir)) + dirNames.push_back(dir.originalRepresentation); + } + QStringList archiveNames; + for (const auto& archive : selectedArchivePaths()) + { + if (mGameSettings.isUserSetting(archive)) + archiveNames.push_back(archive.originalRepresentation); + } + mLauncherSettings.setContentList(profile, dirNames, archiveNames, selectedFilePaths()); addProfile(profile, true); } @@ -704,6 +767,21 @@ void Launcher::DataFilesPage::sortDirectories() } } +void Launcher::DataFilesPage::sortArchives() +{ + // Ensure disabled entries (aka ones from non-user config files) are always at the top. + for (auto i = 1; i < ui.archiveListWidget->count(); ++i) + { + if (!(ui.archiveListWidget->item(i)->flags() & Qt::ItemIsEnabled) + && (ui.archiveListWidget->item(i - 1)->flags() & Qt::ItemIsEnabled)) + { + const auto item = ui.archiveListWidget->takeItem(i); + ui.archiveListWidget->insertItem(i - 1, item); + ui.archiveListWidget->setCurrentRow(i); + } + } +} + void Launcher::DataFilesPage::moveDirectory(int step) { int selectedRow = ui.directoryListWidget->currentRow(); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index e568137e8f..1b92354dab 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -25,6 +25,7 @@ namespace ContentSelectorView namespace Config { class GameSettings; + struct SettingValue; class LauncherSettings; } @@ -73,6 +74,7 @@ namespace Launcher void updateCloneProfileOkButton(const QString& text); void addSubdirectories(bool append); void sortDirectories(); + void sortArchives(); void removeDirectory(); void moveArchives(int step); void moveDirectory(int step); @@ -146,8 +148,8 @@ namespace Launcher * @return the file paths of all selected content files */ QStringList selectedFilePaths() const; - QStringList selectedArchivePaths() const; - QStringList selectedDirectoriesPaths() const; + QList selectedArchivePaths() const; + QList selectedDirectoriesPaths() const; }; } #endif diff --git a/apps/launcher/importpage.cpp b/apps/launcher/importpage.cpp index 44c5867c0d..47075db1bc 100644 --- a/apps/launcher/importpage.cpp +++ b/apps/launcher/importpage.cpp @@ -37,9 +37,9 @@ Launcher::ImportPage::ImportPage(const Files::ConfigurationManager& cfg, Config: // Detect Morrowind configuration files QStringList iniPaths; - for (const QString& path : mGameSettings.getDataDirs()) + for (const auto& path : mGameSettings.getDataDirs()) { - QDir dir(path); + QDir dir(path.value); dir.setPath(dir.canonicalPath()); // Resolve symlinks if (dir.exists(QString("Morrowind.ini"))) @@ -125,7 +125,7 @@ void Launcher::ImportPage::on_importerButton_clicked() arguments.append(QString("--fonts")); arguments.append(QString("--encoding")); - arguments.append(mGameSettings.value(QString("encoding"), QString("win1252"))); + arguments.append(mGameSettings.value(QString("encoding"), { "win1252" }).value); arguments.append(QString("--ini")); arguments.append(settingsComboBox->currentText()); arguments.append(QString("--cfg")); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index f9d07d54a5..aca8a64e31 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -320,7 +320,7 @@ bool Launcher::MainDialog::setupGameSettings() QFile file; - auto loadFile = [&](const QString& path, bool (Config::GameSettings::*reader)(QTextStream&, bool), + auto loadFile = [&](const QString& path, bool (Config::GameSettings::*reader)(QTextStream&, const QString&, bool), bool ignoreContent = false) -> std::optional { file.setFileName(path); if (file.exists()) @@ -337,7 +337,7 @@ bool Launcher::MainDialog::setupGameSettings() QTextStream stream(&file); Misc::ensureUtf8Encoding(stream); - (mGameSettings.*reader)(stream, ignoreContent); + (mGameSettings.*reader)(stream, QFileInfo(path).dir().path(), ignoreContent); file.close(); return true; } @@ -360,12 +360,12 @@ bool Launcher::MainDialog::setupGameSettings() bool Launcher::MainDialog::setupGameData() { - QStringList dataDirs; + bool foundData = false; // Check if the paths actually contain data files - for (const QString& path3 : mGameSettings.getDataDirs()) + for (const auto& path3 : mGameSettings.getDataDirs()) { - QDir dir(path3); + QDir dir(path3.value); QStringList filters; filters << "*.esp" << "*.esm" @@ -373,10 +373,10 @@ bool Launcher::MainDialog::setupGameData() << "*.omwaddon"; if (!dir.entryList(filters).isEmpty()) - dataDirs.append(path3); + foundData = true; } - if (dataDirs.isEmpty()) + if (!foundData) { QMessageBox msgBox; msgBox.setWindowTitle(tr("Error detecting Morrowind installation")); diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 93a724909e..0df871c90d 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -340,7 +340,7 @@ bool Launcher::SettingsPage::loadSettings() { loadSettingBool(Settings::input().mGrabCursor, *grabCursorCheckBox); - bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1; + bool skipMenu = mGameSettings.value("skip-menu").value.toInt() == 1; if (skipMenu) { skipMenuCheckBox->setCheckState(Qt::Checked); @@ -348,8 +348,8 @@ bool Launcher::SettingsPage::loadSettings() startDefaultCharacterAtLabel->setEnabled(skipMenu); startDefaultCharacterAtField->setEnabled(skipMenu); - startDefaultCharacterAtField->setText(mGameSettings.value("start")); - runScriptAfterStartupField->setText(mGameSettings.value("script-run")); + startDefaultCharacterAtField->setText(mGameSettings.value("start").value); + runScriptAfterStartupField->setText(mGameSettings.value("script-run").value); } return true; } @@ -536,17 +536,17 @@ void Launcher::SettingsPage::saveSettings() saveSettingBool(*grabCursorCheckBox, Settings::input().mGrabCursor); int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked; - if (skipMenu != mGameSettings.value("skip-menu").toInt()) - mGameSettings.setValue("skip-menu", QString::number(skipMenu)); + if (skipMenu != mGameSettings.value("skip-menu").value.toInt()) + mGameSettings.setValue("skip-menu", { QString::number(skipMenu) }); QString startCell = startDefaultCharacterAtField->text(); - if (startCell != mGameSettings.value("start")) + if (startCell != mGameSettings.value("start").value) { - mGameSettings.setValue("start", startCell); + mGameSettings.setValue("start", { startCell }); } QString scriptRun = runScriptAfterStartupField->text(); - if (scriptRun != mGameSettings.value("script-run")) - mGameSettings.setValue("script-run", scriptRun); + if (scriptRun != mGameSettings.value("script-run").value) + mGameSettings.setValue("script-run", { scriptRun }); } } diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index e8bd6f7007..0b5cadd979 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -24,6 +24,8 @@ #include "installationpage.hpp" #endif +#include + using namespace Process; Wizard::MainWizard::MainWizard(Files::ConfigurationManager&& cfgMgr, QWidget* parent) @@ -167,7 +169,7 @@ void Wizard::MainWizard::setupGameSettings() QTextStream stream(&file); Misc::ensureUtf8Encoding(stream); - mGameSettings.readUserFile(stream); + mGameSettings.readUserFile(stream, QFileInfo(path).dir().path()); } file.close(); @@ -196,7 +198,7 @@ void Wizard::MainWizard::setupGameSettings() QTextStream stream(&file); Misc::ensureUtf8Encoding(stream); - mGameSettings.readFile(stream); + mGameSettings.readFile(stream, QFileInfo(path2).dir().path()); } file.close(); } @@ -241,11 +243,11 @@ void Wizard::MainWizard::setupLauncherSettings() void Wizard::MainWizard::setupInstallations() { // Check if the paths actually contain a Morrowind installation - for (const QString& path : mGameSettings.getDataDirs()) + for (const auto& path : mGameSettings.getDataDirs()) { - if (findFiles(QLatin1String("Morrowind"), path)) - addInstallation(path); + if (findFiles(QLatin1String("Morrowind"), path.value)) + addInstallation(path.value); } } @@ -332,10 +334,12 @@ void Wizard::MainWizard::addInstallation(const QString& path) mInstallations.insert(QDir::toNativeSeparators(path), install); // Add it to the openmw.cfg too - if (!mGameSettings.getDataDirs().contains(path)) + const auto& dataDirs = mGameSettings.getDataDirs(); + if (std::none_of( + dataDirs.begin(), dataDirs.end(), [&](const Config::SettingValue& dir) { return dir.value == path; })) { - mGameSettings.setMultiValue(QLatin1String("data"), path); - mGameSettings.addDataDir(path); + mGameSettings.setMultiValue(QLatin1String("data"), { path }); + mGameSettings.addDataDir({ path }); } } @@ -394,15 +398,15 @@ void Wizard::MainWizard::writeSettings() if (language == QLatin1String("Polish")) { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1250")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1250" }); } else if (language == QLatin1String("Russian")) { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1251")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1251" }); } else { - mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); + mGameSettings.setValue(QLatin1String("encoding"), { "win1252" }); } // Write the installation path so that openmw can find them @@ -410,7 +414,7 @@ void Wizard::MainWizard::writeSettings() // Make sure the installation path is the last data= entry mGameSettings.removeDataDir(path); - mGameSettings.addDataDir(path); + mGameSettings.addDataDir({ path }); QString userPath(Files::pathToQString(mCfgMgr.getUserConfigPath())); QDir dir(userPath); diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 7cace721bf..9aed4656bc 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -13,7 +13,8 @@ const char Config::GameSettings::sDirectoryKey[] = "data"; namespace { - QStringList reverse(QStringList values) + template + QList reverse(QList values) { std::reverse(values.begin(), values.end()); return values; @@ -27,70 +28,69 @@ Config::GameSettings::GameSettings(const Files::ConfigurationManager& cfg) void Config::GameSettings::validatePaths() { - QStringList paths = mSettings.values(QString("data")); - Files::PathContainer dataDirs; + QList paths = mSettings.values(QString("data")); - for (const QString& path : paths) - { - dataDirs.emplace_back(Files::pathFromQString(path)); - } - - // Parse the data dirs to convert the tokenized paths - mCfgMgr.processPaths(dataDirs, /*basePath=*/""); mDataDirs.clear(); - for (const auto& dataDir : dataDirs) + for (const auto& dataDir : paths) { - if (is_directory(dataDir)) - mDataDirs.append(Files::pathToQString(dataDir)); + if (QDir(dataDir.value).exists()) + mDataDirs.append(dataDir); } // Do the same for data-local - QString local = mSettings.value(QString("data-local")); + const QString& local = mSettings.value(QString("data-local")).value; - if (local.isEmpty()) - return; - - dataDirs.clear(); - dataDirs.emplace_back(Files::pathFromQString(local)); - - mCfgMgr.processPaths(dataDirs, /*basePath=*/""); - - if (!dataDirs.empty()) - { - const auto& path = dataDirs.front(); - if (is_directory(path)) - mDataLocal = Files::pathToQString(path); - } + if (!local.isEmpty() && QDir(local).exists()) + mDataLocal = local; } QString Config::GameSettings::getResourcesVfs() const { - QString resources = mSettings.value(QString("resources"), QString("./resources")); + QString resources = mSettings.value(QString("resources"), { "./resources", "", "" }).value; resources += "/vfs"; return QFileInfo(resources).canonicalFilePath(); } -QStringList Config::GameSettings::values(const QString& key, const QStringList& defaultValues) const +QList Config::GameSettings::values( + const QString& key, const QList& defaultValues) const { if (!mSettings.values(key).isEmpty()) return mSettings.values(key); return defaultValues; } -bool Config::GameSettings::readFile(QTextStream& stream, bool ignoreContent) +bool Config::GameSettings::containsValue(const QString& key, const QString& value) const { - return readFile(stream, mSettings, ignoreContent); + auto [itr, end] = mSettings.equal_range(key); + while (itr != end) + { + if (itr->value == value) + return true; + ++itr; + } + return false; } -bool Config::GameSettings::readUserFile(QTextStream& stream, bool ignoreContent) +bool Config::GameSettings::readFile(QTextStream& stream, const QString& context, bool ignoreContent) { - return readFile(stream, mUserSettings, ignoreContent); + if (readFile(stream, mSettings, context, ignoreContent)) + { + mContexts.push_back(context); + return true; + } + return false; } -bool Config::GameSettings::readFile(QTextStream& stream, QMultiMap& settings, bool ignoreContent) +bool Config::GameSettings::readUserFile(QTextStream& stream, const QString& context, bool ignoreContent) { - QMultiMap cache; + return readFile(stream, mUserSettings, context, ignoreContent); +} + +bool Config::GameSettings::readFile( + QTextStream& stream, QMultiMap& settings, const QString& context, bool ignoreContent) +{ + QMultiMap cache; QRegularExpression replaceRe("^\\s*replace\\s*=\\s*(.+)$"); QRegularExpression keyRe("^([^=]+)\\s*=\\s*(.+)$"); @@ -129,7 +129,7 @@ bool Config::GameSettings::readFile(QTextStream& stream, QMultiMap values = cache.values(key); values.append(settings.values(key)); - if (!values.contains(value)) + bool exists = false; + for (const auto& existingValue : values) + { + if (existingValue.value == value.value) + { + exists = true; + break; + } + } + if (!exists) { cache.insert(key, value); } @@ -216,7 +230,7 @@ bool Config::GameSettings::writeFile(QTextStream& stream) // Equivalent to stream << std::quoted(i.value(), '"', '&'), which won't work on QStrings. QChar delim = '\"'; QChar escape = '&'; - QString string = i.value(); + QString string = i.value().originalRepresentation; stream << delim; for (auto& it : string) @@ -231,7 +245,7 @@ bool Config::GameSettings::writeFile(QTextStream& stream) continue; } - stream << i.key() << "=" << i.value() << "\n"; + stream << i.key() << "=" << i.value().originalRepresentation << "\n"; } return true; @@ -386,10 +400,11 @@ bool Config::GameSettings::writeFileWithComments(QFile& file) *iter = QString(); // assume no match QString key = match.captured(1); QString keyVal = match.captured(1) + "=" + match.captured(2); - QMultiMap::const_iterator i = mUserSettings.find(key); + QMultiMap::const_iterator i = mUserSettings.find(key); while (i != mUserSettings.end() && i.key() == key) { - QString settingLine = i.key() + "=" + i.value(); + // todo: does this need to handle paths? + QString settingLine = i.key() + "=" + i.value().originalRepresentation; QRegularExpressionMatch keyMatch = settingRegex.match(settingLine); if (keyMatch.hasMatch()) { @@ -441,7 +456,7 @@ bool Config::GameSettings::writeFileWithComments(QFile& file) // Equivalent to settingLine += std::quoted(it.value(), '"', '&'), which won't work on QStrings. QChar delim = '\"'; QChar escape = '&'; - QString string = it.value(); + QString string = it.value().originalRepresentation; settingLine += delim; for (auto& iter : string) @@ -453,7 +468,7 @@ bool Config::GameSettings::writeFileWithComments(QFile& file) settingLine += delim; } else - settingLine = it.key() + "=" + it.value(); + settingLine = it.key() + "=" + it.value().originalRepresentation; QRegularExpressionMatch match = settingRegex.match(settingLine); if (match.hasMatch()) @@ -512,11 +527,11 @@ bool Config::GameSettings::writeFileWithComments(QFile& file) bool Config::GameSettings::hasMaster() { bool result = false; - QStringList content = mSettings.values(QString(Config::GameSettings::sContentKey)); + QList content = mSettings.values(QString(Config::GameSettings::sContentKey)); for (int i = 0; i < content.count(); ++i) { - if (content.at(i).endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive) - || content.at(i).endsWith(QLatin1String(".esm"), Qt::CaseInsensitive)) + if (content.at(i).value.endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive) + || content.at(i).value.endsWith(QLatin1String(".esm"), Qt::CaseInsensitive)) { result = true; break; @@ -527,39 +542,49 @@ bool Config::GameSettings::hasMaster() } void Config::GameSettings::setContentList( - const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames) + const QList& dirNames, const QList& archiveNames, const QStringList& fileNames) { auto const reset = [this](const char* key, const QStringList& list) { remove(key); for (auto const& item : list) - setMultiValue(key, item); + setMultiValue(key, { item }); }; - reset(sDirectoryKey, dirNames); - reset(sArchiveKey, archiveNames); + remove(sDirectoryKey); + for (auto const& item : dirNames) + setMultiValue(sDirectoryKey, item); + remove(sArchiveKey); + for (auto const& item : archiveNames) + setMultiValue(sArchiveKey, item); reset(sContentKey, fileNames); } -QStringList Config::GameSettings::getDataDirs() const +QList Config::GameSettings::getDataDirs() const { return reverse(mDataDirs); } -QStringList Config::GameSettings::getArchiveList() const +QList Config::GameSettings::getArchiveList() const { // QMap returns multiple rows in LIFO order, so need to reverse return reverse(values(sArchiveKey)); } -QStringList Config::GameSettings::getContentList() const +QList Config::GameSettings::getContentList() const { // QMap returns multiple rows in LIFO order, so need to reverse return reverse(values(sContentKey)); } +bool Config::GameSettings::isUserSetting(const SettingValue& settingValue) const +{ + return settingValue.context.isEmpty() || settingValue.context == getUserContext(); +} + void Config::GameSettings::clear() { mSettings.clear(); + mContexts.clear(); mUserSettings.clear(); mDataDirs.clear(); mDataLocal.clear(); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index bef108e2c7..14a8fcb155 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -17,33 +17,48 @@ namespace Files namespace Config { + struct SettingValue + { + QString value = ""; + // value as found in openmw.cfg, e.g. relative path with ?slug? + QString originalRepresentation = value; + // path of openmw.cfg, e.g. to resolve relative paths + QString context = ""; + + friend auto operator<=>(const SettingValue&, const SettingValue&) = default; + }; + class GameSettings { public: explicit GameSettings(const Files::ConfigurationManager& cfg); - inline QString value(const QString& key, const QString& defaultValue = QString()) + inline SettingValue value(const QString& key, const SettingValue& defaultValue = {}) { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + return mSettings.contains(key) ? mSettings.value(key) : defaultValue; } - inline void setValue(const QString& key, const QString& value) + inline void setValue(const QString& key, const SettingValue& value) { mSettings.remove(key); mSettings.insert(key, value); mUserSettings.remove(key); - mUserSettings.insert(key, value); + if (isUserSetting(value)) + mUserSettings.insert(key, value); } - inline void setMultiValue(const QString& key, const QString& value) + inline void setMultiValue(const QString& key, const SettingValue& value) { - QStringList values = mSettings.values(key); + QList values = mSettings.values(key); if (!values.contains(value)) mSettings.insert(key, value); - values = mUserSettings.values(key); - if (!values.contains(value)) - mUserSettings.insert(key, value); + if (isUserSetting(value)) + { + values = mUserSettings.values(key); + if (!values.contains(value)) + mUserSettings.insert(key, value); + } } inline void remove(const QString& key) @@ -52,36 +67,48 @@ namespace Config mUserSettings.remove(key); } - QStringList getDataDirs() const; + QList getDataDirs() const; QString getResourcesVfs() const; - inline void removeDataDir(const QString& dir) + inline void removeDataDir(const QString& existingDir) { - if (!dir.isEmpty()) - mDataDirs.removeAll(dir); + if (!existingDir.isEmpty()) + { + // non-user settings can't be removed as we can't edit the openmw.cfg they're in + std::remove_if(mDataDirs.begin(), mDataDirs.end(), + [&](const SettingValue& dir) { return isUserSetting(dir) && dir.value == existingDir; }); + } } - inline void addDataDir(const QString& dir) + + inline void addDataDir(const SettingValue& dir) { - if (!dir.isEmpty()) + if (!dir.value.isEmpty()) mDataDirs.append(dir); } + inline QString getDataLocal() const { return mDataLocal; } bool hasMaster(); - QStringList values(const QString& key, const QStringList& defaultValues = QStringList()) const; + QList values(const QString& key, const QList& defaultValues = {}) const; + bool containsValue(const QString& key, const QString& value) const; - bool readFile(QTextStream& stream, bool ignoreContent = false); - bool readFile(QTextStream& stream, QMultiMap& settings, bool ignoreContent = false); - bool readUserFile(QTextStream& stream, bool ignoreContent = false); + bool readFile(QTextStream& stream, const QString& context, bool ignoreContent = false); + bool readFile(QTextStream& stream, QMultiMap& settings, const QString& context, + bool ignoreContent = false); + bool readUserFile(QTextStream& stream, const QString& context, bool ignoreContent = false); bool writeFile(QTextStream& stream); bool writeFileWithComments(QFile& file); - QStringList getArchiveList() const; - void setContentList(const QStringList& dirNames, const QStringList& archiveNames, const QStringList& fileNames); - QStringList getContentList() const; + QList getArchiveList() const; + void setContentList( + const QList& dirNames, const QList& archiveNames, const QStringList& fileNames); + QList getContentList() const; + + const QString& getUserContext() const { return mContexts.back(); } + bool isUserSetting(const SettingValue& settingValue) const; void clear(); @@ -89,10 +116,12 @@ namespace Config const Files::ConfigurationManager& mCfgMgr; void validatePaths(); - QMultiMap mSettings; - QMultiMap mUserSettings; + QMultiMap mSettings; + QMultiMap mUserSettings; - QStringList mDataDirs; + QStringList mContexts; + + QList mDataDirs; QString mDataLocal; static const char sArchiveKey[]; @@ -102,4 +131,7 @@ namespace Config static bool isOrderedLine(const QString& line); }; } + +Q_DECLARE_METATYPE(Config::SettingValue) + #endif // GAMESETTINGS_HPP diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index 2f4decb762..f9f067e58a 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -223,9 +223,25 @@ QStringList Config::LauncherSettings::getContentLists() void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) { // obtain content list from game settings (if present) - QStringList dirs(gameSettings.getDataDirs()); - const QStringList archives(gameSettings.getArchiveList()); - const QStringList files(gameSettings.getContentList()); + QList dirs(gameSettings.getDataDirs()); + dirs.erase(std::remove_if( + dirs.begin(), dirs.end(), [&](const SettingValue& dir) { return !gameSettings.isUserSetting(dir); }), + dirs.end()); + // archives and content files aren't preprocessed, so we don't need to track their original form + const QList archivesOriginal(gameSettings.getArchiveList()); + QStringList archives; + for (const auto& archive : archivesOriginal) + { + if (gameSettings.isUserSetting(archive)) + archives.push_back(archive.value); + } + const QList filesOriginal(gameSettings.getContentList()); + QStringList files; + for (const auto& file : filesOriginal) + { + if (gameSettings.isUserSetting(file)) + files.push_back(file.value); + } // if openmw.cfg has no content, exit so we don't create an empty content list. if (dirs.isEmpty() || files.isEmpty()) @@ -236,14 +252,25 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) // local data directory and resources/vfs are not part of any profile const auto resourcesVfs = gameSettings.getResourcesVfs(); const auto dataLocal = gameSettings.getDataLocal(); - dirs.removeAll(resourcesVfs); - dirs.removeAll(dataLocal); + dirs.erase( + std::remove_if(dirs.begin(), dirs.end(), [&](const SettingValue& dir) { return dir.value == resourcesVfs; }), + dirs.end()); + dirs.erase( + std::remove_if(dirs.begin(), dirs.end(), [&](const SettingValue& dir) { return dir.value == dataLocal; }), + dirs.end()); // if any existing profile in launcher matches the content list, make that profile the default for (const QString& listName : getContentLists()) { - if (files == getContentListFiles(listName) && archives == getArchiveList(listName) - && dirs == getDataDirectoryList(listName)) + const auto& listDirs = getDataDirectoryList(listName); + if (dirs.length() != listDirs.length()) + continue; + for (int i = 0; i < dirs.length(); ++i) + { + if (dirs[i].value != listDirs[i]) + continue; + } + if (files == getContentListFiles(listName) && archives == getArchiveList(listName)) { setCurrentContentListName(listName); return; @@ -253,7 +280,10 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) // otherwise, add content list QString newContentListName(makeNewContentListName()); setCurrentContentListName(newContentListName); - setContentList(newContentListName, dirs, archives, files); + QStringList newListDirs; + for (const auto& dir : dirs) + newListDirs.push_back(dir.value); + setContentList(newContentListName, newListDirs, archives, files); } void Config::LauncherSettings::setContentList(const QString& contentListName, const QStringList& dirNames, diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 003f2ee241..66fde2063f 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -220,7 +220,8 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int if (file == mGameFile) return QVariant(); - return (file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file)) ? Qt::Checked : Qt::Unchecked; + return (file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file)) ? Qt::Checked + : Qt::Unchecked; } case Qt::UserRole: @@ -467,6 +468,8 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf if (info.fileName().compare("builtin.omwscripts", Qt::CaseInsensitive) == 0) file->setBuiltIn(true); + file->setFromAnotherConfigFile(mNonUserContent.contains(info.fileName().toLower())); + if (info.fileName().endsWith(".omwscripts", Qt::CaseInsensitive)) { file->setDate(info.lastModified()); @@ -660,6 +663,28 @@ void ContentSelectorModel::ContentModel::setNew(const QString& filepath, bool is mNewFiles[filepath] = isNew; } +void ContentSelectorModel::ContentModel::setNonUserContent(const QStringList& fileList) +{ + mNonUserContent.clear(); + for (const auto& file : fileList) + mNonUserContent.insert(file.toLower()); + for (auto* file : mFiles) + file->setFromAnotherConfigFile(mNonUserContent.contains(file->fileName().toLower())); + + int insertPosition = 0; + while (mFiles.at(insertPosition)->builtIn()) + ++insertPosition; + + for (const auto& filepath : fileList) + { + const EsmFile* file = item(filepath); + int filePosition = indexFromItem(file).row(); + mFiles.move(filePosition, insertPosition++); + } + + sortFiles(); +} + bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile* file) const { return mPluginsWithLoadOrderError.contains(file->filePath()); diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index f754b9ea30..3cc05fd3cb 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -62,6 +62,7 @@ namespace ContentSelectorModel bool setCheckState(const QString& filepath, bool isChecked); bool isNew(const QString& filepath) const; void setNew(const QString& filepath, bool isChecked); + void setNonUserContent(const QStringList& fileList); void setContentList(const QStringList& fileList); ContentFileList checkedItems() const; void uncheckAll(); @@ -85,7 +86,7 @@ namespace ContentSelectorModel const EsmFile* mGameFile; ContentFileList mFiles; - QStringList mArchives; + QSet mNonUserContent; std::set mCheckedFiles; QHash mNewFiles; QSet mPluginsWithLoadOrderError; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 00c32e272d..a3fd224390 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -123,6 +123,11 @@ void ContentSelectorView::ContentSelector::buildContextMenu() mContextMenu->addAction(tr("&Copy Path(s) to Clipboard"), this, SLOT(slotCopySelectedItemsPaths())); } +void ContentSelectorView::ContentSelector::setNonUserContent(const QStringList& fileList) +{ + mContentModel->setNonUserContent(fileList); +} + void ContentSelectorView::ContentSelector::setProfileContent(const QStringList& fileList) { clearCheckStates(); @@ -336,4 +341,4 @@ void ContentSelectorView::ContentSelector::slotSearchFilterTextChanged(const QSt void ContentSelectorView::ContentSelector::slotRowsMoved() { ui->addonView->selectionModel()->clearSelection(); -} \ No newline at end of file +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 2b739645ba..2fdd38c799 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -40,6 +40,7 @@ namespace ContentSelectorView void sortFiles(); bool containsDataFiles(const QString& path); void clearFiles(); + void setNonUserContent(const QStringList& fileList); void setProfileContent(const QStringList& fileList); void clearCheckStates(); From 1ae2cc82a1866d5178be218397bccefc0cc25793 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 00:46:01 +0000 Subject: [PATCH 13/36] I do not know how this escaped formatting locally. --- components/contentselector/model/esmfile.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index 42b6cfeff6..4703df562c 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -62,16 +62,18 @@ namespace ContentSelectorModel QString toolTip() const { QString tooltip = mTooltipTemlate.arg(mAuthor) - .arg(mVersion) - .arg(mModified.toString(Qt::ISODate)) - .arg(mPath) - .arg(mDescription) - .arg(mGameFiles.join(", ")); + .arg(mVersion) + .arg(mModified.toString(Qt::ISODate)) + .arg(mPath) + .arg(mDescription) + .arg(mGameFiles.join(", ")); if (mBuiltIn) tooltip += tr("
This content file cannot be disabled because it is part of OpenMW.
"); else if (mFromAnotherConfigFile) - tooltip += tr("
This content file cannot be disabled because it is enabled in a config file other than the user one.
"); + tooltip += tr( + "
This content file cannot be disabled because it is enabled in a config file other than " + "the user one.
"); return tooltip; } From c23e5e105997209043acc8322c7cf055f40ebeb3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 00:47:31 +0000 Subject: [PATCH 14/36] I guess these aren't wired up as a dependency of the build I know the qm generation is so maybe it needs another look --- files/lang/components_de.ts | 8 ++++++++ files/lang/components_fr.ts | 8 ++++++++ files/lang/components_ru.ts | 8 ++++++++ files/lang/launcher_de.ts | 20 ++++++++++++++++++++ files/lang/launcher_fr.ts | 20 ++++++++++++++++++++ files/lang/launcher_ru.ts | 20 ++++++++++++++++++++ files/lang/wizard_de.ts | 28 ++++++++++++++-------------- files/lang/wizard_fr.ts | 28 ++++++++++++++-------------- files/lang/wizard_ru.ts | 28 ++++++++++++++-------------- 9 files changed, 126 insertions(+), 42 deletions(-) diff --git a/files/lang/components_de.ts b/files/lang/components_de.ts index 59ba558328..76b90229fc 100644 --- a/files/lang/components_de.ts +++ b/files/lang/components_de.ts @@ -29,6 +29,14 @@ <b>Author:</b> %1<br/><b>Format version:</b> %2<br/><b>Modified:</b> %3<br/><b>Path:</b><br/>%4<br/><br/><b>Description:</b><br/>%5<br/><br/><b>Dependencies: </b>%6<br/> + + <br/><b>This content file cannot be disabled because it is part of OpenMW.</b><br/> + + + + <br/><b>This content file cannot be disabled because it is enabled in a config file other than the user one.</b><br/> + + ContentSelectorView::ContentSelector diff --git a/files/lang/components_fr.ts b/files/lang/components_fr.ts index c1c70ba277..309424cda4 100644 --- a/files/lang/components_fr.ts +++ b/files/lang/components_fr.ts @@ -29,6 +29,14 @@ <b>Author:</b> %1<br/><b>Format version:</b> %2<br/><b>Modified:</b> %3<br/><b>Path:</b><br/>%4<br/><br/><b>Description:</b><br/>%5<br/><br/><b>Dependencies: </b>%6<br/> + + <br/><b>This content file cannot be disabled because it is part of OpenMW.</b><br/> + + + + <br/><b>This content file cannot be disabled because it is enabled in a config file other than the user one.</b><br/> + + ContentSelectorView::ContentSelector diff --git a/files/lang/components_ru.ts b/files/lang/components_ru.ts index cca6591afe..1618961914 100644 --- a/files/lang/components_ru.ts +++ b/files/lang/components_ru.ts @@ -29,6 +29,14 @@ <b>Author:</b> %1<br/><b>Format version:</b> %2<br/><b>Modified:</b> %3<br/><b>Path:</b><br/>%4<br/><br/><b>Description:</b><br/>%5<br/><br/><b>Dependencies: </b>%6<br/> <b>Автор:</b> %1<br/><b>Версия формата данных:</b> %2<br/><b>Дата изменения:</b> %3<br/><b>Путь к файлу:</b><br/>%4<br/><br/><b>Описание:</b><br/>%5<br/><br/><b>Зависимости: </b>%6<br/> + + <br/><b>This content file cannot be disabled because it is part of OpenMW.</b><br/> + + + + <br/><b>This content file cannot be disabled because it is enabled in a config file other than the user one.</b><br/> + + ContentSelectorView::ContentSelector diff --git a/files/lang/launcher_de.ts b/files/lang/launcher_de.ts index 925733f9d3..6145d21d28 100644 --- a/files/lang/launcher_de.ts +++ b/files/lang/launcher_de.ts @@ -370,6 +370,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov &Uncheck Selected + + Resolved as %1 + + + + This is the data-local directory and cannot be disabled + + + + This directory is part of OpenMW and cannot be disabled + + + + This directory is enabled in an openmw.cfg other than the user one + + + + This archive is enabled in an openmw.cfg other than the user one + + Launcher::GraphicsPage diff --git a/files/lang/launcher_fr.ts b/files/lang/launcher_fr.ts index 3471fc6c5c..3affe3c83f 100644 --- a/files/lang/launcher_fr.ts +++ b/files/lang/launcher_fr.ts @@ -370,6 +370,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov &Uncheck Selected + + Resolved as %1 + + + + This is the data-local directory and cannot be disabled + + + + This directory is part of OpenMW and cannot be disabled + + + + This directory is enabled in an openmw.cfg other than the user one + + + + This archive is enabled in an openmw.cfg other than the user one + + Launcher::GraphicsPage diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index ec7aeccc57..4a71267eb4 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -372,6 +372,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov &Uncheck Selected &Отключить выбранные + + Resolved as %1 + + + + This is the data-local directory and cannot be disabled + + + + This directory is part of OpenMW and cannot be disabled + + + + This directory is enabled in an openmw.cfg other than the user one + + + + This archive is enabled in an openmw.cfg other than the user one + + Launcher::GraphicsPage diff --git a/files/lang/wizard_de.ts b/files/lang/wizard_de.ts index 7bf54e90b1..4fecd1de72 100644 --- a/files/lang/wizard_de.ts +++ b/files/lang/wizard_de.ts @@ -590,59 +590,59 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - + OpenMW Wizard - - + + Error opening Wizard log file - - - + + + <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - + <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - + Error opening OpenMW configuration file - + Quit Wizard - + Are you sure you want to exit the Wizard? - + Error creating OpenMW configuration directory - + <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - + + Error writing OpenMW configuration file diff --git a/files/lang/wizard_fr.ts b/files/lang/wizard_fr.ts index 7f42087dbf..c2950c0a42 100644 --- a/files/lang/wizard_fr.ts +++ b/files/lang/wizard_fr.ts @@ -590,59 +590,59 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - + OpenMW Wizard - - + + Error opening Wizard log file - - - + + + <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - + <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - + Error opening OpenMW configuration file - + Quit Wizard - + Are you sure you want to exit the Wizard? - + Error creating OpenMW configuration directory - + <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - + + Error writing OpenMW configuration file diff --git a/files/lang/wizard_ru.ts b/files/lang/wizard_ru.ts index 3113774cd3..2461204681 100644 --- a/files/lang/wizard_ru.ts +++ b/files/lang/wizard_ru.ts @@ -592,59 +592,59 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - + OpenMW Wizard Мастер OpenMW - - + + Error opening Wizard log file Не удалось открыть лог-файл Мастера - - - + + + <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось открыть %1 для записи</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - + <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось открыть %1 для чтения</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - + Error opening OpenMW configuration file Не удалось открыть файл с настройками OpenMW - + Quit Wizard Завершить работу Мастера - + Are you sure you want to exit the Wizard? Вы уверены, что хотите завершить работу Мастера? - + Error creating OpenMW configuration directory Не удалось создать директорию для настроек OpenMW - + <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось создать %1</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - - + + Error writing OpenMW configuration file Не удалось записать данные в файл с настройками OpenMW From bf24bb71b1a5e4f0259e8c3cd94227f331f4affc Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 01:23:51 +0000 Subject: [PATCH 15/36] Explicitly use std::strong_ordering Otherwise it's ambiguous how to build <=> from <, == and > --- components/config/gamesettings.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 14a8fcb155..96e0864a9e 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -25,7 +25,7 @@ namespace Config // path of openmw.cfg, e.g. to resolve relative paths QString context = ""; - friend auto operator<=>(const SettingValue&, const SettingValue&) = default; + friend std::strong_ordering operator<=>(const SettingValue&, const SettingValue&) = default; }; class GameSettings From 7f1a6a81874a4b4272d5feefbc7316e23504f4ee Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 01:37:40 +0000 Subject: [PATCH 16/36] Fix file that's not used on Windows --- apps/wizard/installationpage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wizard/installationpage.cpp b/apps/wizard/installationpage.cpp index 60e9f3ccf9..aec64e275d 100644 --- a/apps/wizard/installationpage.cpp +++ b/apps/wizard/installationpage.cpp @@ -18,7 +18,7 @@ Wizard::InstallationPage::InstallationPage(QWidget* parent, Config::GameSettings mFinished = false; mThread = std::make_unique(); - mUnshield = std::make_unique(mGameSettings.value("morrowind-bsa-filesize").toLongLong()); + mUnshield = std::make_unique(mGameSettings.value("morrowind-bsa-filesize").value.toLongLong()); mUnshield->moveToThread(mThread.get()); connect(mThread.get(), &QThread::started, mUnshield.get(), &UnshieldWorker::extract); From ed23f487543181c70960bad5421740d131a4b729 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 23:44:24 +0000 Subject: [PATCH 17/36] Actually erase the things we're removing Caused by bad copy and paste --- components/config/gamesettings.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 96e0864a9e..02cf9f9b8b 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -76,8 +76,7 @@ namespace Config if (!existingDir.isEmpty()) { // non-user settings can't be removed as we can't edit the openmw.cfg they're in - std::remove_if(mDataDirs.begin(), mDataDirs.end(), - [&](const SettingValue& dir) { return isUserSetting(dir) && dir.value == existingDir; }); + mDataDirs.erase(std::remove_if(mDataDirs.begin(), mDataDirs.end(), [&](const SettingValue& dir) { return isUserSetting(dir) && dir.value == existingDir; }), mDataDirs.end()); } } From 243b5b6666f9ccdc024f90a7f4a933b8d1e87e8e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 6 Mar 2024 23:52:16 +0000 Subject: [PATCH 18/36] Hopefully convince the old MSVC version on GitLab CI to work The old code was legal, and the things it did worked in other places, so should have worked here, too. Hopefully just rearranging stuff convinces what I assume to be a compiler bug to not happen. --- components/config/gamesettings.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 9aed4656bc..976d5e20f2 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -544,19 +544,15 @@ bool Config::GameSettings::hasMaster() void Config::GameSettings::setContentList( const QList& dirNames, const QList& archiveNames, const QStringList& fileNames) { - auto const reset = [this](const char* key, const QStringList& list) { - remove(key); - for (auto const& item : list) - setMultiValue(key, { item }); - }; - remove(sDirectoryKey); for (auto const& item : dirNames) setMultiValue(sDirectoryKey, item); remove(sArchiveKey); for (auto const& item : archiveNames) setMultiValue(sArchiveKey, item); - reset(sContentKey, fileNames); + remove(sContentKey); + for (auto const& item : fileNames) + setMultiValue(sContentKey, { item }); } QList Config::GameSettings::getDataDirs() const From 36f5c819bbb228becb50c57e2ede1949d27ded5f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 7 Mar 2024 01:48:16 +0000 Subject: [PATCH 19/36] capitulate --- components/config/gamesettings.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 02cf9f9b8b..d23f225eb0 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -76,7 +76,10 @@ namespace Config if (!existingDir.isEmpty()) { // non-user settings can't be removed as we can't edit the openmw.cfg they're in - mDataDirs.erase(std::remove_if(mDataDirs.begin(), mDataDirs.end(), [&](const SettingValue& dir) { return isUserSetting(dir) && dir.value == existingDir; }), mDataDirs.end()); + mDataDirs.erase( + std::remove_if(mDataDirs.begin(), mDataDirs.end(), + [&](const SettingValue& dir) { return isUserSetting(dir) && dir.value == existingDir; }), + mDataDirs.end()); } } From e0b13f0858309e328d512d056de4beda37171952 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 8 Mar 2024 01:44:47 +0000 Subject: [PATCH 20/36] Ensure default config values are present Moving builtin.omwscripts out of the root openmw.cfg means we actually might need to use the defaults, so need to have some. --- components/files/configurationmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 10e10375bb..7b4cbac864 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -68,6 +68,9 @@ namespace Files bool silent = mSilent; mSilent = quiet; + // ensure defaults are present + bpo::store(bpo::parsed_options(&description), variables); + std::optional config = loadConfig(mFixedPath.getLocalPath(), description); if (config) mActiveConfigPaths.push_back(mFixedPath.getLocalPath()); From baab28440e4283d43a5c42c15fe6484f7e0d4ff3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 8 Mar 2024 01:49:23 +0000 Subject: [PATCH 21/36] Russian translations from Capo --- files/lang/components_ru.ts | 4 ++-- files/lang/launcher_ru.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/files/lang/components_ru.ts b/files/lang/components_ru.ts index 1618961914..b16168effb 100644 --- a/files/lang/components_ru.ts +++ b/files/lang/components_ru.ts @@ -31,11 +31,11 @@ <br/><b>This content file cannot be disabled because it is part of OpenMW.</b><br/> - + <br/><b>Этот контентный файл не может быть отключен, потому что он является частью OpenMW.</b><br/> <br/><b>This content file cannot be disabled because it is enabled in a config file other than the user one.</b><br/> - + <br/><b>Этот контентный файл не может быть отключен, потому что он включен в конфигурационном файле, не являющемся пользовательским.</b><br/> diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index 4a71267eb4..843222a423 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -374,23 +374,23 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Resolved as %1 - + Путь разрешен как %1 This is the data-local directory and cannot be disabled - + Это директория data-loсal, и она не может быть отключена This directory is part of OpenMW and cannot be disabled - + Это директория является частью OpenMW и не может быть отключена This directory is enabled in an openmw.cfg other than the user one - + Это директория включена в openmw.cfg, не являющемся пользовательским This archive is enabled in an openmw.cfg other than the user one - + Этот архив включен в openmw.cfg, не являющемся пользовательским From f3b01973ce1735a7d65fd188300458e33df73f98 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sat, 9 Mar 2024 17:12:47 +0000 Subject: [PATCH 22/36] c h a n g e l o g --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea44cab4b1..d282b603f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Bug #6754: Beast to Non-beast transformation mod is not working on OpenMW Bug #6758: Main menu background video can be stopped by opening the options menu Bug #6807: Ultimate Galleon is not working properly + Bug #6846: Launcher only works with default config paths Bug #6893: Lua: Inconsistent behavior with actors affected by Disable and SetDelete commands Bug #6894: Added item combines with equipped stack instead of creating a new unequipped stack Bug #6932: Creatures flee from my followers and we have to chase after them From a06ab94a209e1123ec8c37eedc51c021f1ee4756 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 15 Mar 2024 00:42:15 +0000 Subject: [PATCH 23/36] Canonicalise resolved representation of data directories --- components/config/gamesettings.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 976d5e20f2..21110562d5 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -35,14 +35,20 @@ void Config::GameSettings::validatePaths() for (const auto& dataDir : paths) { if (QDir(dataDir.value).exists()) - mDataDirs.append(dataDir); + { + SettingValue copy = dataDir; + copy.value = QDir(dataDir.value).canonicalPath(); + mDataDirs.append(copy); + } } // Do the same for data-local const QString& local = mSettings.value(QString("data-local")).value; if (!local.isEmpty() && QDir(local).exists()) - mDataLocal = local; + { + mDataLocal = QDir(local).canonicalPath(); + } } QString Config::GameSettings::getResourcesVfs() const From b15f7857c07e788e93a2b78e2da09e12462b0832 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 20 Mar 2024 14:34:23 +0000 Subject: [PATCH 24/36] currentDir.value is already canonicalised --- apps/launcher/datafilespage.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d87073df02..87667bda37 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -293,10 +293,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) std::unordered_set visitedDirectories; for (const Config::SettingValue& currentDir : directories) { - // normalize user supplied directories: resolve symlink, convert to native separator - const QString canonicalDirPath = QDir(QDir::cleanPath(currentDir.value)).canonicalPath(); - - if (!visitedDirectories.insert(canonicalDirPath).second) + if (!visitedDirectories.insert(currentDir.value).second) continue; // add new achives files presents in current directory @@ -305,7 +302,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) QStringList tooltip; // add content files presents in current directory - mSelector->addFiles(currentDir.value, mNewDataDirs.contains(canonicalDirPath)); + mSelector->addFiles(currentDir.value, mNewDataDirs.contains(currentDir.value)); // add current directory to list ui.directoryListWidget->addItem(currentDir.originalRepresentation); @@ -317,7 +314,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) tooltip << tr("Resolved as %1").arg(currentDir.value); // Display new content with custom formatting - if (mNewDataDirs.contains(canonicalDirPath)) + if (mNewDataDirs.contains(currentDir.value)) { tooltip << tr("Will be added to the current profile"); QFont font = item->font(); From 0371791cce91a6bbc7ec8a8da94fb319cffc6728 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 20 Mar 2024 23:12:19 +0000 Subject: [PATCH 25/36] Break --- apps/launcher/maindialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index aca8a64e31..178b254545 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -373,7 +373,10 @@ bool Launcher::MainDialog::setupGameData() << "*.omwaddon"; if (!dir.entryList(filters).isEmpty()) + { foundData = true; + break; + } } if (!foundData) From 0e2f28156da92b9a6b959c614f88606b32b40fa6 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 24 Mar 2024 23:48:37 +0000 Subject: [PATCH 26/36] Restore logging of openmw.cfg paths in launcher Removed here https://gitlab.com/OpenMW/openmw/-/merge_requests/2650/diffs#be09c16519a3f26f4306b920c50e0e4215dffaee_329_328 --- apps/launcher/maindialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 178b254545..5424d4010b 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -351,6 +351,7 @@ bool Launcher::MainDialog::setupGameSettings() for (const auto& path : Files::getActiveConfigPathsQString(mCfgMgr)) { + Log(Debug::Verbose) << "Loading config file: " << path.toUtf8().constData(); if (!loadFile(path, &Config::GameSettings::readFile)) return false; } From 59334f694dbcac004e58a271f92729371861dec3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 26 Mar 2024 23:11:54 +0000 Subject: [PATCH 27/36] Don't forget to add path to UserRole --- apps/launcher/datafilespage.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 87667bda37..8e0d729afa 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -712,6 +712,9 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) if (!ui.directoryListWidget->findItems(rootPath, Qt::MatchFixedString).isEmpty()) return; ui.directoryListWidget->addItem(rootPath); + auto row = ui.directoryListWidget->count() - 1; + auto* item = ui.directoryListWidget->item(row); + item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue(rootPath))); mNewDataDirs.push_back(rootPath); refreshDataFilesView(); return; @@ -741,8 +744,11 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) const auto* dir = select.dirListWidget->item(i); if (dir->checkState() == Qt::Checked) { - ui.directoryListWidget->insertItem(selectedRow++, dir->text()); + ui.directoryListWidget->insertItem(selectedRow, dir->text()); + auto* item = ui.directoryListWidget->item(selectedRow); + item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue(dir->text()))); mNewDataDirs.push_back(dir->text()); + ++selectedRow; } } From a98a824f80ce53d26a7c11a7d7e8b1a13b2283ab Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 27 Mar 2024 13:58:36 +0000 Subject: [PATCH 28/36] Config paths to info log, not verbose --- apps/launcher/maindialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 5424d4010b..5486251731 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -292,7 +292,7 @@ bool Launcher::MainDialog::setupLauncherSettings() if (!QFile::exists(path)) return true; - Log(Debug::Verbose) << "Loading config file: " << path.toUtf8().constData(); + Log(Debug::Info) << "Loading config file: " << path.toUtf8().constData(); QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) @@ -351,7 +351,7 @@ bool Launcher::MainDialog::setupGameSettings() for (const auto& path : Files::getActiveConfigPathsQString(mCfgMgr)) { - Log(Debug::Verbose) << "Loading config file: " << path.toUtf8().constData(); + Log(Debug::Info) << "Loading config file: " << path.toUtf8().constData(); if (!loadFile(path, &Config::GameSettings::readFile)) return false; } From e735bf67e198fcbfb74ae378b7a2e65d0e9270e9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 27 Mar 2024 14:04:08 +0000 Subject: [PATCH 29/36] Brace-initialise SettingValue Clang didn't like it otherwise --- apps/launcher/datafilespage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8e0d729afa..9c04be0e7e 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -714,7 +714,7 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) ui.directoryListWidget->addItem(rootPath); auto row = ui.directoryListWidget->count() - 1; auto* item = ui.directoryListWidget->item(row); - item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue(rootPath))); + item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue{ rootPath })); mNewDataDirs.push_back(rootPath); refreshDataFilesView(); return; @@ -746,7 +746,7 @@ void Launcher::DataFilesPage::addSubdirectories(bool append) { ui.directoryListWidget->insertItem(selectedRow, dir->text()); auto* item = ui.directoryListWidget->item(selectedRow); - item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue(dir->text()))); + item->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue{ dir->text() })); mNewDataDirs.push_back(dir->text()); ++selectedRow; } From 47ef2d018fdcc607fb627764df04490aec191aaf Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 27 Mar 2024 22:25:32 +0000 Subject: [PATCH 30/36] Always set userrole for archive list --- apps/launcher/datafilespage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 9c04be0e7e..f671089bff 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -867,9 +867,8 @@ bool Launcher::DataFilesPage::moveArchive(QListWidgetItem* listItem, int step) if (selectedRow == -1 || newRow < 0 || newRow > ui.archiveListWidget->count() - 1) return false; - const QListWidgetItem* item = ui.archiveListWidget->takeItem(selectedRow); - - addArchive(item->text(), item->checkState(), newRow); + QListWidgetItem* item = ui.archiveListWidget->takeItem(selectedRow); + ui.archiveListWidget->insertItem(newRow, item); ui.archiveListWidget->setCurrentRow(newRow); return true; } @@ -880,6 +879,7 @@ void Launcher::DataFilesPage::addArchive(const QString& name, Qt::CheckState sel row = ui.archiveListWidget->count(); ui.archiveListWidget->insertItem(row, name); ui.archiveListWidget->item(row)->setCheckState(selected); + ui.archiveListWidget->item(row)->setData(Qt::UserRole, QVariant::fromValue(Config::SettingValue{ name })); if (mKnownArchives.filter(name).isEmpty()) // XXX why contains doesn't work here ??? { auto item = ui.archiveListWidget->item(row); From bb3c22e4a584ee550f394e2e1c13aa505ec71eab Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 1 Apr 2024 00:15:58 +0100 Subject: [PATCH 31/36] Add and register SettingValue stream operators --- components/config/gamesettings.cpp | 21 +++++++++++++++++++++ components/config/gamesettings.hpp | 3 +++ 2 files changed, 24 insertions(+) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 21110562d5..a7da8fa150 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -24,6 +24,11 @@ namespace Config::GameSettings::GameSettings(const Files::ConfigurationManager& cfg) : mCfgMgr(cfg) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // this needs calling once so Qt can see its stream operators, which it needs when dragging and dropping + // it's automatic with Qt 6 + qRegisterMetaTypeStreamOperators("Config::SettingValue"); +#endif } void Config::GameSettings::validatePaths() @@ -591,3 +596,19 @@ void Config::GameSettings::clear() mDataDirs.clear(); mDataLocal.clear(); } + +QDataStream& Config::operator<<(QDataStream& out, const SettingValue& settingValue) +{ + out << settingValue.value; + out << settingValue.originalRepresentation; + out << settingValue.context; + return out; +} + +QDataStream& Config::operator>>(QDataStream& in, SettingValue& settingValue) +{ + in >> settingValue.value; + in >> settingValue.originalRepresentation; + in >> settingValue.context; + return in; +} diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index d23f225eb0..7627d5153a 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -132,6 +132,9 @@ namespace Config static bool isOrderedLine(const QString& line); }; + + QDataStream& operator<<(QDataStream& out, const SettingValue& settingValue); + QDataStream& operator>>(QDataStream& in, SettingValue& settingValue); } Q_DECLARE_METATYPE(Config::SettingValue) From f8224b29d4f050c699fb3e7663e9e0ef15673e4d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 7 Apr 2024 21:59:54 +0100 Subject: [PATCH 32/36] Resolve merge conflicts These were caused by https://gitlab.com/OpenMW/openmw/-/merge_requests/4005 I've had to cherry-pick parts of that MR into this commit, otherwise the CI would yell at me when it noticed the location tags had gone. --- CI/check_qt_translations.sh | 6 +- CMakeLists.txt | 6 +- files/lang/components_ru.ts | 2 +- files/lang/launcher_ru.ts | 6 +- files/lang/wizard_de.ts | 186 +---------------------------------- files/lang/wizard_fr.ts | 190 +----------------------------------- files/lang/wizard_ru.ts | 186 +---------------------------------- 7 files changed, 18 insertions(+), 564 deletions(-) diff --git a/CI/check_qt_translations.sh b/CI/check_qt_translations.sh index f3a82ed2e6..1fc2e19002 100755 --- a/CI/check_qt_translations.sh +++ b/CI/check_qt_translations.sh @@ -4,8 +4,8 @@ set -o pipefail LUPDATE="${LUPDATE:-lupdate}" -${LUPDATE:?} apps/wizard -ts files/lang/wizard_*.ts -${LUPDATE:?} apps/launcher -ts files/lang/launcher_*.ts -${LUPDATE:?} components/contentselector components/process -ts files/lang/components_*.ts +${LUPDATE:?} -locations none apps/wizard -ts files/lang/wizard_*.ts +${LUPDATE:?} -locations none apps/launcher -ts files/lang/launcher_*.ts +${LUPDATE:?} -locations none components/contentselector components/process -ts files/lang/components_*.ts ! (git diff --name-only | grep -q "^") || (echo -e "\033[0;31mBuild a 'translations' CMake target to update Qt localization for these files:\033[0;0m"; git diff --name-only | xargs -i echo -e "\033[0;31m{}\033[0;0m"; exit -1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 76aede04c9..8a0a59bf4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1089,17 +1089,17 @@ if (USE_QT) file(GLOB COMPONENTS_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/components_*.ts) get_target_property(QT_LUPDATE_EXECUTABLE Qt::lupdate IMPORTED_LOCATION) add_custom_target(translations - COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES} + COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/components VERBATIM COMMAND_EXPAND_LISTS - COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES} + COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/wizard VERBATIM COMMAND_EXPAND_LISTS - COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES} + COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/launcher VERBATIM COMMAND_EXPAND_LISTS) diff --git a/files/lang/components_ru.ts b/files/lang/components_ru.ts index b16168effb..d70ebe320a 100644 --- a/files/lang/components_ru.ts +++ b/files/lang/components_ru.ts @@ -73,7 +73,7 @@ Arguments: Параметры: - + <html><head/><body><p><b>Could not find %1</b></p><p>The application is not found.</p><p>Please make sure OpenMW is installed correctly and try again.</p></body></html> diff --git a/files/lang/launcher_ru.ts b/files/lang/launcher_ru.ts index 843222a423..5c044d38d4 100644 --- a/files/lang/launcher_ru.ts +++ b/files/lang/launcher_ru.ts @@ -109,7 +109,7 @@ &New Content List - Новый список плагинов + &Новый список плагинов Clone Content List @@ -547,7 +547,7 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov <br><b>Could not create directory %0</b><br><br>%1<br> - <br><b>Не удалось создать директорию %0</b><br><br> + <br><b>Не удалось создать директорию %0</b><br><br>%1<br> @@ -1066,7 +1066,7 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Size of characters in game texts. - Размер символов в текстах + Размер символов в текстах. Font size diff --git a/files/lang/wizard_de.ts b/files/lang/wizard_de.ts index 4fecd1de72..d793175dcb 100644 --- a/files/lang/wizard_de.ts +++ b/files/lang/wizard_de.ts @@ -4,27 +4,22 @@ ComponentSelectionPage - WizardPage - Select Components - Which components should be installed? - <html><head/><body><p>Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to install expansions later by re-running this Wizard.<br/></p></body></html> - Selected components: @@ -32,17 +27,14 @@ ConclusionPage - WizardPage - Completing the OpenMW Wizard - Placeholder @@ -50,27 +42,22 @@ ExistingInstallationPage - WizardPage - Select Existing Installation - Select an existing installation for OpenMW to use or modify. - Detected installations: - Browse... @@ -78,42 +65,34 @@ ImportPage - WizardPage - Import Settings - Import settings from the Morrowind installation. - <html><head/><body><p>OpenMW needs to import settings from the Morrowind configuration file in order to function properly.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to import settings later by re-running this Wizard.</p><p/></body></html> - Import settings from Morrowind.ini - Import add-on and plugin selection - Import bitmap fonts setup from Morrowind.ini - Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts. @@ -123,17 +102,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationPage - WizardPage - Installing - Please wait while Morrowind is installed on your computer. @@ -141,32 +117,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationTargetPage - WizardPage - Select Installation Destination - Where should Morrowind be installed? - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Morrowind will be installed to the following location. - Browse... @@ -174,17 +144,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov IntroPage - WizardPage - Welcome to the OpenMW Wizard - This Wizard will help you install Morrowind and its add-ons for OpenMW to use. @@ -192,27 +159,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov LanguageSelectionPage - WizardPage - Select Morrowind Language - What is the language of the Morrowind installation? - <html><head/><body><p><img src=":/icons/tango/48x48/preferences-desktop-locale.png"/></p></body></html> - Select the language of the Morrowind installation. @@ -220,62 +182,50 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov MethodSelectionPage - WizardPage - Select Installation Method - <html><head/><body><p>Select how you would like to install <i>The Elder Scrolls III: Morrowind</i>.</p></body></html> - Retail CD/DVD - <html><head/><body><p><img src=":/icons/tango/48x48/system-installer.png"/></p></body></html> - Install from a retail disc to a new location. - Existing Installation - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Select an existing installation. - Don't have a copy? - <html><head/><body><p><img src=":/icons/tango/48x48/dollar.png"/></p></body></html> - Buy the game @@ -283,42 +233,34 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov QObject - <br><b>Could not find Morrowind.ini</b><br><br>The Wizard needs to update settings in this file.<br><br>Press "Browse..." to specify the location manually.<br> - B&rowse... - Select configuration file - <b>Morrowind.bsa</b> is missing!<br>Make sure your Morrowind installation is complete. - <br><b>There may be a more recent version of Morrowind available.</b><br><br>Do you wish to continue anyway?<br> - Most recent Morrowind not detected - Select a valid %1 installation media.<br><b>Hint</b>: make sure that it contains at least one <b>.cab</b> file. - There may be a more recent version of Morrowind available.<br><br>Do you wish to continue anyway? @@ -326,57 +268,46 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ComponentSelectionPage - &Install - &Skip - Morrowind (installed) - Morrowind - Tribunal (installed) - Tribunal - Bloodmoon (installed) - Bloodmoon - About to install Tribunal after Bloodmoon - <html><head/><body><p><b>You are about to install Tribunal</b></p><p>Bloodmoon is already installed on your computer.</p><p>However, it is recommended that you install Tribunal before Bloodmoon.</p><p>Would you like to re-install Bloodmoon?</p></body></html> - Re-install &Bloodmoon @@ -384,17 +315,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ConclusionPage - <html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p></body></html> - <html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind installation.</body></html> - <html><head/><body><p>The OpenMW Wizard failed to install Morrowind on your computer.</p><p>Please report any bugs you might have encountered to our <a href="https://gitlab.com/OpenMW/openmw/issues">bug tracker</a>.<br/>Make sure to include the installation log.</p><br/></body></html> @@ -402,32 +330,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ExistingInstallationPage - No existing installations detected - Error detecting Morrowind configuration - Morrowind configuration file (*.ini) - Select Morrowind.esm (located in Data Files) - Morrowind master file (Morrowind.esm) - Error detecting Morrowind files @@ -435,78 +357,62 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationPage - <p>Attempting to install component %1.</p> - Attempting to install component %1. - %1 Installation - Select %1 installation media - - <p><br/><span style="color:red;"><b>Error: The installation was aborted by the user</b></span></p> - <p>Detected old version of component Morrowind.</p> - Detected old version of component Morrowind. - Morrowind Installation - Installation finished - Installation completed successfully! - Installation failed! - <p><br/><span style="color:red;"><b>Error: %1</b></p> - <p><span style="color:red;"><b>%1</b></p> - <html><head/><body><p><b>The Wizard has encountered an error</b></p><p>The error reported was:</p><p>%1</p><p>Press &quot;Show Details...&quot; for more information.</p></body></html> - An error occurred @@ -514,37 +420,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationTargetPage - Error creating destination - <html><head/><body><p><b>Could not create the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> - <html><head/><body><p><b>Could not write to the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> - <html><head/><body><p><b>The destination directory is not empty</b></p><p>An existing Morrowind installation is present in the specified location.</p><p>Please specify a different location, or go back and select the location as an existing installation.</p></body></html> - Insufficient permissions - Destination not empty - Select where to install Morrowind @@ -552,37 +451,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::LanguageSelectionPage - English - French - German - Italian - Polish - Russian - Spanish @@ -590,267 +482,193 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - OpenMW Wizard - - Error opening Wizard log file - - - <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - - Error opening OpenMW configuration file - Quit Wizard - Are you sure you want to exit the Wizard? - Error creating OpenMW configuration directory - - <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> + Error writing OpenMW configuration file - - - Error writing OpenMW configuration file + <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> Wizard::UnshieldWorker - - Failed to open Morrowind configuration file! - - Opening %1 failed: %2. - Failed to write Morrowind configuration file! - Writing to %1 failed: %2. - Installing: %1 - - Installing: %1 directory - Installation finished! - - Component parameter is invalid! - - An invalid component parameter was supplied. - Failed to find a valid archive containing %1.bsa! Retrying. - Installing %1 - Installation media path not set! - The source path for %1 was not set. - - Cannot create temporary directory! - - Failed to create %1. - Cannot move into temporary directory! - Failed to move into %1. - Moving installation files - - - - Could not install directory! - - - - Installing %1 to %2 failed. - Could not install translation file! - Failed to install *%1 files. - Could not install Morrowind data file! - - Failed to install %1. - Could not install Morrowind configuration file! - Installing: Sound directory - Could not find Tribunal data file! - - - Failed to find %1. - Could not find Tribunal patch file! - Could not find Bloodmoon data file! - Updating Morrowind configuration file - %1 installation finished! - Extracting: %1 - - - Failed to open InstallShield Cabinet File. - - - Opening %1 failed. - Failed to extract %1. - Complete path: %1 diff --git a/files/lang/wizard_fr.ts b/files/lang/wizard_fr.ts index c2950c0a42..ba3c8aaa19 100644 --- a/files/lang/wizard_fr.ts +++ b/files/lang/wizard_fr.ts @@ -4,27 +4,22 @@ ComponentSelectionPage - WizardPage - Select Components - Which components should be installed? - <html><head/><body><p>Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to install expansions later by re-running this Wizard.<br/></p></body></html> - Selected components: @@ -32,17 +27,14 @@ ConclusionPage - WizardPage - Completing the OpenMW Wizard - Placeholder @@ -50,27 +42,22 @@ ExistingInstallationPage - WizardPage - Select Existing Installation - Select an existing installation for OpenMW to use or modify. - Detected installations: - Browse... @@ -78,42 +65,34 @@ ImportPage - WizardPage - Import Settings - Import settings from the Morrowind installation. - <html><head/><body><p>OpenMW needs to import settings from the Morrowind configuration file in order to function properly.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to import settings later by re-running this Wizard.</p><p/></body></html> - Import settings from Morrowind.ini - Import add-on and plugin selection - Import bitmap fonts setup from Morrowind.ini - Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts. @@ -123,17 +102,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationPage - WizardPage - Installing - Please wait while Morrowind is installed on your computer. @@ -141,32 +117,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationTargetPage - WizardPage - Select Installation Destination - Where should Morrowind be installed? - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Morrowind will be installed to the following location. - Browse... @@ -174,17 +144,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov IntroPage - WizardPage - Welcome to the OpenMW Wizard - This Wizard will help you install Morrowind and its add-ons for OpenMW to use. @@ -192,27 +159,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov LanguageSelectionPage - WizardPage - Select Morrowind Language - What is the language of the Morrowind installation? - <html><head/><body><p><img src=":/icons/tango/48x48/preferences-desktop-locale.png"/></p></body></html> - Select the language of the Morrowind installation. @@ -220,62 +182,50 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov MethodSelectionPage - WizardPage - Select Installation Method - <html><head/><body><p>Select how you would like to install <i>The Elder Scrolls III: Morrowind</i>.</p></body></html> - Retail CD/DVD - <html><head/><body><p><img src=":/icons/tango/48x48/system-installer.png"/></p></body></html> - Install from a retail disc to a new location. - Existing Installation - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Select an existing installation. - Don't have a copy? - <html><head/><body><p><img src=":/icons/tango/48x48/dollar.png"/></p></body></html> - Buy the game @@ -283,42 +233,34 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov QObject - <br><b>Could not find Morrowind.ini</b><br><br>The Wizard needs to update settings in this file.<br><br>Press "Browse..." to specify the location manually.<br> - B&rowse... - Select configuration file - <b>Morrowind.bsa</b> is missing!<br>Make sure your Morrowind installation is complete. - <br><b>There may be a more recent version of Morrowind available.</b><br><br>Do you wish to continue anyway?<br> - Most recent Morrowind not detected - Select a valid %1 installation media.<br><b>Hint</b>: make sure that it contains at least one <b>.cab</b> file. - There may be a more recent version of Morrowind available.<br><br>Do you wish to continue anyway? @@ -326,57 +268,46 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ComponentSelectionPage - &Install - &Skip - Morrowind (installed) - Morrowind - Tribunal (installed) - Tribunal - Bloodmoon (installed) - Bloodmoon - About to install Tribunal after Bloodmoon - <html><head/><body><p><b>You are about to install Tribunal</b></p><p>Bloodmoon is already installed on your computer.</p><p>However, it is recommended that you install Tribunal before Bloodmoon.</p><p>Would you like to re-install Bloodmoon?</p></body></html> - Re-install &Bloodmoon @@ -384,17 +315,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ConclusionPage - <html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p></body></html> - <html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind installation.</body></html> - <html><head/><body><p>The OpenMW Wizard failed to install Morrowind on your computer.</p><p>Please report any bugs you might have encountered to our <a href="https://gitlab.com/OpenMW/openmw/issues">bug tracker</a>.<br/>Make sure to include the installation log.</p><br/></body></html> @@ -402,32 +330,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ExistingInstallationPage - No existing installations detected - Error detecting Morrowind configuration - Morrowind configuration file (*.ini) - Select Morrowind.esm (located in Data Files) - Morrowind master file (Morrowind.esm) - Error detecting Morrowind files @@ -435,78 +357,62 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationPage - <p>Attempting to install component %1.</p> - Attempting to install component %1. - %1 Installation - Select %1 installation media - - <p><br/><span style="color:red;"><b>Error: The installation was aborted by the user</b></span></p> - <p>Detected old version of component Morrowind.</p> - Detected old version of component Morrowind. - Morrowind Installation - Installation finished - Installation completed successfully! - Installation failed! - <p><br/><span style="color:red;"><b>Error: %1</b></p> - <p><span style="color:red;"><b>%1</b></p> - <html><head/><body><p><b>The Wizard has encountered an error</b></p><p>The error reported was:</p><p>%1</p><p>Press &quot;Show Details...&quot; for more information.</p></body></html> - An error occurred @@ -514,37 +420,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationTargetPage - Error creating destination - <html><head/><body><p><b>Could not create the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> - <html><head/><body><p><b>Could not write to the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> - <html><head/><body><p><b>The destination directory is not empty</b></p><p>An existing Morrowind installation is present in the specified location.</p><p>Please specify a different location, or go back and select the location as an existing installation.</p></body></html> - Insufficient permissions - Destination not empty - Select where to install Morrowind @@ -552,37 +451,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::LanguageSelectionPage - English - French - German - Italian - Polish - Russian - Spanish @@ -590,267 +482,193 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - OpenMW Wizard - - Error opening Wizard log file - - - <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - - Error opening OpenMW configuration file - Quit Wizard - + Error writing OpenMW configuration file + + + Are you sure you want to exit the Wizard? - Error creating OpenMW configuration directory - <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - - - - Error writing OpenMW configuration file - - Wizard::UnshieldWorker - - Failed to open Morrowind configuration file! - - Opening %1 failed: %2. - Failed to write Morrowind configuration file! - Writing to %1 failed: %2. - Installing: %1 - - Installing: %1 directory - Installation finished! - - Component parameter is invalid! - - An invalid component parameter was supplied. - Failed to find a valid archive containing %1.bsa! Retrying. - Installing %1 - Installation media path not set! - The source path for %1 was not set. - - Cannot create temporary directory! - - Failed to create %1. - Cannot move into temporary directory! - Failed to move into %1. - Moving installation files - - - - Could not install directory! - - - - Installing %1 to %2 failed. - Could not install translation file! - Failed to install *%1 files. - Could not install Morrowind data file! - - Failed to install %1. - Could not install Morrowind configuration file! - Installing: Sound directory - Could not find Tribunal data file! - - - Failed to find %1. - Could not find Tribunal patch file! - Could not find Bloodmoon data file! - Updating Morrowind configuration file - %1 installation finished! - Extracting: %1 - - - Failed to open InstallShield Cabinet File. - - - Opening %1 failed. - Failed to extract %1. - Complete path: %1 diff --git a/files/lang/wizard_ru.ts b/files/lang/wizard_ru.ts index 2461204681..0a2e6e5561 100644 --- a/files/lang/wizard_ru.ts +++ b/files/lang/wizard_ru.ts @@ -4,27 +4,22 @@ ComponentSelectionPage - WizardPage WizardPage - Select Components Выбор компонентов - Which components should be installed? Какие компоненты должны быть установлены? - <html><head/><body><p>Select which official Morrowind expansions should be installed. For best results, it is recommended to have both expansions installed.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to install expansions later by re-running this Wizard.<br/></p></body></html> <html><head/><body><p>Выберите, какие дополнения для Morrowind нужно установить. Для достижения наилучших результатов рекомендуется установить оба дополнения.</p><p><span style=" font-weight:bold;">Подсказка:</span> Можно установить дополнения позже, запустив этот Мастер установки заново.<br/></p></body></html> - Selected components: Выбранные компоненты: @@ -32,17 +27,14 @@ ConclusionPage - WizardPage WizardPage - Completing the OpenMW Wizard Завершение работы Мастера установки OpenMW - Placeholder Placeholder @@ -50,27 +42,22 @@ ExistingInstallationPage - WizardPage WizardPage - Select Existing Installation Выбрать установленную копию игры - Select an existing installation for OpenMW to use or modify. Выбрать установленную копию игры для использования или изменения через OpenMW. - Detected installations: Обнаруженные установленные копии: - Browse... Выбрать... @@ -78,42 +65,34 @@ ImportPage - WizardPage WizardPage - Import Settings Импортировать настройки - Import settings from the Morrowind installation. Импортировать настройки из установленной копии Morrowind. - <html><head/><body><p>OpenMW needs to import settings from the Morrowind configuration file in order to function properly.</p><p><span style=" font-weight:bold;">Note:</span> It is possible to import settings later by re-running this Wizard.</p><p/></body></html> <html><head/><body><p>Чтобы OpenMW мог работать правильно, ему нужно импортировать настройки из файла с настройками Morrowind.</p><p><span style=" font-weight:bold;">Подсказка:</span> Также можно импортировать настройки позже, запустив Мастер импорта заново.</p><p/></body></html> - Import settings from Morrowind.ini Импортировать настройки из Morrowind.ini - Import add-on and plugin selection Импортировать список подключенных плагинов - Import bitmap fonts setup from Morrowind.ini Импортировать растровые шрифты из Morrowind.ini - Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, so OpenMW provides another set of fonts to avoid these issues. These fonts use TrueType technology and are quite similar to default Morrowind fonts. Check this box if you still prefer original fonts over OpenMW ones or if you use custom bitmap fonts. @@ -125,17 +104,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationPage - WizardPage WizardPage - Installing Установка - Please wait while Morrowind is installed on your computer. Пожалуйста, подождите, пока Morrowind устанавливается на ваш компьютер. @@ -143,32 +119,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov InstallationTargetPage - WizardPage WizardPage - Select Installation Destination Выберите путь для установки - Where should Morrowind be installed? Куда нужно установить Morrowind? - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Morrowind will be installed to the following location. - Morrowind будет установлен в следующее место. + Morrowind будет установлен в следующее место. - Browse... Выбрать... @@ -176,17 +146,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov IntroPage - WizardPage WizardPage - Welcome to the OpenMW Wizard Добро пожаловать в Мастер установки - This Wizard will help you install Morrowind and its add-ons for OpenMW to use. Этот Мастер поможет вам установить Morrowind и его дополнения, чтобы OpenMW мог их использовать. @@ -194,27 +161,22 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov LanguageSelectionPage - WizardPage WizardPage - Select Morrowind Language Выберите язык вашей копии Morrowind - What is the language of the Morrowind installation? Какой язык использует ваша копия Morrowind? - <html><head/><body><p><img src=":/icons/tango/48x48/preferences-desktop-locale.png"/></p></body></html> ><html><head/><body><p><img src=":/icons/tango/48x48/preferences-desktop-locale.png"/></p></body></html> - Select the language of the Morrowind installation. Выберите язык, используемый вашей копией Morrowind. @@ -222,62 +184,50 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov MethodSelectionPage - WizardPage WizardPage - Select Installation Method Выберите способ установки - <html><head/><body><p>Select how you would like to install <i>The Elder Scrolls III: Morrowind</i>.</p></body></html> <html><head/><body><p>Выберите способ установки <i>The Elder Scrolls III: Morrowind</i>.</p></body></html> - Retail CD/DVD CD/DVD-диск - <html><head/><body><p><img src=":/icons/tango/48x48/system-installer.png"/></p></body></html> <html><head/><body><p><img src=":/icons/tango/48x48/system-installer.png"/></p></body></html> - Install from a retail disc to a new location. - Установить игру с диска + Установить игру с диска. - Existing Installation Установленная копия игры - <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> <html><head/><body><p><img src=":/icons/tango/48x48/folder.png"/></p></body></html> - Select an existing installation. Выбрать установленную копию игры. - Don't have a copy? Нет копии игры? - <html><head/><body><p><img src=":/icons/tango/48x48/dollar.png"/></p></body></html> <html><head/><body><p><img src=":/icons/tango/48x48/dollar.png"/></p></body></html> - Buy the game Купить игру @@ -285,42 +235,34 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov QObject - <br><b>Could not find Morrowind.ini</b><br><br>The Wizard needs to update settings in this file.<br><br>Press "Browse..." to specify the location manually.<br> <br><b>Не удалось найти Morrowind.ini</b><br><br>Мастеру требуется обновить настройки в этом файле.<br><br>Нажмите "Выбрать...", чтобы задать местоположение файла вручную.<br> - B&rowse... &Выбрать... - Select configuration file Выберите файл с настройками - <b>Morrowind.bsa</b> is missing!<br>Make sure your Morrowind installation is complete. <b>Morrowind.bsa</b> не найден!<br>Убедитесь, что Morrowind был установлен правильно. - <br><b>There may be a more recent version of Morrowind available.</b><br><br>Do you wish to continue anyway?<br> <br><b>Может существовать более свежая версия Morrowind.</b><br><br>Все равно продолжить?<br> - Most recent Morrowind not detected Актуальная версия Morrowind не найдена - Select a valid %1 installation media.<br><b>Hint</b>: make sure that it contains at least one <b>.cab</b> file. Выберите корректный установочный дистрибутив %1.<br><b>Подсказка</b>: он должен содержать как минимум один <b>.cab</b>-файл. - There may be a more recent version of Morrowind available.<br><br>Do you wish to continue anyway? Может существовать более свежая версия Morrowind.<br><br>Все равно продолжить? @@ -328,57 +270,46 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ComponentSelectionPage - &Install &Установить - &Skip &Пропустить - Morrowind (installed) Morrowind (установлен) - Morrowind Morrowind - Tribunal (installed) Tribunal (установлен) - Tribunal Tribunal - Bloodmoon (installed) Bloodmoon (установлен) - Bloodmoon Bloodmoon - About to install Tribunal after Bloodmoon Попытка установить Tribunal после Bloodmoon - <html><head/><body><p><b>You are about to install Tribunal</b></p><p>Bloodmoon is already installed on your computer.</p><p>However, it is recommended that you install Tribunal before Bloodmoon.</p><p>Would you like to re-install Bloodmoon?</p></body></html> <html><head/><body><p><b>Вы собираетесь установить Tribunal</b></p><p>Bloodmoon уже установлен на ваш компьютер.</p><p>Tribunal рекомендуется устанавлить перед установкой Bloodmoon.</p><p>Желаете ли вы переустановить Bloodmoon?</p></body></html> - Re-install &Bloodmoon Переустановить &Bloodmoon @@ -386,17 +317,14 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ConclusionPage - <html><head/><body><p>The OpenMW Wizard successfully installed Morrowind on your computer.</p></body></html> <html><head/><body><p>Мастер OpenMW успешно установил Morrowind на ваш компьютер.</p></body></html> - <html><head/><body><p>The OpenMW Wizard successfully modified your existing Morrowind installation.</body></html> <html><head/><body><p>Мастер OpenMW успешно завершил изменение вашей установленной копии Morrowind.</body></html> - <html><head/><body><p>The OpenMW Wizard failed to install Morrowind on your computer.</p><p>Please report any bugs you might have encountered to our <a href="https://gitlab.com/OpenMW/openmw/issues">bug tracker</a>.<br/>Make sure to include the installation log.</p><br/></body></html> <html><head/><body><p>Мастеру OpenMW не удалось установить Morrowind на ваш компьютер.</p><p>Пожалуйста, сообщите о встреченных вами ошибках на наш <a href="https://gitlab.com/OpenMW/openmw/issues">багтрекер</a>.<br/>Не забудьте включить туда лог установки.</p><br/></body></html> @@ -404,32 +332,26 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::ExistingInstallationPage - No existing installations detected Установленные копии игры не найдены - Error detecting Morrowind configuration Попытка найти настройки Morrowind завершилась ошибкой - Morrowind configuration file (*.ini) Файл настроек Morrowind (*.ini) - Select Morrowind.esm (located in Data Files) Выберите Morrowind.esm (расположен в Data Files) - Morrowind master file (Morrowind.esm) Мастер-файл Morrowind (Morrowind.esm) - Error detecting Morrowind files Не удалось обнаружить файлы Morrowind @@ -437,78 +359,62 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationPage - <p>Attempting to install component %1.</p> <p>Попытка установить компонент %1.</p> - Attempting to install component %1. Попытка установить компонент %1. - %1 Installation Установка %1 - Select %1 installation media Выберите установочный дистрибутив %1 - - <p><br/><span style="color:red;"><b>Error: The installation was aborted by the user</b></span></p> <p><br/><span style="color:red;"><b>Ошибка: Установка была прервана пользователем</b></span></p> - <p>Detected old version of component Morrowind.</p> lt;p>Обнаружена устаревшая версия компонента Morrowind.</p> - Detected old version of component Morrowind. Обнаружена устаревшая версия компонента Morrowind. - Morrowind Installation Установка Morrowind - Installation finished Установка завершена - Installation completed successfully! Установка успешно завершена! - Installation failed! Установка не удалась! - <p><br/><span style="color:red;"><b>Error: %1</b></p> <p><br/><span style="color:red;"><b>Ошибка: %1</b></p> - <p><span style="color:red;"><b>%1</b></p> <p><span style="color:red;"><b>%1</b></p> - <html><head/><body><p><b>The Wizard has encountered an error</b></p><p>The error reported was:</p><p>%1</p><p>Press &quot;Show Details...&quot; for more information.</p></body></html> <html><head/><body><p><b>При работе Мастера возникла ошибка</b></p><p>Обнаруженная ошибка:</p><p>%1</p><p>Нажмите &quot;Показать детали...&quot; для получения дополнительной информации.</p></body></html> - An error occurred Произошла ошибка @@ -516,37 +422,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::InstallationTargetPage - Error creating destination Не удалось создать директорию назначения - <html><head/><body><p><b>Could not create the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> <html><head/><body><p><b>Не удалось создать директорию назначения</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку, или же выберите другую директорию.</p></body> - <html><head/><body><p><b>Could not write to the destination directory</b></p><p>Please make sure you have the right permissions and try again, or specify a different location.</p></body></html> <html><head/><body><p><b>Не удалось записать данные в директорию назначения</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку, или же выберите другую директорию.</p></body></html> - <html><head/><body><p><b>The destination directory is not empty</b></p><p>An existing Morrowind installation is present in the specified location.</p><p>Please specify a different location, or go back and select the location as an existing installation.</p></body></html> <html><head/><body><p><b>Директория назначения содержит файлы</b></p><p>В указанной директории найдена установленная копия Morrowind.</p><p>Пожалуйста, выберите другую директорию, или же вернитесь на предыдущий шаг и выберите подключение установленной копии игры.</p></body></html> - Insufficient permissions Не хватает прав доступа - Destination not empty Выбранная директория не пустая - Select where to install Morrowind Выберите, куда установить Morrowind @@ -554,37 +453,30 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::LanguageSelectionPage - English Английский - French Французский - German Немецкий - Italian Итальянский - Polish Польский - Russian Русский - Spanish Испанский @@ -592,59 +484,42 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::MainWizard - OpenMW Wizard Мастер OpenMW - - Error opening Wizard log file Не удалось открыть лог-файл Мастера - - - <html><head/><body><p><b>Could not open %1 for writing</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось открыть %1 для записи</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - - <html><head/><body><p><b>Could not open %1 for reading</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось открыть %1 для чтения</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - - - Error opening OpenMW configuration file Не удалось открыть файл с настройками OpenMW - Quit Wizard Завершить работу Мастера - Are you sure you want to exit the Wizard? Вы уверены, что хотите завершить работу Мастера? - Error creating OpenMW configuration directory Не удалось создать директорию для настроек OpenMW - <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> <html><head/><body><p><b>Не удалось создать %1</b></p><p>Пожалуйста, проверьте права доступа и повторите попытку.</p></body></html> - - Error writing OpenMW configuration file Не удалось записать данные в файл с настройками OpenMW @@ -652,207 +527,150 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Wizard::UnshieldWorker - - Failed to open Morrowind configuration file! Не удалось открыть файл с настройками Morrowind! - - Opening %1 failed: %2. Попытка открыть %1 не удалась: %2. - Failed to write Morrowind configuration file! Не удалось записать данные в файл с настройками Morrowind! - Writing to %1 failed: %2. Запись в %1 завершилась с ошибкой: %2. - Installing: %1 Установка: %1 - - Installing: %1 directory Установка: директория %1 - Installation finished! Установка завершена! - - Component parameter is invalid! Некорректный параметр для компонента! - - An invalid component parameter was supplied. Задан некорректный параметр для компонента. - Failed to find a valid archive containing %1.bsa! Retrying. Не удалось найти архив, содержащий %1.bsa! Повторная попытка. - Installing %1 Установка %1 - Installation media path not set! путь к установочному дистрибутиву не задан! - The source path for %1 was not set. Исходный пусть для %1 не задан. - - Cannot create temporary directory! Не удалось создать временную директорию! - - Failed to create %1. Не удалось создать %1. - Cannot move into temporary directory! Не удалось переместить во временную директорию! - Failed to move into %1. Не удалось переместить в %1. - Moving installation files Перемещение файлов установки - - - - Could not install directory! Не удалось установить директорию! - - - - Installing %1 to %2 failed. Не удалось установить %1 в %2. - Could not install translation file! Не удалось установить файл с переводом! - Failed to install *%1 files. Не удалось установить файлы *%1. - Could not install Morrowind data file! Не удалось установить файл с данными Morrowind! - - Failed to install %1. Не удалось установить %1. - Could not install Morrowind configuration file! Не удалось установить файл с настройками Morrowind! - Installing: Sound directory Установка: директория Sound - Could not find Tribunal data file! Не удалось найти файл с данными Tribunal! - - - Failed to find %1. Не удалось найти %1. - Could not find Tribunal patch file! Не удалось найти файл с патчем для Tribunal! - Could not find Bloodmoon data file! Не удалось найти файл с данными Bloodmoon! - Updating Morrowind configuration file Обновление файла с настройками Morrowind - %1 installation finished! Установка %1 завершена! - Extracting: %1 Извлечение: %1 - - - Failed to open InstallShield Cabinet File. Не удалось открыть файл InstallShield Cabinet. - - - Opening %1 failed. Не удалось открыть %1. - Failed to extract %1. Не удалось извлечь %1. - Complete path: %1 Полный путь: %1 From 48f1f085378e84d802d928de53a405dff0cc0bcd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 7 Apr 2024 22:12:39 +0100 Subject: [PATCH 33/36] Hide things that depend on present-but-inactive game files https://gitlab.com/OpenMW/openmw/-/merge_requests/3925#note_1843962919 --- components/contentselector/model/contentmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 66fde2063f..8e7b77d308 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -110,7 +110,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index return Qt::NoItemFlags; if (file->builtIn() || file->fromAnotherConfigFile()) - return Qt::NoItemFlags; + return Qt::ItemIsEnabled; // game files can always be checked if (file == mGameFile) @@ -228,7 +228,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int { if (file == mGameFile) return ContentType_GameFile; - else + else if (flags(index)) return ContentType_Addon; break; From 810e2c44a6d5cf1a131f15626b6365fc9c135486 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 7 Apr 2024 22:34:17 +0100 Subject: [PATCH 34/36] Merge conflict resolution part II --- files/lang/wizard_de.ts | 4 ++-- files/lang/wizard_fr.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/files/lang/wizard_de.ts b/files/lang/wizard_de.ts index d793175dcb..5749cf2d5d 100644 --- a/files/lang/wizard_de.ts +++ b/files/lang/wizard_de.ts @@ -514,11 +514,11 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov - Error writing OpenMW configuration file + <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> - <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> + Error writing OpenMW configuration file diff --git a/files/lang/wizard_fr.ts b/files/lang/wizard_fr.ts index ba3c8aaa19..9b5acbc9e9 100644 --- a/files/lang/wizard_fr.ts +++ b/files/lang/wizard_fr.ts @@ -505,10 +505,6 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov Quit Wizard - - Error writing OpenMW configuration file - - Are you sure you want to exit the Wizard? @@ -521,6 +517,10 @@ to default Morrowind fonts. Check this box if you still prefer original fonts ov <html><head/><body><p><b>Could not create %1</b></p><p>Please make sure you have the right permissions and try again.</p></body></html> + + Error writing OpenMW configuration file + + Wizard::UnshieldWorker From 0d547c54385c6eb7750e449aef6dc3fa812a863b Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 8 Apr 2024 13:37:36 +0100 Subject: [PATCH 35/36] Resolve merge conflicts from https://gitlab.com/OpenMW/openmw/-/merge_requests/3893 --- apps/wizard/ui/importpage.ui | 6 +++--- files/lang/wizard_de.ts | 6 +++--- files/lang/wizard_fr.ts | 6 +++--- files/lang/wizard_ru.ts | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/wizard/ui/importpage.ui b/apps/wizard/ui/importpage.ui index acc0d268ec..669920c5f3 100644 --- a/apps/wizard/ui/importpage.ui +++ b/apps/wizard/ui/importpage.ui @@ -33,7 +33,7 @@ - Import settings from Morrowind.ini + Import Settings From Morrowind.ini true @@ -43,7 +43,7 @@ - Import add-on and plugin selection + Import Add-on and Plugin Selection true @@ -53,7 +53,7 @@ - Import bitmap fonts setup from Morrowind.ini + Import Bitmap Fonts Setup From Morrowind.ini Fonts shipped with the original engine are blurry with UI scaling and support only a small amount of characters, diff --git a/files/lang/wizard_de.ts b/files/lang/wizard_de.ts index 5749cf2d5d..33c7a5fb53 100644 --- a/files/lang/wizard_de.ts +++ b/files/lang/wizard_de.ts @@ -81,15 +81,15 @@ - Import settings from Morrowind.ini + Import Settings From Morrowind.ini - Import add-on and plugin selection + Import Add-on and Plugin Selection - Import bitmap fonts setup from Morrowind.ini + Import Bitmap Fonts Setup From Morrowind.ini diff --git a/files/lang/wizard_fr.ts b/files/lang/wizard_fr.ts index 9b5acbc9e9..ab0b01af73 100644 --- a/files/lang/wizard_fr.ts +++ b/files/lang/wizard_fr.ts @@ -81,15 +81,15 @@ - Import settings from Morrowind.ini + Import Settings From Morrowind.ini - Import add-on and plugin selection + Import Add-on and Plugin Selection - Import bitmap fonts setup from Morrowind.ini + Import Bitmap Fonts Setup From Morrowind.ini diff --git a/files/lang/wizard_ru.ts b/files/lang/wizard_ru.ts index 0a2e6e5561..e26735443e 100644 --- a/files/lang/wizard_ru.ts +++ b/files/lang/wizard_ru.ts @@ -81,15 +81,15 @@ <html><head/><body><p>Чтобы OpenMW мог работать правильно, ему нужно импортировать настройки из файла с настройками Morrowind.</p><p><span style=" font-weight:bold;">Подсказка:</span> Также можно импортировать настройки позже, запустив Мастер импорта заново.</p><p/></body></html> - Import settings from Morrowind.ini + Import Settings From Morrowind.ini Импортировать настройки из Morrowind.ini - Import add-on and plugin selection + Import Add-on and Plugin Selection Импортировать список подключенных плагинов - Import bitmap fonts setup from Morrowind.ini + Import Bitmap Fonts Setup From Morrowind.ini Импортировать растровые шрифты из Morrowind.ini From a179e9c001c36ac8f7dbe4915c7ee6e02d80e466 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Mon, 8 Apr 2024 13:43:42 +0100 Subject: [PATCH 36/36] The rest of the merge conflict I didn't notice it as GitLab didn't highlight the diff properly. --- files/lang/wizard_ru.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/lang/wizard_ru.ts b/files/lang/wizard_ru.ts index e26735443e..5784b11eac 100644 --- a/files/lang/wizard_ru.ts +++ b/files/lang/wizard_ru.ts @@ -86,7 +86,7 @@ Import Add-on and Plugin Selection - Импортировать список подключенных плагинов + Импортировать список подключенных аддонов и плагинов Import Bitmap Fonts Setup From Morrowind.ini