diff --git a/README b/README
index bcb184dd..a20ef096 100644
--- a/README
+++ b/README
@@ -42,13 +42,15 @@ BOSSv3 won't have a command line interface. Command line options may be added to
Roadmap
=======
-1. Write new masterlist, userlist, settings file parsers. DONE
-2. Implement per-game handling. DONE
-3. Write API. DONE
-4. Develop sorting algorithm.
-5. Tie together automatic 'simple' sorting with masterlist, userlist data parsing and usage.
-6. Write masterlist updating code.
-7. Develop graphical user interface.
+- Write new masterlist, userlist, settings file parsers. DONE
+- Implement per-game handling. DONE
+- Write API. DONE
+- Develop sorting algorithm.
+- Tie together automatic 'simple' sorting with masterlist, userlist data parsing and usage. DONE
+- Write masterlist updating code.
+- Develop graphical user interface.
+- Error handling.
+- Initialisation and finishing routines (read settings file, write log file, etc.).
Sorting Algorithm
diff --git a/docs/BOSS Metadata File Syntax.html b/docs/BOSS Metadata File Syntax.html
index 29905d9a..f027c0ae 100644
--- a/docs/BOSS Metadata File Syntax.html
+++ b/docs/BOSS Metadata File Syntax.html
@@ -234,11 +234,11 @@ condition: "file(\"Mart's Monster Mod for OOO.esm\") or file(\"FCOM_Convergence.
tag | tag list | ✗ | An unordered list of Bash Tags suggested for this plugin.
Example:
name: "Oscuro's_Oblivion_Overhaul.esm"
diff --git a/src/metadata.cpp b/src/metadata.cpp
index 067c2625..edcff95a 100644
--- a/src/metadata.cpp
+++ b/src/metadata.cpp
@@ -169,7 +169,7 @@ namespace boss {
}
bool Tag::operator < (const Tag& rhs) const {
- return Name() < rhs.Name();
+ return (Name() < rhs.Name());
}
bool Tag::IsAddition() const {
@@ -216,6 +216,39 @@ namespace boss {
}
}
+ void Plugin::Merge(const Plugin& plugin) {
+ //If 'name' differs or if 'enabled' is false for the given plugin, don't change anything.
+ if (!boost::iequals(name, plugin.Name()) || !plugin.Enabled())
+ return;
+
+ //The following should be replaced.
+ priority = plugin.Priority();
+ masters = plugin.Masters();
+ formIDs = plugin.FormIDs();
+ isMaster = plugin.IsMaster();
+
+ //Merge the following. If any files in the source already exist in the destination, they will be skipped. Files have display strings and condition strings which aren't considered when comparing them, so will be lost if the plugin being merged in has additional data in these strings.
+ std::set files = plugin.LoadAfter();
+ loadAfter.insert(files.begin(), files.end());
+
+ files = plugin.Reqs();
+ requirements.insert(files.begin(), files.end());
+
+ files = plugin.Incs();
+ incompatibilities.insert(files.begin(), files.end());
+
+ //Merge Bash Tags too, but Tags can be added or removed, and BOSS should not be doing both for any one Tag, so whether a tag is added or removed is not considered during comparison. As such, the Tags from the plugin being merged in should override any equal-but-opposite Tags that already exist.
+ std::set bashTags = plugin.Tags();
+ bashTags.insert(tags.begin(), tags.end());
+ tags = bashTags;
+
+ //Messages are in an ordered list, and should be fully merged.
+ std::list pMessages = plugin.Messages();
+ messages.insert(messages.end(), pMessages.begin(), pMessages.end());
+
+ return;
+ }
+
std::string Plugin::Name() const {
return name;
}
@@ -228,11 +261,11 @@ namespace boss {
return priority;
}
- std::list Plugin::LoadAfter() const {
+ std::set Plugin::LoadAfter() const {
return loadAfter;
}
- std::list Plugin::Reqs() const {
+ std::set Plugin::Reqs() const {
return requirements;
}
@@ -244,7 +277,7 @@ namespace boss {
return messages;
}
- std::list Plugin::Tags() const {
+ std::set Plugin::Tags() const {
return tags;
}
@@ -260,11 +293,11 @@ namespace boss {
priority = p;
}
- void Plugin::LoadAfter(const std::list& l) {
+ void Plugin::LoadAfter(const std::set& l) {
loadAfter = l;
}
- void Plugin::Reqs(const std::list& r) {
+ void Plugin::Reqs(const std::set& r) {
requirements = r;
}
@@ -276,21 +309,21 @@ namespace boss {
messages = m;
}
- void Plugin::Tags(const std::list& t) {
+ void Plugin::Tags(const std::set& t) {
tags = t;
}
void Plugin::EvalAllConditions(boss::Game& game) {
- for (list::iterator it = loadAfter.begin(); it != loadAfter.end();) {
+ for (set::iterator it = loadAfter.begin(); it != loadAfter.end();) {
if (!it->EvalCondition(game))
- it = loadAfter.erase(it);
+ loadAfter.erase(it++);
else
++it;
}
- for (list::iterator it = requirements.begin(); it != requirements.end();) {
+ for (set::iterator it = requirements.begin(); it != requirements.end();) {
if (!it->EvalCondition(game))
- it = requirements.erase(it);
+ requirements.erase(it++);
else
++it;
}
@@ -309,9 +342,9 @@ namespace boss {
++it;
}
- for (list::iterator it = tags.begin(); it != tags.end();) {
+ for (set::iterator it = tags.begin(); it != tags.end();) {
if (!it->EvalCondition(game))
- it = tags.erase(it++);
+ tags.erase(it++);
else
++it;
}
@@ -352,6 +385,12 @@ namespace boss {
if (IsChildOf(rhs)) // Should probably also check for cyclic masters.
return false;
+ if (Priority() > rhs.Priority())
+ return true;
+
+ if (Priority() < rhs.Priority())
+ return false;
+
if (!OverlapFormIDs(rhs).empty() && formIDs.size() != rhs.FormIDs().size())
return formIDs.size() > rhs.FormIDs().size();
diff --git a/src/metadata.h b/src/metadata.h
index 3824ba92..b262da4f 100644
--- a/src/metadata.h
+++ b/src/metadata.h
@@ -125,23 +125,25 @@ namespace boss {
Plugin(const std::string& name);
Plugin(const std::string& name, const std::string& path);
+ void Merge(const Plugin& plugin);
+
std::string Name() const;
bool Enabled() const;
int Priority() const;
- std::list LoadAfter() const;
- std::list Reqs() const;
+ std::set LoadAfter() const;
+ std::set Reqs() const;
std::set Incs() const;
std::list Messages() const;
- std::list Tags() const;
+ std::set Tags() const;
void Name(const std::string& name);
void Enabled(const bool enabled);
void Priority(const int priority);
- void LoadAfter(const std::list& after);
- void Reqs(const std::list& reqs);
+ void LoadAfter(const std::set& after);
+ void Reqs(const std::set& reqs);
void Incs(const std::set& incs);
void Messages(const std::list& messages);
- void Tags(const std::list& tags);
+ void Tags(const std::set& tags);
void EvalAllConditions(boss::Game& game);
bool HasNameOnly() const;
@@ -167,17 +169,16 @@ namespace boss {
std::string name;
bool enabled; //Default to true.
int priority; //Default to 0 : >0 is higher, <0 is lower priorities.
- std::list loadAfter;
- std::list requirements;
+ std::set loadAfter;
+ std::set requirements;
std::set incompatibilities;
std::list messages;
- std::list tags;
+ std::set tags;
std::vector masters;
std::set formIDs;
bool isMaster;
};
-
}
#endif
diff --git a/src/parsers.h b/src/parsers.h
index 02dafc88..55760948 100644
--- a/src/parsers.h
+++ b/src/parsers.h
@@ -185,15 +185,15 @@ namespace YAML {
rhs.Priority(node["priority"].as());
if (node["after"])
- rhs.LoadAfter(node["after"].as< std::list >());
+ rhs.LoadAfter(node["after"].as< std::set >());
if (node["req"])
- rhs.Reqs(node["req"].as< std::list >());
+ rhs.Reqs(node["req"].as< std::set >());
if (node["inc"])
rhs.Incs(node["inc"].as< std::set >());
if (node["msg"])
rhs.Messages(node["msg"].as< std::list >());
if (node["tag"])
- rhs.Tags(node["tag"].as< std::list >());
+ rhs.Tags(node["tag"].as< std::set >());
return true;
}
};
diff --git a/src/tester.cpp b/src/tester.cpp
index 6f273617..e29af193 100644
--- a/src/tester.cpp
+++ b/src/tester.cpp
@@ -50,58 +50,26 @@ using namespace std;
const char * const libespm_options_path = "libespm.cfg";
const char * const libespm_game = "Skyrim";
+const char * const masterlist_path = "masterlist.yaml";
+const char * const userlist_path = "userlist.yaml";
+
int main(int argc, char *argv[]) {
- /* cout << "Testing masterlist parser." << endl;
+ /* Stuff still missing from a normal execution of BOSS:
- YAML::Node test = YAML::LoadFile("masterlist-example.yaml");
+ - Reading of settings file.
+ - Detecting game to run for.
+ - Error handling.
+ - Masterlist updating.
+ - Writing of log file.
- list globalMessages = test["globals"].as< list >();
- list pluginData = test["plugins"].as< list >();
+ Need to include libespm setup into game setup, once the former is made to be not global.
+ */
- cout << "Testing masterlist generator." << endl;
-
- // minimisePluginList(pluginData);
-
- ofstream out("generated.yaml");
- YAML::Emitter yout;
- yout.SetIndent(2);
- yout << YAML::BeginMap
- << YAML::Key << "globals" << YAML::Value << globalMessages
- << YAML::Key << "plugins" << YAML::Value << pluginData
- << YAML::EndMap;
-
- out << yout.c_str();
- out.close();
-
- cout << "Testing game settings structure." << endl;
-
- boss::Game game(boss::GAME_TES5, "/media/oliver/6CF05918F058EA3A/Program Files (x86)/Steam/steamapps/common/skyrim");
-
- for (list::iterator it=pluginData.begin(), endIt=pluginData.end(); it != endIt; ++it) {
- try {
- it->EvalAllConditions(game);
- } catch (boss::error& e) {
- cout << e.what() << endl;
- }
- }
-
- ofstream out2("evaled.yaml");
- YAML::Emitter yout2;
- yout2.SetIndent(2);
- yout2 << YAML::BeginMap
- << YAML::Key << "globals" << YAML::Value << globalMessages
- << YAML::Key << "plugins" << YAML::Value << pluginData
- << YAML::EndMap;
-
- out2 << yout2.c_str();
- out2.close();
- */
cout << "Setting up libespm and BOSS..." << endl;
-
-
+
// Set up libesm.
common::options::setGame(libespm_game);
ifstream input(libespm_options_path);
@@ -136,6 +104,82 @@ int main(int argc, char *argv[]) {
cout << "Time taken to read plugins: " << (end - start) << " seconds." << endl;
start = time(NULL);
+ YAML::Node mlist, ulist;
+ list messages, mlist_messages, ulist_messages;
+ list mlist_plugins, ulist_plugins;
+
+ if (fs::exists(masterlist_path)) {
+ cout << "Parsing masterlist..." << endl;
+
+ mlist = YAML::LoadFile(masterlist_path);
+ if (mlist["globals"])
+ mlist_messages = mlist["globals"].as< list >();
+ if (mlist["plugins"])
+ mlist_plugins = mlist["plugins"].as< list >();
+
+ end = time(NULL);
+ cout << "Time taken to parse masterlist: " << (end - start) << " seconds." << endl;
+ start = time(NULL);
+ }
+
+ if (fs::exists(userlist_path)) {
+ cout << "Parsing userlist..." << endl;
+
+ ulist = YAML::LoadFile(userlist_path);
+ if (ulist["globals"])
+ ulist_messages = ulist["globals"].as< list >();
+ if (ulist["plugins"])
+ ulist_plugins = ulist["plugins"].as< list >();
+
+ end = time(NULL);
+ cout << "Time taken to parse userlist: " << (end - start) << " seconds." << endl;
+ start = time(NULL);
+ }
+
+ if (fs::exists(masterlist_path) || fs::exists(userlist_path)) {
+ cout << "Merging plugin lists..." << endl;
+
+ //Merge all global message lists.
+ messages = mlist_messages;
+ messages.insert(messages.end(), ulist_messages.begin(), ulist_messages.end());
+
+ //Merge plugin list, masterlist and userlist plugin data.
+ for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) {
+ //Check if there is already a plugin in the 'plugins' list or not.
+ list::iterator pos = std::find(mlist_plugins.begin(), mlist_plugins.end(), *it);
+
+ if (pos != mlist_plugins.end()) {
+ //Need to merge plugins.
+ it->Merge(*pos);
+ }
+
+ pos = std::find(ulist_plugins.begin(), ulist_plugins.end(), *it);
+
+ if (pos != ulist_plugins.end()) {
+ //Need to merge plugins.
+ it->Merge(*pos);
+ }
+ }
+
+ end = time(NULL);
+ cout << "Time taken to merge lists: " << (end - start) << " seconds." << endl;
+ start = time(NULL);
+ }
+
+ cout << "Evaluating plugin list..." << endl;
+
+ for (list::iterator it=plugins.begin(), endIt=plugins.end(); it != endIt; ++it) {
+ try {
+ it->EvalAllConditions(game);
+ } catch (boss::error& e) {
+ cout << e.what() << endl;
+ }
+ }
+
+ end = time(NULL);
+ cout << "Time taken to evaluate plugin list: " << (end - start) << " seconds." << endl;
+ start = time(NULL);
+
for (list::iterator it=plugins.begin(), endIt = plugins.end(); it != endIt; ++it) {
cout << it->Name() << endl
<< '\t' << "Number of records: " << it->FormIDs().size() << endl
@@ -163,7 +207,8 @@ int main(int argc, char *argv[]) {
cout << "Sorting plugins..." << endl;
plugins.sort();
-
+
+ end = time(NULL);
cout << "Time taken to sort plugins: " << (end - start) << " seconds." << endl;
for (list::iterator it=plugins.begin(), endIt = plugins.end(); it != endIt; ++it)
|