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);