diff --git a/docs/LOOT Metadata Syntax.html b/docs/LOOT Metadata Syntax.html
index c9840bdc..28bbd793 100644
--- a/docs/LOOT Metadata Syntax.html
+++ b/docs/LOOT Metadata Syntax.html
@@ -537,6 +537,7 @@ msg:
| Many Plugins Active Status | many_active("regex") | Returns true if more than one file matching regex is a .esp or .esm file that is both installed and active, false otherwise.
The different types of variable used in the above functions are described in more detail below.
diff --git a/src/backend/metadata/condition_grammar.h b/src/backend/metadata/condition_grammar.h
index 8dae5a99..a49a4faf 100644
--- a/src/backend/metadata/condition_grammar.h
+++ b/src/backend/metadata/condition_grammar.h
@@ -87,6 +87,7 @@ public:
| ("checksum(" > filePath_ > ',' > qi::hex > ')')[phoenix::bind(&ConditionGrammar::CheckSum, this, qi::labels::_val, qi::labels::_1, qi::labels::_2)]
| ("version(" > filePath_ > ',' > quotedStr_ > ',' > comparator_ > ')')[phoenix::bind(&ConditionGrammar::CheckVersion, this, qi::labels::_val, qi::labels::_1, qi::labels::_2, qi::labels::_3)]
| ("active(" > filePath_ > ')')[phoenix::bind(&ConditionGrammar::CheckActive, this, qi::labels::_val, qi::labels::_1)]
+ | ("many_active(" > quotedStr_ > ')')[phoenix::bind(&ConditionGrammar::CheckManyActive, this, qi::labels::_val, qi::labels::_1)]
;
quotedStr_ %= '"' > +(char_ - '"') > '"';
@@ -376,6 +377,36 @@ private:
BOOST_LOG_TRIVIAL(trace) << "Active check result: " << result;
}
+ void CheckManyActive(bool& result, const std::string& regexStr) const {
+ result = false;
+
+ BOOST_LOG_TRIVIAL(trace) << "Checking to see if more than one file matching the regex \"" << regexStr << "\" exist.";
+
+ std::pair pathRegex = SplitRegex(regexStr);
+
+ if (game_ == nullptr)
+ return;
+
+ // 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() / pathRegex.first;
+ 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;
+ }
+
+ size_t count = 0;
+ for (boost::filesystem::directory_iterator itr(parent_path); itr != boost::filesystem::directory_iterator(); ++itr) {
+ const std::string filename = itr->path().filename().string();
+ if (std::regex_match(filename, pathRegex.second) && game_->IsPluginActive(filename)) {
+ ++count;
+ BOOST_LOG_TRIVIAL(trace) << "Matching file found: " << itr->path();
+ }
+ }
+
+ result = count > 1;
+ }
+
void SyntaxError(Iterator const& /*first*/, Iterator const& last, Iterator const& errorpos, boost::spirit::info const& what) {
std::string context(errorpos, last);
boost::trim(context);
diff --git a/src/tests/backend/metadata/condition_grammar_test.h b/src/tests/backend/metadata/condition_grammar_test.h
index 82d925a0..8f959148 100644
--- a/src/tests/backend/metadata/condition_grammar_test.h
+++ b/src/tests/backend/metadata/condition_grammar_test.h
@@ -561,6 +561,51 @@ TEST_P(ConditionGrammarTest, anActiveConditionWithAPluginThatIsNotActiveShouldEv
EXPECT_FALSE(result_);
}
+TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingMoreThanOnePluginThatIsActiveShouldEvaluateToTrue) {
+ ASSERT_NO_THROW(game_.Init(false, localPath));
+
+ Grammar grammar(&game_);
+ std::string condition("many_active(\"Blank( - Different Master Dependent)?\\.es(m|p)\")");
+
+ success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
+ std::cend(condition),
+ grammar,
+ skipper_,
+ result_);
+ EXPECT_TRUE(success_);
+ EXPECT_TRUE(result_);
+}
+
+TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingOnlyOnePluginThatIsActiveShouldEvaluateToFalse) {
+ ASSERT_NO_THROW(game_.Init(false, localPath));
+
+ Grammar grammar(&game_);
+ std::string condition("many_active(\"Blank\\.esm\")");
+
+ success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
+ std::cend(condition),
+ grammar,
+ skipper_,
+ result_);
+ EXPECT_TRUE(success_);
+ EXPECT_FALSE(result_);
+}
+
+TEST_P(ConditionGrammarTest, aManyActiveConditionWithARegexMatchingNoPluginsThatAreActiveShouldEvaluateToFalse) {
+ ASSERT_NO_THROW(game_.Init(false, localPath));
+
+ Grammar grammar(&game_);
+ std::string condition("many_active(\"Blank\\.esp\")");
+
+ success_ = boost::spirit::qi::phrase_parse(std::cbegin(condition),
+ std::cend(condition),
+ grammar,
+ skipper_,
+ result_);
+ EXPECT_TRUE(success_);
+ EXPECT_FALSE(result_);
+}
+
TEST_P(ConditionGrammarTest, aFalseConditionPrecededByANegatorShouldEvaluateToTrue) {
Grammar grammar(&game_);
std::string condition("not file(\"" + missingEsp + "\")");
|