diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 5f910a1670..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 diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 831dcad4a7..756c30d473 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -53,6 +53,10 @@ + + + + @@ -149,6 +153,9 @@ + + + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 3c9b952742..4ffd6c0575 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -34,6 +34,10 @@ + + + + @@ -232,6 +236,9 @@ + + + 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; +}; +}