From 0efe8f5465d652d42a7191d8a7c5223dfa711466 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 14 Jun 2015 14:42:08 +1000 Subject: [PATCH 1/4] Fix broken launcher build for windows. --- apps/launcher/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 66cc157d3..3c7b39314 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -100,6 +100,9 @@ target_link_libraries(openmw-launcher if (DESIRED_QT_VERSION MATCHES 4) target_link_libraries(openmw-launcher ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY}) + if(WIN32) + target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY}) + endif(WIN32) else() qt5_use_modules(openmw-launcher Widgets Core) endif() From c22c9c271d6cfab22ac2f6c35714eeb266b2fa0c Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 14 Jun 2015 14:51:01 +1000 Subject: [PATCH 2/4] Allow comments (lines starting with # character) and blank lines in openmw.cfg. Should resolve Feature #2535. - allows moving various config entries up or down - comment lines above config entries stay as a pair --- apps/launcher/maindialog.cpp | 7 +- components/config/gamesettings.cpp | 209 +++++++++++++++++++++++++++++ components/config/gamesettings.hpp | 4 + 3 files changed, 216 insertions(+), 4 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index fd36993bf..2f7a9db33 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -490,7 +490,7 @@ bool Launcher::MainDialog::writeSettings() // Game settings QFile file(userPath + QString("openmw.cfg")); - if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) { + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { // File cannot be opened or created QMessageBox msgBox; msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); @@ -503,10 +503,8 @@ bool Launcher::MainDialog::writeSettings() return false; } - QTextStream stream(&file); - stream.setCodec(QTextCodec::codecForName("UTF-8")); - mGameSettings.writeFile(stream); + mGameSettings.writeFileWithComments(file); file.close(); // Graphics settings @@ -525,6 +523,7 @@ bool Launcher::MainDialog::writeSettings() return false; } + QTextStream stream(&file); stream.setDevice(&file); stream.setCodec(QTextCodec::codecForName("UTF-8")); diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 0481235c7..4339369cf 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,214 @@ bool Config::GameSettings::writeFile(QTextStream &stream) return true; } +bool Config::GameSettings::isOrderedLine(const QString& line) const +{ + return line.contains(QRegExp("^\\s*fallback-archive\\s*=")) + || line.contains(QRegExp("^\\s*fallback\\s*=")) + || line.contains(QRegExp("^\\s*data\\s*=")) + || line.contains(QRegExp("^\\s*data-local\\s*=")) + || line.contains(QRegExp("^\\s*resources\\s*=")) + || line.contains(QRegExp("^\\s*content\\s*=")); +} + +// Policy: +// +// - Always ignore a line beginning with '#' or empty lines; added above a config +// entry. +// +// - 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 +// +// - 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); + + // start + // | + // | +----------------------------------------------------------+ + // | | | + // v v | + // skip non-"ordered" lines (remove "ordered" lines) | + // | ^ | + // | | | + // | non-"ordered" line, write saved comments | + // | ^ | + // v | | + // blank or comment line, save in temp buffer <--------+ | + // | | | | + // v +------- comment line ------+ | + // "ordered" line | + // | | + // v | + // save in a separate map of comments keyed by "ordered" line | + // | | + // +----------------------------------------------------------+ + // + // + QRegExp settingRegex("^([^=]+)\\s*=\\s*([^,]+)(.*)$"); + std::vector comments; + std::vector::iterator commentStart = fileCopy.end(); + std::map > commentsMap; + for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + { + if (isOrderedLine(*iter)) + { + // save in a separate map of comments keyed by "ordered" line + if (!comments.empty()) + { + if (settingRegex.indexIn(*iter) != -1) + { + commentsMap[settingRegex.cap(1)+"="+settingRegex.cap(2)] = comments; + comments.clear(); + commentStart = fileCopy.end(); + } + // else do nothing, malformed line + } + + *iter = QString(); // "ordered" lines to be removed later + } + else if ((*iter).isEmpty() || (*iter).contains(QRegExp("^\\s*#"))) + { + // comment line, save in temp buffer + if (comments.empty()) + commentStart = iter; + comments.push_back(*iter); + *iter = QString(); // assume to be deleted later + } + else + { + int index = settingRegex.indexIn(*iter); + + // blank or non-"ordered" line, write saved comments + if (!comments.empty() && index != -1 && settingRegex.captureCount() >= 2 && + mUserSettings.find(settingRegex.cap(1)) != mUserSettings.end()) + { + for (std::vector::const_iterator it = comments.begin(); it != comments.end(); ++it) + { + *commentStart = *it; + ++commentStart; + } + comments.clear(); + commentStart = fileCopy.end(); + } + + // keep blank lines and non-"ordered" lines other than comments + + // look for a key in the line + if (index == -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(); + comments.clear(); + commentStart = fileCopy.end(); + continue; + } + + // look for a matching key in user settings + *iter = QString(); // assume no match + QString key = settingRegex.cap(1); + QString keyVal = settingRegex.cap(1)+"="+settingRegex.cap(2); + QMap::const_iterator i = mUserSettings.find(key); + while (i != mUserSettings.end() && i.key() == key) + { + QString settingLine = i.key() + "=" + i.value(); + if (settingRegex.indexIn(settingLine) != -1) + { + if ((settingRegex.cap(1)+"="+settingRegex.cap(2)) == keyVal) + { + *iter = settingLine; + break; + } + } + ++i; + } + } + } + + // comments at top of file + for (std::vector::iterator iter = fileCopy.begin(); iter != fileCopy.end(); ++iter) + { + if ((*iter).isNull()) + continue; + + // Below is based on readFile() code, if that changes corresponding change may be + // required (for example duplicates may be inserted if the rules don't match) + if (/*(*iter).isEmpty() ||*/ (*iter).contains(QRegExp("^\\s*#"))) + { + stream << *iter << "\n"; + continue; + } + } + + // Iterate in reverse order to preserve insertion order + QString settingLine; + QMapIterator it(mUserSettings); + it.toBack(); + + while (it.hasPrevious()) + { + it.previous(); + + // Quote paths with spaces + if ((it.key() == QLatin1String("data") + || it.key() == QLatin1String("data-local") + || it.key() == QLatin1String("resources")) && it.value().contains(QChar(' '))) + { + QString stripped = it.value(); + stripped.remove(QChar('\"')); // Remove quotes + + settingLine = it.key() + "=\"" + stripped + "\""; + } + else + settingLine = it.key() + "=" + it.value(); + + if (settingRegex.indexIn(settingLine) != -1) + { + std::map >::const_iterator i = + commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); + + if (i != commentsMap.end()) + { + std::vector cLines = i->second; + for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) + stream << *ci << "\n"; + } + } + + stream << settingLine << "\n"; + } + + // flush any end comments + if (!comments.empty()) + { + for (std::vector::const_iterator ci = comments.begin(); ci != comments.end(); ++ci) + stream << *ci << "\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..992a3e565 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; @@ -81,6 +83,8 @@ namespace Config QString mDataLocal; static const char sContentKey[]; + + bool isOrderedLine(const QString& line) const; }; } #endif // GAMESETTINGS_HPP From 36aa4aa9bdeb50a3512d5f2212bb5daaa94477b2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 15 Jun 2015 09:53:26 +1000 Subject: [PATCH 3/4] If a removed 'content=' item has comments, keep them in config file for later use. --- components/config/gamesettings.cpp | 47 ++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 4339369cf..74d92d55d 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -195,6 +195,9 @@ bool Config::GameSettings::isOrderedLine(const QString& line) const // // - If there is no corresponding line in file, add at the end // +// - Removed content items are saved as comments if the item had any comments. +// Content items prepended with '##' are considered previously removed. +// bool Config::GameSettings::writeFileWithComments(QFile &file) { QTextStream stream(&file); @@ -227,7 +230,8 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) // v | | // blank or comment line, save in temp buffer <--------+ | // | | | | - // v +------- comment line ------+ | + // | +------- comment line ------+ | + // v (special processing '##') | // "ordered" line | // | | // v | @@ -263,7 +267,20 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) // comment line, save in temp buffer if (comments.empty()) commentStart = iter; - comments.push_back(*iter); + + // special removed content processing + if ((*iter).contains(QRegExp("^##content\\s*="))) + { + if (!comments.empty()) + { + commentsMap[*iter] = comments; + comments.clear(); + commentStart = fileCopy.end(); + } + } + else + comments.push_back(*iter); + *iter = QString(); // assume to be deleted later } else @@ -359,17 +376,43 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) std::map >::const_iterator i = commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); + // check if previous removed content item with comments + if (i == commentsMap.end()) + i = commentsMap.find("##"+settingRegex.cap(1)+"="+settingRegex.cap(2)); + if (i != commentsMap.end()) { std::vector cLines = i->second; for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) stream << *ci << "\n"; + + commentsMap.erase(i); } } stream << settingLine << "\n"; } + // flush any removed settings + if (!commentsMap.empty()) + { + std::map >::const_iterator i = commentsMap.begin(); + for (; i != commentsMap.end(); ++i) + { + if (i->first.contains(QRegExp("^\\s*content\\s*="))) + { + std::vector cLines = i->second; + for (std::vector::const_iterator ci = cLines.begin(); ci != cLines.end(); ++ci) + stream << *ci << "\n"; + + // mark the content line entry for future preocessing + stream << "##" << i->first << "\n"; + + //commentsMap.erase(i); + } + } + } + // flush any end comments if (!comments.empty()) { From 6b28955f316d8e3261d68b99c48bd6ce8a8e3f48 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Mon, 15 Jun 2015 10:26:16 +1000 Subject: [PATCH 4/4] Fix attempt to erase using a const_iterator. --- components/config/gamesettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/config/gamesettings.cpp b/components/config/gamesettings.cpp index 74d92d55d..ca6bfd80d 100644 --- a/components/config/gamesettings.cpp +++ b/components/config/gamesettings.cpp @@ -373,7 +373,7 @@ bool Config::GameSettings::writeFileWithComments(QFile &file) if (settingRegex.indexIn(settingLine) != -1) { - std::map >::const_iterator i = + std::map >::iterator i = commentsMap.find(settingRegex.cap(1)+"="+settingRegex.cap(2)); // check if previous removed content item with comments