mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-29 09:32:45 +00:00
c2b383ea92
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).
151 lines
4.7 KiB
C++
151 lines
4.7 KiB
C++
#ifndef GAMESETTINGS_HPP
|
|
#define GAMESETTINGS_HPP
|
|
|
|
#include <QFile>
|
|
#include <QMultiMap>
|
|
#include <QString>
|
|
#include <QStringList>
|
|
#include <QTextStream>
|
|
|
|
#include <filesystem>
|
|
|
|
namespace Files
|
|
{
|
|
typedef std::vector<std::filesystem::path> PathContainer;
|
|
struct ConfigurationManager;
|
|
}
|
|
|
|
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 std::strong_ordering operator<=>(const SettingValue&, const SettingValue&) = default;
|
|
};
|
|
|
|
class GameSettings
|
|
{
|
|
public:
|
|
explicit GameSettings(const Files::ConfigurationManager& cfg);
|
|
|
|
inline SettingValue value(const QString& key, const SettingValue& defaultValue = {})
|
|
{
|
|
return mSettings.contains(key) ? mSettings.value(key) : defaultValue;
|
|
}
|
|
|
|
inline void setValue(const QString& key, const SettingValue& value)
|
|
{
|
|
remove(key);
|
|
mSettings.insert(key, value);
|
|
if (isUserSetting(value))
|
|
mUserSettings.insert(key, value);
|
|
}
|
|
|
|
inline void setMultiValue(const QString& key, const SettingValue& value)
|
|
{
|
|
QList<SettingValue> values = mSettings.values(key);
|
|
if (!values.contains(value))
|
|
mSettings.insert(key, value);
|
|
|
|
if (isUserSetting(value))
|
|
{
|
|
values = mUserSettings.values(key);
|
|
if (!values.contains(value))
|
|
mUserSettings.insert(key, value);
|
|
}
|
|
}
|
|
|
|
inline void remove(const QString& key)
|
|
{
|
|
// simplify to removeIf when Qt5 goes
|
|
for (auto itr = mSettings.lowerBound(key); itr != mSettings.upperBound(key);)
|
|
{
|
|
if (isUserSetting(*itr))
|
|
itr = mSettings.erase(itr);
|
|
else
|
|
++itr;
|
|
}
|
|
mUserSettings.remove(key);
|
|
}
|
|
|
|
QList<SettingValue> getDataDirs() const;
|
|
|
|
QString getResourcesVfs() const;
|
|
|
|
inline void removeDataDir(const QString& existingDir)
|
|
{
|
|
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());
|
|
}
|
|
}
|
|
|
|
inline void addDataDir(const SettingValue& dir)
|
|
{
|
|
if (!dir.value.isEmpty())
|
|
mDataDirs.append(dir);
|
|
}
|
|
|
|
inline QString getDataLocal() const { return mDataLocal; }
|
|
|
|
bool hasMaster();
|
|
|
|
QList<SettingValue> values(const QString& key, const QList<SettingValue>& defaultValues = {}) const;
|
|
bool containsValue(const QString& key, const QString& value) const;
|
|
|
|
bool readFile(QTextStream& stream, const QString& context, bool ignoreContent = false);
|
|
bool readFile(QTextStream& stream, QMultiMap<QString, SettingValue>& 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);
|
|
|
|
QList<SettingValue> getArchiveList() const;
|
|
void setContentList(
|
|
const QList<SettingValue>& dirNames, const QList<SettingValue>& archiveNames, const QStringList& fileNames);
|
|
QList<SettingValue> getContentList() const;
|
|
|
|
const QString& getUserContext() const { return mContexts.back(); }
|
|
bool isUserSetting(const SettingValue& settingValue) const;
|
|
|
|
SettingValue procesPathSettingValue(const SettingValue& value);
|
|
|
|
void clear();
|
|
|
|
private:
|
|
const Files::ConfigurationManager& mCfgMgr;
|
|
|
|
void validatePaths();
|
|
QMultiMap<QString, SettingValue> mSettings;
|
|
QMultiMap<QString, SettingValue> mUserSettings;
|
|
|
|
QStringList mContexts;
|
|
|
|
QList<SettingValue> mDataDirs;
|
|
QString mDataLocal;
|
|
|
|
static const char sArchiveKey[];
|
|
static const char sContentKey[];
|
|
static const char sDirectoryKey[];
|
|
|
|
static bool isOrderedLine(const QString& line);
|
|
};
|
|
|
|
QDataStream& operator<<(QDataStream& out, const SettingValue& settingValue);
|
|
QDataStream& operator>>(QDataStream& in, SettingValue& settingValue);
|
|
}
|
|
|
|
Q_DECLARE_METATYPE(Config::SettingValue)
|
|
|
|
#endif // GAMESETTINGS_HPP
|