mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Merge branch 'multithreading'
Manually merged src/gui/main.cpp to retain changes from both the 'multithreading' branch and the 'graph-sorting' branch.
This commit is contained in:
@@ -565,17 +565,13 @@ namespace YAML {
|
||||
}
|
||||
|
||||
inline Emitter& operator << (Emitter& out, const boss::MessageContent& rhs) {
|
||||
if (rhs.Language() == boss::g_lang_any)
|
||||
out << rhs.Str();
|
||||
else {
|
||||
out << BeginMap;
|
||||
out << BeginMap;
|
||||
|
||||
out << Key << "lang" << Value << boss::GetLangString(rhs.Language());
|
||||
out << Key << "lang" << Value << boss::GetLangString(rhs.Language());
|
||||
|
||||
out << Key << "str" << Value << rhs.Str();
|
||||
out << Key << "str" << Value << rhs.Str();
|
||||
|
||||
out << EndMap;
|
||||
}
|
||||
out << EndMap;
|
||||
}
|
||||
|
||||
inline Emitter& operator << (Emitter& out, const boss::Message& rhs) {
|
||||
@@ -588,7 +584,10 @@ namespace YAML {
|
||||
else
|
||||
out << Key << "type" << Value << "error";
|
||||
|
||||
out << Key << "content" << Value << rhs.Content();
|
||||
if (rhs.Content().size() == 1 && rhs.Content().front().Language() == boss::g_lang_any)
|
||||
out << Key << "content" << Value << rhs.Content().front().Str();
|
||||
else
|
||||
out << Key << "content" << Value << rhs.Content();
|
||||
|
||||
if (!rhs.Condition().empty())
|
||||
out << Key << "condition" << Value << rhs.Condition();
|
||||
|
||||
@@ -501,11 +501,6 @@ namespace boss {
|
||||
std::list<Message>::iterator it = messages.begin();
|
||||
while (it != messages.end()) {
|
||||
|
||||
std::string condition = it->Condition();
|
||||
ConvertCondition(condition);
|
||||
|
||||
*it = Message(it->Type(), it->Content(), condition);
|
||||
|
||||
if (it->Type() == g_message_tag) {
|
||||
std::string message = it->ChooseContent(g_lang_any).Str();
|
||||
|
||||
@@ -684,6 +679,8 @@ namespace boss {
|
||||
std::vector<MessageContent> mc_vec;
|
||||
mc_vec.push_back(MessageContent(content, langInt));
|
||||
|
||||
ConvertCondition(condition);
|
||||
|
||||
message = Message(type, mc_vec, condition);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -83,6 +84,8 @@ namespace boss {
|
||||
}
|
||||
|
||||
bool ConditionStruct::EvalCondition(boss::Game& game) const {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Evaluating condition: " << _condition;
|
||||
|
||||
if (_condition.empty())
|
||||
return true;
|
||||
|
||||
@@ -153,6 +156,10 @@ namespace boss {
|
||||
}
|
||||
|
||||
bool Message::EvalCondition(boss::Game& game, const unsigned int language) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Choosing message language.";
|
||||
|
||||
|
||||
if (_content.size() > 1) {
|
||||
if (language == g_lang_any) //Can use a message of any language, so use the first string.
|
||||
_content.resize(1);
|
||||
|
||||
+19
-7
@@ -193,7 +193,7 @@ namespace boss {
|
||||
throw boss::error(boss::error::git_error, error_message);
|
||||
}
|
||||
|
||||
std::string UpdateMasterlist(Game& game, std::vector<std::string>& parsingErrors) {
|
||||
std::string UpdateMasterlist(Game& game, std::list<Message>& parsingErrors, std::list<Plugin>& plugins, std::list<Message>& messages) {
|
||||
|
||||
//First need to decide how the masterlist is updated: using Git or Subversion?
|
||||
//Look at the update URL to decide.
|
||||
@@ -268,16 +268,19 @@ namespace boss {
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Reading the masterlist version from the svn info output.";
|
||||
revision = GetRevision(output);
|
||||
std::string newRevision = GetRevision(output);
|
||||
|
||||
//Check if revision has changed. If it hasn't, exit early.
|
||||
if (newRevision == revision)
|
||||
return revision;
|
||||
else
|
||||
revision = newRevision;
|
||||
|
||||
try {
|
||||
//Now test masterlist to see if it parses OK.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Testing the new masterlist to see if it parses OK.";
|
||||
YAML::Node mlist = YAML::LoadFile(game.MasterlistPath().string());
|
||||
|
||||
list<boss::Message> messages;
|
||||
list<boss::Plugin> plugins;
|
||||
|
||||
if (mlist["globals"])
|
||||
messages = mlist["globals"].as< list<boss::Message> >();
|
||||
if (mlist["plugins"])
|
||||
@@ -298,7 +301,7 @@ namespace boss {
|
||||
|
||||
//Roll back one revision if there's an error.
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + revision + ": " + e.what();
|
||||
parsingErrors.push_back("Masterlist revision " + revision + ": " + e.what());
|
||||
parsingErrors.push_back(boss::Message(boss::g_message_error, "Masterlist revision " + revision + ": " + e.what()));
|
||||
|
||||
|
||||
command = g_path_svn.string() + " update --revision PREV \"" + game.MasterlistPath().string() + "\"";
|
||||
@@ -446,6 +449,10 @@ namespace boss {
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Received " << stats->indexed_objects << " of " << stats->total_objects << " objects in " << stats->received_bytes << " bytes.";
|
||||
|
||||
bool exitEarly = false;
|
||||
if (stats->received_bytes == 0) //No update received.
|
||||
exitEarly = true;
|
||||
|
||||
// Disconnect from the remote repository.
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Disconnecting from remote.";
|
||||
@@ -508,6 +515,11 @@ namespace boss {
|
||||
|
||||
git_object_free(ptrs.obj);
|
||||
|
||||
if (exitEarly) {
|
||||
ptrs.free();
|
||||
return string(revision);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Testing masterlist parsing.";
|
||||
|
||||
//Now try parsing the masterlist.
|
||||
@@ -536,7 +548,7 @@ namespace boss {
|
||||
//Roll back one revision if there's an error.
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Masterlist revision " + string(revision) + ": " + e.what();
|
||||
|
||||
parsingErrors.push_back("Masterlist revision " + string(revision) + ": " + e.what());
|
||||
parsingErrors.push_back(boss::Message(boss::g_message_error, "Masterlist revision " + string(revision) + ": " + e.what()));
|
||||
}
|
||||
} while (parsingFailed);
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "game.h"
|
||||
#include "metadata.h"
|
||||
|
||||
namespace boss {
|
||||
|
||||
@@ -62,6 +63,6 @@ namespace boss {
|
||||
So when using Git, the Game::URL() function would return the path to the repository, and BOSS would expect that the masterlist be in the root directory of the repository, which is not unreasonable.
|
||||
*/
|
||||
|
||||
std::string UpdateMasterlist(Game& game, std::vector<std::string>& parsingErrors);
|
||||
std::string UpdateMasterlist(Game& game, std::list<Message>& parsingErrors, std::list<Plugin>& plugins, std::list<Message>& messages);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -167,8 +167,9 @@ namespace YAML {
|
||||
std::vector<boss::MessageContent> content;
|
||||
if (node["content"].IsSequence())
|
||||
content = node["content"].as< std::vector<boss::MessageContent> >();
|
||||
else
|
||||
content.push_back(node["content"].as<std::string>());
|
||||
else {
|
||||
content.push_back(boss::MessageContent(node["content"].as<std::string>()));
|
||||
}
|
||||
|
||||
//Check now that at least one item in content is English if there are multiple items.
|
||||
if (content.size() > 1) {
|
||||
@@ -177,9 +178,8 @@ namespace YAML {
|
||||
if (it->Language() == boss::g_lang_english)
|
||||
found = true;
|
||||
}
|
||||
//Commented out because the auto-converted masterlist has localisations as separate messages and so will always fail.
|
||||
// if (!found)
|
||||
// return false;
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string condition;
|
||||
@@ -570,6 +570,9 @@ namespace boss {
|
||||
|
||||
//Checks that the path (not regex) doesn't go outside any game folders.
|
||||
bool IsSafePath(const std::string& path) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Checking to see if the path \"" << path << "\" is safe.";
|
||||
|
||||
std::vector<std::string> components;
|
||||
boost::split(components, path, boost::is_any_of("/\\"));
|
||||
components.pop_back();
|
||||
|
||||
+106
-59
@@ -56,7 +56,7 @@
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/support/date_time.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include <wx/snglinst.h>
|
||||
#include <wx/aboutdlg.h>
|
||||
@@ -75,10 +75,13 @@ namespace loc = boost::locale;
|
||||
|
||||
|
||||
struct plugin_loader {
|
||||
plugin_loader(boss::Plugin& plugin, boss::Game& game, const string& filename, bool b) : _plugin(plugin), _game(game), _filename(filename), _b(b) {}
|
||||
plugin_loader(boss::Plugin& plugin, boss::Game& game) : _plugin(plugin), _game(game) {
|
||||
}
|
||||
|
||||
void operator () () {
|
||||
_plugin = boss::Plugin(_game, _filename, _b);
|
||||
BOOST_LOG_TRIVIAL(info) << "Loading: " << _plugin.Name();
|
||||
_plugin = boss::Plugin(_game, _plugin.Name(), false);
|
||||
BOOST_LOG_TRIVIAL(info) << "Finished loading: " << _plugin.Name();
|
||||
}
|
||||
|
||||
boss::Plugin& _plugin;
|
||||
@@ -92,13 +95,57 @@ struct plugin_list_loader {
|
||||
|
||||
void operator () () {
|
||||
for (list<boss::Plugin>::iterator it=_plugins.begin(), endit=_plugins.end(); it != endit; ++it) {
|
||||
if (!boost::iequals(it->Name(), _game.Master()))
|
||||
if (skipPlugins.find(it->Name()) == skipPlugins.end()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Loading: " << it->Name();
|
||||
*it = boss::Plugin(_game, it->Name(), false);
|
||||
BOOST_LOG_TRIVIAL(info) << "Finished loading: " << it->Name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list<boss::Plugin>& _plugins;
|
||||
boss::Game& _game;
|
||||
set<string> skipPlugins;
|
||||
};
|
||||
|
||||
struct masterlist_updater_parser {
|
||||
masterlist_updater_parser(boss::Game& game, list<boss::Message>& errors, list<boss::Plugin>& plugins, list<boss::Message>& messages, string& revision) : _game(game), _errors(errors), _plugins(plugins), _messages(messages), _revision(revision) {}
|
||||
|
||||
void operator () () {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Updating masterlist";
|
||||
try {
|
||||
_revision = UpdateMasterlist(_game, _errors, _plugins, _messages);
|
||||
} catch (boss::error& e) {
|
||||
_plugins.clear();
|
||||
_messages.clear();
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist update failed. Details: " << e.what();
|
||||
_errors.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Masterlist update failed. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
|
||||
if (_plugins.empty() && _messages.empty() && fs::exists(_game.MasterlistPath())) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Parsing masterlist...";
|
||||
try {
|
||||
YAML::Node mlist = YAML::LoadFile(_game.MasterlistPath().string());
|
||||
|
||||
if (mlist["globals"])
|
||||
_messages = mlist["globals"].as< list<boss::Message> >();
|
||||
if (mlist["plugins"])
|
||||
_plugins = mlist["plugins"].as< list<boss::Plugin> >();
|
||||
} catch (YAML::Exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Details: " << e.what();
|
||||
_errors.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Masterlist parsing failed. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace) << "Finished parsing masterlist.";
|
||||
}
|
||||
}
|
||||
|
||||
boss::Game& _game;
|
||||
list<boss::Message>& _errors;
|
||||
list<boss::Plugin>& _plugins;
|
||||
list<boss::Message>& _messages;
|
||||
string& _revision;
|
||||
};
|
||||
|
||||
bool BossGUI::OnInit() {
|
||||
@@ -407,66 +454,56 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
YAML::Node mlist, ulist;
|
||||
list<boss::Message> messages, mlist_messages, ulist_messages;
|
||||
list<boss::Plugin> mlist_plugins, ulist_plugins;
|
||||
boost::thread_group group;
|
||||
list<boss::Plugin> plugins;
|
||||
string revision;
|
||||
|
||||
wxProgressDialog *progDia = new wxProgressDialog(translate("BOSS: Working..."),translate("BOSS working..."), 1000, this, wxPD_APP_MODAL|wxPD_AUTO_HIDE|wxPD_ELAPSED_TIME);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Updating masterlist";
|
||||
masterlist_updater_parser mup(_game, messages, mlist_plugins, mlist_messages, revision);
|
||||
group.create_thread(mup);
|
||||
|
||||
vector<string> parsingErrors;
|
||||
string revision;
|
||||
try {
|
||||
// revision = UpdateMasterlist(_game, parsingErrors);
|
||||
} catch (boss::error& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist update failed. Details: " << e.what();
|
||||
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Masterlist update failed. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
for (vector<string>::const_iterator it=parsingErrors.begin(), endit=parsingErrors.end(); it != endit; ++it) {
|
||||
messages.push_back(boss::Message(boss::g_message_error, *it));
|
||||
}
|
||||
|
||||
// Get a list of the plugins.
|
||||
list<boss::Plugin> plugins;
|
||||
boost::thread_group group;
|
||||
//First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation.
|
||||
size_t meanFileSize = 0;
|
||||
boost::unordered_map<size_t, boss::Plugin> tempMap;
|
||||
for (fs::directory_iterator it(_game.DataPath()); it != fs::directory_iterator(); ++it) {
|
||||
if (fs::is_regular_file(it->status()) && IsPlugin(it->path().string())) {
|
||||
size_t fileSize = fs::file_size(it->path());
|
||||
meanFileSize += fileSize;
|
||||
|
||||
string filename = it->path().filename().string();
|
||||
BOOST_LOG_TRIVIAL(trace) << "Reading plugin: " << filename;
|
||||
|
||||
plugins.push_back(boss::Plugin(filename));
|
||||
|
||||
if (filename == _game.Master()) {
|
||||
plugin_loader pl(plugins.back(), _game, filename, false);
|
||||
group.create_thread(pl);
|
||||
}
|
||||
|
||||
progDia->Pulse();
|
||||
tempMap.emplace(fileSize, boss::Plugin(it->path().filename().string()));
|
||||
}
|
||||
}
|
||||
meanFileSize /= tempMap.size();
|
||||
|
||||
//Now load plugins.
|
||||
plugin_list_loader pll(plugins, _game);
|
||||
for (boost::unordered_map<size_t, boss::Plugin>::const_iterator it=tempMap.begin(), endit=tempMap.end(); it != endit; ++it) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Found plugin: " << it->second.Name();
|
||||
|
||||
plugins.push_back(it->second);
|
||||
|
||||
if (it->first > meanFileSize) {
|
||||
plugin_loader pl(plugins.back(), _game);
|
||||
pll.skipPlugins.insert(it->second.Name());
|
||||
group.create_thread(pl);
|
||||
}
|
||||
|
||||
progDia->Pulse();
|
||||
}
|
||||
group.create_thread(pll);
|
||||
group.join_all();
|
||||
|
||||
if (fs::exists(_game.MasterlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Parsing masterlist...";
|
||||
|
||||
try {
|
||||
mlist = YAML::LoadFile(_game.MasterlistPath().string());
|
||||
} catch (YAML::ParserException& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Masterlist parsing failed. Details: " << e.what();
|
||||
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Masterlist parsing failed. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
if (mlist["globals"])
|
||||
mlist_messages = mlist["globals"].as< list<boss::Message> >();
|
||||
if (mlist["plugins"])
|
||||
mlist_plugins = mlist["plugins"].as< list<boss::Plugin> >();
|
||||
}
|
||||
|
||||
//Now load userlist.
|
||||
if (fs::exists(_game.UserlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Parsing userlist...";
|
||||
|
||||
try {
|
||||
ulist = YAML::LoadFile(_game.UserlistPath().string());
|
||||
YAML::Node ulist = YAML::LoadFile(_game.UserlistPath().string());
|
||||
|
||||
if (ulist["plugins"])
|
||||
ulist_plugins = ulist["plugins"].as< list<boss::Plugin> >();
|
||||
} catch (YAML::ParserException& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Userlist parsing failed. Details: " << e.what();
|
||||
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("Userlist parsing failed. Details: %1%")) % e.what()).str()));
|
||||
@@ -477,24 +514,34 @@ void Launcher::OnSortPlugins(wxCommandEvent& event) {
|
||||
|
||||
progDia->Pulse();
|
||||
|
||||
bool cyclicDependenciesExist = false;
|
||||
if (fs::exists(_game.MasterlistPath()) || fs::exists(_game.UserlistPath())) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Merging plugin lists, evaluating conditions and and checking for install validity...";
|
||||
BOOST_LOG_TRIVIAL(trace) << "Merging plugin lists, evaluating conditions and checking for install validity...";
|
||||
|
||||
//Set language.
|
||||
unsigned int lang = GetLangNum(_settings["Language"].as<string>());
|
||||
|
||||
|
||||
//Merge all global message lists.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Merging all global message lists.";
|
||||
messages = mlist_messages;
|
||||
messages.insert(messages.end(), ulist_messages.begin(), ulist_messages.end());
|
||||
|
||||
//Set language.
|
||||
unsigned int lang = GetLangNum(_settings["Language"].as<string>());
|
||||
if (!mlist_messages.empty())
|
||||
messages.insert(messages.end(), mlist_messages.begin(), mlist_messages.end());
|
||||
if (!ulist_messages.empty())
|
||||
messages.insert(messages.end(), ulist_messages.begin(), ulist_messages.end());
|
||||
|
||||
//Evaluate any conditions in the global messages.
|
||||
list<boss::Message>::iterator it=messages.begin();
|
||||
while (it != messages.end()) {
|
||||
if (!it->EvalCondition(_game, lang))
|
||||
it = messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
BOOST_LOG_TRIVIAL(trace) << "Evaluating global message conditions...";
|
||||
try {
|
||||
list<boss::Message>::iterator it=messages.begin();
|
||||
while (it != messages.end()) {
|
||||
if (!it->EvalCondition(_game, lang))
|
||||
it = messages.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
} catch (boss::error& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what();
|
||||
messages.push_back(boss::Message(boss::g_message_error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
|
||||
}
|
||||
|
||||
//Merge plugin list, masterlist and userlist plugin data.
|
||||
|
||||
Reference in New Issue
Block a user