Refactor load order interaction into a separate class.

The Game class now inherits from the LoadOrderHandler class too.
This commit is contained in:
Oliver Hamlet
2015-06-13 22:29:45 +01:00
parent a400162811
commit 652461cf9a
6 changed files with 296 additions and 198 deletions
+2
View File
@@ -88,6 +88,7 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.c
"${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game/load_order_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/metadata_list.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/masterlist.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/plugin.cpp"
@@ -111,6 +112,7 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h"
"${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.h"
"${CMAKE_SOURCE_DIR}/src/backend/game.h"
"${CMAKE_SOURCE_DIR}/src/backend/game_settings.h"
"${CMAKE_SOURCE_DIR}/src/backend/game/load_order_handler.h"
"${CMAKE_SOURCE_DIR}/src/backend/metadata_list.h"
"${CMAKE_SOURCE_DIR}/src/backend/masterlist.h"
"${CMAKE_SOURCE_DIR}/src/backend/plugin.h"
+9 -184
View File
@@ -40,7 +40,7 @@ namespace fs = boost::filesystem;
namespace lc = boost::locale;
namespace loot {
Game::Game() : GameSettings(), gh(nullptr) {}
Game::Game() {}
Game::Game(const GameSettings& gameSettings) : Game(gameSettings.Id(), gameSettings.FolderName()) {
this->SetDetails(gameSettings.Name(),
@@ -51,13 +51,9 @@ namespace loot {
gameSettings.RegistryKey());
}
Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder), gh(nullptr) {}
Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder) {}
Game::~Game() {
lo_destroy_handle(gh);
}
Game& Game::Init(bool createFolder, const boost::filesystem::path& gameLocalAppData) {
void Game::Init(bool createFolder, const boost::filesystem::path& gameLocalAppData) {
if (Id() != Game::tes4 && Id() != Game::tes5 && Id() != Game::fo3 && Id() != Game::fonv) {
throw error(error::invalid_args, lc::translate("Invalid game ID supplied.").str());
}
@@ -69,17 +65,13 @@ namespace loot {
throw error(error::path_not_found, lc::translate("Game path could not be detected.").str());
}
// Set the path to the game's folder in %LOCALAPPDATA%.
_gameLocalDataPath = gameLocalAppData;
InitLibloHandle();
RefreshActivePluginsList();
if (createFolder) {
CreateLOOTGameFolder();
}
return *this;
LoadOrderHandler::Init(*this, gameLocalAppData);
RefreshActivePluginsList();
}
bool Game::operator == (const Game& rhs) const {
@@ -94,181 +86,15 @@ namespace loot {
return (boost::iequals(Name(), nameOrFolderName) || boost::iequals(FolderName(), nameOrFolderName));
}
void Game::InitLibloHandle() {
const char * gameLocalDataPath = nullptr;
std::string localAppData = _gameLocalDataPath.string();
if (!localAppData.empty())
gameLocalDataPath = localAppData.c_str();
// If the handle has already been initialised, close it and open another.
if (gh != nullptr) {
lo_destroy_handle(gh);
gh = nullptr;
}
int ret;
if (Id() == Game::tes4)
ret = lo_create_handle(&gh, LIBLO_GAME_TES4, GamePath().string().c_str(), gameLocalDataPath);
else if (Id() == Game::tes5)
ret = lo_create_handle(&gh, LIBLO_GAME_TES5, GamePath().string().c_str(), gameLocalDataPath);
else if (Id() == Game::fo3)
ret = lo_create_handle(&gh, LIBLO_GAME_FO3, GamePath().string().c_str(), gameLocalDataPath);
else if (Id() == Game::fonv)
ret = lo_create_handle(&gh, LIBLO_GAME_FNV, GamePath().string().c_str(), gameLocalDataPath);
else
ret = LIBLO_ERROR_INVALID_ARGS;
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to create a game handle. Details could not be fetched.";
err = lc::translate("libloadorder failed to create a game handle. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to create a game handle. Details: " << e;
err = lc::translate("libloadorder failed to create a game handle. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
if (Id() != Game::tes5) {
ret = lo_set_game_master(gh, Master().c_str());
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
lo_destroy_handle(gh);
gh = nullptr;
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to initialise game master file support. Details could not be fetched.";
err = lc::translate("libloadorder failed to initialise game master file support. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to initialise game master file support. Details: " << e;
err = lc::translate("libloadorder failed to initialise game master file support. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
}
}
void Game::RefreshActivePluginsList() {
BOOST_LOG_TRIVIAL(debug) << "Refreshing active plugins list for game: " << Name();
char ** pluginArr;
size_t pluginArrSize;
unsigned int ret = lo_get_active_plugins(gh, &pluginArr, &pluginArrSize);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the active plugins list. Details could not be fetched.";
err = lc::translate("libloadorder failed to get the active plugins list. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the active plugins list. Details: " << e;
err = lc::translate("libloadorder failed to get the active plugins list. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
activePlugins.clear();
for (size_t i = 0; i < pluginArrSize; ++i) {
activePlugins.insert(boost::locale::to_lower(string(pluginArr[i])));
}
}
void Game::GetLoadOrder(std::list<std::string>& loadOrder) const {
BOOST_LOG_TRIVIAL(debug) << "Getting load order for game: " << Name();
char ** pluginArr;
size_t pluginArrSize;
unsigned int ret = lo_get_load_order(gh, &pluginArr, &pluginArrSize);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the load order. Details could not be fetched.";
err = lc::translate("libloadorder failed to get the load order. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the load order. Details: " << e;
err = lc::translate("libloadorder failed to get the load order. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
loadOrder.clear();
for (size_t i = 0; i < pluginArrSize; ++i) {
loadOrder.push_back(string(pluginArr[i]));
}
}
void Game::SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const {
BOOST_LOG_TRIVIAL(debug) << "Setting load order for game: " << Name();
unsigned int ret = lo_set_load_order(gh, loadOrder, numPlugins);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to set the load order. Details could not be fetched.";
err = lc::translate("libloadorder failed to set the load order. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to set the load order. Details: " << e;
err = lc::translate("libloadorder failed to set the load order. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
}
void Game::SetLoadOrder(const std::list<std::string>& loadOrder) const {
BOOST_LOG_TRIVIAL(info) << "Setting load order:";
size_t pluginArrSize = loadOrder.size();
char ** pluginArr = new char*[pluginArrSize];
int i = 0;
for (const auto &plugin : loadOrder) {
BOOST_LOG_TRIVIAL(info) << '\t' << '\t' << plugin;
pluginArr[i] = new char[plugin.length() + 1];
strcpy(pluginArr[i], plugin.c_str());
++i;
}
try {
SetLoadOrder(pluginArr, pluginArrSize);
}
catch (error &/*e*/) {
for (size_t i = 0; i < pluginArrSize; i++)
delete[] pluginArr[i];
delete[] pluginArr;
throw;
}
for (size_t i = 0; i < pluginArrSize; i++)
delete[] pluginArr[i];
delete[] pluginArr;
activePlugins = GetActivePlugins();
}
void Game::RedatePlugins() {
if (Id() != tes5)
return;
list<string> loadorder;
GetLoadOrder(loadorder);
list<string> loadorder = GetLoadOrder();
if (!loadorder.empty()) {
time_t lastTime;
fs::path filepath = DataPath() / *loadorder.begin();
@@ -409,8 +235,7 @@ namespace loot {
}
// Get the existing load order.
list<string> loadorder;
GetLoadOrder(loadorder);
list<string> loadorder = GetLoadOrder();
BOOST_LOG_TRIVIAL(info) << "Fetched existing load order: ";
for (const auto &plugin : loadorder)
BOOST_LOG_TRIVIAL(info) << plugin;
+3 -12
View File
@@ -26,6 +26,7 @@
#define __LOOT_GAME__
#include "game_settings.h"
#include "game/load_order_handler.h"
#include "plugin.h"
#include "metadata_list.h"
#include "masterlist.h"
@@ -43,24 +44,20 @@
#include <yaml-cpp/yaml.h>
namespace loot {
class Game : public GameSettings {
class Game : public GameSettings, public LoadOrderHandler {
public:
//Game functions.
Game(); //Sets game to LOOT_Game::autodetect, with all other vars being empty.
Game(const GameSettings& gameSettings);
Game(const unsigned int baseGameCode, const std::string& lootFolder = "");
~Game();
Game& Init(bool createFolder, const boost::filesystem::path& gameLocalAppData = "");
void Init(bool createFolder, const boost::filesystem::path& gameLocalAppData = "");
//Compare names and folder names.
bool operator == (const Game& rhs) const;
bool operator == (const GameSettings& rhs) const;
bool operator == (const std::string& nameOrFolderName) const;
void GetLoadOrder(std::list<std::string>& loadOrder) const;
void SetLoadOrder(const std::list<std::string>& loadOrder) const; //Modifies game load order, even though const.
void SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const; // For API.
void RefreshActivePluginsList();
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
@@ -77,12 +74,6 @@ namespace loot {
Masterlist masterlist;
MetadataList userlist;
std::unordered_map<std::string, Plugin> plugins; //Map so that plugin data can be edited.
private:
boost::filesystem::path _gameLocalDataPath; // Path to the game's folder in %LOCALAPPDATA%.
lo_game_handle gh;
void InitLibloHandle();
};
std::list<Game> ToGames(const std::list<GameSettings>& settings);
+224
View File
@@ -0,0 +1,224 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2015 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 "load_order_handler.h"
#include "../error.h"
#include <boost/algorithm/string.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
using namespace std;
namespace fs = boost::filesystem;
namespace lc = boost::locale;
namespace loot {
LoadOrderHandler::LoadOrderHandler() : _gh(nullptr) {}
LoadOrderHandler::~LoadOrderHandler() {
lo_destroy_handle(_gh);
}
void LoadOrderHandler::Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData) {
if (game.Id() != GameSettings::tes4
&& game.Id() != GameSettings::tes5
&& game.Id() != GameSettings::fo3
&& game.Id() != GameSettings::fonv) {
throw error(error::invalid_args, lc::translate("Unsupported game ID supplied.").str());
}
if (game.GamePath().empty()) {
BOOST_LOG_TRIVIAL(error) << "Game path is not initialised.";
throw error(error::invalid_args, lc::translate("Game path is not initialised.").str());
}
const char * gameLocalDataPath = nullptr;
if (!gameLocalAppData.empty())
gameLocalDataPath = gameLocalAppData.string().c_str();
// If the handle has already been initialised, close it and open another.
if (_gh != nullptr) {
lo_destroy_handle(_gh);
_gh = nullptr;
}
int ret;
if (game.Id() == GameSettings::tes4)
ret = lo_create_handle(&_gh, LIBLO_GAME_TES4, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Id() == GameSettings::tes5)
ret = lo_create_handle(&_gh, LIBLO_GAME_TES5, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Id() == GameSettings::fo3)
ret = lo_create_handle(&_gh, LIBLO_GAME_FO3, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Id() == GameSettings::fonv)
ret = lo_create_handle(&_gh, LIBLO_GAME_FNV, game.GamePath().string().c_str(), gameLocalDataPath);
else
ret = LIBLO_ERROR_INVALID_ARGS;
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to create a game handle. Details could not be fetched.";
err = lc::translate("libloadorder failed to create a game handle. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to create a game handle. Details: " << e;
err = lc::translate("libloadorder failed to create a game handle. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
if (game.Id() != GameSettings::tes5) {
ret = lo_set_game_master(_gh, game.Master().c_str());
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
lo_destroy_handle(_gh);
_gh = nullptr;
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to initialise game master file support. Details could not be fetched.";
err = lc::translate("libloadorder failed to initialise game master file support. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to initialise game master file support. Details: " << e;
err = lc::translate("libloadorder failed to initialise game master file support. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
}
}
std::unordered_set<std::string> LoadOrderHandler::GetActivePlugins() const {
BOOST_LOG_TRIVIAL(debug) << "Getting active plugins.";
char ** pluginArr;
size_t pluginArrSize;
unsigned int ret = lo_get_active_plugins(_gh, &pluginArr, &pluginArrSize);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the active plugins list. Details could not be fetched.";
err = lc::translate("libloadorder failed to get the active plugins list. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the active plugins list. Details: " << e;
err = lc::translate("libloadorder failed to get the active plugins list. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
std::unordered_set<std::string> activePlugins;
for (size_t i = 0; i < pluginArrSize; ++i) {
activePlugins.insert(boost::locale::to_lower(string(pluginArr[i])));
}
return activePlugins;
}
std::list<std::string> LoadOrderHandler::GetLoadOrder() const {
BOOST_LOG_TRIVIAL(debug) << "Getting load order.";
char ** pluginArr;
size_t pluginArrSize;
unsigned int ret = lo_get_load_order(_gh, &pluginArr, &pluginArrSize);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the load order. Details could not be fetched.";
err = lc::translate("libloadorder failed to get the load order. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to get the load order. Details: " << e;
err = lc::translate("libloadorder failed to get the load order. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
std::list<std::string> loadOrder;
for (size_t i = 0; i < pluginArrSize; ++i) {
loadOrder.push_back(string(pluginArr[i]));
}
return loadOrder;
}
void LoadOrderHandler::SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const {
BOOST_LOG_TRIVIAL(debug) << "Setting load order.";
unsigned int ret = lo_set_load_order(_gh, loadOrder, numPlugins);
if (ret != LIBLO_OK && ret != LIBLO_WARN_BAD_FILENAME && ret != LIBLO_WARN_INVALID_LIST && ret != LIBLO_WARN_LO_MISMATCH) {
const char * e = nullptr;
string err;
lo_get_error_message(&e);
if (e == nullptr) {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to set the load order. Details could not be fetched.";
err = lc::translate("libloadorder failed to set the load order. Details could not be fetched.").str();
}
else {
BOOST_LOG_TRIVIAL(error) << "libloadorder failed to set the load order. Details: " << e;
err = lc::translate("libloadorder failed to set the load order. Details:").str() + " " + e;
}
lo_cleanup();
throw error(error::liblo_error, err);
}
}
void LoadOrderHandler::SetLoadOrder(const std::list<std::string>& loadOrder) const {
BOOST_LOG_TRIVIAL(info) << "Setting load order.";
size_t pluginArrSize = loadOrder.size();
char ** pluginArr = new char*[pluginArrSize];
int i = 0;
for (const auto &plugin : loadOrder) {
BOOST_LOG_TRIVIAL(info) << '\t' << '\t' << plugin;
pluginArr[i] = new char[plugin.length() + 1];
strcpy(pluginArr[i], plugin.c_str());
++i;
}
try {
SetLoadOrder(pluginArr, pluginArrSize);
}
catch (error &/*e*/) {
for (size_t i = 0; i < pluginArrSize; i++)
delete[] pluginArr[i];
delete[] pluginArr;
throw;
}
for (size_t i = 0; i < pluginArrSize; i++)
delete[] pluginArr[i];
delete[] pluginArr;
}
}
+57
View File
@@ -0,0 +1,57 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2015 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_LOAD_ORDER_HANDLER__
#define __LOOT_LOAD_ORDER_HANDLER__
#include "../game_settings.h"
#include <string>
#include <list>
#include <unordered_set>
#include <boost/filesystem.hpp>
#include <api/libloadorder.h>
namespace loot {
class LoadOrderHandler {
public:
LoadOrderHandler();
~LoadOrderHandler();
void Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData = "");
std::unordered_set<std::string> GetActivePlugins() const;
std::list<std::string> GetLoadOrder() const;
//These modify game load order, even though const.
void SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const; // For API.
void SetLoadOrder(const std::list<std::string>& loadOrder) const;
private:
lo_game_handle _gh;
};
}
#endif
+1 -2
View File
@@ -636,8 +636,7 @@ namespace loot {
//Sort plugins into their load order.
list<loot::Plugin> installed;
list<string> loadOrder;
_lootState.CurrentGame().GetLoadOrder(loadOrder);
list<string> loadOrder = _lootState.CurrentGame().GetLoadOrder();
for (const auto &pluginName : loadOrder) {
const auto pos = _lootState.CurrentGame().plugins.find(boost::locale::to_lower(pluginName));