From 8360e358ee56ef5cef9aba78a57b7d0ae358ed01 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 14 Jan 2016 16:36:11 -0600 Subject: [PATCH] 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); +}