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