@oh-my-pi/pi-utils 17.4.0 → 17.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/LICENSE +22 -0
- package/THIRD-PARTY-NOTICES.txt +22909 -0
- package/dist/types/ar/arj.d.ts +5 -0
- package/dist/types/ar/asar.d.ts +7 -0
- package/dist/types/ar/bytes.d.ts +20 -0
- package/dist/types/ar/cab.d.ts +5 -0
- package/dist/types/ar/checksums.d.ts +10 -0
- package/dist/types/ar/codecs/bzip2.d.ts +4 -0
- package/dist/types/ar/codecs/gzip.d.ts +6 -0
- package/dist/types/ar/codecs/lzma.d.ts +6 -0
- package/dist/types/ar/codecs/lzw.d.ts +4 -0
- package/dist/types/ar/codecs/lzx.d.ts +7 -0
- package/dist/types/ar/codecs/xz.d.ts +4 -0
- package/dist/types/ar/codecs/zstd.d.ts +6 -0
- package/dist/types/ar/cpio.d.ts +7 -0
- package/dist/types/ar/deb.d.ts +5 -0
- package/dist/types/ar/entries.d.ts +22 -0
- package/dist/types/ar/error.d.ts +8 -0
- package/dist/types/ar/index.d.ts +11 -0
- package/dist/types/ar/iso.d.ts +5 -0
- package/dist/types/ar/limits.d.ts +33 -0
- package/dist/types/ar/lzh.d.ts +7 -0
- package/dist/types/ar/open.d.ts +45 -0
- package/dist/types/ar/paths.d.ts +18 -0
- package/dist/types/ar/rar/rar4-decoder.d.ts +6 -0
- package/dist/types/ar/rar/rar5-decoder.d.ts +6 -0
- package/dist/types/ar/rar.d.ts +5 -0
- package/dist/types/ar/reader.d.ts +28 -0
- package/dist/types/ar/registry.d.ts +17 -0
- package/dist/types/ar/rpm.d.ts +5 -0
- package/dist/types/ar/sevenzip/decode.d.ts +33 -0
- package/dist/types/ar/sevenzip.d.ts +5 -0
- package/dist/types/ar/source.d.ts +54 -0
- package/dist/types/ar/tar.d.ts +9 -0
- package/dist/types/ar/types.d.ts +99 -0
- package/dist/types/ar/unix-ar.d.ts +7 -0
- package/dist/types/ar/write.d.ts +10 -0
- package/dist/types/ar/zip.d.ts +10 -0
- package/dist/types/postmortem.d.ts +33 -0
- package/package.json +9 -3
- package/src/ar/arj.ts +314 -0
- package/src/ar/asar.ts +480 -0
- package/src/ar/bytes.ts +78 -0
- package/src/ar/cab.ts +377 -0
- package/src/ar/checksums.ts +62 -0
- package/src/ar/codecs/bzip2.ts +489 -0
- package/src/ar/codecs/gzip.ts +25 -0
- package/src/ar/codecs/lzma.ts +437 -0
- package/src/ar/codecs/lzw.ts +191 -0
- package/src/ar/codecs/lzx.ts +366 -0
- package/src/ar/codecs/xz.ts +522 -0
- package/src/ar/codecs/zstd.ts +25 -0
- package/src/ar/cpio.ts +389 -0
- package/src/ar/deb.ts +160 -0
- package/src/ar/entries.ts +97 -0
- package/src/ar/error.ts +11 -0
- package/src/ar/index.ts +16 -0
- package/src/ar/iso.ts +712 -0
- package/src/ar/limits.ts +80 -0
- package/src/ar/lzh.ts +659 -0
- package/src/ar/open.ts +225 -0
- package/src/ar/paths.ts +70 -0
- package/src/ar/rar/rar4-decoder.ts +459 -0
- package/src/ar/rar/rar5-decoder.ts +400 -0
- package/src/ar/rar.ts +735 -0
- package/src/ar/reader.ts +162 -0
- package/src/ar/registry.ts +208 -0
- package/src/ar/rpm.ts +320 -0
- package/src/ar/sevenzip/decode.ts +239 -0
- package/src/ar/sevenzip.ts +634 -0
- package/src/ar/source.ts +190 -0
- package/src/ar/tar.ts +771 -0
- package/src/ar/types.ts +131 -0
- package/src/ar/unix-ar.ts +312 -0
- package/src/ar/write.ts +55 -0
- package/src/ar/zip.ts +719 -0
- package/src/browsers.ts +2 -149
- package/src/docx/converter.ts +9 -9
- package/src/postmortem.ts +74 -0
- package/dist/types/docx/zip.d.ts +0 -6
- package/src/docx/zip.ts +0 -87
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Decompression structure adapted from unrar5j by Stephane Bury
|
|
3
|
+
* (Copyright 2025, Apache License 2.0).
|
|
4
|
+
*/
|
|
5
|
+
import { crc32 } from "../checksums";
|
|
6
|
+
import { ArchiveError } from "../error";
|
|
7
|
+
|
|
8
|
+
const MAIN_SIZE = 299;
|
|
9
|
+
const DIST_SIZE = 60;
|
|
10
|
+
const LOW_DIST_SIZE = 17;
|
|
11
|
+
const LEN_SIZE = 28;
|
|
12
|
+
const TOTAL_SIZE = MAIN_SIZE + DIST_SIZE + LOW_DIST_SIZE + LEN_SIZE;
|
|
13
|
+
const LEN_BASE = [
|
|
14
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224,
|
|
15
|
+
] as const;
|
|
16
|
+
const LEN_BITS = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5] as const;
|
|
17
|
+
const SHORT_DIST_BASE = [0, 4, 8, 16, 32, 64, 128, 192] as const;
|
|
18
|
+
const SHORT_DIST_BITS = [2, 2, 3, 4, 5, 6, 6, 6] as const;
|
|
19
|
+
const DIST_BASE = new Int32Array(DIST_SIZE);
|
|
20
|
+
const DIST_BITS = new Uint8Array(DIST_SIZE);
|
|
21
|
+
{
|
|
22
|
+
const counts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12];
|
|
23
|
+
let distance = 0;
|
|
24
|
+
let slot = 0;
|
|
25
|
+
for (let bits = 0; bits < counts.length; bits++) {
|
|
26
|
+
for (let count = 0; count < counts[bits]!; count++) {
|
|
27
|
+
DIST_BASE[slot] = distance;
|
|
28
|
+
DIST_BITS[slot] = bits;
|
|
29
|
+
distance += 2 ** bits;
|
|
30
|
+
slot++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class Bits {
|
|
36
|
+
readonly bytes: Uint8Array;
|
|
37
|
+
pos = 0;
|
|
38
|
+
bit = 0;
|
|
39
|
+
|
|
40
|
+
constructor(packed: Uint8Array) {
|
|
41
|
+
this.bytes = new Uint8Array(packed.byteLength + 4);
|
|
42
|
+
this.bytes.set(packed);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
read(count: number): number {
|
|
46
|
+
if (count === 0) return 0;
|
|
47
|
+
let value = 0;
|
|
48
|
+
for (let left = count; left > 0; ) {
|
|
49
|
+
if (this.pos >= this.bytes.byteLength - 4) corrupt("truncated compressed data");
|
|
50
|
+
const take = Math.min(left, 8 - this.bit);
|
|
51
|
+
value = value * 2 ** take + ((this.bytes[this.pos]! >>> (8 - this.bit - take)) & (2 ** take - 1));
|
|
52
|
+
this.bit += take;
|
|
53
|
+
left -= take;
|
|
54
|
+
if (this.bit === 8) {
|
|
55
|
+
this.bit = 0;
|
|
56
|
+
this.pos++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
align(): void {
|
|
63
|
+
if (this.bit !== 0) {
|
|
64
|
+
this.pos++;
|
|
65
|
+
this.bit = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class Huffman {
|
|
71
|
+
#counts = new Int32Array(16);
|
|
72
|
+
#firstCode = new Int32Array(16);
|
|
73
|
+
#firstSymbol = new Int32Array(16);
|
|
74
|
+
#symbols: Int32Array;
|
|
75
|
+
#ready = false;
|
|
76
|
+
|
|
77
|
+
constructor(size: number) {
|
|
78
|
+
this.#symbols = new Int32Array(size);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
build(lengths: Uint8Array, offset: number, size: number): void {
|
|
82
|
+
this.#counts.fill(0);
|
|
83
|
+
for (let i = 0; i < size; i++) {
|
|
84
|
+
const length = lengths[offset + i]! & 15;
|
|
85
|
+
if (length !== 0) this.#counts[length]++;
|
|
86
|
+
}
|
|
87
|
+
let code = 0;
|
|
88
|
+
let symbolPosition = 0;
|
|
89
|
+
for (let bits = 1; bits <= 15; bits++) {
|
|
90
|
+
code = (code + this.#counts[bits - 1]!) << 1;
|
|
91
|
+
this.#firstCode[bits] = code;
|
|
92
|
+
this.#firstSymbol[bits] = symbolPosition;
|
|
93
|
+
symbolPosition += this.#counts[bits]!;
|
|
94
|
+
}
|
|
95
|
+
const next = this.#firstSymbol.slice();
|
|
96
|
+
for (let symbol = 0; symbol < size; symbol++) {
|
|
97
|
+
const length = lengths[offset + symbol]! & 15;
|
|
98
|
+
if (length !== 0) this.#symbols[next[length]++] = symbol;
|
|
99
|
+
}
|
|
100
|
+
this.#ready = symbolPosition !== 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
decode(bits: Bits): number {
|
|
104
|
+
if (!this.#ready) corrupt("empty Huffman table");
|
|
105
|
+
let code = 0;
|
|
106
|
+
for (let length = 1; length <= 15; length++) {
|
|
107
|
+
code = (code << 1) | bits.read(1);
|
|
108
|
+
const index = code - this.#firstCode[length]!;
|
|
109
|
+
if (index >= 0 && index < this.#counts[length]!) return this.#symbols[this.#firstSymbol[length]! + index]!;
|
|
110
|
+
}
|
|
111
|
+
corrupt("invalid Huffman symbol");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface PendingFilter {
|
|
116
|
+
type: number;
|
|
117
|
+
start: number;
|
|
118
|
+
size: number;
|
|
119
|
+
channels: number;
|
|
120
|
+
fileOffset: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Stateful decoder for the RAR 2.9 LZ/Huffman algorithm used by RAR3/4 archives. */
|
|
124
|
+
export class Rar4Decoder {
|
|
125
|
+
#history = new Uint8Array(0);
|
|
126
|
+
#oldDistances = [0, 0, 0, 0];
|
|
127
|
+
#lastDistance = 0;
|
|
128
|
+
#lastLength = 0;
|
|
129
|
+
#previousLowDistance = 0;
|
|
130
|
+
#lowDistanceRepeats = 0;
|
|
131
|
+
#carriedLengths = new Uint8Array(TOTAL_SIZE);
|
|
132
|
+
#filterTypes: number[] = [];
|
|
133
|
+
#filterLengths: number[] = [];
|
|
134
|
+
#lastFilter = 0;
|
|
135
|
+
|
|
136
|
+
reset(): void {
|
|
137
|
+
this.#history = new Uint8Array(0);
|
|
138
|
+
this.#oldDistances = [0, 0, 0, 0];
|
|
139
|
+
this.#lastDistance = 0;
|
|
140
|
+
this.#lastLength = 0;
|
|
141
|
+
this.#previousLowDistance = 0;
|
|
142
|
+
this.#lowDistanceRepeats = 0;
|
|
143
|
+
this.#carriedLengths.fill(0);
|
|
144
|
+
this.#filterTypes = [];
|
|
145
|
+
this.#filterLengths = [];
|
|
146
|
+
this.#lastFilter = 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
decode(
|
|
150
|
+
packed: Uint8Array,
|
|
151
|
+
unpackedSize: number,
|
|
152
|
+
dictionarySize: number,
|
|
153
|
+
solid: boolean,
|
|
154
|
+
version: number,
|
|
155
|
+
): Uint8Array {
|
|
156
|
+
if (version < 29) throw new ArchiveError(`Unsupported RAR4 compression algorithm version ${version}`);
|
|
157
|
+
if (!solid) this.reset();
|
|
158
|
+
const prior =
|
|
159
|
+
this.#history.byteLength > dictionarySize
|
|
160
|
+
? this.#history.subarray(this.#history.byteLength - dictionarySize)
|
|
161
|
+
: this.#history;
|
|
162
|
+
const output = new Uint8Array(prior.byteLength + unpackedSize + 260);
|
|
163
|
+
output.set(prior);
|
|
164
|
+
const outputStart = prior.byteLength;
|
|
165
|
+
const outputEnd = outputStart + unpackedSize;
|
|
166
|
+
let outputPosition = outputStart;
|
|
167
|
+
const bits = new Bits(packed);
|
|
168
|
+
const main = new Huffman(MAIN_SIZE);
|
|
169
|
+
const distanceDecoder = new Huffman(DIST_SIZE);
|
|
170
|
+
const lowDistanceDecoder = new Huffman(LOW_DIST_SIZE);
|
|
171
|
+
const lengthDecoder = new Huffman(LEN_SIZE);
|
|
172
|
+
const filters: PendingFilter[] = [];
|
|
173
|
+
|
|
174
|
+
const readTables = (): void => {
|
|
175
|
+
bits.align();
|
|
176
|
+
if (bits.read(1) !== 0) throw new ArchiveError("Unsupported RAR4 PPMd compressed block");
|
|
177
|
+
const keepPrevious = bits.read(1) !== 0;
|
|
178
|
+
this.#previousLowDistance = 0;
|
|
179
|
+
this.#lowDistanceRepeats = 0;
|
|
180
|
+
if (!keepPrevious) this.#carriedLengths.fill(0);
|
|
181
|
+
const lengths = readLengthTable(bits, this.#carriedLengths, TOTAL_SIZE);
|
|
182
|
+
let offset = 0;
|
|
183
|
+
main.build(lengths, offset, MAIN_SIZE);
|
|
184
|
+
offset += MAIN_SIZE;
|
|
185
|
+
distanceDecoder.build(lengths, offset, DIST_SIZE);
|
|
186
|
+
offset += DIST_SIZE;
|
|
187
|
+
lowDistanceDecoder.build(lengths, offset, LOW_DIST_SIZE);
|
|
188
|
+
offset += LOW_DIST_SIZE;
|
|
189
|
+
lengthDecoder.build(lengths, offset, LEN_SIZE);
|
|
190
|
+
this.#carriedLengths.set(lengths);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const copyMatch = (distance: number, length: number): void => {
|
|
194
|
+
if (distance <= 0 || distance > dictionarySize || distance > outputPosition) corrupt("invalid LZ distance");
|
|
195
|
+
length = Math.min(length, outputEnd - outputPosition);
|
|
196
|
+
for (let i = 0; i < length; i++) output[outputPosition + i] = output[outputPosition + i - distance]!;
|
|
197
|
+
outputPosition += length;
|
|
198
|
+
};
|
|
199
|
+
const rememberDistance = (distance: number): void => {
|
|
200
|
+
this.#oldDistances[3] = this.#oldDistances[2]!;
|
|
201
|
+
this.#oldDistances[2] = this.#oldDistances[1]!;
|
|
202
|
+
this.#oldDistances[1] = this.#oldDistances[0]!;
|
|
203
|
+
this.#oldDistances[0] = distance;
|
|
204
|
+
};
|
|
205
|
+
const readFilter = (): void => {
|
|
206
|
+
const firstByte = bits.read(8);
|
|
207
|
+
let size = (firstByte & 7) + 1;
|
|
208
|
+
if (size === 7) size = bits.read(8) + 7;
|
|
209
|
+
else if (size === 8) size = bits.read(16);
|
|
210
|
+
const code = new Uint8Array(size);
|
|
211
|
+
for (let index = 0; index < size; index++) code[index] = bits.read(8);
|
|
212
|
+
const vm = new VmBits(code);
|
|
213
|
+
let filterPosition: number;
|
|
214
|
+
if ((firstByte & 0x80) !== 0) {
|
|
215
|
+
filterPosition = vm.readData();
|
|
216
|
+
if (filterPosition === 0) {
|
|
217
|
+
this.#filterTypes = [];
|
|
218
|
+
this.#filterLengths = [];
|
|
219
|
+
} else filterPosition--;
|
|
220
|
+
} else {
|
|
221
|
+
filterPosition = this.#lastFilter;
|
|
222
|
+
}
|
|
223
|
+
if (filterPosition < 0 || filterPosition > this.#filterTypes.length) corrupt("invalid RarVM filter index");
|
|
224
|
+
this.#lastFilter = filterPosition;
|
|
225
|
+
const isNew = filterPosition === this.#filterTypes.length;
|
|
226
|
+
let start = vm.readData();
|
|
227
|
+
if ((firstByte & 0x40) !== 0) start += 258;
|
|
228
|
+
start += outputPosition - outputStart;
|
|
229
|
+
const blockSize = (firstByte & 0x20) !== 0 ? vm.readData() : (this.#filterLengths[filterPosition] ?? 0);
|
|
230
|
+
const registers = new Int32Array(7);
|
|
231
|
+
registers[3] = 0x3c000;
|
|
232
|
+
registers[4] = blockSize;
|
|
233
|
+
if ((firstByte & 0x10) !== 0) {
|
|
234
|
+
const mask = vm.read(7);
|
|
235
|
+
for (let register = 0; register < 7; register++) {
|
|
236
|
+
if ((mask & (1 << register)) !== 0) registers[register] = vm.readData();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
let type: number;
|
|
240
|
+
if (isNew) {
|
|
241
|
+
const programSize = vm.readData();
|
|
242
|
+
const program = new Uint8Array(programSize);
|
|
243
|
+
for (let index = 0; index < programSize; index++) program[index] = vm.read(8);
|
|
244
|
+
type = identifyFilter(program);
|
|
245
|
+
this.#filterTypes.push(type);
|
|
246
|
+
this.#filterLengths.push(blockSize);
|
|
247
|
+
} else {
|
|
248
|
+
type = this.#filterTypes[filterPosition]!;
|
|
249
|
+
this.#filterLengths[filterPosition] = blockSize;
|
|
250
|
+
}
|
|
251
|
+
if (type !== 1 && type !== 2 && type !== 6) {
|
|
252
|
+
throw new ArchiveError(`Unsupported RAR4 RarVM filter type ${type || "unknown"}`);
|
|
253
|
+
}
|
|
254
|
+
if (start < 0 || blockSize < 0 || start + blockSize > unpackedSize) corrupt("invalid RarVM filter range");
|
|
255
|
+
filters.push({ type, start, size: blockSize, channels: registers[0]!, fileOffset: registers[6]! });
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
readTables();
|
|
259
|
+
while (outputPosition < outputEnd) {
|
|
260
|
+
const symbol = main.decode(bits);
|
|
261
|
+
if (symbol < 256) {
|
|
262
|
+
output[outputPosition++] = symbol;
|
|
263
|
+
} else if (symbol >= 271) {
|
|
264
|
+
const slot = symbol - 271;
|
|
265
|
+
let length = LEN_BASE[slot]! + 3 + bits.read(LEN_BITS[slot]!);
|
|
266
|
+
const distanceSlot = distanceDecoder.decode(bits);
|
|
267
|
+
let distance = DIST_BASE[distanceSlot]! + 1;
|
|
268
|
+
const extraBits = DIST_BITS[distanceSlot]!;
|
|
269
|
+
if (extraBits !== 0) {
|
|
270
|
+
if (distanceSlot > 9) {
|
|
271
|
+
if (extraBits > 4) distance += bits.read(extraBits - 4) * 16;
|
|
272
|
+
if (this.#lowDistanceRepeats > 0) {
|
|
273
|
+
this.#lowDistanceRepeats--;
|
|
274
|
+
distance += this.#previousLowDistance;
|
|
275
|
+
} else {
|
|
276
|
+
const low = lowDistanceDecoder.decode(bits);
|
|
277
|
+
if (low === 16) {
|
|
278
|
+
this.#lowDistanceRepeats = 15;
|
|
279
|
+
distance += this.#previousLowDistance;
|
|
280
|
+
} else {
|
|
281
|
+
distance += low;
|
|
282
|
+
this.#previousLowDistance = low;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
} else distance += bits.read(extraBits);
|
|
286
|
+
}
|
|
287
|
+
if (distance >= 0x2000) length++;
|
|
288
|
+
if (distance >= 0x40000) length++;
|
|
289
|
+
rememberDistance(distance);
|
|
290
|
+
this.#lastDistance = distance;
|
|
291
|
+
this.#lastLength = length;
|
|
292
|
+
copyMatch(distance, length);
|
|
293
|
+
} else if (symbol === 256) {
|
|
294
|
+
if (bits.read(1) !== 0) {
|
|
295
|
+
readTables();
|
|
296
|
+
} else {
|
|
297
|
+
const newTable = bits.read(1) !== 0;
|
|
298
|
+
if (newTable) readTables();
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
} else if (symbol === 257) {
|
|
302
|
+
readFilter();
|
|
303
|
+
} else if (symbol === 258) {
|
|
304
|
+
if (this.#lastLength !== 0) copyMatch(this.#lastDistance, this.#lastLength);
|
|
305
|
+
} else if (symbol < 263) {
|
|
306
|
+
const index = symbol - 259;
|
|
307
|
+
const distance = this.#oldDistances[index]!;
|
|
308
|
+
for (let i = index; i > 0; i--) this.#oldDistances[i] = this.#oldDistances[i - 1]!;
|
|
309
|
+
this.#oldDistances[0] = distance;
|
|
310
|
+
const slot = lengthDecoder.decode(bits);
|
|
311
|
+
const length = LEN_BASE[slot]! + 2 + bits.read(LEN_BITS[slot]!);
|
|
312
|
+
this.#lastDistance = distance;
|
|
313
|
+
this.#lastLength = length;
|
|
314
|
+
copyMatch(distance, length);
|
|
315
|
+
} else {
|
|
316
|
+
const slot = symbol - 263;
|
|
317
|
+
const distance = SHORT_DIST_BASE[slot]! + 1 + bits.read(SHORT_DIST_BITS[slot]!);
|
|
318
|
+
rememberDistance(distance);
|
|
319
|
+
this.#lastDistance = distance;
|
|
320
|
+
this.#lastLength = 2;
|
|
321
|
+
copyMatch(distance, 2);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (outputPosition !== outputEnd)
|
|
325
|
+
corrupt(`decompressed size mismatch (${outputPosition - outputStart} != ${unpackedSize})`);
|
|
326
|
+
const extracted = output.slice(outputStart, outputEnd);
|
|
327
|
+
applyFilters(extracted, filters);
|
|
328
|
+
this.#history = output.slice(Math.max(0, outputEnd - dictionarySize), outputEnd);
|
|
329
|
+
return extracted;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
class VmBits {
|
|
334
|
+
readonly #bytes: Uint8Array;
|
|
335
|
+
#position = 0;
|
|
336
|
+
|
|
337
|
+
constructor(bytes: Uint8Array) {
|
|
338
|
+
this.#bytes = bytes;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
read(count: number): number {
|
|
342
|
+
let value = 0;
|
|
343
|
+
for (let left = count; left > 0; ) {
|
|
344
|
+
const bytePosition = this.#position >>> 3;
|
|
345
|
+
if (bytePosition >= this.#bytes.byteLength) corrupt("truncated RarVM code");
|
|
346
|
+
const bitPosition = this.#position & 7;
|
|
347
|
+
const take = Math.min(left, 8 - bitPosition);
|
|
348
|
+
value = value * 2 ** take + ((this.#bytes[bytePosition]! >>> (8 - bitPosition - take)) & (2 ** take - 1));
|
|
349
|
+
this.#position += take;
|
|
350
|
+
left -= take;
|
|
351
|
+
}
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
readData(): number {
|
|
356
|
+
const prefix = this.read(2);
|
|
357
|
+
if (prefix === 0) return this.read(4);
|
|
358
|
+
if (prefix === 1) {
|
|
359
|
+
const first = this.read(4);
|
|
360
|
+
if (first === 0) return this.read(8) | 0xffffff00 | 0;
|
|
361
|
+
return first * 16 + this.read(4);
|
|
362
|
+
}
|
|
363
|
+
if (prefix === 2) return this.read(16);
|
|
364
|
+
return this.read(16) * 0x10000 + this.read(16);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function identifyFilter(program: Uint8Array): number {
|
|
369
|
+
const checksum = crc32(program);
|
|
370
|
+
if (program.byteLength === 53 && checksum === 0xad576887) return 1;
|
|
371
|
+
if (program.byteLength === 57 && checksum === 0x3cd7e57e) return 2;
|
|
372
|
+
if (program.byteLength === 29 && checksum === 0x0e06077d) return 6;
|
|
373
|
+
if (program.byteLength === 120 && checksum === 0x3769893f) return 3;
|
|
374
|
+
if (program.byteLength === 149 && checksum === 0x1c2c5dc8) return 4;
|
|
375
|
+
if (program.byteLength === 216 && checksum === 0xbc85e701) return 5;
|
|
376
|
+
if (program.byteLength === 40 && checksum === 0x46b9c560) return 7;
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function applyFilters(output: Uint8Array, filters: PendingFilter[]): void {
|
|
381
|
+
for (const filter of filters) {
|
|
382
|
+
const data = output.subarray(filter.start, filter.start + filter.size);
|
|
383
|
+
if (filter.type === 6) {
|
|
384
|
+
const source = data.slice();
|
|
385
|
+
let sourcePosition = 0;
|
|
386
|
+
for (let channel = 0; channel < filter.channels; channel++) {
|
|
387
|
+
let previous = 0;
|
|
388
|
+
for (let position = channel; position < data.byteLength; position += filter.channels) {
|
|
389
|
+
previous = (previous - source[sourcePosition++]!) & 0xff;
|
|
390
|
+
data[position] = previous;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const compareOpcode = filter.type === 2 ? 0xe9 : 0xe8;
|
|
396
|
+
for (let position = 0; position + 4 < data.byteLength; ) {
|
|
397
|
+
const opcode = data[position++]!;
|
|
398
|
+
if (opcode !== 0xe8 && opcode !== compareOpcode) continue;
|
|
399
|
+
const offset = position + (filter.fileOffset >>> 0);
|
|
400
|
+
const address = readInt32(data, position);
|
|
401
|
+
if (address < 0) {
|
|
402
|
+
if (address + offset >= 0) writeInt32(data, position, address + 0x1000000);
|
|
403
|
+
} else if (address - 0x1000000 < 0) {
|
|
404
|
+
writeInt32(data, position, address - offset);
|
|
405
|
+
}
|
|
406
|
+
position += 4;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function readInt32(bytes: Uint8Array, offset: number): number {
|
|
412
|
+
return bytes[offset]! | (bytes[offset + 1]! << 8) | (bytes[offset + 2]! << 16) | (bytes[offset + 3]! << 24);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function writeInt32(bytes: Uint8Array, offset: number, value: number): void {
|
|
416
|
+
bytes[offset] = value;
|
|
417
|
+
bytes[offset + 1] = value >>> 8;
|
|
418
|
+
bytes[offset + 2] = value >>> 16;
|
|
419
|
+
bytes[offset + 3] = value >>> 24;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function readLengthTable(bits: Bits, previous: Uint8Array, size: number): Uint8Array {
|
|
423
|
+
const preLengths = new Uint8Array(20);
|
|
424
|
+
for (let index = 0; index < 20; index++) {
|
|
425
|
+
const length = bits.read(4);
|
|
426
|
+
if (length === 15) {
|
|
427
|
+
let zeroCount = bits.read(4);
|
|
428
|
+
if (zeroCount !== 0) {
|
|
429
|
+
zeroCount += 2;
|
|
430
|
+
while (zeroCount-- > 0 && index < 20) preLengths[index++] = 0;
|
|
431
|
+
index--;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
preLengths[index] = length;
|
|
436
|
+
}
|
|
437
|
+
const preTable = new Huffman(20);
|
|
438
|
+
preTable.build(preLengths, 0, 20);
|
|
439
|
+
const lengths = new Uint8Array(size);
|
|
440
|
+
for (let position = 0; position < size; ) {
|
|
441
|
+
const symbol = preTable.decode(bits);
|
|
442
|
+
if (symbol < 16) {
|
|
443
|
+
lengths[position] = (symbol + previous[position]!) & 15;
|
|
444
|
+
position++;
|
|
445
|
+
} else if (symbol === 16 || symbol === 17) {
|
|
446
|
+
let count = bits.read(symbol === 16 ? 3 : 7) + (symbol === 16 ? 3 : 11);
|
|
447
|
+
const value = position === 0 ? 0 : lengths[position - 1]!;
|
|
448
|
+
while (count-- > 0 && position < size) lengths[position++] = value;
|
|
449
|
+
} else {
|
|
450
|
+
let count = bits.read(symbol === 18 ? 3 : 7) + (symbol === 18 ? 3 : 11);
|
|
451
|
+
while (count-- > 0 && position < size) lengths[position++] = 0;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return lengths;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function corrupt(reason: string): never {
|
|
458
|
+
throw new ArchiveError(`Invalid RAR4 archive: ${reason}`);
|
|
459
|
+
}
|