diff --git a/src/backend/legacy-parser.h b/src/backend/legacy-parser.h
new file mode 100644
index 00000000..df67d63d
--- /dev/null
+++ b/src/backend/legacy-parser.h
@@ -0,0 +1,445 @@
+/* BOSS
+
+ A "one-click" program for users that quickly optimises and avoids
+ detrimental conflicts in their TES IV: Oblivion, Nehrim - At Fate's Edge,
+ TES V: Skyrim, Fallout 3 and Fallout: New Vegas mod load orders.
+
+ Copyright (C) 2009-2012 BOSS Development Team.
+
+ This file is part of BOSS.
+
+ BOSS 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.
+
+ BOSS 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 BOSS. If not, see
+ .
+
+ $Revision: 2188 $, $Date: 2011-01-20 10:05:16 +0000 (Thu, 20 Jan 2011) $
+*/
+
+#ifndef __BOSS_GRAMMAR_H__
+#define __BOSS_GRAMMAR_H__
+
+#ifndef BOOST_SPIRIT_UNICODE
+#define BOOST_SPIRIT_UNICODE
+#endif
+
+#include "Parsing/Grammar.h"
+#include "Common/Globals.h"
+#include "Common/Error.h"
+#include "Support/Helpers.h"
+#include "Support/Logger.h"
+#include "Output/Output.h"
+#include "Common/Classes.h"
+#include "Common/Game.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+//////////////////////////////
+// Modlist data conversions
+//////////////////////////////
+
+BOOST_FUSION_ADAPT_STRUCT(
+ boss::MasterlistVar,
+ (std::string, conditions)
+ (std::string, data)
+)
+
+BOOST_FUSION_ADAPT_STRUCT(
+ boss::Message,
+ (std::string, conditions)
+ (uint32_t, key)
+ (std::string, data)
+)
+
+BOOST_FUSION_ADAPT_STRUCT(
+ boss::Item,
+ (std::string, conditions)
+ (uint32_t, type)
+ (std::string, data)
+ (std::vector, messages)
+)
+
+
+namespace boss {
+ namespace fs = boost::filesystem;
+ namespace qi = boost::spirit::qi;
+ namespace unicode = boost::spirit::unicode;
+ namespace phoenix = boost::phoenix;
+
+ using namespace qi::labels;
+ using boost::algorithm::to_lower_copy;
+
+ using qi::skip;
+ using qi::eol;
+ using qi::eoi;
+ using qi::lexeme;
+ using qi::on_error;
+ using qi::fail;
+ using qi::lit;
+ using qi::omit;
+ using qi::eps;
+ using qi::hex;
+ using qi::bool_;
+ using qi::uint_;
+
+ using unicode::char_;
+ using unicode::no_case;
+ using unicode::space;
+ using unicode::xdigit;
+
+ using namespace std;
+
+ using qi::grammar;
+ using boost::spirit::info;
+
+ //typedef boost::u8_to_u32_iterator iterator_type;
+ //typedef iterator_type grammarIter;
+ typedef string::const_iterator grammarIter;
+
+ ///////////////////////////////
+ // Keyword structures
+ ///////////////////////////////
+
+ struct masterlistMsgKey_ : qi::symbols {
+ masterlistMsgKey_::masterlistMsgKey_() {
+ add //New Message keywords.
+ ("say",SAY)
+ ("tag",TAG)
+ ("req",REQ)
+ ("inc", INC)
+ ("dirty",DIRTY)
+ ("warn",WARN)
+ ("error",ERR)
+ ;
+ }
+ };
+
+ struct typeKey_ : qi::symbols {
+ typeKey_::typeKey_() {
+ add //Group keywords.
+ ("begingroup:", BEGINGROUP) //Needs the colon there unfortunately.
+ ("endgroup:", ENDGROUP) //Needs the colon there unfortunately.
+ ("endgroup", ENDGROUP)
+ ("mod:", MOD) //Needs the colon there unfortunately.
+ ("regex:", REGEX)
+ ;
+ }
+ };
+
+
+ ///////////////////////////////
+ //Skipper Grammar
+ ///////////////////////////////
+
+ //Skipper for userlist, modlist and ini parsers.
+ class Skipper : public grammar {
+ public:
+ Skipper::Skipper() : Skipper::base_type(start, "skipper grammar") {
+
+ start =
+ spc
+ | UTF8
+ | CComment
+ | CPlusPlusComment
+ | iniComment
+ | eof;
+
+ spc = space - eol;
+
+ UTF8 = char_("\xef") >> char_("\xbb") >> char_("\xbf"); //UTF8 BOM
+
+ CComment = "/*" >> *(char_ - "*/") >> "*/";
+
+ iniComment = lit("#") >> *(char_ - eol);
+
+ CPlusPlusComment = !(lit("http:") | lit("https:") | lit("file:")) >> "//" >> *(char_ - eol);
+
+ eof = *(spc | CComment | CPlusPlusComment | eol) >> eoi;
+ }
+
+ void Skipper::SkipIniComments(const bool b) {
+ if (b)
+ iniComment = (lit("#") | lit(";")) >> *(char_ - eol);
+ else
+ iniComment = CComment;
+ }
+ private:
+ qi::rule start, spc, eof, CComment, CPlusPlusComment, lineComment, iniComment, UTF8;
+ };
+
+ ///////////////////////////////
+ //Modlist/Masterlist Grammar
+ ///////////////////////////////
+
+ //Modlist/Masterlist grammar.
+ class modlist_grammar : public grammar(), Skipper> {
+ public:
+ modlist_grammar::modlist_grammar()
+ : modlist_grammar::base_type(modList, "modlist grammar"),
+ errorBuffer(NULL) {
+ masterlistMsgKey_ masterlistMsgKey;
+ typeKey_ typeKey;
+ const vector noMessages; //An empty set of messages.
+
+ modList =
+ *eol
+ >
+ (
+ listVar [phoenix::bind(&modlist_grammar::StoreVar, this, _1)]
+ | globalMessage [phoenix::bind(&modlist_grammar::StoreGlobalMessage, this, _1)]
+ | listItem [phoenix::bind(&modlist_grammar::StoreItem, this, _val, _1)]
+ ) % +eol;
+
+ listVar %=
+ conditionals
+ >> no_case[lit("set")]
+ >> (
+ ':'
+ > charString
+ );
+
+ globalMessage =
+ conditionals
+ >> no_case[lit("global")]
+ >> (
+ messageKeyword
+ >> ':'
+ >> charString
+ );
+
+ listItem %=
+ conditionals
+ > ItemType
+ > itemName
+ > itemMessages;
+
+ ItemType %=
+ no_case[typeKey]
+ | eps [_val = MOD];
+
+ itemName =
+ charString [phoenix::bind(&modlist_grammar::ToName, this, _val, _1)]
+ | eps [phoenix::bind(&modlist_grammar::ToName, this, _val, "")];
+
+ itemMessages %=
+ (
+ +eol
+ >> itemMessage % +eol
+ ) | eps [_1 = noMessages];
+
+ itemMessage %=
+ conditionals
+ >> messageKeyword
+ >> ':'
+ >> charString //The double >> matters. A single > doesn't work.
+ ;
+
+ charString %= lexeme[+(char_ - eol)]; //String, with no skipper.
+
+ messageKeyword %= no_case[masterlistMsgKey];
+
+ conditionals =
+ (
+ conditional [_val = _1]
+ > *((andOr > conditional) [_val += _1 + _2])
+ )
+ | no_case[unicode::string("else")][_val = _1]
+ | eps [_val = ""];
+
+ andOr %=
+ unicode::string("&&")
+ | unicode::string("||");
+
+ conditional %=
+ (
+ no_case[unicode::string("ifnot")
+ | unicode::string("if")]
+ )
+ > functCondition;
+
+ functCondition %=
+ (
+ no_case[unicode::string("var")] > char_('(') > variable > char_(')') //Variable condition.
+ ) | (
+ no_case[unicode::string("file")] > char_('(') > file > char_(')') //File condition.
+ ) | (
+ no_case[unicode::string("checksum")] > char_('(') > file > char_(',') > checksum > char_(')') //Checksum condition.
+ ) | (
+ no_case[unicode::string("version")] > char_('(') > file > char_(',') > version > char_(',') > comparator > char_(')') //Version condition.
+ ) | (
+ no_case[unicode::string("regex")] > char_('(') > regex > char_(')') //Regex condition.
+ ) | (
+ no_case[unicode::string("active")] > char_('(') > file > char_(')') //Active condition.
+ ) | (
+ no_case[unicode::string("lang")] > char_('(') > language > char_(')') //Language condition.
+ )
+ ;
+
+ variable %= +(char_ - (')' | eol));
+
+ file %= lexeme[char_('"') > +(char_ - ('"' | eol)) > char_('"')];
+
+ checksum %= +xdigit;
+
+ version %= file;
+
+ comparator %= char_('=') | char_('>') | char_('<');
+
+ regex %= file;
+
+ language %= file;
+
+
+ modList.name("modList");
+ listVar.name("listVar");
+ listItem.name("listItem");
+ ItemType.name("ItemType");
+ itemName.name("itemName");
+ itemMessages.name("itemMessages");
+ itemMessage.name("itemMessage");
+ charString.name("charString");
+ messageKeyword.name("messageKeyword");
+ conditionals.name("conditional");
+ andOr.name("andOr");
+ conditional.name("conditional");
+ functCondition.name("condition");
+ shortCondition.name("condition");
+ variable.name("variable");
+ file.name("file");
+ checksum.name("checksum");
+ version.name("version");
+ comparator.name("comparator");
+ regex.name("regex file");
+ file.name("language");
+
+ on_error(modList, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(listVar, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(listItem, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(ItemType, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(itemName, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(itemMessages, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(itemMessage, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(charString, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(messageKeyword, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(conditionals, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(andOr, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(conditional, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(functCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(shortCondition, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(variable, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(file, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(checksum, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(version, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(comparator, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(regex, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ on_error(language, phoenix::bind(&modlist_grammar::SyntaxError, this, _1, _2, _3, _4));
+ }
+
+ void modlist_grammar::SetErrorBuffer(ParsingError * inErrorBuffer) {
+ errorBuffer = inErrorBuffer;
+ }
+
+ void modlist_grammar::SetGlobalMessageBuffer(vector * inGlobalMessageBuffer) {
+ globalMessageBuffer = inGlobalMessageBuffer;
+ }
+
+ void modlist_grammar::SetVarStore(vector * varStore) {
+ setVars = varStore;
+ }
+
+ void modlist_grammar::SetCRCStore(boost::unordered_map * CRCStore) {
+ fileCRCs = CRCStore;
+ }
+
+ void modlist_grammar::SetParentGame(const Game * game) {
+ parentGame = game;
+ }
+ private:
+ qi::rule(), Skipper> modList;
+ qi::rule listItem;
+ qi::rule ItemType;
+ qi::rule itemName;
+ qi::rule(), Skipper> itemMessages;
+ qi::rule itemMessage, globalMessage;
+ qi::rule listVar;
+ qi::rule charString, andOr, conditional, conditionals, functCondition, shortCondition, variable, file, checksum, version, comparator, regex, language;
+ qi::rule messageKeyword;
+ ParsingError * errorBuffer;
+ vector * globalMessageBuffer;
+ vector * setVars; //Vars set by masterlist.
+ boost::unordered_map * fileCRCs; //CRCs calculated.
+ const Game * parentGame;
+ vector openGroups; //Need to keep track of which groups are open to match up endings properly in MF1.
+
+ //Parser error reporter.
+ void modlist_grammar::SyntaxError(grammarIter const& /*first*/, grammarIter const& last, grammarIter const& errorpos, boost::spirit::info const& what) {
+ if (errorBuffer == NULL || !errorBuffer->Empty())
+ return;
+
+ ostringstream out;
+ out << what;
+ string expect = out.str();
+
+ string context(errorpos, min(errorpos +50, last));
+ boost::trim_left(context);
+
+ ParsingError e(str(MasterlistParsingErrorHeader % expect), context, MasterlistParsingErrorFooter);
+ *errorBuffer = e;
+ LOG_ERROR(Outputter(PLAINTEXT, e).AsString().c_str());
+ return;
+ }
+
+ //Stores the given item and records any changes to open groups.
+ void modlist_grammar::StoreItem(vector- & list, Item currentItem) {
+ if (currentItem.Type() == BEGINGROUP)
+ openGroups.push_back(currentItem.Name());
+ else if (currentItem.Type() == ENDGROUP)
+ openGroups.pop_back();
+ if (!currentItem.Name().empty()) //A blank line can be confused with a mod entry.
+ list.push_back(currentItem);
+ }
+
+ //Stores the masterlist variable.
+ void modlist_grammar::StoreVar(const MasterlistVar var) {
+ setVars->push_back(var);
+ }
+
+ //Stores the global message.
+ void modlist_grammar::StoreGlobalMessage(const Message message) {
+ globalMessageBuffer->push_back(message);
+ }
+
+ //Turns a given string into a path. Can't be done directly because of the openGroups checks.
+ void modlist_grammar::ToName(string& p, string itemName) {
+ boost::algorithm::trim(itemName);
+ if (itemName.empty() && !openGroups.empty())
+ p = openGroups.back();
+ else
+ p = itemName;
+ }
+ };
+}
+#endif