mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-07 15:36:26 +00:00
split rpcs3 and hle libraries
merge rpcs3 utilities
This commit is contained in:
parent
b33e2662b6
commit
62ad27d1e2
1233 changed files with 7004 additions and 3819 deletions
721
rpcs3/util/Config.cpp
Normal file
721
rpcs3/util/Config.cpp
Normal file
|
|
@ -0,0 +1,721 @@
|
|||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
template <>
|
||||
void fmt_class_string<cfg::node>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg).to_string();
|
||||
}
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
u32 _base::id_counter = 0;
|
||||
|
||||
_base::_base(type _type)
|
||||
: m_type(_type), m_id(id_counter++)
|
||||
{
|
||||
if (_type != type::node)
|
||||
{
|
||||
cfg_log.fatal("Invalid root node");
|
||||
}
|
||||
}
|
||||
|
||||
_base::_base(type _type, node* owner, std::string name, bool dynamic)
|
||||
: m_type(_type), m_parent(owner), m_dynamic(dynamic), m_name(std::move(name)), m_id(id_counter++)
|
||||
{
|
||||
for (const auto& node : owner->m_nodes)
|
||||
{
|
||||
if (node->get_name() == m_name)
|
||||
{
|
||||
cfg_log.fatal("Node already exists: %s", m_name);
|
||||
}
|
||||
}
|
||||
|
||||
owner->m_nodes.emplace_back(this);
|
||||
}
|
||||
|
||||
bool _base::from_string(std::string_view, bool)
|
||||
{
|
||||
cfg_log.fatal("cfg::_base::from_string() purecall");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _base::from_list(std::vector<std::string>&&)
|
||||
{
|
||||
cfg_log.fatal("cfg::_base::from_list() purecall");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _base::save(std::string_view cfg_name) const
|
||||
{
|
||||
if (fs::pending_file cfg_file(cfg_name); !!cfg_file.file)
|
||||
{
|
||||
cfg_file.file.write(to_string());
|
||||
return cfg_file.commit();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emit YAML
|
||||
static void encode(YAML::Emitter& out, const class _base& rhs);
|
||||
|
||||
// Incrementally load config entries from YAML::Node.
|
||||
// The config value is preserved if the corresponding YAML node doesn't exist.
|
||||
static void decode(const YAML::Node& data, class _base& rhs, bool dynamic = false);
|
||||
} // namespace cfg
|
||||
|
||||
std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||
{
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_int64(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
s64 result;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
int sign = +1;
|
||||
|
||||
if (start[0] == '-')
|
||||
{
|
||||
sign = -1;
|
||||
start += 1;
|
||||
}
|
||||
|
||||
if (start[0] == '0' && value.size() >= 2 && (start[1] == 'x' || start[1] == 'X'))
|
||||
{
|
||||
// Limited hex support
|
||||
base = 16;
|
||||
start += 2;
|
||||
}
|
||||
|
||||
const auto ret = std::from_chars(start, end, result, base);
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end || (start[0] == '-' && sign < 0))
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_int64('%s'): invalid integer", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
result *= sign;
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_int64('%s'): out of bounds (val=%d, min=%d, max=%d)", value, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::make_uint_range(u64 min, u64 max)
|
||||
{
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_uint64(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 result;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
|
||||
if (start[0] == '0' && value.size() >= 2 && (start[1] == 'x' || start[1] == 'X'))
|
||||
{
|
||||
// Limited hex support
|
||||
base = 16;
|
||||
start += 2;
|
||||
}
|
||||
|
||||
const auto ret = std::from_chars(start, end, result, base);
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_uint64('%s'): invalid integer", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (val=%u, min=%u, max=%u)", value, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::make_float_range(f64 min, f64 max)
|
||||
{
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_float(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
// std::from_chars float is yet to be implemented on Xcode it seems
|
||||
// And strtod doesn't support ranged view so we need to ensure it meets a null terminator
|
||||
const std::string str = std::string{value};
|
||||
|
||||
char* end_check{};
|
||||
const double result = std::strtod(str.data(), &end_check);
|
||||
|
||||
if (end_check != str.data() + str.size())
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_float('%s'): invalid float", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_float('%s'): out of bounds (val=%f, min=%f, max=%f)", value, result, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool try_to_string(std::string* out, const f64& value)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if (out)
|
||||
*out = std::to_string(value);
|
||||
return true;
|
||||
#else
|
||||
std::array<char, 32> str{};
|
||||
|
||||
if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value, std::chars_format::fixed); ec == std::errc())
|
||||
{
|
||||
if (out)
|
||||
*out = std::string(str.data(), ptr);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_string(): could not convert value '%f' to string. error='%s'", value, std::make_error_code(ec).message());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value)
|
||||
{
|
||||
u64 max = umax;
|
||||
std::size_t gap = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
if (++gap > 100)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gap = 0;
|
||||
max = i;
|
||||
}
|
||||
|
||||
u64 result;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
|
||||
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
|
||||
{
|
||||
// Limited hex support
|
||||
base = 16;
|
||||
start += 2;
|
||||
}
|
||||
|
||||
const auto ret = std::from_chars(start, end, result, base);
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_enum_value('%s'): invalid enum or integer", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result > max)
|
||||
{
|
||||
if (out)
|
||||
cfg_log.error("cfg::try_to_enum_value('%s'): out of bounds(val=%u, min=0, max=%u)", value, result, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::format) func)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
std::size_t gap = 0;
|
||||
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
std::string var;
|
||||
func(var, i);
|
||||
|
||||
std::string hex;
|
||||
fmt_class_string<u64>::format(hex, i);
|
||||
if (var == hex)
|
||||
{
|
||||
if (++gap > 100)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gap = 0;
|
||||
|
||||
result.emplace_back(std::move(var));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
{
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
case type::node:
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& node : static_cast<const node&>(rhs).get_nodes())
|
||||
{
|
||||
out << YAML::Key << node->get_name();
|
||||
out << YAML::Value;
|
||||
encode(out, *node);
|
||||
}
|
||||
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;
|
||||
}
|
||||
case type::map:
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& np : static_cast<const map_entry&>(rhs).get_map())
|
||||
{
|
||||
out << YAML::Key << np.first;
|
||||
out << YAML::Value << fmt::format("%s", np.second);
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
case type::node_map:
|
||||
{
|
||||
// out << YAML::Block; // Does nothing, output is in Flow mode still (TODO)
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& np : static_cast<const node_map_entry&>(rhs).get_map())
|
||||
{
|
||||
out << YAML::Key << np.first;
|
||||
out << YAML::Value << fmt::format("%s", np.second);
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
case type::device:
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& [key, info] : static_cast<const device_entry&>(rhs).get_map())
|
||||
{
|
||||
out << YAML::Key << key;
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "Path" << YAML::Value << info.path;
|
||||
out << YAML::Key << "Serial" << YAML::Value << info.serial;
|
||||
out << YAML::Key << "VID" << YAML::Value << info.vid;
|
||||
out << YAML::Key << "PID" << YAML::Value << info.pid;
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
out << rhs.to_string();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
|
||||
{
|
||||
if (dynamic && !rhs.get_is_dynamic())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
for (const auto& node : static_cast<node&>(rhs).get_nodes())
|
||||
{
|
||||
if (node->get_name() == pair.first.Scalar())
|
||||
{
|
||||
decode(pair.second, *node, dynamic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case type::set:
|
||||
{
|
||||
std::vector<std::string> values;
|
||||
|
||||
if (YAML::convert<decltype(values)>::decode(data, values))
|
||||
{
|
||||
rhs.from_list(std::move(values));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case type::map:
|
||||
case type::node_map:
|
||||
{
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
map_of_type<std::string> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar() || !pair.second.IsScalar())
|
||||
continue;
|
||||
|
||||
values.emplace(pair.first.Scalar(), pair.second.Scalar());
|
||||
}
|
||||
|
||||
static_cast<map_entry&>(rhs).set_map(std::move(values));
|
||||
break;
|
||||
}
|
||||
case type::log:
|
||||
{
|
||||
if (data.IsScalar() || data.IsSequence())
|
||||
{
|
||||
return; // ???
|
||||
}
|
||||
|
||||
map_of_type<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;
|
||||
}
|
||||
case type::device:
|
||||
{
|
||||
if (!data.IsMap())
|
||||
{
|
||||
return; // ???
|
||||
}
|
||||
|
||||
map_of_type<device_info> values;
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar() || !pair.second.IsMap())
|
||||
continue;
|
||||
|
||||
device_info info{};
|
||||
|
||||
for (const auto& key_value : pair.second)
|
||||
{
|
||||
if (!key_value.first.IsScalar() || !key_value.second.IsScalar())
|
||||
continue;
|
||||
|
||||
if (key_value.first.Scalar() == "Path")
|
||||
info.path = key_value.second.Scalar();
|
||||
|
||||
if (key_value.first.Scalar() == "Serial")
|
||||
info.serial = key_value.second.Scalar();
|
||||
|
||||
if (key_value.first.Scalar() == "VID")
|
||||
info.vid = key_value.second.Scalar();
|
||||
|
||||
if (key_value.first.Scalar() == "PID")
|
||||
info.pid = key_value.second.Scalar();
|
||||
}
|
||||
|
||||
values.emplace(pair.first.Scalar(), std::move(info));
|
||||
}
|
||||
|
||||
static_cast<device_entry&>(rhs).set_map(std::move(values));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::string value;
|
||||
|
||||
if (YAML::convert<std::string>::decode(data, value))
|
||||
{
|
||||
rhs.from_string(value, dynamic);
|
||||
}
|
||||
|
||||
break; // ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfg::node::to_string() const
|
||||
{
|
||||
YAML::Emitter out;
|
||||
cfg::encode(out, *this);
|
||||
|
||||
return {out.c_str(), out.size()};
|
||||
}
|
||||
|
||||
nlohmann::ordered_json cfg::node::to_json() const
|
||||
{
|
||||
auto result = nlohmann::ordered_json::object();
|
||||
|
||||
for (const auto& node : get_nodes())
|
||||
{
|
||||
result[node->get_name()] = node->to_json();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool cfg::node::from_json(const nlohmann::json& json, bool dynamic)
|
||||
{
|
||||
if (!json.is_object())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto find_node = [this](std::string_view name) -> _base*
|
||||
{
|
||||
for (const auto& node : get_nodes())
|
||||
{
|
||||
if (node->get_name() == name)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
for (auto& [key, value] : json.get<nlohmann::json::object_t>())
|
||||
{
|
||||
auto keyNode = find_node(key);
|
||||
|
||||
if (keyNode == nullptr || (dynamic && !keyNode->get_is_dynamic()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keyNode->from_json(value, dynamic);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cfg::node::from_string(std::string_view value, bool dynamic)
|
||||
{
|
||||
auto [result, error] = yaml_load(std::string(value));
|
||||
|
||||
if (error.empty())
|
||||
{
|
||||
cfg::decode(result, *this, dynamic);
|
||||
return true;
|
||||
}
|
||||
|
||||
cfg_log.error("Failed to load node: %s", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cfg::node::from_default()
|
||||
{
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
node->from_default();
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::_bool::from_default()
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
void cfg::string::from_default()
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
void cfg::set_entry::from_default()
|
||||
{
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
std::string cfg::map_entry::get_value(std::string_view key)
|
||||
{
|
||||
if (auto it = m_map.find(key); it != m_map.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void cfg::map_entry::set_value(std::string key, std::string value)
|
||||
{
|
||||
m_map[std::move(key)] = std::move(value);
|
||||
}
|
||||
|
||||
void cfg::map_entry::set_map(map_of_type<std::string>&& map)
|
||||
{
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
void cfg::map_entry::erase(std::string_view key)
|
||||
{
|
||||
if (auto it = m_map.find(key); it != m_map.end())
|
||||
{
|
||||
m_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::map_entry::from_default()
|
||||
{
|
||||
set_map({});
|
||||
}
|
||||
|
||||
void cfg::log_entry::set_map(map_of_type<logs::level>&& map)
|
||||
{
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
void cfg::log_entry::from_default()
|
||||
{
|
||||
set_map({});
|
||||
}
|
||||
|
||||
std::pair<u16, u16> cfg::device_info::get_usb_ids() const
|
||||
{
|
||||
auto string_to_hex = [](const std::string& str) -> u16
|
||||
{
|
||||
u16 value = 0x0000;
|
||||
if (!str.empty() && std::from_chars(str.data(), str.data() + str.size(), value, 16).ec != std::errc{})
|
||||
cfg_log.error("Failed to parse hex from string \"%s\"", str);
|
||||
return value;
|
||||
};
|
||||
return {string_to_hex(vid), string_to_hex(pid)};
|
||||
}
|
||||
|
||||
void cfg::device_entry::set_map(map_of_type<device_info>&& map)
|
||||
{
|
||||
m_map = std::move(map);
|
||||
}
|
||||
|
||||
void cfg::device_entry::from_default()
|
||||
{
|
||||
m_map = m_default;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue