Merge pull request #4917 from leoetlino/config

New configuration namespace
This commit is contained in:
Anthony 2017-02-26 15:54:25 -08:00 committed by GitHub
commit 0dde642b7d
10 changed files with 876 additions and 0 deletions

View File

@ -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

View File

@ -53,6 +53,10 @@
<ClInclude Include="CommonFuncs.h" />
<ClInclude Include="CommonPaths.h" />
<ClInclude Include="CommonTypes.h" />
<ClInclude Include="Config\Config.h" />
<ClInclude Include="Config\Enums.h" />
<ClInclude Include="Config\Layer.h" />
<ClInclude Include="Config\Section.h" />
<ClInclude Include="CPUDetect.h" />
<ClInclude Include="DebugInterface.h" />
<ClInclude Include="ENetUtil.h" />
@ -149,6 +153,9 @@
<ClCompile Include="Analytics.cpp" />
<ClCompile Include="CDUtils.cpp" />
<ClCompile Include="ColorUtil.cpp" />
<ClCompile Include="Config\Config.cpp" />
<ClCompile Include="Config\Layer.cpp" />
<ClCompile Include="Config\Section.cpp" />
<ClCompile Include="ENetUtil.cpp" />
<ClCompile Include="FileSearch.cpp" />
<ClCompile Include="FileUtil.cpp" />

View File

@ -34,6 +34,10 @@
<ClInclude Include="CommonFuncs.h" />
<ClInclude Include="CommonPaths.h" />
<ClInclude Include="CommonTypes.h" />
<ClInclude Include="Config\Config.h" />
<ClInclude Include="Config\Enums.h" />
<ClInclude Include="Config\Layer.h" />
<ClInclude Include="Config\Section.h" />
<ClInclude Include="CPUDetect.h" />
<ClInclude Include="DebugInterface.h" />
<ClInclude Include="ENetUtil.h" />
@ -232,6 +236,9 @@
<ItemGroup>
<ClCompile Include="CDUtils.cpp" />
<ClCompile Include="ColorUtil.cpp" />
<ClCompile Include="Config\Config.cpp" />
<ClCompile Include="Config\Layer.cpp" />
<ClCompile Include="Config\Section.cpp" />
<ClCompile Include="ENetUtil.cpp" />
<ClCompile Include="FileSearch.cpp" />
<ClCompile Include="FileUtil.cpp" />

View File

@ -0,0 +1,140 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <list>
#include <map>
#include "Common/Assert.h"
#include "Common/Config/Config.h"
namespace Config
{
static Layers s_layers;
static std::list<ConfigChangedCallback> 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> layer)
{
s_layers[layer->GetLayer()] = std::move(layer);
InvokeConfigChangedCallbacks();
}
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader)
{
AddLayer(std::make_unique<Layer>(std::move(loader)));
}
void AddLoadLayer(std::unique_ptr<Layer> layer)
{
layer->Load();
AddLayer(std::move(layer));
}
void AddLoadLayer(std::unique_ptr<ConfigLayerLoader> loader)
{
AddLoadLayer(std::make_unique<Layer>(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<RecursiveLayer>();
}
void Shutdown()
{
s_layers.clear();
s_callbacks.clear();
}
static const std::map<System, std::string> 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<LayerType, std::string> 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);
}
}

View File

@ -0,0 +1,63 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <string>
#include "Common/Config/Enums.h"
#include "Common/Config/Layer.h"
#include "Common/Config/Section.h"
namespace Config
{
using Layers = std::map<LayerType, std::unique_ptr<Layer>>;
using ConfigChangedCallback = std::function<void()>;
// 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> layer);
void AddLayer(std::unique_ptr<ConfigLayerLoader> loader);
void AddLoadLayer(std::unique_ptr<Layer> layer);
void AddLoadLayer(std::unique_ptr<ConfigLayerLoader> 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 <typename T>
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 <typename T>
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);
}

View File

@ -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,
};
}

View File

@ -0,0 +1,148 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <map>
#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<ConfigLayerLoader> 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 &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(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;
}
}

View File

@ -0,0 +1,80 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "Common/Config/Enums.h"
#include "Common/Config/Section.h"
namespace Config
{
using LayerMap = std::map<System, std::vector<Section>>;
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<ConfigLayerLoader> 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 <typename T>
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<ConfigLayerLoader> 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;
};
}

View File

@ -0,0 +1,286 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <array>
#include <cstddef>
#include <map>
#include <memory>
#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<std::string>& 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<std::string>* 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<std::string>& 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<LayerType, 7> 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!");
}
}

View File

@ -0,0 +1,110 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <string>
#include <vector>
// 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<std::string, std::string, CaseInsensitiveStringCompare>;
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 <typename T>
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 <typename T>
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<std::string>& lines);
// XXX: Add to recursive layer
virtual bool GetLines(std::vector<std::string>* lines, const bool remove_comments = true) const;
virtual bool HasLines() const;
const std::string& GetName() const;
const SectionValueMap& GetValues() const;
const std::vector<std::string>& 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<std::string> m_deleted_keys;
std::vector<std::string> 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;
};
}