mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-11 01:34:17 +02:00
YAML: Add support for resolving anchors, and use it for GameDB
This commit is contained in:
+88
-26
@@ -17,7 +17,7 @@ struct RapidYAMLContext
|
|||||||
Error* error = nullptr;
|
Error* error = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error)
|
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors)
|
||||||
{
|
{
|
||||||
RapidYAMLContext context;
|
RapidYAMLContext context;
|
||||||
context.error = error;
|
context.error = error;
|
||||||
@@ -28,40 +28,93 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
|
|||||||
callbacks.set_user_data(static_cast<void*>(&context));
|
callbacks.set_user_data(static_cast<void*>(&context));
|
||||||
|
|
||||||
callbacks.set_error_basic([](ryml::csubstr msg, const ryml::ErrorDataBasic& errdata, void* user_data) {
|
callbacks.set_error_basic([](ryml::csubstr msg, const ryml::ErrorDataBasic& errdata, void* user_data) {
|
||||||
std::string description;
|
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||||
auto callback = [&description](ryml::csubstr string) {
|
// This scope needs to stay, so all objects destruct before std::longjump
|
||||||
description.append(string.str, string.len);
|
{
|
||||||
};
|
std::string description;
|
||||||
ryml::err_basic_format(std::move(callback), msg, errdata);
|
auto callback = [&description](ryml::csubstr string) {
|
||||||
|
description.append(string.str, string.len);
|
||||||
|
};
|
||||||
|
ryml::err_basic_format(std::move(callback), msg, errdata);
|
||||||
|
|
||||||
// We might have already returned, so don't try to recover.
|
if (context != nullptr)
|
||||||
pxFailRel(description.c_str());
|
{
|
||||||
std::abort();
|
Error::SetString(context->error, std::move(description));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pxFailRel(description.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context != nullptr)
|
||||||
|
{
|
||||||
|
std::longjmp(context->env, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
callbacks.set_error_parse([](ryml::csubstr msg, const ryml::ErrorDataParse& errdata, void* user_data) {
|
callbacks.set_error_parse([](ryml::csubstr msg, const ryml::ErrorDataParse& errdata, void* user_data) {
|
||||||
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||||
|
// This scope needs to stay, so all objects destruct before std::longjump
|
||||||
|
{
|
||||||
|
std::string description;
|
||||||
|
auto callback = [&description](ryml::csubstr string) {
|
||||||
|
description.append(string.str, string.len);
|
||||||
|
};
|
||||||
|
ryml::err_parse_format(std::move(callback), msg, errdata);
|
||||||
|
|
||||||
std::string description;
|
if (context != nullptr)
|
||||||
auto callback = [&description](ryml::csubstr string) {
|
{
|
||||||
description.append(string.str, string.len);
|
Error::SetString(context->error, std::move(description));
|
||||||
};
|
}
|
||||||
ryml::err_parse_format(std::move(callback), msg, errdata);
|
else
|
||||||
|
{
|
||||||
|
pxFailRel(description.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Error::SetString(context->error, std::move(description));
|
if (context != nullptr)
|
||||||
std::longjmp(context->env, 1);
|
{
|
||||||
|
std::longjmp(context->env, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
callbacks.set_error_visit([](ryml::csubstr msg, const ryml::ErrorDataVisit& errdata, void* user_data) {
|
callbacks.set_error_visit([](ryml::csubstr msg, const ryml::ErrorDataVisit& errdata, void* user_data) {
|
||||||
std::string description;
|
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
|
||||||
auto callback = [&description](ryml::csubstr string) {
|
// This scope needs to stay, so all objects destruct before std::longjump
|
||||||
description.append(string.str, string.len);
|
{
|
||||||
};
|
std::string description;
|
||||||
ryml::err_visit_format(std::move(callback), msg, errdata);
|
auto callback = [&description](ryml::csubstr string) {
|
||||||
|
description.append(string.str, string.len);
|
||||||
|
};
|
||||||
|
ryml::err_visit_format(std::move(callback), msg, errdata);
|
||||||
|
|
||||||
// We've probably already returned, so don't try to recover.
|
if (context != nullptr)
|
||||||
pxFailRel(description.c_str());
|
{
|
||||||
std::abort();
|
Error::SetString(context->error, std::move(description));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pxFailRel(description.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context != nullptr)
|
||||||
|
{
|
||||||
|
std::longjmp(context->env, 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
callbacks.m_user_data = static_cast<void*>(&context);
|
callbacks.m_user_data = static_cast<void*>(&context);
|
||||||
@@ -76,15 +129,24 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
|
|||||||
ryml::EventHandlerTree event_handler(callbacks);
|
ryml::EventHandlerTree event_handler(callbacks);
|
||||||
ryml::Parser parser(&event_handler);
|
ryml::Parser parser(&event_handler);
|
||||||
|
|
||||||
ryml::Tree tree;
|
ryml::Tree tree(callbacks);
|
||||||
|
|
||||||
// The only options RapidYAML provides for recovering from errors are
|
// The only options RapidYAML provides for recovering from errors are
|
||||||
// throwing an exception or using setjmp/longjmp. Since we have exceptions
|
// throwing an exception or using setjmp/longjmp. Since we have exceptions
|
||||||
// disabled we have to use the latter option.
|
// disabled we have to use the latter option.
|
||||||
if (setjmp(context.env))
|
if (setjmp(context.env) != 0)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
ryml::parse_in_arena(&parser, file_name, yaml, &tree);
|
ryml::parse_in_arena(&parser, file_name, yaml, &tree);
|
||||||
|
if (resolve_anchors)
|
||||||
|
{
|
||||||
|
tree.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks passed to ryml::Tree are used for value parsing errors later,
|
||||||
|
// so we need to clear the context before it goes out of scope.
|
||||||
|
callbacks.set_user_data(nullptr);
|
||||||
|
tree.callbacks(callbacks);
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -14,4 +14,6 @@
|
|||||||
/// parsing errors (as is recommended by the documentation for cases where
|
/// parsing errors (as is recommended by the documentation for cases where
|
||||||
/// exceptions are disabled). The file_name parameter is only used for error
|
/// exceptions are disabled). The file_name parameter is only used for error
|
||||||
/// messages, which are returned via the error parameter.
|
/// messages, which are returned via the error parameter.
|
||||||
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error);
|
/// If resolve_anchors is set to true, YAML anchors and aliases will be resolved.
|
||||||
|
/// It's a potentially slow operation, so it's opt-in.
|
||||||
|
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors = false);
|
||||||
|
|||||||
@@ -1030,7 +1030,7 @@ void GameDatabase::initDatabase()
|
|||||||
const ryml::csubstr yaml = ryml::to_csubstr(*buffer);
|
const ryml::csubstr yaml = ryml::to_csubstr(*buffer);
|
||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
std::optional<ryml::Tree> tree = ParseYAMLFromString(yaml, ryml::to_csubstr(name), &error);
|
std::optional<ryml::Tree> tree = ParseYAMLFromString(yaml, ryml::to_csubstr(name), &error, true);
|
||||||
if (!tree.has_value())
|
if (!tree.has_value())
|
||||||
{
|
{
|
||||||
Console.ErrorFmt("GameDB: Failed to parse game database file {}:", path);
|
Console.ErrorFmt("GameDB: Failed to parse game database file {}:", path);
|
||||||
|
|||||||
Reference in New Issue
Block a user