1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 12:12:50 +01:00

patch manager: improve error handling

There shouldn't be much left that can crash this thing
This commit is contained in:
Megamouse 2020-06-13 02:16:28 +02:00
parent a7ee059419
commit cc5c89539b
2 changed files with 78 additions and 27 deletions

View File

@ -32,6 +32,7 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
{
switch (value)
{
case patch_type::invalid: return "invalid";
case patch_type::load: return "load";
case patch_type::byte: return "byte";
case patch_type::le16: return "le16";
@ -72,12 +73,12 @@ std::string patch_engine::get_patch_config_path()
static void append_log_message(std::stringstream* log_messages, const std::string& message)
{
if (log_messages)
*log_messages << message;
*log_messages << message << std::endl;
};
bool patch_engine::load(patch_map& patches_map, const std::string& path, bool importing, std::stringstream* log_messages)
{
append_log_message(log_messages, fmt::format("Reading file %s\n", path));
append_log_message(log_messages, fmt::format("Reading file %s", path));
// Load patch file
fs::file file{ path };
@ -91,9 +92,9 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
// Interpret yaml nodes
auto [root, error] = yaml_load(file.to_string());
if (!error.empty())
if (!error.empty() || !root)
{
append_log_message(log_messages, "Fatal Error: Failed to load file!\n");
append_log_message(log_messages, "Fatal Error: Failed to load file!");
patch_log.fatal("Failed to load patch file %s:\n%s", path, error);
return false;
}
@ -116,19 +117,19 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
if (version != patch_engine_version)
{
append_log_message(log_messages, fmt::format("Error: Patch engine target version %s does not match file version %s\n", patch_engine_version, version));
append_log_message(log_messages, fmt::format("Error: Patch engine target version %s does not match file version %s", patch_engine_version, version));
patch_log.error("Patch engine target version %s does not match file version %s in %s", patch_engine_version, version, path);
return false;
}
append_log_message(log_messages, fmt::format("Patch file version: %s\n", version));
append_log_message(log_messages, fmt::format("Patch file version: %s", version));
// We don't need the Version node in local memory anymore
root.remove("Version");
}
else if (importing)
{
append_log_message(log_messages, fmt::format("Error: Patch engine target version %s does not match file version %s\n", patch_engine_version, version));
append_log_message(log_messages, fmt::format("Error: Patch engine target version %s does not match file version %s", patch_engine_version, version));
patch_log.error("Patch engine version %s: No 'Version' entry found for file %s", patch_engine_version, path);
return false;
}
@ -170,7 +171,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
if (const auto yml_type = pair.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping key %s: expected Map, found %s\n", main_key, yml_type));
append_log_message(log_messages, fmt::format("Error: Skipping key %s: expected Map, found %s", main_key, yml_type));
patch_log.error("Skipping key %s: expected Map, found %s (file: %s)", main_key, yml_type, path);
is_valid = false;
continue;
@ -199,7 +200,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
{
if (const auto yml_type = patches_node.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping Patches: expected Map, found %s (key: %s)\n", yml_type, main_key));
append_log_message(log_messages, fmt::format("Error: Skipping Patches: expected Map, found %s (key: %s)", yml_type, main_key));
patch_log.error("Skipping Patches: expected Map, found %s (key: %s, file: %s)", yml_type, main_key, path);
is_valid = false;
continue;
@ -226,7 +227,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
if (const auto yml_type = patches_entry.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping Patch key %s: expected Map, found %s (key: %s)\n", description, yml_type, main_key));
append_log_message(log_messages, fmt::format("Error: Skipping Patch key %s: expected Map, found %s (key: %s)", description, yml_type, main_key));
patch_log.error("Skipping Patch key %s: expected Map, found %s (key: %s, file: %s)", description, yml_type, main_key, path);
is_valid = false;
continue;
@ -275,23 +276,39 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, bool im
patch_type patch_engine::get_patch_type(YAML::Node node)
{
u64 type_val = 0;
cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, node.Scalar());
if (!node || !node.IsScalar() || !cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, node.Scalar()))
{
return patch_type::invalid;
}
return static_cast<patch_type>(type_val);
}
bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifier, const YAML::Node& root, std::stringstream* log_messages)
{
if (!node || !node.IsSequence())
{
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s)", info.description, info.hash));
patch_log.error("Skipping invalid patch node %s. (key: %s)", info.description, info.hash);
return false;
}
const auto type_node = node[0];
auto addr_node = node[1];
const auto value_node = node[2];
struct patch_data p_data{};
p_data.type = get_patch_type(type_node);
p_data.offset = addr_node.as<u32>(0) + modifier;
const auto type = get_patch_type(type_node);
switch (p_data.type)
if (type == patch_type::invalid)
{
case patch_type::load:
const auto type_str = type_node && type_node.IsScalar() ? type_node.Scalar() : "";
append_log_message(log_messages, fmt::format("Skipping patch node %s: type '%s' is invalid. (key: %s)", info.description, type_str, info.hash));
patch_log.error("Skipping patch node %s: type '%s' is invalid. (key: %s)", info.description, type_str, info.hash);
return false;
}
if (type == patch_type::load)
{
// Special syntax: anchors (named sequence)
@ -300,6 +317,12 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
{
if (const auto yml_type = addr_node.Type(); yml_type == YAML::NodeType::Scalar)
{
if (!root)
{
patch_log.fatal("Trying to parse legacy patch with invalid root."); // Sanity Check
return false;
}
const auto anchor = addr_node.Scalar();
const auto anchor_node = root[anchor];
@ -341,19 +364,38 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
return is_valid;
}
case patch_type::bef32:
case patch_type::lef32:
case patch_type::bef64:
case patch_type::lef64:
struct patch_data p_data{};
p_data.type = type;
p_data.offset = addr_node.as<u32>(0) + modifier;
// Use try/catch instead of YAML::Node::as<T>(fallback) in order to get an error message
try
{
p_data.value.double_value = value_node.as<f64>();
break;
switch (p_data.type)
{
case patch_type::bef32:
case patch_type::lef32:
case patch_type::bef64:
case patch_type::lef64:
{
p_data.value.double_value = value_node.as<f64>();
break;
}
default:
{
p_data.value.long_value = value_node.as<u64>();
break;
}
}
}
default:
catch (const std::exception& e)
{
p_data.value.long_value = value_node.as<u64>();
break;
}
const std::string error_message = fmt::format("Skipping patch data entry: [ %s, 0x%.8x, %s ] (key: %s) %s",
p_data.type, p_data.offset, value_node.IsScalar() && value_node.Scalar().size() ? value_node.Scalar() : "?", info.hash, e.what());
append_log_message(log_messages, error_message);
patch_log.error("%s", error_message);
return false;
}
info.data_list.emplace_back(p_data);
@ -363,6 +405,13 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
bool patch_engine::read_patch_node(patch_info& info, YAML::Node node, const YAML::Node& root, std::stringstream* log_messages)
{
if (!node)
{
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s)", info.description, info.hash));
patch_log.error("Skipping invalid patch node %s. (key: %s)" HERE, info.description, info.hash);
return false;
}
if (const auto yml_type = node.Type(); yml_type != YAML::NodeType::Sequence)
{
append_log_message(log_messages, fmt::format("Skipping patch node %s: expected Sequence, found %s (key: %s)", info.description, yml_type, info.hash));
@ -464,6 +513,7 @@ std::size_t patch_engine::apply_patch(const std::string& name, u8* dst, u32 file
switch (p.type)
{
case patch_type::invalid:
case patch_type::load:
{
// Invalid in this context
@ -686,7 +736,7 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
for (const auto& data : info.data_list)
{
if (data.type == patch_type::load)
if (data.type == patch_type::invalid || data.type == patch_type::load)
{
// Unreachable with current logic
continue;

View File

@ -9,6 +9,7 @@
enum class patch_type
{
invalid,
load,
byte,
le16,