Move GameSettings into GUI code and split Game

GameSettings is only needed by the LOOT GUI, and many functions in Game
could also be GUI-specific, so they were moved to a new loot::gui::Game
class (the namespace is temporary, to prevent conflicts until the
loot::Game class is hidden inside the API).

This also involved minor modifications to LoadOrderHandler, the removal
of Masterlist::Update(Game& game), and the (re)addition of a new API
function for specifying the game's main master file.
This commit is contained in:
Oliver Hamlet
2017-02-06 18:02:17 +00:00
parent f53139453a
commit ff575e107e
38 changed files with 963 additions and 683 deletions
+12 -5
View File
@@ -176,7 +176,6 @@ set (LOOT_SRC "${CMAKE_BINARY_DIR}/generated/loot_version.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/metadata/tag.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game/game.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game/game_cache.cpp"
"${CMAKE_SOURCE_DIR}/src/backend/game/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"
@@ -191,7 +190,6 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_evaluator.
"${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h"
"${CMAKE_SOURCE_DIR}/src/backend/game/game.h"
"${CMAKE_SOURCE_DIR}/src/backend/game/game_cache.h"
"${CMAKE_SOURCE_DIR}/src/backend/game/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"
@@ -236,6 +234,8 @@ set (LOOT_GUI_SRC "${CMAKE_SOURCE_DIR}/src/gui/main.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/loot_scheme_handler_factory.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/query_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/game.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.cpp"
@@ -278,6 +278,8 @@ set (LOOT_GUI_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/editor_message.h"
"${CMAKE_SOURCE_DIR}/src/gui/query/sort_plugins_query.h"
"${CMAKE_SOURCE_DIR}/src/gui/query/update_masterlist_query.h"
"${CMAKE_SOURCE_DIR}/src/gui/query_handler.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/game.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.h"
@@ -311,7 +313,6 @@ set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/tests/backend/main.cpp")
set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_cache_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/game/game_settings_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/game/load_order_handler_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/git_helper_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/backend/helpers/helpers_test.h"
@@ -343,14 +344,20 @@ set(LOOT_API_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/api/api_game_operatio
"${CMAKE_SOURCE_DIR}/src/tests/api/is_compatible_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/common_game_test_fixture.h")
set(LOOT_GUI_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp"
set(LOOT_GUI_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/gui/state/game.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.cpp"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.cpp"
"${CMAKE_SOURCE_DIR}/src/tests/gui/main.cpp")
set (LOOT_GUI_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h"
set (LOOT_GUI_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/gui/state/game.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/game_settings.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_paths.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_settings.h"
"${CMAKE_SOURCE_DIR}/src/gui/state/loot_state.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/state/game_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/state/game_settings_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_paths_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_settings_test.h"
"${CMAKE_SOURCE_DIR}/src/tests/gui/state/loot_state_test.h")
+7
View File
@@ -70,6 +70,13 @@ public:
* @{
*/
/**
* @brief Identify the game's main master file.
* @details When sorting, LOOT always only loads the headers of the game's
* main master file as a performance optimisation.
*/
virtual void IdentifyMainMasterFile(const std::string& masterFile) = 0;
/**
* @brief Calculates a new load order for the game's installed plugins
* (including inactive plugins) and outputs the sorted order.
+6 -3
View File
@@ -36,8 +36,7 @@
namespace loot {
ApiDatabase::ApiDatabase(const GameType game, const std::string& gamePath, const std::string& gameLocalDataPath)
: game_(Game(GameSettings(GameType(game)), "", gameLocalDataPath)) {
game_.SetGamePath(gamePath);
: game_(game, gamePath, gameLocalDataPath) {
game_.Init();
}
@@ -85,13 +84,17 @@ void ApiDatabase::EvalLists() {
game_.GetUserlist() = userTemp;
}
void ApiDatabase::IdentifyMainMasterFile(const std::string& masterFile) {
masterFile_ = masterFile;
}
////////////////////////////////////
// LOOT Functionality Functions
////////////////////////////////////
std::vector<std::string> ApiDatabase::SortPlugins(const std::vector<std::string>& plugins) {
// Always reload all the plugins.
game_.LoadPlugins(plugins, false);
game_.LoadPlugins(plugins, masterFile_, false);
//Sort plugins into their load order.
PluginSorter sorter;
+4
View File
@@ -43,6 +43,8 @@ struct ApiDatabase : public DatabaseInterface {
void EvalLists();
void IdentifyMainMasterFile(const std::string& masterFile);
std::vector<std::string> SortPlugins(const std::vector<std::string>& plugins);
bool UpdateMasterlist(const std::string& masterlist_path,
@@ -64,6 +66,8 @@ struct ApiDatabase : public DatabaseInterface {
private:
Game game_;
std::string masterFile_;
Masterlist unevaluatedMasterlist_;
MetadataList unevaluatedUserlist_;
};
+22 -186
View File
@@ -58,103 +58,36 @@ using std::vector;
namespace fs = boost::filesystem;
namespace loot {
Game::Game(const GameSettings& gameSettings,
const boost::filesystem::path& lootDataPath,
Game::Game(const GameType gameType,
const boost::filesystem::path& gamePath,
const boost::filesystem::path& localDataPath) :
GameSettings(gameSettings),
lootDataPath_(lootDataPath),
localDataPath_(localDataPath),
pluginsFullyLoaded_(false) {
this->SetName(gameSettings.Name())
.SetMaster(gameSettings.Master())
.SetRepoURL(gameSettings.RepoURL())
.SetRepoBranch(gameSettings.RepoBranch())
.SetGamePath(gameSettings.GamePath())
.SetRegistryKey(gameSettings.RegistryKey());
type_(gameType),
gamePath_(gamePath),
localDataPath_(localDataPath) {}
GameType Game::Type() const {
return type_;
}
bool Game::IsInstalled() {
try {
BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << Name() << "\" is installed.";
if (!GamePath().empty() && fs::exists(GamePath() / "Data" / Master()))
return true;
boost::filesystem::path Game::DataPath() const {
return gamePath_ / "Data";
}
if (fs::exists(fs::path("..") / "Data" / Master())) {
SetGamePath("..");
return true;
}
#ifdef _WIN32
std::string path;
std::string key_parent = fs::path(RegistryKey()).parent_path().string();
std::string key_name = fs::path(RegistryKey()).filename().string();
path = RegKeyStringValue("HKEY_LOCAL_MACHINE", key_parent, key_name);
if (!path.empty() && fs::exists(fs::path(path) / "Data" / Master())) {
SetGamePath(path);
return true;
}
#endif
} catch (std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << Name() << "\" is installed: " << e.what();
}
return false;
std::string Game::GetArchiveFileExtension() const {
if (type_ == GameType::fo4)
return ".ba2";
else
return ".bsa";
}
void Game::Init() {
BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << Name();
BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game of type " << (int) type_ << " at: " << gamePath_;
if (!this->IsInstalled()) {
throw GameDetectionError("Game path could not be detected.");
}
if (!lootDataPath_.empty()) {
//Make sure that the LOOT game path exists.
try {
if (!fs::exists(lootDataPath_ / FolderName()))
fs::create_directories(lootDataPath_ / FolderName());
} catch (fs::filesystem_error& e) {
throw FileAccessError((boost::format("Could not create LOOT folder for game. Details: %1%") % e.what()).str());
}
}
loadOrderHandler_.Init(*this, localDataPath_);
loadOrderHandler_.Init(type_, gamePath_, localDataPath_);
StoreLoadOrder(loadOrderHandler_.GetLoadOrder());
}
void Game::RedatePlugins() {
if (Type() != GameType::tes5 && Type() != GameType::tes5se) {
BOOST_LOG_TRIVIAL(warning) << "Cannot redate plugins for game " << Name();
return;
}
vector<string> loadorder = GetLoadOrder();
if (!loadorder.empty()) {
time_t lastTime = 0;
for (const auto &pluginName : loadorder) {
fs::path filepath = DataPath() / pluginName;
if (!fs::exists(filepath)) {
if (fs::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
else
continue;
}
time_t thisTime = fs::last_write_time(filepath);
BOOST_LOG_TRIVIAL(info) << "Current timestamp for \"" << filepath.filename().string() << "\": " << thisTime;
if (thisTime >= lastTime) {
lastTime = thisTime;
BOOST_LOG_TRIVIAL(trace) << "No need to redate \"" << filepath.filename().string() << "\".";
} else {
lastTime += 60;
fs::last_write_time(filepath, lastTime); //Space timestamps by a minute.
BOOST_LOG_TRIVIAL(info) << "Redated \"" << filepath.filename().string() << "\" to: " << lastTime;
}
}
}
}
void Game::LoadPlugins(const std::vector<std::string>& plugins, bool headersOnly) {
void Game::LoadPlugins(const std::vector<std::string>& plugins, const std::string& masterFile, bool headersOnly) {
uintmax_t meanFileSize = 0;
std::multimap<uintmax_t, string> sizeMap;
@@ -206,7 +139,7 @@ void Game::LoadPlugins(const std::vector<std::string>& plugins, bool headersOnly
threads.push_back(thread([&]() {
for (auto pluginName : pluginGroup) {
BOOST_LOG_TRIVIAL(trace) << "Loading " << pluginName;
if (boost::iequals(pluginName, Master()))
if (boost::iequals(pluginName, masterFile))
AddPlugin(Plugin(*this, pluginName, true));
else
AddPlugin(Plugin(*this, pluginName, headersOnly));
@@ -219,28 +152,6 @@ void Game::LoadPlugins(const std::vector<std::string>& plugins, bool headersOnly
if (thread.joinable())
thread.join();
}
pluginsFullyLoaded_ = !headersOnly;
}
void Game::LoadAllInstalledPlugins(bool headersOnly) {
std::vector<std::string> plugins;
BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath();
for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && Plugin::IsValid(it->path().filename().string(), *this)) {
string name = it->path().filename().string();
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << name;
plugins.push_back(name);
}
}
LoadPlugins(plugins, headersOnly);
}
bool Game::ArePluginsFullyLoaded() const {
return pluginsFullyLoaded_;
}
bool Game::IsPluginActive(const std::string& pluginName) const {
@@ -251,91 +162,16 @@ bool Game::IsPluginActive(const std::string& pluginName) const {
}
}
short Game::GetActiveLoadOrderIndex(const std::string & pluginName) const {
return GetActiveLoadOrderIndex(pluginName, GetLoadOrder());
}
short Game::GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector<std::string>& loadOrder) const {
// Get the full load order, then count the number of active plugins until the
// given plugin is encountered. If the plugin isn't active or in the load
// order, return -1.
if (!IsPluginActive(pluginName))
return -1;
short numberOfActivePlugins = 0;
for (const std::string& plugin : loadOrder) {
if (boost::iequals(plugin, pluginName))
return numberOfActivePlugins;
if (IsPluginActive(plugin))
++numberOfActivePlugins;
}
return -1;
}
std::vector<std::string> Game::GetLoadOrder() const {
auto loadOrder = GameCache::GetLoadOrder();
if (loadOrder.empty())
return loadOrderHandler_.GetLoadOrder();
return loadOrder;
}
void Game::SetLoadOrder(const std::vector<std::string>& loadOrder) {
loadOrderHandler_.BackupLoadOrder(GetLoadOrder(), lootDataPath_ / FolderName());
loadOrderHandler_.SetLoadOrder(loadOrder);
StoreLoadOrder(loadOrder);
}
fs::path Game::MasterlistPath() const {
if (lootDataPath_.empty() || FolderName().empty())
return "";
else
return lootDataPath_ / FolderName() / "masterlist.yaml";
}
fs::path Game::UserlistPath() const {
if (lootDataPath_.empty() || FolderName().empty())
return "";
else
return lootDataPath_ / FolderName() / "userlist.yaml";
}
#ifdef _WIN32
std::string Game::RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) {
HKEY hKey = NULL;
DWORD len = MAX_PATH;
std::wstring wstr(MAX_PATH, 0);
if (keyStr == "HKEY_CLASSES_ROOT")
hKey = HKEY_CLASSES_ROOT;
else if (keyStr == "HKEY_CURRENT_CONFIG")
hKey = HKEY_CURRENT_CONFIG;
else if (keyStr == "HKEY_CURRENT_USER")
hKey = HKEY_CURRENT_USER;
else if (keyStr == "HKEY_LOCAL_MACHINE")
hKey = HKEY_LOCAL_MACHINE;
else if (keyStr == "HKEY_USERS")
hKey = HKEY_USERS;
else
throw std::invalid_argument("Invalid registry key given.");
BOOST_LOG_TRIVIAL(trace) << "Getting string for registry key, subkey and value: " << keyStr << " + " << subkey << " + " << value;
LONG ret = RegGetValue(hKey,
ToWinWide(subkey).c_str(),
ToWinWide(value).c_str(),
RRF_RT_REG_SZ | KEY_WOW64_32KEY,
NULL,
&wstr[0],
&len);
if (ret == ERROR_SUCCESS) {
BOOST_LOG_TRIVIAL(info) << "Found string: " << wstr.c_str();
return FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer.
} else {
BOOST_LOG_TRIVIAL(info) << "Failed to get string value.";
return "";
}
}
#endif
}
+10 -23
View File
@@ -30,45 +30,32 @@
#include <boost/filesystem.hpp>
#include "backend/game/game_cache.h"
#include "backend/game/game_settings.h"
#include "backend/game/load_order_handler.h"
namespace loot {
class Game : public GameSettings, public GameCache {
class Game : public GameCache {
public:
Game(const GameSettings& gameSettings,
const boost::filesystem::path& lootDataPath,
Game(const GameType gameType,
const boost::filesystem::path& gamePath,
const boost::filesystem::path& localDataPath = "");
bool IsInstalled(); //Sets gamePath if the current value is not valid and a valid path is found.
GameType Type() const;
boost::filesystem::path DataPath() const;
std::string GetArchiveFileExtension() const;
void Init();
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
void LoadPlugins(const std::vector<std::string>& plugins, const std::string& masterFile, bool headersOnly);
void LoadPlugins(const std::vector<std::string>& plugins, bool headersOnly);
void LoadAllInstalledPlugins(bool headersOnly); //Loads all installed plugins.
bool ArePluginsFullyLoaded() const; // Checks if the game's plugins have already been loaded.
// Check if the plugin is active by using the cached value if
// available, and otherwise asking the load order handler.
bool IsPluginActive(const std::string& pluginName) const;
short GetActiveLoadOrderIndex(const std::string & pluginName) const;
short GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector<std::string>& loadOrder) const;
std::vector<std::string> GetLoadOrder() const;
void SetLoadOrder(const std::vector<std::string>& loadOrder);
boost::filesystem::path MasterlistPath() const;
boost::filesystem::path UserlistPath() const;
private:
#ifdef _WIN32
std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value);
#endif
const boost::filesystem::path lootDataPath_;
const GameType type_;
const boost::filesystem::path gamePath_;
const boost::filesystem::path localDataPath_;
bool pluginsFullyLoaded_;
LoadOrderHandler loadOrderHandler_;
};
}
+16 -33
View File
@@ -40,8 +40,10 @@ LoadOrderHandler::~LoadOrderHandler() {
lo_destroy_handle(gh_);
}
void LoadOrderHandler::Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData) {
if (game.GamePath().empty()) {
void LoadOrderHandler::Init(const GameType& gameType,
const boost::filesystem::path& gamePath,
const boost::filesystem::path& gameLocalAppData) {
if (gamePath.empty()) {
throw std::invalid_argument("Game path is not initialised.");
}
@@ -57,18 +59,18 @@ void LoadOrderHandler::Init(const GameSettings& game, const boost::filesystem::p
}
int ret;
if (game.Type() == GameType::tes4)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES4, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Type() == GameType::tes5)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES5, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Type() == GameType::tes5se)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES5SE, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Type() == GameType::fo3)
ret = lo_create_handle(&gh_, LIBLO_GAME_FO3, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Type() == GameType::fonv)
ret = lo_create_handle(&gh_, LIBLO_GAME_FNV, game.GamePath().string().c_str(), gameLocalDataPath);
else if (game.Type() == GameType::fo4)
ret = lo_create_handle(&gh_, LIBLO_GAME_FO4, game.GamePath().string().c_str(), gameLocalDataPath);
if (gameType == GameType::tes4)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath);
else if (gameType == GameType::tes5)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES5, gamePath.string().c_str(), gameLocalDataPath);
else if (gameType == GameType::tes5se)
ret = lo_create_handle(&gh_, LIBLO_GAME_TES5SE, gamePath.string().c_str(), gameLocalDataPath);
else if (gameType == GameType::fo3)
ret = lo_create_handle(&gh_, LIBLO_GAME_FO3, gamePath.string().c_str(), gameLocalDataPath);
else if (gameType == GameType::fonv)
ret = lo_create_handle(&gh_, LIBLO_GAME_FNV, gamePath.string().c_str(), gameLocalDataPath);
else if (gameType == GameType::fo4)
ret = lo_create_handle(&gh_, LIBLO_GAME_FO4, gamePath.string().c_str(), gameLocalDataPath);
else
ret = LIBLO_ERROR_INVALID_ARGS;
@@ -166,23 +168,4 @@ void LoadOrderHandler::SetLoadOrder(const std::vector<std::string>& loadOrder) c
throw std::system_error(ret, libloadorder_category(), err);
}
}
void LoadOrderHandler::BackupLoadOrder(const std::vector<std::string>& loadOrder,
const boost::filesystem::path & backupDirectory) {
const int maxBackupIndex = 2;
boost::format filenameFormat = boost::format("loadorder.bak.%1%");
boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % 2).str();
if (boost::filesystem::exists(backupFilePath))
boost::filesystem::remove(backupFilePath);
for (int i = maxBackupIndex - 1; i > -1; --i) {
const boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % i).str();
if (boost::filesystem::exists(backupFilePath))
boost::filesystem::rename(backupFilePath, backupDirectory / (filenameFormat % (i + 1)).str());
}
boost::filesystem::ofstream out(backupDirectory / (filenameFormat % 0).str());
for (const auto &plugin : loadOrder)
out << plugin << std::endl;
}
}
+4 -5
View File
@@ -32,7 +32,7 @@
#include <boost/filesystem.hpp>
#include <libloadorder/libloadorder.h>
#include "backend/game/game_settings.h"
#include "loot/enum/game_type.h"
namespace loot {
class LoadOrderHandler {
@@ -40,16 +40,15 @@ public:
LoadOrderHandler();
~LoadOrderHandler();
void Init(const GameSettings& game, const boost::filesystem::path& gameLocalAppData = "");
void Init(const GameType& game,
const boost::filesystem::path& gamePath,
const boost::filesystem::path& gameLocalAppData = "");
std::vector<std::string> GetLoadOrder() const;
bool IsPluginActive(const std::string& pluginName) const;
void SetLoadOrder(const std::vector<std::string>& loadOrder) const;
static void BackupLoadOrder(const std::vector<std::string>& loadOrder,
const boost::filesystem::path& backupDirectory);
private:
lo_game_handle gh_;
};
-4
View File
@@ -84,10 +84,6 @@ MasterlistInfo Masterlist::GetInfo(const boost::filesystem::path& path, bool sho
return info;
}
bool Masterlist::Update(const Game& game) {
return Update(game.MasterlistPath(), game.RepoURL(), game.RepoBranch());
}
bool Masterlist::Update(const boost::filesystem::path& path, const std::string& repoUrl, const std::string& repoBranch) {
GitHelper git;
fs::path repoPath = path.parent_path();
-1
View File
@@ -37,7 +37,6 @@ class Game;
class Masterlist : public MetadataList {
public:
bool Update(const Game& game);
bool Update(const boost::filesystem::path& path,
const std::string& repoURL,
const std::string& repoBranch);
+2 -2
View File
@@ -25,7 +25,7 @@ along with LOOT. If not, see
#ifndef LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY
#define LOOT_GUI_QUERY_CLEAR_ALL_METADATA_QUERY
#include "backend/game/game.h"
#include "gui/state/game.h"
#include "gui/query/json.h"
#include "gui/query/metadata_query.h"
@@ -77,7 +77,7 @@ private:
return "[]";
}
Game& game_;
gui::Game& game_;
};
}
+2 -2
View File
@@ -25,7 +25,7 @@ along with LOOT. If not, see
#ifndef LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY
#define LOOT_GUI_QUERY_CLEAR_PLUGIN_METADATA_QUERY
#include "backend/game/game.h"
#include "gui/state/game.h"
#include "gui/query/json.h"
#include "gui/query/metadata_query.h"
@@ -52,7 +52,7 @@ public:
}
private:
Game& game_;
gui::Game& game_;
const std::string pluginName_;
};
}
@@ -25,7 +25,7 @@ along with LOOT. If not, see
#ifndef LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY
#define LOOT_GUI_QUERY_GET_CONFLICTING_PLUGINS_QUERY
#include "backend/game/game.h"
#include "gui/state/game.h"
#include "gui/query/json.h"
#include "gui/query/metadata_query.h"
@@ -75,7 +75,7 @@ private:
return pluginNode;
}
Game& game_;
gui::Game& game_;
const std::string pluginName_;
};
}
+1 -1
View File
@@ -25,7 +25,7 @@ along with LOOT. If not, see
#ifndef LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY
#define LOOT_GUI_QUERY_GET_GAME_TYPES_QUERY
#include "backend/game/game_settings.h"
#include "gui/state/game_settings.h"
#include "gui/query/json.h"
#include "gui/query/query.h"
-1
View File
@@ -28,7 +28,6 @@ along with LOOT. If not, see
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include "backend/game/game.h"
#include "backend/plugin/plugin.h"
#include "gui/query/query.h"
#include "loot/exception/file_access_error.h"
+3 -3
View File
@@ -25,7 +25,7 @@ along with LOOT. If not, see
#ifndef LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY
#define LOOT_GUI_QUERY_UPDATE_MASTERLIST_QUERY
#include "backend/game/game.h"
#include "gui/state/game.h"
#include "gui/query/json.h"
#include "gui/query/metadata_query.h"
@@ -49,7 +49,7 @@ public:
private:
bool updateMasterlist() {
try {
return game_.GetMasterlist().Update(game_);
return game_.GetMasterlist().Update(game_.MasterlistPath(), game_.RepoURL(), game_.RepoBranch());
} catch (std::exception&) {
try {
game_.GetMasterlist().Load(game_.MasterlistPath());
@@ -104,7 +104,7 @@ private:
return pluginNode;
}
Game& game_;
gui::Game& game_;
};
}
+268
View File
@@ -0,0 +1,268 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2016 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
<https://www.gnu.org/licenses/>.
*/
#include "gui/state/game.h"
#include <algorithm>
#include <thread>
#include <cmath>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include "loot/exception/file_access_error.h"
#include "loot/exception/game_detection_error.h"
#include "backend/helpers/helpers.h"
#ifdef _WIN32
# ifndef UNICODE
# define UNICODE
# endif
# ifndef _UNICODE
# define _UNICODE
# endif
# define NOMINMAX
# include "windows.h"
# include "shlobj.h"
# include "shlwapi.h"
#endif
using std::list;
using std::string;
using std::thread;
using std::vector;
namespace fs = boost::filesystem;
namespace loot {
namespace gui {
Game::Game(const GameSettings& gameSettings,
const boost::filesystem::path& lootDataPath,
const boost::filesystem::path& localDataPath) :
loot::Game(gameSettings.Type(), gameSettings.GamePath(), localDataPath),
GameSettings(gameSettings),
lootDataPath_(lootDataPath),
pluginsFullyLoaded_(false) {}
bool Game::IsInstalled() {
try {
BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << Name() << "\" is installed.";
if (!GamePath().empty() && fs::exists(GamePath() / "Data" / Master()))
return true;
if (fs::exists(fs::path("..") / "Data" / Master())) {
SetGamePath("..");
return true;
}
#ifdef _WIN32
std::string path;
std::string key_parent = fs::path(RegistryKey()).parent_path().string();
std::string key_name = fs::path(RegistryKey()).filename().string();
path = RegKeyStringValue("HKEY_LOCAL_MACHINE", key_parent, key_name);
if (!path.empty() && fs::exists(fs::path(path) / "Data" / Master())) {
SetGamePath(path);
return true;
}
#endif
} catch (std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << Name() << "\" is installed: " << e.what();
}
return false;
}
void Game::Init() {
BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << Name();
if (!this->IsInstalled()) {
throw GameDetectionError("Game path could not be detected.");
}
if (!lootDataPath_.empty()) {
//Make sure that the LOOT game path exists.
try {
if (!fs::exists(lootDataPath_ / FolderName()))
fs::create_directories(lootDataPath_ / FolderName());
} catch (fs::filesystem_error& e) {
throw FileAccessError((boost::format("Could not create LOOT folder for game. Details: %1%") % e.what()).str());
}
}
loot::Game::Init();
}
void Game::RedatePlugins() {
if (Type() != GameType::tes5 && Type() != GameType::tes5se) {
BOOST_LOG_TRIVIAL(warning) << "Cannot redate plugins for game " << Name();
return;
}
vector<string> loadorder = GetLoadOrder();
if (!loadorder.empty()) {
time_t lastTime = 0;
for (const auto &pluginName : loadorder) {
fs::path filepath = DataPath() / pluginName;
if (!fs::exists(filepath)) {
if (fs::exists(filepath.string() + ".ghost"))
filepath += ".ghost";
else
continue;
}
time_t thisTime = fs::last_write_time(filepath);
BOOST_LOG_TRIVIAL(info) << "Current timestamp for \"" << filepath.filename().string() << "\": " << thisTime;
if (thisTime >= lastTime) {
lastTime = thisTime;
BOOST_LOG_TRIVIAL(trace) << "No need to redate \"" << filepath.filename().string() << "\".";
} else {
lastTime += 60;
fs::last_write_time(filepath, lastTime); //Space timestamps by a minute.
BOOST_LOG_TRIVIAL(info) << "Redated \"" << filepath.filename().string() << "\" to: " << lastTime;
}
}
}
}
void Game::LoadAllInstalledPlugins(bool headersOnly) {
std::vector<std::string> plugins;
BOOST_LOG_TRIVIAL(trace) << "Scanning for plugins in " << this->DataPath();
for (fs::directory_iterator it(this->DataPath()); it != fs::directory_iterator(); ++it) {
if (fs::is_regular_file(it->status()) && Plugin::IsValid(it->path().filename().string(), *this)) {
string name = it->path().filename().string();
BOOST_LOG_TRIVIAL(info) << "Found plugin: " << name;
plugins.push_back(name);
}
}
LoadPlugins(plugins, Master(), headersOnly);
pluginsFullyLoaded_ = !headersOnly;
}
bool Game::ArePluginsFullyLoaded() const {
return pluginsFullyLoaded_;
}
fs::path Game::MasterlistPath() const {
if (lootDataPath_.empty() || FolderName().empty())
return "";
else
return lootDataPath_ / FolderName() / "masterlist.yaml";
}
fs::path Game::UserlistPath() const {
if (lootDataPath_.empty() || FolderName().empty())
return "";
else
return lootDataPath_ / FolderName() / "userlist.yaml";
}
short Game::GetActiveLoadOrderIndex(const std::string & pluginName) const {
return GetActiveLoadOrderIndex(pluginName, GetLoadOrder());
}
short Game::GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector<std::string>& loadOrder) const {
// Get the full load order, then count the number of active plugins until the
// given plugin is encountered. If the plugin isn't active or in the load
// order, return -1.
if (!IsPluginActive(pluginName))
return -1;
short numberOfActivePlugins = 0;
for (const std::string& plugin : loadOrder) {
if (boost::iequals(plugin, pluginName))
return numberOfActivePlugins;
if (IsPluginActive(plugin))
++numberOfActivePlugins;
}
return -1;
}
void Game::BackupLoadOrder(const std::vector<std::string>& loadOrder,
const boost::filesystem::path & backupDirectory) {
const int maxBackupIndex = 2;
boost::format filenameFormat = boost::format("loadorder.bak.%1%");
boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % 2).str();
if (boost::filesystem::exists(backupFilePath))
boost::filesystem::remove(backupFilePath);
for (int i = maxBackupIndex - 1; i > -1; --i) {
const boost::filesystem::path backupFilePath = backupDirectory / (filenameFormat % i).str();
if (boost::filesystem::exists(backupFilePath))
boost::filesystem::rename(backupFilePath, backupDirectory / (filenameFormat % (i + 1)).str());
}
boost::filesystem::ofstream out(backupDirectory / (filenameFormat % 0).str());
for (const auto &plugin : loadOrder)
out << plugin << std::endl;
}
#ifdef _WIN32
std::string Game::RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value) {
HKEY hKey = NULL;
DWORD len = MAX_PATH;
std::wstring wstr(MAX_PATH, 0);
if (keyStr == "HKEY_CLASSES_ROOT")
hKey = HKEY_CLASSES_ROOT;
else if (keyStr == "HKEY_CURRENT_CONFIG")
hKey = HKEY_CURRENT_CONFIG;
else if (keyStr == "HKEY_CURRENT_USER")
hKey = HKEY_CURRENT_USER;
else if (keyStr == "HKEY_LOCAL_MACHINE")
hKey = HKEY_LOCAL_MACHINE;
else if (keyStr == "HKEY_USERS")
hKey = HKEY_USERS;
else
throw std::invalid_argument("Invalid registry key given.");
BOOST_LOG_TRIVIAL(trace) << "Getting string for registry key, subkey and value: " << keyStr << " + " << subkey << " + " << value;
LONG ret = RegGetValue(hKey,
ToWinWide(subkey).c_str(),
ToWinWide(value).c_str(),
RRF_RT_REG_SZ | KEY_WOW64_32KEY,
NULL,
&wstr[0],
&len);
if (ret == ERROR_SUCCESS) {
BOOST_LOG_TRIVIAL(info) << "Found string: " << wstr.c_str();
return FromWinWide(wstr.c_str()); // Passing c_str() cuts off any unused buffer.
} else {
BOOST_LOG_TRIVIAL(info) << "Failed to get string value.";
return "";
}
}
#endif
}
}
+73
View File
@@ -0,0 +1,73 @@
/* LOOT
A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and
Fallout: New Vegas.
Copyright (C) 2012-2016 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
<https://www.gnu.org/licenses/>.
*/
#ifndef LOOT_GUI_STATE_GAME
#define LOOT_GUI_STATE_GAME
#include <string>
#include <boost/filesystem.hpp>
#include "backend/game/game.h"
#include "gui/state/game_settings.h"
namespace loot {
namespace gui {
class Game : public loot::Game, public GameSettings {
public:
Game(const GameSettings& gameSettings,
const boost::filesystem::path& lootDataPath,
const boost::filesystem::path& localDataPath = "");
using GameSettings::Type;
bool IsInstalled(); //Sets gamePath if the current value is not valid and a valid path is found.
void Init();
void RedatePlugins(); //Change timestamps to match load order (Skyrim only).
void LoadAllInstalledPlugins(bool headersOnly); //Loads all installed plugins.
bool ArePluginsFullyLoaded() const; // Checks if the game's plugins have already been loaded.
boost::filesystem::path MasterlistPath() const;
boost::filesystem::path UserlistPath() const;
short GetActiveLoadOrderIndex(const std::string & pluginName) const;
short GetActiveLoadOrderIndex(const std::string & pluginName, const std::vector<std::string>& loadOrder) const;
static void BackupLoadOrder(const std::vector<std::string>& loadOrder,
const boost::filesystem::path& backupDirectory);
private:
#ifdef _WIN32
std::string RegKeyStringValue(const std::string& keyStr, const std::string& subkey, const std::string& value);
#endif
const boost::filesystem::path lootDataPath_;
bool pluginsFullyLoaded_;
};
}
}
#endif
@@ -22,7 +22,7 @@
<https://www.gnu.org/licenses/>.
*/
#include "backend/game/game_settings.h"
#include "gui/state/game_settings.h"
#include <boost/algorithm/string.hpp>
#include <boost/locale.hpp>
@@ -124,20 +124,6 @@ fs::path GameSettings::GamePath() const {
return gamePath_;
}
fs::path GameSettings::DataPath() const {
if (gamePath_.empty())
return "";
else
return gamePath_ / "Data";
}
std::string GameSettings::GetArchiveFileExtension() const {
if (type_ == GameType::fo4)
return ".ba2";
else
return ".bsa";
}
GameSettings& GameSettings::SetName(const std::string& name) {
BOOST_LOG_TRIVIAL(trace) << "Setting \"" << name_ << "\" name to: " << name;
name_ = name;
@@ -22,8 +22,8 @@
<https://www.gnu.org/licenses/>.
*/
#ifndef LOOT_BACKEND_GAME_GAME_SETTINGS
#define LOOT_BACKEND_GAME_GAME_SETTINGS
#ifndef LOOT_GUI_STATE_GAME_SETTINGS
#define LOOT_GUI_STATE_GAME_SETTINGS
#include <string>
#include <list>
@@ -50,11 +50,7 @@ public:
std::string RegistryKey() const;
std::string RepoURL() const;
std::string RepoBranch() const;
boost::filesystem::path GamePath() const;
boost::filesystem::path DataPath() const;
std::string GetArchiveFileExtension() const;
GameSettings& SetName(const std::string& name);
GameSettings& SetMaster(const std::string& masterFile);

Some files were not shown because too many files have changed in this diff Show More