From 19ad6b15ef013e3aa53967939e3ca26caf2b5905 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 10 Jan 2016 15:21:51 +0000 Subject: [PATCH 1/5] Reimplement C++ LOOT settings handling It deserves its own class. Improvements over the previous implementation include: * Unit tests * If a setting is missing in loaded data, its default value is used * Invalid game settings are skipped * Settings are stored in named member variables instead of as untyped map elements, so it's clear what settings exist and what their types are. Thread safety is ensured using a reader/writer mutex lock. A readers/writer lock would be better, but isn't available in C++11 (it's in C++17). A lock may not be necessary at all, as most settings changes are called from JavaScript callbacks, which are single-threaded, but I'm not sure how V8 actually runs them, so better to be safe than sorry. --- CMakeLists.txt | 11 +- src/backend/game/game_settings.cpp | 27 -- src/backend/game/game_settings.h | 2 - src/gui/loot_settings.cpp | 295 ++++++++++++ src/gui/loot_settings.h | 90 ++++ src/tests/backend/game/test_game_settings.h | 39 -- src/tests/gui/test_loot_settings.h | 479 ++++++++++++++++++++ src/tests/main.cpp | 1 + 8 files changed, 873 insertions(+), 71 deletions(-) create mode 100644 src/gui/loot_settings.cpp create mode 100644 src/gui/loot_settings.h create mode 100644 src/tests/gui/test_loot_settings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b9bec963..10fb2d9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,7 @@ set (LOOT_GUI_SRC ${LOOT_SRC} "${CMAKE_SOURCE_DIR}/src/gui/handler.cpp" "${CMAKE_SOURCE_DIR}/src/gui/loot_handler.cpp" "${CMAKE_SOURCE_DIR}/src/gui/loot_app.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/loot_settings.cpp" "${CMAKE_SOURCE_DIR}/src/gui/loot_state.cpp" "${CMAKE_SOURCE_DIR}/src/gui/scheme.cpp" "${CMAKE_SOURCE_DIR}/src/resource.rc") @@ -143,6 +144,7 @@ set (LOOT_GUI_HEADERS ${LOOT_HEADERS} "${CMAKE_SOURCE_DIR}/src/gui/handler.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_handler.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_app.h" + "${CMAKE_SOURCE_DIR}/src/gui/loot_settings.h" "${CMAKE_SOURCE_DIR}/src/gui/loot_state.h" "${CMAKE_SOURCE_DIR}/src/gui/scheme.h" "${CMAKE_SOURCE_DIR}/src/gui/resource.h") @@ -160,7 +162,9 @@ set (LOOT_VALIDATOR_SRC ${LOOT_SRC} set (LOOT_VALIDATOR_HEADERS ${LOOT_HEADERS}) -set (LOOT_TESTS_SRC "${CMAKE_SOURCE_DIR}/src/api/loot_db.cpp" +set (LOOT_TESTS_SRC ${LOOT_SRC} + "${CMAKE_SOURCE_DIR}/src/api/loot_db.cpp" + "${CMAKE_SOURCE_DIR}/src/gui/loot_settings.cpp" "${CMAKE_SOURCE_DIR}/src/tests/main.cpp") set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h" @@ -188,7 +192,8 @@ set (LOOT_TESTS_HEADERS "${CMAKE_SOURCE_DIR}/src/tests/fixtures.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/plugin/test_plugin.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/test_metadata_list.h" "${CMAKE_SOURCE_DIR}/src/tests/backend/test_masterlist.h" - "${CMAKE_SOURCE_DIR}/src/tests/backend/test_plugin_sorter.h") + "${CMAKE_SOURCE_DIR}/src/tests/backend/test_plugin_sorter.h" + "${CMAKE_SOURCE_DIR}/src/tests/gui/test_loot_settings.h") source_group("Header Files" FILES ${LOOT_HEADERS} ${LOOT_GUI_HEADERS} ${LOOT_API_HEADERS} ${LOOT_TESTS_HEADERS}) @@ -309,7 +314,7 @@ target_link_libraries (loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${YAML_CPP_LIBRARI IF (${GTEST_FOUND}) # Build tests. - add_executable(tests ${LOOT_TESTS_SRC} ${LOOT_SRC} ${LOOT_TESTS_HEADERS}) + add_executable(tests ${LOOT_TESTS_SRC} ${LOOT_TESTS_HEADERS}) target_link_libraries(tests loot${PROJECT_ARCH} ${Boost_LIBRARIES} ${YAML_CPP_LIBRARIES} ${LOOT_LIBS} ${GTEST_BOTH_LIBRARIES}) ENDIF () diff --git a/src/backend/game/game_settings.cpp b/src/backend/game/game_settings.cpp index 3682e9c8..6cc25d27 100644 --- a/src/backend/game/game_settings.cpp +++ b/src/backend/game/game_settings.cpp @@ -226,33 +226,6 @@ namespace loot { _gamePath = path; return *this; } - - 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 (find(games.begin(), games.end(), GameSettings(GameSettings::fo4)) == games.end()) - games.push_back(GameSettings(GameSettings::fo4)); - - // If there were any missing defaults, make sure they're in settings now. - settings["games"] = games; - - return games; - } } namespace YAML { diff --git a/src/backend/game/game_settings.h b/src/backend/game/game_settings.h index a85e8d80..f5b94879 100644 --- a/src/backend/game/game_settings.h +++ b/src/backend/game/game_settings.h @@ -85,8 +85,6 @@ namespace loot { boost::filesystem::path _gamePath; //Path to the game's folder. }; - - std::list GetGameSettings(YAML::Node& settings); } namespace YAML { diff --git a/src/gui/loot_settings.cpp b/src/gui/loot_settings.cpp new file mode 100644 index 00000000..b4a33016 --- /dev/null +++ b/src/gui/loot_settings.cpp @@ -0,0 +1,295 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2014-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 "loot_settings.h" +#include "backend/globals.h" + +#include + +#include + +using namespace std; + +namespace loot { + LootSettings::WindowPosition::WindowPosition() : top(0), bottom(0), left(0), right(0) {} + + LootSettings::LootSettings() : + gameSettings({ + GameSettings(GameSettings::tes4), + GameSettings(GameSettings::tes5), + GameSettings(GameSettings::fo3), + GameSettings(GameSettings::fonv), + GameSettings(GameSettings::fo4), + GameSettings(GameSettings::tes4, "Nehrim") + .SetName("Nehrim - At Fate's Edge") + .SetMaster("Nehrim.esm") + .SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation"), + }), + enableDebugLogging(false), + updateMasterlist(true), + game("auto"), + language(Language(Language::english)), + lastGame("auto"), + lastVersion(std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch)) {} + + void LootSettings::load(YAML::Node& settings) { + std::lock_guard guard(mutex); + + upgradeYaml(settings); + + if (settings["enableDebugLogging"]) + enableDebugLogging = settings["enableDebugLogging"].as(); + if (settings["updateMasterlist"]) + updateMasterlist = settings["updateMasterlist"].as(); + if (settings["game"]) + game = settings["game"].as(); + if (settings["language"]) + language = Language(settings["language"].as()); + if (settings["lastGame"]) + lastGame = settings["lastGame"].as(); + if (settings["lastVersion"]) + lastVersion = settings["lastVersion"].as(); + + if (settings["window"] + && settings["window"]["top"] && settings["window"]["bottom"] + && settings["window"]["left"] && settings["window"]["right"]) { + windowPosition.top = settings["window"]["top"].as(); + windowPosition.bottom = settings["window"]["bottom"].as(); + windowPosition.left = settings["window"]["left"].as(); + windowPosition.right = settings["window"]["right"].as(); + } + + if (settings["games"]) { + gameSettings = settings["games"].as>(); + + // If a base game isn't in the settings, add it. + if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::tes4)) == end(gameSettings)) + gameSettings.push_back(GameSettings(GameSettings::tes4)); + + if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::tes5)) == end(gameSettings)) + gameSettings.push_back(GameSettings(GameSettings::tes5)); + + if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fo3)) == end(gameSettings)) + gameSettings.push_back(GameSettings(GameSettings::fo3)); + + if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fonv)) == end(gameSettings)) + gameSettings.push_back(GameSettings(GameSettings::fonv)); + + if (find(begin(gameSettings), end(gameSettings), GameSettings(GameSettings::fo4)) == end(gameSettings)) + gameSettings.push_back(GameSettings(GameSettings::fo4)); + } + + if (settings["filters"]) + filters = settings["filters"].as>(); + } + + void LootSettings::load(const boost::filesystem::path& file) { + boost::filesystem::ifstream in(file); + YAML::Node content = YAML::Load(in); + load(content); + } + + void LootSettings::save(const boost::filesystem::path& file) { + std::lock_guard guard(mutex); + + YAML::Emitter yout; + yout.SetIndent(2); + yout << toYaml(); + + boost::filesystem::ofstream out(file); + out << yout.c_str(); + } + + bool LootSettings::isDebugLoggingEnabled() const { + std::lock_guard guard(mutex); + + return enableDebugLogging; + } + + bool LootSettings::isWindowPositionStored() const { + std::lock_guard guard(mutex); + + return windowPosition.top != 0 || windowPosition.bottom != 0 || windowPosition.left != 0 || windowPosition.right != 0; + } + + std::string LootSettings::getGame() const { + std::lock_guard guard(mutex); + + return game; + } + + std::string LootSettings::getLastGame() const { + std::lock_guard guard(mutex); + + return lastGame; + } + + std::string LootSettings::getLastVersion() const { + std::lock_guard guard(mutex); + + return lastVersion; + } + + const Language& LootSettings::getLanguage() const { + std::lock_guard guard(mutex); + + return language; + } + + const LootSettings::WindowPosition& LootSettings::getWindowPosition() const { + std::lock_guard guard(mutex); + + return windowPosition; + } + + std::vector LootSettings::getGameSettings() const { + std::lock_guard guard(mutex); + + return gameSettings; + } + + void LootSettings::storeLastGame(const std::string& lastGame) { + std::lock_guard guard(mutex); + + this->lastGame = lastGame; + } + + void LootSettings::storeWindowPosition(const WindowPosition& position) { + std::lock_guard guard(mutex); + + windowPosition = position; + } + + void LootSettings::storeGameSettings(const std::vector& gameSettings) { + std::lock_guard guard(mutex); + + this->gameSettings = gameSettings; + } + + void LootSettings::storeFilterState(const std::string& filterId, bool enabled) { + std::lock_guard guard(mutex); + + filters[filterId] = enabled; + } + + void LootSettings::updateLastVersion() { + std::lock_guard guard(mutex); + + lastVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch); + } + + YAML::Node LootSettings::toYaml() const { + std::lock_guard guard(mutex); + + YAML::Node node; + + node["enableDebugLogging"] = enableDebugLogging; + node["updateMasterlist"] = updateMasterlist; + node["game"] = game; + node["language"] = language.Locale(); + node["lastGame"] = lastGame; + node["lastVersion"] = lastVersion; + + if (isWindowPositionStored()) { + node["window"]["top"] = windowPosition.top; + node["window"]["bottom"] = windowPosition.bottom; + node["window"]["left"] = windowPosition.left; + node["window"]["right"] = windowPosition.right; + } + + node["games"] = gameSettings; + + if (!filters.empty()) + node["filters"] = filters; + + return node; + } + + void LootSettings::upgradeYaml(YAML::Node& yaml) { + // Upgrade YAML settings' keys and values from those used in earlier + // versions of LOOT. + + if (yaml["Debug Verbosity"] && !yaml["enableDebugLogging"]) + yaml["enableDebugLogging"] = yaml["Debug Verbosity"].as() > 0; + + if (yaml["Update Masterlist"] && !yaml["updateMasterlist"]) + yaml["updateMasterlist"] = yaml["Update Masterlist"]; + + if (yaml["Game"] && !yaml["game"]) + yaml["game"] = yaml["Game"]; + + if (yaml["Language"] && !yaml["language"]) + yaml["language"] = yaml["Language"]; + + if (yaml["Last Game"] && !yaml["lastGame"]) + yaml["lastGame"] = yaml["Last Game"]; + + if (yaml["Games"] && !yaml["games"]) { + yaml["games"] = yaml["Games"]; + + for (auto node : yaml["games"]) { + if (node["url"]) { + node["repo"] = node["url"]; + node["branch"] = "v0.8"; + } + } + } + + if (yaml["games"]) { + const set oldDefaultBranches({ + "master", + "v0.7", + }); + + // Handle exception if YAML is invalid, eg. if an unrecognised + // game type is used (which can happen if downgrading from a + // later version of LOOT that supports more game types). + // However, can't remove elements from a sequence Node, so have to + // copy the valid elements into a new node then overwrite the + // original. + YAML::Node validGames; + for (auto node : yaml["games"]) { + try { + GameSettings settings(node.as()); + + if (!yaml["Games"]) { + // Update existing default branch, if the default + // repositories are used. + if (settings.RepoURL() == GameSettings(settings.Id()).RepoURL() + && oldDefaultBranches.count(settings.RepoBranch()) == 1) { + settings.SetRepoBranch("v0.8"); + } + } + + validGames.push_back(settings); + } + catch (...) {} + } + yaml["games"] = validGames; + } + + if (yaml["filters"]) + yaml["filters"].remove("contentFilter"); + } +} diff --git a/src/gui/loot_settings.h b/src/gui/loot_settings.h new file mode 100644 index 00000000..ab7a3c8d --- /dev/null +++ b/src/gui/loot_settings.h @@ -0,0 +1,90 @@ +/* LOOT + + A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and + Fallout: New Vegas. + + Copyright (C) 2014-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_GUI_LOOT_SETTINGS +#define LOOT_GUI_LOOT_SETTINGS + +#include "backend/game/game_settings.h" +#include "backend/helpers/language.h" + +#include +#include +#include +#include + +#include +#include + +namespace loot { + class LootSettings { + public: + struct WindowPosition { + WindowPosition(); + + long top; + long bottom; + long left; + long right; + }; + + LootSettings(); + + void load(YAML::Node& settings); + void load(const boost::filesystem::path& file); + void save(const boost::filesystem::path& file); + + bool isDebugLoggingEnabled() const; + bool isWindowPositionStored() const; + std::string getGame() const; + std::string getLastGame() const; + std::string getLastVersion() const; + const Language& getLanguage() const; + const WindowPosition& getWindowPosition() const; + std::vector getGameSettings() const; + + void storeLastGame(const std::string& lastGame); + void storeWindowPosition(const WindowPosition& position); + void storeGameSettings(const std::vector& gameSettings); + void storeFilterState(const std::string& filterId, bool enabled); + void updateLastVersion(); + + YAML::Node toYaml() const; + private: + bool enableDebugLogging; + bool updateMasterlist; + std::string game; + std::string lastGame; + std::string lastVersion; + Language language; + WindowPosition windowPosition; + std::vector gameSettings; + std::map filters; + + mutable std::recursive_mutex mutex; + + static void upgradeYaml(YAML::Node& yaml); + }; +} + +#endif diff --git a/src/tests/backend/game/test_game_settings.h b/src/tests/backend/game/test_game_settings.h index 651cb785..bbc27f30 100644 --- a/src/tests/backend/game/test_game_settings.h +++ b/src/tests/backend/game/test_game_settings.h @@ -251,43 +251,4 @@ TEST_F(GameSettings, YamlDecode) { EXPECT_EQ("branch1", game.RepoBranch()); EXPECT_EQ("", game.GamePath()); } - -TEST(GetGameSettings, AllMissing) { - YAML::Node settings; - - std::list expected = { - loot::GameSettings(loot::GameSettings::tes4), - loot::GameSettings(loot::GameSettings::tes5), - loot::GameSettings(loot::GameSettings::fo3), - loot::GameSettings(loot::GameSettings::fonv), - loot::GameSettings(loot::GameSettings::fo4) - }; - - EXPECT_FALSE(settings["games"]); - EXPECT_EQ(expected, loot::GetGameSettings(settings)); - EXPECT_EQ(expected, settings["games"].as>()); -} - -TEST(GetGameSettings, MissingFallout3) { - std::list initial({ - loot::GameSettings(loot::GameSettings::tes4, "folder1"), - loot::GameSettings(loot::GameSettings::tes5, "folder2"), - loot::GameSettings(loot::GameSettings::fonv, "folder3"), - loot::GameSettings(loot::GameSettings::fo4, "folder4") - }); - - YAML::Node settings; - settings["games"] = initial; - - std::list expected = { - loot::GameSettings(loot::GameSettings::tes4, "folder1"), - loot::GameSettings(loot::GameSettings::tes5, "folder2"), - loot::GameSettings(loot::GameSettings::fonv, "folder3"), - loot::GameSettings(loot::GameSettings::fo4, "folder4"), - loot::GameSettings(loot::GameSettings::fo3) - }; - - EXPECT_EQ(expected, loot::GetGameSettings(settings)); - EXPECT_EQ(expected, settings["games"].as>()); -} #endif diff --git a/src/tests/gui/test_loot_settings.h b/src/tests/gui/test_loot_settings.h new file mode 100644 index 00000000..f96722ba --- /dev/null +++ b/src/tests/gui/test_loot_settings.h @@ -0,0 +1,479 @@ +/* LOOT + +A load order optimisation tool for Oblivion, Skyrim, Fallout 3 and +Fallout: New Vegas. + +Copyright (C) 2014-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_TEST_GUI_LOOT_SETTINGS +#define LOOT_TEST_GUI_LOOT_SETTINGS + +#include "gui/loot_settings.h" +#include "backend/globals.h" + +#include "tests/fixtures.h" + +namespace loot { + namespace test { + class LootSettings : public ::testing::Test { + protected: + LootSettings() : settingsFile("./settings.yaml") {} + + ~LootSettings() { + boost::filesystem::remove(settingsFile); + } + + boost::filesystem::path settingsFile; + loot::LootSettings settings; + }; + + TEST_F(LootSettings, defaultConstructorShouldSetDefaultValues) { + const std::string currentVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch); + const std::vector expectedGameSettings({ + GameSettings(GameSettings::tes4), + GameSettings(GameSettings::tes5), + GameSettings(GameSettings::fo3), + GameSettings(GameSettings::fonv), + GameSettings(GameSettings::fo4), + GameSettings(GameSettings::tes4, "Nehrim") + .SetName("Nehrim - At Fate's Edge") + .SetMaster("Nehrim.esm") + .SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation"), + }); + + EXPECT_FALSE(settings.isDebugLoggingEnabled()); + EXPECT_EQ("auto", settings.getGame()); + EXPECT_EQ("en", settings.getLanguage().Locale()); + EXPECT_EQ("auto", settings.getLastGame()); + EXPECT_FALSE(settings.isWindowPositionStored()); + + const YAML::Node node = settings.toYaml(); + EXPECT_TRUE(node["updateMasterlist"].as()); + EXPECT_EQ(currentVersion, node["lastVersion"].as()); + EXPECT_FALSE(node["filters"]); + + // GameSettings equality only checks name and folder, so check + // other settings individually. + const std::vector actualGameSettings = settings.getGameSettings(); + EXPECT_EQ(expectedGameSettings, actualGameSettings); + + EXPECT_EQ(expectedGameSettings[0].Id(), actualGameSettings[0].Id()); + EXPECT_EQ(expectedGameSettings[0].Master(), actualGameSettings[0].Master()); + EXPECT_EQ(expectedGameSettings[0].RegistryKey(), actualGameSettings[0].RegistryKey()); + EXPECT_EQ(expectedGameSettings[0].RepoURL(), actualGameSettings[0].RepoURL()); + EXPECT_EQ(expectedGameSettings[0].RepoBranch(), actualGameSettings[0].RepoBranch()); + + EXPECT_EQ(expectedGameSettings[1].Id(), actualGameSettings[1].Id()); + EXPECT_EQ(expectedGameSettings[1].Master(), actualGameSettings[1].Master()); + EXPECT_EQ(expectedGameSettings[1].RegistryKey(), actualGameSettings[1].RegistryKey()); + EXPECT_EQ(expectedGameSettings[1].RepoURL(), actualGameSettings[1].RepoURL()); + EXPECT_EQ(expectedGameSettings[1].RepoBranch(), actualGameSettings[1].RepoBranch()); + + EXPECT_EQ(expectedGameSettings[2].Id(), actualGameSettings[2].Id()); + EXPECT_EQ(expectedGameSettings[2].Master(), actualGameSettings[2].Master()); + EXPECT_EQ(expectedGameSettings[2].RegistryKey(), actualGameSettings[2].RegistryKey()); + EXPECT_EQ(expectedGameSettings[2].RepoURL(), actualGameSettings[2].RepoURL()); + EXPECT_EQ(expectedGameSettings[2].RepoBranch(), actualGameSettings[2].RepoBranch()); + + EXPECT_EQ(expectedGameSettings[3].Id(), actualGameSettings[3].Id()); + EXPECT_EQ(expectedGameSettings[3].Master(), actualGameSettings[3].Master()); + EXPECT_EQ(expectedGameSettings[3].RegistryKey(), actualGameSettings[3].RegistryKey()); + EXPECT_EQ(expectedGameSettings[3].RepoURL(), actualGameSettings[3].RepoURL()); + EXPECT_EQ(expectedGameSettings[3].RepoBranch(), actualGameSettings[3].RepoBranch()); + + EXPECT_EQ(expectedGameSettings[4].Id(), actualGameSettings[4].Id()); + EXPECT_EQ(expectedGameSettings[4].Master(), actualGameSettings[4].Master()); + EXPECT_EQ(expectedGameSettings[4].RegistryKey(), actualGameSettings[4].RegistryKey()); + EXPECT_EQ(expectedGameSettings[4].RepoURL(), actualGameSettings[4].RepoURL()); + EXPECT_EQ(expectedGameSettings[4].RepoBranch(), actualGameSettings[4].RepoBranch()); + + EXPECT_EQ(expectedGameSettings[5].Id(), actualGameSettings[5].Id()); + EXPECT_EQ(expectedGameSettings[5].Master(), actualGameSettings[5].Master()); + EXPECT_EQ(expectedGameSettings[5].RegistryKey(), actualGameSettings[5].RegistryKey()); + EXPECT_EQ(expectedGameSettings[5].RepoURL(), actualGameSettings[5].RepoURL()); + EXPECT_EQ(expectedGameSettings[5].RepoBranch(), actualGameSettings[5].RepoBranch()); + } + + TEST_F(LootSettings, loadingFromFileShouldLoadContentAsYaml) { + boost::filesystem::ofstream out(settingsFile); + out << "enableDebugLogging: true" << std::endl; + out.close(); + + settings.load(settingsFile); + + EXPECT_TRUE(settings.isDebugLoggingEnabled()); + } + + TEST_F(LootSettings, loadingFromYamlShouldStoreLoadedValues) { + const bool enableDebugLogging = true; + const bool updateMasterlist = true; + const std::string game = "Oblivion"; + const std::string language = "fr"; + const std::string lastGame = "Skyrim"; + const std::string lastVersion = "0.7.1"; + const std::map window({ + {"top", 1}, + {"bottom", 2}, + {"left", 3}, + {"right", 4}, + }); + const std::vector games({ + GameSettings(GameSettings::tes4).SetName("Game Name"), + }); + const std::map filters({ + {"hideBashTags", false}, + {"hideCRCs", true}, + }); + + YAML::Node inputYaml; + inputYaml["enableDebugLogging"] = enableDebugLogging; + inputYaml["updateMasterlist"] = updateMasterlist; + inputYaml["game"] = game; + inputYaml["language"] = language; + inputYaml["lastGame"] = lastGame; + inputYaml["lastVersion"] = lastVersion; + inputYaml["window"] = window; + inputYaml["games"] = games; + inputYaml["filters"] = filters; + + settings.load(inputYaml); + + EXPECT_EQ(enableDebugLogging, settings.isDebugLoggingEnabled()); + EXPECT_EQ(game, settings.getGame()); + EXPECT_EQ(language, settings.getLanguage().Locale()); + EXPECT_EQ(lastGame, settings.getLastGame()); + + EXPECT_EQ(1, settings.getWindowPosition().top); + EXPECT_EQ(2, settings.getWindowPosition().bottom); + EXPECT_EQ(3, settings.getWindowPosition().left); + EXPECT_EQ(4, settings.getWindowPosition().right); + + const YAML::Node outputYaml = settings.toYaml(); + EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as()); + EXPECT_EQ(lastVersion, outputYaml["lastVersion"].as()); + + for (const auto& filter : filters) { + EXPECT_EQ(filter.second, outputYaml["filters"][filter.first].as()); + } + + EXPECT_EQ(games[0].Name(), settings.getGameSettings()[0].Name()); + } + + TEST_F(LootSettings, loadingFromEmptyYamlShouldNotThrow) { + YAML::Node yaml; + EXPECT_NO_THROW(settings.load(yaml)); + } + + TEST_F(LootSettings, loadingFromYamlShouldUpgradeFromVersion0Point6Format) { + const unsigned int DebugVerbosity = 3; + const bool UpdateMasterlist = true; + const std::string Game = "Oblivion"; + const std::string Language = "fr"; + const std::string LastGame = "Skyrim"; + const std::vector Games({ + GameSettings(GameSettings::tes4).SetName("Game Name"), + }); + + YAML::Node inputYaml; + inputYaml["Debug Verbosity"] = DebugVerbosity; + inputYaml["Update Masterlist"] = UpdateMasterlist; + inputYaml["Game"] = Game; + inputYaml["Language"] = Language; + inputYaml["Last Game"] = LastGame; + + inputYaml["Games"] = Games; + inputYaml["Games"][0]["url"] = inputYaml["Games"][0]["repo"]; + inputYaml["Games"][0].remove("repo"); + inputYaml["Games"][0].remove("branch"); + + settings.load(inputYaml); + + const YAML::Node outputYaml = settings.toYaml(); + EXPECT_TRUE(settings.isDebugLoggingEnabled()); + EXPECT_EQ(UpdateMasterlist, outputYaml["updateMasterlist"].as()); + EXPECT_EQ(Game, settings.getGame()); + EXPECT_EQ(Language, settings.getLanguage().Locale()); + EXPECT_EQ(LastGame, settings.getLastGame()); + + EXPECT_EQ(Games[0].Name(), settings.getGameSettings()[0].Name()); + EXPECT_EQ(Games[0].RepoURL(), settings.getGameSettings()[0].RepoURL()); + EXPECT_EQ(Games[0].RepoBranch(), settings.getGameSettings()[0].RepoBranch()); + } + + TEST_F(LootSettings, loadingFromYamlShouldNotUpgradeVersion0Point6SettingsIfEquivalentsAlreadyExist) { + const unsigned int DebugVerbosity = 3; + const bool enableDebugLogging = false; + const bool UpdateMasterlist = true; + const bool updateMasterlist = false; + const std::string Game = "Oblivion"; + const std::string game = "auto"; + const std::string Language = "fr"; + const std::string language = "en"; + const std::string LastGame = "Skyrim"; + const std::string lastGame = "auto"; + const std::vector Games({ + GameSettings(GameSettings::tes4).SetName("Old Game Name"), + }); + const std::vector games({ + GameSettings(GameSettings::fo3).SetName("Game Name"), + }); + + YAML::Node inputYaml; + inputYaml["Debug Verbosity"] = DebugVerbosity; + inputYaml["enableDebugLogging"] = enableDebugLogging; + inputYaml["Update Masterlist"] = UpdateMasterlist; + inputYaml["updateMasterlist"] = updateMasterlist; + inputYaml["Game"] = Game; + inputYaml["game"] = game; + inputYaml["Language"] = Language; + inputYaml["language"] = language; + inputYaml["Last Game"] = LastGame; + inputYaml["lastGame"] = lastGame; + + inputYaml["Games"] = Games; + inputYaml["Games"][0]["url"] = inputYaml["Games"][0]["repo"]; + inputYaml["Games"][0].remove("repo"); + inputYaml["Games"][0].remove("branch"); + inputYaml["games"] = games; + + settings.load(inputYaml); + + const YAML::Node outputYaml = settings.toYaml(); + + EXPECT_EQ(enableDebugLogging, settings.isDebugLoggingEnabled()); + EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as()); + EXPECT_EQ(game, settings.getGame()); + EXPECT_EQ(language, settings.getLanguage().Locale()); + EXPECT_EQ(lastGame, settings.getLastGame()); + + EXPECT_EQ(games[0].Name(), settings.getGameSettings()[0].Name()); + } + + TEST_F(LootSettings, loadingFromYamlShouldUpgradeOldDefaultGameRepositoryBranches) { + const std::vector games({GameSettings(GameSettings::tes4)}); + + YAML::Node inputYaml; + inputYaml["games"] = games; + inputYaml["games"][0]["branch"] = "v0.7"; + + settings.load(inputYaml); + + EXPECT_EQ(games[0].RepoBranch(), settings.getGameSettings()[0].RepoBranch()); + } + + TEST_F(LootSettings, loadingFromYamlShouldNotUpgradeNonDefaultGameRepositoryBranches) { + const std::vector games({GameSettings(GameSettings::tes4)}); + + YAML::Node inputYaml; + inputYaml["games"] = games; + inputYaml["games"][0]["branch"] = "foo"; + + settings.load(inputYaml); + + EXPECT_EQ("foo", settings.getGameSettings()[0].RepoBranch()); + } + + TEST_F(LootSettings, loadingFromYamlShouldAddMissingBaseGames) { + const std::vector games({GameSettings(GameSettings::tes4)}); + YAML::Node inputYaml; + inputYaml["games"] = games; + + settings.load(inputYaml); + + const std::vector expectedGameSettings({ + GameSettings(GameSettings::tes4), + GameSettings(GameSettings::tes5), + GameSettings(GameSettings::fo3), + GameSettings(GameSettings::fonv), + GameSettings(GameSettings::fo4), + }); + EXPECT_EQ(expectedGameSettings, settings.getGameSettings()); + } + + TEST_F(LootSettings, loadingFromYamlShouldSkipUnrecognisedGames) { + YAML::Node inputYaml; + inputYaml["games"][0] = GameSettings(GameSettings::tes4); + inputYaml["games"][0]["type"] = "Foobar"; + inputYaml["games"][0]["name"] = "Foobar"; + inputYaml["games"][1] = GameSettings(GameSettings::tes5).SetName("Game Name"); + + settings.load(inputYaml); + + EXPECT_EQ("Game Name", settings.getGameSettings()[0].Name()); + } + + TEST_F(LootSettings, loadingFromYamlShouldRemoveTheContentFilterSetting) { + YAML::Node inputYaml; + inputYaml["filters"]["contentFilter"] = "foo"; + + settings.load(inputYaml); + } + + TEST_F(LootSettings, saveShouldWriteSettingsAsYamlToPassedFile) { + settings.storeLastGame("Skyrim"); + settings.save(settingsFile); + + settings.storeLastGame("auto"); + settings.load(settingsFile); + + EXPECT_EQ("Skyrim", settings.getLastGame()); + } + + TEST_F(LootSettings, getLanguageShouldReturnTheCurrentValue) { + YAML::Node inputYaml; + inputYaml["language"] = "fr"; + + settings.load(inputYaml); + + EXPECT_EQ("fr", settings.getLanguage().Locale()); + } + + TEST_F(LootSettings, isWindowPositionStoredShouldReturnFalseIfAllPositionValuesAreZero) { + loot::LootSettings::WindowPosition position; + settings.storeWindowPosition(position); + + EXPECT_FALSE(settings.isWindowPositionStored()); + } + + TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfTopPositionValueIsNonZero) { + loot::LootSettings::WindowPosition position; + position.top = 1; + settings.storeWindowPosition(position); + + EXPECT_TRUE(settings.isWindowPositionStored()); + } + + TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfBottomPositionValueIsNonZero) { + loot::LootSettings::WindowPosition position; + position.bottom = 1; + settings.storeWindowPosition(position); + + EXPECT_TRUE(settings.isWindowPositionStored()); + } + + TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfLeftPositionValueIsNonZero) { + loot::LootSettings::WindowPosition position; + position.left = 1; + settings.storeWindowPosition(position); + + EXPECT_TRUE(settings.isWindowPositionStored()); + } + + TEST_F(LootSettings, isWindowPositionStoredShouldReturnTrueIfRightPositionValueIsNonZero) { + loot::LootSettings::WindowPosition position; + position.right = 1; + settings.storeWindowPosition(position); + + EXPECT_TRUE(settings.isWindowPositionStored()); + } + + TEST_F(LootSettings, storeGameSettingsShouldReplaceExistingGameSettings) { + const std::vector gameSettings({GameSettings(GameSettings::tes5)}); + settings.storeGameSettings(gameSettings); + + EXPECT_EQ(gameSettings, settings.getGameSettings()); + } + + TEST_F(LootSettings, storeLastGameShouldReplaceExistingValue) { + settings.storeLastGame("Fallout3"); + + EXPECT_EQ("Fallout3", settings.getLastGame()); + } + + TEST_F(LootSettings, storeWindowPositionShouldReplaceExistingValue) { + loot::LootSettings::WindowPosition expectedPosition; + expectedPosition.top = 1; + settings.storeWindowPosition(expectedPosition); + + loot::LootSettings::WindowPosition actualPosition = settings.getWindowPosition(); + EXPECT_EQ(expectedPosition.top, actualPosition.top); + EXPECT_EQ(expectedPosition.bottom, actualPosition.bottom); + EXPECT_EQ(expectedPosition.left, actualPosition.left); + EXPECT_EQ(expectedPosition.right, actualPosition.right); + } + + TEST_F(LootSettings, updateLastVersionShouldSetValueToCurrentLootVersion) { + const std::string currentVersion = std::to_string(g_version_major) + "." + std::to_string(g_version_minor) + "." + std::to_string(g_version_patch); + YAML::Node inputYaml; + inputYaml["lastVersion"] = "v0.7.1"; + + settings.load(inputYaml); + settings.updateLastVersion(); + + EXPECT_EQ(currentVersion, settings.getLastVersion()); + } + + TEST_F(LootSettings, toYamlShouldOutputStoredSettings) { + const bool enableDebugLogging = true; + const bool updateMasterlist = true; + const std::string game = "Oblivion"; + const std::string language = "fr"; + const std::string lastGame = "Skyrim"; + const std::string lastVersion = "0.7.1"; + const std::map window({ + {"top", 1}, + {"bottom", 2}, + {"left", 3}, + {"right", 4}, + }); + const std::vector games({ + GameSettings(GameSettings::tes4).SetName("Game Name"), + }); + const std::map filters({ + {"hideBashTags", false}, + {"hideCRCs", true}, + }); + + YAML::Node inputYaml; + inputYaml["enableDebugLogging"] = enableDebugLogging; + inputYaml["updateMasterlist"] = updateMasterlist; + inputYaml["game"] = game; + inputYaml["language"] = language; + inputYaml["lastGame"] = lastGame; + inputYaml["lastVersion"] = lastVersion; + inputYaml["window"] = window; + inputYaml["games"] = games; + inputYaml["filters"] = filters; + + settings.load(inputYaml); + + const YAML::Node outputYaml = settings.toYaml(); + + EXPECT_EQ(enableDebugLogging, outputYaml["enableDebugLogging"].as()); + EXPECT_EQ(updateMasterlist, outputYaml["updateMasterlist"].as()); + EXPECT_EQ(game, outputYaml["game"].as()); + EXPECT_EQ(language, outputYaml["language"].as()); + EXPECT_EQ(lastGame, outputYaml["lastGame"].as()); + EXPECT_EQ(lastVersion, outputYaml["lastVersion"].as()); + + for (const auto& position : window) { + EXPECT_EQ(position.second, outputYaml["window"][position.first].as()); + } + + for (const auto& filter : filters) { + EXPECT_EQ(filter.second, outputYaml["filters"][filter.first].as()); + } + + EXPECT_EQ(games[0].Name(), outputYaml["games"][0]["name"].as()); + } + } +} + +#endif diff --git a/src/tests/main.cpp b/src/tests/main.cpp index ea814bea..78023cc1 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -51,6 +51,7 @@ #include "backend/test_metadata_list.h" #include "backend/test_masterlist.h" #include "backend/test_plugin_sorter.h" +#include "gui/test_loot_settings.h" #include From 1754939aab89e5842216eb2b63a357306f62daa9 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 10 Jan 2016 18:29:18 +0000 Subject: [PATCH 2/5] Fix indentation --- src/gui/loot_state.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/loot_state.cpp b/src/gui/loot_state.cpp index f70bb2a0..ec93e396 100644 --- a/src/gui/loot_state.cpp +++ b/src/gui/loot_state.cpp @@ -89,11 +89,11 @@ namespace loot { boost::log::keywords::file_name = g_path_log.string().c_str(), boost::log::keywords::auto_flush = true, boost::log::keywords::format = ( - boost::log::expressions::stream - << "[" << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%H:%M:%S") << "]" - << " [" << boost::log::trivial::severity << "]: " - << boost::log::expressions::smessage - ) + boost::log::expressions::stream + << "[" << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "%H:%M:%S") << "]" + << " [" << boost::log::trivial::severity << "]: " + << boost::log::expressions::smessage + ) ); boost::log::add_common_attributes(); bool enableDebugLogging = false; From 6307a3c1944d6b66a5cf3e666e985629c1e58553 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 10 Jan 2016 18:30:38 +0000 Subject: [PATCH 3/5] Implement the new GameSettings class * Make LootState inherit from LootSettings * Refactor ToGames()/ToGameSettings() as static members of LootState As the conversion functions are no longer accessible, their unit tests have been removed. --- src/backend/game/game.cpp | 8 - src/backend/game/game.h | 3 - src/gui/handler.cpp | 61 ++------ src/gui/loot_app.cpp | 8 +- src/gui/loot_handler.cpp | 33 ++-- src/gui/loot_state.cpp | 240 +++++------------------------ src/gui/loot_state.h | 22 ++- src/tests/backend/game/test_game.h | 44 ------ 8 files changed, 80 insertions(+), 339 deletions(-) diff --git a/src/backend/game/game.cpp b/src/backend/game/game.cpp index ec823149..d24bab55 100644 --- a/src/backend/game/game.cpp +++ b/src/backend/game/game.cpp @@ -191,12 +191,4 @@ namespace loot { return LoadOrderHandler::IsPluginActive(pluginName); } } - - std::list ToGames(const std::list& settings) { - return list(settings.begin(), settings.end()); - } - - std::list ToGameSettings(const std::list& games) { - return list(games.begin(), games.end()); - } } diff --git a/src/backend/game/game.h b/src/backend/game/game.h index 43f7cb3a..8142adb4 100644 --- a/src/backend/game/game.h +++ b/src/backend/game/game.h @@ -54,9 +54,6 @@ namespace loot { private: bool _pluginsFullyLoaded; }; - - std::list ToGames(const std::list& settings); - std::list ToGameSettings(const std::list& games); } #endif diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index e58d3609..db75cd8c 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -275,20 +275,16 @@ namespace loot { else if (requestName == "closeSettings") { BOOST_LOG_TRIVIAL(trace) << "Settings dialog closed and changes accepted, updating settings object."; - // Update the game details and settings. - _lootState.UpdateSettings(request["args"][0]); + // Update the settings. + _lootState.load(request["args"][0]); // If the user has deleted a default game, we don't want to restore it now. // 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 >()); - _lootState.UpdateGames(games); + _lootState.UpdateGamesFromSettings(); // Also enable/disable debug logging as required. - if (request["args"][0]["enableDebugLogging"] && request["args"][0]["enableDebugLogging"].as()) - boost::log::core::get()->set_logging_enabled(true); - else - boost::log::core::get()->set_logging_enabled(false); + boost::log::core::get()->set_logging_enabled(_lootState.isDebugLoggingEnabled()); // Now send back the new list of installed games to the UI. BOOST_LOG_TRIVIAL(trace) << "Getting new list of installed games."; @@ -381,11 +377,7 @@ namespace loot { // Has two args: the first is the filter ID, the second is the value. BOOST_LOG_TRIVIAL(trace) << "Saving filter states."; try { - YAML::Node settings = _lootState.GetSettings(); - - settings["filters"][request["args"][0].as()] = request["args"][1]; - - _lootState.UpdateSettings(settings); + _lootState.storeFilterState(request["args"][0].as(), request["args"][1].as()); callback->Success(""); } catch (exception &e) { @@ -581,7 +573,7 @@ namespace loot { std::string Handler::GetSettings() { BOOST_LOG_TRIVIAL(info) << "Getting LOOT settings."; - return JSON::stringify(_lootState.GetSettings()); + return JSON::stringify(_lootState.toYaml()); } std::string Handler::GetLanguages() { @@ -779,13 +771,7 @@ namespace loot { } SendProgressUpdate(frame, loc::translate("Loading general messages...")); - //Set language. - unsigned int language; - if (_lootState.GetSettings()["language"]) - language = Language(_lootState.GetSettings()["language"].as()).Code(); - else - language = Language::any; - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name(); + BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name(); //Evaluate any conditions in the global messages. BOOST_LOG_TRIVIAL(debug) << "Evaluating global message conditions."; @@ -801,7 +787,7 @@ namespace loot { try { list::iterator it = messages.begin(); while (it != messages.end()) { - if (!it->EvalCondition(_lootState.CurrentGame(), language)) + if (!it->EvalCondition(_lootState.CurrentGame(), _lootState.getLanguage().Code())) it = messages.erase(it); else ++it; @@ -830,14 +816,7 @@ namespace loot { void Handler::UpdateMasterlist(CefRefPtr frame, CefRefPtr callback) { try { BOOST_LOG_TRIVIAL(debug) << "Updating and parsing masterlist."; - - //Set language. - unsigned int language; - if (_lootState.GetSettings()["language"]) - language = Language(_lootState.GetSettings()["language"].as()).Code(); - else - language = Language::any; - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name(); + BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name(); // Update / parse masterlist. bool wasChanged = true; @@ -914,7 +893,7 @@ namespace loot { try { list::iterator it = messages.begin(); while (it != messages.end()) { - if (!it->EvalCondition(_lootState.CurrentGame(), language)) + if (!it->EvalCondition(_lootState.CurrentGame(), _lootState.getLanguage().Code())) it = messages.erase(it); else ++it; @@ -974,13 +953,7 @@ namespace loot { void Handler::SortPlugins(CefRefPtr frame, CefRefPtr callback) { BOOST_LOG_TRIVIAL(info) << "Beginning sorting operation."; - //Set language. - unsigned int language; - if (_lootState.GetSettings()["language"]) - language = Language(_lootState.GetSettings()["language"].as()).Code(); - else - language = Language::any; - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name(); + BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name(); try { // Always reload all the plugins. @@ -989,7 +962,7 @@ namespace loot { //Sort plugins into their load order. PluginSorter sorter; - list plugins = sorter.Sort(_lootState.CurrentGame(), language, [this, frame](const string& message) { + list plugins = sorter.Sort(_lootState.CurrentGame(), _lootState.getLanguage().Code(), [this, frame](const string& message) { this->SendProgressUpdate(frame, message); }); @@ -1024,13 +997,7 @@ namespace loot { } YAML::Node Handler::GenerateDerivedMetadata(const Plugin& file, const PluginMetadata& masterlist, const PluginMetadata& userlist) { - //Set language. - unsigned int language; - if (_lootState.GetSettings()["language"]) - language = Language(_lootState.GetSettings()["language"].as()).Code(); - else - language = Language::any; - BOOST_LOG_TRIVIAL(info) << "Using message language: " << Language(language).Name(); + BOOST_LOG_TRIVIAL(info) << "Using message language: " << _lootState.getLanguage().Name(); // Now rederive the displayed metadata from the masterlist and userlist. Plugin tempPlugin(file); @@ -1041,7 +1008,7 @@ namespace loot { //Evaluate any conditions BOOST_LOG_TRIVIAL(trace) << "Evaluate conditions for merged plugin data."; try { - tempPlugin.EvalAllConditions(_lootState.CurrentGame(), language); + tempPlugin.EvalAllConditions(_lootState.CurrentGame(), _lootState.getLanguage().Code()); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << "\"" << tempPlugin.Name() << "\" contains a condition that could not be evaluated. Details: " << e.what(); diff --git a/src/gui/loot_app.cpp b/src/gui/loot_app.cpp index 1d6dba02..0db6d492 100644 --- a/src/gui/loot_app.cpp +++ b/src/gui/loot_app.cpp @@ -96,15 +96,13 @@ namespace loot { // Need to set the global locale for this process so that messages will // be translated. BOOST_LOG_TRIVIAL(debug) << "Initialising language settings in UI thread."; - const YAML::Node& settings = lootState.GetSettings(); - if (settings["language"] && settings["language"].as() != Language(Language::english).Locale()) { + if (lootState.getLanguage().Code() != Language::english) { boost::locale::generator gen; gen.add_messages_path(g_path_l10n.string()); gen.add_messages_domain("loot"); - loot::Language lang(settings["language"].as()); - BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lang.Name(); - locale::global(gen(lang.Locale() + ".UTF-8")); + BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lootState.getLanguage().Name(); + locale::global(gen(lootState.getLanguage().Locale() + ".UTF-8")); boost::filesystem::path::imbue(locale()); } diff --git a/src/gui/loot_handler.cpp b/src/gui/loot_handler.cpp index 9d08e98d..df652804 100644 --- a/src/gui/loot_handler.cpp +++ b/src/gui/loot_handler.cpp @@ -97,15 +97,13 @@ namespace loot { #endif // Set window size & position. - YAML::Node settings = _lootState.GetSettings(); - - if (settings["window"]["left"] && settings["window"]["top"] && settings["window"]["right"] && settings["window"]["bottom"]) { + if (_lootState.isWindowPositionStored()) { #ifdef _WIN32 RECT rc; - rc.left = settings["window"]["left"].as(); - rc.top = settings["window"]["top"].as(); - rc.right = settings["window"]["right"].as(); - rc.bottom = settings["window"]["bottom"].as(); + rc.left = _lootState.getWindowPosition().left; + rc.top = _lootState.getWindowPosition().top; + rc.right = _lootState.getWindowPosition().right; + rc.bottom = _lootState.getWindowPosition().bottom; // Fit the saved window size/position to the current monitor setup. @@ -175,21 +173,24 @@ namespace loot { void LootHandler::OnBeforeClose(CefRefPtr browser) { assert(CefCurrentlyOn(TID_UI)); - // Save window size & position. - YAML::Node settings = _lootState.GetSettings(); - #ifdef _WIN32 RECT rc; GetWindowRect(browser->GetHost()->GetWindowHandle(), &rc); - settings["window"]["left"] = rc.left; - settings["window"]["top"] = rc.top; - settings["window"]["right"] = rc.right; - settings["window"]["bottom"] = rc.bottom; + LootSettings::WindowPosition position; + position.top = rc.top; + position.bottom = rc.bottom; + position.left = rc.left; + position.right = rc.right; + _lootState.storeWindowPosition(position); #endif - _lootState.UpdateSettings(settings); - _lootState.SaveSettings(); + try { + _lootState.save(g_path_settings); + } + catch (std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "Failed to save LOOT's settings. Error: " << e.what(); + } // Cancel any javascript callbacks. browser_side_router_->OnBeforeClose(browser); diff --git a/src/gui/loot_state.cpp b/src/gui/loot_state.cpp index ec93e396..9eb89571 100644 --- a/src/gui/loot_state.cpp +++ b/src/gui/loot_state.cpp @@ -71,18 +71,12 @@ namespace loot { } if (fs::exists(g_path_settings)) { try { - fs::ifstream in(g_path_settings); - _settings = YAML::Load(in); - in.close(); + load(g_path_settings); } catch (exception& e) { _initErrors.push_back((format(translate("Error: Settings parsing failed. %1%")) % e.what()).str()); } } - // Check if the settings are valid (or if they don't exist). - if (!AreSettingsValid()) { - _settings = GetDefaultSettings(); - } //Set up logging. boost::log::add_file_log( @@ -96,14 +90,7 @@ namespace loot { ) ); boost::log::add_common_attributes(); - bool enableDebugLogging = false; - if (_settings["enableDebugLogging"]) { - enableDebugLogging = _settings["enableDebugLogging"].as(); - } - if (enableDebugLogging) - boost::log::core::get()->set_logging_enabled(true); - else - boost::log::core::get()->set_logging_enabled(false); + boost::log::core::get()->set_logging_enabled(isDebugLoggingEnabled()); // Log some useful info. BOOST_LOG_TRIVIAL(info) << "LOOT Version: " << g_version_major << "." << g_version_minor << "." << g_version_patch; @@ -121,9 +108,9 @@ namespace loot { fs::remove(g_path_local / "CEFDebugLog.txt"); // Now that settings have been loaded, set the locale again to handle translations. - if (_settings["language"] && _settings["language"].as() != Language(Language::english).Locale()) { + if (getLanguage().Code() != Language::english) { BOOST_LOG_TRIVIAL(debug) << "Initialising language settings."; - loot::Language lang(_settings["language"].as()); + loot::Language lang(getLanguage()); BOOST_LOG_TRIVIAL(debug) << "Selected language: " << lang.Name(); //Boost.Locale initialisation: Generate and imbue locales. @@ -136,17 +123,7 @@ namespace loot { //Detect installed games. BOOST_LOG_TRIVIAL(debug) << "Detecting installed games."; - try { - _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. - YAML::Node node; - _games = ToGames(GetGameSettings(node)); - } + _games = ToGames(getGameSettings()); try { BOOST_LOG_TRIVIAL(debug) << "Selecting game."; @@ -154,7 +131,7 @@ namespace loot { BOOST_LOG_TRIVIAL(debug) << "Initialising game-specific settings."; _currentGame->Init(true); // Update game path in settings object. - _settings["games"] = ToGameSettings(_games); + storeGameSettings(ToGameSettings(_games)); } catch (loot::error &e) { if (e.code() == loot::error::no_game_detected) { @@ -172,7 +149,13 @@ namespace loot { return _initErrors; } - void LootState::UpdateGames(std::list& games) { + void LootState::save(const boost::filesystem::path & file) { + storeLastGame(_currentGame->FolderName()); + updateLastVersion(); + LootSettings::save(file); + } + + void LootState::UpdateGamesFromSettings() { // Acquire the lock for the scope of this method. base::AutoLock lock_scope(_lock); @@ -180,7 +163,7 @@ namespace loot { // Update existing games, add new games. BOOST_LOG_TRIVIAL(trace) << "Updating existing games and adding new games."; - for (auto &game : games) { + for (const auto &game : getGameSettings()) { auto pos = find(_games.begin(), _games.end(), game); if (pos != _games.end()) { @@ -214,7 +197,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"] = ToGameSettings(_games); + storeGameSettings(ToGameSettings(_games)); } void LootState::ChangeGame(const std::string& newGameFolder) { @@ -226,7 +209,7 @@ namespace loot { _currentGame->Init(true); // Update game path in settings object. - _settings["games"] = ToGameSettings(_games); + storeGameSettings(ToGameSettings(_games)); BOOST_LOG_TRIVIAL(debug) << "New game is " << _currentGame->Name(); } @@ -246,186 +229,37 @@ namespace loot { return installedGames; } - const YAML::Node& LootState::GetSettings() const { - return _settings; - } - - void LootState::UpdateSettings(const YAML::Node& settings) { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); - - _settings = settings; - } - - void LootState::SaveSettings() { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); - - _settings["lastGame"] = _currentGame->FolderName(); - _settings["lastVersion"] = to_string(g_version_major) + "." + to_string(g_version_minor) + "." + to_string(g_version_patch); - - //Save settings. - try { - BOOST_LOG_TRIVIAL(debug) << "Saving LOOT settings."; - YAML::Emitter yout; - yout.SetIndent(2); - yout << _settings; - - fs::ofstream out(loot::g_path_settings); - out << yout.c_str(); - out.close(); - } - catch (std::exception &e) { - BOOST_LOG_TRIVIAL(error) << "Failed to save LOOT's settings. Error: " << e.what(); - } - } - void LootState::SelectGame(std::string preferredGame) { if (preferredGame.empty()) { // Get preferred game from settings. - if (_settings["game"] && _settings["game"].as() != "auto") - preferredGame = _settings["game"].as(); - else if (_settings["lastGame"] && _settings["lastGame"].as() != "auto") - preferredGame = _settings["lastGame"].as(); + if (getGame() != "auto") + preferredGame = getGame(); + else if (getLastGame() != "auto") + preferredGame = getLastGame(); } - // Get iterator to preferred game if there is one. - _currentGame = _games.end(); - for (auto it = _games.begin(); it != _games.end(); ++it) { - if ((preferredGame.empty() && it->IsInstalled()) - || (!preferredGame.empty() && preferredGame == it->FolderName() && it->IsInstalled())) { - _currentGame = it; - return; - } + // Get iterator to preferred game. + _currentGame = find_if(begin(_games), end(_games), [&](auto& game) { + return (preferredGame.empty() || preferredGame == game.FolderName()) && game.IsInstalled(); + }); + // If the preferred game cannot be found, get the first installed game. + if (_currentGame == end(_games)) { + _currentGame = find_if(begin(_games), end(_games), [](auto& game) { + return game.IsInstalled(); + }); } - - // Preferred game not found, just pick the first installed one. - for (auto it = _games.begin(); it != _games.end(); ++it) { - if (it->IsInstalled()) { - _currentGame = it; - return; - } + // If no game can be selected, throw an exception. + if (_currentGame == end(_games)) { + BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected."; + throw error(error::no_game_detected, translate("None of the supported games were detected.")); } - - BOOST_LOG_TRIVIAL(error) << "None of the supported games were detected."; - throw error(error::no_game_detected, translate("None of the supported games were detected.")); } - bool LootState::AreSettingsValid() { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); - - if (!_settings["language"]) { - if (_settings["Language"]) { - // Conversion from 0.6 key. - _settings["language"] = _settings["Language"]; - _settings.remove("Language"); - } - else - return false; - } - if (!_settings["game"]) { - if (_settings["Game"]) { - // Conversion from 0.6 key. - _settings["game"] = _settings["Game"]; - _settings.remove("Game"); - } - else - return false; - } - if (!_settings["lastGame"]) { - if (_settings["Last Game"]) { - // Conversion from 0.6 key. - _settings["lastGame"] = _settings["Last Game"]; - _settings.remove("Last Game"); - } - else - return false; - } - if (!_settings["enableDebugLogging"]) { - if (_settings["Debug Verbosity"]) { - // Conversion from 0.6 key. - _settings["enableDebugLogging"] = (_settings["Debug Verbosity"].as() > 0); - _settings.remove("Debug Verbosity"); - } - else if (_settings["debugVerbosity"]) { - // Conversion from 0.7 alpha key - _settings["enableDebugLogging"] = (_settings["debugVerbosity"].as() > 0); - _settings.remove("debugVerbosity"); - } - else - return false; - } - if (!_settings["updateMasterlist"]) { - if (_settings["Update Masterlist"]) { - // Conversion from 0.6 key. - _settings["updateMasterlist"] = _settings["Update Masterlist"]; - _settings.remove("Update Masterlist"); - } - else - return false; - } - if (!_settings["games"]) { - if (_settings["Games"]) { - // Conversion from 0.6 key. - _settings["games"] = _settings["Games"]; - - for (auto node : _settings["games"]) { - if (node["url"]) { - node["repo"] = node["url"]; - node["branch"] = "v0.8"; - node.remove("url"); - } - } - - _settings.remove("Games"); - } - else - return false; - } - else { - // Update existing default branches to new version default, if the - // default repositories are used. - for (auto node : _settings["games"]) { - GameSettings settings(node.as()); - - set oldDefaultBranches({ - "master", - "v0.7", - }); - - if (settings.RepoURL() == GameSettings(settings.Id()).RepoURL() - && oldDefaultBranches.count(settings.RepoBranch()) == 1) { - node["branch"] = "v0.8"; - } - } - } - - if (_settings["windows"]) - _settings.remove("windows"); - - return true; + std::list LootState::ToGames(const std::vector& settings) { + return list(settings.begin(), settings.end()); } - YAML::Node LootState::GetDefaultSettings() const { - YAML::Node root; - - root["language"] = "en"; - root["game"] = "auto"; - root["lastGame"] = "auto"; - root["enableDebugLogging"] = false; - root["updateMasterlist"] = true; - - // Add base game definitions, and Nehrim. - GetGameSettings(root); - - GameSettings settings(GameSettings::tes4, "Nehrim"); - settings.SetName("Nehrim - At Fate's Edge") - .SetMaster("Nehrim.esm") - .SetRegistryKey("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Nehrim - At Fate's Edge_is1\\InstallLocation"); - - root["games"].push_back(settings); - - return root; + std::vector LootState::ToGameSettings(const std::list& games) { + return vector(games.begin(), games.end()); } } diff --git a/src/gui/loot_state.h b/src/gui/loot_state.h index 88a7d572..c6683d73 100644 --- a/src/gui/loot_state.h +++ b/src/gui/loot_state.h @@ -25,35 +25,32 @@ #ifndef __LOOT_GUI_LOOT_STATE__ #define __LOOT_GUI_LOOT_STATE__ -#include "../backend/game/game.h" +#include "loot_settings.h" +#include "backend/game/game.h" #include #include -#include - namespace loot { - class LootState : public CefBase { + class LootState : public CefBase, public LootSettings { public: LootState(); void Init(const std::string& cmdLineGame); const std::vector& InitErrors() const; + void save(const boost::filesystem::path& file); + Game& CurrentGame(); void ChangeGame(const std::string& newGameFolder); - void UpdateGames(std::list& games); + void UpdateGamesFromSettings(); + // Get the folder names of the installed games. std::vector InstalledGames(); - const YAML::Node& GetSettings() const; - void UpdateSettings(const YAML::Node& settings); - void SaveSettings(); - // Used to check if LOOT has unaccepted sorting or metadata changes on quit. int numUnappliedChanges; private: - YAML::Node _settings; std::list _games; std::list::iterator _currentGame; std::vector _initErrors; @@ -61,9 +58,8 @@ namespace loot { // Select initial game. void SelectGame(std::string cmdLineGame); - // Check if the settings file has the right root keys (doesn't check their values). - bool AreSettingsValid(); - YAML::Node GetDefaultSettings() const; + static std::list ToGames(const std::vector& settings); + static std::vector ToGameSettings(const std::list& games); // Lock used to protect access to member variables. base::Lock _lock; diff --git a/src/tests/backend/game/test_game.h b/src/tests/backend/game/test_game.h index cb4cf5b4..56c750a9 100644 --- a/src/tests/backend/game/test_game.h +++ b/src/tests/backend/game/test_game.h @@ -655,48 +655,4 @@ TEST_F(Game, shouldShowBlankEspAsActiveIfItHasBeenFullyLoadedAndTheGameHasBeenIn EXPECT_FALSE(game.IsPluginActive("Blank.esp")); } -TEST(ToGames, EmptySettings) { - EXPECT_EQ(std::list(), loot::ToGames(std::list())); -} - -TEST(ToGames, NonEmptySettings) { - std::list settings({ - loot::GameSettings(loot::GameSettings::tes4), - loot::GameSettings(loot::GameSettings::tes5), - loot::GameSettings(loot::GameSettings::fo3), - loot::GameSettings(loot::GameSettings::fonv), - }); - - std::list expected({ - loot::Game(loot::Game::tes4), - loot::Game(loot::Game::tes5), - loot::Game(loot::Game::fo3), - loot::Game(loot::Game::fonv), - }); - - EXPECT_EQ(expected, loot::ToGames(settings)); -} - -TEST(ToGameSettings, EmptyGames) { - EXPECT_EQ(std::list(), loot::ToGameSettings(std::list())); -} - -TEST(ToGameSettings, NonEmptyGames) { - std::list games({ - loot::Game(loot::Game::tes4), - loot::Game(loot::Game::tes5), - loot::Game(loot::Game::fo3), - loot::Game(loot::Game::fonv), - }); - - std::list expected({ - loot::GameSettings(loot::GameSettings::tes4), - loot::GameSettings(loot::GameSettings::tes5), - loot::GameSettings(loot::GameSettings::fo3), - loot::GameSettings(loot::GameSettings::fonv), - }); - - EXPECT_EQ(expected, loot::ToGameSettings(games)); -} - #endif From e2ee8f838c99e5d9af4b921e34b38e157e6d8581 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 10 Jan 2016 19:18:00 +0000 Subject: [PATCH 4/5] Make unapplied changes counter private --- src/gui/handler.cpp | 12 ++++++------ src/gui/loot_handler.cpp | 2 +- src/gui/loot_state.cpp | 14 +++++++++++++- src/gui/loot_state.h | 8 ++++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/gui/handler.cpp b/src/gui/handler.cpp index db75cd8c..b185dd6b 100644 --- a/src/gui/handler.cpp +++ b/src/gui/handler.cpp @@ -160,12 +160,12 @@ namespace loot { return true; } else if (request == "cancelSort") { - --_lootState.numUnappliedChanges; + _lootState.decrementUnappliedChangeCounter(); callback->Success(""); return true; } else if (request == "editorOpened") { - ++_lootState.numUnappliedChanges; + _lootState.incrementUnappliedChangeCounter(); callback->Success(""); return true; } @@ -173,7 +173,7 @@ namespace loot { // This version of the editorClosed query has no arguments as it is // sent when editing is cancelled. Just update the unapplied changes // counter. - --_lootState.numUnappliedChanges; + _lootState.decrementUnappliedChangeCounter(); callback->Success(""); return true; } @@ -253,7 +253,7 @@ namespace loot { // One argument, which is the plugin metadata that has changed (+ its name). try { callback->Success(ApplyUserEdits(request["args"][0])); - --_lootState.numUnappliedChanges; + _lootState.decrementUnappliedChangeCounter(); } catch (loot::error &e) { BOOST_LOG_TRIVIAL(error) << "Failed to apply plugin metadata. Details: " << e.what(); @@ -297,7 +297,7 @@ namespace loot { return true; } else if (requestName == "applySort") { - --_lootState.numUnappliedChanges; + _lootState.decrementUnappliedChangeCounter(); BOOST_LOG_TRIVIAL(trace) << "User has accepted sorted load order, applying it."; try { _lootState.CurrentGame().SetLoadOrder(request["args"][0].as>()); @@ -983,7 +983,7 @@ namespace loot { node.push_back(pluginNode); } - ++_lootState.numUnappliedChanges; + _lootState.incrementUnappliedChangeCounter(); if (node.size() > 0) callback->Success(JSON::stringify(node)); diff --git a/src/gui/loot_handler.cpp b/src/gui/loot_handler.cpp index df652804..1ef3bcec 100644 --- a/src/gui/loot_handler.cpp +++ b/src/gui/loot_handler.cpp @@ -152,7 +152,7 @@ namespace loot { assert(CefCurrentlyOn(TID_UI)); // Check if unapplied changes exist. - if (_lootState.numUnappliedChanges > 0) { + if (_lootState.hasUnappliedChanges()) { browser->GetMainFrame()->ExecuteJavaScript("onQuit();", browser->GetMainFrame()->GetURL(), 0); return true; } diff --git a/src/gui/loot_state.cpp b/src/gui/loot_state.cpp index 9eb89571..9fcbd5ea 100644 --- a/src/gui/loot_state.cpp +++ b/src/gui/loot_state.cpp @@ -46,7 +46,7 @@ using boost::format; namespace fs = boost::filesystem; namespace loot { - LootState::LootState() : numUnappliedChanges(0), _currentGame(_games.end()) {} + LootState::LootState() : unappliedChangeCounter(0), _currentGame(_games.end()) {} void LootState::Init(const std::string& cmdLineGame) { // Do some preliminary locale / UTF-8 support setup here, in case the settings file reading requires it. @@ -229,6 +229,18 @@ namespace loot { return installedGames; } + bool LootState::hasUnappliedChanges() const { + return unappliedChangeCounter > 0; + } + + void LootState::incrementUnappliedChangeCounter() { + ++unappliedChangeCounter; + } + + void LootState::decrementUnappliedChangeCounter() { + --unappliedChangeCounter; + } + void LootState::SelectGame(std::string preferredGame) { if (preferredGame.empty()) { // Get preferred game from settings. diff --git a/src/gui/loot_state.h b/src/gui/loot_state.h index c6683d73..26205135 100644 --- a/src/gui/loot_state.h +++ b/src/gui/loot_state.h @@ -48,13 +48,17 @@ namespace loot { // Get the folder names of the installed games. std::vector InstalledGames(); - // Used to check if LOOT has unaccepted sorting or metadata changes on quit. - int numUnappliedChanges; + bool hasUnappliedChanges() const; + void incrementUnappliedChangeCounter(); + void decrementUnappliedChangeCounter(); private: std::list _games; std::list::iterator _currentGame; std::vector _initErrors; + // Used to check if LOOT has unaccepted sorting or metadata changes on quit. + size_t unappliedChangeCounter; + // Select initial game. void SelectGame(std::string cmdLineGame); From 33d74d0ab51ada310921c11fb145b297ea34bb21 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Sun, 10 Jan 2016 19:27:11 +0000 Subject: [PATCH 5/5] Replace CEF thread locking with C++11 locking This removes the CEF dependency from LootState. --- src/gui/loot_state.cpp | 13 +++++++------ src/gui/loot_state.h | 10 +++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/gui/loot_state.cpp b/src/gui/loot_state.cpp index 9fcbd5ea..d2ac57fa 100644 --- a/src/gui/loot_state.cpp +++ b/src/gui/loot_state.cpp @@ -39,6 +39,10 @@ #include #include +#ifdef _WIN32 +#include +#endif + using namespace std; using boost::locale::translate; using boost::format; @@ -156,8 +160,7 @@ namespace loot { } void LootState::UpdateGamesFromSettings() { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); + std::lock_guard guard(mutex); unordered_set newGameFolders; @@ -201,8 +204,7 @@ namespace loot { } void LootState::ChangeGame(const std::string& newGameFolder) { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); + std::lock_guard guard(mutex); BOOST_LOG_TRIVIAL(debug) << "Changing current game to that with folder: " << newGameFolder; _currentGame = find(_games.begin(), _games.end(), Game(Game::autodetect, newGameFolder)); @@ -214,8 +216,7 @@ namespace loot { } Game& LootState::CurrentGame() { - // Acquire the lock for the scope of this method. - base::AutoLock lock_scope(_lock); + std::lock_guard guard(mutex); return *_currentGame; } diff --git a/src/gui/loot_state.h b/src/gui/loot_state.h index 26205135..b43242ca 100644 --- a/src/gui/loot_state.h +++ b/src/gui/loot_state.h @@ -28,11 +28,8 @@ #include "loot_settings.h" #include "backend/game/game.h" -#include -#include - namespace loot { - class LootState : public CefBase, public LootSettings { + class LootState : public LootSettings { public: LootState(); @@ -65,9 +62,8 @@ namespace loot { static std::list ToGames(const std::vector& settings); static std::vector ToGameSettings(const std::list& games); - // Lock used to protect access to member variables. - base::Lock _lock; - IMPLEMENT_REFCOUNTING(LootState); + // Mutex used to protect access to member variables. + std::mutex mutex; }; }