diff options
| author | 2018-09-30 18:13:22 -0400 | |
|---|---|---|
| committer | 2018-10-04 11:32:10 -0400 | |
| commit | 306739c2c479b646270f7cd8000bb11483613d50 (patch) | |
| tree | 969575650ff70c1bbb1df7a32ecd2637a526ecfd /src/core | |
| parent | hex_util: Add HexVectorToString and HexStringToVector (diff) | |
| download | yuzu-306739c2c479b646270f7cd8000bb11483613d50.tar.gz yuzu-306739c2c479b646270f7cd8000bb11483613d50.tar.xz yuzu-306739c2c479b646270f7cd8000bb11483613d50.zip | |
ips_layer: Add IPSwitchCompiler to process IPSwitch format
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/file_sys/ips_layer.cpp | 142 | ||||
| -rw-r--r-- | src/core/file_sys/ips_layer.h | 26 |
2 files changed, 168 insertions, 0 deletions
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index df933ee36..6bce138a8 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <sstream> | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/hex_util.h" | ||
| 6 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 7 | #include "core/file_sys/ips_layer.h" | 9 | #include "core/file_sys/ips_layer.h" |
| 8 | #include "core/file_sys/vfs_vector.h" | 10 | #include "core/file_sys/vfs_vector.h" |
| @@ -85,4 +87,144 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 85 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | 87 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 90 | IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) | ||
| 91 | : valid(false), patch_text(std::move(patch_text_)), nso_build_id{}, is_little_endian(false), | ||
| 92 | offset_shift(0), print_values(false) { | ||
| 93 | Parse(); | ||
| 94 | } | ||
| 95 | |||
| 96 | std::array<u8, 32> IPSwitchCompiler::GetBuildID() const { | ||
| 97 | return nso_build_id; | ||
| 98 | } | ||
| 99 | |||
| 100 | bool IPSwitchCompiler::IsValid() const { | ||
| 101 | return valid; | ||
| 102 | } | ||
| 103 | |||
| 104 | static bool StartsWith(const std::string& base, const std::string& check) { | ||
| 105 | return base.size() >= check.size() && base.substr(0, check.size()) == check; | ||
| 106 | } | ||
| 107 | |||
| 108 | void IPSwitchCompiler::Parse() { | ||
| 109 | const auto bytes = patch_text->ReadAllBytes(); | ||
| 110 | std::stringstream s; | ||
| 111 | s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()); | ||
| 112 | |||
| 113 | std::vector<std::string> lines; | ||
| 114 | std::string line; | ||
| 115 | while (std::getline(s, line)) { | ||
| 116 | // Remove a trailing \r | ||
| 117 | if (!line.empty() && line[line.size() - 1] == '\r') | ||
| 118 | line = line.substr(0, line.size() - 1); | ||
| 119 | lines.push_back(line); | ||
| 120 | } | ||
| 121 | |||
| 122 | for (std::size_t i = 0; i < lines.size(); ++i) { | ||
| 123 | auto line = lines[i]; | ||
| 124 | if (StartsWith(line, "@stop")) { | ||
| 125 | // Force stop | ||
| 126 | break; | ||
| 127 | } else if (StartsWith(line, "@nsobid-")) { | ||
| 128 | // NSO Build ID Specifier | ||
| 129 | auto raw_build_id = line.substr(8); | ||
| 130 | if (raw_build_id.size() != 0x40) | ||
| 131 | raw_build_id.resize(0x40, '0'); | ||
| 132 | nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); | ||
| 133 | } else if (StartsWith(line, "@flag offset_shift ")) { | ||
| 134 | // Offset Shift Flag | ||
| 135 | offset_shift = std::stoull(line.substr(19), nullptr, 0); | ||
| 136 | } else if (StartsWith(line, "#")) { | ||
| 137 | // Mandatory Comment | ||
| 138 | LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}", | ||
| 139 | patch_text->GetName(), line.substr(1)); | ||
| 140 | } else if (StartsWith(line, "@little-endian")) { | ||
| 141 | // Set values to read as little endian | ||
| 142 | is_little_endian = true; | ||
| 143 | } else if (StartsWith(line, "@big-endian")) { | ||
| 144 | // Set values to read as big endian | ||
| 145 | is_little_endian = false; | ||
| 146 | } else if (StartsWith(line, "@flag print_values")) { | ||
| 147 | // Force printing of applied values | ||
| 148 | print_values = true; | ||
| 149 | } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) { | ||
| 150 | // Start of patch | ||
| 151 | const auto enabled = StartsWith(line, "@enabled"); | ||
| 152 | if (i == 0) | ||
| 153 | return; | ||
| 154 | const auto name = lines[i - 1].substr(3); | ||
| 155 | LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})", | ||
| 156 | patch_text->GetName(), name, line.substr(1)); | ||
| 157 | |||
| 158 | IPSwitchPatch patch{name, enabled, {}}; | ||
| 159 | |||
| 160 | // Read rest of patch | ||
| 161 | while (true) { | ||
| 162 | if (i + 1 >= lines.size()) | ||
| 163 | break; | ||
| 164 | line = lines[++i]; | ||
| 165 | |||
| 166 | // 11 - 8 hex digit offset + space + minimum two digit overwrite val | ||
| 167 | if (line.length() < 11) | ||
| 168 | break; | ||
| 169 | auto offset = std::stoul(line.substr(0, 8), nullptr, 16); | ||
| 170 | offset += offset_shift; | ||
| 171 | |||
| 172 | std::vector<u8> replace; | ||
| 173 | // 9 - first char of replacement val | ||
| 174 | if (line[9] == '\"') { | ||
| 175 | // string replacement | ||
| 176 | const auto end_index = line.find_last_of('\"'); | ||
| 177 | if (end_index == std::string::npos || end_index < 10) | ||
| 178 | return; | ||
| 179 | const auto value = line.substr(10, end_index - 10); | ||
| 180 | replace.reserve(value.size()); | ||
| 181 | std::copy(value.begin(), value.end(), std::back_inserter(replace)); | ||
| 182 | } else { | ||
| 183 | // hex replacement | ||
| 184 | const auto value = line.substr(9); | ||
| 185 | replace.reserve(value.size() / 2); | ||
| 186 | replace = Common::HexStringToVector(value, is_little_endian); | ||
| 187 | } | ||
| 188 | |||
| 189 | if (print_values) { | ||
| 190 | LOG_INFO(Loader, | ||
| 191 | "[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} " | ||
| 192 | "with byte string '{}'", | ||
| 193 | patch_text->GetName(), offset, Common::HexVectorToString(replace)); | ||
| 194 | } | ||
| 195 | |||
| 196 | patch.records.emplace(offset, replace); | ||
| 197 | } | ||
| 198 | |||
| 199 | patches.push_back(std::move(patch)); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | valid = true; | ||
| 204 | } | ||
| 205 | |||
| 206 | VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { | ||
| 207 | if (in == nullptr || !valid) | ||
| 208 | return nullptr; | ||
| 209 | |||
| 210 | auto in_data = in->ReadAllBytes(); | ||
| 211 | |||
| 212 | for (const auto& patch : patches) { | ||
| 213 | if (!patch.enabled) | ||
| 214 | continue; | ||
| 215 | |||
| 216 | for (const auto& record : patch.records) { | ||
| 217 | if (record.first >= in_data.size()) | ||
| 218 | continue; | ||
| 219 | auto replace_size = record.second.size(); | ||
| 220 | if (record.first + replace_size > in_data.size()) | ||
| 221 | replace_size = in_data.size() - record.first; | ||
| 222 | for (std::size_t i = 0; i < replace_size; ++i) | ||
| 223 | in_data[i + record.first] = record.second[i]; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | ||
| 228 | } | ||
| 229 | |||
| 88 | } // namespace FileSys | 230 | } // namespace FileSys |
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 81c163494..bb35542c8 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h | |||
| @@ -12,4 +12,30 @@ namespace FileSys { | |||
| 12 | 12 | ||
| 13 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); | 13 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); |
| 14 | 14 | ||
| 15 | class IPSwitchCompiler { | ||
| 16 | public: | ||
| 17 | explicit IPSwitchCompiler(VirtualFile patch_text); | ||
| 18 | std::array<u8, 0x20> GetBuildID() const; | ||
| 19 | bool IsValid() const; | ||
| 20 | VirtualFile Apply(const VirtualFile& in) const; | ||
| 21 | |||
| 22 | private: | ||
| 23 | void Parse(); | ||
| 24 | |||
| 25 | bool valid; | ||
| 26 | |||
| 27 | struct IPSwitchPatch { | ||
| 28 | std::string name; | ||
| 29 | bool enabled; | ||
| 30 | std::map<u32, std::vector<u8>> records; | ||
| 31 | }; | ||
| 32 | |||
| 33 | VirtualFile patch_text; | ||
| 34 | std::vector<IPSwitchPatch> patches; | ||
| 35 | std::array<u8, 0x20> nso_build_id; | ||
| 36 | bool is_little_endian; | ||
| 37 | u64 offset_shift; | ||
| 38 | bool print_values; | ||
| 39 | }; | ||
| 40 | |||
| 15 | } // namespace FileSys | 41 | } // namespace FileSys |