From f5b7a230fc1fcf69deb0ecd7e73b45f0488f95e2 Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Sat, 5 May 2018 13:30:45 +0300 Subject: [PATCH 1/7] ESMReader::close now clears mHeader --- components/esm/esmreader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 2a716427e..67b9d6a38 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -57,6 +57,7 @@ void ESMReader::close() mCtx.subCached = false; mCtx.recName.clear(); mCtx.subName.clear(); + mHeader.blank(); } void ESMReader::openRaw(Files::IStreamPtr _esm, const std::string& name) From 905cde10dbefa4ac43ed3ea3921173a847ea523a Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Mon, 23 Apr 2018 22:21:23 +0300 Subject: [PATCH 2/7] Smart-sorting in iniimporter (time + dependency) --- apps/mwiniimporter/importer.cpp | 98 +++++++++++++++++++++++++++++---- apps/mwiniimporter/importer.hpp | 9 ++- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 24646b844..66b56f8a6 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -824,33 +825,73 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } } -void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const { - std::vector > contentFiles; +void MwIniImporter::dependencySortStep(std::string& el, MwIniImporter::deplist& src, std::vector& ret) +{ + auto it = std::find_if(src.begin(), src.end(), [&el](std::pair< std::string, std::vector >& o) + { + return o.first == el; + }); + if (it != src.end()) + { + auto o = std::move(*it); + src.erase(it); + for (auto name : o.second) + { + MwIniImporter::dependencySortStep(name, src, ret); + } + ret.push_back(std::move(o.first)); + } +} + +std::vector MwIniImporter::dependencySort(MwIniImporter::deplist src) +{ + std::vector ret; + while (!src.empty()) + { + MwIniImporter::dependencySortStep(src.begin()->first, src, ret); + } + return ret; +} + +std::vector::iterator MwIniImporter::findString(std::vector& v, const std::string& s) +{ + return std::find_if(v.begin(), v.end(), [&s](const std::string& str) + { + return Misc::StringUtils::ciEqual(str, s); + }); +} + +void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const +{ + std::vector> contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); std::time_t defaultTime = 0; + ToUTF8::Utf8Encoder encoder(mEncoding); // assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files"); multistrmap::const_iterator it = ini.begin(); - for(int i=0; it != ini.end(); i++) { + for (int i=0; it != ini.end(); i++) + { gameFile = baseGameFile; gameFile.append(this->numberToString(i)); it = ini.find(gameFile); - if(it == ini.end()) { + if(it == ini.end()) break; - } - for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { + for(std::vector::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) + { std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::lowerCaseInPlace(filetype); - if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { + if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) + { boost::filesystem::path filepath(gameFilesDir); filepath /= *entry; - contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry)); + contentFiles.push_back({lastWriteTime(filepath, defaultTime), filepath}); } } } @@ -858,11 +899,46 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co cfg.erase("content"); cfg.insert( std::make_pair("content", std::vector() ) ); - // this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed. + // sort by timestamp sort(contentFiles.begin(), contentFiles.end()); - for(std::vector >::const_iterator iter=contentFiles.begin(); iter!=contentFiles.end(); ++iter) { - cfg["content"].push_back(iter->second); + + MwIniImporter::deplist unsortedFiles; + + ESM::ESMReader reader; + reader.setEncoder(&encoder); + for (auto& file : contentFiles) + { + reader.open(file.second.string()); + std::vector deps; + for (auto& depFile : reader.getGameFiles()) + { + deps.push_back(depFile.name); + } + unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), deps); + reader.close(); + } + + auto sortedFiles = dependencySort(unsortedFiles); + + // hard-coded dependency Morrowind - Tribunal - Bloodmoon + if(findString(sortedFiles, "Morrowind.esm") != sortedFiles.end()) + { + auto foundTribunal = findString(sortedFiles, "Tribunal.esm"); + auto foundBloodmoon = findString(sortedFiles, "Bloodmoon.esm"); + + if (foundBloodmoon != sortedFiles.end() && foundTribunal != sortedFiles.end()) + { + size_t dstBloodmoon = std::distance(sortedFiles.begin(), foundBloodmoon); + size_t dstTribunal = std::distance(sortedFiles.begin(), foundTribunal); + if (dstBloodmoon < dstTribunal) + dstTribunal++; + sortedFiles.insert(foundBloodmoon, *foundTribunal); + sortedFiles.erase(sortedFiles.begin() + dstTribunal); + } } + + for (auto& file : sortedFiles) + cfg["content"].push_back(file); } void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) { diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index c73cc65b5..44b2af83c 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -14,6 +14,7 @@ class MwIniImporter { public: typedef std::map strmap; typedef std::map > multistrmap; + typedef std::vector< std::pair< std::string, std::vector > > deplist; MwIniImporter(); void setInputEncoding(const ToUTF8::FromType& encoding); @@ -22,12 +23,17 @@ class MwIniImporter { static multistrmap loadCfgFile(const boost::filesystem::path& filename); void merge(multistrmap &cfg, const multistrmap &ini) const; void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; - void importGameFiles(multistrmap &cfg, const multistrmap &ini, + void importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const; static void writeToFile(std::ostream &out, const multistrmap &cfg); + static std::vector dependencySort(MwIniImporter::deplist src); + private: + static void dependencySortStep(std::string& el, MwIniImporter::deplist& src, std::vector& ret); + static std::vector::iterator findString(std::vector& v, const std::string& s); + static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static std::string numberToString(int n); @@ -40,5 +46,4 @@ class MwIniImporter { ToUTF8::FromType mEncoding; }; - #endif From 103a07b7441fd3635be6fd42dfdfa669239e9dbe Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Tue, 8 May 2018 18:32:06 +0300 Subject: [PATCH 3/7] Less cryptic abbreviations --- apps/mwiniimporter/importer.cpp | 70 +++++++++++++++++---------------- apps/mwiniimporter/importer.hpp | 8 ++-- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 66b56f8a6..74c21da29 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -825,39 +825,43 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } } -void MwIniImporter::dependencySortStep(std::string& el, MwIniImporter::deplist& src, std::vector& ret) +void MwIniImporter::dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector& result) { - auto it = std::find_if(src.begin(), src.end(), [&el](std::pair< std::string, std::vector >& o) - { - return o.first == el; - }); - if (it != src.end()) + auto iter = std::find_if( + source.begin(), + source.end(), + [&element](std::pair< std::string, std::vector >& sourceElement) + { + return sourceElement.first == element; + } + ); + if (iter != source.end()) { - auto o = std::move(*it); - src.erase(it); - for (auto name : o.second) + auto foundElement = std::move(*iter); + source.erase(iter); + for (auto name : foundElement.second) { - MwIniImporter::dependencySortStep(name, src, ret); + MwIniImporter::dependencySortStep(name, source, result); } - ret.push_back(std::move(o.first)); + result.push_back(std::move(foundElement.first)); } } -std::vector MwIniImporter::dependencySort(MwIniImporter::deplist src) +std::vector MwIniImporter::dependencySort(MwIniImporter::dependencyList source) { - std::vector ret; - while (!src.empty()) + std::vector result; + while (!source.empty()) { - MwIniImporter::dependencySortStep(src.begin()->first, src, ret); + MwIniImporter::dependencySortStep(source.begin()->first, source, result); } - return ret; + return result; } -std::vector::iterator MwIniImporter::findString(std::vector& v, const std::string& s) +std::vector::iterator MwIniImporter::findString(std::vector& source, const std::string& string) { - return std::find_if(v.begin(), v.end(), [&s](const std::string& str) + return std::find_if(source.begin(), source.end(), [&string](const std::string& sourceString) { - return Misc::StringUtils::ciEqual(str, s); + return Misc::StringUtils::ciEqual(sourceString, string); }); } @@ -902,19 +906,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co // sort by timestamp sort(contentFiles.begin(), contentFiles.end()); - MwIniImporter::deplist unsortedFiles; + MwIniImporter::dependencyList unsortedFiles; ESM::ESMReader reader; reader.setEncoder(&encoder); for (auto& file : contentFiles) { reader.open(file.second.string()); - std::vector deps; - for (auto& depFile : reader.getGameFiles()) + std::vector dependencies; + for (auto& gameFile : reader.getGameFiles()) { - deps.push_back(depFile.name); + dependencies.push_back(gameFile.name); } - unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), deps); + unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), dependencies); reader.close(); } @@ -923,17 +927,17 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co // hard-coded dependency Morrowind - Tribunal - Bloodmoon if(findString(sortedFiles, "Morrowind.esm") != sortedFiles.end()) { - auto foundTribunal = findString(sortedFiles, "Tribunal.esm"); - auto foundBloodmoon = findString(sortedFiles, "Bloodmoon.esm"); + auto tribunalIter = findString(sortedFiles, "Tribunal.esm"); + auto bloodmoonIter = findString(sortedFiles, "Bloodmoon.esm"); - if (foundBloodmoon != sortedFiles.end() && foundTribunal != sortedFiles.end()) + if (bloodmoonIter != sortedFiles.end() && tribunalIter != sortedFiles.end()) { - size_t dstBloodmoon = std::distance(sortedFiles.begin(), foundBloodmoon); - size_t dstTribunal = std::distance(sortedFiles.begin(), foundTribunal); - if (dstBloodmoon < dstTribunal) - dstTribunal++; - sortedFiles.insert(foundBloodmoon, *foundTribunal); - sortedFiles.erase(sortedFiles.begin() + dstTribunal); + size_t bloodmoonIndex = std::distance(sortedFiles.begin(), bloodmoonIter); + size_t tribunalIndex = std::distance(sortedFiles.begin(), tribunalIter); + if (bloodmoonIndex < tribunalIndex) + tribunalIndex++; + sortedFiles.insert(bloodmoonIter, *tribunalIter); + sortedFiles.erase(sortedFiles.begin() + tribunalIndex); } } diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index 44b2af83c..e1595ad96 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -14,7 +14,7 @@ class MwIniImporter { public: typedef std::map strmap; typedef std::map > multistrmap; - typedef std::vector< std::pair< std::string, std::vector > > deplist; + typedef std::vector< std::pair< std::string, std::vector > > dependencyList; MwIniImporter(); void setInputEncoding(const ToUTF8::FromType& encoding); @@ -28,11 +28,11 @@ class MwIniImporter { void importArchives(multistrmap &cfg, const multistrmap &ini) const; static void writeToFile(std::ostream &out, const multistrmap &cfg); - static std::vector dependencySort(MwIniImporter::deplist src); + static std::vector dependencySort(MwIniImporter::dependencyList source); private: - static void dependencySortStep(std::string& el, MwIniImporter::deplist& src, std::vector& ret); - static std::vector::iterator findString(std::vector& v, const std::string& s); + static void dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector& result); + static std::vector::iterator findString(std::vector& source, const std::string& string); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static std::string numberToString(int n); From 9126e844bfeb12b4f68e56e88476d536a0ae4156 Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Fri, 11 May 2018 17:24:36 +0300 Subject: [PATCH 4/7] Use data paths from config (bug #4412) --- apps/mwiniimporter/importer.cpp | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 74c21da29..3601f0e9b 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -873,8 +873,20 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co std::time_t defaultTime = 0; ToUTF8::Utf8Encoder encoder(mEncoding); - // assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini - const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files"); + std::vector dataPaths; + if (cfg.count("data")) + { + for (std::string filePathString : cfg["data"]) + { + if (filePathString.front() == '"') + { + filePathString.erase(filePathString.begin()); + filePathString.erase(filePathString.end() - 1); + } + dataPaths.emplace_back(filePathString); + } + } + dataPaths.push_back(iniFilename.parent_path() /= "Data Files"); multistrmap::const_iterator it = ini.begin(); for (int i=0; it != ini.end(); i++) @@ -893,9 +905,20 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { - boost::filesystem::path filepath(gameFilesDir); - filepath /= *entry; - contentFiles.push_back({lastWriteTime(filepath, defaultTime), filepath}); + bool found = false; + for (auto & dataPath : dataPaths) + { + boost::filesystem::path path = dataPath / *entry; + std::time_t time = lastWriteTime(path, defaultTime); + if (time != defaultTime) + { + contentFiles.push_back({time, path}); + found = true; + break; + } + } + if (!found) + std::cout << "Warning: " << *entry << " not found, ignoring" << std::endl; } } } @@ -981,9 +1004,5 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename std::cout << "content file: " << resolved << " timestamp = (" << writeTime << ") " << timeStrBuffer << std::endl; } - else - { - std::cout << "content file: " << filename << " not found" << std::endl; - } return writeTime; } From 3b86f73ae71eb6129c3f7209e01f6ad2c2f725fb Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Thu, 17 May 2018 21:04:40 +0300 Subject: [PATCH 5/7] Replace MwIniImporter::numberToString with std::to_string --- apps/mwiniimporter/importer.cpp | 10 ++-------- apps/mwiniimporter/importer.hpp | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 3601f0e9b..3433fe5c7 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -654,12 +654,6 @@ void MwIniImporter::setVerbose(bool verbose) { mVerbose = verbose; } -std::string MwIniImporter::numberToString(int n) { - std::stringstream str; - str << n; - return str.str(); -} - MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const { std::cout << "load ini file: " << filename << std::endl; @@ -801,7 +795,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con multistrmap::const_iterator it = ini.begin(); for(int i=0; it != ini.end(); i++) { archive = baseArchive; - archive.append(this->numberToString(i)); + archive.append(std::to_string(i)); it = ini.find(archive); if(it == ini.end()) { @@ -892,7 +886,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co for (int i=0; it != ini.end(); i++) { gameFile = baseGameFile; - gameFile.append(this->numberToString(i)); + gameFile.append(std::to_string(i)); it = ini.find(gameFile); if(it == ini.end()) diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index e1595ad96..d99866533 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -35,7 +35,6 @@ class MwIniImporter { static std::vector::iterator findString(std::vector& source, const std::string& string); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); - static std::string numberToString(int n); /// \return file's "last modified time", used in original MW to determine plug-in load order static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime); From 7e03dd0f1247f152e10bf6e09ec7478b2972da00 Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Thu, 17 May 2018 21:07:20 +0300 Subject: [PATCH 6/7] Read data paths from `data-local` section too --- apps/mwiniimporter/importer.cpp | 27 ++++++++++++++++----------- apps/mwiniimporter/importer.hpp | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 3433fe5c7..6f5c2e2cd 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -859,6 +859,17 @@ std::vector::iterator MwIniImporter::findString(std::vector& output, std::vector input) { + for (auto& path : input) { + if (path.front() == '"') + { + path.erase(path.begin()); + path.erase(path.end() - 1); + } + output.emplace_back(path); + } +} + void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const { std::vector> contentFiles; @@ -869,17 +880,11 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co std::vector dataPaths; if (cfg.count("data")) - { - for (std::string filePathString : cfg["data"]) - { - if (filePathString.front() == '"') - { - filePathString.erase(filePathString.begin()); - filePathString.erase(filePathString.end() - 1); - } - dataPaths.emplace_back(filePathString); - } - } + addPaths(dataPaths, cfg["data"]); + + if (cfg.count("data-local")) + addPaths(dataPaths, cfg["data-local"]); + dataPaths.push_back(iniFilename.parent_path() /= "Data Files"); multistrmap::const_iterator it = ini.begin(); diff --git a/apps/mwiniimporter/importer.hpp b/apps/mwiniimporter/importer.hpp index d99866533..7b710a4a4 100644 --- a/apps/mwiniimporter/importer.hpp +++ b/apps/mwiniimporter/importer.hpp @@ -35,6 +35,7 @@ class MwIniImporter { static std::vector::iterator findString(std::vector& source, const std::string& string); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); + static void addPaths(std::vector& output, std::vector input); /// \return file's "last modified time", used in original MW to determine plug-in load order static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime); From f2613a74b1fb329cd66f1ffdbb47a6e102d378ec Mon Sep 17 00:00:00 2001 From: tri4ng1e Date: Thu, 17 May 2018 21:20:04 +0300 Subject: [PATCH 7/7] Write settings before invoking openmw-iniimporter --- apps/wizard/mainwizard.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/apps/wizard/mainwizard.cpp b/apps/wizard/mainwizard.cpp index 0f8fb0c49..57d080cf8 100644 --- a/apps/wizard/mainwizard.cpp +++ b/apps/wizard/mainwizard.cpp @@ -231,29 +231,13 @@ void Wizard::MainWizard::setupInstallations() void Wizard::MainWizard::runSettingsImporter() { + writeSettings(); + QString path(field(QLatin1String("installation.path")).toString()); - // Create the file if it doesn't already exist, else the importer will fail QString userPath(toQString(mCfgMgr.getUserConfigPath())); QFile file(userPath + QLatin1String("openmw.cfg")); - if (!file.exists()) { - if (!file.open(QIODevice::ReadWrite)) { - // File cannot be created - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Error writing OpenMW configuration file")); - msgBox.setIcon(QMessageBox::Critical); - msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("

Could not open or create %1 for writing

\ -

Please make sure you have the right permissions \ - and try again.

").arg(file.fileName())); - msgBox.exec(); - return qApp->quit(); - } - - file.close(); - } - // Construct the arguments to run the importer QStringList arguments;