diff --git a/src/api/loadorder.h b/src/api/loadorder.h index 7e96d5c..de90f48 100644 --- a/src/api/loadorder.h +++ b/src/api/loadorder.h @@ -36,14 +36,11 @@ * - The first plugin in the load order must be the game's main master file. * - Loads all master files before all plugin files. Master bit flag value, * rather than file extension, is checked. - * - Loads each plugin after all its masters. * - * Note that due to the complexity of satisfying the final condition above, - * libloadorder does not attempt to satisfy it when lo_fix_plugin_lists() is - * run. In addition, if the load order passed to lo_set_load_order() does - * not contain an entry for all installed plugins, then libloadorder must - * provide load order positions for any missing plugins itself, and these - * positions may not satisfy the final condition above. + * Note that libloadorder does not attempt to load each plugin after all + * its masters. Note also that if the load order passed to lo_set_load_order() + * does not contain an entry for all installed plugins, then libloadorder must + * provide load order positions for any missing plugins itself. */ #ifndef __LIBLO_LOAD_ORDER__ diff --git a/src/backend/plugins.cpp b/src/backend/plugins.cpp index 61d441b..1594964 100644 --- a/src/backend/plugins.cpp +++ b/src/backend/plugins.cpp @@ -74,16 +74,17 @@ namespace liblo { } bool Plugin::IsMasterFile(const _lo_game_handle_int& parentGame) const { + espm::File * file = nullptr; try { - espm::File * file = ReadHeader(parentGame); - + file = ReadHeader(parentGame); bool ret = file->isMaster(parentGame.espm_settings); - + isEsm = ret; delete file; return ret; } catch (std::exception&) { // TODO(ut): log it ! + if (file != nullptr) delete file; return false; } } @@ -93,7 +94,9 @@ namespace liblo { } bool Plugin::Exists(const _lo_game_handle_int& parentGame) const { - return (fs::exists(parentGame.PluginsFolder() / name) || fs::exists(parentGame.PluginsFolder() / fs::path(name + ".ghost"))); + exist = fs::exists(parentGame.PluginsFolder() / name) || + fs::exists(parentGame.PluginsFolder() / fs::path(name + ".ghost")); + return exist; } time_t Plugin::GetModTime(const _lo_game_handle_int& parentGame) const { @@ -176,6 +179,9 @@ namespace liblo { } } + bool Plugin::esm() const { return isEsm; } + bool Plugin::exists() const { return exist; } + ///////////////////////// // LoadOrder Members ///////////////////////// @@ -342,26 +348,32 @@ namespace liblo { if (empty()) return; - if (at(0) != Plugin(parentGame.MasterFile())) - throw error(LIBLO_WARN_INVALID_LIST, "\"" + parentGame.MasterFile() + "\" is not the first plugin in the load order. " + at(0).Name() + " is first."); + std::string msg = ""; + + Plugin masterEsm = Plugin(parentGame.MasterFile()); + if (at(0) != masterEsm) + msg += "\"" + masterEsm.Name() + "\" is not the first plugin in the load order. " + + at(0).Name() + " is first.\n"; - bool wasMaster = true; - unordered_set hashset; + bool wasMaster = at(0).IsMasterFile(parentGame); + unordered_set hashset; // check for duplicates for (const auto plugin : *this) { - if (!plugin.Exists(parentGame)) - throw error(LIBLO_WARN_INVALID_LIST, "\"" + plugin.Name() + "\" is not installed."); + if (hashset.find(plugin) != hashset.end()) { + msg += "\"" + plugin.Name() + "\" is in the load order twice.\n"; + if (plugin.exists()) wasMaster = plugin.esm(); + continue; + } else hashset.insert(plugin); + if (!plugin.Exists(parentGame)){ + msg += "\"" + plugin.Name() + "\" is not installed.\n"; + continue; + } + // plugin exists bool isMaster = plugin.IsMasterFile(parentGame); if (isMaster && !wasMaster) - throw error(LIBLO_WARN_INVALID_LIST, "Master plugin \"" + plugin.Name() + "\" loaded after a non-master plugin."); - if (hashset.find(plugin) != hashset.end()) - throw error(LIBLO_WARN_INVALID_LIST, "\"" + plugin.Name() + "\" is in the load order twice."); - for (const auto &master : plugin.GetMasters(parentGame)) { - if (hashset.find(master) == hashset.end() && this->Find(master) != this->end()) //Only complain about masters loading after the plugin if the master is installed (so that Filter patches do not cause false positives). This means libloadorder doesn't check to ensure all a plugin's masters are present, but I don't think it should get mixed up with Bash Tag detection. - throw error(LIBLO_WARN_INVALID_LIST, "\"" + plugin.Name() + "\" is loaded before one of its masters (\"" + master.Name() + "\")."); - } - hashset.insert(plugin); + msg += "Master plugin \"" + plugin.Name() + "\" loaded after a non-master plugin.\n"; wasMaster = isMaster; } + if (msg != "") throw error(LIBLO_WARN_INVALID_LIST, msg); } bool LoadOrder::HasChanged(const _lo_game_handle_int& parentGame) const { @@ -605,12 +617,6 @@ namespace liblo { throw error(LIBLO_WARN_INVALID_LIST, "\"" + plugin.Name() + "\" is not installed."); else if (!plugin.IsValid(parentGame)) throw error(LIBLO_WARN_INVALID_LIST, "\"" + plugin.Name() + "\" is not a valid plugin file."); - /*vector masters = plugin.GetMasters(parentGame); - //Disabled because it causes false positives for Filter patches. This means libloadorder doesn't check to ensure all a plugin's masters are active, but I don't think it should get mixed up with Bash Tag detection. - for (const auto& master: masters) { - if (this->find(master) == this->end()) - throw error(LIBLO_ERROR_INVALID_ARGS, "\"" + plugin.Name() + "\" has a master (\"" + master.Name() + "\") which isn't active."); - }*/ } if (size() > 255) diff --git a/src/backend/plugins.h b/src/backend/plugins.h index e72c651..e0b3b4d 100644 --- a/src/backend/plugins.h +++ b/src/backend/plugins.h @@ -56,9 +56,12 @@ namespace liblo { bool operator == (const Plugin& rhs) const; bool operator != (const Plugin& rhs) const; + bool esm() const; + bool exists() const; private: std::string name; - + mutable bool isEsm = false; + mutable bool exist = false; espm::File * ReadHeader(const _lo_game_handle_int& parentGame) const; };