From 5f8daa2db5e3a91b2ec2c14ab0c1d993041d04fd Mon Sep 17 00:00:00 2001 From: WrinklyNinja Date: Thu, 8 May 2014 18:58:55 +0100 Subject: [PATCH] Removed support for parsing BOSS masterlists. --- CMakeLists.txt | 10 +- src/api/api.cpp | 16 +- src/api/api.h | 4 +- src/backend/legacy-parser.h | 831 ------------------------------------ src/converter.cpp | 111 ----- 5 files changed, 7 insertions(+), 965 deletions(-) delete mode 100644 src/backend/legacy-parser.h delete mode 100644 src/converter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b5fb4adb..50bd2541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,7 @@ ELSEIF (MSVC) set (CMAKE_GENERATOR_TOOLSET "v120_xp" CACHE STRING "Platform Toolset" FORCE) set (BOOST_SUFFIX "-vc120-mt-1_55") ELSEIF (MSVC_VERSION EQUAL 1700) - set (BOOST_SUFFIX "-vc110-mt-1_55") + set (BOOST_SUFFIX "-vc110-mt-1_55") ENDIF () include_directories("${PROJECT_LIBS_DIR}/wxWidgets/lib/vc_lib/mswu") link_directories("${PROJECT_LIBS_DIR}/wxWidgets/lib/vc_lib") @@ -158,11 +158,3 @@ target_link_libraries (loot${PROJECT_ARCH} ${LOOT_LIBS}) # Build application. add_executable (LOOT ${LOOT_GUI_SRC}) target_link_libraries (LOOT ${LOOT_GUI_LIBS}) - -# Build converter. -add_executable (masterlist-converter ${LOOT_SRC} "${CMAKE_SOURCE_DIR}/src/converter.cpp") -IF (MSVC) - set_target_properties (masterlist-converter PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE") -ENDIF () -target_link_libraries (masterlist-converter ${LOOT_LIBS}) - diff --git a/src/api/api.cpp b/src/api/api.cpp index 27fab368..ea82a0e9 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -29,7 +29,6 @@ #include "../backend/generators.h" #include "../backend/error.h" #include "../backend/streams.h" -#include "../backend/legacy-parser.h" #include @@ -277,17 +276,10 @@ LOOT_API unsigned int loot_load_lists (loot_db db, const char * const masterlist try { if (boost::filesystem::exists(masterlistPath)) { - if (boost::algorithm::iends_with(masterlistPath, ".yaml")) { - loot::ifstream in(masterlistPath); - YAML::Node tempNode = YAML::Load(in); - in.close(); - temp = tempNode["plugins"].as< std::list >(); - } - else { - boost::filesystem::path p(masterlistPath); - std::list filler; - LoadBOSSMasterlist(p, temp, filler); - } + loot::ifstream in(masterlistPath); + YAML::Node tempNode = YAML::Load(in); + in.close(); + temp = tempNode["plugins"].as< std::list >(); } } catch (std::exception& e) { return c_error(loot_error_parse_fail, e.what()); diff --git a/src/api/api.h b/src/api/api.h index 9652ff7c..b9615188 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -274,8 +274,8 @@ LOOT_API void loot_destroy_db (loot_db db); @brief Loads the masterlist and userlist from the paths specified. @details Can be called multiple times, each time replacing the previously-loaded data. @param db The database the function acts on. - @param masterlistPath A string containing the relative or absolute path to the masterlist file that should be loaded. The API supports loading both LOOT and BOSS masterlists. - @param userlistPath A string containing the relative or absolute path to the userlist file that should be loaded, or `NULL`. If `NULL`, no userlist will be loaded. The API only upports loading v3 userlists. + @param masterlistPath A string containing the relative or absolute path to the masterlist file that should be loaded. + @param userlistPath A string containing the relative or absolute path to the userlist file that should be loaded, or `NULL`. If `NULL`, no userlist will be loaded. @returns A return code. */ LOOT_API unsigned int loot_load_lists (loot_db db, const char * const masterlistPath, diff --git a/src/backend/legacy-parser.h b/src/backend/legacy-parser.h deleted file mode 100644 index c9c4e208..00000000 --- a/src/backend/legacy-parser.h +++ /dev/null @@ -1,831 +0,0 @@ -/* LOOT - - A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and - Fallout: New Vegas. - - Copyright (C) 2012-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 - . -*/ - -#ifndef __LOOT_LEGACY_PARSER__ -#define __LOOT_LEGACY_PARSER__ - -// This file holds the code necessary for inputting a BOSS masterlist and getting a list of plugins and a list of global messages from it. - -#ifndef BOOST_SPIRIT_UNICODE -#define BOOST_SPIRIT_UNICODE -#endif - -#include "metadata.h" -#include "error.h" -#include "game.h" -#include "streams.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace loot { - namespace qi = boost::spirit::qi; - namespace unicode = boost::spirit::unicode; - namespace phoenix = boost::phoenix; - - //Message types for those that will be converted, but need identifying in the interim. Must not conflict with the values for the global types. - unsigned int message_inc = 1000; - unsigned int message_req = 1001; - unsigned int message_dirty = 1002; - - - /////////////////////////////// - // Keyword structures - /////////////////////////////// - - struct masterlistMsgKey_ : qi::symbols { - masterlistMsgKey_() { - add //New Message keywords. - ("say",Message::say) - ("tag",Message::tag) - ("req",message_req) - ("inc", message_inc) - ("dirty",message_dirty) - ("warn",Message::warn) - ("error",Message::error) - ; - } - }; - - - /////////////////////////////// - //Skipper Grammar - /////////////////////////////// - - //Skipper for userlist, modlist and ini parsers. - template - class Skipper : public qi::grammar { - public: - Skipper() : Skipper::base_type(start, "skipper grammar") { - - start = - spc - | UTF8 - | CComment - | CPlusPlusComment - | iniComment - | eof; - - spc = unicode::space - qi::eol; - - UTF8 = unicode::char_("\xef") >> unicode::char_("\xbb") >> unicode::char_("\xbf"); //UTF8 BOM - - CComment = "/*" >> *(unicode::char_ - "*/") >> "*/"; - - iniComment = qi::lit("#") >> *(unicode::char_ - qi::eol); - - CPlusPlusComment = !(qi::lit("http:") | qi::lit("https:") | qi::lit("file:")) >> "//" >> *(unicode::char_ - qi::eol); - - eof = *(spc | CComment | CPlusPlusComment | qi::eol) >> qi::eoi; - } - - void SkipIniComments(const bool b) { - if (b) - iniComment = (qi::lit("#") | qi::lit(";")) >> *(unicode::char_ - qi::eol); - else - iniComment = CComment; - } - private: - qi::rule start, spc, eof, CComment, CPlusPlusComment, lineComment, iniComment, UTF8; - }; - - /////////////////////////////// - //Modlist/Masterlist Grammar - /////////////////////////////// - - //Modlist/Masterlist grammar. - template - class LegacyMasterlistGrammar : public qi::grammar(), Skipper > { - public: - LegacyMasterlistGrammar() - : LegacyMasterlistGrammar::base_type(modList, "modlist grammar") { - masterlistMsgKey_ masterlistMsgKey; - const std::list noMessages; //An empty set of messages. - globalMessageBuffer = NULL; - - modList = - *qi::eol - > - ( - listVar - | globalMessage [phoenix::bind(&LegacyMasterlistGrammar::StoreGlobalMessage, this, qi::_1)] - | groupLine - | listPlugin [phoenix::push_back(qi::_val, qi::_1)] - | qi::eps - ) % +qi::eol; - - listVar = - (conditionals [phoenix::ref(lastConditional) = qi::_1] - >> unicode::no_case[qi::lit("set")] - >> ( - ':' - > charString - )) [phoenix::bind(&LegacyMasterlistGrammar::RecordVarCondition, this, qi::_1, qi::_2)] - ; - - globalMessage = - (conditionals [phoenix::ref(lastConditional) = qi::_1] - >> unicode::no_case[qi::lit("global")] - >> ( - messageKeyword - >> ':' - >> charString - ) - )[phoenix::bind(&LegacyMasterlistGrammar::MakeMessage, this, qi::_val, qi::_1, qi::_2, qi::_3)] - ; - - groupLine = - conditionals [phoenix::ref(lastConditional) = qi::_1] - >> unicode::no_case[qi::lit("begingroup:") - | qi::lit("endgroup")] - > charString; - - listPlugin = - conditionals [phoenix::ref(lastConditional) = qi::_1] - > -unicode::no_case[qi::lit("mod:") | qi::lit("regex:")] - > (charString - > pluginMessages) [phoenix::bind(&LegacyMasterlistGrammar::MakePlugin, this, qi::_val, qi::_1, qi::_2)] - ; - - pluginMessages %= - ( - +qi::eol - >> pluginMessage % +qi::eol - ) | qi::eps [qi::_1 = noMessages]; - - pluginMessage = - (conditionals [phoenix::ref(lastConditional) = qi::_1] - >> messageKeyword - >> ':' - >> charString) [phoenix::bind(&LegacyMasterlistGrammar::MakeMessage, this, qi::_val, qi::_1, qi::_2, qi::_3)] //The double >> matters. A single > doesn't work. - ; - - charString %= qi::lexeme[+(unicode::char_ - qi::eol)]; //String, with no skipper. - - messageKeyword %= unicode::no_case[masterlistMsgKey]; - - conditionals = - ( - conditional [qi::_val = qi::_1] - > *((andOr > conditional) [qi::_val += qi::_1 + qi::_2]) - ) - | unicode::no_case[unicode::string("else")][phoenix::bind(&LegacyMasterlistGrammar::InvertCondition, this, qi::_val, phoenix::ref(lastConditional))] - | qi::eps [qi::_val = ""]; - - andOr = - qi::lit("&&") [qi::_val = " and "] - | qi::lit("||") [qi::_val = " or "]; - - conditional = - (ifIfnot - > functCondition) [phoenix::bind(&LegacyMasterlistGrammar::ConvertVarCondition, this, qi::_val, qi::_1, qi::_2)]; - - ifIfnot %= - unicode::no_case[ - unicode::string("ifnot")[qi::_val = "ifnot "] - | unicode::string("if") [qi::_val = "if "] - ]; - - functCondition %= - ( - unicode::no_case[unicode::string("var")][qi::_1 = "var"] > unicode::char_('(') > variable > unicode::char_(')') //Variable condition. - ) | ( - unicode::no_case[unicode::string("file")][qi::_1 = "file"] > unicode::char_('(') > file > unicode::char_(')') //File condition. - ) | ( - unicode::no_case[unicode::string("checksum")][qi::_1 = "checksum"] > unicode::char_('(') > file > unicode::char_(',') > checksum > unicode::char_(')') //Checksum condition. - ) | ( - unicode::no_case[unicode::string("version")][qi::_1 = "version"] > unicode::char_('(') > file > unicode::char_(',') > version > unicode::char_(',') > comparator > unicode::char_(')') //Version condition. - ) | ( - unicode::no_case[unicode::string("regex")][qi::_1 = "regex"] > unicode::char_('(') > regex > unicode::char_(')') //Regex condition. - ) | ( - unicode::no_case[unicode::string("active")][qi::_1 = "active"] > unicode::char_('(') > file > unicode::char_(')') //Active condition. - ) | ( - unicode::no_case[unicode::string("lang")][qi::_1 = "lang"] > unicode::char_('(') > language > unicode::char_(')') //Language condition. - ) - ; - - variable %= +(unicode::char_ - (')' | qi::eol)); - - file %= qi::lexeme[unicode::char_('"') > - +(unicode::char_ - ('"' | qi::eol)) - > unicode::char_('"')]; - - checksum %= +unicode::xdigit; - - version %= file; - - comparator %= - unicode::string("=") // [qi::_val = "=="] - | unicode::string(">") - | unicode::string("<") - ; - - regex %= file; - - language %= file; - - - modList.name("modList"); - listVar.name("listVar"); - listPlugin.name("listPlugin"); - pluginMessages.name("pluginMessages"); - pluginMessage.name("pluginMessage"); - charString.name("charString"); - messageKeyword.name("messageKeyword"); - conditionals.name("conditional"); - andOr.name("andOr"); - conditional.name("conditional"); - ifIfnot.name("ifIfnot"); - functCondition.name("condition"); - variable.name("variable"); - file.name("file"); - checksum.name("checksum"); - version.name("version"); - comparator.name("comparator"); - regex.name("regex file"); - file.name("language"); - - qi::on_error(modList, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(listVar, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(listPlugin, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(pluginMessages, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(pluginMessage, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(charString, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(messageKeyword, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(conditionals, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(andOr, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(conditional, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(ifIfnot, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(functCondition, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(variable, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(file, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(checksum, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(version, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(comparator, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(regex, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - qi::on_error(language, phoenix::bind(&LegacyMasterlistGrammar::SyntaxError, this, qi::_1, qi::_2, qi::_3, qi::_4)); - } - - void SetGlobalMessageBuffer(std::list * buffer) { - globalMessageBuffer = buffer; - } - private: - qi::rule(), Skipper > modList; - qi::rule > listVar, groupLine; - qi::rule > listPlugin; - qi::rule(), Skipper > pluginMessages; - qi::rule > globalMessage, pluginMessage; - qi::rule messageKeyword; - qi::rule > charString, andOr, conditional, conditionals, ifIfnot, functCondition, variable, file, checksum, version, comparator, regex, language; - - std::map varConditionMap; - std::string lastConditional; - std::list * globalMessageBuffer; - - void StoreGlobalMessage(Message& message) { - if (globalMessageBuffer != NULL) - globalMessageBuffer->push_back(message); - } - - void RecordVarCondition(std::string& condition, std::string& var) { - varConditionMap.insert(std::pair(boost::to_lower_copy(var), condition)); - } - - bool IsPosInQuotedText(const std::string& text, size_t pos) { - std::string::const_iterator last = text.begin(); - std::advance(last, pos); - - return (std::count(text.begin(), last, '"') % 2); - } - - void InvertCondition(std::string& output, const std::string& input) { - //Want to swap all the "if" and "ifnot" instances that occur outside of quoted strings (in case a filename contains them). - - output = input; - - size_t pos = 0; - while (pos < output.length()) { - pos = output.find("if", pos); - - if (pos == std::string::npos) - break; - - if (!IsPosInQuotedText(output, pos)) { - if (output.substr(pos, 5) == "ifnot") - output.erase(pos + 2, 3); - else - output.insert(pos + 2, "not"); - } - - pos += 2; - } - - //Also want to swap all instances of "or" and "and" outside of quoted strings. - pos = 0; - while (pos < output.length()) { - size_t pos1 = output.find("and", pos); - size_t pos2 = output.find("or", pos); - - if (pos1 == std::string::npos && pos2 == std::string::npos) - break; - - if (pos2 == std::string::npos || pos1 < pos2) - pos = pos1; - else if (pos1 == std::string::npos || pos2 < pos1) - pos = pos2; - - if (!IsPosInQuotedText(output, pos)) { - if (pos == pos1) { - output.erase(pos, 3); - output.insert(pos, "or"); - } else { - output.erase(pos, 2); - output.insert(pos, "and"); - } - } - - pos += 3; - } - - } - - void ConvertVarCondition(std::string& output, std::string& ifIfnotStr, std::string& function) { - - if (function.find("var(") == std::string::npos) { - output = ifIfnotStr + function; - return; - } - - size_t pos1 = function.find("var(") + 4; - size_t pos2 = function.find(')', pos1); - - std::string var = function.substr(pos1, pos2 - pos1); - - std::map::const_iterator it = varConditionMap.find(boost::to_lower_copy(var)); - - if (it == varConditionMap.end()) { - BOOST_LOG_TRIVIAL(error) << "The variable \"" << var << "\" was not previously defined."; - throw loot::error(loot::error::condition_eval_fail, "The variable \"" + var + "\" was not previously defined."); //Isn't used by LOOT application, so don't need to translate. - } - - if (ifIfnotStr == "if ") - output = it->second; - else { - if (!it->second.empty()) - InvertCondition(output, it->second); - } - } - - void ConvertCondition(std::string& condition) { - /*Convert the BOSS order of evaluation to the standard order. - The only consistent way to do this is to bracket everything, - which is supported by the LOOT syntax. */ - - int bracketNo = 0; - size_t pos = 0; - - while ((pos = condition.find(" and ", pos)) != std::string::npos) { - - if (IsPosInQuotedText(condition, pos)) { - pos += 7; - continue; - } - - condition.insert(pos, " )"); - bracketNo++; - pos += 7; - - } - - pos = 0; - while ((pos = condition.find(" or ", pos)) != std::string::npos) { - - if (IsPosInQuotedText(condition, pos)) { - pos += 6; - continue; - } - - condition.insert(pos, " )"); - bracketNo++; - pos += 6; - } - - for (int i=0; i < bracketNo; ++i) { - condition.insert(0, "( "); - } - - //Convert "if" and "ifnot" to "" and "not". - - while ((pos = condition.find("if ")) != std::string::npos) { - - if (IsPosInQuotedText(condition, pos)) - continue; - - condition.erase(pos, 3); - - } - - while ((pos = condition.find("ifnot ")) != std::string::npos) { - - if (IsPosInQuotedText(condition, pos)) - continue; - - condition.erase(pos, 2); - } - - //Also convert the "=" comparator in version conditions to "==". For some reason doing this in-parser causes most of the version condition to disappear... - - pos = condition.find("version("); - if (pos != std::string::npos) { - pos = condition.find(",=)", pos); - - if (pos != std::string::npos) - condition.insert(pos + 1, "="); - } - - //Also convert file placeholders. - boost::replace_all(condition, "\"OBSE\"", "\"..\\obse_1_2_416.dll\""); - boost::replace_all(condition, "\"FOSE\"", "\"..\\fose_loader.exe\""); - boost::replace_all(condition, "\"NVSE\"", "\"..\\nvse_loader.exe\""); - boost::replace_all(condition, "\"SKSE\"", "\"..\\skse_loader.exe\""); - boost::replace_all(condition, "\"MWSE\"", "\"..\\MWSE.dll\""); - boost::replace_all(condition, "\"TES3\"", "\"..\\Morrowind.exe\""); - boost::replace_all(condition, "\"TES4\"", "\"..\\Oblivion.exe\""); - boost::replace_all(condition, "\"TES5\"", "\"..\\TESV.exe\""); - boost::replace_all(condition, "\"FO3\"", "\"..\\Fallout3.exe\""); - boost::replace_all(condition, "\"FONV\"", "\"..\\FalloutNV.exe\""); - - } - - void MakePlugin(Plugin& plugin, std::string& name, std::list& messages) { - plugin.Name(boost::algorithm::trim_copy(name)); - - //Need to remove any Bash Tag suggestion messages and apply them properly. - std::set tags; - std::set dirtyInfo; - std::list::iterator it = messages.begin(); - while (it != messages.end()) { - - if (it->Type() == Message::tag) { - std::string message = it->ChooseContent(Language::any).Str(); - - std::string addedList, removedList; - size_t pos1, pos2 = std::string::npos; - - pos1 = message.find("{{BASH:"); - if (pos1 != std::string::npos) - pos2 = message.find("}}", pos1); - if (pos2 != std::string::npos) - addedList = message.substr(pos1 + 7, pos2 - pos1 - 7); - - pos1 = message.find("["); - pos2 = std::string::npos; - if (pos1 != std::string::npos) - pos2 = message.find("]", pos1); - if (pos2 != std::string::npos) - removedList = message.substr(pos1+1,pos2-pos1-1); - - if (!addedList.empty()) { - //Now we move through the list of added Tags. - //Search the set of already added Tags for each one and only add if it's not found. - std::string name; - pos1 = 0; - pos2 = addedList.find(",", pos1); - while (pos2 != std::string::npos) { - name = boost::algorithm::trim_copy(addedList.substr(pos1,pos2-pos1)); - if (tags.find(Tag(name)) == tags.end()) - tags.insert(Tag(name, true, it->Condition())); - pos1 = pos2+1; - pos2 = addedList.find(",", pos1); - } - name = boost::algorithm::trim_copy(addedList.substr(pos1)); - if (tags.find(Tag(name)) == tags.end()) - tags.insert(Tag(name, true, it->Condition())); - } - - if (!removedList.empty()) { - std::string name; - pos1 = 0; - pos2 = removedList.find(",", pos1); - while (pos2 != std::string::npos) { - name = boost::algorithm::trim_copy(removedList.substr(pos1,pos2-pos1)); - if (tags.find(Tag(name, false)) == tags.end()) - tags.insert(Tag(name, false, it->Condition())); - pos1 = pos2+1; - pos2 = removedList.find(",", pos1); - } - name = boost::algorithm::trim_copy(removedList.substr(pos1)); - if (tags.find(Tag(name, false)) == tags.end()) - tags.insert(Tag(name, false, it->Condition())); - } - - it = messages.erase(it); - } else if (it->Type() == Message::say) { - std::string content = it->ChooseContent(Language::any).Str(); - unsigned int langInt = it->ChooseContent(Language::any).Language(); - std::string opener; - if (langInt == Language::english || langInt == Language::any) - opener = "Requires: "; - else if (langInt == Language::spanish) - opener = "Requiere: "; - else if (langInt == Language::russian) - opener = "Требуется: "; - - if (boost::starts_with(content, opener) && (boost::icontains(content, ".esp") || boost::icontains(content, ".esm"))) { - //Remove any requirement messages that contain a plugin filename, since it's probably a master, according to SilentSpike's experience. - it = messages.erase(it); - } else - ++it; - } else if (it->Type() == Message::warn) { - std::string content = it->ChooseContent(Language::any).Str(); - unsigned int langInt = it->ChooseContent(Language::any).Language(); - std::string opener; - if (langInt == Language::english || langInt == Language::any) { - opener = "Contains dirty edits: "; - } - else if (langInt == Language::spanish) { - opener = "Contiene ediciones sucia: "; - } - else if (langInt == Language::russian) { - opener = "\"Грязные\" правки: "; - } - - if (boost::starts_with(content, opener)) { - //Convert to new data structure. - //Extract ITM, UDR counts from message, CRC from condition, and utility + link from message. - unsigned int itm = 0; - unsigned int udr = 0; - uint32_t crc; - std::string utility; - size_t pos1, pos2; - std::string content = it->ChooseContent(Language::any).Str(); - std::string condition = it->Condition(); - - //Extract CRC. - pos1 = condition.find("checksum("); - if (pos1 == std::string::npos) { - ++it; - continue; - } - pos1 = condition.find(',', pos1); - if (pos1 == std::string::npos) { - ++it; - continue; - } - pos2 = condition.find(')', pos1); - if (pos2 == std::string::npos) { - ++it; - continue; - } - crc = strtoul(condition.substr(pos1+1, pos2-pos1-1).c_str(), NULL, 16); - - //Extract ITM count. - pos1 = content.find("ITM"); - if (pos1 != std::string::npos) { - pos2 = content.rfind(':', pos1); - if (pos2 != std::string::npos) { - itm = atoi(content.substr(pos2+1, pos1-pos2-1).c_str()); - } - } - - //Extract UDR count. - pos2 = content.find("UDR"); - if (pos2 != std::string::npos) { - pos1 = content.rfind(',', pos2); - if (pos1 != std::string::npos) { - udr = atoi(content.substr(pos1+1, pos2-pos1-1).c_str()); - } - } - - //Extract utility and link. - pos1 = content.find('"'); - if (pos1 != std::string::npos) { - pos2 = content.find(' ', pos1); - if (pos2 == std::string::npos) - pos2 = content.find('"', pos1+1); - if (pos2 != std::string::npos) { - utility = content.substr(pos1, pos2-pos1); - } - } - pos2 = content.find("Edit"); - if (pos2 != std::string::npos) { - pos1 = content.rfind(' ', pos2); - if (pos1 != std::string::npos) { - if (utility.empty()) - utility = content.substr(pos1 + 1, pos2 + 3 - pos1); - else - utility += " " + content.substr(pos1 + 1, pos2 + 3 - pos1) + '"'; - } - } - dirtyInfo.insert(PluginDirtyInfo(crc, itm, udr, 0, utility)); - it = messages.erase(it); - - } else - ++it; - } else - ++it; - } - - plugin.DirtyInfo(dirtyInfo); - plugin.Messages(messages); - plugin.Tags(tags); - } - - void MakeMessage(Message& message, std::string& condition, unsigned int type, std::string& content) { - - //First check if the message has a language condition. If it does, remove it and use it to set the language of the message content. - - //Just in case there are multiple language conditions, erase them all and use the last. - std::string lang; - size_t pos1; - while ((pos1 = condition.find("lang(")) != std::string::npos) { - - if (IsPosInQuotedText(condition, pos1)) { - pos1 += 5; - continue; - } - - size_t pos2 = condition.find(')', pos1); - - lang = condition.substr(pos1 + 6, pos2 - 1 - (pos1 + 6)); - condition.erase(pos1, pos2 + 1 - pos1); - - //Also need to trim any leftover "and", "or", "if" and "ifnot". - - //First erase "if" and "ifnot". - if (pos1 >= 6) { - if (condition.substr(pos1 - 6, 5) == "ifnot") { - condition.erase(pos1 - 6, 6); - pos1 -= 6; - } else if (condition.substr(pos1 - 3, 2) == "if") { - condition.erase(pos1 - 3, 3); - pos1 -= 3; - } - } else if (pos1 == 3) { //First "if" condition. - condition.erase(0, 3); - pos1 = 0; - } - - //Now look immediately before the erased conditional for "and" and "or" and erase any found. If the conditional is the first, then do the same for any "and" and "or" immediately after it. - - if (pos1 == 0) { - if (condition.substr(pos1, 5) == " and ") - condition.erase(pos1, 5); - else if (condition.substr(pos1, 4) == " or ") - condition.erase(pos1, 4); - } else { - - assert(pos1 > 5); //Shouldn't be anything <= 5 here, but just to be safe. - if (condition.substr(pos1 - 5, 5) == " and ") - condition.erase(pos1 - 5, 5); - else if (condition.substr(pos1 - 4, 4) == " or ") - condition.erase(pos1 - 4, 4); - } - } - - //LOOT supports Chinese and German in addition to English, Russian and Spanish, but they aren't used in any of the masterlists so don't bother mapping them. - unsigned int langInt; - if (lang == "english" || lang == "chinese" || lang == "german") - langInt = Language::english; - else if (lang == "russian") - langInt = Language::russian; - else if (lang == "spanish") - langInt = Language::spanish; - else - langInt = Language::any; - - - /* Now convert the temporary message types into say or warning messages as appropriate. Translations were obtained from the LOOT translation files. Thanks to the translators for them. */ - - if (type == message_dirty) { - - std::string search; - std::string opener; - if (langInt == Language::english || langInt == Language::any) { - search = "do not clean"; - opener = "Contains dirty edits: "; - } - else if (langInt == Language::spanish) { - search = "no se limpia"; - opener = "Contiene ediciones sucia: "; - } - else if (langInt == Language::russian) { - search = "Не очищать"; - opener = "\"Грязные\" правки: "; - } - - if (boost::icontains(content, search)) - type = Message::say; - else - type = Message::warn; - - content = opener + content; - } else if (type == message_req) { - type = Message::say; - - std::string opener; - if (langInt == Language::english || langInt == Language::any) - opener = "Requires: "; - else if (langInt == Language::spanish) - opener = "Requiere: "; - else if (langInt == Language::russian) - opener = "Требуется: "; - - content = opener + content; - } else if (type == message_inc) { - type = Message::say; - - std::string opener; - if (langInt == Language::english || langInt == Language::any) - opener = "Incompatible with: "; - else if (langInt == Language::spanish) - opener = "Incompatible con: "; - else if (langInt == Language::russian) - opener = "Несовместим с: "; - - content = opener + content; - } - - std::vector mc_vec; - mc_vec.push_back(MessageContent(content, langInt)); - - if (!condition.empty()) - ConvertCondition(condition); - - message = Message(type, mc_vec, condition); - } - - //Parser error reporter. - void SyntaxError(Iter const& /*first*/, Iter const& last, Iter const& errorpos, boost::spirit::info const& what) { - std::string context(errorpos, min(errorpos +50, last)); - boost::trim(context); - - BOOST_LOG_TRIVIAL(error) << "Error parsing at \"" << context << "\", expected \"" << what.tag << "\"."; - throw loot::error(loot::error::condition_eval_fail, "Expected \"" + what.tag + "\" at \"" + context + "\"."); //Isn't used by LOOT application, so don't need to translate. - } - }; - - void LoadBOSSMasterlist(boost::filesystem::path& file, std::list& plugins, std::list& globalMessages) { - Skipper skipper; - LegacyMasterlistGrammar grammar; - std::string::const_iterator begin, end; - std::string contents; - - grammar.SetGlobalMessageBuffer(&globalMessages); - - loot::ifstream ifile(file); - ifile.unsetf(std::ios::skipws); // No white space skipping! - - std::copy( - std::istream_iterator(ifile), - std::istream_iterator(), - std::back_inserter(contents) - ); - - begin = contents.begin(); - end = contents.end(); - - bool r = phrase_parse(begin, end, grammar, skipper, plugins); - - if (!r || begin != end) { - BOOST_LOG_TRIVIAL(error) << "Failed to read file: " << file.string(); - throw loot::error(loot::error::path_read_fail, file.string()); - } - } -} -#endif diff --git a/src/converter.cpp b/src/converter.cpp deleted file mode 100644 index be6ec6f0..00000000 --- a/src/converter.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* 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 "backend/legacy-parser.h" -#include "backend/generators.h" -#include "backend/streams.h" -#include "backend/metadata.h" - -using namespace std; - -/** - @file converter.cpp - @brief The masterlist converter utility. - - @section about_sec About - - The masterlist converter is a command line utility that takes two optional - command line arguments: - - ``` - masterlist-converter.exe [BOSS masterlist input] [LOOT masterlist output] - ``` - - If only one argument is given, or if no arguments are given, the converter - assumes it was called as - - ``` - masterlist-converter.exe masterlist.txt masterlist.yaml - ``` - - On encountering an error, it will print an error message to the console, - and will not output a LOOT masterlist. - - The converter does not perform a lossless conversion. The following do not - get transferred into the new masterlist: - - * Silent comments. (Search regex: `^(/\*|//)`) - * Plugin conditions. (Search regex: `^IF(NOT)?.+MOD:`) - * Requirement messages containing plugin filenames. (Search regex: - `REQ:.+(\.esp|\.esm)`) - * Dirty message content. The ITM, UDR and Navmesh counts, along with CRCs - and the dirty utility referenced are transferred, but any additional - content, such as links to additional instructions, are lost. (Search - regex: `DIRTY:`.) - - In addition, while other data is retained, it needs some manual adjustment, - eg. translated messages need are converted as separate messages and should - be placed into message content objects. -*/ - -int main(int argc, char * argv[]) { - string in_str, out_str; - if (argc < 3) { - in_str = "masterlist.txt"; - out_str = "masterlist.yaml"; - } else { - in_str = argv[1]; - out_str = argv[2]; - } - - boost::log::core::get()->set_logging_enabled(false); - - // Set up emitter. - YAML::Emitter yout; - yout.SetIndent(2); - - list test_plugins; - list globalMessages; - - //Parse the BOSS masterlist and output it as a LOOT masterlist. - try { - LoadBOSSMasterlist(boost::filesystem::path(in_str), test_plugins, globalMessages); - } catch (exception& e) { - cout << "An error was encountered during masterlist parsing. Message: " << e.what() << endl; - return 1; - } - - yout << YAML::BeginMap - << YAML::Key << "globals" << YAML::Value << globalMessages - << YAML::Key << "plugins" << YAML::Value << test_plugins - << YAML::EndMap; - - // Save output. - boost::filesystem::path out_path(out_str); - loot::ofstream out(out_path); - out << yout.c_str(); - out.close(); - - return 0; -}