From 8360e358ee56ef5cef9aba78a57b7d0ae358ed01 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 14 Jan 2016 16:36:11 -0600 Subject: [PATCH 1/6] New configuration namespace --- Source/Core/Common/CMakeLists.txt | 1 + Source/Core/Common/Common.vcxproj | 2 + Source/Core/Common/Common.vcxproj.filters | 2 + Source/Core/Common/Config.cpp | 461 ++++++++++++++++++++++ Source/Core/Common/Config.h | 187 +++++++++ 5 files changed, 653 insertions(+) create mode 100644 Source/Core/Common/Config.cpp create mode 100644 Source/Core/Common/Config.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 5f910a1670..7ed79d07d1 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -15,6 +15,7 @@ set(SRCS Analytics.cpp MsgHandler.cpp NandPaths.cpp Network.cpp + Config.cpp PcapFile.cpp PerformanceCounter.cpp Profiler.cpp diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 831dcad4a7..047b863443 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -123,6 +123,7 @@ + @@ -172,6 +173,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 3c9b952742..52625071a4 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -53,6 +53,7 @@ + @@ -245,6 +246,7 @@ + diff --git a/Source/Core/Common/Config.cpp b/Source/Core/Common/Config.cpp new file mode 100644 index 0000000000..e41f123243 --- /dev/null +++ b/Source/Core/Common/Config.cpp @@ -0,0 +1,461 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/Assert.h" +#include "Common/Config.h" +#include "Common/StringUtil.h" + +namespace Config +{ +const std::string& Section::NULL_STRING = ""; +static Layers s_layers; +static std::list s_callbacks; + +void CallbackSystems(); + +// Only to be used with the meta-layer +class RecursiveSection final : public Section +{ +public: + RecursiveSection(LayerType layer, System system, const std::string& name) + : Section(layer, system, name) + { + } + + bool Exists(const std::string& key) const override; + + bool Get(const std::string& key, std::string* value, + const std::string& default_value = NULL_STRING) const override; + + void Set(const std::string& key, const std::string& value) override; +}; + +bool RecursiveSection::Exists(const std::string& key) const +{ + auto layers_it = s_layers.find(LayerType::Meta); + do + { + Section* layer_section = layers_it->second->GetSection(m_system, m_name); + if (layer_section && layer_section->Exists(key)) + { + return true; + } + } while (--layers_it != s_layers.end()); + + return false; +} + +bool RecursiveSection::Get(const std::string& key, std::string* value, + const std::string& default_value) const +{ + static const std::array search_order = {{ + // Skip the meta layer + LayerType::CurrentRun, LayerType::CommandLine, LayerType::Movie, LayerType::Netplay, + LayerType::LocalGame, LayerType::GlobalGame, LayerType::Base, + }}; + + for (auto layer_id : search_order) + { + auto layers_it = s_layers.find(layer_id); + if (layers_it == s_layers.end()) + continue; + + Section* layer_section = layers_it->second->GetSection(m_system, m_name); + if (layer_section && layer_section->Exists(key)) + { + return layer_section->Get(key, value, default_value); + } + } + + return Section::Get(key, value, default_value); +} + +void RecursiveSection::Set(const std::string& key, const std::string& value) +{ + // The RecursiveSection can't set since it is used to recursively get values from the layer + // map. + // It is only a part of the meta layer, and the meta layer isn't allowed to set any values. + _assert_msg_(COMMON, false, "Don't try to set values here!"); +} + +class RecursiveLayer final : public Layer +{ +public: + RecursiveLayer() : Layer(LayerType::Meta) {} + Section* GetSection(System system, const std::string& section_name) override; + Section* GetOrCreateSection(System system, const std::string& section_name) override; +}; + +Section* RecursiveLayer::GetSection(System system, const std::string& section_name) +{ + // Always queries backwards recursively, so it doesn't matter if it exists or not on this layer + return GetOrCreateSection(system, section_name); +} + +Section* RecursiveLayer::GetOrCreateSection(System system, const std::string& section_name) +{ + Section* section = Layer::GetSection(system, section_name); + if (!section) + { + m_sections[system].emplace_back(new RecursiveSection(m_layer, system, section_name)); + section = m_sections[system].back(); + } + return section; +} + +bool Section::Exists(const std::string& key) const +{ + return m_values.find(key) != m_values.end(); +} + +bool Section::Delete(const std::string& key) +{ + auto it = m_values.find(key); + if (it == m_values.end()) + return false; + + m_values.erase(it); + return true; +} + +void Section::Set(const std::string& key, const std::string& value) +{ + auto it = m_values.find(key); + if (it != m_values.end()) + it->second = value; + else + m_values[key] = value; +} + +void Section::Set(const std::string& key, u32 newValue) +{ + Section::Set(key, StringFromFormat("0x%08x", newValue)); +} + +void Section::Set(const std::string& key, float newValue) +{ + Section::Set(key, StringFromFormat("%#.9g", newValue)); +} + +void Section::Set(const std::string& key, double newValue) +{ + Section::Set(key, StringFromFormat("%#.17g", newValue)); +} + +void Section::Set(const std::string& key, int newValue) +{ + Section::Set(key, StringFromInt(newValue)); +} + +void Section::Set(const std::string& key, bool newValue) +{ + Section::Set(key, StringFromBool(newValue)); +} + +void Section::Set(const std::string& key, const std::string& newValue, + const std::string& defaultValue) +{ + if (newValue != defaultValue) + Set(key, newValue); + else + Delete(key); +} + +bool Section::Get(const std::string& key, std::string* value, + const std::string& default_value) const +{ + const auto& it = m_values.find(key); + if (it != m_values.end()) + { + *value = it->second; + return true; + } + else if (&default_value != &NULL_STRING) + { + *value = default_value; + return true; + } + + return false; +} + +bool Section::Get(const std::string& key, int* value, int defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, u32* value, u32 defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, bool* value, bool defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, float* value, float defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, double* value, double defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +// Return a list of all lines in a section +bool Section::GetLines(std::vector* lines, const bool remove_comments) const +{ + lines->clear(); + + for (std::string line : m_lines) + { + line = StripSpaces(line); + + if (remove_comments) + { + size_t commentPos = line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + } + + lines->push_back(line); + } + + return true; +} + +// Onion layers +Layer::Layer(std::unique_ptr loader) + : m_layer(loader->GetLayer()), m_loader(std::move(loader)) +{ + Load(); +} + +Layer::~Layer() +{ + Save(); +} + +bool Layer::Exists(System system, const std::string& section_name, const std::string& key) +{ + Section* section = GetSection(system, section_name); + if (!section) + return false; + return section->Exists(key); +} + +bool Layer::DeleteKey(System system, const std::string& section_name, const std::string& key) +{ + Section* section = GetSection(system, section_name); + if (!section) + return false; + return section->Delete(key); +} + +Section* Layer::GetSection(System system, const std::string& section_name) +{ + for (Section* section : m_sections[system]) + if (!strcasecmp(section->m_name.c_str(), section_name.c_str())) + return section; + return nullptr; +} + +Section* Layer::GetOrCreateSection(System system, const std::string& section_name) +{ + Section* section = GetSection(system, section_name); + if (!section) + { + if (m_layer == LayerType::Meta) + m_sections[system].emplace_back(new RecursiveSection(m_layer, system, section_name)); + else + m_sections[system].emplace_back(new Section(m_layer, system, section_name)); + section = m_sections[system].back(); + } + return section; +} + +void Layer::Load() +{ + if (m_loader) + m_loader->Load(this); + CallbackSystems(); +} + +void Layer::Save() +{ + if (m_loader) + m_loader->Save(this); +} + +Section* GetOrCreateSection(System system, const std::string& section_name) +{ + return s_layers[LayerType::Meta]->GetOrCreateSection(system, section_name); +} + +Layers* GetLayers() +{ + return &s_layers; +} + +void AddLayer(std::unique_ptr layer) +{ + s_layers[layer->GetLayer()] = std::move(layer); + CallbackSystems(); +} + +void AddLayer(std::unique_ptr loader) +{ + AddLayer(std::make_unique(std::move(loader))); +} + +void AddLoadLayer(std::unique_ptr layer) +{ + layer->Load(); + AddLayer(std::move(layer)); +} + +void AddLoadLayer(std::unique_ptr loader) +{ + AddLoadLayer(std::make_unique(std::move(loader))); +} + +Layer* GetLayer(LayerType layer) +{ + if (!LayerExists(layer)) + return nullptr; + return s_layers[layer].get(); +} + +void RemoveLayer(LayerType layer) +{ + s_layers.erase(layer); + CallbackSystems(); +} +bool LayerExists(LayerType layer) +{ + return s_layers.find(layer) != s_layers.end(); +} + +void AddConfigChangedCallback(ConfigChangedCallback func) +{ + s_callbacks.emplace_back(func); +} + +void CallbackSystems() +{ + for (const auto& callback : s_callbacks) + callback(); +} + +// Explicit load and save of layers +void Load() +{ + for (auto& layer : s_layers) + layer.second->Load(); + + CallbackSystems(); +} + +void Save() +{ + for (auto& layer : s_layers) + layer.second->Save(); +} + +void Init() +{ + // This layer always has to exist + s_layers[LayerType::Meta] = std::make_unique(); +} + +void Shutdown() +{ + s_layers.clear(); + s_callbacks.clear(); +} + +static std::map system_to_name = { + {System::Main, "Dolphin"}, {System::GCPad, "GCPad"}, {System::WiiPad, "Wiimote"}, + {System::GCKeyboard, "GCKeyboard"}, {System::GFX, "Graphics"}, {System::Logger, "Logger"}, + {System::Debugger, "Debugger"}, {System::UI, "UI"}, +}; + +const std::string& GetSystemName(System system) +{ + return system_to_name[system]; +} + +System GetSystemFromName(const std::string& system) +{ + for (auto& val : system_to_name) + if (val.second == system) + return val.first; + + _assert_msg_(COMMON, false, "Programming error! Couldn't convert '%s' to system!", + system.c_str()); + return System::Main; +} + +const std::string& GetLayerName(LayerType layer) +{ + static std::map layer_to_name = { + {LayerType::Base, "Base"}, + {LayerType::GlobalGame, "Global GameINI"}, + {LayerType::LocalGame, "Local GameINI"}, + {LayerType::Netplay, "Netplay"}, + {LayerType::Movie, "Movie"}, + {LayerType::CommandLine, "Command Line"}, + {LayerType::CurrentRun, "Current Run"}, + {LayerType::Meta, "Top"}, + }; + return layer_to_name[layer]; +} +} diff --git a/Source/Core/Common/Config.h b/Source/Core/Common/Config.h new file mode 100644 index 0000000000..ac7fb5d6ad --- /dev/null +++ b/Source/Core/Common/Config.h @@ -0,0 +1,187 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +// XXX: Purely for case insensitive compare +#include "Common/IniFile.h" + +namespace Config +{ +enum class LayerType +{ + Base, + GlobalGame, + LocalGame, + Movie, + Netplay, + CommandLine, + CurrentRun, + Meta, +}; + +enum class System +{ + Main, + GCPad, + WiiPad, + GCKeyboard, + GFX, + Logger, + Debugger, + UI, +}; + +class Section; +class Layer; +class ConfigLayerLoader; + +using SectionValueMap = std::map; +using LayerMap = std::map>; +using Layers = std::map>; +using ConfigChangedCallback = std::function; + +class Section +{ + friend Layer; + friend ConfigLayerLoader; + +public: + Section(LayerType layer, System system, const std::string& name) + : m_layer(layer), m_system(system), m_name(name) + { + } + + virtual bool Exists(const std::string& key) const; + bool Delete(const std::string& key); + + // Setters + virtual void Set(const std::string& key, const std::string& value); + + void Set(const std::string& key, u32 newValue); + void Set(const std::string& key, float newValue); + void Set(const std::string& key, double newValue); + void Set(const std::string& key, int newValue); + void Set(const std::string& key, bool newValue); + + // Setters with default values + void Set(const std::string& key, const std::string& newValue, const std::string& defaultValue); + template + void Set(const std::string& key, T newValue, const T defaultValue) + { + if (newValue != defaultValue) + Set(key, newValue); + else + Delete(key); + } + + // Getters + virtual bool Get(const std::string& key, std::string* value, + const std::string& default_value = NULL_STRING) const; + + bool Get(const std::string& key, int* value, int defaultValue = 0) const; + bool Get(const std::string& key, u32* value, u32 defaultValue = 0) const; + bool Get(const std::string& key, bool* value, bool defaultValue = false) const; + bool Get(const std::string& key, float* value, float defaultValue = 0.0f) const; + bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; + + // Section chunk + void SetLines(const std::vector& lines) { m_lines = lines; } + // XXX: Add to recursive layer + virtual bool GetLines(std::vector* lines, const bool remove_comments = true) const; + virtual bool HasLines() const { return m_lines.size() > 0; } + const std::string& GetName() const { return m_name; } + const SectionValueMap& GetValues() const { return m_values; } +protected: + LayerType m_layer; + System m_system; + const std::string m_name; + static const std::string& NULL_STRING; + + SectionValueMap m_values; + + std::vector m_lines; +}; + +// XXX: Allow easy migration! +class ConfigLayerLoader +{ +public: + ConfigLayerLoader(LayerType layer) : m_layer(layer) {} + ~ConfigLayerLoader() {} + virtual void Load(Layer* config_layer) = 0; + virtual void Save(Layer* config_layer) = 0; + + LayerType GetLayer() const { return m_layer; } +private: + const LayerType m_layer; +}; + +class Layer +{ +public: + explicit Layer(LayerType layer) : m_layer(layer) {} + Layer(std::unique_ptr loader); + virtual ~Layer(); + + // Convenience functions + bool Exists(System system, const std::string& section_name, const std::string& key); + bool DeleteKey(System system, const std::string& section_name, const std::string& key); + template + bool GetIfExists(System system, const std::string& section_name, const std::string& key, T* value) + { + if (Exists(system, section_name, key)) + return GetOrCreateSection(system, section_name)->Get(key, value); + + return false; + } + + virtual Section* GetSection(System system, const std::string& section_name); + virtual Section* GetOrCreateSection(System system, const std::string& section_name); + + // Explicit load and save of layers + void Load(); + void Save(); + + LayerType GetLayer() const { return m_layer; } + const LayerMap& GetLayerMap() { return m_sections; } + // Stay away from this routine as much as possible + ConfigLayerLoader* GetLoader() { return m_loader.get(); } +protected: + LayerMap m_sections; + const LayerType m_layer; + std::unique_ptr m_loader; +}; + +// Common function used for getting configuration +Section* GetOrCreateSection(System system, const std::string& section_name); + +// Layer management +Layers* GetLayers(); +void AddLayer(std::unique_ptr layer); +void AddLayer(std::unique_ptr loader); +void AddLoadLayer(std::unique_ptr layer); +void AddLoadLayer(std::unique_ptr loader); +Layer* GetLayer(LayerType layer); +void RemoveLayer(LayerType layer); +bool LayerExists(LayerType layer); + +void AddConfigChangedCallback(ConfigChangedCallback func); + +// Explicit load and save of layers +void Load(); +void Save(); + +void Init(); +void Shutdown(); + +const std::string& GetSystemName(System system); +System GetSystemFromName(const std::string& system); +const std::string& GetLayerName(LayerType layer); +} From b51c6023bab3fd3df63bcdda2c84c1d0ebdb7543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 14 Feb 2017 18:05:54 +0100 Subject: [PATCH 2/6] Config: Only save settings if they have been changed --- Source/Core/Common/Config.cpp | 42 +++++++++++++++++++++++++++++++---- Source/Core/Common/Config.h | 9 +++++++- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Source/Core/Common/Config.cpp b/Source/Core/Common/Config.cpp index e41f123243..5ed41ef589 100644 --- a/Source/Core/Common/Config.cpp +++ b/Source/Core/Common/Config.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include @@ -119,16 +120,23 @@ bool Section::Delete(const std::string& key) return false; m_values.erase(it); + m_dirty = true; return true; } void Section::Set(const std::string& key, const std::string& value) { auto it = m_values.find(key); - if (it != m_values.end()) + if (it != m_values.end() && it->second != value) + { it->second = value; - else + m_dirty = true; + } + else if (it == m_values.end()) + { m_values[key] = value; + m_dirty = true; + } } void Section::Set(const std::string& key, u32 newValue) @@ -165,6 +173,12 @@ void Section::Set(const std::string& key, const std::string& newValue, Delete(key); } +void Section::SetLines(const std::vector& lines) +{ + m_lines = lines; + m_dirty = true; +} + bool Section::Get(const std::string& key, std::string* value, const std::string& default_value) const { @@ -326,13 +340,33 @@ void Layer::Load() { if (m_loader) m_loader->Load(this); + ClearDirty(); CallbackSystems(); } void Layer::Save() { - if (m_loader) - m_loader->Save(this); + if (!m_loader || !IsDirty()) + return; + + m_loader->Save(this); + ClearDirty(); +} + +bool Layer::IsDirty() const +{ + return std::any_of(m_sections.begin(), m_sections.end(), [](const auto& system) { + return std::any_of(system.second.begin(), system.second.end(), + [](const auto& section) { return section->IsDirty(); }); + }); +} + +void Layer::ClearDirty() +{ + std::for_each(m_sections.begin(), m_sections.end(), [](const auto& system) { + std::for_each(system.second.begin(), system.second.end(), + [](const auto& section) { section->ClearDirty(); }); + }); } Section* GetOrCreateSection(System system, const std::string& section_name) diff --git a/Source/Core/Common/Config.h b/Source/Core/Common/Config.h index ac7fb5d6ad..3b8614f264 100644 --- a/Source/Core/Common/Config.h +++ b/Source/Core/Common/Config.h @@ -92,13 +92,17 @@ public: bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; // Section chunk - void SetLines(const std::vector& lines) { m_lines = lines; } + void SetLines(const std::vector& lines); // XXX: Add to recursive layer virtual bool GetLines(std::vector* lines, const bool remove_comments = true) const; virtual bool HasLines() const { return m_lines.size() > 0; } const std::string& GetName() const { return m_name; } const SectionValueMap& GetValues() const { return m_values; } + bool IsDirty() const { return m_dirty; } + void ClearDirty() { m_dirty = false; } protected: + bool m_dirty = false; + LayerType m_layer; System m_system; const std::string m_name; @@ -154,6 +158,9 @@ public: // Stay away from this routine as much as possible ConfigLayerLoader* GetLoader() { return m_loader.get(); } protected: + bool IsDirty() const; + void ClearDirty(); + LayerMap m_sections; const LayerType m_layer; std::unique_ptr m_loader; From 9c3265f1ef1a443c611ca71eb19a137271c2434c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 15 Feb 2017 16:25:37 +0100 Subject: [PATCH 3/6] Config: Keep track of deleted keys This allows deleted keys to be deleted from INIs properly. --- Source/Core/Common/Config.cpp | 2 ++ Source/Core/Common/Config.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Source/Core/Common/Config.cpp b/Source/Core/Common/Config.cpp index 5ed41ef589..796ed62653 100644 --- a/Source/Core/Common/Config.cpp +++ b/Source/Core/Common/Config.cpp @@ -120,6 +120,8 @@ bool Section::Delete(const std::string& key) return false; m_values.erase(it); + + m_deleted_keys.push_back(key); m_dirty = true; return true; } diff --git a/Source/Core/Common/Config.h b/Source/Core/Common/Config.h index 3b8614f264..f08de4ec8b 100644 --- a/Source/Core/Common/Config.h +++ b/Source/Core/Common/Config.h @@ -98,6 +98,7 @@ public: virtual bool HasLines() const { return m_lines.size() > 0; } const std::string& GetName() const { return m_name; } const SectionValueMap& GetValues() const { return m_values; } + const std::vector& GetDeletedKeys() const { return m_deleted_keys; } bool IsDirty() const { return m_dirty; } void ClearDirty() { m_dirty = false; } protected: @@ -109,6 +110,7 @@ protected: static const std::string& NULL_STRING; SectionValueMap m_values; + std::vector m_deleted_keys; std::vector m_lines; }; From 7bd34ac0b500127ded632750d52546c6f4df9604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 15 Feb 2017 23:06:06 +0100 Subject: [PATCH 4/6] Config: Trigger callbacks on layer save And remove an extraneous callback trigger in Config::Load. --- Source/Core/Common/Config.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Core/Common/Config.cpp b/Source/Core/Common/Config.cpp index 796ed62653..eb88f79019 100644 --- a/Source/Core/Common/Config.cpp +++ b/Source/Core/Common/Config.cpp @@ -353,6 +353,7 @@ void Layer::Save() m_loader->Save(this); ClearDirty(); + CallbackSystems(); } bool Layer::IsDirty() const @@ -436,8 +437,6 @@ void Load() { for (auto& layer : s_layers) layer.second->Load(); - - CallbackSystems(); } void Save() From abe6f8766a5a1d9007584b4224ba9b74fdf645f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 16 Feb 2017 10:59:22 +0100 Subject: [PATCH 5/6] Config: Add a few helper functions for repetitive tasks Getting and setting configuration from the base config layer are common and repetitive tasks. This commit adds some simpler to use functions to make the new system easier to work with. Config::Get and Config::Set are intended to make switching from SConfig a bit less painful. They always operate on the main system. Example usage: // before auto base_layer = Config::GetLayer(Config::LayerType::Base); auto core = base_layer->GetOrCreateSection(Config::System::Main, "Core"); u8 language; core->Get("Language", &language, 0); SetData("IPL.LNG", language); // now auto base_layer = Config::GetLayer(Config::LayerType::Base); auto core = base_layer->GetOrCreateSection(Config::System::Main, "Core"); SetData("IPL.LNG", core->Get("Language", 0)); // or simply SetData("IPL.LNG", Config::Get("Core", "Language", 0)); --- Source/Core/Common/Config.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Core/Common/Config.h b/Source/Core/Common/Config.h index f08de4ec8b..cc880df5e2 100644 --- a/Source/Core/Common/Config.h +++ b/Source/Core/Common/Config.h @@ -91,6 +91,14 @@ public: bool Get(const std::string& key, float* value, float defaultValue = 0.0f) const; bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; + template + T Get(const std::string& key, const T& default_value) const + { + T value; + Get(key, value, default_value); + return value; + } + // Section chunk void SetLines(const std::vector& lines); // XXX: Add to recursive layer @@ -187,6 +195,22 @@ void AddConfigChangedCallback(ConfigChangedCallback func); void Load(); void Save(); +// Often used functions for getting or setting configuration on the base layer for the main system +template +T Get(const std::string& section_name, const std::string& key, const T& default_value) +{ + auto base_layer = GetLayer(Config::LayerType::Base); + return base_layer->GetOrCreateSection(Config::System::Main, section_name) + ->Get(key, default_value); +} + +template +void Set(const std::string& section_name, const std::string& key, const T& value) +{ + auto base_layer = GetLayer(Config::LayerType::Base); + base_layer->GetOrCreateSection(Config::System::Main, section_name)->Set(key, value); +} + void Init(); void Shutdown(); From 88a21dd2b9539292838aff32f5f29c182d4d6c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Thu, 16 Feb 2017 16:58:40 +0100 Subject: [PATCH 6/6] Fix things mentioned during code review Ref: https://github.com/dolphin-emu/dolphin/pull/4917 --- Source/Core/Common/CMakeLists.txt | 4 +- Source/Core/Common/Common.vcxproj | 9 +- Source/Core/Common/Common.vcxproj.filters | 9 +- Source/Core/Common/Config.cpp | 496 ---------------------- Source/Core/Common/Config.h | 220 ---------- Source/Core/Common/Config/Config.cpp | 140 ++++++ Source/Core/Common/Config/Config.h | 63 +++ Source/Core/Common/Config/Enums.h | 32 ++ Source/Core/Common/Config/Layer.cpp | 148 +++++++ Source/Core/Common/Config/Layer.h | 80 ++++ Source/Core/Common/Config/Section.cpp | 286 +++++++++++++ Source/Core/Common/Config/Section.h | 110 +++++ 12 files changed, 876 insertions(+), 721 deletions(-) delete mode 100644 Source/Core/Common/Config.cpp delete mode 100644 Source/Core/Common/Config.h create mode 100644 Source/Core/Common/Config/Config.cpp create mode 100644 Source/Core/Common/Config/Config.h create mode 100644 Source/Core/Common/Config/Enums.h create mode 100644 Source/Core/Common/Config/Layer.cpp create mode 100644 Source/Core/Common/Config/Layer.h create mode 100644 Source/Core/Common/Config/Section.cpp create mode 100644 Source/Core/Common/Config/Section.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 7ed79d07d1..9b8d3ba3b2 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -1,6 +1,9 @@ set(SRCS Analytics.cpp CDUtils.cpp ColorUtil.cpp + Config/Config.cpp + Config/Layer.cpp + Config/Section.cpp ENetUtil.cpp FileSearch.cpp FileUtil.cpp @@ -15,7 +18,6 @@ set(SRCS Analytics.cpp MsgHandler.cpp NandPaths.cpp Network.cpp - Config.cpp PcapFile.cpp PerformanceCounter.cpp Profiler.cpp diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 047b863443..756c30d473 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -53,6 +53,10 @@ + + + + @@ -123,7 +127,6 @@ - @@ -150,6 +153,9 @@ + + + @@ -173,7 +179,6 @@ - diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 52625071a4..4ffd6c0575 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -34,6 +34,10 @@ + + + + @@ -53,7 +57,6 @@ - @@ -233,6 +236,9 @@ + + + @@ -246,7 +252,6 @@ - diff --git a/Source/Core/Common/Config.cpp b/Source/Core/Common/Config.cpp deleted file mode 100644 index eb88f79019..0000000000 --- a/Source/Core/Common/Config.cpp +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include -#include - -#include "Common/Assert.h" -#include "Common/Config.h" -#include "Common/StringUtil.h" - -namespace Config -{ -const std::string& Section::NULL_STRING = ""; -static Layers s_layers; -static std::list s_callbacks; - -void CallbackSystems(); - -// Only to be used with the meta-layer -class RecursiveSection final : public Section -{ -public: - RecursiveSection(LayerType layer, System system, const std::string& name) - : Section(layer, system, name) - { - } - - bool Exists(const std::string& key) const override; - - bool Get(const std::string& key, std::string* value, - const std::string& default_value = NULL_STRING) const override; - - void Set(const std::string& key, const std::string& value) override; -}; - -bool RecursiveSection::Exists(const std::string& key) const -{ - auto layers_it = s_layers.find(LayerType::Meta); - do - { - Section* layer_section = layers_it->second->GetSection(m_system, m_name); - if (layer_section && layer_section->Exists(key)) - { - return true; - } - } while (--layers_it != s_layers.end()); - - return false; -} - -bool RecursiveSection::Get(const std::string& key, std::string* value, - const std::string& default_value) const -{ - static const std::array search_order = {{ - // Skip the meta layer - LayerType::CurrentRun, LayerType::CommandLine, LayerType::Movie, LayerType::Netplay, - LayerType::LocalGame, LayerType::GlobalGame, LayerType::Base, - }}; - - for (auto layer_id : search_order) - { - auto layers_it = s_layers.find(layer_id); - if (layers_it == s_layers.end()) - continue; - - Section* layer_section = layers_it->second->GetSection(m_system, m_name); - if (layer_section && layer_section->Exists(key)) - { - return layer_section->Get(key, value, default_value); - } - } - - return Section::Get(key, value, default_value); -} - -void RecursiveSection::Set(const std::string& key, const std::string& value) -{ - // The RecursiveSection can't set since it is used to recursively get values from the layer - // map. - // It is only a part of the meta layer, and the meta layer isn't allowed to set any values. - _assert_msg_(COMMON, false, "Don't try to set values here!"); -} - -class RecursiveLayer final : public Layer -{ -public: - RecursiveLayer() : Layer(LayerType::Meta) {} - Section* GetSection(System system, const std::string& section_name) override; - Section* GetOrCreateSection(System system, const std::string& section_name) override; -}; - -Section* RecursiveLayer::GetSection(System system, const std::string& section_name) -{ - // Always queries backwards recursively, so it doesn't matter if it exists or not on this layer - return GetOrCreateSection(system, section_name); -} - -Section* RecursiveLayer::GetOrCreateSection(System system, const std::string& section_name) -{ - Section* section = Layer::GetSection(system, section_name); - if (!section) - { - m_sections[system].emplace_back(new RecursiveSection(m_layer, system, section_name)); - section = m_sections[system].back(); - } - return section; -} - -bool Section::Exists(const std::string& key) const -{ - return m_values.find(key) != m_values.end(); -} - -bool Section::Delete(const std::string& key) -{ - auto it = m_values.find(key); - if (it == m_values.end()) - return false; - - m_values.erase(it); - - m_deleted_keys.push_back(key); - m_dirty = true; - return true; -} - -void Section::Set(const std::string& key, const std::string& value) -{ - auto it = m_values.find(key); - if (it != m_values.end() && it->second != value) - { - it->second = value; - m_dirty = true; - } - else if (it == m_values.end()) - { - m_values[key] = value; - m_dirty = true; - } -} - -void Section::Set(const std::string& key, u32 newValue) -{ - Section::Set(key, StringFromFormat("0x%08x", newValue)); -} - -void Section::Set(const std::string& key, float newValue) -{ - Section::Set(key, StringFromFormat("%#.9g", newValue)); -} - -void Section::Set(const std::string& key, double newValue) -{ - Section::Set(key, StringFromFormat("%#.17g", newValue)); -} - -void Section::Set(const std::string& key, int newValue) -{ - Section::Set(key, StringFromInt(newValue)); -} - -void Section::Set(const std::string& key, bool newValue) -{ - Section::Set(key, StringFromBool(newValue)); -} - -void Section::Set(const std::string& key, const std::string& newValue, - const std::string& defaultValue) -{ - if (newValue != defaultValue) - Set(key, newValue); - else - Delete(key); -} - -void Section::SetLines(const std::vector& lines) -{ - m_lines = lines; - m_dirty = true; -} - -bool Section::Get(const std::string& key, std::string* value, - const std::string& default_value) const -{ - const auto& it = m_values.find(key); - if (it != m_values.end()) - { - *value = it->second; - return true; - } - else if (&default_value != &NULL_STRING) - { - *value = default_value; - return true; - } - - return false; -} - -bool Section::Get(const std::string& key, int* value, int defaultValue) const -{ - std::string temp; - bool retval = Get(key, &temp); - - if (retval && TryParse(temp, value)) - return true; - - *value = defaultValue; - return false; -} - -bool Section::Get(const std::string& key, u32* value, u32 defaultValue) const -{ - std::string temp; - bool retval = Get(key, &temp); - - if (retval && TryParse(temp, value)) - return true; - - *value = defaultValue; - return false; -} - -bool Section::Get(const std::string& key, bool* value, bool defaultValue) const -{ - std::string temp; - bool retval = Get(key, &temp); - - if (retval && TryParse(temp, value)) - return true; - - *value = defaultValue; - return false; -} - -bool Section::Get(const std::string& key, float* value, float defaultValue) const -{ - std::string temp; - bool retval = Get(key, &temp); - - if (retval && TryParse(temp, value)) - return true; - - *value = defaultValue; - return false; -} - -bool Section::Get(const std::string& key, double* value, double defaultValue) const -{ - std::string temp; - bool retval = Get(key, &temp); - - if (retval && TryParse(temp, value)) - return true; - - *value = defaultValue; - return false; -} - -// Return a list of all lines in a section -bool Section::GetLines(std::vector* lines, const bool remove_comments) const -{ - lines->clear(); - - for (std::string line : m_lines) - { - line = StripSpaces(line); - - if (remove_comments) - { - size_t commentPos = line.find('#'); - if (commentPos == 0) - { - continue; - } - - if (commentPos != std::string::npos) - { - line = StripSpaces(line.substr(0, commentPos)); - } - } - - lines->push_back(line); - } - - return true; -} - -// Onion layers -Layer::Layer(std::unique_ptr loader) - : m_layer(loader->GetLayer()), m_loader(std::move(loader)) -{ - Load(); -} - -Layer::~Layer() -{ - Save(); -} - -bool Layer::Exists(System system, const std::string& section_name, const std::string& key) -{ - Section* section = GetSection(system, section_name); - if (!section) - return false; - return section->Exists(key); -} - -bool Layer::DeleteKey(System system, const std::string& section_name, const std::string& key) -{ - Section* section = GetSection(system, section_name); - if (!section) - return false; - return section->Delete(key); -} - -Section* Layer::GetSection(System system, const std::string& section_name) -{ - for (Section* section : m_sections[system]) - if (!strcasecmp(section->m_name.c_str(), section_name.c_str())) - return section; - return nullptr; -} - -Section* Layer::GetOrCreateSection(System system, const std::string& section_name) -{ - Section* section = GetSection(system, section_name); - if (!section) - { - if (m_layer == LayerType::Meta) - m_sections[system].emplace_back(new RecursiveSection(m_layer, system, section_name)); - else - m_sections[system].emplace_back(new Section(m_layer, system, section_name)); - section = m_sections[system].back(); - } - return section; -} - -void Layer::Load() -{ - if (m_loader) - m_loader->Load(this); - ClearDirty(); - CallbackSystems(); -} - -void Layer::Save() -{ - if (!m_loader || !IsDirty()) - return; - - m_loader->Save(this); - ClearDirty(); - CallbackSystems(); -} - -bool Layer::IsDirty() const -{ - return std::any_of(m_sections.begin(), m_sections.end(), [](const auto& system) { - return std::any_of(system.second.begin(), system.second.end(), - [](const auto& section) { return section->IsDirty(); }); - }); -} - -void Layer::ClearDirty() -{ - std::for_each(m_sections.begin(), m_sections.end(), [](const auto& system) { - std::for_each(system.second.begin(), system.second.end(), - [](const auto& section) { section->ClearDirty(); }); - }); -} - -Section* GetOrCreateSection(System system, const std::string& section_name) -{ - return s_layers[LayerType::Meta]->GetOrCreateSection(system, section_name); -} - -Layers* GetLayers() -{ - return &s_layers; -} - -void AddLayer(std::unique_ptr layer) -{ - s_layers[layer->GetLayer()] = std::move(layer); - CallbackSystems(); -} - -void AddLayer(std::unique_ptr loader) -{ - AddLayer(std::make_unique(std::move(loader))); -} - -void AddLoadLayer(std::unique_ptr layer) -{ - layer->Load(); - AddLayer(std::move(layer)); -} - -void AddLoadLayer(std::unique_ptr loader) -{ - AddLoadLayer(std::make_unique(std::move(loader))); -} - -Layer* GetLayer(LayerType layer) -{ - if (!LayerExists(layer)) - return nullptr; - return s_layers[layer].get(); -} - -void RemoveLayer(LayerType layer) -{ - s_layers.erase(layer); - CallbackSystems(); -} -bool LayerExists(LayerType layer) -{ - return s_layers.find(layer) != s_layers.end(); -} - -void AddConfigChangedCallback(ConfigChangedCallback func) -{ - s_callbacks.emplace_back(func); -} - -void CallbackSystems() -{ - for (const auto& callback : s_callbacks) - callback(); -} - -// Explicit load and save of layers -void Load() -{ - for (auto& layer : s_layers) - layer.second->Load(); -} - -void Save() -{ - for (auto& layer : s_layers) - layer.second->Save(); -} - -void Init() -{ - // This layer always has to exist - s_layers[LayerType::Meta] = std::make_unique(); -} - -void Shutdown() -{ - s_layers.clear(); - s_callbacks.clear(); -} - -static std::map system_to_name = { - {System::Main, "Dolphin"}, {System::GCPad, "GCPad"}, {System::WiiPad, "Wiimote"}, - {System::GCKeyboard, "GCKeyboard"}, {System::GFX, "Graphics"}, {System::Logger, "Logger"}, - {System::Debugger, "Debugger"}, {System::UI, "UI"}, -}; - -const std::string& GetSystemName(System system) -{ - return system_to_name[system]; -} - -System GetSystemFromName(const std::string& system) -{ - for (auto& val : system_to_name) - if (val.second == system) - return val.first; - - _assert_msg_(COMMON, false, "Programming error! Couldn't convert '%s' to system!", - system.c_str()); - return System::Main; -} - -const std::string& GetLayerName(LayerType layer) -{ - static std::map layer_to_name = { - {LayerType::Base, "Base"}, - {LayerType::GlobalGame, "Global GameINI"}, - {LayerType::LocalGame, "Local GameINI"}, - {LayerType::Netplay, "Netplay"}, - {LayerType::Movie, "Movie"}, - {LayerType::CommandLine, "Command Line"}, - {LayerType::CurrentRun, "Current Run"}, - {LayerType::Meta, "Top"}, - }; - return layer_to_name[layer]; -} -} diff --git a/Source/Core/Common/Config.h b/Source/Core/Common/Config.h deleted file mode 100644 index cc880df5e2..0000000000 --- a/Source/Core/Common/Config.h +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2016 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include - -// XXX: Purely for case insensitive compare -#include "Common/IniFile.h" - -namespace Config -{ -enum class LayerType -{ - Base, - GlobalGame, - LocalGame, - Movie, - Netplay, - CommandLine, - CurrentRun, - Meta, -}; - -enum class System -{ - Main, - GCPad, - WiiPad, - GCKeyboard, - GFX, - Logger, - Debugger, - UI, -}; - -class Section; -class Layer; -class ConfigLayerLoader; - -using SectionValueMap = std::map; -using LayerMap = std::map>; -using Layers = std::map>; -using ConfigChangedCallback = std::function; - -class Section -{ - friend Layer; - friend ConfigLayerLoader; - -public: - Section(LayerType layer, System system, const std::string& name) - : m_layer(layer), m_system(system), m_name(name) - { - } - - virtual bool Exists(const std::string& key) const; - bool Delete(const std::string& key); - - // Setters - virtual void Set(const std::string& key, const std::string& value); - - void Set(const std::string& key, u32 newValue); - void Set(const std::string& key, float newValue); - void Set(const std::string& key, double newValue); - void Set(const std::string& key, int newValue); - void Set(const std::string& key, bool newValue); - - // Setters with default values - void Set(const std::string& key, const std::string& newValue, const std::string& defaultValue); - template - void Set(const std::string& key, T newValue, const T defaultValue) - { - if (newValue != defaultValue) - Set(key, newValue); - else - Delete(key); - } - - // Getters - virtual bool Get(const std::string& key, std::string* value, - const std::string& default_value = NULL_STRING) const; - - bool Get(const std::string& key, int* value, int defaultValue = 0) const; - bool Get(const std::string& key, u32* value, u32 defaultValue = 0) const; - bool Get(const std::string& key, bool* value, bool defaultValue = false) const; - bool Get(const std::string& key, float* value, float defaultValue = 0.0f) const; - bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; - - template - T Get(const std::string& key, const T& default_value) const - { - T value; - Get(key, value, default_value); - return value; - } - - // Section chunk - void SetLines(const std::vector& lines); - // XXX: Add to recursive layer - virtual bool GetLines(std::vector* lines, const bool remove_comments = true) const; - virtual bool HasLines() const { return m_lines.size() > 0; } - const std::string& GetName() const { return m_name; } - const SectionValueMap& GetValues() const { return m_values; } - const std::vector& GetDeletedKeys() const { return m_deleted_keys; } - bool IsDirty() const { return m_dirty; } - void ClearDirty() { m_dirty = false; } -protected: - bool m_dirty = false; - - LayerType m_layer; - System m_system; - const std::string m_name; - static const std::string& NULL_STRING; - - SectionValueMap m_values; - std::vector m_deleted_keys; - - std::vector m_lines; -}; - -// XXX: Allow easy migration! -class ConfigLayerLoader -{ -public: - ConfigLayerLoader(LayerType layer) : m_layer(layer) {} - ~ConfigLayerLoader() {} - virtual void Load(Layer* config_layer) = 0; - virtual void Save(Layer* config_layer) = 0; - - LayerType GetLayer() const { return m_layer; } -private: - const LayerType m_layer; -}; - -class Layer -{ -public: - explicit Layer(LayerType layer) : m_layer(layer) {} - Layer(std::unique_ptr loader); - virtual ~Layer(); - - // Convenience functions - bool Exists(System system, const std::string& section_name, const std::string& key); - bool DeleteKey(System system, const std::string& section_name, const std::string& key); - template - bool GetIfExists(System system, const std::string& section_name, const std::string& key, T* value) - { - if (Exists(system, section_name, key)) - return GetOrCreateSection(system, section_name)->Get(key, value); - - return false; - } - - virtual Section* GetSection(System system, const std::string& section_name); - virtual Section* GetOrCreateSection(System system, const std::string& section_name); - - // Explicit load and save of layers - void Load(); - void Save(); - - LayerType GetLayer() const { return m_layer; } - const LayerMap& GetLayerMap() { return m_sections; } - // Stay away from this routine as much as possible - ConfigLayerLoader* GetLoader() { return m_loader.get(); } -protected: - bool IsDirty() const; - void ClearDirty(); - - LayerMap m_sections; - const LayerType m_layer; - std::unique_ptr m_loader; -}; - -// Common function used for getting configuration -Section* GetOrCreateSection(System system, const std::string& section_name); - -// Layer management -Layers* GetLayers(); -void AddLayer(std::unique_ptr layer); -void AddLayer(std::unique_ptr loader); -void AddLoadLayer(std::unique_ptr layer); -void AddLoadLayer(std::unique_ptr loader); -Layer* GetLayer(LayerType layer); -void RemoveLayer(LayerType layer); -bool LayerExists(LayerType layer); - -void AddConfigChangedCallback(ConfigChangedCallback func); - -// Explicit load and save of layers -void Load(); -void Save(); - -// Often used functions for getting or setting configuration on the base layer for the main system -template -T Get(const std::string& section_name, const std::string& key, const T& default_value) -{ - auto base_layer = GetLayer(Config::LayerType::Base); - return base_layer->GetOrCreateSection(Config::System::Main, section_name) - ->Get(key, default_value); -} - -template -void Set(const std::string& section_name, const std::string& key, const T& value) -{ - auto base_layer = GetLayer(Config::LayerType::Base); - base_layer->GetOrCreateSection(Config::System::Main, section_name)->Set(key, value); -} - -void Init(); -void Shutdown(); - -const std::string& GetSystemName(System system); -System GetSystemFromName(const std::string& system); -const std::string& GetLayerName(LayerType layer); -} diff --git a/Source/Core/Common/Config/Config.cpp b/Source/Core/Common/Config/Config.cpp new file mode 100644 index 0000000000..bcfb9a2619 --- /dev/null +++ b/Source/Core/Common/Config/Config.cpp @@ -0,0 +1,140 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Assert.h" +#include "Common/Config/Config.h" + +namespace Config +{ +static Layers s_layers; +static std::list s_callbacks; + +void InvokeConfigChangedCallbacks(); + +Section* GetOrCreateSection(System system, const std::string& section_name) +{ + return s_layers[LayerType::Meta]->GetOrCreateSection(system, section_name); +} + +Layers* GetLayers() +{ + return &s_layers; +} + +void AddLayer(std::unique_ptr layer) +{ + s_layers[layer->GetLayer()] = std::move(layer); + InvokeConfigChangedCallbacks(); +} + +void AddLayer(std::unique_ptr loader) +{ + AddLayer(std::make_unique(std::move(loader))); +} + +void AddLoadLayer(std::unique_ptr layer) +{ + layer->Load(); + AddLayer(std::move(layer)); +} + +void AddLoadLayer(std::unique_ptr loader) +{ + AddLoadLayer(std::make_unique(std::move(loader))); +} + +Layer* GetLayer(LayerType layer) +{ + if (!LayerExists(layer)) + return nullptr; + return s_layers[layer].get(); +} + +void RemoveLayer(LayerType layer) +{ + s_layers.erase(layer); + InvokeConfigChangedCallbacks(); +} +bool LayerExists(LayerType layer) +{ + return s_layers.find(layer) != s_layers.end(); +} + +void AddConfigChangedCallback(ConfigChangedCallback func) +{ + s_callbacks.emplace_back(func); +} + +void InvokeConfigChangedCallbacks() +{ + for (const auto& callback : s_callbacks) + callback(); +} + +// Explicit load and save of layers +void Load() +{ + for (auto& layer : s_layers) + layer.second->Load(); +} + +void Save() +{ + for (auto& layer : s_layers) + layer.second->Save(); +} + +void Init() +{ + // This layer always has to exist + s_layers[LayerType::Meta] = std::make_unique(); +} + +void Shutdown() +{ + s_layers.clear(); + s_callbacks.clear(); +} + +static const std::map system_to_name = { + {System::Main, "Dolphin"}, {System::GCPad, "GCPad"}, {System::WiiPad, "Wiimote"}, + {System::GCKeyboard, "GCKeyboard"}, {System::GFX, "Graphics"}, {System::Logger, "Logger"}, + {System::Debugger, "Debugger"}, {System::UI, "UI"}, +}; + +const std::string& GetSystemName(System system) +{ + return system_to_name.at(system); +} + +System GetSystemFromName(const std::string& name) +{ + const auto system = std::find_if(system_to_name.begin(), system_to_name.end(), + [&name](const auto& entry) { return entry.second == name; }); + if (system != system_to_name.end()) + return system->first; + + _assert_msg_(COMMON, false, "Programming error! Couldn't convert '%s' to system!", name.c_str()); + return System::Main; +} + +const std::string& GetLayerName(LayerType layer) +{ + static const std::map layer_to_name = { + {LayerType::Base, "Base"}, + {LayerType::GlobalGame, "Global GameINI"}, + {LayerType::LocalGame, "Local GameINI"}, + {LayerType::Netplay, "Netplay"}, + {LayerType::Movie, "Movie"}, + {LayerType::CommandLine, "Command Line"}, + {LayerType::CurrentRun, "Current Run"}, + {LayerType::Meta, "Top"}, + }; + return layer_to_name.at(layer); +} +} diff --git a/Source/Core/Common/Config/Config.h b/Source/Core/Common/Config/Config.h new file mode 100644 index 0000000000..a46cd0de2d --- /dev/null +++ b/Source/Core/Common/Config/Config.h @@ -0,0 +1,63 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "Common/Config/Enums.h" +#include "Common/Config/Layer.h" +#include "Common/Config/Section.h" + +namespace Config +{ +using Layers = std::map>; +using ConfigChangedCallback = std::function; + +// Common function used for getting configuration +Section* GetOrCreateSection(System system, const std::string& section_name); + +// Layer management +Layers* GetLayers(); +void AddLayer(std::unique_ptr layer); +void AddLayer(std::unique_ptr loader); +void AddLoadLayer(std::unique_ptr layer); +void AddLoadLayer(std::unique_ptr loader); +Layer* GetLayer(LayerType layer); +void RemoveLayer(LayerType layer); +bool LayerExists(LayerType layer); + +void AddConfigChangedCallback(ConfigChangedCallback func); +void InvokeConfigChangedCallbacks(); + +// Explicit load and save of layers +void Load(); +void Save(); + +// Often used functions for getting or setting configuration on the base layer for the main system +template +T Get(const std::string& section_name, const std::string& key, const T& default_value) +{ + auto base_layer = GetLayer(Config::LayerType::Base); + return base_layer->GetOrCreateSection(Config::System::Main, section_name) + ->Get(key, default_value); +} + +template +void Set(const std::string& section_name, const std::string& key, const T& value) +{ + auto base_layer = GetLayer(Config::LayerType::Base); + base_layer->GetOrCreateSection(Config::System::Main, section_name)->Set(key, value); +} + +void Init(); +void Shutdown(); + +const std::string& GetSystemName(System system); +System GetSystemFromName(const std::string& system); +const std::string& GetLayerName(LayerType layer); +} diff --git a/Source/Core/Common/Config/Enums.h b/Source/Core/Common/Config/Enums.h new file mode 100644 index 0000000000..d5f866ff1e --- /dev/null +++ b/Source/Core/Common/Config/Enums.h @@ -0,0 +1,32 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +namespace Config +{ +enum class LayerType +{ + Base, + GlobalGame, + LocalGame, + Movie, + Netplay, + CommandLine, + CurrentRun, + Meta, +}; + +enum class System +{ + Main, + GCPad, + WiiPad, + GCKeyboard, + GFX, + Logger, + Debugger, + UI, +}; +} diff --git a/Source/Core/Common/Config/Layer.cpp b/Source/Core/Common/Config/Layer.cpp new file mode 100644 index 0000000000..ee5d5e2ad6 --- /dev/null +++ b/Source/Core/Common/Config/Layer.cpp @@ -0,0 +1,148 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Config/Config.h" +#include "Common/Config/Layer.h" +#include "Common/Config/Section.h" + +namespace Config +{ +ConfigLayerLoader::ConfigLayerLoader(LayerType layer) : m_layer(layer) +{ +} + +ConfigLayerLoader::~ConfigLayerLoader() = default; + +LayerType ConfigLayerLoader::GetLayer() const +{ + return m_layer; +} + +Layer::Layer(LayerType type) : m_layer(type) +{ +} + +Layer::Layer(std::unique_ptr loader) + : m_layer(loader->GetLayer()), m_loader(std::move(loader)) +{ + Load(); +} + +Layer::~Layer() +{ + Save(); +} + +bool Layer::Exists(System system, const std::string& section_name, const std::string& key) +{ + Section* section = GetSection(system, section_name); + if (!section) + return false; + return section->Exists(key); +} + +bool Layer::DeleteKey(System system, const std::string& section_name, const std::string& key) +{ + Section* section = GetSection(system, section_name); + if (!section) + return false; + return section->Delete(key); +} + +Section* Layer::GetSection(System system, const std::string& section_name) +{ + for (auto& section : m_sections[system]) + if (!strcasecmp(section.m_name.c_str(), section_name.c_str())) + return §ion; + return nullptr; +} + +Section* Layer::GetOrCreateSection(System system, const std::string& section_name) +{ + Section* section = GetSection(system, section_name); + if (!section) + { + if (m_layer == LayerType::Meta) + m_sections[system].emplace_back(RecursiveSection(m_layer, system, section_name)); + else + m_sections[system].emplace_back(Section(m_layer, system, section_name)); + section = &m_sections[system].back(); + } + return section; +} + +void Layer::Load() +{ + if (m_loader) + m_loader->Load(this); + ClearDirty(); + InvokeConfigChangedCallbacks(); +} + +void Layer::Save() +{ + if (!m_loader || !IsDirty()) + return; + + m_loader->Save(this); + ClearDirty(); + InvokeConfigChangedCallbacks(); +} + +LayerType Layer::GetLayer() const +{ + return m_layer; +} + +const LayerMap& Layer::GetLayerMap() const +{ + return m_sections; +} + +ConfigLayerLoader* Layer::GetLoader() const +{ + return m_loader.get(); +} + +bool Layer::IsDirty() const +{ + return std::any_of(m_sections.begin(), m_sections.end(), [](const auto& system) { + return std::any_of(system.second.begin(), system.second.end(), + [](const auto& section) { return section.IsDirty(); }); + }); +} + +void Layer::ClearDirty() +{ + std::for_each(m_sections.begin(), m_sections.end(), [](auto& system) { + std::for_each(system.second.begin(), system.second.end(), + [](auto& section) { section.ClearDirty(); }); + }); +} + +RecursiveLayer::RecursiveLayer() : Layer(LayerType::Meta) +{ +} + +Section* RecursiveLayer::GetSection(System system, const std::string& section_name) +{ + // Always queries backwards recursively, so it doesn't matter if it exists or not on this layer + return GetOrCreateSection(system, section_name); +} + +Section* RecursiveLayer::GetOrCreateSection(System system, const std::string& section_name) +{ + Section* section = Layer::GetSection(system, section_name); + if (!section) + { + m_sections[system].emplace_back(RecursiveSection(m_layer, system, section_name)); + section = &m_sections[system].back(); + } + return section; +} +} diff --git a/Source/Core/Common/Config/Layer.h b/Source/Core/Common/Config/Layer.h new file mode 100644 index 0000000000..ade534ce02 --- /dev/null +++ b/Source/Core/Common/Config/Layer.h @@ -0,0 +1,80 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +#include "Common/Config/Enums.h" +#include "Common/Config/Section.h" + +namespace Config +{ +using LayerMap = std::map>; + +class ConfigLayerLoader +{ +public: + explicit ConfigLayerLoader(LayerType layer); + virtual ~ConfigLayerLoader(); + virtual void Load(Layer* config_layer) = 0; + virtual void Save(Layer* config_layer) = 0; + + LayerType GetLayer() const; + +private: + const LayerType m_layer; +}; + +class Layer +{ +public: + explicit Layer(LayerType layer); + explicit Layer(std::unique_ptr loader); + virtual ~Layer(); + + // Convenience functions + bool Exists(System system, const std::string& section_name, const std::string& key); + bool DeleteKey(System system, const std::string& section_name, const std::string& key); + template + bool GetIfExists(System system, const std::string& section_name, const std::string& key, T* value) + { + if (Exists(system, section_name, key)) + return GetOrCreateSection(system, section_name)->Get(key, value); + + return false; + } + + virtual Section* GetSection(System system, const std::string& section_name); + virtual Section* GetOrCreateSection(System system, const std::string& section_name); + + // Explicit load and save of layers + void Load(); + void Save(); + + LayerType GetLayer() const; + const LayerMap& GetLayerMap() const; + // Stay away from this routine as much as possible + ConfigLayerLoader* GetLoader() const; + +protected: + bool IsDirty() const; + void ClearDirty(); + + LayerMap m_sections; + const LayerType m_layer; + std::unique_ptr m_loader; +}; + +class RecursiveLayer final : public Layer +{ +public: + RecursiveLayer(); + Section* GetSection(System system, const std::string& section_name) override; + Section* GetOrCreateSection(System system, const std::string& section_name) override; +}; +} diff --git a/Source/Core/Common/Config/Section.cpp b/Source/Core/Common/Config/Section.cpp new file mode 100644 index 0000000000..5c7f52eb63 --- /dev/null +++ b/Source/Core/Common/Config/Section.cpp @@ -0,0 +1,286 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/Assert.h" +#include "Common/Config/Config.h" +#include "Common/Config/Layer.h" +#include "Common/Config/Section.h" +#include "Common/StringUtil.h" + +namespace Config +{ +const std::string& Section::NULL_STRING = ""; + +Section::Section(LayerType layer, System system, const std::string& name) + : m_layer(layer), m_system(system), m_name(name) +{ +} + +bool Section::Exists(const std::string& key) const +{ + return m_values.find(key) != m_values.end(); +} + +bool Section::Delete(const std::string& key) +{ + auto it = m_values.find(key); + if (it == m_values.end()) + return false; + + m_values.erase(it); + + m_deleted_keys.push_back(key); + m_dirty = true; + return true; +} + +void Section::Set(const std::string& key, const std::string& value) +{ + auto it = m_values.find(key); + if (it != m_values.end() && it->second != value) + { + it->second = value; + m_dirty = true; + } + else if (it == m_values.end()) + { + m_values[key] = value; + m_dirty = true; + } +} + +void Section::Set(const std::string& key, u32 newValue) +{ + Section::Set(key, StringFromFormat("0x%08x", newValue)); +} + +void Section::Set(const std::string& key, float newValue) +{ + Section::Set(key, StringFromFormat("%#.9g", newValue)); +} + +void Section::Set(const std::string& key, double newValue) +{ + Section::Set(key, StringFromFormat("%#.17g", newValue)); +} + +void Section::Set(const std::string& key, int newValue) +{ + Section::Set(key, StringFromInt(newValue)); +} + +void Section::Set(const std::string& key, bool newValue) +{ + Section::Set(key, StringFromBool(newValue)); +} + +void Section::Set(const std::string& key, const std::string& newValue, + const std::string& defaultValue) +{ + if (newValue != defaultValue) + Set(key, newValue); + else + Delete(key); +} + +void Section::SetLines(const std::vector& lines) +{ + m_lines = lines; + m_dirty = true; +} + +bool Section::Get(const std::string& key, std::string* value, + const std::string& default_value) const +{ + const auto& it = m_values.find(key); + if (it != m_values.end()) + { + *value = it->second; + return true; + } + else if (&default_value != &NULL_STRING) + { + *value = default_value; + return true; + } + + return false; +} + +bool Section::Get(const std::string& key, int* value, int defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, u32* value, u32 defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, bool* value, bool defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, float* value, float defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +bool Section::Get(const std::string& key, double* value, double defaultValue) const +{ + std::string temp; + bool retval = Get(key, &temp); + + if (retval && TryParse(temp, value)) + return true; + + *value = defaultValue; + return false; +} + +// Return a list of all lines in a section +bool Section::GetLines(std::vector* lines, const bool remove_comments) const +{ + lines->clear(); + + for (std::string line : m_lines) + { + line = StripSpaces(line); + + if (remove_comments) + { + size_t commentPos = line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + } + + lines->push_back(line); + } + + return true; +} + +bool Section::HasLines() const +{ + return !m_lines.empty(); +} + +const std::string& Section::GetName() const +{ + return m_name; +} + +const SectionValueMap& Section::GetValues() const +{ + return m_values; +} + +const std::vector& Section::GetDeletedKeys() const +{ + return m_deleted_keys; +} + +bool Section::IsDirty() const +{ + return m_dirty; +} +void Section::ClearDirty() +{ + m_dirty = false; +} + +RecursiveSection::RecursiveSection(LayerType layer, System system, const std::string& name) + : Section(layer, system, name) +{ +} + +bool RecursiveSection::Exists(const std::string& key) const +{ + auto layers_it = Config::GetLayers()->find(LayerType::Meta); + do + { + const Section* layer_section = layers_it->second->GetSection(m_system, m_name); + if (layer_section && layer_section->Exists(key)) + { + return true; + } + } while (--layers_it != Config::GetLayers()->end()); + + return false; +} + +bool RecursiveSection::Get(const std::string& key, std::string* value, + const std::string& default_value) const +{ + static constexpr std::array search_order = {{ + // Skip the meta layer + LayerType::CurrentRun, LayerType::CommandLine, LayerType::Movie, LayerType::Netplay, + LayerType::LocalGame, LayerType::GlobalGame, LayerType::Base, + }}; + + for (auto layer_id : search_order) + { + auto layers_it = Config::GetLayers()->find(layer_id); + if (layers_it == Config::GetLayers()->end()) + continue; + + const Section* layer_section = layers_it->second->GetSection(m_system, m_name); + if (layer_section && layer_section->Exists(key)) + { + return layer_section->Get(key, value, default_value); + } + } + + return Section::Get(key, value, default_value); +} + +void RecursiveSection::Set(const std::string& key, const std::string& value) +{ + // The RecursiveSection can't set since it is used to recursively get values from the layer + // map. + // It is only a part of the meta layer, and the meta layer isn't allowed to set any values. + _assert_msg_(COMMON, false, "Don't try to set values here!"); +} +} diff --git a/Source/Core/Common/Config/Section.h b/Source/Core/Common/Config/Section.h new file mode 100644 index 0000000000..4b4ba263ad --- /dev/null +++ b/Source/Core/Common/Config/Section.h @@ -0,0 +1,110 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +// XXX: Purely for case insensitive compare +#include "Common/CommonTypes.h" +#include "Common/Config/Enums.h" +#include "Common/IniFile.h" + +namespace Config +{ +class Layer; +class ConfigLayerLoader; + +using SectionValueMap = std::map; + +class Section +{ + friend Layer; + friend ConfigLayerLoader; + +public: + Section(LayerType layer, System system, const std::string& name); + + virtual bool Exists(const std::string& key) const; + bool Delete(const std::string& key); + + // Setters + virtual void Set(const std::string& key, const std::string& value); + + void Set(const std::string& key, u32 newValue); + void Set(const std::string& key, float newValue); + void Set(const std::string& key, double newValue); + void Set(const std::string& key, int newValue); + void Set(const std::string& key, bool newValue); + + // Setters with default values + void Set(const std::string& key, const std::string& newValue, const std::string& defaultValue); + template + void Set(const std::string& key, T newValue, const T defaultValue) + { + if (newValue != defaultValue) + Set(key, newValue); + else + Delete(key); + } + + // Getters + virtual bool Get(const std::string& key, std::string* value, + const std::string& default_value = NULL_STRING) const; + + bool Get(const std::string& key, int* value, int defaultValue = 0) const; + bool Get(const std::string& key, u32* value, u32 defaultValue = 0) const; + bool Get(const std::string& key, bool* value, bool defaultValue = false) const; + bool Get(const std::string& key, float* value, float defaultValue = 0.0f) const; + bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; + + template + T Get(const std::string& key, const T& default_value) const + { + T value; + Get(key, value, default_value); + return value; + } + + // Section chunk + void SetLines(const std::vector& lines); + // XXX: Add to recursive layer + virtual bool GetLines(std::vector* lines, const bool remove_comments = true) const; + virtual bool HasLines() const; + const std::string& GetName() const; + const SectionValueMap& GetValues() const; + const std::vector& GetDeletedKeys() const; + bool IsDirty() const; + void ClearDirty(); + +protected: + bool m_dirty = false; + + LayerType m_layer; + System m_system; + const std::string m_name; + static const std::string& NULL_STRING; + + SectionValueMap m_values; + std::vector m_deleted_keys; + + std::vector m_lines; +}; + +// Only to be used with the meta-layer +class RecursiveSection final : public Section +{ +public: + RecursiveSection(LayerType layer, System system, const std::string& name); + + bool Exists(const std::string& key) const override; + + bool Get(const std::string& key, std::string* value, + const std::string& default_value = NULL_STRING) const override; + + void Set(const std::string& key, const std::string& value) override; +}; +}