From 0302a53dcb05d62b44aab0cb7dd4890a34570819 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sat, 13 Jun 2015 16:33:33 +0100 Subject: [PATCH] Split game settings into new GameSettings class. I added some conversion functions to minimise changes in the rest of the code, they might be replaced by some other way of handling things though. The code files may also be moved into a subdirectory. --- CMakeLists.txt | 2 + src/api/api.cpp | 3 +- src/backend/game.cpp | 255 +++++----------------------------- src/backend/game.h | 105 ++------------ src/backend/game_settings.cpp | 250 +++++++++++++++++++++++++++++++++ src/backend/game_settings.h | 145 +++++++++++++++++++ src/gui/handler.cpp | 2 +- src/gui/loot_state.cpp | 27 ++-- src/gui/loot_state.h | 4 +- 9 files changed, 456 insertions(+), 337 deletions(-) create mode 100644 src/backend/game_settings.cpp create mode 100644 src/backend/game_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 48e17914..e191f635 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,7 @@ set (LOOT_SRC "${CMAKE_SOURCE_DIR}/src/backend/metadata/conditional_metadata.c "${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_metadata.cpp" "${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/metadata_list.cpp" "${CMAKE_SOURCE_DIR}/src/backend/masterlist.cpp" "${CMAKE_SOURCE_DIR}/src/backend/plugin.cpp" @@ -108,6 +109,7 @@ set (LOOT_HEADERS "${CMAKE_SOURCE_DIR}/src/backend/metadata/condition_grammar.h" "${CMAKE_SOURCE_DIR}/src/backend/metadata/plugin_metadata.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/metadata_list.h" "${CMAKE_SOURCE_DIR}/src/backend/masterlist.h" "${CMAKE_SOURCE_DIR}/src/backend/plugin.h" diff --git a/src/api/api.cpp b/src/api/api.cpp index 42bdf5b2..99f1d520 100644 --- a/src/api/api.cpp +++ b/src/api/api.cpp @@ -99,7 +99,8 @@ struct _loot_db_int : public loot::Game { extStringArraySize(0), extRevisionID(nullptr), extRevisionDate(nullptr) { - this->SetDetails("", "", "", "", gamePath, "").Init(false, gameLocalDataPath); + this->SetDetails("", "", "", "", gamePath, ""); + this->Init(false, gameLocalDataPath); } ~_loot_db_int() { diff --git a/src/backend/game.cpp b/src/backend/game.cpp index f9164d5c..b73ba959 100644 --- a/src/backend/game.cpp +++ b/src/backend/game.cpp @@ -40,143 +40,44 @@ namespace fs = boost::filesystem; namespace lc = boost::locale; namespace loot { - std::list GetGames(YAML::Node& settings) { - list games; + Game::Game() : GameSettings(), gh(nullptr) {} - if (settings["games"]) - games = settings["games"].as< list >(); - - if (find(games.begin(), games.end(), Game(Game::tes4)) == games.end()) - games.push_back(Game(Game::tes4)); - - if (find(games.begin(), games.end(), Game(Game::tes5)) == games.end()) - games.push_back(Game(Game::tes5)); - - if (find(games.begin(), games.end(), Game(Game::fo3)) == games.end()) - games.push_back(Game(Game::fo3)); - - if (find(games.begin(), games.end(), Game(Game::fonv)) == games.end()) - games.push_back(Game(Game::fonv)); - - // If there were any missing defaults, make sure they're in settings now. - settings["games"] = games; - - return games; + Game::Game(const GameSettings& gameSettings) : Game(gameSettings.Id(), gameSettings.FolderName()) { + this->SetDetails(gameSettings.Name(), + gameSettings.Master(), + gameSettings.RepoURL(), + gameSettings.RepoBranch(), + gameSettings.GamePath().string(), + gameSettings.RegistryKey()); } - // Game member functions - //---------------------- - - Game::Game() : id(Game::autodetect), gh(nullptr) {} - - Game::Game(const unsigned int gameCode, const std::string& folder) : id(gameCode), gh(nullptr) { + Game::Game(const unsigned int gameCode, const std::string& folder) : GameSettings(gameCode, folder), gh(nullptr) { if (Id() == Game::tes4) { - _name = "TES IV: Oblivion"; - registryKey = "Software\\Bethesda Softworks\\Oblivion\\Installed Path"; - lootFolderName = "Oblivion"; - _masterFile = "Oblivion.esm"; espm_settings = espm::Settings("tes4"); - _repositoryURL = "https://github.com/loot/oblivion.git"; - _repositoryBranch = "master"; } else if (Id() == Game::tes5) { - _name = "TES V: Skyrim"; - registryKey = "Software\\Bethesda Softworks\\Skyrim\\Installed Path"; - lootFolderName = "Skyrim"; - _masterFile = "Skyrim.esm"; espm_settings = espm::Settings("tes5"); - _repositoryURL = "https://github.com/loot/skyrim.git"; - _repositoryBranch = "master"; } else if (Id() == Game::fo3) { - _name = "Fallout 3"; - registryKey = "Software\\Bethesda Softworks\\Fallout3\\Installed Path"; - lootFolderName = "Fallout3"; - _masterFile = "Fallout3.esm"; espm_settings = espm::Settings("fo3"); - _repositoryURL = "https://github.com/loot/fallout3.git"; - _repositoryBranch = "master"; } else if (Id() == Game::fonv) { - _name = "Fallout: New Vegas"; - registryKey = "Software\\Bethesda Softworks\\FalloutNV\\Installed Path"; - lootFolderName = "FalloutNV"; - _masterFile = "FalloutNV.esm"; espm_settings = espm::Settings("fonv"); - _repositoryURL = "https://github.com/loot/falloutnv.git"; - _repositoryBranch = "master"; } - - if (!folder.empty()) - lootFolderName = folder; } Game::~Game() { lo_destroy_handle(gh); } - Game& Game::SetDetails(const std::string& name, const std::string& masterFile, - const std::string& repositoryURL, const std::string& repositoryBranch, const std::string& path, const std::string& registry) { - BOOST_LOG_TRIVIAL(info) << "Setting new details for game: " << _name; - - if (!name.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting name to: " << name; - _name = name; - } - - if (!masterFile.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting master file to: " << masterFile; - _masterFile = masterFile; - } - - if (!repositoryURL.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting repo URL to: " << repositoryURL; - _repositoryURL = repositoryURL; - } - - if (!repositoryBranch.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting repo branch to: " << repositoryBranch; - _repositoryBranch = repositoryBranch; - } - - if (!path.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting game path to: " << path; - gamePath = path; - } - - if (!registry.empty()) { - BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting registry key to: " << registry; - registryKey = registry; - } - - return *this; - } - Game& Game::Init(bool createFolder, const boost::filesystem::path& gameLocalAppData) { - if (id != Game::tes4 && id != Game::tes5 && id != Game::fo3 && id != Game::fonv) { + 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()); } - BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << _name; + BOOST_LOG_TRIVIAL(info) << "Initialising filesystem-related data for game: " << Name(); - //First look for local install, then look for Registry. - if (gamePath.empty() || !fs::exists(gamePath / "Data" / _masterFile)) { - if (fs::exists(fs::path("..") / "Data" / _masterFile)) { - gamePath = ".."; -#ifdef _WIN32 - } - else { - string path; - string key_parent = fs::path(registryKey).parent_path().string(); - 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" / _masterFile)) - gamePath = fs::path(path); -#endif - } - } - - if (gamePath.empty()) { + if (!this->IsInstalled()) { BOOST_LOG_TRIVIAL(error) << "Game path could not be detected."; throw error(error::path_not_found, lc::translate("Game path could not be detected.").str()); } @@ -194,81 +95,16 @@ namespace loot { return *this; } - bool Game::IsInstalled() const { - try { - BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << _name << "\" is installed."; - if (!gamePath.empty() && fs::exists(gamePath / "Data" / _masterFile)) - return true; - - if (fs::exists(fs::path("..") / "Data" / _masterFile)) - return true; - -#ifdef _WIN32 - string path; - string key_parent = fs::path(registryKey).parent_path().string(); - 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" / _masterFile)) - return true; -#endif - } - catch (exception &e) { - BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << _name << "\" is installed: " << e.what(); - } - - return false; + bool Game::operator == (const Game& rhs) const { + return (boost::iequals(Name(), rhs.Name()) || boost::iequals(FolderName(), rhs.FolderName())); } - bool Game::operator == (const Game& rhs) const { - return (boost::iequals(_name, rhs.Name()) || boost::iequals(lootFolderName, rhs.FolderName())); + bool Game::operator == (const GameSettings& rhs) const { + return (boost::iequals(Name(), rhs.Name()) || boost::iequals(FolderName(), rhs.FolderName())); } bool Game::operator == (const std::string& nameOrFolderName) const { - return (boost::iequals(_name, nameOrFolderName) || boost::iequals(lootFolderName, nameOrFolderName)); - } - - unsigned int Game::Id() const { - return id; - } - - string Game::Name() const { - return _name; - } - - string Game::FolderName() const { - return lootFolderName; - } - - std::string Game::Master() const { - return _masterFile; - } - - std::string Game::RegistryKey() const { - return registryKey; - } - - std::string Game::RepoURL() const { - return _repositoryURL; - } - - std::string Game::RepoBranch() const { - return _repositoryBranch; - } - - fs::path Game::GamePath() const { - return gamePath; - } - - fs::path Game::DataPath() const { - return GamePath() / "Data"; - } - - fs::path Game::MasterlistPath() const { - return g_path_local / lootFolderName / "masterlist.yaml"; - } - - fs::path Game::UserlistPath() const { - return g_path_local / lootFolderName / "userlist.yaml"; + return (boost::iequals(Name(), nameOrFolderName) || boost::iequals(FolderName(), nameOrFolderName)); } void Game::InitLibloHandle() { @@ -285,13 +121,13 @@ namespace loot { int ret; if (Id() == Game::tes4) - ret = lo_create_handle(&gh, LIBLO_GAME_TES4, gamePath.string().c_str(), gameLocalDataPath); + 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); + 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); + 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); + ret = lo_create_handle(&gh, LIBLO_GAME_FNV, GamePath().string().c_str(), gameLocalDataPath); else ret = LIBLO_ERROR_INVALID_ARGS; @@ -311,8 +147,8 @@ namespace loot { throw error(error::liblo_error, err); } - if (id != Game::tes5) { - ret = lo_set_game_master(gh, _masterFile.c_str()); + 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; @@ -335,7 +171,7 @@ namespace loot { } void Game::RefreshActivePluginsList() { - BOOST_LOG_TRIVIAL(debug) << "Refreshing active plugins list for game: " << _name; + BOOST_LOG_TRIVIAL(debug) << "Refreshing active plugins list for game: " << Name(); char ** pluginArr; size_t pluginArrSize; @@ -363,7 +199,7 @@ namespace loot { } void Game::GetLoadOrder(std::list& loadOrder) const { - BOOST_LOG_TRIVIAL(debug) << "Getting load order for game: " << _name; + BOOST_LOG_TRIVIAL(debug) << "Getting load order for game: " << Name(); char ** pluginArr; size_t pluginArrSize; @@ -392,7 +228,7 @@ namespace loot { } void Game::SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const { - BOOST_LOG_TRIVIAL(debug) << "Setting load order for game: " << _name; + 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) { @@ -440,7 +276,7 @@ namespace loot { } void Game::RedatePlugins() { - if (id != tes5) + if (Id() != tes5) return; list loadorder; @@ -539,7 +375,7 @@ namespace loot { bool Game::HasBeenLoaded() { // Easy way to check is by checking the game's master file, // which definitely shouldn't be empty. - auto pairIt = plugins.find(boost::locale::to_lower(_masterFile)); + auto pairIt = plugins.find(boost::locale::to_lower(Master())); if (pairIt != plugins.end()) return !pairIt->second.FormIDs().empty(); @@ -547,18 +383,6 @@ namespace loot { return false; } - void Game::CreateLOOTGameFolder() { - //Make sure that the LOOT game path exists. - try { - if (fs::exists(g_path_local) && !fs::exists(g_path_local / lootFolderName)) - fs::create_directory(g_path_local / lootFolderName); - } - catch (fs::filesystem_error& e) { - BOOST_LOG_TRIVIAL(error) << "Could not create LOOT folder for game. Details: " << e.what(); - throw error(error::path_write_fail, lc::translate("Could not create LOOT folder for game. Details:").str() + " " + e.what()); - } - } - std::list Game::Sort(const unsigned int language, std::function progressCallback) { //Create a plugin graph containing the plugin and masterlist data. loot::PluginGraph graph; @@ -608,21 +432,12 @@ namespace loot { progressCallback(lc::translate("Adding edges to plugin graph and performing topological sort...")); return loot::Sort(graph, loadorder); } -} -namespace YAML { - Emitter& operator << (Emitter& out, const loot::Game& rhs) { - out << BeginMap - << Key << "type" << Value << YAML::SingleQuoted << loot::Game(rhs.Id()).FolderName() - << Key << "folder" << Value << YAML::SingleQuoted << rhs.FolderName() - << Key << "name" << Value << YAML::SingleQuoted << rhs.Name() - << Key << "master" << Value << YAML::SingleQuoted << rhs.Master() - << Key << "repo" << Value << YAML::SingleQuoted << rhs.RepoURL() - << Key << "branch" << Value << YAML::SingleQuoted << rhs.RepoBranch() - << Key << "path" << Value << YAML::SingleQuoted << rhs.GamePath().string() - << Key << "registry" << Value << YAML::SingleQuoted << rhs.RegistryKey() - << EndMap; - - return out; + std::list ToGames(const std::list& settings) { + return list(settings.begin(), settings.end()); } -} \ No newline at end of file + + std::list ToGameSettings(const std::list& games) { + return list(games.begin(), games.end()); + } +} diff --git a/src/backend/game.h b/src/backend/game.h index 8f0f8b03..37e13537 100644 --- a/src/backend/game.h +++ b/src/backend/game.h @@ -25,6 +25,7 @@ #ifndef __LOOT_GAME__ #define __LOOT_GAME__ +#include "game_settings.h" #include "plugin.h" #include "metadata_list.h" #include "masterlist.h" @@ -43,36 +44,21 @@ #include namespace loot { - class Game { + class Game : public GameSettings { 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& SetDetails(const std::string& name, const std::string& masterFile, - const std::string& repositoryURL, const std::string& repositoryBranch, - const std::string& path, const std::string& registry); Game& Init(bool createFolder, const boost::filesystem::path& gameLocalAppData = ""); - bool IsInstalled() const; - - bool operator == (const Game& rhs) const; //Compares names and folder names. + //Compare names and folder names. + bool operator == (const Game& rhs) const; + bool operator == (const GameSettings& rhs) const; bool operator == (const std::string& nameOrFolderName) const; - unsigned int Id() const; - std::string Name() const; //Returns the game's name, eg. "TES IV: Oblivion". - std::string FolderName() const; - std::string Master() const; - std::string RegistryKey() const; - std::string RepoURL() const; - std::string RepoBranch() const; - - boost::filesystem::path GamePath() const; - boost::filesystem::path DataPath() const; - boost::filesystem::path MasterlistPath() const; - boost::filesystem::path UserlistPath() const; - void GetLoadOrder(std::list& loadOrder) const; void SetLoadOrder(const std::list& loadOrder) const; //Modifies game load order, even though const. void SetLoadOrder(const char * const * const loadOrder, const size_t numPlugins) const; // For API. @@ -95,91 +81,16 @@ namespace loot { std::unordered_map plugins; //Map so that plugin data can be edited. espm::Settings espm_settings; - - static const unsigned int autodetect = 0; - static const unsigned int tes4 = 1; - static const unsigned int tes5 = 2; - static const unsigned int fo3 = 3; - static const unsigned int fonv = 4; private: - unsigned int id; - std::string _name; - std::string _masterFile; - - std::string registryKey; - - std::string lootFolderName; - std::string _repositoryURL; - std::string _repositoryBranch; - - boost::filesystem::path gamePath; //Path to the game's folder. boost::filesystem::path _gameLocalDataPath; // Path to the game's folder in %LOCALAPPDATA%. lo_game_handle gh; - //Creates directory in LOOT folder for LOOT's game-specific files. - void CreateLOOTGameFolder(); - void InitLibloHandle(); }; - std::list GetGames(YAML::Node& settings); -} - -namespace YAML { - template<> - struct convert < loot::Game > { - static Node encode(const loot::Game& rhs) { - Node node; - - node["type"] = loot::Game(rhs.Id()).FolderName(); - node["name"] = rhs.Name(); - node["folder"] = rhs.FolderName(); - node["master"] = rhs.Master(); - node["repo"] = rhs.RepoURL(); - node["branch"] = rhs.RepoBranch(); - node["path"] = rhs.GamePath().string(); - node["registry"] = rhs.RegistryKey(); - - return node; - } - - static bool decode(const Node& node, loot::Game& rhs) { - if (!node.IsMap() || !node["folder"] || !node["type"]) - return false; - - if (node["type"].as() == loot::Game(loot::Game::tes4).FolderName()) - rhs = loot::Game(loot::Game::tes4, node["folder"].as()); - else if (node["type"].as() == loot::Game(loot::Game::tes5).FolderName()) - rhs = loot::Game(loot::Game::tes5, node["folder"].as()); - else if (node["type"].as() == loot::Game(loot::Game::fo3).FolderName()) - rhs = loot::Game(loot::Game::fo3, node["folder"].as()); - else if (node["type"].as() == loot::Game(loot::Game::fonv).FolderName()) - rhs = loot::Game(loot::Game::fonv, node["folder"].as()); - else - return false; - - std::string name, master, repo, branch, path, registry; - if (node["name"]) - name = node["name"].as(); - if (node["master"]) - master = node["master"].as(); - if (node["repo"]) - repo = node["repo"].as(); - if (node["branch"]) - branch = node["branch"].as(); - if (node["path"]) - path = node["path"].as(); - if (node["registry"]) - registry = node["registry"].as(); - - rhs.SetDetails(name, master, repo, branch, path, registry); - - return true; - } - }; - - Emitter& operator << (Emitter& out, const loot::Game& rhs); + std::list ToGames(const std::list& settings); + std::list ToGameSettings(const std::list& games); } #endif diff --git a/src/backend/game_settings.cpp b/src/backend/game_settings.cpp new file mode 100644 index 00000000..33db1327 --- /dev/null +++ b/src/backend/game_settings.cpp @@ -0,0 +1,250 @@ +/* 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 + . + */ + +#include "game_settings.h" +#include "globals.h" +#include "helpers.h" +#include "error.h" + +#include +#include +#include + +using namespace std; + +namespace fs = boost::filesystem; +namespace lc = boost::locale; + +namespace loot { + GameSettings::GameSettings() : _id(GameSettings::autodetect) {} + + GameSettings::GameSettings(const unsigned int gameCode, const std::string& folder) : _id(gameCode) { + if (Id() == GameSettings::tes4) { + _name = "TES IV: Oblivion"; + _registryKey = "Software\\Bethesda Softworks\\Oblivion\\Installed Path"; + _lootFolderName = "Oblivion"; + _masterFile = "Oblivion.esm"; + _repositoryURL = "https://github.com/loot/oblivion.git"; + _repositoryBranch = "master"; + } + else if (Id() == GameSettings::tes5) { + _name = "TES V: Skyrim"; + _registryKey = "Software\\Bethesda Softworks\\Skyrim\\Installed Path"; + _lootFolderName = "Skyrim"; + _masterFile = "Skyrim.esm"; + _repositoryURL = "https://github.com/loot/skyrim.git"; + _repositoryBranch = "master"; + } + else if (Id() == GameSettings::fo3) { + _name = "Fallout 3"; + _registryKey = "Software\\Bethesda Softworks\\Fallout3\\Installed Path"; + _lootFolderName = "Fallout3"; + _masterFile = "Fallout3.esm"; + _repositoryURL = "https://github.com/loot/fallout3.git"; + _repositoryBranch = "master"; + } + else if (Id() == GameSettings::fonv) { + _name = "Fallout: New Vegas"; + _registryKey = "Software\\Bethesda Softworks\\FalloutNV\\Installed Path"; + _lootFolderName = "FalloutNV"; + _masterFile = "FalloutNV.esm"; + _repositoryURL = "https://github.com/loot/falloutnv.git"; + _repositoryBranch = "master"; + } + + if (!folder.empty()) + _lootFolderName = folder; + } + + GameSettings& GameSettings::SetDetails(const std::string& name, const std::string& masterFile, + const std::string& repositoryURL, const std::string& repositoryBranch, const std::string& path, const std::string& registry) { + BOOST_LOG_TRIVIAL(info) << "Setting new details for game: " << _name; + + if (!name.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting name to: " << name; + _name = name; + } + + if (!masterFile.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting master file to: " << masterFile; + _masterFile = masterFile; + } + + if (!repositoryURL.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting repo URL to: " << repositoryURL; + _repositoryURL = repositoryURL; + } + + if (!repositoryBranch.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting repo branch to: " << repositoryBranch; + _repositoryBranch = repositoryBranch; + } + + if (!path.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting game path to: " << path; + _gamePath = path; + } + + if (!registry.empty()) { + BOOST_LOG_TRIVIAL(trace) << '\t' << "Setting registry key to: " << registry; + _registryKey = registry; + } + + return *this; + } + + bool GameSettings::IsInstalled() { + try { + BOOST_LOG_TRIVIAL(trace) << "Checking if game \"" << _name << "\" is installed."; + if (!_gamePath.empty() && fs::exists(_gamePath / "Data" / _masterFile)) + return true; + + if (fs::exists(fs::path("..") / "Data" / _masterFile)) { + _gamePath = ".."; + return true; + } + +#ifdef _WIN32 + string path; + string key_parent = fs::path(_registryKey).parent_path().string(); + 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" / _masterFile)) { + _gamePath = path; + return true; + } +#endif + } + catch (exception &e) { + BOOST_LOG_TRIVIAL(error) << "Error while checking if game \"" << _name << "\" is installed: " << e.what(); + } + + return false; + } + + bool GameSettings::operator == (const GameSettings& rhs) const { + return (boost::iequals(_name, rhs.Name()) || boost::iequals(_lootFolderName, rhs.FolderName())); + } + + bool GameSettings::operator == (const std::string& nameOrFolderName) const { + return (boost::iequals(_name, nameOrFolderName) || boost::iequals(_lootFolderName, nameOrFolderName)); + } + + unsigned int GameSettings::Id() const { + return _id; + } + + string GameSettings::Name() const { + return _name; + } + + string GameSettings::FolderName() const { + return _lootFolderName; + } + + std::string GameSettings::Master() const { + return _masterFile; + } + + std::string GameSettings::RegistryKey() const { + return _registryKey; + } + + std::string GameSettings::RepoURL() const { + return _repositoryURL; + } + + std::string GameSettings::RepoBranch() const { + return _repositoryBranch; + } + + fs::path GameSettings::GamePath() const { + return _gamePath; + } + + fs::path GameSettings::DataPath() const { + return _gamePath / "Data"; + } + + fs::path GameSettings::MasterlistPath() const { + return g_path_local / _lootFolderName / "masterlist.yaml"; + } + + fs::path GameSettings::UserlistPath() const { + return g_path_local / _lootFolderName / "userlist.yaml"; + } + + void GameSettings::CreateLOOTGameFolder() { + //Make sure that the LOOT game path exists. + try { + if (fs::exists(g_path_local) && !fs::exists(g_path_local / _lootFolderName)) + fs::create_directory(g_path_local / _lootFolderName); + } + catch (fs::filesystem_error& e) { + BOOST_LOG_TRIVIAL(error) << "Could not create LOOT folder for game. Details: " << e.what(); + throw error(error::path_write_fail, lc::translate("Could not create LOOT folder for game. Details:").str() + " " + e.what()); + } + } + + std::list GetGameSettings(YAML::Node& settings) { + list games; + + if (settings["games"]) + games = settings["games"].as< list >(); + + if (find(games.begin(), games.end(), GameSettings(GameSettings::tes4)) == games.end()) + games.push_back(GameSettings(GameSettings::tes4)); + + if (find(games.begin(), games.end(), GameSettings(GameSettings::tes5)) == games.end()) + games.push_back(GameSettings(GameSettings::tes5)); + + if (find(games.begin(), games.end(), GameSettings(GameSettings::fo3)) == games.end()) + games.push_back(GameSettings(GameSettings::fo3)); + + if (find(games.begin(), games.end(), GameSettings(GameSettings::fonv)) == games.end()) + games.push_back(GameSettings(GameSettings::fonv)); + + // If there were any missing defaults, make sure they're in settings now. + settings["games"] = games; + + return games; + } +} + +namespace YAML { + Emitter& operator << (Emitter& out, const loot::GameSettings& rhs) { + out << BeginMap + << Key << "type" << Value << YAML::SingleQuoted << loot::GameSettings(rhs.Id()).FolderName() + << Key << "folder" << Value << YAML::SingleQuoted << rhs.FolderName() + << Key << "name" << Value << YAML::SingleQuoted << rhs.Name() + << Key << "master" << Value << YAML::SingleQuoted << rhs.Master() + << Key << "repo" << Value << YAML::SingleQuoted << rhs.RepoURL() + << Key << "branch" << Value << YAML::SingleQuoted << rhs.RepoBranch() + << Key << "path" << Value << YAML::SingleQuoted << rhs.GamePath().string() + << Key << "registry" << Value << YAML::SingleQuoted << rhs.RegistryKey() + << EndMap; + + return out; + } +} \ No newline at end of file diff --git a/src/backend/game_settings.h b/src/backend/game_settings.h new file mode 100644 index 00000000..06a13481 --- /dev/null +++ b/src/backend/game_settings.h @@ -0,0 +1,145 @@ +/* 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 + . + */ + +#ifndef __LOOT_GAME_SETTINGS__ +#define __LOOT_GAME_SETTINGS__ + +#include +#include + +#include + +#include + +namespace loot { + class GameSettings { + public: + //Game functions. + GameSettings(); //Sets game to LOOT_Game::autodetect, with all other vars being empty. + GameSettings(const unsigned int baseGameCode, const std::string& lootFolder = ""); + + GameSettings& SetDetails(const std::string& name, const std::string& masterFile, + const std::string& repositoryURL, const std::string& repositoryBranch, + const std::string& path, const std::string& registry); + + bool IsInstalled(); //Sets gamePath if the current value is not valid and a valid path is found. + + bool operator == (const GameSettings& rhs) const; //Compares names and folder names. + bool operator == (const std::string& nameOrFolderName) const; + + unsigned int Id() const; + std::string Name() const; //Returns the game's name, eg. "TES IV: Oblivion". + std::string FolderName() const; + std::string Master() const; + std::string RegistryKey() const; + std::string RepoURL() const; + std::string RepoBranch() const; + + boost::filesystem::path GamePath() const; + boost::filesystem::path DataPath() const; + boost::filesystem::path MasterlistPath() const; + boost::filesystem::path UserlistPath() const; + + static const unsigned int autodetect = 0; + static const unsigned int tes4 = 1; + static const unsigned int tes5 = 2; + static const unsigned int fo3 = 3; + static const unsigned int fonv = 4; + protected: + //Creates directory in LOOT folder for LOOT's game-specific files. + void CreateLOOTGameFolder(); + private: + unsigned int _id; + std::string _name; + std::string _masterFile; + + std::string _registryKey; + + std::string _lootFolderName; + std::string _repositoryURL; + std::string _repositoryBranch; + + boost::filesystem::path _gamePath; //Path to the game's folder. + }; + + std::list GetGameSettings(YAML::Node& settings); +} + +namespace YAML { + template<> + struct convert < loot::GameSettings > { + static Node encode(const loot::GameSettings& rhs) { + Node node; + + node["type"] = loot::GameSettings(rhs.Id()).FolderName(); + node["name"] = rhs.Name(); + node["folder"] = rhs.FolderName(); + node["master"] = rhs.Master(); + node["repo"] = rhs.RepoURL(); + node["branch"] = rhs.RepoBranch(); + node["path"] = rhs.GamePath().string(); + node["registry"] = rhs.RegistryKey(); + + return node; + } + + static bool decode(const Node& node, loot::GameSettings& rhs) { + if (!node.IsMap() || !node["folder"] || !node["type"]) + return false; + + if (node["type"].as() == loot::GameSettings(loot::GameSettings::tes4).FolderName()) + rhs = loot::GameSettings(loot::GameSettings::tes4, node["folder"].as()); + else if (node["type"].as() == loot::GameSettings(loot::GameSettings::tes5).FolderName()) + rhs = loot::GameSettings(loot::GameSettings::tes5, node["folder"].as()); + else if (node["type"].as() == loot::GameSettings(loot::GameSettings::fo3).FolderName()) + rhs = loot::GameSettings(loot::GameSettings::fo3, node["folder"].as()); + else if (node["type"].as() == loot::GameSettings(loot::GameSettings::fonv).FolderName()) + rhs = loot::GameSettings(loot::GameSettings::fonv, node["folder"].as()); + else + return false; + + std::string name, master, repo, branch, path, registry; + if (node["name"]) + name = node["name"].as(); + if (node["master"]) + master = node["master"].as(); + if (node["repo"]) + repo = node["repo"].as(); + if (node["branch"]) + branch = node["branch"].as(); + if (node["path"]) + path = node["path"].as(); + if (node["registry"]) + registry = node["registry"].as(); + + rhs.SetDetails(name, master, repo, branch, path, registry); + + return true; + } + }; + + Emitter& operator << (Emitter& out, const loot::GameSettings& rhs); +} + +#endif diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index 3c913c78..afb66a40 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -259,7 +259,7 @@ namespace loot { // It will be restored when LOOT is next loaded. try { BOOST_LOG_TRIVIAL(trace) << "Updating games object."; - list games(request["args"][0]["games"].as< list >()); + list games(request["args"][0]["games"].as< list >()); _lootState.UpdateGames(games); // Also enable/disable debug logging as required. diff --git a/src/gui/loot_state.cpp b/src/gui/loot_state.cpp index 3c9c3e7d..471fe9bb 100644 --- a/src/gui/loot_state.cpp +++ b/src/gui/loot_state.cpp @@ -137,14 +137,14 @@ namespace loot { //Detect installed games. BOOST_LOG_TRIVIAL(debug) << "Detecting installed games."; try { - _games = GetGames(_settings); + _games = ToGames(GetGameSettings(_settings)); } catch (YAML::Exception& e) { BOOST_LOG_TRIVIAL(error) << "Games' settings parsing failed. " << e.what(); _initErrors.push_back((format(translate("Error: Games' settings parsing failed. %1%")) % e.what()).str()); // Now redo, but with no games settings, so only the hardcoded defaults get loaded. It means the user can // at least still then edit them. - _games = GetGames(YAML::Node()); + _games = ToGames(GetGameSettings(YAML::Node())); } try { @@ -153,7 +153,7 @@ namespace loot { BOOST_LOG_TRIVIAL(debug) << "Initialising game-specific settings."; _currentGame->Init(true); // Update game path in settings object. - _settings["games"] = _games; + _settings["games"] = ToGameSettings(_games); } catch (loot::error &e) { if (e.code() == loot::error::no_game_detected) { @@ -171,7 +171,7 @@ namespace loot { return _initErrors; } - void LootState::UpdateGames(std::list& games) { + void LootState::UpdateGames(std::list& games) { // Acquire the lock for the scope of this method. base::AutoLock lock_scope(_lock); @@ -208,7 +208,7 @@ namespace loot { // Re-initialise the current game in case the game path setting was changed. _currentGame->Init(true); // Update game path in settings object. - _settings["games"] = _games; + _settings["games"] = ToGameSettings(_games); } void LootState::ChangeGame(const std::string& newGameFolder) { @@ -220,7 +220,7 @@ namespace loot { _currentGame->Init(true); // Update game path in settings object. - _settings["games"] = _games; + _settings["games"] = ToGameSettings(_games); BOOST_LOG_TRIVIAL(debug) << "New game is " << _currentGame->Name(); } @@ -231,9 +231,9 @@ namespace loot { return *_currentGame; } - std::vector LootState::InstalledGames() const { + std::vector LootState::InstalledGames() { vector installedGames; - for (const auto &game : _games) { + for (auto &game : _games) { if (game.IsInstalled()) installedGames.push_back(game.FolderName()); } @@ -393,14 +393,9 @@ namespace loot { root["enableDebugLogging"] = false; root["updateMasterlist"] = true; - std::list games; - games.push_back(Game(Game::tes4)); - games.push_back(Game(Game::tes5)); - games.push_back(Game(Game::fo3)); - games.push_back(Game(Game::fonv)); - games.push_back(Game(Game::tes4, "Nehrim").SetDetails("Nehrim - At Fate's Edge", "Nehrim.esm", "https://github.com/loot/oblivion.git", "master", "", "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation")); - - root["games"] = games; + // Add base game definitions, and Nehrim. + GetGameSettings(root); + root["games"].push_back(GameSettings(GameSettings::tes4, "Nehrim").SetDetails("Nehrim - At Fate's Edge", "Nehrim.esm", "https://github.com/loot/oblivion.git", "master", "", "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation")); return root; } diff --git a/src/gui/loot_state.h b/src/gui/loot_state.h index 75701f73..e306cdc4 100644 --- a/src/gui/loot_state.h +++ b/src/gui/loot_state.h @@ -42,9 +42,9 @@ namespace loot { Game& CurrentGame(); void ChangeGame(const std::string& newGameFolder); - void UpdateGames(std::list& games); + void UpdateGames(std::list& games); // Get the folder names of the installed games. - std::vector InstalledGames() const; + std::vector InstalledGames(); const YAML::Node& GetSettings() const; void UpdateSettings(const YAML::Node& settings);