rpcs3/Utilities/Config.cpp

311 lines
5.5 KiB
C++
Raw Normal View History

2016-02-01 21:55:43 +00:00
#include "stdafx.h"
#include "Config.h"
#include "yaml-cpp/yaml.h"
namespace cfg
{
2017-05-13 18:30:37 +00:00
logs::channel cfg("CFG");
2016-02-01 21:55:43 +00:00
2017-05-20 11:45:02 +00:00
_base::_base(type _type)
2016-02-01 21:55:43 +00:00
: m_type(_type)
{
if (_type != type::node)
{
fmt::throw_exception<std::logic_error>("Invalid root node" HERE);
2016-02-01 21:55:43 +00:00
}
}
2017-05-20 11:45:02 +00:00
_base::_base(type _type, node* owner, const std::string& name)
2016-02-01 21:55:43 +00:00
: m_type(_type)
{
2017-05-20 11:45:02 +00:00
for (const auto& pair : owner->m_nodes)
2016-02-01 21:55:43 +00:00
{
2017-05-20 11:45:02 +00:00
if (pair.first == name)
{
fmt::throw_exception<std::logic_error>("Node already exists: %s" HERE, name);
}
2016-02-01 21:55:43 +00:00
}
2017-05-20 11:45:02 +00:00
owner->m_nodes.emplace_back(name, this);
}
2017-05-20 11:45:02 +00:00
bool _base::from_string(const std::string&)
{
fmt::throw_exception<std::logic_error>("from_string() purecall" HERE);
}
2017-05-20 11:45:02 +00:00
bool _base::from_list(std::vector<std::string>&&)
{
fmt::throw_exception<std::logic_error>("from_list() purecall" HERE);
2016-02-01 21:55:43 +00:00
}
// Emit YAML
2017-05-20 11:45:02 +00:00
static void encode(YAML::Emitter& out, const class _base& rhs);
2016-02-01 21:55:43 +00:00
// Incrementally load config entries from YAML::Node.
// The config value is preserved if the corresponding YAML node doesn't exist.
2017-05-20 11:45:02 +00:00
static void decode(const YAML::Node& data, class _base& rhs);
2016-02-01 21:55:43 +00:00
}
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
{
// TODO: this could be rewritten without exceptions (but it should be as safe as possible and provide logs)
s64 result;
std::size_t pos;
try
{
result = std::stoll(value, &pos, 0 /* Auto-detect numeric base */);
}
catch (const std::exception& e)
{
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
return false;
}
if (pos != value.size())
{
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
return false;
}
if (result < min || result > max)
{
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
return false;
}
if (out) *out = result;
return true;
}
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
{
for (u64 i = 0;; i++)
{
std::string var;
func(var, i);
if (var == value)
{
if (out) *out = i;
return true;
}
std::string hex;
fmt_class_string<u64>::format(hex, i);
if (var == hex)
{
break;
}
}
try
{
const auto val = std::stoull(value, nullptr, 0);
if (out) *out = val;
return true;
}
catch (...)
{
return false;
}
}
std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::format) func)
{
std::vector<std::string> result;
for (u64 i = 0;; i++)
{
std::string var;
func(var, i);
std::string hex;
fmt_class_string<u64>::format(hex, i);
if (var == hex)
{
break;
}
result.emplace_back(std::move(var));
}
return result;
}
2017-05-20 11:45:02 +00:00
void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
2016-02-01 21:55:43 +00:00
{
switch (rhs.get_type())
{
case type::node:
{
out << YAML::BeginMap;
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
{
out << YAML::Key << np.first;
2017-05-13 18:30:37 +00:00
out << YAML::Value;
encode(out, *np.second);
2016-02-01 21:55:43 +00:00
}
out << YAML::EndMap;
return;
}
case type::set:
{
out << YAML::BeginSeq;
for (const auto& str : static_cast<const set_entry&>(rhs).get_set())
{
out << str;
}
out << YAML::EndSeq;
return;
}
2017-05-13 18:30:37 +00:00
case type::log:
{
out << YAML::BeginMap;
for (const auto& np : static_cast<const log_entry&>(rhs).get_map())
{
if (np.second == logs::level::notice) continue;
out << YAML::Key << np.first;
out << YAML::Value << fmt::format("%s", np.second);
}
out << YAML::EndMap;
return;
}
2016-02-01 21:55:43 +00:00
}
out << rhs.to_string();
}
2017-05-20 11:45:02 +00:00
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
2016-02-01 21:55:43 +00:00
{
switch (rhs.get_type())
{
case type::node:
{
if (data.IsScalar() || data.IsSequence())
{
return; // ???
}
for (const auto& pair : data)
{
if (!pair.first.IsScalar()) continue;
// Find the key among existing nodes
2017-05-20 11:45:02 +00:00
for (const auto& _pair : static_cast<node&>(rhs).get_nodes())
2016-02-01 21:55:43 +00:00
{
2017-05-20 11:45:02 +00:00
if (_pair.first == pair.first.Scalar())
{
decode(pair.second, *_pair.second);
}
2016-02-01 21:55:43 +00:00
}
}
break;
}
case type::set:
{
std::vector<std::string> values;
if (YAML::convert<decltype(values)>::decode(data, values))
{
rhs.from_list(std::move(values));
}
break;
}
2017-05-13 18:30:37 +00:00
case type::log:
{
if (data.IsScalar() || data.IsSequence())
{
return; // ???
}
std::map<std::string, logs::level> values;
for (const auto& pair : data)
{
if (!pair.first.IsScalar() || !pair.second.IsScalar()) continue;
u64 value;
if (cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar()))
{
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
}
}
static_cast<log_entry&>(rhs).set_map(std::move(values));
break;
}
2016-02-01 21:55:43 +00:00
default:
{
std::string value;
if (YAML::convert<std::string>::decode(data, value))
{
rhs.from_string(value);
}
break; // ???
}
}
}
std::string cfg::node::to_string() const
{
YAML::Emitter out;
cfg::encode(out, *this);
return{ out.c_str(), out.size() };
}
bool cfg::node::from_string(const std::string& value)
{
cfg::decode(YAML::Load(value), *this);
return true;
}
void cfg::node::from_default()
{
for (auto& node : m_nodes)
{
node.second->from_default();
}
}
2017-05-20 11:45:02 +00:00
void cfg::_bool::from_default()
2017-01-24 23:22:19 +00:00
{
2017-05-20 11:45:02 +00:00
m_value = def;
2017-01-24 23:22:19 +00:00
}
2017-05-20 11:45:02 +00:00
void cfg::string::from_default()
2017-01-24 23:22:19 +00:00
{
2017-05-20 11:45:02 +00:00
m_value = def;
2017-01-24 23:22:19 +00:00
}
void cfg::set_entry::from_default()
{
m_set = {};
}
2017-05-13 18:30:37 +00:00
void cfg::log_entry::set_map(std::map<std::string, logs::level>&& map)
{
logs::reset();
for (auto&& pair : (m_map = std::move(map)))
{
logs::set_level(pair.first, pair.second);
}
}
void cfg::log_entry::from_default()
{
set_map({});
}