From 6394960f5412802f24bf664d1574f9126b1bb799 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 9 Oct 2021 03:17:38 +0200 Subject: Core: Add ability to specify and launch a riivolution-modded game via a .json file. --- Source/Core/DiscIO/GameModDescriptor.cpp | 147 +++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 Source/Core/DiscIO/GameModDescriptor.cpp (limited to 'Source/Core/DiscIO/GameModDescriptor.cpp') diff --git a/Source/Core/DiscIO/GameModDescriptor.cpp b/Source/Core/DiscIO/GameModDescriptor.cpp new file mode 100644 index 0000000000..4a1c479fca --- /dev/null +++ b/Source/Core/DiscIO/GameModDescriptor.cpp @@ -0,0 +1,147 @@ +// Copyright 2021 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "DiscIO/GameModDescriptor.h" + +#include + +#include "Common/IOFile.h" +#include "Common/MathUtil.h" +#include "Common/StringUtil.h" + +namespace DiscIO +{ +static std::string MakeAbsolute(const std::string& directory, const std::string& path) +{ +#ifdef _WIN32 + return PathToString(StringToPath(directory) / StringToPath(path)); +#else + if (StringBeginsWith(path, "/")) + return path; + return directory + "/" + path; +#endif +} + +std::optional ParseGameModDescriptorFile(const std::string& filename) +{ + ::File::IOFile f(filename, "rb"); + if (!f) + return std::nullopt; + + std::vector data; + data.resize(f.GetSize()); + if (!f.ReadBytes(data.data(), data.size())) + return std::nullopt; + +#ifdef _WIN32 + std::string path = ReplaceAll(filename, "\\", "/"); +#else + const std::string& path = filename; +#endif + return ParseGameModDescriptorString(std::string_view(data.data(), data.size()), path); +} + +static std::vector +ParseRiivolutionOptions(const picojson::array& array) +{ + std::vector options; + for (const auto& option_object : array) + { + if (!option_object.is()) + continue; + + auto& option = options.emplace_back(); + for (const auto& [key, value] : option_object.get()) + { + if (key == "section-name" && value.is()) + option.section_name = value.get(); + else if (key == "option-id" && value.is()) + option.option_id = value.get(); + else if (key == "option-name" && value.is()) + option.option_name = value.get(); + else if (key == "choice" && value.is()) + option.choice = MathUtil::SaturatingCast(value.get()); + } + } + return options; +} + +static GameModDescriptorRiivolution ParseRiivolutionObject(const std::string& json_directory, + const picojson::object& object) +{ + GameModDescriptorRiivolution r; + for (const auto& [element_key, element_value] : object) + { + if (element_key == "patches" && element_value.is()) + { + for (const auto& patch_object : element_value.get()) + { + if (!patch_object.is()) + continue; + + auto& patch = r.patches.emplace_back(); + for (const auto& [key, value] : patch_object.get()) + { + if (key == "xml" && value.is()) + patch.xml = MakeAbsolute(json_directory, value.get()); + else if (key == "root" && value.is()) + patch.root = MakeAbsolute(json_directory, value.get()); + else if (key == "options" && value.is()) + patch.options = ParseRiivolutionOptions(value.get()); + } + } + } + } + return r; +} + +std::optional ParseGameModDescriptorString(std::string_view json, + std::string_view json_path) +{ + std::string json_directory; + SplitPath(json_path, &json_directory, nullptr, nullptr); + + picojson::value json_root; + std::string err; + picojson::parse(json_root, json.begin(), json.end(), &err); + if (!err.empty()) + return std::nullopt; + if (!json_root.is()) + return std::nullopt; + + GameModDescriptor descriptor; + bool is_valid_version = false; + for (const auto& [key, value] : json_root.get()) + { + if (key == "version" && value.is()) + { + is_valid_version = value.get() == 1.0; + } + else if (key == "base-file" && value.is()) + { + descriptor.base_file = MakeAbsolute(json_directory, value.get()); + } + else if (key == "display-name" && value.is()) + { + descriptor.display_name = value.get(); + } + else if (key == "banner" && value.is()) + { + descriptor.banner = MakeAbsolute(json_directory, value.get()); + } + else if (key == "riivolution" && value.is()) + { + descriptor.riivolution = + ParseRiivolutionObject(json_directory, value.get()); + } + } + if (!is_valid_version) + return std::nullopt; + return descriptor; +} +} // namespace DiscIO -- cgit v1.2.3