diff --git a/src/backend/generators.cpp b/src/backend/generators.cpp index 4476eed4..7e9c44db 100644 --- a/src/backend/generators.cpp +++ b/src/backend/generators.cpp @@ -36,381 +36,25 @@ along with LOOT. If not, see using namespace std; namespace loot { - //LOOT Report generation stuff. - void GetOldReportDetails(const boost::filesystem::path& filepath, YAML::Node& node) { - if (boost::filesystem::exists(filepath)) { - BOOST_LOG_TRIVIAL(debug) << "Reading the previous report's details section."; - //Read the whole file in. - std::string contents; - loot::ifstream in(filepath, std::ios::binary); - in.seekg(0, std::ios::end); - contents.resize(in.tellg()); - in.seekg(0, std::ios::beg); - in.read(&contents[0], contents.size()); - in.close(); - - //Cut off non-JSON part of file. - contents = contents.substr(11); //"var data = ". - //Parse as YAML. - node = YAML::Load(contents); - if (node["plugins"]) { - node = node["plugins"]; - } - } - } - - bool AreDetailsEqual(const YAML::Node& lhs, const YAML::Node& rhs) { - //We want to check for plugin order and messages. - if (lhs.IsSequence() && rhs.IsSequence()) { - std::list lhs_names, rhs_names; - std::list lhs_messages, rhs_messages; - - for (const auto &element: lhs) { - if (element["name"]) - lhs_names.push_back(element["name"].as()); - if (element["messages"]) { - std::list messages = element["messages"].as< std::list >(); - lhs_messages.insert(lhs_messages.end(), messages.begin(), messages.end()); - } - } - for (const auto &element: rhs) { - if (element["name"]) - rhs_names.push_back(element["name"].as()); - if (element["messages"]) { - std::list messages = element["messages"].as< std::list >(); - rhs_messages.insert(rhs_messages.end(), messages.begin(), messages.end()); - } - } - - return lhs_names == rhs_names && lhs_messages == rhs_messages; - } - else + bool AreSettingsValid(const YAML::Node& settings) { + if (!settings["language"]) return false; - } - - void WriteMessage(YAML::Emitter& out, const Message& message) { - - //Look for Markdown URL syntax and convert any found. - regex reg("(\\[([^\\]]+)\\]\\s?\\(|<)((file|https?)://\\S+)(\\)|>)", regex::ECMAScript | regex::icase); // \2 is the label, \3 is the URL. - - std::match_results results; - std::string content = message.Content().front().Str(); - std::string converted; - std::string::iterator start, end; - start = content.begin(); - end = content.end(); - - while (regex_search(start, end, results, reg)) { - - //Get data from match. - std::string url, label; - if (results[3].matched) - url = std::string(results[3].first, results[3].second); - if (results[2].matched) - label = std::string(results[2].first, results[2].second); - else - label = url; - - //Insert normal text preceding hyperlink. - converted += std::string(results.prefix().first, results.prefix().second); - - //Insert hyperlink. - converted += "" + label + ""; - - //Set string to end of matched section. - start = results.suffix().first; - } - - //Insert any leftover text. - converted += std::string(start, end); - - //Now emit as YAML. - out << YAML::BeginMap; - - if (message.Type() == Message::say) { - out << YAML::Key << "type" << YAML::Value << "say"; - converted = boost::locale::translate("Note:").str() + " " + converted; - } - else if (message.Type() == Message::warn) { - out << YAML::Key << "type" << YAML::Value << "warn"; - converted = boost::locale::translate("Warning:").str() + " " + converted; - } - else { - out << YAML::Key << "type" << YAML::Value << "error"; - converted = boost::locale::translate("Error:").str() + " " + converted; - } - - out << YAML::Key << "content" << YAML::Value << converted; - - out << YAML::EndMap; - } - - void WritePlugin(YAML::Emitter& out, const Plugin& plugin, const Game& game) { - out << YAML::BeginMap; - - out << YAML::Key << "name" - << YAML::Value << plugin.Name(); - - out << YAML::Key << "isActive"; - if (game.IsActive(plugin.Name())) - out << YAML::Value << true; - else - out << YAML::Value << false; - - if (plugin.Crc() != 0) { - out << YAML::Key << "crc" - << YAML::Value << IntToHexString(plugin.Crc()); - } - - if (!plugin.Version().empty()) { - out << YAML::Key << "version" - << YAML::Value << boost::locale::translate("Version").str() + ": " + plugin.Version(); - } - - std::set tags = plugin.Tags(); - std::set tagsAdd, tagsRemove; - if (!tags.empty()) { - for (const auto &tag: tags) { - if (tag.IsAddition()) - tagsAdd.insert(Tag(tag.Name())); // Remove condition. - else - tagsRemove.insert(Tag(tag.Name(), false)); // Remove condition. - } - if (!tagsAdd.empty()) { - out << YAML::Key << "tagsAdd" - << YAML::Value << tagsAdd; - } - if (!tagsRemove.empty()) { - out << YAML::Key << "tagsRemove" - << YAML::Value << tagsRemove; - } - } - - std::list messages = plugin.Messages(); - std::set dirtyInfo = plugin.DirtyInfo(); - size_t numDirtyInfo(0); - for (const auto &element: dirtyInfo) { - boost::format f; - if (element.ITMs() > 0 && element.UDRs() > 0 && element.DeletedNavmeshes() > 0) - f = boost::format(boost::locale::translate("Contains %1% ITM records, %2% UDR records and %3% deleted navmeshes. Clean with %4%.")) % element.ITMs() % element.UDRs() % element.DeletedNavmeshes() % element.CleaningUtility(); - else if (element.ITMs() == 0 && element.UDRs() == 0 && element.DeletedNavmeshes() == 0) - f = boost::format(boost::locale::translate("Clean with %1%.")) % element.CleaningUtility(); - - - else if (element.ITMs() == 0 && element.UDRs() > 0 && element.DeletedNavmeshes() > 0) - f = boost::format(boost::locale::translate("Contains %1% UDR records and %2% deleted navmeshes. Clean with %3%.")) % element.UDRs() % element.DeletedNavmeshes() % element.CleaningUtility(); - else if (element.ITMs() == 0 && element.UDRs() == 0 && element.DeletedNavmeshes() > 0) - f = boost::format(boost::locale::translate("Contains %1% deleted navmeshes. Clean with %2%.")) % element.DeletedNavmeshes() % element.CleaningUtility(); - else if (element.ITMs() == 0 && element.UDRs() > 0 && element.DeletedNavmeshes() == 0) - f = boost::format(boost::locale::translate("Contains %1% UDR records. Clean with %2%.")) % element.UDRs() % element.CleaningUtility(); - - else if (element.ITMs() > 0 && element.UDRs() == 0 && element.DeletedNavmeshes() > 0) - f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% deleted navmeshes. Clean with %3%.")) % element.ITMs() % element.DeletedNavmeshes() % element.CleaningUtility(); - else if (element.ITMs() > 0 && element.UDRs() == 0 && element.DeletedNavmeshes() == 0) - f = boost::format(boost::locale::translate("Contains %1% ITM records. Clean with %2%.")) % element.ITMs() % element.CleaningUtility(); - - else if (element.ITMs() > 0 && element.UDRs() > 0 && element.DeletedNavmeshes() == 0) - f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% UDR records. Clean with %3%.")) % element.ITMs() % element.UDRs() % element.CleaningUtility(); - - messages.push_back(loot::Message(loot::Message::warn, f.str())); - ++numDirtyInfo; - } - - //Also add some metadata so that the summary can count the total number of cleaning messages. - out << YAML::Key << "isDirty"; - if (numDirtyInfo > 0) - out << YAML::Value << true; - else - out << YAML::Value << false; - - if (!messages.empty()) { - out << YAML::Key << "messages" - << YAML::Value << YAML::BeginSeq; - for (const auto &message: messages) { - WriteMessage(out, message); - } - out << YAML::EndSeq; - } - - out << YAML::EndMap; - } - - void GenerateReportData(const Game& game, - std::list& messages, - const std::list& plugins, - const std::string& masterlistVersion, - const std::string& masterlistDate, - const bool masterlistUpdateEnabled) { - - YAML::Node oldDetails; - GetOldReportDetails(game.ReportDataPath(), oldDetails); - - //Need to output YAML as a JSON Javascript variable. - YAML::Emitter yout; - yout.SetOutputCharset(YAML::EscapeNonAscii); - yout.SetStringFormat(YAML::DoubleQuoted); - yout.SetBoolFormat(YAML::TrueFalseBool); - yout.SetSeqFormat(YAML::Flow); - yout.SetMapFormat(YAML::Flow); - - BOOST_LOG_TRIVIAL(debug) << "Generating JSON report data."; - - yout << YAML::BeginMap; - - yout << YAML::Key << "lootVersion" - << YAML::Value << std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch); - - yout << YAML::Key << "masterlist" - << YAML::BeginMap - << YAML::Key << "updaterEnabled" - << YAML::Value; - - if (masterlistUpdateEnabled) - yout << boost::locale::translate("Enabled").str(); - else - yout << boost::locale::translate("Disabled").str(); - - yout << YAML::Key << "revision" - << YAML::Value << masterlistVersion - << YAML::Key << "date" - << YAML::Value << masterlistDate - << YAML::EndMap; - - if (!plugins.empty()) { - BOOST_LOG_TRIVIAL(debug) << "Generating JSON plugin data."; - YAML::Emitter tempout; - tempout.SetOutputCharset(YAML::EscapeNonAscii); - tempout.SetStringFormat(YAML::DoubleQuoted); - tempout.SetBoolFormat(YAML::TrueFalseBool); - tempout.SetSeqFormat(YAML::Flow); - tempout.SetMapFormat(YAML::Flow); - - tempout << YAML::BeginSeq; - for (const auto &plugin: plugins) { - WritePlugin(tempout, plugin, game); - } - tempout << YAML::EndSeq; - - YAML::Node node = YAML::Load(tempout.c_str()); - - if (AreDetailsEqual(node, oldDetails)) - messages.push_front(loot::Message(loot::Message::say, boost::locale::translate("There have been no changes in the Details tab since LOOT was last run for this game.").str())); - - //Need to generate output twice because passing the node causes ! to be written before every key and value for some reason. - yout << YAML::Key << "plugins" - << YAML::Value << YAML::BeginSeq; - for (const auto &plugin: plugins) { - WritePlugin(yout, plugin, game); - } - yout << YAML::EndSeq; - } - else if (oldDetails.size() == 0) - messages.push_front(loot::Message(loot::Message::say, boost::locale::translate("There have been no changes in the Details tab since LOOT was last run for this game.").str())); - - if (!messages.empty()) { - BOOST_LOG_TRIVIAL(debug) << "Generating JSON general message data."; - yout << YAML::Key << "globalMessages" - << YAML::Value << YAML::BeginSeq; - for (const auto &message: messages) { - WriteMessage(yout, message); - } - yout << YAML::EndSeq; - } - - BOOST_LOG_TRIVIAL(debug) << "Generating text translations."; - yout << YAML::Key << "l10n" - << YAML::BeginMap - - << YAML::Key << "txtSummarySec" - << YAML::Value << boost::locale::translate("Summary").str() - - << YAML::Key << "txtSummary" - << YAML::Value << boost::locale::translate("Version Information").str() - - << YAML::Key << "txtLootVersion" - << YAML::Value << boost::locale::translate("LOOT Version").str() - - << YAML::Key << "txtMasterlistRevision" - << YAML::Value << boost::locale::translate("Masterlist Revision").str() - - << YAML::Key << "txtMasterlistDate" - << YAML::Value << boost::locale::translate("Masterlist Date").str() - - << YAML::Key << "txtMasterlistUpdating" - << YAML::Value << boost::locale::translate("Masterlist Updating").str() - - << YAML::Key << "txtCounts" - << YAML::Value << boost::locale::translate("Plugin & Message Counts").str() - - << YAML::Key << "txtTotalPluginNo" - << YAML::Value << boost::locale::translate("Total Plugins").str() - - << YAML::Key << "txtActivePluginNo" - << YAML::Value << boost::locale::translate("Active Plugins").str() - - << YAML::Key << "txtDirtyPluginNo" - << YAML::Value << boost::locale::translate("Dirty Plugins").str() - - << YAML::Key << "txtTotalMessageNo" - << YAML::Value << boost::locale::translate("Total Messages").str() - - << YAML::Key << "txtTotalWarningNo" - << YAML::Value << boost::locale::translate("Warnings").str() - - << YAML::Key << "txtTotalErrorNo" - << YAML::Value << boost::locale::translate("Errors").str() - - << YAML::Key << "txtGeneralMessages" - << YAML::Value << boost::locale::translate("General Messages").str() - - << YAML::Key << "txtDetailsSec" - << YAML::Value << boost::locale::translate("Details").str() - - << YAML::Key << "filtersToggle" - << YAML::Value << boost::locale::translate("Filters").str() - - << YAML::Key << "txtHideVersionNumbers" - << YAML::Value << boost::locale::translate("Hide Version Numbers").str() - - << YAML::Key << "txtHideCRCs" - << YAML::Value << boost::locale::translate("Hide CRCs").str() - - << YAML::Key << "txtHideBashTags" - << YAML::Value << boost::locale::translate("Hide Bash Tag Suggestions").str() - - << YAML::Key << "txtHideNotes" - << YAML::Value << boost::locale::translate("Hide Notes").str() - - << YAML::Key << "txtHideDoNotCleanMessages" - << YAML::Value << boost::locale::translate("Hide 'Do Not Clean' Messages").str() - - << YAML::Key << "txtHideInactivePluginMessages" - << YAML::Value << boost::locale::translate("Hide Inactive Plugin Messages").str() - - << YAML::Key << "txtHideAllPluginMessages" - << YAML::Value << boost::locale::translate("Hide All Plugin Messages").str() - - << YAML::Key << "txtHideMessagelessPlugins" - << YAML::Value << boost::locale::translate("Hide Messageless Plugins").str() - - << YAML::Key << "txtPluginsHidden" - << YAML::Value << boost::locale::translate("Plugins hidden").str() - - << YAML::Key << "txtMessagesHidden" - << YAML::Value << boost::locale::translate("Messages hidden").str() - - << YAML::EndMap; - - yout << YAML::EndMap; - - loot::ofstream out(game.ReportDataPath()); - out << "var data = " << yout.c_str(); - out.close(); + if (!settings["game"]) + return false; + if (!settings["lastGame"]) + return false; + if (!settings["debugVerbosity"]) + return false; + if (!settings["updateMasterlist"]) + return false; + if (!settings["games"]) + return false; + + return true; } //Default settings file generation. void GenerateDefaultSettingsFile(const boost::filesystem::path& file) { - BOOST_LOG_TRIVIAL(info) << "Generating default settings file."; YAML::Node root; diff --git a/src/backend/generators.h b/src/backend/generators.h index 93e160bf..1493b51a 100644 --- a/src/backend/generators.h +++ b/src/backend/generators.h @@ -33,13 +33,9 @@ #include namespace loot { - //LOOT Report generation stuff. - void GenerateReportData(const Game& game, - std::list& messages, - const std::list& plugins, - const std::string& masterlistVersion, - const std::string& masterlistDate, - const bool masterlistUpdateEnabled); + // Check if the settings file has the right root keys (doesn't check + // their values). + bool AreSettingsValid(const YAML::Node& settings); //Default settings file generation. void GenerateDefaultSettingsFile(const boost::filesystem::path& file); diff --git a/src/gui/app.cpp b/src/gui/app.cpp index e8ab315f..cf2d3e63 100644 --- a/src/gui/app.cpp +++ b/src/gui/app.cpp @@ -120,16 +120,18 @@ namespace loot { string initError; //Load settings. - if (!fs::exists(g_path_settings)) { + if (!fs::exists(g_path_settings) || !AreSettingsValid(_settings)) { + BOOST_LOG_TRIVIAL(error) << "Settings file invalid, or doesn't exist, generating new file."; try { - if (!fs::exists(g_path_settings.parent_path())) - fs::create_directory(g_path_settings.parent_path()); + fs::create_directory(g_path_settings.parent_path()); + GenerateDefaultSettingsFile(g_path_settings); } catch (fs::filesystem_error& /*e*/) { initError = "Error: Could not create local app data LOOT folder."; } - GenerateDefaultSettingsFile(g_path_settings); } + + BOOST_LOG_TRIVIAL(info) << "Settings file found, parsing..."; try { loot::ifstream in(g_path_settings); _settings = YAML::Load(in);