yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
bps.cc
Go to the documentation of this file.
1
2#include "bps.h"
3
4#include <zlib.h>
5
6#include <cstdint>
7#include <vector>
8#include <stdexcept>
9
10namespace yaze {
11namespace util {
12
13namespace {
14
15uint32_t crc32(const std::vector<uint8_t> &data) {
16 uint32_t crc = ::crc32(0L, Z_NULL, 0);
17 return ::crc32(crc, data.data(), data.size());
18}
19
20void encode(uint64_t data, std::vector<uint8_t> &output) {
21 while (true) {
22 uint8_t x = data & 0x7f;
23 data >>= 7;
24 if (data == 0) {
25 output.push_back(0x80 | x);
26 break;
27 }
28 output.push_back(x);
29 data--;
30 }
31}
32
33uint64_t decode(const std::vector<uint8_t> &input, size_t &offset) {
34 uint64_t data = 0;
35 uint64_t shift = 1;
36 while (true) {
37 uint8_t x = input[offset++];
38 data += (x & 0x7f) * shift;
39 if (x & 0x80) break;
40 shift <<= 7;
41 data += shift;
42 }
43 return data;
44}
45
46} // namespace
47
48void CreateBpsPatch(const std::vector<uint8_t> &source,
49 const std::vector<uint8_t> &target,
50 std::vector<uint8_t> &patch) {
51 patch.clear();
52 patch.insert(patch.end(), {'B', 'P', 'S', '1'});
53
54 encode(source.size(), patch);
55 encode(target.size(), patch);
56 encode(0, patch); // No metadata
57
58 size_t source_offset = 0;
59 size_t target_offset = 0;
60 int64_t source_rel_offset = 0;
61 int64_t target_rel_offset = 0;
62
63 while (target_offset < target.size()) {
64 if (source_offset < source.size() &&
65 source[source_offset] == target[target_offset]) {
66 size_t length = 0;
67 while (source_offset + length < source.size() &&
68 target_offset + length < target.size() &&
69 source[source_offset + length] == target[target_offset + length]) {
70 length++;
71 }
72 encode((length - 1) << 2 | 0, patch); // SourceRead
73 source_offset += length;
74 target_offset += length;
75 } else {
76 size_t length = 0;
77 while (
78 target_offset + length < target.size() &&
79 (source_offset + length >= source.size() ||
80 source[source_offset + length] != target[target_offset + length])) {
81 length++;
82 }
83 if (length > 0) {
84 encode((length - 1) << 2 | 1, patch); // TargetRead
85 for (size_t i = 0; i < length; i++) {
86 patch.push_back(target[target_offset + i]);
87 }
88 target_offset += length;
89 }
90 }
91
92 // SourceCopy
93 if (source_offset < source.size()) {
94 size_t length = 0;
95 int64_t offset = source_offset - source_rel_offset;
96 while (source_offset + length < source.size() &&
97 target_offset + length < target.size() &&
98 source[source_offset + length] == target[target_offset + length]) {
99 length++;
100 }
101 if (length > 0) {
102 encode((length - 1) << 2 | 2, patch);
103 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
104 source_offset += length;
105 target_offset += length;
106 source_rel_offset = source_offset;
107 }
108 }
109
110 // TargetCopy
111 if (target_offset > 0) {
112 size_t length = 0;
113 int64_t offset = target_offset - target_rel_offset;
114 while (target_offset + length < target.size() &&
115 target[target_offset - 1] == target[target_offset + length]) {
116 length++;
117 }
118 if (length > 0) {
119 encode((length - 1) << 2 | 3, patch);
120 encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
121 target_offset += length;
122 target_rel_offset = target_offset;
123 }
124 }
125 }
126
127 patch.resize(patch.size() + 12); // Make space for the checksums
128 uint32_t source_checksum = crc32(source);
129 uint32_t target_checksum = crc32(target);
130 uint32_t patch_checksum = crc32(patch);
131
132 memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
133 memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
134 memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
135}
136
137void ApplyBpsPatch(const std::vector<uint8_t> &source,
138 const std::vector<uint8_t> &patch,
139 std::vector<uint8_t> &target) {
140 if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' ||
141 patch[2] != 'S' || patch[3] != '1') {
142 throw std::runtime_error("Invalid patch format");
143 }
144
145 size_t patch_offset = 4;
146 uint64_t target_size = decode(patch, patch_offset);
147 uint64_t metadata_size = decode(patch, patch_offset);
148 patch_offset += metadata_size;
149
150 target.resize(target_size);
151 size_t source_offset = 0;
152 size_t target_offset = 0;
153 int64_t source_rel_offset = 0;
154 int64_t target_rel_offset = 0;
155
156 while (patch_offset < patch.size() - 12) {
157 uint64_t data = decode(patch, patch_offset);
158 uint64_t command = data & 3;
159 uint64_t length = (data >> 2) + 1;
160
161 switch (command) {
162 case 0: // SourceRead
163 while (length--) {
164 target[target_offset++] = source[source_offset++];
165 }
166 break;
167 case 1: // TargetRead
168 while (length--) {
169 target[target_offset++] = patch[patch_offset++];
170 }
171 break;
172 case 2: // SourceCopy
173 {
174 int64_t offsetData = decode(patch, patch_offset);
175 source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
176 while (length--) {
177 target[target_offset++] = source[source_rel_offset++];
178 }
179 } break;
180 case 3: // TargetCopy
181 {
182 uint64_t offsetData = decode(patch, patch_offset);
183 target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
184 while (length--) {
185 target[target_offset++] = target[target_rel_offset++];
186 }
187 }
188 default:
189 throw std::runtime_error("Invalid patch command");
190 }
191 }
192
193 uint32_t source_checksum;
194 uint32_t target_checksum;
195 uint32_t patch_checksum;
196 memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
197 memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
198 memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
199
200 if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
201 patch_checksum !=
202 crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
203 throw std::runtime_error("Checksum mismatch");
204 }
205}
206
207} // namespace util
208} // namespace yaze
uint32_t crc32(const std::vector< uint8_t > &data)
Definition bps.cc:15
uint64_t decode(const std::vector< uint8_t > &input, size_t &offset)
Definition bps.cc:33
void encode(uint64_t data, std::vector< uint8_t > &output)
Definition bps.cc:20
void CreateBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &target, std::vector< uint8_t > &patch)
Definition bps.cc:48
void ApplyBpsPatch(const std::vector< uint8_t > &source, const std::vector< uint8_t > &patch, std::vector< uint8_t > &target)
Definition bps.cc:137
Main namespace for the application.
Definition controller.cc:12