diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3144b2bb..537b0031 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/network.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/globals.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/graph.cpp"
+ "${CMAKE_SOURCE_DIR}/src/backend/generators.cpp"
"${PROJECT_LIBS_DIR}/pugixml/src/pugixml.cpp")
set (LOOT_GUI_SRC ${LOOT_SRC}
diff --git a/src/backend/generators.cpp b/src/backend/generators.cpp
new file mode 100644
index 00000000..36cce6d8
--- /dev/null
+++ b/src/backend/generators.cpp
@@ -0,0 +1,773 @@
+/* LOOT
+
+A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
+Fallout: New Vegas.
+
+Copyright (C) 2013-2014 WrinklyNinja
+
+This file is part of LOOT.
+
+LOOT is free software: you can redistribute
+it and/or modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
+
+LOOT is distributed in the hope that it will
+be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with LOOT. If not, see
+.
+*/
+
+#include "generators.h"
+
+namespace loot {
+ //LOOT Report generation stuff.
+ std::string GetOldReportDetails(const boost::filesystem::path& filepath) {
+ 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();
+
+ //Slim down to only the details section.
+ size_t pos1 = contents.find("
", pos1);
+ if (pos1 != std::string::npos) {
+ size_t pos2 = contents.find("
", pos1);
+ if (pos2 != std::string::npos) {
+ pos2 -= 6; // "
"
+ return contents.substr(pos1, pos2 - pos1);
+ }
+ }
+ }
+ }
+ return "";
+ }
+
+ struct xml_string_writer : pugi::xml_writer {
+ std::string result;
+
+ virtual void write(const void* data, size_t size) {
+ result += std::string(static_cast
(data), size);
+ }
+ };
+
+ void WriteMessage(pugi::xml_node& listItem, unsigned int type, std::string content) {
+
+ if (type == Message::say)
+ content = boost::locale::translate("Note:").str() + " " + content;
+ else if (type == Message::tag) {
+ content = boost::locale::translate("Bash Tag Suggestion(s):").str() + " " + content;
+ }
+ else if (type == Message::warn)
+ content = boost::locale::translate("Warning:").str() + " " + content;
+ else
+ content = boost::locale::translate("Error:").str() + " " + content;
+
+ //Now look for Markdown URL syntax and convert any found.
+ boost::regex regex("(\\[([^\\]]+)\\]\\s?\\(|<)((file|https?)://\\S+)(\\)|>)", boost::regex::perl | boost::regex::icase); // \2 is the label, \3 is the URL.
+
+ boost::match_results results;
+ std::string::iterator start, end;
+ start = content.begin();
+ end = content.end();
+
+ while (boost::regex_search(start, end, results, regex)) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Results size: " << results.size();
+
+ //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.
+ listItem.append_child(pugi::node_pcdata).set_value(std::string(results.prefix().first, results.prefix().second).c_str());
+
+ //Insert hyperlink.
+ pugi::xml_node a = listItem.append_child();
+ a.set_name("a");
+ a.append_attribute("href").set_value(url.c_str());
+ a.text().set(label.c_str());
+
+ //Set string to end of matched section.
+ start = results.suffix().first;
+ }
+
+ //Insert any leftover text.
+ listItem.append_child(pugi::node_pcdata).set_value(std::string(start, end).c_str());
+
+ }
+
+ void GenerateHead(pugi::xml_document& doc) {
+ BOOST_LOG_TRIVIAL(trace) << "Creating LOOT report head.";
+
+ //Add DOCTYPE node.
+ doc.append_child(pugi::node_doctype).set_value("html");
+
+ //Create 'head' node.
+ pugi::xml_node head = doc.append_child();
+ head.set_name("head");
+
+ //Now add head elements.
+ pugi::xml_node node;
+
+ node = head.append_child();
+ node.set_name("meta");
+ node.append_attribute("http-equiv").set_value("X-UA-Compatible");
+ node.append_attribute("content").set_value("IE=edge");
+
+ node = head.append_child();
+ node.set_name("meta");
+ node.append_attribute("charset").set_value("utf-8");
+
+ node = head.append_child();
+ node.set_name("title");
+ node.text().set(boost::locale::translate("LOOT Report").str().c_str());
+
+ node = head.append_child();
+ node.set_name("link");
+ node.append_attribute("rel").set_value("stylesheet");
+ node.append_attribute("href").set_value(ToFileURL(g_path_css).c_str());
+
+ node = head.append_child();
+ node.set_name("script");
+ node.append_attribute("src").set_value(ToFileURL(g_path_polyfill).c_str());
+ node.text().set(" ");
+ }
+
+ void AppendNav(pugi::xml_node& body) {
+ BOOST_LOG_TRIVIAL(trace) << "Appending navigation bar to LOOT report.";
+
+ pugi::xml_node nav, div;
+
+ nav = body.append_child();
+ nav.set_name("div");
+ nav.append_attribute("id").set_value("nav");
+
+ div = nav.append_child();
+ div.set_name("div");
+ div.append_attribute("class").set_value("button current");
+ div.append_attribute("data-section").set_value("summary");
+ div.text().set(boost::locale::translate("Summary").str().c_str());
+
+ div = nav.append_child();
+ div.set_name("div");
+ div.append_attribute("class").set_value("button");
+ div.append_attribute("data-section").set_value("plugins");
+ div.text().set(boost::locale::translate("Details").str().c_str());
+
+ div = nav.append_child();
+ div.set_name("div");
+ div.append_attribute("class").set_value("button hidden");
+ div.append_attribute("id").set_value("filtersToggle");
+ div.text().set(boost::locale::translate("Filters").str().c_str());
+ }
+
+ void AppendMessages(pugi::xml_node& parent, const std::list& messages, int& warnNo, int& errorNo) {
+ if (!messages.empty()) {
+ pugi::xml_node list = parent.append_child();
+ list.set_name("ul");
+
+ for (std::list::const_iterator it = messages.begin(), endit = messages.end(); it != endit; ++it) {
+
+ pugi::xml_node li = list.append_child();
+ li.set_name("li");
+
+ if (it->Type() == Message::say)
+ li.append_attribute("class").set_value("say");
+ else if (it->Type() == Message::tag)
+ li.append_attribute("class").set_value("tag");
+ else if (it->Type() == Message::warn) {
+ li.append_attribute("class").set_value("warn");
+ ++warnNo;
+ }
+ else {
+ li.append_attribute("class").set_value("error");
+ ++errorNo;
+ }
+
+ //Turn any urls into hyperlinks.
+ WriteMessage(li, it->Type(), it->Content().front().Str());
+ }
+ }
+ }
+
+ void AppendSummary(pugi::xml_node& main,
+ bool hasChanged,
+ const std::string& masterlistVersion,
+ bool masterlistUpdateEnabled,
+ int messageNo,
+ int warnNo,
+ int errorNo,
+ std::list& messages) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Appending summary tab to LOOT report.";
+
+ pugi::xml_node summary = main.append_child();
+ summary.set_name("div");
+ summary.append_attribute("id").set_value("summary");
+
+ pugi::xml_node div = summary.append_child();
+ div.set_name("div");
+
+ pugi::xml_node heading = div.append_child();
+ heading.set_name("h2");
+ heading.text().set(boost::locale::translate("Summary").str().c_str());
+
+ pugi::xml_node table = div.append_child();
+ table.set_name("table");
+ table = table.append_child();
+ table.set_name("tbody");
+
+ pugi::xml_node row, cell;
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("LOOT Version").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set((IntToString(g_version_major) + "." + IntToString(g_version_minor) + "." + IntToString(g_version_patch)).c_str());
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("Masterlist Version").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(masterlistVersion.c_str());
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("Masterlist Updating").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ if (masterlistUpdateEnabled)
+ cell.text().set(boost::locale::translate("Enabled").str().c_str());
+ else
+ cell.text().set(boost::locale::translate("Disabled").str().c_str());
+
+ if (!hasChanged) {
+ BOOST_LOG_TRIVIAL(info) << "No changes in the LOOT report details tab since the last run.";
+ /*pugi::xml_node note = summary.append_child();
+ note.set_name("div");
+ note.append_attribute("id").set_value("noChanges");
+ note.text().set(boost::locale::translate("No change in details since last run.").str().c_str());
+ */
+ 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()) {
+
+ div = summary.append_child();
+ div.set_name("div");
+
+ heading = div.append_child();
+ heading.set_name("h2");
+ heading.text().set(boost::locale::translate("General Messages").str().c_str());
+
+ AppendMessages(div, messages, warnNo, errorNo);
+ messageNo += messages.size();
+ }
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("Total Number of Messages").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(IntToString(messageNo).c_str());
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("Number of Warnings").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(IntToString(warnNo).c_str());
+
+ row = table.append_child();
+ row.set_name("tr");
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(boost::locale::translate("Number of Errors").str().c_str());
+ cell = row.append_child();
+ cell.set_name("td");
+ cell.text().set(IntToString(errorNo).c_str());
+ }
+
+ bool AppendDetails(pugi::xml_node& main, const std::list& plugins, int& messageNo, int& warnNo, int& errorNo, const std::string& oldDetails) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Appending details tab to LOOT report.";
+
+ pugi::xml_node details = main.append_child();
+ details.set_name("div");
+ details.append_attribute("id").set_value("plugins");
+ details.append_attribute("class").set_value("hidden");
+
+ if (!plugins.empty()) {
+
+ details = details.append_child();
+ details.set_name("ul");
+
+ for (std::list::const_iterator it = plugins.begin(), endit = plugins.end(); it != endit; ++it) {
+ BOOST_LOG_TRIVIAL(trace) << "Appending details for plugin: " << it->Name();
+ pugi::xml_node plugin = details.append_child();
+ plugin.set_name("li");
+
+ pugi::xml_node node = plugin.append_child();
+ node.set_name("span");
+ node.append_attribute("class").set_value("mod");
+ node.text().set(it->Name().c_str());
+
+ if (it->Crc() != 0) {
+ node = plugin.append_child();
+ node.set_name("div");
+ node.append_attribute("class").set_value("crc");
+ node.text().set(("CRC: " + IntToHexString(it->Crc())).c_str());
+ }
+
+ if (!it->Version().empty()) {
+ node = plugin.append_child();
+ node.set_name("div");
+ node.append_attribute("class").set_value("version");
+ node.text().set((boost::locale::translate("Version:").str() + " " + it->Version()).c_str());
+ }
+
+ std::list messages = it->Messages();
+ messageNo += messages.size();
+
+ std::set tags = it->Tags();
+
+ if (!tags.empty()) {
+ std::string add, remove, content;
+ for (std::set::const_iterator jt = tags.begin(), endjt = tags.end(); jt != endjt; ++jt) {
+ if (jt->IsAddition())
+ add += ", " + jt->Name();
+ else
+ remove += ", " + jt->Name();
+ }
+ if (!add.empty()) {
+ node = plugin.append_child();
+ node.set_name("div");
+ node.append_attribute("class").set_value("tag add");
+ node.text().set((boost::locale::translate("Add:").str() + " " + add.substr(2)).c_str());
+ }
+ if (!remove.empty()) {
+ node = plugin.append_child();
+ node.set_name("div");
+ node.append_attribute("class").set_value("tag remove");
+ node.text().set((boost::locale::translate("Remove:").str() + " " + remove.substr(2)).c_str());
+ }
+ }
+
+ std::set dirtyInfo = it->DirtyInfo();
+ for (std::set::const_iterator jt = dirtyInfo.begin(), endjt = dirtyInfo.end(); jt != endjt; ++jt) {
+ boost::format f;
+ if (jt->ITMs() > 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() > 0)
+ f = boost::format(boost::locale::translate("Contains %1% ITM records, %2% UDR records and %3% deleted navmeshes. Clean with %4%.")) % jt->ITMs() % jt->UDRs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
+ else if (jt->ITMs() == 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() == 0)
+ f = boost::format(boost::locale::translate("Clean with %1%.")) % jt->CleaningUtility();
+
+
+ else if (jt->ITMs() == 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() > 0)
+ f = boost::format(boost::locale::translate("Contains %1% UDR records and %2% deleted navmeshes. Clean with %3%.")) % jt->UDRs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
+ else if (jt->ITMs() == 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() > 0)
+ f = boost::format(boost::locale::translate("Contains %1% deleted navmeshes. Clean with %2%.")) % jt->DeletedNavmeshes() % jt->CleaningUtility();
+ else if (jt->ITMs() == 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() == 0)
+ f = boost::format(boost::locale::translate("Contains %1% UDR records. Clean with %2%.")) % jt->UDRs() % jt->CleaningUtility();
+
+ else if (jt->ITMs() > 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() > 0)
+ f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% deleted navmeshes. Clean with %3%.")) % jt->ITMs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
+ else if (jt->ITMs() > 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() == 0)
+ f = boost::format(boost::locale::translate("Contains %1% ITM records. Clean with %2%.")) % jt->ITMs() % jt->CleaningUtility();
+
+ else if (jt->ITMs() > 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() == 0)
+ f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% UDR records. Clean with %3%.")) % jt->ITMs() % jt->UDRs() % jt->CleaningUtility();
+
+ messages.push_back(loot::Message(loot::Message::warn, f.str()));
+ }
+
+ AppendMessages(plugin, messages, warnNo, errorNo);
+ }
+ }
+
+ xml_string_writer writer;
+ details.print(writer, "", pugi::format_default | pugi::format_no_declaration | pugi::format_raw);
+
+ return writer.result != oldDetails;
+ }
+
+ void AppendMain(pugi::xml_node& body,
+ const std::string& oldDetails,
+ const std::string& masterlistVersion,
+ bool masterlistUpdateEnabled,
+ std::list& messages,
+ const std::list& plugins,
+ int& pluginMessageNo
+ ) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Appending main content to LOOT report.";
+
+ pugi::xml_node main = body.append_child();
+ main.set_name("div");
+ main.append_attribute("id").set_value("main");
+
+ pugi::xml_node noscript, div;
+
+ noscript = main.append_child();
+ noscript.set_name("noscript");
+ div = noscript.append_child();
+ div.set_name("div");
+ div.text().set(boost::locale::translate("The LOOT Report requires Javascript to be enabled in order to function.").str().c_str());
+
+ int messageNo = 0, warnNo = 0, errorNo = 0;
+ bool hasChanged = AppendDetails(main, plugins, messageNo, warnNo, errorNo, oldDetails);
+ pluginMessageNo = messageNo;
+
+ AppendSummary(main, hasChanged, masterlistVersion, masterlistUpdateEnabled, messageNo, warnNo, errorNo, messages);
+ }
+
+ void AppendFilters(pugi::xml_node& body, int messageNo, int pluginNo) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Appending filters to LOOT report.";
+
+ pugi::xml_node filters = body.append_child();
+ filters.set_name("div");
+ filters.append_attribute("id").set_value("filters");
+ filters.append_attribute("class").set_value("hidden");
+
+ pugi::xml_node label, input;
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideVersionNumbers");
+ input.append_attribute("data-class").set_value("version");
+ label.text().set(boost::locale::translate("Hide Version Numbers").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideCRCs");
+ input.append_attribute("data-class").set_value("crc");
+ label.text().set(boost::locale::translate("Hide CRCs").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideBashTags");
+ input.append_attribute("data-class").set_value("tag");
+ label.text().set(boost::locale::translate("Hide Bash Tag Suggestions").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideNotes");
+ label.text().set(boost::locale::translate("Hide Notes").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideDoNotCleanMessages");
+ label.text().set(boost::locale::translate("Hide 'Do Not Clean' Messages").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideAllPluginMessages");
+ label.text().set(boost::locale::translate("Hide All Plugin Messages").str().c_str());
+
+ label = filters.append_child();
+ label.set_name("label");
+ input = label.append_child();
+ input.set_name("input");
+ input.append_attribute("type").set_value("checkbox");
+ input.append_attribute("id").set_value("hideMessagelessPlugins");
+ label.text().set(boost::locale::translate("Hide Messageless Plugins").str().c_str());
+
+ pugi::xml_node span;
+
+ label = filters.append_child();
+ label.set_name("div");
+ span = label.append_child();
+ span.set_name("span");
+ span.append_attribute("id").set_value("hiddenPluginNo");
+ span.text().set("0");
+ label.append_child(pugi::node_pcdata).set_value((boost::format(boost::locale::translate("%1% of %2% plugins hidden.")) % "" % IntToString(pluginNo)).str().c_str()); //Blank string is so that translators get full string format.
+
+ label = filters.append_child();
+ label.set_name("div");
+ span = label.append_child();
+ span.set_name("span");
+ span.append_attribute("id").set_value("hiddenMessageNo");
+ span.text().set("0");
+ label.append_child(pugi::node_pcdata).set_value((boost::format(boost::locale::translate("%1% of %2% messages hidden.")) % "" % IntToString(messageNo)).str().c_str()); //Blank string is so that translators get full string format.
+ }
+
+ void AppendScripts(pugi::xml_node& body) {
+
+ BOOST_LOG_TRIVIAL(trace) << "Appending scripts to LOOT report.";
+
+ pugi::xml_node node;
+
+ node = body.append_child();
+ node.set_name("script");
+ node.append_attribute("src").set_value(ToFileURL(g_path_js).c_str());
+ node.text().set(" ");
+
+ }
+
+ void GenerateReport(const boost::filesystem::path& file,
+ std::list& messages,
+ const std::list& plugins,
+ const std::string& masterlistVersion,
+ const bool masterlistUpdateEnabled) {
+
+ pugi::xml_document doc;
+
+ GenerateHead(doc);
+
+ pugi::xml_node body = doc.append_child();
+ body.set_name("body");
+
+ AppendNav(body);
+
+ int messageNo = 0;
+ //Read the details section of the previous report, if it exists.
+ std::string oldDetails = GetOldReportDetails(file);
+ AppendMain(body, oldDetails, masterlistVersion, masterlistUpdateEnabled, messages, plugins, messageNo);
+
+ AppendFilters(body, messageNo, plugins.size());
+
+ AppendScripts(body);
+ //PugiXML's save_file doesn't handle Unicode paths right (it can't open them right), so use a stream instead.
+ boost::filesystem::path outpath(file);
+ loot::ofstream out(outpath);
+ doc.save(out, "\t", pugi::format_default | pugi::format_no_declaration | pugi::format_raw);
+ out.close();
+
+ }
+
+ //Default settings file generation.
+ void GenerateDefaultSettingsFile(const std::string& file) {
+
+ BOOST_LOG_TRIVIAL(info) << "Generating default settings file.";
+
+ YAML::Node root;
+ std::vector games;
+
+ root["Language"] = "eng";
+ root["Game"] = "auto";
+ root["Last Game"] = "auto";
+ root["Debug Verbosity"] = 0;
+ root["Update Masterlist"] = true;
+ root["View Report Externally"] = false;
+
+ games.push_back(Game(Game::tes4));
+ games.push_back(Game(Game::tes5));
+ games.push_back(Game(Game::fo3));
+ games.push_back(Game(Game::fonv));
+ games.push_back(Game(Game::tes4, "Nehrim").SetDetails("Nehrim - At Fate's Edge", "Nehrim.esm", "https://github.com/loot-developers/loot-oblivion.git", "gh-pages", "", "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1"));
+
+ root["Games"] = games;
+
+ //Save settings.
+ YAML::Emitter yout;
+ yout.SetIndent(2);
+ yout << root;
+
+ boost::filesystem::path p(file);
+ loot::ofstream out(p);
+ out << yout.c_str();
+ out.close();
+ }
+}
+
+namespace YAML {
+
+ Emitter& operator << (Emitter& out, const loot::PluginDirtyInfo& rhs) {
+ out << BeginMap
+ << Key << "crc" << Value << Hex << rhs.CRC() << Dec
+ << Key << "util" << Value << rhs.CleaningUtility();
+
+ if (rhs.ITMs() > 0)
+ out << Key << "itm" << Value << rhs.ITMs();
+ if (rhs.UDRs() > 0)
+ out << Key << "udr" << Value << rhs.UDRs();
+ if (rhs.DeletedNavmeshes() > 0)
+ out << Key << "nav" << Value << rhs.DeletedNavmeshes();
+
+ out << EndMap;
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::Game& rhs) {
+ out << BeginMap
+ << Key << "folder" << Value << rhs.FolderName()
+ << Key << "name" << Value << rhs.Name()
+ << Key << "master" << Value << rhs.Master()
+ << Key << "repo" << Value << rhs.RepoURL()
+ << Key << "branch" << Value << rhs.RepoBranch()
+ << Key << "path" << Value << rhs.GamePath().string()
+ << Key << "registry" << Value << rhs.RegistryKey();
+
+ if (rhs.Id() == loot::Game::tes4)
+ out << Key << Value << loot::Game(loot::Game::tes4).FolderName();
+ else if (rhs.Id() == loot::Game::tes5)
+ out << Key << Value << loot::Game(loot::Game::tes5).FolderName();
+ else if (rhs.Id() == loot::Game::fo3)
+ out << Key << Value << loot::Game(loot::Game::fo3).FolderName();
+ else if (rhs.Id() == loot::Game::fonv)
+ out << Key << Value << loot::Game(loot::Game::fonv).FolderName();
+
+ out << EndMap;
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::MessageContent& rhs) {
+ out << BeginMap;
+
+ out << Key << "lang" << Value << loot::Language(rhs.Language()).Locale();
+
+ out << Key << "str" << Value << rhs.Str();
+
+ out << EndMap;
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::Message& rhs) {
+ out << BeginMap;
+
+ if (rhs.Type() == loot::Message::say)
+ out << Key << "type" << Value << "say";
+ else if (rhs.Type() == loot::Message::warn)
+ out << Key << "type" << Value << "warn";
+ else
+ out << Key << "type" << Value << "error";
+
+ if (rhs.Content().size() == 1 && rhs.Content().front().Language() == loot::Language::any)
+ out << Key << "content" << Value << rhs.Content().front().Str();
+ else
+ out << Key << "content" << Value << rhs.Content();
+
+ if (!rhs.Condition().empty())
+ out << Key << "condition" << Value << rhs.Condition();
+
+ out << EndMap;
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::File& rhs) {
+ if (!rhs.IsConditional() && rhs.DisplayName().empty())
+ out << rhs.Name();
+ else {
+ out << BeginMap
+ << Key << "name" << Value << rhs.Name();
+
+ if (rhs.IsConditional())
+ out << Key << "condition" << Value << rhs.Condition();
+
+ if (rhs.DisplayName() != rhs.Name())
+ out << Key << "display" << Value << rhs.DisplayName();
+
+ out << EndMap;
+ }
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::Tag& rhs) {
+ if (!rhs.IsConditional()) {
+ if (rhs.IsAddition())
+ out << rhs.Name();
+ else
+ out << '-' << rhs.Name();
+ }
+ else {
+ out << BeginMap;
+ if (rhs.IsAddition())
+ out << Key << "name" << Value << rhs.Name();
+ else
+ out << Key << "name" << Value << ('-' + rhs.Name());
+
+ out << Key << "condition" << Value << rhs.Condition()
+ << EndMap;
+ }
+
+ return out;
+ }
+
+ Emitter& operator << (Emitter& out, const loot::Plugin& rhs) {
+ if (!rhs.HasNameOnly()) {
+
+ out << BeginMap
+ << Key << "name" << Value << rhs.Name();
+
+ if (rhs.IsPriorityExplicit())
+ out << Key << "priority" << Value << rhs.Priority();
+
+ if (!rhs.Enabled())
+ out << Key << "enabled" << Value << rhs.Enabled();
+
+ if (!rhs.LoadAfter().empty())
+ out << Key << "after" << Value << rhs.LoadAfter();
+
+ if (!rhs.Reqs().empty())
+ out << Key << "req" << Value << rhs.Reqs();
+
+ if (!rhs.Incs().empty())
+ out << Key << "inc" << Value << rhs.Incs();
+
+ if (!rhs.Messages().empty())
+ out << Key << "msg" << Value << rhs.Messages();
+
+ if (!rhs.Tags().empty())
+ out << Key << "tag" << Value << rhs.Tags();
+
+ if (!rhs.DirtyInfo().empty())
+ out << Key << "dirty" << Value << rhs.DirtyInfo();
+
+ out << EndMap;
+ }
+
+ return out;
+ }
+}
\ No newline at end of file
diff --git a/src/backend/generators.h b/src/backend/generators.h
index 9f4020cc..77d7da82 100644
--- a/src/backend/generators.h
+++ b/src/backend/generators.h
@@ -40,597 +40,15 @@
#include
namespace loot {
-
//LOOT Report generation stuff.
-
- inline std::string GetOldReportDetails(boost::filesystem::path& filepath) {
- 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, 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();
-
- //Slim down to only the details section.
- size_t pos1 = contents.find("", pos1);
- if (pos1 != string::npos) {
- size_t pos2 = contents.find("
", pos1);
- if (pos2 != string::npos) {
- pos2 -= 6; // "
"
- return contents.substr(pos1, pos2 - pos1);
- }
- }
- }
- }
- return "";
- }
-
- struct xml_string_writer: pugi::xml_writer {
- std::string result;
-
- virtual void write(const void* data, size_t size) {
- result += std::string(static_cast
(data), size);
- }
- };
-
- inline void WriteMessage(pugi::xml_node& listItem, unsigned int type, std::string content) {
-
- if (type == Message::say)
- content = boost::locale::translate("Note:").str() + " " + content;
- else if (type == Message::tag) {
- content = boost::locale::translate("Bash Tag Suggestion(s):").str() + " " + content;
- } else if (type == Message::warn)
- content = boost::locale::translate("Warning:").str() + " " + content;
- else
- content = boost::locale::translate("Error:").str() + " " + content;
-
- //Now look for Markdown URL syntax and convert any found.
- boost::regex regex("(\\[([^\\]]+)\\]\\s?\\(|<)((file|https?)://\\S+)(\\)|>)", boost::regex::perl | boost::regex::icase); // \2 is the label, \3 is the URL.
-
- boost::match_results results;
- std::string::iterator start, end;
- start = content.begin();
- end = content.end();
-
- while (boost::regex_search(start, end, results, regex)) {
-
- BOOST_LOG_TRIVIAL(trace) << "Results size: " << results.size();
-
- //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.
- listItem.append_child(pugi::node_pcdata).set_value(std::string(results.prefix().first, results.prefix().second).c_str());
-
- //Insert hyperlink.
- pugi::xml_node a = listItem.append_child();
- a.set_name("a");
- a.append_attribute("href").set_value(url.c_str());
- a.text().set(label.c_str());
-
- //Set string to end of matched section.
- start = results.suffix().first;
- }
-
- //Insert any leftover text.
- listItem.append_child(pugi::node_pcdata).set_value(std::string(start, end).c_str());
-
- }
-
- inline void GenerateHead(pugi::xml_document& doc) {
- BOOST_LOG_TRIVIAL(trace) << "Creating LOOT report head.";
-
- //Add DOCTYPE node.
- doc.append_child(pugi::node_doctype).set_value("html");
-
- //Create 'head' node.
- pugi::xml_node head = doc.append_child();
- head.set_name("head");
-
- //Now add head elements.
- pugi::xml_node node;
-
- node = head.append_child();
- node.set_name("meta");
- node.append_attribute("http-equiv").set_value("X-UA-Compatible");
- node.append_attribute("content").set_value("IE=edge");
-
- node = head.append_child();
- node.set_name("meta");
- node.append_attribute("charset").set_value("utf-8");
-
- node = head.append_child();
- node.set_name("title");
- node.text().set(boost::locale::translate("LOOT Report").str().c_str());
-
- node = head.append_child();
- node.set_name("link");
- node.append_attribute("rel").set_value("stylesheet");
- node.append_attribute("href").set_value(ToFileURL(g_path_css).c_str());
-
- node = head.append_child();
- node.set_name("script");
- node.append_attribute("src").set_value(ToFileURL(g_path_polyfill).c_str());
- node.text().set(" ");
- }
-
- inline void AppendNav(pugi::xml_node& body) {
- BOOST_LOG_TRIVIAL(trace) << "Appending navigation bar to LOOT report.";
-
- pugi::xml_node nav, div;
-
- nav = body.append_child();
- nav.set_name("div");
- nav.append_attribute("id").set_value("nav");
-
- div = nav.append_child();
- div.set_name("div");
- div.append_attribute("class").set_value("button current");
- div.append_attribute("data-section").set_value("summary");
- div.text().set(boost::locale::translate("Summary").str().c_str());
-
- div = nav.append_child();
- div.set_name("div");
- div.append_attribute("class").set_value("button");
- div.append_attribute("data-section").set_value("plugins");
- div.text().set(boost::locale::translate("Details").str().c_str());
-
- div = nav.append_child();
- div.set_name("div");
- div.append_attribute("class").set_value("button hidden");
- div.append_attribute("id").set_value("filtersToggle");
- div.text().set(boost::locale::translate("Filters").str().c_str());
- }
-
- inline void AppendMessages(pugi::xml_node& parent, const std::list& messages, int& warnNo, int& errorNo) {
- if (!messages.empty()) {
- pugi::xml_node list = parent.append_child();
- list.set_name("ul");
-
- for (std::list::const_iterator it=messages.begin(), endit=messages.end(); it != endit; ++it) {
-
- pugi::xml_node li = list.append_child();
- li.set_name("li");
-
- if (it->Type() == Message::say)
- li.append_attribute("class").set_value("say");
- else if (it->Type() == Message::tag)
- li.append_attribute("class").set_value("tag");
- else if (it->Type() == Message::warn) {
- li.append_attribute("class").set_value("warn");
- ++warnNo;
- } else {
- li.append_attribute("class").set_value("error");
- ++errorNo;
- }
-
- //Turn any urls into hyperlinks.
- WriteMessage(li, it->Type(), it->Content().front().Str());
- }
- }
- }
-
- inline void AppendSummary(pugi::xml_node& main,
- bool hasChanged,
- const std::string& masterlistVersion,
- bool masterlistUpdateEnabled,
- int messageNo,
- int warnNo,
- int errorNo,
- std::list& messages) {
-
- BOOST_LOG_TRIVIAL(trace) << "Appending summary tab to LOOT report.";
-
- pugi::xml_node summary = main.append_child();
- summary.set_name("div");
- summary.append_attribute("id").set_value("summary");
-
- pugi::xml_node div = summary.append_child();
- div.set_name("div");
-
- pugi::xml_node heading = div.append_child();
- heading.set_name("h2");
- heading.text().set(boost::locale::translate("Summary").str().c_str());
-
- pugi::xml_node table = div.append_child();
- table.set_name("table");
- table = table.append_child();
- table.set_name("tbody");
-
- pugi::xml_node row, cell;
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("LOOT Version").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set((IntToString(g_version_major)+"."+IntToString(g_version_minor)+"."+IntToString(g_version_patch)).c_str());
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("Masterlist Version").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(masterlistVersion.c_str());
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("Masterlist Updating").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- if (masterlistUpdateEnabled)
- cell.text().set(boost::locale::translate("Enabled").str().c_str());
- else
- cell.text().set(boost::locale::translate("Disabled").str().c_str());
-
- if (!hasChanged) {
- BOOST_LOG_TRIVIAL(info) << "No changes in the LOOT report details tab since the last run.";
- /*pugi::xml_node note = summary.append_child();
- note.set_name("div");
- note.append_attribute("id").set_value("noChanges");
- note.text().set(boost::locale::translate("No change in details since last run.").str().c_str());
- */
- 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()) {
-
- div = summary.append_child();
- div.set_name("div");
-
- heading = div.append_child();
- heading.set_name("h2");
- heading.text().set(boost::locale::translate("General Messages").str().c_str());
-
- AppendMessages(div, messages, warnNo, errorNo);
- messageNo += messages.size();
- }
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("Total Number of Messages").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(IntToString(messageNo).c_str());
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("Number of Warnings").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(IntToString(warnNo).c_str());
-
- row = table.append_child();
- row.set_name("tr");
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(boost::locale::translate("Number of Errors").str().c_str());
- cell = row.append_child();
- cell.set_name("td");
- cell.text().set(IntToString(errorNo).c_str());
- }
-
- inline bool AppendDetails(pugi::xml_node& main, const std::list& plugins, int& messageNo, int& warnNo, int& errorNo, const std::string& oldDetails) {
-
- BOOST_LOG_TRIVIAL(trace) << "Appending details tab to LOOT report.";
-
- pugi::xml_node details = main.append_child();
- details.set_name("div");
- details.append_attribute("id").set_value("plugins");
- details.append_attribute("class").set_value("hidden");
-
- if (!plugins.empty()) {
-
- details = details.append_child();
- details.set_name("ul");
-
- for (std::list::const_iterator it=plugins.begin(), endit=plugins.end(); it != endit; ++it) {
- BOOST_LOG_TRIVIAL(trace) << "Appending details for plugin: " << it->Name();
- pugi::xml_node plugin = details.append_child();
- plugin.set_name("li");
-
- pugi::xml_node node = plugin.append_child();
- node.set_name("span");
- node.append_attribute("class").set_value("mod");
- node.text().set(it->Name().c_str());
-
- if (it->Crc() != 0) {
- node = plugin.append_child();
- node.set_name("div");
- node.append_attribute("class").set_value("crc");
- node.text().set(("CRC: " + IntToHexString(it->Crc())).c_str());
- }
-
- if (!it->Version().empty()) {
- node = plugin.append_child();
- node.set_name("div");
- node.append_attribute("class").set_value("version");
- node.text().set((boost::locale::translate("Version:").str() + " " + it->Version()).c_str());
- }
-
- std::list messages = it->Messages();
- messageNo += messages.size();
-
- std::set tags = it->Tags();
-
- if (!tags.empty()) {
- std::string add, remove, content;
- for (std::set::const_iterator jt=tags.begin(), endjt=tags.end(); jt != endjt; ++jt) {
- if (jt->IsAddition())
- add += ", " + jt->Name();
- else
- remove += ", " + jt->Name();
- }
- if (!add.empty()) {
- node = plugin.append_child();
- node.set_name("div");
- node.append_attribute("class").set_value("tag add");
- node.text().set((boost::locale::translate("Add:").str() + " " + add.substr(2)).c_str());
- }
- if (!remove.empty()) {
- node = plugin.append_child();
- node.set_name("div");
- node.append_attribute("class").set_value("tag remove");
- node.text().set((boost::locale::translate("Remove:").str() + " " + remove.substr(2)).c_str());
- }
- }
-
- std::set dirtyInfo = it->DirtyInfo();
- for (std::set::const_iterator jt=dirtyInfo.begin(), endjt=dirtyInfo.end(); jt != endjt; ++jt) {
- boost::format f;
- if (jt->ITMs() > 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() > 0)
- f = boost::format(boost::locale::translate("Contains %1% ITM records, %2% UDR records and %3% deleted navmeshes. Clean with %4%.")) % jt->ITMs() % jt->UDRs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
- else if (jt->ITMs() == 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() == 0)
- f = boost::format(boost::locale::translate("Clean with %1%.")) % jt->CleaningUtility();
-
-
- else if (jt->ITMs() == 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() > 0)
- f = boost::format(boost::locale::translate("Contains %1% UDR records and %2% deleted navmeshes. Clean with %3%.")) % jt->UDRs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
- else if (jt->ITMs() == 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() > 0)
- f = boost::format(boost::locale::translate("Contains %1% deleted navmeshes. Clean with %2%.")) % jt->DeletedNavmeshes() % jt->CleaningUtility();
- else if (jt->ITMs() == 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() == 0)
- f = boost::format(boost::locale::translate("Contains %1% UDR records. Clean with %2%.")) % jt->UDRs() % jt->CleaningUtility();
-
- else if (jt->ITMs() > 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() > 0)
- f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% deleted navmeshes. Clean with %3%.")) % jt->ITMs() % jt->DeletedNavmeshes() % jt->CleaningUtility();
- else if (jt->ITMs() > 0 && jt->UDRs() == 0 && jt->DeletedNavmeshes() == 0)
- f = boost::format(boost::locale::translate("Contains %1% ITM records. Clean with %2%.")) % jt->ITMs() % jt->CleaningUtility();
-
- else if (jt->ITMs() > 0 && jt->UDRs() > 0 && jt->DeletedNavmeshes() == 0)
- f = boost::format(boost::locale::translate("Contains %1% ITM records and %2% UDR records. Clean with %3%.")) % jt->ITMs() % jt->UDRs() % jt->CleaningUtility();
-
- messages.push_back(loot::Message(loot::Message::warn, f.str()));
- }
-
- AppendMessages(plugin, messages, warnNo, errorNo);
- }
- }
-
- xml_string_writer writer;
- details.print(writer, "", pugi::format_default | pugi::format_no_declaration | pugi::format_raw);
-
- return writer.result != oldDetails;
- }
-
- inline void AppendMain(pugi::xml_node& body,
- const std::string& oldDetails,
- const std::string& masterlistVersion,
- bool masterlistUpdateEnabled,
- std::list& messages,
- const std::list& plugins,
- int& pluginMessageNo
- ) {
-
- BOOST_LOG_TRIVIAL(trace) << "Appending main content to LOOT report.";
-
- pugi::xml_node main = body.append_child();
- main.set_name("div");
- main.append_attribute("id").set_value("main");
-
- pugi::xml_node noscript, div;
-
- noscript = main.append_child();
- noscript.set_name("noscript");
- div = noscript.append_child();
- div.set_name("div");
- div.text().set(boost::locale::translate("The LOOT Report requires Javascript to be enabled in order to function.").str().c_str());
-
- int messageNo=0, warnNo=0, errorNo=0;
- bool hasChanged = AppendDetails(main, plugins, messageNo, warnNo, errorNo, oldDetails);
- pluginMessageNo = messageNo;
-
- AppendSummary(main, hasChanged, masterlistVersion, masterlistUpdateEnabled, messageNo, warnNo, errorNo, messages);
- }
-
- inline void AppendFilters(pugi::xml_node& body, int messageNo, int pluginNo) {
-
- BOOST_LOG_TRIVIAL(trace) << "Appending filters to LOOT report.";
-
- pugi::xml_node filters = body.append_child();
- filters.set_name("div");
- filters.append_attribute("id").set_value("filters");
- filters.append_attribute("class").set_value("hidden");
-
- pugi::xml_node label, input;
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideVersionNumbers");
- input.append_attribute("data-class").set_value("version");
- label.text().set(boost::locale::translate("Hide Version Numbers").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideCRCs");
- input.append_attribute("data-class").set_value("crc");
- label.text().set(boost::locale::translate("Hide CRCs").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideBashTags");
- input.append_attribute("data-class").set_value("tag");
- label.text().set(boost::locale::translate("Hide Bash Tag Suggestions").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideNotes");
- label.text().set(boost::locale::translate("Hide Notes").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideDoNotCleanMessages");
- label.text().set(boost::locale::translate("Hide 'Do Not Clean' Messages").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideAllPluginMessages");
- label.text().set(boost::locale::translate("Hide All Plugin Messages").str().c_str());
-
- label = filters.append_child();
- label.set_name("label");
- input = label.append_child();
- input.set_name("input");
- input.append_attribute("type").set_value("checkbox");
- input.append_attribute("id").set_value("hideMessagelessPlugins");
- label.text().set(boost::locale::translate("Hide Messageless Plugins").str().c_str());
-
- pugi::xml_node span;
-
- label = filters.append_child();
- label.set_name("div");
- span = label.append_child();
- span.set_name("span");
- span.append_attribute("id").set_value("hiddenPluginNo");
- span.text().set("0");
- label.append_child(pugi::node_pcdata).set_value((boost::format(boost::locale::translate("%1% of %2% plugins hidden.")) % "" % IntToString(pluginNo)).str().c_str()); //Blank string is so that translators get full string format.
-
- label = filters.append_child();
- label.set_name("div");
- span = label.append_child();
- span.set_name("span");
- span.append_attribute("id").set_value("hiddenMessageNo");
- span.text().set("0");
- label.append_child(pugi::node_pcdata).set_value((boost::format(boost::locale::translate("%1% of %2% messages hidden.")) % "" % IntToString(messageNo)).str().c_str()); //Blank string is so that translators get full string format.
- }
-
- inline void AppendScripts(pugi::xml_node& body) {
-
- BOOST_LOG_TRIVIAL(trace) << "Appending scripts to LOOT report.";
-
- pugi::xml_node node;
-
- node = body.append_child();
- node.set_name("script");
- node.append_attribute("src").set_value(ToFileURL(g_path_js).c_str());
- node.text().set(" ");
-
- }
-
- inline void GenerateReport(const boost::filesystem::path& file,
- std::list& messages,
- const std::list& plugins,
- const std::string& masterlistVersion,
- const bool masterlistUpdateEnabled) {
-
- pugi::xml_document doc;
-
- GenerateHead(doc);
-
- pugi::xml_node body = doc.append_child();
- body.set_name("body");
-
- AppendNav(body);
-
- int messageNo = 0;
- //Read the details section of the previous report, if it exists.
- string oldDetails = GetOldReportDetails(file);
- AppendMain(body, oldDetails, masterlistVersion, masterlistUpdateEnabled, messages, plugins, messageNo);
-
- AppendFilters(body, messageNo, plugins.size());
-
- AppendScripts(body);
- //PugiXML's save_file doesn't handle Unicode paths right (it can't open them right), so use a stream instead.
- boost::filesystem::path outpath(file);
- loot::ofstream out(outpath);
- doc.save(out, "\t", pugi::format_default | pugi::format_no_declaration | pugi::format_raw);
- out.close();
-
- }
+ void GenerateReport(const boost::filesystem::path& file,
+ std::list& messages,
+ const std::list& plugins,
+ const std::string& masterlistVersion,
+ const bool masterlistUpdateEnabled);
//Default settings file generation.
-
- inline void GenerateDefaultSettingsFile(const std::string& file) {
-
- BOOST_LOG_TRIVIAL(info) << "Generating default settings file.";
-
- YAML::Node root;
- std::vector games;
-
- root["Language"] = "eng";
- root["Game"] = "auto";
- root["Last Game"] = "auto";
- root["Debug Verbosity"] = 0;
- root["Update Masterlist"] = true;
- root["View Report Externally"] = false;
-
- games.push_back(Game(Game::tes4));
- games.push_back(Game(Game::tes5));
- games.push_back(Game(Game::fo3));
- games.push_back(Game(Game::fonv));
- games.push_back(Game(Game::tes4, "Nehrim").SetDetails("Nehrim - At Fate's Edge", "Nehrim.esm", "https://github.com/loot-developers/loot-oblivion.git", "gh-pages", "", "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1"));
-
- root["Games"] = games;
-
- //Save settings.
- YAML::Emitter yout;
- yout.SetIndent(2);
- yout << root;
-
- boost::filesystem::path p(file);
- loot::ofstream out(p);
- out << yout.c_str();
- out.close();
- }
+ void GenerateDefaultSettingsFile(const std::string& file);
}
namespace YAML {
@@ -645,156 +63,19 @@ namespace YAML {
return out;
}
- inline Emitter& operator << (Emitter& out, const loot::PluginDirtyInfo& rhs) {
- out << BeginMap
- << Key << "crc" << Value << Hex << rhs.CRC() << Dec
- << Key << "util" << Value << rhs.CleaningUtility();
+ Emitter& operator << (Emitter& out, const loot::PluginDirtyInfo& rhs);
- if (rhs.ITMs() > 0)
- out << Key << "itm" << Value << rhs.ITMs();
- if (rhs.UDRs() > 0)
- out << Key << "udr" << Value << rhs.UDRs();
- if (rhs.DeletedNavmeshes() > 0)
- out << Key << "nav" << Value << rhs.DeletedNavmeshes();
+ Emitter& operator << (Emitter& out, const loot::Game& rhs);
- out << EndMap;
+ Emitter& operator << (Emitter& out, const loot::MessageContent& rhs);
- return out;
- }
+ Emitter& operator << (Emitter& out, const loot::Message& rhs);
- inline Emitter& operator << (Emitter& out, const loot::Game& rhs) {
- out << BeginMap
- << Key << "folder" << Value << rhs.FolderName()
- << Key << "name" << Value << rhs.Name()
- << Key << "master" << Value << rhs.Master()
- << Key << "repo" << Value << rhs.RepoURL()
- << Key << "branch" << Value << rhs.RepoBranch()
- << Key << "path" << Value << rhs.GamePath().string()
- << Key << "registry" << Value << rhs.RegistryKey();
+ Emitter& operator << (Emitter& out, const loot::File& rhs);
- if (rhs.Id() == loot::Game::tes4)
- out << Key << Value << loot::Game(loot::Game::tes4).FolderName();
- else if (rhs.Id() == loot::Game::tes5)
- out << Key << Value << loot::Game(loot::Game::tes5).FolderName();
- else if (rhs.Id() == loot::Game::fo3)
- out << Key << Value << loot::Game(loot::Game::fo3).FolderName();
- else if (rhs.Id() == loot::Game::fonv)
- out << Key << Value << loot::Game(loot::Game::fonv).FolderName();
+ Emitter& operator << (Emitter& out, const loot::Tag& rhs);
- out << EndMap;
-
- return out;
- }
-
- inline Emitter& operator << (Emitter& out, const loot::MessageContent& rhs) {
- out << BeginMap;
-
- out << Key << "lang" << Value << loot::Language(rhs.Language()).Locale();
-
- out << Key << "str" << Value << rhs.Str();
-
- out << EndMap;
-
- return out;
- }
-
- inline Emitter& operator << (Emitter& out, const loot::Message& rhs) {
- out << BeginMap;
-
- if (rhs.Type() == loot::Message::say)
- out << Key << "type" << Value << "say";
- else if (rhs.Type() == loot::Message::warn)
- out << Key << "type" << Value << "warn";
- else
- out << Key << "type" << Value << "error";
-
- if (rhs.Content().size() == 1 && rhs.Content().front().Language() == loot::Language::any)
- out << Key << "content" << Value << rhs.Content().front().Str();
- else
- out << Key << "content" << Value << rhs.Content();
-
- if (!rhs.Condition().empty())
- out << Key << "condition" << Value << rhs.Condition();
-
- out << EndMap;
-
- return out;
- }
-
- inline Emitter& operator << (Emitter& out, const loot::File& rhs) {
- if (!rhs.IsConditional() && rhs.DisplayName().empty())
- out << rhs.Name();
- else {
- out << BeginMap
- << Key << "name" << Value << rhs.Name();
-
- if (rhs.IsConditional())
- out << Key << "condition" << Value << rhs.Condition();
-
- if (rhs.DisplayName() != rhs.Name())
- out << Key << "display" << Value << rhs.DisplayName();
-
- out << EndMap;
- }
-
- return out;
- }
-
- inline Emitter& operator << (Emitter& out, const loot::Tag& rhs) {
- if (!rhs.IsConditional()) {
- if (rhs.IsAddition())
- out << rhs.Name();
- else
- out << '-' << rhs.Name();
- } else {
- out << BeginMap;
- if (rhs.IsAddition())
- out << Key << "name" << Value << rhs.Name();
- else
- out << Key << "name" << Value << ('-' + rhs.Name());
-
- out << Key << "condition" << Value << rhs.Condition()
- << EndMap;
- }
-
- return out;
- }
-
- inline Emitter& operator << (Emitter& out, const loot::Plugin& rhs) {
- if (!rhs.HasNameOnly()) {
-
- out << BeginMap
- << Key << "name" << Value << rhs.Name();
-
- if (rhs.IsPriorityExplicit())
- out << Key << "priority" << Value << rhs.Priority();
-
- if (!rhs.Enabled())
- out << Key << "enabled" << Value << rhs.Enabled();
-
- if (!rhs.LoadAfter().empty())
- out << Key << "after" << Value << rhs.LoadAfter();
-
- if (!rhs.Reqs().empty())
- out << Key << "req" << Value << rhs.Reqs();
-
- if (!rhs.Incs().empty())
- out << Key << "inc" << Value << rhs.Incs();
-
- if (!rhs.Messages().empty())
- out << Key << "msg" << Value << rhs.Messages();
-
- if (!rhs.Tags().empty())
- out << Key << "tag" << Value << rhs.Tags();
-
- if (!rhs.DirtyInfo().empty())
- out << Key << "dirty" << Value << rhs.DirtyInfo();
-
- out << EndMap;
- }
-
- return out;
- }
+ Emitter& operator << (Emitter& out, const loot::Plugin& rhs);
}
#endif