Masterlist update now only tests condition parsing.

Instead of evaluating the conditions, and modifying the data stored in
the masterlist object. This will probably also speed up masterlist
updating a little. Fixes #257.
This commit is contained in:
WrinklyNinja
2014-08-28 22:34:06 +01:00
parent 26ed75d66a
commit adf1c3295b
4 changed files with 90 additions and 33 deletions
+3 -6
View File
@@ -424,17 +424,14 @@ namespace loot {
try {
this->MetadataList::Load(game.MasterlistPath());
unordered_set<Plugin> tempSet;
for (auto &plugin : plugins) {
tempSet.insert(Plugin(plugin).EvalAllConditions(game, language));
plugin.ParseAllConditions(game);
}
plugins = tempSet;
for (auto &plugin : regexPlugins) {
plugin.EvalAllConditions(game, language);
plugin.ParseAllConditions(game);
}
for (auto &message: messages) {
message.EvalCondition(game, language);
message.ParseCondition(game);
}
parsingFailed = false;
+54 -4
View File
@@ -118,7 +118,7 @@ namespace loot {
return _condition;
}
bool ConditionStruct::EvalCondition(loot::Game& game) const {
bool ConditionStruct::EvalCondition(Game& game) const {
if (_condition.empty())
return true;
@@ -128,12 +128,11 @@ namespace loot {
if (it != game.conditionCache.end())
return it->second;
condition_grammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar;
condition_grammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar(game, false);
boost::spirit::qi::space_type skipper;
std::string::const_iterator begin, end;
bool eval;
grammar.SetGame(game);
begin = _condition.begin();
end = _condition.end();
@@ -155,6 +154,39 @@ namespace loot {
return eval;
}
void ConditionStruct::ParseCondition(Game& game) const {
if (_condition.empty())
return;
BOOST_LOG_TRIVIAL(trace) << "Testing condition syntax: " << _condition;
// If the same condition string has already been evaluated, it must be written correctly.
unordered_map<std::string, bool>::const_iterator it = game.conditionCache.find(boost::locale::to_lower(_condition));
if (it != game.conditionCache.end())
return;
condition_grammar<std::string::const_iterator, boost::spirit::qi::space_type> grammar(game, true);
boost::spirit::qi::space_type skipper;
std::string::const_iterator begin, end;
begin = _condition.begin();
end = _condition.end();
bool r;
try {
r = boost::spirit::qi::phrase_parse(begin, end, grammar, skipper);
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\": " << e.what();
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\": %2%")) % _condition % e.what()).str());
}
if (!r || begin != end) {
BOOST_LOG_TRIVIAL(error) << "Failed to parse condition \"" << _condition << "\".";
throw loot::error(loot::error::condition_eval_fail, (boost::format(lc::translate("Failed to parse condition \"%1%\".")) % _condition).str());
}
}
MessageContent::MessageContent() : _language(Language::english) {}
MessageContent::MessageContent(const std::string& str, const unsigned int language) : _str(str), _language(language) {}
@@ -574,7 +606,7 @@ namespace loot {
_dirtyInfo = dirtyInfo;
}
Plugin& Plugin::EvalAllConditions(loot::Game& game, const unsigned int language) {
Plugin& Plugin::EvalAllConditions(Game& game, const unsigned int language) {
for (auto it = loadAfter.begin(); it != loadAfter.end();) {
if (!it->EvalCondition(game))
loadAfter.erase(it++);
@@ -634,6 +666,24 @@ namespace loot {
return *this;
}
void Plugin::ParseAllConditions(Game& game) const {
for (const File& file : loadAfter) {
file.ParseCondition(game);
}
for (const File& file : requirements) {
file.ParseCondition(game);
}
for (const File& file : incompatibilities) {
file.ParseCondition(game);
}
for (const Message& message : messages) {
message.ParseCondition(game);
}
for (const Tag& tag : tags) {
tag.ParseCondition(game);
}
}
bool Plugin::HasNameOnly() const {
return !IsPriorityExplicit() && loadAfter.empty() && requirements.empty() && incompatibilities.empty() && messages.empty() && tags.empty() && _dirtyInfo.empty();
}
+6 -4
View File
@@ -83,7 +83,8 @@ namespace loot {
ConditionStruct(const std::string& condition);
bool IsConditional() const;
bool EvalCondition(loot::Game& game) const;
bool EvalCondition(Game& game) const;
void ParseCondition(Game& game) const; // Throws error on parsing failure.
std::string Condition() const;
private:
@@ -116,7 +117,7 @@ namespace loot {
bool operator < (const Message& rhs) const;
bool operator == (const Message& rhs) const;
bool EvalCondition(loot::Game& game, const unsigned int language);
bool EvalCondition(Game& game, const unsigned int language);
unsigned int Type() const;
std::vector<MessageContent> Content() const;
@@ -166,7 +167,7 @@ namespace loot {
public:
Plugin();
Plugin(const std::string& name);
Plugin(loot::Game& game, const std::string& name, const bool headerOnly);
Plugin(Game& game, const std::string& name, const bool headerOnly);
//Merges from the given plugin into this one, unless there is already equal metadata present.
//For 'enabled' and 'priority' metadata, use the given plugin's values, but if the 'priority' user value is zero, ignore it.
@@ -204,7 +205,8 @@ namespace loot {
void Tags(const std::set<Tag>& tags);
void DirtyInfo(const std::set<PluginDirtyInfo>& info);
Plugin& EvalAllConditions(loot::Game& game, const unsigned int language);
Plugin& EvalAllConditions(Game& game, const unsigned int language);
void ParseAllConditions(Game& game) const;
bool HasNameOnly() const;
bool IsRegexPlugin() const;
bool LoadsBSA(const Game& game) const;
+27 -19
View File
@@ -400,7 +400,7 @@ namespace loot {
template<typename Iterator, typename Skipper>
class condition_grammar : public qi::grammar<Iterator, bool(), Skipper> {
public:
condition_grammar() : condition_grammar::base_type(expression, "condition grammar") {
condition_grammar(Game& game, bool parseOnly) : condition_grammar::base_type(expression, "condition grammar"), _game(game), _parseOnly(parseOnly) {
expression =
compound [qi::labels::_val = qi::labels::_1]
@@ -468,19 +468,18 @@ namespace loot {
qi::on_error<qi::fail>(invalidPathChars, phoenix::bind(&condition_grammar::SyntaxError, this, qi::labels::_1, qi::labels::_2, qi::labels::_3, qi::labels::_4));
}
void SetGame(loot::Game& g) {
game = &g;
}
private:
qi::rule<Iterator, bool(), Skipper> expression, compound, condition, function;
qi::rule<Iterator, std::string()> quotedStr, filePath, comparator;
qi::rule<Iterator, char()> invalidPathChars;
loot::Game * game;
Game& _game;
bool _parseOnly;
//Eval's exact paths. Check for files and ghosted plugins.
void CheckFile(bool& result, const std::string& file) {
if (_parseOnly)
return;
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the file \"" << file << "\" exists.";
@@ -495,9 +494,9 @@ namespace loot {
}
if (IsPlugin(file))
result = boost::filesystem::exists(game->DataPath() / file) || boost::filesystem::exists(game->DataPath() / (file + ".ghost"));
result = boost::filesystem::exists(_game.DataPath() / file) || boost::filesystem::exists(_game.DataPath() / (file + ".ghost"));
else
result = boost::filesystem::exists(game->DataPath() / file);
result = boost::filesystem::exists(_game.DataPath() / file);
if (result)
BOOST_LOG_TRIVIAL(trace) << "The file does exist.";
@@ -506,6 +505,8 @@ namespace loot {
}
void CheckRegex(bool& result, const std::string& regexStr) {
if (_parseOnly)
return;
result = false;
//Can't support a regex string where all path components may be regex, since this could
//lead to massive scanning if an unfortunately-named directory is encountered.
@@ -549,7 +550,7 @@ namespace loot {
//Now we have a valid parent path and a regex filename. Check that
//the parent path exists and is a directory.
boost::filesystem::path parent_path = game->DataPath() / parent;
boost::filesystem::path parent_path = _game.DataPath() / parent;
if (!boost::filesystem::exists(parent_path) || !boost::filesystem::is_directory(parent_path)) {
BOOST_LOG_TRIVIAL(trace) << "The path \"" << parent_path << "\" does not exist or is not a directory.";
return;
@@ -573,6 +574,8 @@ namespace loot {
}
void CheckSum(bool& result, const std::string& file, const uint32_t checksum) {
if (_parseOnly)
return;
BOOST_LOG_TRIVIAL(trace) << "Checking the CRC of the file \"" << file << "\".";
@@ -582,29 +585,31 @@ namespace loot {
}
uint32_t crc;
unordered_map<std::string,uint32_t>::iterator it = game->crcCache.find(boost::to_lower_copy(file));
unordered_map<std::string,uint32_t>::iterator it = _game.crcCache.find(boost::to_lower_copy(file));
if (it != game->crcCache.end())
if (it != _game.crcCache.end())
crc = it->second;
else {
if (file == "LOOT")
crc = GetCrc32(boost::filesystem::absolute("LOOT.exe"));
if (boost::filesystem::exists(game->DataPath() / file))
crc = GetCrc32(game->DataPath() / file);
else if (IsPlugin(file) && boost::filesystem::exists(game->DataPath() / (file + ".ghost")))
crc = GetCrc32(game->DataPath() / (file + ".ghost"));
if (boost::filesystem::exists(_game.DataPath() / file))
crc = GetCrc32(_game.DataPath() / file);
else if (IsPlugin(file) && boost::filesystem::exists(_game.DataPath() / (file + ".ghost")))
crc = GetCrc32(_game.DataPath() / (file + ".ghost"));
else {
result = false;
return;
}
game->crcCache.emplace(boost::to_lower_copy(file), crc);
_game.crcCache.emplace(boost::to_lower_copy(file), crc);
}
result = checksum == crc;
}
void CheckVersion(bool& result, const std::string& file, const std::string& version, const std::string& comparator) {
if (_parseOnly)
return;
BOOST_LOG_TRIVIAL(trace) << "Checking version of file \"" << file << "\".";
@@ -621,10 +626,10 @@ namespace loot {
if (file == "LOOT")
trueVersion = Version(boost::filesystem::absolute("LOOT.exe"));
else if (IsPlugin(file)) {
Plugin plugin(*game, file, true);
Plugin plugin(_game, file, true);
trueVersion = Version(plugin.Version());
} else
trueVersion = Version(game->DataPath() / file);
trueVersion = Version(_game.DataPath() / file);
BOOST_LOG_TRIVIAL(trace) << "Version extracted: " << trueVersion.AsString();
@@ -640,10 +645,13 @@ namespace loot {
}
void CheckActive(bool& result, const std::string& file) {
if (_parseOnly)
return;
if (file == "LOOT")
result = false;
else
result = game->IsActive(file);
result = _game.IsActive(file);
BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result;
}