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