diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index fd36993bf..27d37dbf0 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -490,7 +490,16 @@ bool Launcher::MainDialog::writeSettings() // Game settings QFile file(userPath + QString("openmw.cfg")); - if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + QIODevice::OpenMode mode(0); + bool keepComments = mLauncherSettings.value(QString("Settings/keep-comments"), QString("true")) + == QLatin1String("true"); + + if (keepComments) + mode = QIODevice::ReadWrite | QIODevice::Text; + else + mode = QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate; + + if (!file.open(mode)) { // File cannot be opened or created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); @@ -504,9 +513,15 @@ bool Launcher::MainDialog::writeSettings() } QTextStream stream(&file); - stream.setCodec(QTextCodec::codecForName("UTF-8")); - mGameSettings.writeFile(stream); + if (keepComments) + mGameSettings.writeFileWithComments(file); + else + { + stream.setCodec(QTextCodec::codecForName("UTF-8")); + mGameSettings.writeFile(stream); + } + file.close(); // Graphics settings diff --git a/apps/launcher/settingspage.cpp b/apps/launcher/settingspage.cpp index 843b51391..bc8ffe618 100644 --- a/apps/launcher/settingspage.cpp +++ b/apps/launcher/settingspage.cpp @@ -260,6 +260,10 @@ void Launcher::SettingsPage::saveSettings() } else { mGameSettings.setValue(QLatin1String("encoding"), QLatin1String("win1252")); } + + QString keepComments(saveCommentsCheckBox->isChecked() ? "true" : "false"); + + mLauncherSettings.setValue(QLatin1String("Settings/keep-comments"), keepComments); } bool Launcher::SettingsPage::loadSettings() @@ -271,5 +275,9 @@ bool Launcher::SettingsPage::loadSettings() if (index != -1) languageComboBox->setCurrentIndex(index); + QString keepComments(mLauncherSettings.value(QLatin1String("Settings/keep-comments"))); + + saveCommentsCheckBox->setChecked(keepComments == "true"); + return true; } diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 0481235c7..51c7883ad 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -1,6 +1,7 @@ #include "gamesettings.hpp" #include "launchersettings.hpp" +#include #include #include #include @@ -173,6 +174,133 @@ bool Config::GameSettings::writeFile(QTextStream &stream) return true; } +// Policy: +// +// - Always ignore a line beginning with '#' or empty lines +// +// - If a line in file exists with matching key and first part of value (before ',', +// '\n', etc) also matches, then replace the line with that of mUserSettings. +// - else remove line (maybe replace the line with '#' in front instead?) +// +// - If there is no corresponding line in file, add at the end +// +bool Config::GameSettings::writeFileWithComments(QFile &file) +{ + QTextStream stream(&file); + stream.setCodec(QTextCodec::codecForName("UTF-8")); + + // slurp + std::vector fileCopy; + QString line = stream.readLine(); + while (!line.isNull()) + { + fileCopy.push_back(line); + line = stream.readLine(); + } + stream.seek(0); + + // empty file, no comments to keep + if (fileCopy.empty()) + return writeFile(stream); + + // Temp copy of settings to save, but with the keys appended with the first part of the value + // + // ATTENTION! + // + // A hack to avoid looping through each line, makes use of the fact that fallbacks values + // are comma separated. + QMap userSettingsCopy; + QRegExp settingRegex("^([^=]+)\\s*=\\s*([^,]+)(.*)$"); + QString settingLine; + QMap::const_iterator settingsIter = mUserSettings.begin(); + for (; settingsIter != mUserSettings.end(); ++settingsIter) + { + settingLine = settingsIter.key()+"="+settingsIter.value(); + if (settingRegex.indexIn(settingLine) != -1) + { + userSettingsCopy[settingRegex.cap(1)+"="+settingRegex.cap(2)] = + (settingRegex.captureCount() < 3) ? "" : settingRegex.cap(3); + } + } + + QString keyVal; + for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + { + // skip empty or comment lines + if ((*iter).isEmpty() || (*iter).startsWith("#")) + continue; + + // look for a key in the line + if (settingRegex.indexIn(*iter) == -1 || settingRegex.captureCount() < 2) + { + // no key or first part of value found in line, replace with a null string which + // will be remved later + *iter = QString(); + continue; + } + + // look for a matching key in user settings + keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); + QMap::iterator it = userSettingsCopy.find(keyVal); + if (it == userSettingsCopy.end()) + { + // no such key+valStart, replace with a null string which will be remved later + *iter = QString(); + } + else + { + *iter = QString(it.key()+it.value()); + userSettingsCopy.erase(it); + } + } + + // write the new config file + QString key; + QString value; + for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + { + if ((*iter).isNull()) + continue; + + if ((*iter).isEmpty() || (*iter).startsWith("#")) + stream << *iter << "\n"; + + if (settingRegex.indexIn(*iter) == -1 || settingRegex.captureCount() < 2) + continue; + + // Quote paths with spaces + key = settingRegex.cap(1); + value = settingRegex.cap(2)+settingRegex.cap(3); + if (key == QLatin1String("data") + || key == QLatin1String("data-local") + || key == QLatin1String("resources")) + { + if (value.contains(QChar(' '))) + { + value.remove(QChar('\"')); // Remove quotes + + stream << key << "=\"" << value << "\"\n"; + continue; + } + } + stream << key << "=" << value << "\n"; + } + + if (!userSettingsCopy.empty()) + { + stream << "# new entries" << "\n"; + QMap::const_iterator it = userSettingsCopy.begin(); + for (; it != userSettingsCopy.end(); ++it) + { + stream << it.key() << it.value() << "\n"; + } + } + + file.resize(file.pos()); + + return true; +} + bool Config::GameSettings::hasMaster() { bool result = false; diff --git a/components/config/gamesettings.hpp b/components/config/gamesettings.hpp index cc5033f35..35614113f 100644 --- a/components/config/gamesettings.hpp +++ b/components/config/gamesettings.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,7 @@ namespace Config bool readUserFile(QTextStream &stream); bool writeFile(QTextStream &stream); + bool writeFileWithComments(QFile &file); void setContentList(const QStringList& fileNames); QStringList getContentList() const; diff --git a/files/ui/settingspage.ui b/files/ui/settingspage.ui index 7f5e4a7de..f38ba764c 100644 --- a/files/ui/settingspage.ui +++ b/files/ui/settingspage.ui @@ -40,6 +40,16 @@ + + + + Keep comments in openmw.cfg + + + false + + +