RiivolutionParser: Add code to read and write the Riivolution config XMLs that remember the last selection.
This commit is contained in:
parent
2d3ed2c3ad
commit
2be62d94fc
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue