RiivolutionParser: Add code to read and write the Riivolution config XMLs that remember the last selection.

This commit is contained in:
Admiral H. Curtiss 2021-10-19 23:30:02 +02:00
parent 2d3ed2c3ad
commit 2be62d94fc
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
2 changed files with 127 additions and 0 deletions

View File

@ -4,6 +4,7 @@
#include "DiscIO/RiivolutionParser.h" #include "DiscIO/RiivolutionParser.h"
#include <algorithm> #include <algorithm>
#include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector> #include <vector>
@ -380,4 +381,106 @@ std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor(
} }
return result; return result;
} }
std::optional<Config> ParseConfigFile(const std::string& filename)
{
::File::IOFile f(filename, "rb");
if (!f)
return std::nullopt;
std::vector<char> data;
data.resize(f.GetSize());
if (!f.ReadBytes(data.data(), data.size()))
return std::nullopt;
return ParseConfigString(std::string_view(data.data(), data.size()));
}
std::optional<Config> ParseConfigString(std::string_view xml)
{
pugi::xml_document doc;
const auto parse_result = doc.load_buffer(xml.data(), xml.size());
if (!parse_result)
return std::nullopt;
const auto riivolution = doc.child("riivolution");
if (!riivolution)
return std::nullopt;
Config config;
config.m_version = riivolution.attribute("version").as_int(-1);
if (config.m_version != 2)
return std::nullopt;
const auto options = riivolution.children("option");
for (const auto& option_node : options)
{
auto& option = config.m_options.emplace_back();
option.m_id = option_node.attribute("id").as_string();
option.m_default = option_node.attribute("default").as_uint(0);
}
return config;
}
std::string WriteConfigString(const Config& config)
{
pugi::xml_document doc;
auto riivolution = doc.append_child("riivolution");
riivolution.append_attribute("version").set_value(config.m_version);
for (const auto& option : config.m_options)
{
auto option_node = riivolution.append_child("option");
option_node.append_attribute("id").set_value(option.m_id.c_str());
option_node.append_attribute("default").set_value(option.m_default);
}
std::stringstream ss;
doc.print(ss, " ");
return ss.str();
}
bool WriteConfigFile(const std::string& filename, const Config& config)
{
auto xml = WriteConfigString(config);
if (xml.empty())
return false;
::File::IOFile f(filename, "wb");
if (!f)
return false;
if (!f.WriteString(xml))
return false;
return true;
}
void ApplyConfigDefaults(Disc* disc, const Config& config)
{
for (const auto& config_option : config.m_options)
{
auto* matching_option = [&]() -> DiscIO::Riivolution::Option* {
for (auto& section : disc->m_sections)
{
for (auto& option : section.m_options)
{
if (option.m_id.empty())
{
if ((section.m_name + option.m_name) == config_option.m_id)
return &option;
}
else
{
if (option.m_id == config_option.m_id)
return &option;
}
}
}
return nullptr;
}();
if (matching_option)
matching_option->m_selected_choice = config_option.m_default;
}
}
} // namespace DiscIO::Riivolution } // namespace DiscIO::Riivolution

View File

@ -199,9 +199,33 @@ struct Disc
std::vector<Patch> GeneratePatches(const std::string& game_id) const; std::vector<Patch> GeneratePatches(const std::string& game_id) const;
}; };
// Config format that remembers which patches are enabled/disabled for the next run.
// Some patches ship with pre-made config XMLs instead of baking their defaults into the actual
// patch XMLs, so it makes sense to support this format directly.
struct ConfigOption
{
// The identifier for the referenced Option.
std::string m_id;
// The selected Choice index.
u32 m_default;
};
struct Config
{
// Config version. Riivolution itself only accepts '2' here.
int m_version = 2;
std::vector<ConfigOption> m_options;
};
std::optional<Disc> ParseFile(const std::string& filename); std::optional<Disc> ParseFile(const std::string& filename);
std::optional<Disc> ParseString(std::string_view xml, std::string xml_path); std::optional<Disc> ParseString(std::string_view xml, std::string xml_path);
std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor( std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor(
const GameModDescriptorRiivolution& descriptor, const std::string& game_id, const GameModDescriptorRiivolution& descriptor, const std::string& game_id,
std::optional<u16> revision, std::optional<u8> disc_number); std::optional<u16> revision, std::optional<u8> disc_number);
std::optional<Config> ParseConfigFile(const std::string& filename);
std::optional<Config> ParseConfigString(std::string_view xml);
std::string WriteConfigString(const Config& config);
bool WriteConfigFile(const std::string& filename, const Config& config);
void ApplyConfigDefaults(Disc* disc, const Config& config);
} // namespace DiscIO::Riivolution } // namespace DiscIO::Riivolution