generated from 32blit/32blit-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbyte-killer.hpp
178 lines (147 loc) · 5.15 KB
/
byte-killer.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#pragma once
#include <cstdint>
struct ByteKiller {
private:
uint32_t bit_stream;
uint32_t crc;
uint8_t *ps;
uint8_t *pd;
uint16_t read_uint16_bigendian(const void* p) {
const uint8_t* b = (const uint8_t*)p;
return (b[0] << 8) | b[1];
}
uint32_t read_uint32_bigendian(const void* p) {
const uint8_t* b = (const uint8_t*)p;
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
}
bool get_stream_bit() {
bool result;
if(bit_stream == 0b1) {
// the final bit tells us we're at the end of this block
// so we need to load in the next block
bit_stream = read_uint32_bigendian(ps); ps -= 4;
crc ^= bit_stream;
// grab the low command bit and shift the stream
result = bit_stream & 0b1;
bit_stream >>= 1;
// set the high bit so we detect the next time we run out
// of command bits
bit_stream |= 0x80000000;
}else{
// grab the low command bit and shift the stream
result = bit_stream & 0b1;
bit_stream >>= 1;
}
return result;
}
uint16_t get_value(uint8_t bit_count) {
uint16_t value = 0;
while(bit_count--) {
value <<= 1;
value |= get_stream_bit() ? 0b1 : 0b0;
}
return value;
}
void copy(uint16_t count) {
while(count--) {
*pd = get_value(8);
pd--;
}
}
void repeat_from_offset(uint16_t count, uint16_t offset) {
while(count--) {
*pd = *(pd + offset);
pd--;
}
}
public:
ByteKiller() = default;
bool unpack(uint8_t *buffer, uint32_t packed_size) {
// the data is unpacked from end to start using two pointers
// the source pointer `ps` and destination pointer `pd`
// because the packed data is smaller than the unpacked data
// it can be unpacked inline in the same buffer without any risk
// over the destination pointer overruning the source pointer.
// effectively both pointers "race" to the start of the buffer
// meeting there once the unpacking is complete.
// source pointer starts by pointing at the last 32-bit word of
// the packed resource data
ps = buffer + packed_size - 4;
// the last 32-bits of the source data contain a 32-bit unsigned
// integer which tells us the unpacked size of the data
uint32_t unpacked_size = read_uint32_bigendian(ps); ps -= 4;
// destination pointer starts by pointing at the last byte of
// the unpacked resource memory area
pd = buffer + unpacked_size - 1;
// crc is only used to confirm file loaded correctly, it can be
// ignored with regard to the actual unpacking of the data
crc = read_uint32_bigendian(ps); ps -= 4;
// high bit is always set in `command` when read from the file, as
// the `command` bits are shifted off one by one it needs the high
// bit set to detect when the final shift occurs and to then
// fetch the next command
bit_stream = read_uint32_bigendian(ps); ps -= 4;
crc ^= bit_stream;
// the stream is composed of a mixture of commands and data
// the commands are variable length encoded (either two or three
// bits) and tell the unpacker what to do with the following data
//
// all commands boil down to two options; either copy bytes from
// the bitstream (copy() method) or copy bytes from the unpacked
// buffer at the specified offset (repeat_from_offset())
do {
bool b0 = get_stream_bit();
if(b0) {
bool b1 = get_stream_bit();
bool b2 = get_stream_bit();
if(b1 && b2) {
// 111 xxxxxxxx
// copy between 9 and 275 (xxx + 9) bytes from bitstream to destination
uint16_t count = get_value(8) + 9;
copy(count);
}
if(b1 && !b2) {
// 110 xxxxxxxx oooooooooooo
// repeat between 2 and 256 bytes from destination + offset to destination
uint16_t count = get_value(8) + 1;
uint16_t offset = get_value(12);
repeat_from_offset(count, offset);
}
if(!b1 && b2) {
// 101 oooooooooo
// repeat 4 bytes from from destination + offset to destination
uint16_t count = 4;
uint16_t offset = get_value(10);
repeat_from_offset(count, offset);
}
if(!b1 && !b2) {
// 100 ooooooooo
// repeat 3 bytes from from destination + offset to destination
uint16_t count = 3;
uint16_t offset = get_value(9);
repeat_from_offset(count, offset);
}
}
if(!b0) {
bool b1 = get_stream_bit();
if(b1) {
// 11 oooooooo
// repeat 2 bytes from from destination + offset to destination
uint16_t offset = get_value(8);
repeat_from_offset(2, offset);
}
if(!b1) {
// 00 xxx
// copy between 2 and 8 (xxx + 1) bytes from bitstream to destination
uint16_t count = get_value(3) + 1;
copy(count);
}
}
if (pd - buffer < 100) {
static int a = 0;
a++;
}
} while((pd - buffer) != -1); // until we reach the end of the bitstream
return crc == 0;
}
};