diff --git a/src/api/api.cpp b/src/api/api.cpp index caeaeb04..59000490 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -88,13 +88,16 @@ const unsigned int loot_needs_cleaning_no = 0; const unsigned int loot_needs_cleaning_yes = 1; const unsigned int loot_needs_cleaning_unknown = 2; -struct _loot_db_int { - _loot_db_int() - : extTagMap(nullptr), +struct _loot_db_int : public loot::Game { + _loot_db_int(const unsigned int clientGame, const std::string gamePath) + : Game(clientGame), + extTagMap(nullptr), extAddedTagIds(nullptr), extRemovedTagIds(nullptr), extMessageArray(nullptr), - extMessageArraySize(0) {} + extMessageArraySize(0) { + this->SetDetails("", "", "", "", gamePath, "").Init(); + } ~_loot_db_int() { delete[] extAddedTagIds; @@ -113,8 +116,8 @@ struct _loot_db_int { } } - loot::Game game; - std::list metadata, rawMetadata, userMetadata, rawUserMetadata; + loot::MetadataList rawUserMetadata; + loot::Masterlist rawMetadata; std::unordered_map bashTagMap; @@ -229,10 +232,8 @@ LOOT_API unsigned int loot_create_db(loot_db * const db, const unsigned int clie return c_error(loot_error_invalid_args, "Null pointer passed."); //Set the locale to get encoding conversions working correctly. - std::setlocale(LC_CTYPE, ""); - std::locale global_loc = std::locale(); - std::locale loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet()); - boost::filesystem::path::imbue(loc); + std::locale::global(boost::locale::generator().generate("")); + boost::filesystem::path::imbue(std::locale()); //Disable logging or else stdout will get overrun. boost::log::core::get()->set_logging_enabled(false); @@ -241,22 +242,16 @@ LOOT_API unsigned int loot_create_db(loot_db * const db, const unsigned int clie if (gamePath != nullptr) game_path = gamePath; - loot::Game game; + loot_db retVal ={ 0 }; try { - game = loot::Game(clientGame).SetPath(game_path).Init(); //This also checks to see if the game is installed if game_path is empty and throws an exception if it is not detected. It also creates a folder in %LOCALAPPDATA% and reads the active plugins list, but that shouldn't be an issue. + retVal = new _loot_db_int(clientGame, game_path); } catch (loot::error& e) { return c_error(e); } - - loot_db retVal; - try { - retVal = new _loot_db_int; - } catch (std::bad_alloc& e) { return c_error(loot_error_no_mem, e.what()); } - retVal->game = game; *db = retVal; return loot_ok; @@ -280,15 +275,13 @@ LOOT_API unsigned int loot_load_lists(loot_db db, const char * const masterlistP if (db == nullptr || masterlistPath == nullptr) return c_error(loot_error_invalid_args, "Null pointer passed."); - std::list temp; - std::list userTemp; + loot::Masterlist temp; + loot::MetadataList userTemp; try { if (boost::filesystem::exists(masterlistPath)) { - loot::ifstream in(masterlistPath); - YAML::Node tempNode = YAML::Load(in); - in.close(); - temp = tempNode["plugins"].as< std::list >(); + // We don't want to update the masterlist too. + temp.MetadataList::Load(masterlistPath); } } catch (std::exception& e) { @@ -298,12 +291,7 @@ LOOT_API unsigned int loot_load_lists(loot_db db, const char * const masterlistP try { if (userlistPath != nullptr) { if (boost::filesystem::exists(userlistPath)) { - if (boost::algorithm::iends_with(userlistPath, ".yaml")) { - loot::ifstream in(userlistPath); - YAML::Node tempNode = YAML::Load(in); - in.close(); - userTemp = tempNode["plugins"].as< std::list >(); - } + userTemp.Load(masterlistPath); } } } @@ -333,9 +321,9 @@ LOOT_API unsigned int loot_load_lists(loot_db db, const char * const masterlistP db->extTagMap = nullptr; db->extMessageArray = nullptr; + db->masterlist = temp; db->rawMetadata = temp; - db->metadata = temp; - db->userMetadata = userTemp; + db->userlist = userTemp; db->rawUserMetadata = userTemp; return loot_ok; @@ -351,72 +339,23 @@ LOOT_API unsigned int loot_eval_lists(loot_db db, const unsigned int language) { if (db == nullptr) return c_error(loot_error_invalid_args, "Null pointer passed."); - std::list temp = db->rawMetadata; - try { - db->game.RefreshActivePluginsList(); - for (auto it=temp.begin(); it != temp.end();) { - it->EvalAllConditions(db->game, language); - if (it->IsRegexPlugin()) { - std::regex reg; - try { - reg = std::regex(it->Name(), std::regex::ECMAScript | std::regex::icase); - } - catch (std::exception& e) { - return c_error(loot_error_regex_eval_fail, e.what()); - } + // Clear caches before evaluating conditions. + db->conditionCache.clear(); + db->crcCache.clear(); - for (boost::filesystem::directory_iterator itr(db->game.DataPath()); itr != boost::filesystem::directory_iterator(); ++itr) { - const std::string filename = itr->path().filename().string(); - if (std::regex_match(filename, reg)) { - loot::Plugin p = *it; - p.Name(filename); - temp.push_back(p); - } - } - it = temp.erase(it); - } - else { - ++it; - } - } + loot::Masterlist temp = db->rawMetadata; + loot::MetadataList userTemp = db->rawUserMetadata; + try { + // Refresh active plugins before evaluating conditions. + db->RefreshActivePluginsList(); + temp.EvalAllConditions(*db, language); + userTemp.EvalAllConditions(*db, language); } catch (loot::error& e) { return c_error(e); } - db->metadata = temp; - - temp = db->rawUserMetadata; - try { - for (auto it=temp.begin(); it != temp.end();) { - it->EvalAllConditions(db->game, language); - if (it->IsRegexPlugin()) { - std::regex reg; - try { - reg = std::regex(it->Name(), std::regex::ECMAScript | std::regex::icase); - } - catch (std::exception& e) { - return c_error(loot_error_regex_eval_fail, e.what()); - } - - for (boost::filesystem::directory_iterator itr(db->game.DataPath()); itr != boost::filesystem::directory_iterator(); ++itr) { - const std::string filename = itr->path().filename().string(); - if (std::regex_match(filename, reg)) { - loot::Plugin p = *it; - p.Name(filename); - temp.push_back(p); - } - } - it = temp.erase(it); - } - else { - ++it; - } - } - } - catch (loot::error& e) { - return c_error(e); - } - db->userMetadata = temp; + db->masterlist = temp; + db->userlist = userTemp; return loot_ok; } @@ -447,13 +386,13 @@ LOOT_API unsigned int loot_get_tag_map(loot_db db, char *** const tagMap, size_t std::unordered_set allTags; - for (const auto &plugin : db->metadata) { + for (const auto &plugin : db->masterlist.Plugins()) { std::set tags(plugin.Tags()); for (const auto &tag : tags) { allTags.insert(tag.Name()); } } - for (const auto &plugin : db->userMetadata) { + for (const auto &plugin : db->userlist.Plugins()) { std::set tags(plugin.Tags()); for (const auto &tag : tags) { allTags.insert(tag.Name()); @@ -519,10 +458,9 @@ LOOT_API unsigned int loot_get_plugin_tags(loot_db db, const char * const plugin *numTags_removed = 0; std::unordered_set tagsAdded, tagsRemoved; - std::list::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->metadata.end()) { - std::set tags(pluginIt->Tags()); - for (const auto &tag : tags) { + loot::Plugin p = db->masterlist.FindPlugin(loot::Plugin(plugin)); + if (!p.HasNameOnly()) { + for (const auto &tag : p.Tags()) { if (tag.IsAddition()) tagsAdded.insert(tag.Name()); else @@ -530,11 +468,10 @@ LOOT_API unsigned int loot_get_plugin_tags(loot_db db, const char * const plugin } } - pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->userMetadata.end()) { + p = db->userlist.FindPlugin(loot::Plugin(plugin)); + if (!p.HasNameOnly()) { *userlistModified = true; - std::set tags(pluginIt->Tags()); - for (const auto &tag : tags) { + for (const auto &tag : p.Tags()) { if (tag.IsAddition()) tagsAdded.insert(tag.Name()); else @@ -608,17 +545,12 @@ LOOT_API unsigned int loot_get_plugin_messages(loot_db db, const char * const pl *messages = nullptr; *numMessages = 0; - std::list pluginMessages; - std::list::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->metadata.end()) { - pluginMessages = pluginIt->Messages(); - } + loot::Plugin p = db->masterlist.FindPlugin(loot::Plugin(plugin)); + std::list pluginMessages(p.Messages()); - pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->userMetadata.end()) { - std::list temp = pluginIt->Messages(); - pluginMessages.insert(pluginMessages.end(), temp.begin(), temp.end()); - } + p = db->userlist.FindPlugin(loot::Plugin(plugin)); + std::list temp(p.Messages()); + pluginMessages.insert(pluginMessages.end(), temp.begin(), temp.end()); db->extMessageArraySize = pluginMessages.size(); try { @@ -646,16 +578,12 @@ LOOT_API unsigned int loot_get_dirty_info(loot_db db, const char * const plugin, *needsCleaning = loot_needs_cleaning_unknown; //Get all dirty info. - std::set dirtyInfo; - std::list::iterator pluginIt = std::find(db->metadata.begin(), db->metadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->metadata.end()) { - dirtyInfo = pluginIt->DirtyInfo(); - } - pluginIt = std::find(db->userMetadata.begin(), db->userMetadata.end(), loot::Plugin(plugin)); - if (pluginIt != db->userMetadata.end()) { - std::set temp = pluginIt->DirtyInfo(); - dirtyInfo.insert(temp.begin(), temp.end()); - } + loot::Plugin p = db->masterlist.FindPlugin(loot::Plugin(plugin)); + std::set dirtyInfo(p.DirtyInfo()); + + p = db->userlist.FindPlugin(loot::Plugin(plugin)); + std::set temp(p.DirtyInfo()); + dirtyInfo.insert(temp.begin(), temp.end()); if (!dirtyInfo.empty()) { *needsCleaning = loot_needs_cleaning_yes; @@ -678,19 +606,19 @@ LOOT_API unsigned int loot_write_minimal_list(loot_db db, const char * const out if (boost::filesystem::exists(outputFile) && !overwrite) return c_error(loot_error_invalid_args, "Output file exists but overwrite is not set to true."); - std::list temp = db->metadata; - for (auto &plugin : temp) { + loot::Masterlist temp = db->masterlist; + std::unordered_set minimalPlugins; + for (const auto &plugin : temp.Plugins()) { loot::Plugin p(plugin.Name()); p.Tags(plugin.Tags()); p.DirtyInfo(plugin.DirtyInfo()); - - plugin = p; + minimalPlugins.insert(p); } YAML::Emitter yout; yout.SetIndent(2); yout << YAML::BeginMap - << YAML::Key << "plugins" << YAML::Value << temp + << YAML::Key << "plugins" << YAML::Value << minimalPlugins << YAML::EndMap; boost::filesystem::path p(outputFile); diff --git a/src/backend/game.cpp b/src/backend/game.cpp index 0892806f..c8f51644 100644 --- a/src/backend/game.cpp +++ b/src/backend/game.cpp @@ -216,6 +216,22 @@ namespace loot { } } + void MetadataList::EvalAllConditions(Game& game, const unsigned int language) { + unordered_set replacementSet; + for (auto &plugin : plugins) { + Plugin p(plugin); + p.EvalAllConditions(game, language); + replacementSet.insert(p); + } + plugins = replacementSet; + for (auto &plugin : regexPlugins) { + plugin.EvalAllConditions(game, language); + } + for (auto &message : messages) { + message.EvalCondition(game, language); + } + } + // Masterlist member functions //---------------------------- @@ -321,12 +337,6 @@ namespace loot { return *this; } - Game& Game::SetPath(const std::string& path) { - gamePath = path; - - return *this; - } - Game& Game::Init() { if (id != Game::tes4 && id != Game::tes5 && id != Game::fo3 && id != Game::fonv) { throw error(error::invalid_args, lc::translate("Invalid game ID supplied.").str()); diff --git a/src/backend/game.h b/src/backend/game.h index 9d282fcf..c628739c 100644 --- a/src/backend/game.h +++ b/src/backend/game.h @@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License along with LOOT. If not, see . -*/ + */ #ifndef __LOOT_GAME__ #define __LOOT_GAME__ @@ -41,7 +41,6 @@ #include namespace loot { - class Game; /* Each Game object should store the config details specific to that game. @@ -52,7 +51,7 @@ namespace loot { global message lists. Each game should have functions to load this plugin and masterlist / userlist data. Plugin data should be loaded as header-only and as full data. - */ + */ class MetadataList { public: @@ -67,6 +66,9 @@ namespace loot { void AddPlugin(const Plugin& plugin); void ErasePlugin(const Plugin& plugin); + // Eval plugin conditions. + void EvalAllConditions(Game& game, const unsigned int language); + std::list messages; protected: std::unordered_set plugins; @@ -96,9 +98,8 @@ namespace loot { Game(const unsigned int baseGameCode, const std::string& lootFolder = ""); Game& SetDetails(const std::string& name, const std::string& masterFile, - const std::string& repositoryURL, const std::string& repositoryBranch, - const std::string& path, const std::string& registry); - Game& SetPath(const std::string& path); //Used by API. + const std::string& repositoryURL, const std::string& repositoryBranch, + const std::string& path, const std::string& registry); Game& Init(); bool IsInstalled() const;