yaze 0.2.0
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
common.cc
Go to the documentation of this file.
1#include "common.h"
2
3#include <zlib.h>
4
5#include <cstdint>
6#include <cstring>
7#include <fstream>
8#include <memory>
9#include <sstream>
10#include <string>
11#include <unordered_map>
12#include <vector>
13
14#include "absl/status/statusor.h"
15#include "absl/strings/str_format.h"
16#include "absl/strings/string_view.h"
17#include "app/core/constants.h"
18#include "app/gui/icons.h"
19#include "imgui/imgui.h"
20#include "imgui/misc/cpp/imgui_stdlib.h"
21
22namespace yaze {
23namespace app {
24namespace core {
25
26namespace {
27
28void encode(uint64_t data, std::vector<uint8_t> &output) {
29 while (true) {
30 uint8_t x = data & 0x7f;
31 data >>= 7;
32 if (data == 0) {
33 output.push_back(0x80 | x);
34 break;
35 }
36 output.push_back(x);
37 data--;
38 }
39}
40
41uint64_t decode(const std::vector<uint8_t> &input, size_t &offset) {
42 uint64_t data = 0;
43 uint64_t shift = 1;
44 while (true) {
45 uint8_t x = input[offset++];
46 data += (x & 0x7f) * shift;
47 if (x & 0x80) break;
48 shift <<= 7;
49 data += shift;
50 }
51 return data;
52}
53
54uint32_t crc32(const std::vector<uint8_t> &data) {
55 uint32_t crc = ::crc32(0L, Z_NULL, 0);
56 return ::crc32(crc, data.data(), data.size());
57}
58
59// "load little endian value at the given byte offset and shift to get its
60// value relative to the base offset (powers of 256, essentially)"
61unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
62 uint32_t v = p_arr[p_index];
63 v <<= (8 * p_index);
64 return v;
65}
66
67void stle(uint8_t *const p_arr, size_t const p_index, unsigned const p_val) {
68 uint8_t v = (p_val >> (8 * p_index)) & 0xff;
69
70 p_arr[p_index] = v;
71}
72
73void stle0(uint8_t *const p_arr, unsigned const p_val) {
74 stle(p_arr, 0, p_val);
75}
76
77void stle1(uint8_t *const p_arr, unsigned const p_val) {
78 stle(p_arr, 1, p_val);
79}
80
81void stle2(uint8_t *const p_arr, unsigned const p_val) {
82 stle(p_arr, 2, p_val);
83}
84
85void stle3(uint8_t *const p_arr, unsigned const p_val) {
86 stle(p_arr, 3, p_val);
87}
88
89// Helper function to get the first byte in a little endian number
90uint32_t ldle0(uint8_t const *const p_arr) { return ldle(p_arr, 0); }
91
92// Helper function to get the second byte in a little endian number
93uint32_t ldle1(uint8_t const *const p_arr) { return ldle(p_arr, 1); }
94
95// Helper function to get the third byte in a little endian number
96uint32_t ldle2(uint8_t const *const p_arr) { return ldle(p_arr, 2); }
97
98// Helper function to get the third byte in a little endian number
99uint32_t ldle3(uint8_t const *const p_arr) { return ldle(p_arr, 3); }
100
101} // namespace
102
103std::string UppercaseHexByte(uint8_t byte, bool leading) {
104 if (leading) {
105 std::string result = absl::StrFormat("0x%02X", byte);
106 return result;
107 }
108 std::string result = absl::StrFormat("%02X", byte);
109 return result;
110}
111std::string UppercaseHexWord(uint16_t word, bool leading) {
112 if (leading) {
113 std::string result = absl::StrFormat("0x%04X", word);
114 return result;
115 }
116 std::string result = absl::StrFormat("%04X", word);
117 return result;
118}
119std::string UppercaseHexLong(uint32_t dword) {
120 std::string result = absl::StrFormat("0x%06X", dword);
121 return result;
122}
123std::string UppercaseHexLongLong(uint64_t qword) {
124 std::string result = absl::StrFormat("0x%08X", qword);
125 return result;
126}
127
128bool StringReplace(std::string &str, const std::string &from,
129 const std::string &to) {
130 size_t start = str.find(from);
131 if (start == std::string::npos) return false;
132
133 str.replace(start, from.length(), to);
134 return true;
135}
136
137std::shared_ptr<ExperimentFlags::Flags> ExperimentFlags::flags_;
138
139uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc) {
140 uint32_t ret =
141 (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr];
142 if (pc) {
143 return SnesToPc(ret);
144 }
145 return ret;
146}
147
148void stle16b_i(uint8_t *const p_arr, size_t const p_index,
149 uint16_t const p_val) {
150 stle16b(p_arr + (p_index * 2), p_val);
151}
152
153void stle16b(uint8_t *const p_arr, uint16_t const p_val) {
154 stle0(p_arr, p_val);
155 stle1(p_arr, p_val);
156}
157
158uint16_t ldle16b(uint8_t const *const p_arr) {
159 uint16_t v = 0;
160 v |= (ldle0(p_arr) | ldle1(p_arr));
161 return v;
162}
163
164uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
165 return ldle16b(p_arr + (2 * p_index));
166}
167
168void CreateBpsPatch(const std::vector<uint8_t> &source,
169 const std::vector<uint8_t> &target,
170 std::vector<uint8_t> &patch) {
171 patch.clear();
172 patch.insert(patch.end(), {'B', 'P', 'S', '1'});
173
174 encode(source.size(), patch);
175 encode(target.size(), patch);
176 encode(0, patch); // No metadata
177
178 size_t source_offset = 0;
179 size_t target_offset = 0;
180 int64_t source_rel_offset = 0;
181 int64_t target_rel_offset = 0;
182
183 while (target_offset < target.size()) {
184 if (source_offset < source.size() &&
185 source[source_offset] == target[target_offset]) {
186 size_t length = 0;
187 while (source_offset + length < source.size() &&
188 target_offset + length < target.size() &&
189 source[source_offset + length] == target[target_offset + length]) {
190 length++;
191 }
192 encode((length - 1) << 2 | 0, patch); // SourceRead
193 source_offset += length;
194 target_offset += length;
195 } else {
196 size_t length = 0;
197 while (
198 target_offset + length < target.size() &&
199 (source_offset + length >= source.size() ||
200 source[source_offset + length] != target[target_offset + length])) {
201 length++;
202 }
203 if (length > 0) {
204 encode((length - 1) << 2 | 1, patch); // TargetRead
205 for (size_t i = 0; i < length; i++) {
206 patch.push_back(target[target_offset + i]);
207 }
208 target_offset += length;
209 }
210 }
211
212 // SourceCopy
213 if (source_offset < source.size()) {
214 size_t length = 0;
215 int64_t offset = source_offset - source_rel_offset;
216 while (source_offset + length < source.size() &&
217 target_offset + length < target.size() &&
218 source[source_offset + length] == target[target_offset + length]) {
219 length++;
220 }
221 if (length > 0) {
222 encode((length - 1) << 2 | 2, patch);
223 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
224 source_offset += length;
225 target_offset += length;
226 source_rel_offset = source_offset;
227 }
228 }
229
230 // TargetCopy
231 if (target_offset > 0) {
232 size_t length = 0;
233 int64_t offset = target_offset - target_rel_offset;
234 while (target_offset + length < target.size() &&
235 target[target_offset - 1] == target[target_offset + length]) {
236 length++;
237 }
238 if (length > 0) {
239 encode((length - 1) << 2 | 3, patch);
240 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
241 target_offset += length;
242 target_rel_offset = target_offset;
243 }
244 }
245 }
246
247 patch.resize(patch.size() + 12); // Make space for the checksums
248 uint32_t source_checksum = crc32(source);
249 uint32_t target_checksum = crc32(target);
250 uint32_t patch_checksum = crc32(patch);
251
252 memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
253 memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
254 memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
255}
256
257void ApplyBpsPatch(const std::vector<uint8_t> &source,
258 const std::vector<uint8_t> &patch,
259 std::vector<uint8_t> &target) {
260 if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' ||
261 patch[2] != 'S' || patch[3] != '1') {
262 throw std::runtime_error("Invalid patch format");
263 }
264
265 size_t patch_offset = 4;
266 uint64_t target_size = decode(patch, patch_offset);
267 uint64_t metadata_size = decode(patch, patch_offset);
268 patch_offset += metadata_size;
269
270 target.resize(target_size);
271 size_t source_offset = 0;
272 size_t target_offset = 0;
273 int64_t source_rel_offset = 0;
274 int64_t target_rel_offset = 0;
275
276 while (patch_offset < patch.size() - 12) {
277 uint64_t data = decode(patch, patch_offset);
278 uint64_t command = data & 3;
279 uint64_t length = (data >> 2) + 1;
280
281 switch (command) {
282 case 0: // SourceRead
283 while (length--) {
284 target[target_offset++] = source[source_offset++];
285 }
286 break;
287 case 1: // TargetRead
288 while (length--) {
289 target[target_offset++] = patch[patch_offset++];
290 }
291 break;
292 case 2: // SourceCopy
293 {
294 int64_t offsetData = decode(patch, patch_offset);
295 source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
296 while (length--) {
297 target[target_offset++] = source[source_rel_offset++];
298 }
299 } break;
300 case 3: // TargetCopy
301 {
302 uint64_t offsetData = decode(patch, patch_offset);
303 target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
304 while (length--) {
305 target[target_offset++] = target[target_rel_offset++];
306 }
307 }
308 default:
309 throw std::runtime_error("Invalid patch command");
310 }
311 }
312
313 uint32_t source_checksum;
314 uint32_t target_checksum;
315 uint32_t patch_checksum;
316 memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
317 memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
318 memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
319
320 if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
321 patch_checksum !=
322 crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
323 throw std::runtime_error("Checksum mismatch");
324 }
325}
326
327absl::StatusOr<std::string> CheckVersion(const char *version) {
328 std::string version_string = version;
329 if (version_string != kYazeVersion) {
330 std::string message =
331 absl::StrFormat("Yaze version mismatch: expected %s, got %s",
332 kYazeVersion.data(), version_string.c_str());
333 return absl::InvalidArgumentError(message);
334 }
335 return version_string;
336}
337
338} // namespace core
339} // namespace app
340} // namespace yaze
static std::shared_ptr< Flags > flags_
Definition common.h:154
uint32_t ldle2(uint8_t const *const p_arr)
Definition common.cc:96
void stle1(uint8_t *const p_arr, unsigned const p_val)
Definition common.cc:77
void stle(uint8_t *const p_arr, size_t const p_index, unsigned const p_val)
Definition common.cc:67
uint32_t ldle3(uint8_t const *const p_arr)
Definition common.cc:99
void stle2(uint8_t *const p_arr, unsigned const p_val)
Definition common.cc:81
unsigned ldle(uint8_t const *const p_arr, unsigned const p_index)
Definition common.cc:61
uint64_t decode(const std::vector< uint8_t > &input, size_t &offset)
Definition common.cc:41
void encode(uint64_t data, std::vector< uint8_t > &output)
Definition common.cc:28
void stle3(uint8_t *const p_arr, unsigned const p_val)
Definition common.cc:85
uint32_t crc32(const std::vector< uint8_t > &data)
Definition common.cc:54
void stle0(uint8_t *const p_arr, unsigned const p_val)
Definition common.cc:73
uint32_t ldle1(uint8_t const *const p_arr)
Definition common.cc:93
uint32_t ldle0(uint8_t const *const p_arr)
Definition common.cc:90
void ApplyBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &patch, std::vector< uint8_t > &target)
Definition common.cc:257
std::string UppercaseHexLongLong(uint64_t qword)
Definition common.cc:123
bool StringReplace(std::string &str, const std::string &from, const std::string &to)
Definition common.cc:128
uint32_t PcToSnes(uint32_t addr)
Definition common.h:234
void stle16b(uint8_t *const p_arr, uint16_t const p_val)
Definition common.cc:153
uint16_t ldle16b(uint8_t const *const p_arr)
Definition common.cc:158
absl::StatusOr< std::string > CheckVersion(const char *version)
Definition common.cc:327
uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc)
Definition common.cc:139
void CreateBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &target, std::vector< uint8_t > &patch)
Definition common.cc:168
std::string UppercaseHexLong(uint32_t dword)
Definition common.cc:119
uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index)
Load little endian halfword (16-bit) dereferenced from an arrays of bytes. This version provides an i...
Definition common.cc:164
uint32_t SnesToPc(uint32_t addr) noexcept
Definition common.h:226
void stle16b_i(uint8_t *const p_arr, size_t const p_index, uint16_t const p_val)
Store little endian 16-bit value using a byte pointer, offset by an index before dereferencing.
Definition common.cc:148
std::string UppercaseHexByte(uint8_t byte, bool leading)
Definition common.cc:103
constexpr std::string_view kYazeVersion
Definition common.h:22
std::string UppercaseHexWord(uint16_t word, bool leading)
Definition common.cc:111
Definition common.cc:22