Merge remote-tracking branch 'origin/master' into cef-implement

This commit is contained in:
WrinklyNinja
2014-07-15 08:59:00 +01:00
15 changed files with 539 additions and 577 deletions
+3 -2
View File
@@ -49,12 +49,13 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/helpers.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/globals.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/generators.cpp")
"${CMAKE_SOURCE_DIR}/src/backend/generators.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/sort.cpp")
set (LOOT_GUI_SRC ${LOOT_SRC}
# Code the API doesn't need.
"${CMAKE_SOURCE_DIR}/src/backend/graph.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/network.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/git.cpp"
# Actual GUI code.
"${CMAKE_SOURCE_DIR}/src/gui/main_win.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/handler.cpp"
+15
View File
@@ -158,6 +158,21 @@ unsigned int c_error(const unsigned int code, const std::string& what) {
return c_error(loot::error(code, what.c_str()));
}
////////////////////////////////////
// Dummy Masterlist member functions
////////////////////////////////////
// The API doesn't depend on libgit2, by not compiling ".git.cpp", so the member functions are defined
// below as dummies.
namespace loot {
void Masterlist::GetGitInfo(boost::filesystem::path& path) {}
void Masterlist::Update(Game& game, const unsigned int language) {
this->MetadataList::Load(game.MasterlistPath());
}
}
//////////////////////////////
// Error Handling Functions
+162 -7
View File
@@ -28,8 +28,11 @@
#include "error.h"
#include "metadata.h"
#include "parsers.h"
#include "streams.h"
#include "generators.h"
#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>
using namespace std;
@@ -59,6 +62,128 @@ namespace loot {
return games;
}
size_t SelectGame(const YAML::Node& settings, const std::vector<Game>& games, const std::string& cmdLineGame) {
string preferredGame(cmdLineGame);
if (preferredGame.empty()) {
// Get preferred game from settings.
if (settings["Game"] && settings["Game"].as<string>() != "auto")
preferredGame = settings["Game"].as<string>();
else if (settings["Last Game"] && settings["Last Game"].as<string>() != "auto")
preferredGame = settings["Last Game"].as<string>();
}
// Get index of preferred game if there is one.
for (size_t i = 0; i < games.size(); ++i) {
if (preferredGame.empty() && games[i].IsInstalled())
return i;
else if (!preferredGame.empty() && preferredGame == games[i].FolderName() && games[i].IsInstalled())
return i;
}
throw error(error::no_game_detected, "None of the supported games were detected.");
}
// MetadataList member functions
//------------------------------
void MetadataList::Load(const boost::filesystem::path& filepath) {
plugins.clear();
messages.clear();
BOOST_LOG_TRIVIAL(debug) << "Loading file: " << filepath;
loot::ifstream in(filepath);
YAML::Node metadataList = YAML::Load(in);
in.close();
if (metadataList["plugins"])
plugins = metadataList["plugins"].as< list<Plugin> >();
if (metadataList["globals"])
messages = metadataList["globals"].as< list<Message> >();
BOOST_LOG_TRIVIAL(debug) << "File loaded successfully.";
}
void MetadataList::Save(const boost::filesystem::path& filepath) {
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
<< YAML::Key << "plugins" << YAML::Value << plugins
<< YAML::Key << "globals" << YAML::Value << messages
<< YAML::EndMap;
loot::ofstream uout(filepath);
uout << yout.c_str();
uout.close();
}
bool MetadataList::operator == (const MetadataList& rhs) const {
if (this->plugins.size() != rhs.plugins.size() || this->messages.size() != rhs.messages.size()) {
BOOST_LOG_TRIVIAL(info) << "Metadata edited for some plugin, new and old userlists differ in size.";
return false;
}
else {
for (const auto& rhsPlugin : rhs.plugins) {
const auto it = std::find(this->plugins.begin(), this->plugins.end(), rhsPlugin);
if (it == this->plugins.end()) {
BOOST_LOG_TRIVIAL(info) << "Metadata added for plugin: " << it->Name();
return false;
}
if (!it->DiffMetadata(rhsPlugin).HasNameOnly()) {
BOOST_LOG_TRIVIAL(info) << "Metadata edited for plugin: " << it->Name();
return false;
}
}
// Messages are compared exactly by the '==' operator, so there's no need to do a more
// fine-grained check.
for (const auto& rhsMessage : rhs.messages) {
const auto it = std::find(this->messages.begin(), this->messages.end(), rhsMessage);
if (it == this->messages.end()) {
return false;
}
}
}
return true;
}
// Masterlist member functions
//----------------------------
void Masterlist::Load(Game& game, const unsigned int language) {
try {
Update(game, language);
}
catch (error& e) {
if (e.code() != error::ok) {
// Error wasn't a parsing error. Need to try parsing masterlist if it exists.
try {
MetadataList::Load(game.MasterlistPath());
}
catch (...) {}
}
throw e;
}
}
std::string Masterlist::GetRevision(const boost::filesystem::path& path) {
if (revision.empty())
GetGitInfo(path);
return revision;
}
std::string Masterlist::GetDate(const boost::filesystem::path& path) {
if (date.empty())
GetGitInfo(path);
return date;
}
// Game member functions
//----------------------
Game::Game() : id(Game::autodetect) {}
Game::Game(const unsigned int gameCode, const std::string& folder) : id(gameCode) {
@@ -514,19 +639,49 @@ namespace loot {
}
void Game::LoadPlugins(bool headersOnly) {
//Add all plugins in data folder not already in the hashset to the hashset, and load them.
for (fs::directory_iterator it(DataPath()); it != fs::directory_iterator(); ++it) {
boost::thread_group group;
uintmax_t meanFileSize = 0;
unordered_map<std::string, uintmax_t> tempMap;
std::vector<Plugin*> groupPlugins;
//First calculate the mean plugin size. Store it temporarily in a map to reduce filesystem lookups and file size recalculation.
for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && IsPlugin(it->path().string())) {
const string filename = it->path().filename().string();
if (plugins.find(filename) == plugins.end())
plugins.insert(std::pair<string, Plugin>(filename, Plugin(filename)));
uintmax_t fileSize = fs::file_size(it->path());
meanFileSize += fileSize;
tempMap.emplace(it->path().filename().string(), fileSize);
}
}
meanFileSize /= tempMap.size(); //Rounding error, but not important.
for (auto &pluginPair: plugins) {
pluginPair.second = Plugin(*this, pluginPair.second.Name(), headersOnly);
//Now load plugins.
for (const auto &pluginPair : tempMap) {
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << pluginPair.first;
auto plugin = plugins.emplace(pluginPair.first, Plugin(pluginPair.first));
if (pluginPair.second > meanFileSize) {
BOOST_LOG_TRIVIAL(trace) << "Creating individual loading thread for: " << pluginPair.first;
group.create_thread([this, plugin, headersOnly]() {
BOOST_LOG_TRIVIAL(trace) << "Loading " << plugin.first->second.Name() << " individually.";
plugin.first->second = Plugin(*this, plugin.first->first, headersOnly);
});
}
else {
groupPlugins.push_back(&plugin.first->second);
}
}
group.create_thread([this, &groupPlugins, headersOnly]() {
for (auto plugin : groupPlugins) {
const std::string name = plugin->Name();
BOOST_LOG_TRIVIAL(trace) << "Loading " << plugin->Name() << " as part of a group.";
*plugin = Plugin(*this, name, headersOnly);
}
});
group.join_all();
}
void Game::CreateLOOTGameFolder() {
+35 -2
View File
@@ -40,6 +40,8 @@
#include <yaml-cpp/yaml.h>
namespace loot {
class Game;
/* Each Game object should store the config details specific to that game.
It should also store the plugin and masterlist data for that game.
@@ -51,6 +53,33 @@ namespace loot {
data. Plugin data should be loaded as header-only and as full data.
*/
class MetadataList {
public:
void Load(const boost::filesystem::path& filepath);
void Save(const boost::filesystem::path& filepath);
bool operator == (const MetadataList& rhs) const; //Compares content.
std::list<Plugin> plugins;
std::list<Message> messages;
};
class Masterlist : public MetadataList {
public:
void Load(Game& game, const unsigned int language); //Handles update with load fallback.
void Update(Game& game, const unsigned int language);
std::string GetRevision(const boost::filesystem::path& path);
std::string GetDate(const boost::filesystem::path& path);
private:
void GetGitInfo(const boost::filesystem::path& path);
std::string revision;
std::string date;
};
class Game {
public:
//Game functions.
@@ -82,7 +111,6 @@ namespace loot {
boost::filesystem::path ReportDataPath() const;
//Game plugin functions.
bool IsActive(const std::string& plugin) const;
void GetLoadOrder(std::list<std::string>& loadOrder) const;
@@ -92,12 +120,15 @@ namespace loot {
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
void LoadPlugins(bool headersOnly); //Loads all installed plugins.
void SortPrep(const unsigned int language, std::list<Message>& messages, std::function<void(const std::string&)> progressCallback);
std::list<Plugin> Sort(const unsigned int language, std::list<Message>& messages, std::function<void(const std::string&)> progressCallback);
//Caches for condition results, active plugins and CRCs.
std::unordered_map<std::string, bool> conditionCache; //Holds lowercased strings.
std::unordered_map<std::string, uint32_t> crcCache; //Holds lowercased strings.
//Plugin data and metadata lists.
MetadataList masterlist;
Masterlist masterlist;
MetadataList userlist;
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
@@ -128,6 +159,8 @@ namespace loot {
};
std::vector<Game> GetGames(const YAML::Node& settings);
size_t SelectGame(const YAML::Node& settings, const std::vector<Game>& games, const std::string& cmdLineGame);
}
#endif
+25 -29
View File
@@ -22,11 +22,11 @@
<http://www.gnu.org/licenses/>.
*/
#include "network.h"
#include "error.h"
#include "parsers.h"
#include "streams.h"
#include "helpers.h"
#include "game.h"
#include <boost/log/trivial.hpp>
#include <boost/locale.hpp>
@@ -92,11 +92,6 @@ namespace loot {
std::string ui_message;
};
int progress_cb(const char *str, int len, void *data) {
BOOST_LOG_TRIVIAL(info) << string(str, len);
return 0;
}
bool are_files_equal(const void * buf1, size_t buf1_size, const void * buf2, size_t buf2_size) {
if (buf1_size != buf2_size)
return false;
@@ -114,12 +109,17 @@ namespace loot {
return git_repository_open_ext(NULL, path.string().c_str(), GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) == 0;
}
std::pair<string, string> GetMasterlistRevision(const Game& game) {
if (!fs::exists(game.MasterlistPath().parent_path() / ".git")) {
return pair<string, string>("Unknown: Git repository missing", "Unknown: Git repository missing");
void Masterlist::GetGitInfo(const boost::filesystem::path& path) {
if (!fs::exists(path.parent_path() / ".git")) {
revision = "Unknown: Git repository missing";
date = "Unknown: Git repository missing";
return;
}
else if (!fs::exists(path)) {
revision = "N/A: No masterlist present";
date = "N/A: No masterlist present";
return;
}
else if (!fs::exists(game.MasterlistPath()))
return pair<string, string>("N/A: No masterlist present", "N/A: No masterlist present");
else {
/* Compares HEAD to the working dir.
1. Get an object for the masterlist in HEAD.
@@ -128,9 +128,9 @@ namespace loot {
4. Compare the file and blob buffers.
*/
git_handler git;
git.ui_message = "An error occurred while trying to read the local masterlist's version. If this error happens again, try deleting the \".git\" folder in \"%LOCALAPPDATA%\\LOOT\\" + game.FolderName() + "\".";
git.ui_message = "An error occurred while trying to read the local masterlist's version. If this error happens again, try deleting the \".git\" folder in " + path.parent_path().string() + ".";
BOOST_LOG_TRIVIAL(debug) << "Existing repository found, attempting to open it.";
git.call(git_repository_open(&git.repo, game.MasterlistPath().parent_path().string().c_str()));
git.call(git_repository_open(&git.repo, path.parent_path().string().c_str()));
BOOST_LOG_TRIVIAL(trace) << "Getting HEAD masterlist object.";
git.call(git_revparse_single(&git.obj, git.repo, "HEAD:masterlist.yaml"));
@@ -140,7 +140,7 @@ namespace loot {
BOOST_LOG_TRIVIAL(debug) << "Opening masterlist in working directory.";
std::string mlist;
loot::ifstream ifile(game.MasterlistPath().string().c_str(), ios::binary);
loot::ifstream ifile(path, ios::binary);
if (ifile.fail())
throw error(error::path_read_fail, "Couldn't open masterlist.");
ifile.unsetf(ios::skipws); // No white space skipping!
@@ -153,7 +153,6 @@ namespace loot {
BOOST_LOG_TRIVIAL(debug) << "Comparing files.";
if (are_files_equal(git_blob_rawcontent(git.blob), git_blob_rawsize(git.blob), mlist.data(), mlist.length())) {
string revision, date;
//Need to get the HEAD object, because the individual file has a different SHA.
git_object_free(git.obj);
git.obj = nullptr; //Just to be safe.
@@ -176,15 +175,17 @@ namespace loot {
out << boost::locale::as::ftime("%Y-%m-%d") << dateTime;
date = out.str();
return pair<string, string>(revision, date);
return;
}
else {
return pair<string, string>("Unknown: Masterlist edited", "Unknown: Masterlist edited");
revision = "Unknown: Masterlist edited";
date = "Unknown: Masterlist edited";
return;
}
}
}
std::pair<std::string, std::string> UpdateMasterlist(Game& game, std::list<Message>& parsingErrors, std::list<Plugin>& plugins, std::list<Message>& messages, const unsigned int language) {
void Masterlist::Update(Game& game, const unsigned int language) {
git_handler git;
fs::path repo_path = game.MasterlistPath().parent_path();
string repo_branch = game.RepoBranch();
@@ -387,7 +388,7 @@ namespace loot {
// and try again.
bool parsingFailed = false;
string revision, date;
std::string parsingError;
git.ui_message = "An error occurred while trying to read information on the updated masterlist. If this error happens again, try deleting the \".git\" folder in \"%LOCALAPPDATA%\\LOOT\\" + game.FolderName() + "\".";
do {
// Get some descriptive info about what was checked out.
@@ -428,14 +429,7 @@ namespace loot {
//Now try parsing the masterlist.
BOOST_LOG_TRIVIAL(debug) << "Testing masterlist parsing.";
try {
loot::ifstream in(game.MasterlistPath());
YAML::Node mlist = YAML::Load(in);
in.close();
if (mlist["globals"])
messages = mlist["globals"].as< list<loot::Message> >();
if (mlist["plugins"])
plugins = mlist["plugins"].as< list<loot::Plugin> >();
this->MetadataList::Load(game.MasterlistPath());
for (auto &plugin: plugins) {
plugin.EvalAllConditions(game, language);
@@ -466,10 +460,12 @@ namespace loot {
BOOST_LOG_TRIVIAL(trace) << "Performing a Git checkout of HEAD.";
git.call(git_checkout_head(git.repo, &checkout_opts));
parsingErrors.push_back(loot::Message(loot::Message::error, boost::locale::translate("Masterlist revision").str() + " " + string(revision) + ": " + e.what() + " " + boost::locale::translate("Rolled back to the previous revision.").str()));
if (parsingError.empty())
parsingError = boost::locale::translate("Masterlist revision").str() + " " + string(revision) + ": " + e.what() + " " + boost::locale::translate("Rolled back to the previous revision.").str();
}
} while (parsingFailed);
return pair<string, string>(revision, date);
if (!parsingError.empty())
throw error(error::ok, parsingError); //Throw an OK because the process still completed in a successful state.
}
}
+9 -4
View File
@@ -90,7 +90,7 @@ namespace loot {
return false;
}
void Sort(const PluginGraph& graph, std::list<Plugin>& plugins) {
std::list<Plugin> Sort(const PluginGraph& graph) {
//Topological sort requires an index map, which std::list-based VertexList graphs don't have, so one needs to be built separately.
@@ -105,15 +105,19 @@ namespace loot {
std::list<vertex_t> sortedVertices;
boost::topological_sort(graph, std::front_inserter(sortedVertices), boost::vertex_index_map(v_index_map));
/* Sorting now evaluates conditions inside the graph, so existing plugins list is missing
data present in the graph, so we need to swap the two lists. */
BOOST_LOG_TRIVIAL(info) << "Calculated order: ";
list<string> tempPlugins;
list<Plugin> plugins;
for (const auto &vertex: sortedVertices) {
BOOST_LOG_TRIVIAL(info) << '\t' << graph[vertex].Name();
tempPlugins.push_back(graph[vertex].Name());
plugins.push_back(graph[vertex]);
}
return plugins;
//Now sort exist plugins list according to order in tempPlugins.
plugins.sort([tempPlugins](const Plugin& first, const Plugin& second){
/*plugins.sort([tempPlugins](const Plugin& first, const Plugin& second){
//Find both plugins, and compare distances from beginning.
auto fIt = find(tempPlugins.begin(), tempPlugins.end(), first);
auto sIt = find(tempPlugins.begin(), tempPlugins.end(), second);
@@ -123,6 +127,7 @@ namespace loot {
return distance(tempPlugins.begin(), fIt) < distance(tempPlugins.begin(), sIt);
});
*/
}
void CheckForCycles(const PluginGraph& graph) {
+1 -1
View File
@@ -54,7 +54,7 @@ namespace loot {
bool GetVertexByName(const PluginGraph& graph, const std::string& name, vertex_t& vertex);
void Sort(const PluginGraph& graph, std::list<Plugin>& plugins);
std::list<Plugin> Sort(const PluginGraph& graph);
void CheckForCycles(const PluginGraph& graph);
-18
View File
@@ -763,24 +763,6 @@ namespace loot {
return boost::filesystem::exists(game.DataPath() / (name.substr(0, name.length() - 3) + "bsa"));
}
void MetadataList::Load(boost::filesystem::path& filepath) {
plugins.clear();
messages.clear();
BOOST_LOG_TRIVIAL(debug) << "Loading file: " << filepath;
loot::ifstream in(filepath);
YAML::Node metadataList = YAML::Load(in);
in.close();
if (metadataList["plugins"])
plugins = metadataList["plugins"].as< list<Plugin> >();
if (metadataList["globals"])
messages = metadataList["globals"].as< list<Message> >();
BOOST_LOG_TRIVIAL(debug) << "File loaded successfully.";
}
size_t plugin_hash::operator () (const Plugin& p) const {
size_t seed = 0;
boost::hash_combine(seed, p.Name());
-8
View File
@@ -243,14 +243,6 @@ namespace loot {
size_t numOverrideRecords;
};
class MetadataList {
public:
void Load(boost::filesystem::path& filepath);
std::list<Plugin> plugins;
std::list<Message> messages;
};
struct plugin_hash : std::unary_function<Plugin, size_t> {
size_t operator () (const Plugin& p) const;
};
-40
View File
@@ -1,40 +0,0 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2014 WrinklyNinja
This file is part of LOOT.
LOOT 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.
LOOT 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 LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#ifndef __LOOT_NETWORK__
#define __LOOT_NETWORK__
#include <vector>
#include <string>
#include "game.h"
#include "metadata.h"
namespace loot {
std::pair<std::string, std::string> UpdateMasterlist(Game& game, std::list<Message>& parsingErrors, std::list<Plugin>& plugins, std::list<Message>& messages, const unsigned int language);
std::pair<std::string, std::string> GetMasterlistRevision(const Game& game);
}
#endif
+203
View File
@@ -0,0 +1,203 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2014 WrinklyNinja
This file is part of LOOT.
LOOT 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.
LOOT 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 LOOT. If not, see
<http://www.gnu.org/licenses/>.
*/
#include "game.h"
#include "helpers.h"
#include "graph.h"
#include <boost/log/trivial.hpp>
#include <boost/thread.hpp>
#include <boost/format.hpp>
#include <boost/locale.hpp>
using namespace std;
using boost::format;
namespace loc = boost::locale;
namespace fs = boost::filesystem;
namespace loot {
void Game::SortPrep(const unsigned int language, std::list<Message>& messages, std::function<void(const std::string&)> progressCallback) {
boost::thread_group group;
BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name();
///////////////////////////////////////////////////////
// Load Plugins & Lists
///////////////////////////////////////////////////////
progressCallback("Reading installed plugins...");
group.create_thread([this, language, &messages]() {
try {
this->masterlist.Load(*this, language);
}
catch (exception &e) {
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("Masterlist parsing failed. Details: %1%")) % e.what()).str()));
}
});
group.create_thread([this]() {
this->LoadPlugins(false);
});
group.join_all();
//Now load userlist.
if (fs::exists(this->UserlistPath())) {
BOOST_LOG_TRIVIAL(debug) << "Parsing userlist at: " << this->UserlistPath();
try {
this->userlist.Load(this->UserlistPath());
}
catch (exception& e) {
BOOST_LOG_TRIVIAL(error) << "Userlist parsing failed. Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("Userlist parsing failed. Details: %1%")) % e.what()).str()));
}
}
///////////////////////////////////////////////////////
// Evaluate Global Messages
///////////////////////////////////////////////////////
progressCallback("Evaluating global messages...");
//Merge all global message lists.
BOOST_LOG_TRIVIAL(debug) << "Merging all global message lists.";
if (!this->masterlist.messages.empty())
messages.insert(messages.end(), this->masterlist.messages.begin(), this->masterlist.messages.end());
if (!this->userlist.messages.empty())
messages.insert(messages.end(), this->userlist.messages.begin(), this->userlist.messages.end());
//Evaluate any conditions in the global messages.
BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions.";
try {
list<loot::Message>::iterator it = messages.begin();
while (it != messages.end()) {
if (!it->EvalCondition(*this, language))
it = messages.erase(it);
else
++it;
}
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "A global message contains a condition that could not be evaluated. Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("A global message contains a condition that could not be evaluated. Details: %1%")) % e.what()).str()));
}
////////////////////////////////////////////////////////
// Slim down masterlist
////////////////////////////////////////////////////////
//
// Userlist data gets replaced every time sorting is looped, so there's no point evaluating it
// outside the loop, but the masterlist can be slimmed down now.
progressCallback("Filtering masterlist...");
std::list<Plugin> tempMasterlistPlugins;
for (const auto &plugin : this->plugins) {
list<loot::Plugin>::iterator pos = std::find(this->masterlist.plugins.begin(), this->masterlist.plugins.end(), plugin.second);
if (pos != this->masterlist.plugins.end()) {
// The plugin exists in the masterlist, store a copy of its metadata.
tempMasterlistPlugins.push_back(*pos);
}
}
// Now replace the current full masterlist plugin metadata list with the install-specific one.
this->masterlist.plugins = tempMasterlistPlugins;
}
std::list<Plugin> Game::Sort(const unsigned int language, std::list<Message>& messages, std::function<void(const std::string&)> progressCallback) {
//Create a plugin graph containing the plugin and masterlist data.
loot::PluginGraph graph;
progressCallback("Building plugin graph...");
BOOST_LOG_TRIVIAL(info) << "Merging masterlist, userlist into plugin list, evaluating conditions and checking for install validity.";
for (const auto &plugin : this->plugins) {
vertex_t v = boost::add_vertex(plugin.second, graph);
list<loot::Plugin>::iterator pos;
BOOST_LOG_TRIVIAL(trace) << "Merging for plugin \"" << graph[v].Name() << "\"";
//Check if there is a plugin entry in the masterlist. This will also find matching regex entries.
pos = std::find(this->masterlist.plugins.begin(), this->masterlist.plugins.end(), graph[v]);
if (pos != this->masterlist.plugins.end()) {
BOOST_LOG_TRIVIAL(trace) << "Merging masterlist data down to plugin list data.";
graph[v].MergeMetadata(*pos);
}
//Check if there is a plugin entry in the userlist. This will also find matching regex entries.
pos = std::find(this->userlist.plugins.begin(), this->userlist.plugins.end(), graph[v]);
if (pos != this->userlist.plugins.end() && pos->Enabled()) {
BOOST_LOG_TRIVIAL(trace) << "Merging userlist data down to plugin list data.";
graph[v].MergeMetadata(*pos);
}
//Now that items are merged, evaluate any conditions they have.
BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data.";
try {
graph[v].EvalAllConditions(*this, language);
}
catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "\"" << graph[v].Name() << "\" contains a condition that could not be evaluated. Details: " << e.what();
messages.push_back(loot::Message(loot::Message::error, (format(loc::translate("\"%1%\" contains a condition that could not be evaluated. Details: %2%")) % graph[v].Name() % e.what()).str()));
}
//Also check install validity.
BOOST_LOG_TRIVIAL(trace) << "Checking that the current install is valid according to this plugin's data.";
graph[v].CheckInstallValidity(*this);
}
BOOST_LOG_TRIVIAL(info) << "Building the plugin dependency graph...";
//Now add the interactions between plugins to the graph as edges.
std::map<std::string, int> overriddenPriorities;
BOOST_LOG_TRIVIAL(debug) << "Adding non-overlap edges.";
loot::AddSpecificEdges(graph, overriddenPriorities);
BOOST_LOG_TRIVIAL(debug) << "Adding priority edges.";
loot::AddPriorityEdges(graph);
BOOST_LOG_TRIVIAL(debug) << "Adding overlap edges.";
loot::AddOverlapEdges(graph);
progressCallback("Checking for graph cycles...");
BOOST_LOG_TRIVIAL(info) << "Checking to see if the graph is cyclic.";
loot::CheckForCycles(graph);
for (const auto &overriddenPriority : overriddenPriorities) {
vertex_t vertex;
if (loot::GetVertexByName(graph, overriddenPriority.first, vertex)) {
graph[vertex].Priority(overriddenPriority.second);
}
}
BOOST_LOG_TRIVIAL(info) << "Performing a topological sort.";
progressCallback("Performing topological sort...");
return loot::Sort(graph);
}
}
+8 -15
View File
@@ -1041,8 +1041,10 @@ void EditorPanel::ApplyCurrentEdits() {
ApplyEdits(currentPlugin);
}
const std::list<loot::Plugin>& EditorPanel::GetNewUserlist() const {
return _editedPlugins;
loot::MetadataList EditorPanel::GetNewUserlist() const {
loot::MetadataList newUserlist;
newUserlist.plugins = _editedPlugins;
return newUserlist;
}
loot::Plugin EditorPanel::GetMasterData(const wxString& plugin) const {
@@ -1197,7 +1199,7 @@ void MiniEditor::OnResize(wxSizeEvent& event) {
event.Skip();
}
const std::list<loot::Plugin>& MiniEditor::GetNewUserlist() const {
loot::MetadataList MiniEditor::GetNewUserlist() const {
return editorPanel->GetNewUserlist();
}
@@ -1206,7 +1208,7 @@ const std::list<loot::Plugin>& MiniEditor::GetNewUserlist() const {
// Full Editor Class
///////////////////////////////////
FullEditor::FullEditor(wxWindow *parent, const wxString& title, wxPoint pos, wxSize size, const std::string userlistPath, const std::list<loot::Plugin>& basePlugins, std::list<loot::Plugin>& editedPlugins, const unsigned int language, const loot::Game& game, YAML::Node &settings) : wxFrame(parent, wxID_ANY, title, pos, size), _userlistPath(userlistPath), _settings(settings) {
FullEditor::FullEditor(wxWindow *parent, const wxString& title, wxPoint pos, wxSize size, const boost::filesystem::path& userlistPath, const std::list<loot::Plugin>& basePlugins, std::list<loot::Plugin>& editedPlugins, const unsigned int language, const loot::Game& game, YAML::Node &settings) : wxFrame(parent, wxID_ANY, title, pos, size), _userlistPath(userlistPath), _settings(settings) {
//Set up content.
editorPanel = new EditorPanel(this, basePlugins, editedPlugins, language, game);
applyBtn = new wxButton(this, BUTTON_Apply, translate("Save Changes"));
@@ -1245,17 +1247,8 @@ void FullEditor::OnQuit(wxCommandEvent& event) {
BOOST_LOG_TRIVIAL(debug) << "Saving metadata edits to userlist.";
//Save edits to userlist.
YAML::Emitter yout;
yout.SetIndent(2);
yout << YAML::BeginMap
<< YAML::Key << "plugins" << YAML::Value << editorPanel->GetNewUserlist()
<< YAML::EndMap;
boost::filesystem::path p(_userlistPath);
loot::ofstream out(p);
out << yout.c_str();
out.close();
loot::MetadataList userlist = editorPanel->GetNewUserlist();
userlist.Save(_userlistPath);
}
Close();
}
+6 -4
View File
@@ -26,7 +26,9 @@
#include "ids.h"
#include "misc.h"
#include "../backend/metadata.h"
#include "../backend/game.h"
#include <string>
#include <list>
@@ -69,7 +71,7 @@ public:
void SetSimpleView(bool on = true);
void ApplyCurrentEdits();
const std::list<loot::Plugin>& GetNewUserlist() const;
loot::MetadataList GetNewUserlist() const;
void OnPluginSelect(wxListEvent& event);
void OnPluginListRightClick(wxListEvent& event);
@@ -134,7 +136,7 @@ public:
void OnApply(wxCommandEvent& event);
void OnResize(wxSizeEvent& event);
const std::list<loot::Plugin>& GetNewUserlist() const;
loot::MetadataList GetNewUserlist() const;
private:
EditorPanel * editorPanel;
wxStaticText * descText;
@@ -144,7 +146,7 @@ private:
class FullEditor : public wxFrame {
public:
FullEditor(wxWindow *parent, const wxString& title, wxPoint pos, wxSize size, const std::string userlistPath, const std::list<loot::Plugin>& basePlugins, std::list<loot::Plugin>& editedPlugins, const unsigned int language, const loot::Game& game, YAML::Node &settings);
FullEditor(wxWindow *parent, const wxString& title, wxPoint pos, wxSize size, const boost::filesystem::path& userlistPath, const std::list<loot::Plugin>& basePlugins, std::list<loot::Plugin>& editedPlugins, const unsigned int language, const loot::Game& game, YAML::Node &settings);
void OnQuit(wxCommandEvent& event);
void OnClose(wxCloseEvent &event);
@@ -153,7 +155,7 @@ private:
wxButton * applyBtn;
wxButton * cancelBtn;
const std::string _userlistPath;
const boost::filesystem::path _userlistPath;
YAML::Node& _settings;
};
#endif
+70 -445
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -45,7 +45,7 @@ private:
class Launcher : public wxFrame {
public:
Launcher(const wxChar *title, YAML::Node& settings, loot::Game * inGame, std::vector<loot::Game>& games, wxPoint pos, wxSize size);
Launcher(const wxChar *title, YAML::Node& settings, std::vector<loot::Game>& games, size_t currentGame, wxPoint pos, wxSize size);
void OnSortPlugins(wxCommandEvent& event);
void OnEditMetadata(wxCommandEvent& event);
@@ -65,9 +65,9 @@ private:
wxMenuItem * RedatePluginsItem;
wxButton * ViewButton;
loot::Game * _game;
YAML::Node& _settings; //LOOT Settings.
std::vector<loot::Game>& _games;
size_t _currentGame;
void GetWindowSizePos(const YAML::Node& node, wxPoint& pos, wxSize& size);
};