From c2b383ea9289b83c78f244056d8f3e42d56d7a96 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 25 Oct 2024 00:49:59 +0100 Subject: [PATCH 1/4] Store original representation of paths in content lists Also compare against existing content lists in a more forgiving way. The first improvement makes it possible to use relative paths in openmw.cfg without the launcher canonicalising them. This was really annoying if you used a relative path on purpose. It also stops the launcher converting all paths to Qt's convention, where forward slashes are used on Windows even though they're not native. The engine doesn't care, so you could always put either in the config file, but the launcher wouldn't stand for that, and would make them match. To make this work, we need to store a path's originalRepresentation in the content list, compare paths loaded from openmw.cfg based on their originalRepresentation, and convert paths from originalRepresentation to absolute value when loading them from a content list. The second improvement means that paths that are equivalent, but expressed differently (e.g. mismatched case on Windows, mismatched separators on Windows, or mild differences like unnecessary `./`es and doubled separators) don't trigger the creation of a new effectively-identical content list. To make this work, we had to switch the comparison to lexicaly normalise the path first. It could only be lexical normalisation as originalRepresentation might be absolute, relative, or absolute-but-based-on-a-path-slug, and we didn't want slugs to break things or relative paths to count as equivalent to absolute ones that refer to the same file. The comparison is case-insensitive on Windows, and case-sensitive elsewhere. This isn't strictly right, as you can have case-sensitive things mounted on Windows or tell a Linux directory to be case-insensitive, but we can't tell when that might happen based on a lexical path as it depends on real directory properties (and might differ for different parts of the path, which is too much hassle to support). --- apps/launcher/datafilespage.cpp | 2 +- components/config/gamesettings.cpp | 12 +++++++++--- components/config/gamesettings.hpp | 2 ++ components/config/launchersettings.cpp | 16 +++++++++++++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 9e812138c8..8db0076af5 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -301,7 +301,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) [&](const Config::SettingValue& dir) { return mGameSettings.isUserSetting(dir); }), directories.end()); for (const auto& dir : contentModelDirectories) - directories.push_back({ dir }); + directories.push_back(mGameSettings.procesPathSettingValue({ dir })); } mDataLocal = mGameSettings.getDataLocal(); diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index a7da8fa150..7015f86c04 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -178,9 +178,7 @@ bool Config::GameSettings::readFile( value.originalRepresentation = value.value; } - std::filesystem::path path = Files::pathFromQString(value.value); - mCfgMgr.processPath(path, Files::pathFromQString(context)); - value.value = Files::pathToQString(path); + value = procesPathSettingValue(value); } if (ignoreContent && (key == QLatin1String("content") || key == QLatin1String("data"))) continue; @@ -588,6 +586,14 @@ bool Config::GameSettings::isUserSetting(const SettingValue& settingValue) const return settingValue.context.isEmpty() || settingValue.context == getUserContext(); } +Config::SettingValue Config::GameSettings::procesPathSettingValue(const SettingValue& value) +{ + std::filesystem::path path = Files::pathFromQString(value.value); + std::filesystem::path basePath = Files::pathFromQString(value.context.isEmpty() ? getUserContext() : value.context); + mCfgMgr.processPath(path, basePath); + return SettingValue{ Files::pathToQString(path), value.originalRepresentation, value.context }; +} + void Config::GameSettings::clear() { mSettings.clear(); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index 89139ff41a..d31d3bf897 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -118,6 +118,8 @@ namespace Config const QString& getUserContext() const { return mContexts.back(); } bool isUserSetting(const SettingValue& settingValue) const; + SettingValue procesPathSettingValue(const SettingValue& value); + void clear(); private: diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index f4749d0f93..449edd1b89 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -1,6 +1,7 @@ #include "launchersettings.hpp" #include +#include #include #include #include @@ -263,8 +264,17 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) for (const QString& listName : getContentLists()) { const auto& listDirs = getDataDirectoryList(listName); - if (!std::ranges::equal( - dirs, listDirs, [](const SettingValue& dir, const QString& listDir) { return dir.value == listDir; })) +#ifdef Q_OS_WINDOWS + constexpr auto caseSensitivity = Qt::CaseInsensitive; +#else + constexpr auto caseSensitivity = Qt::CaseSensitive; +#endif + constexpr auto compareDataDirectories = [caseSensitivity](const SettingValue& dir, const QString& listDir) { + return dir.originalRepresentation == listDir + || QDir::cleanPath(dir.originalRepresentation) + .compare(QDir::cleanPath(listDir), caseSensitivity) == 0; + }; + if (!std::ranges::equal(dirs, listDirs, compareDataDirectories)) continue; constexpr auto compareFiles = [](const QString& a, const QString& b) { return a.compare(b, Qt::CaseInsensitive) == 0; }; @@ -281,7 +291,7 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) setCurrentContentListName(newContentListName); QStringList newListDirs; for (const auto& dir : dirs) - newListDirs.push_back(dir.value); + newListDirs.push_back(dir.originalRepresentation); setContentList(newContentListName, newListDirs, archives, files); } From b5969023296f0aa30b5d1340d8b00bf0739f41f1 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 25 Oct 2024 00:53:27 +0100 Subject: [PATCH 2/4] c a p i t u l a t e Looks like I forgot to reformat after a typo fix. --- components/config/launchersettings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index 449edd1b89..8c18d60210 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -271,8 +271,7 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) #endif constexpr auto compareDataDirectories = [caseSensitivity](const SettingValue& dir, const QString& listDir) { return dir.originalRepresentation == listDir - || QDir::cleanPath(dir.originalRepresentation) - .compare(QDir::cleanPath(listDir), caseSensitivity) == 0; + || QDir::cleanPath(dir.originalRepresentation).compare(QDir::cleanPath(listDir), caseSensitivity) == 0; }; if (!std::ranges::equal(dirs, listDirs, compareDataDirectories)) continue; From 7640b6bcf471e8ca4b37ab80dc30367cb49792ba Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 25 Oct 2024 00:32:12 +0000 Subject: [PATCH 3/4] Typo --- apps/launcher/datafilespage.cpp | 2 +- components/config/gamesettings.cpp | 4 ++-- components/config/gamesettings.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8db0076af5..73d224f181 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -301,7 +301,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName) [&](const Config::SettingValue& dir) { return mGameSettings.isUserSetting(dir); }), directories.end()); for (const auto& dir : contentModelDirectories) - directories.push_back(mGameSettings.procesPathSettingValue({ dir })); + directories.push_back(mGameSettings.processPathSettingValue({ dir })); } mDataLocal = mGameSettings.getDataLocal(); diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 7015f86c04..a2cbe5bf9a 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -178,7 +178,7 @@ bool Config::GameSettings::readFile( value.originalRepresentation = value.value; } - value = procesPathSettingValue(value); + value = processPathSettingValue(value); } if (ignoreContent && (key == QLatin1String("content") || key == QLatin1String("data"))) continue; @@ -586,7 +586,7 @@ bool Config::GameSettings::isUserSetting(const SettingValue& settingValue) const return settingValue.context.isEmpty() || settingValue.context == getUserContext(); } -Config::SettingValue Config::GameSettings::procesPathSettingValue(const SettingValue& value) +Config::SettingValue Config::GameSettings::processPathSettingValue(const SettingValue& value) { std::filesystem::path path = Files::pathFromQString(value.value); std::filesystem::path basePath = Files::pathFromQString(value.context.isEmpty() ? getUserContext() : value.context); diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index d31d3bf897..4b66e6e8e5 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -118,7 +118,7 @@ namespace Config const QString& getUserContext() const { return mContexts.back(); } bool isUserSetting(const SettingValue& settingValue) const; - SettingValue procesPathSettingValue(const SettingValue& value); + SettingValue processPathSettingValue(const SettingValue& value); void clear(); From 31c84e0407883faadae17a0bbc5190eb76b3f18f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 25 Oct 2024 00:49:46 +0000 Subject: [PATCH 4/4] We don't need to capture constexpr stuff --- components/config/launchersettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/config/launchersettings.cpp b/components/config/launchersettings.cpp index 8c18d60210..30398038aa 100644 --- a/components/config/launchersettings.cpp +++ b/components/config/launchersettings.cpp @@ -269,7 +269,7 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings) #else constexpr auto caseSensitivity = Qt::CaseSensitive; #endif - constexpr auto compareDataDirectories = [caseSensitivity](const SettingValue& dir, const QString& listDir) { + constexpr auto compareDataDirectories = [](const SettingValue& dir, const QString& listDir) { return dir.originalRepresentation == listDir || QDir::cleanPath(dir.originalRepresentation).compare(QDir::cleanPath(listDir), caseSensitivity) == 0; };