Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/bps/delta.hpp
2 views
1
#ifndef NALL_BPS_DELTA_HPP
2
#define NALL_BPS_DELTA_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 bpsdelta {
13
inline void source(const uint8_t *data, unsigned size);
14
inline void target(const uint8_t *data, unsigned size);
15
16
inline bool source(const string &filename);
17
inline bool target(const string &filename);
18
inline bool create(const string &filename, const string &metadata = "");
19
20
protected:
21
enum : unsigned { SourceRead, TargetRead, SourceCopy, TargetCopy };
22
enum : unsigned { Granularity = 1 };
23
24
struct Node {
25
unsigned offset;
26
Node *next;
27
inline Node() : offset(0), next(nullptr) {}
28
inline ~Node() { if(next) delete next; }
29
};
30
31
filemap sourceFile;
32
const uint8_t *sourceData;
33
unsigned sourceSize;
34
35
filemap targetFile;
36
const uint8_t *targetData;
37
unsigned targetSize;
38
};
39
40
void bpsdelta::source(const uint8_t *data, unsigned size) {
41
sourceData = data;
42
sourceSize = size;
43
}
44
45
void bpsdelta::target(const uint8_t *data, unsigned size) {
46
targetData = data;
47
targetSize = size;
48
}
49
50
bool bpsdelta::source(const string &filename) {
51
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
52
source(sourceFile.data(), sourceFile.size());
53
return true;
54
}
55
56
bool bpsdelta::target(const string &filename) {
57
if(targetFile.open(filename, filemap::mode::read) == false) return false;
58
target(targetFile.data(), targetFile.size());
59
return true;
60
}
61
62
bool bpsdelta::create(const string &filename, const string &metadata) {
63
file modifyFile;
64
if(modifyFile.open(filename, file::mode::write) == false) return false;
65
66
uint32_t sourceChecksum = ~0, modifyChecksum = ~0;
67
unsigned sourceRelativeOffset = 0, targetRelativeOffset = 0, outputOffset = 0;
68
69
auto write = [&](uint8_t data) {
70
modifyFile.write(data);
71
modifyChecksum = crc32_adjust(modifyChecksum, data);
72
};
73
74
auto encode = [&](uint64_t data) {
75
while(true) {
76
uint64_t x = data & 0x7f;
77
data >>= 7;
78
if(data == 0) {
79
write(0x80 | x);
80
break;
81
}
82
write(x);
83
data--;
84
}
85
};
86
87
write('B');
88
write('P');
89
write('S');
90
write('1');
91
92
encode(sourceSize);
93
encode(targetSize);
94
95
unsigned markupSize = metadata.length();
96
encode(markupSize);
97
for(unsigned n = 0; n < markupSize; n++) write(metadata[n]);
98
99
Node *sourceTree[65536], *targetTree[65536];
100
for(unsigned n = 0; n < 65536; n++) sourceTree[n] = 0, targetTree[n] = 0;
101
102
//source tree creation
103
for(unsigned offset = 0; offset < sourceSize; offset++) {
104
uint16_t symbol = sourceData[offset + 0];
105
sourceChecksum = crc32_adjust(sourceChecksum, symbol);
106
if(offset < sourceSize - 1) symbol |= sourceData[offset + 1] << 8;
107
Node *node = new Node;
108
node->offset = offset;
109
node->next = sourceTree[symbol];
110
sourceTree[symbol] = node;
111
}
112
113
unsigned targetReadLength = 0;
114
115
auto targetReadFlush = [&]() {
116
if(targetReadLength) {
117
encode(TargetRead | ((targetReadLength - 1) << 2));
118
unsigned offset = outputOffset - targetReadLength;
119
while(targetReadLength) write(targetData[offset++]), targetReadLength--;
120
}
121
};
122
123
while(outputOffset < targetSize) {
124
unsigned maxLength = 0, maxOffset = 0, mode = TargetRead;
125
126
uint16_t symbol = targetData[outputOffset + 0];
127
if(outputOffset < targetSize - 1) symbol |= targetData[outputOffset + 1] << 8;
128
129
{ //source read
130
unsigned length = 0, offset = outputOffset;
131
while(offset < sourceSize && offset < targetSize && sourceData[offset] == targetData[offset]) {
132
length++;
133
offset++;
134
}
135
if(length > maxLength) maxLength = length, mode = SourceRead;
136
}
137
138
{ //source copy
139
Node *node = sourceTree[symbol];
140
while(node) {
141
unsigned length = 0, x = node->offset, y = outputOffset;
142
while(x < sourceSize && y < targetSize && sourceData[x++] == targetData[y++]) length++;
143
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = SourceCopy;
144
node = node->next;
145
}
146
}
147
148
{ //target copy
149
Node *node = targetTree[symbol];
150
while(node) {
151
unsigned length = 0, x = node->offset, y = outputOffset;
152
while(y < targetSize && targetData[x++] == targetData[y++]) length++;
153
if(length > maxLength) maxLength = length, maxOffset = node->offset, mode = TargetCopy;
154
node = node->next;
155
}
156
157
//target tree append
158
node = new Node;
159
node->offset = outputOffset;
160
node->next = targetTree[symbol];
161
targetTree[symbol] = node;
162
}
163
164
{ //target read
165
if(maxLength < 4) {
166
maxLength = min((unsigned)Granularity, targetSize - outputOffset);
167
mode = TargetRead;
168
}
169
}
170
171
if(mode != TargetRead) targetReadFlush();
172
173
switch(mode) {
174
case SourceRead:
175
encode(SourceRead | ((maxLength - 1) << 2));
176
break;
177
case TargetRead:
178
//delay write to group sequential TargetRead commands into one
179
targetReadLength += maxLength;
180
break;
181
case SourceCopy:
182
case TargetCopy:
183
encode(mode | ((maxLength - 1) << 2));
184
signed relativeOffset;
185
if(mode == SourceCopy) {
186
relativeOffset = maxOffset - sourceRelativeOffset;
187
sourceRelativeOffset = maxOffset + maxLength;
188
} else {
189
relativeOffset = maxOffset - targetRelativeOffset;
190
targetRelativeOffset = maxOffset + maxLength;
191
}
192
encode((relativeOffset < 0) | (abs(relativeOffset) << 1));
193
break;
194
}
195
196
outputOffset += maxLength;
197
}
198
199
targetReadFlush();
200
201
sourceChecksum = ~sourceChecksum;
202
for(unsigned n = 0; n < 32; n += 8) write(sourceChecksum >> n);
203
uint32_t targetChecksum = crc32_calculate(targetData, targetSize);
204
for(unsigned n = 0; n < 32; n += 8) write(targetChecksum >> n);
205
uint32_t outputChecksum = ~modifyChecksum;
206
for(unsigned n = 0; n < 32; n += 8) write(outputChecksum >> n);
207
208
modifyFile.close();
209
return true;
210
}
211
212
}
213
214
#endif
215
216