Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/nall/lzss.hpp
2 views
1
#ifndef NALL_LZSS_HPP
2
#define NALL_LZSS_HPP
3
4
#include <nall/file.hpp>
5
#include <nall/filemap.hpp>
6
#include <nall/stdint.hpp>
7
#include <nall/string.hpp>
8
9
namespace nall {
10
11
//19:5 pulldown
12
//8:1 marker: d7-d0
13
//length: { 4 - 35 }, offset: { 1 - 0x80000 }
14
//4-byte file size header
15
//little-endian encoding
16
struct lzss {
17
inline void source(const uint8_t *data, unsigned size);
18
inline bool source(const string &filename);
19
inline unsigned size() const;
20
inline bool compress(const string &filename);
21
inline bool decompress(uint8_t *targetData, unsigned targetSize);
22
inline bool decompress(const string &filename);
23
24
protected:
25
struct Node {
26
unsigned offset;
27
Node *next;
28
inline Node() : offset(0), next(nullptr) {}
29
inline ~Node() { if(next) delete next; }
30
} *tree[65536];
31
32
filemap sourceFile;
33
const uint8_t *sourceData;
34
unsigned sourceSize;
35
36
public:
37
inline lzss() : sourceData(nullptr), sourceSize(0) {}
38
};
39
40
void lzss::source(const uint8_t *data, unsigned size) {
41
sourceData = data;
42
sourceSize = size;
43
}
44
45
bool lzss::source(const string &filename) {
46
if(sourceFile.open(filename, filemap::mode::read) == false) return false;
47
sourceData = sourceFile.data();
48
sourceSize = sourceFile.size();
49
return true;
50
}
51
52
unsigned lzss::size() const {
53
unsigned size = 0;
54
if(sourceSize < 4) return size;
55
for(unsigned n = 0; n < 32; n += 8) size |= sourceData[n >> 3] << n;
56
return size;
57
}
58
59
bool lzss::compress(const string &filename) {
60
file targetFile;
61
if(targetFile.open(filename, file::mode::write) == false) return false;
62
63
for(unsigned n = 0; n < 32; n += 8) targetFile.write(sourceSize >> n);
64
for(unsigned n = 0; n < 65536; n++) tree[n] = 0;
65
66
uint8_t buffer[25];
67
unsigned sourceOffset = 0;
68
69
while(sourceOffset < sourceSize) {
70
uint8_t mask = 0x00;
71
unsigned bufferOffset = 1;
72
73
for(unsigned iteration = 0; iteration < 8; iteration++) {
74
if(sourceOffset >= sourceSize) break;
75
76
uint16_t symbol = sourceData[sourceOffset + 0];
77
if(sourceOffset < sourceSize - 1) symbol |= sourceData[sourceOffset + 1] << 8;
78
Node *node = tree[symbol];
79
unsigned maxLength = 0, maxOffset = 0;
80
81
while(node) {
82
if(node->offset < sourceOffset - 0x80000) {
83
//out-of-range: all subsequent nodes will also be, so free up their memory
84
if(node->next) { delete node->next; node->next = 0; }
85
break;
86
}
87
88
unsigned length = 0, x = sourceOffset, y = node->offset;
89
while(length < 35 && x < sourceSize && sourceData[x++] == sourceData[y++]) length++;
90
if(length > maxLength) maxLength = length, maxOffset = node->offset;
91
if(length == 35) break;
92
93
node = node->next;
94
}
95
96
//attach current symbol to top of tree for subsequent searches
97
node = new Node;
98
node->offset = sourceOffset;
99
node->next = tree[symbol];
100
tree[symbol] = node;
101
102
if(maxLength < 4) {
103
buffer[bufferOffset++] = sourceData[sourceOffset++];
104
} else {
105
unsigned output = ((maxLength - 4) << 19) | (sourceOffset - 1 - maxOffset);
106
for(unsigned n = 0; n < 24; n += 8) buffer[bufferOffset++] = output >> n;
107
mask |= 0x80 >> iteration;
108
sourceOffset += maxLength;
109
}
110
}
111
112
buffer[0] = mask;
113
targetFile.write(buffer, bufferOffset);
114
}
115
116
sourceFile.close();
117
targetFile.close();
118
return true;
119
}
120
121
bool lzss::decompress(uint8_t *targetData, unsigned targetSize) {
122
if(targetSize < size()) return false;
123
124
unsigned sourceOffset = 4, targetOffset = 0;
125
while(sourceOffset < sourceSize) {
126
uint8_t mask = sourceData[sourceOffset++];
127
128
for(unsigned iteration = 0; iteration < 8; iteration++) {
129
if(sourceOffset >= sourceSize) break;
130
131
if((mask & (0x80 >> iteration)) == 0) {
132
targetData[targetOffset++] = sourceData[sourceOffset++];
133
} else {
134
unsigned code = 0;
135
for(unsigned n = 0; n < 24; n += 8) code |= sourceData[sourceOffset++] << n;
136
unsigned length = (code >> 19) + 4;
137
unsigned offset = targetOffset - 1 - (code & 0x7ffff);
138
while(length--) targetData[targetOffset++] = targetData[offset++];
139
}
140
}
141
}
142
}
143
144
bool lzss::decompress(const string &filename) {
145
if(sourceSize < 4) return false;
146
unsigned targetSize = size();
147
148
file fp;
149
if(fp.open(filename, file::mode::write) == false) return false;
150
fp.truncate(targetSize);
151
fp.close();
152
153
filemap targetFile;
154
if(targetFile.open(filename, filemap::mode::readwrite) == false) return false;
155
uint8_t *targetData = targetFile.data();
156
157
bool result = decompress(targetData, targetSize);
158
sourceFile.close();
159
targetFile.close();
160
return result;
161
}
162
163
}
164
165
#endif
166
167