Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/bps/patch.hpp
2 views
1
#ifndef NALL_BPS_PATCH_HPP
2
#define NALL_BPS_PATCH_HPP
3
4
#include <nall/crc32.hpp>
5
#include <nall/file.hpp>
6
#include <nall/filemap.hpp>
7
#include <nall/stdint.hpp>
8
#include <nall/string.hpp>
9
10
namespace nall {
11
12
struct bpspatch {
13
inline bool modify(const uint8_t *data, unsigned size);
14
inline void source(const uint8_t *data, unsigned size);
15
inline void target(uint8_t *data, unsigned size);
16
17
inline bool modify(const string &filename);
18
inline bool source(const string &filename);
19
inline bool target(const string &filename);
20
21
inline string metadata() const;
22
inline unsigned size() const;
23
24
enum result : unsigned {
25
unknown,
26
success,
27
patch_too_small,
28
patch_invalid_header,
29
source_too_small,
30
target_too_small,
31
source_checksum_invalid,
32
target_checksum_invalid,
33
patch_checksum_invalid,
34
};
35
36
inline result apply();
37
38
protected:
39
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
40
41
filemap modifyFile;
42
const uint8_t *modifyData;
43
unsigned modifySize;
44
45
filemap sourceFile;
46
const uint8_t *sourceData;
47
unsigned sourceSize;
48
49
filemap targetFile;
50
uint8_t *targetData;
51
unsigned targetSize;
52
53
unsigned modifySourceSize;
54
unsigned modifyTargetSize;
55
unsigned modifyMarkupSize;
56
string metadataString;
57
};
58
59
bool bpspatch::modify(const uint8_t *data, unsigned size) {
60
if(size < 19) return false;
61
modifyData = data;
62
modifySize = size;
63
64
unsigned offset = 4;
65
auto decode = [&]() -> uint64_t {
66
uint64_t data = 0, shift = 1;
67
while(true) {
68
uint8_t x = modifyData[offset++];
69
data += (x & 0x7f) * shift;
70
if(x & 0x80) break;
71
shift <<= 7;
72
data += shift;
73
}
74
return data;
75
};
76
77
modifySourceSize = decode();
78
modifyTargetSize = decode();
79
modifyMarkupSize = decode();
80
81
char buffer[modifyMarkupSize + 1];
82
for(unsigned n = 0; n < modifyMarkupSize; n++) buffer[n] = modifyData[offset++];
83
buffer[modifyMarkupSize] = 0;
84
metadataString = (const char*)buffer;
85
86
return true;
87
}
88
89
void bpspatch::source(const uint8_t *data, unsigned size) {
90
sourceData = data;
91
sourceSize = size;
92
}
93
94
void bpspatch::target(uint8_t *data, unsigned size) {
95
targetData = data;
96
targetSize = size;
97
}
98
99
bool bpspatch::modify(const string &filename) {
100
if(modifyFile.open(filename, filemap::mode::read) == false) return false;
101
return modify(modifyFile.data(), modifyFile.size());
102
}
103
104
bool bpspatch::source(const string &filename) {
105
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
106
source(sourceFile.data(), sourceFile.size());
107
return true;
108
}
109
110
bool bpspatch::target(const string &filename) {
111
file fp;
112
if(fp.open(filename, file::mode::write) == false) return false;
113
fp.truncate(modifyTargetSize);
114
fp.close();
115
116
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
117
target(targetFile.data(), targetFile.size());
118
return true;
119
}
120
121
string bpspatch::metadata() const {
122
return metadataString;
123
}
124
125
unsigned bpspatch::size() const {
126
return modifyTargetSize;
127
}
128
129
bpspatch::result bpspatch::apply() {
130
if(modifySize < 19) return result::patch_too_small;
131
132
uint32_t modifyChecksum = ~0, targetChecksum = ~0;
133
unsigned modifyOffset = 0, sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
134
135
auto read = [&]() -> uint8_t {
136
uint8_t data = modifyData[modifyOffset++];
137
modifyChecksum = crc32_adjust(modifyChecksum, data);
138
return data;
139
};
140
141
auto decode = [&]() -> uint64_t {
142
uint64_t data = 0, shift = 1;
143
while(true) {
144
uint8_t x = read();
145
data += (x & 0x7f) * shift;
146
if(x & 0x80) break;
147
shift <<= 7;
148
data += shift;
149
}
150
return data;
151
};
152
153
auto write = [&](uint8_t data) {
154
targetData[outputOffset++] = data;
155
targetChecksum = crc32_adjust(targetChecksum, data);
156
};
157
158
if(read() != 'B') return result::patch_invalid_header;
159
if(read() != 'P') return result::patch_invalid_header;
160
if(read() != 'S') return result::patch_invalid_header;
161
if(read() != '1') return result::patch_invalid_header;
162
163
modifySourceSize = decode();
164
modifyTargetSize = decode();
165
modifyMarkupSize = decode();
166
for(unsigned n = 0; n < modifyMarkupSize; n++) read();
167
168
if(modifySourceSize > sourceSize) return result::source_too_small;
169
if(modifyTargetSize > targetSize) return result::target_too_small;
170
171
while(modifyOffset < modifySize - 12) {
172
unsigned length = decode();
173
unsigned mode = length & 3;
174
length = (length >> 2) + 1;
175
176
switch(mode) {
177
case SourceRead:
178
while(length--) write(sourceData[outputOffset]);
179
break;
180
case TargetRead:
181
while(length--) write(read());
182
break;
183
case SourceCopy:
184
case TargetCopy:
185
signed offset = decode();
186
bool negative = offset & 1;
187
offset >>= 1;
188
if(negative) offset = -offset;
189
190
if(mode == SourceCopy) {
191
sourceRelativeOffset += offset;
192
while(length--) write(sourceData[sourceRelativeOffset++]);
193
} else {
194
targetRelativeOffset += offset;
195
while(length--) write(targetData[targetRelativeOffset++]);
196
}
197
break;
198
}
199
}
200
201
uint32_t modifySourceChecksum = 0, modifyTargetChecksum = 0, modifyModifyChecksum = 0;
202
for(unsigned n = 0; n < 32; n += 8) modifySourceChecksum |= read() << n;
203
for(unsigned n = 0; n < 32; n += 8) modifyTargetChecksum |= read() << n;
204
uint32_t checksum = ~modifyChecksum;
205
for(unsigned n = 0; n < 32; n += 8) modifyModifyChecksum |= read() << n;
206
207
uint32_t sourceChecksum = crc32_calculate(sourceData, modifySourceSize);
208
targetChecksum = ~targetChecksum;
209
210
if(sourceChecksum != modifySourceChecksum) return result::source_checksum_invalid;
211
if(targetChecksum != modifyTargetChecksum) return result::target_checksum_invalid;
212
if(checksum != modifyModifyChecksum) return result::patch_checksum_invalid;
213
214
return result::success;
215
}
216
217
}
218
219
#endif
220
221