@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,400 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Decompression structure adapted from unrar5j by Stephane Bury
|
|
3
|
+
* (Copyright 2025, Apache License 2.0).
|
|
4
|
+
*/
|
|
5
|
+
import { ArchiveError } from "../error";
|
|
6
|
+
|
|
7
|
+
const MAIN_SIZE = 306;
|
|
8
|
+
const DIST_SIZE = 80;
|
|
9
|
+
const ALIGN_SIZE = 16;
|
|
10
|
+
const LEN_SIZE = 44;
|
|
11
|
+
const TABLE_SIZE = MAIN_SIZE + DIST_SIZE + ALIGN_SIZE + LEN_SIZE;
|
|
12
|
+
const LEN_PLUS = [
|
|
13
|
+
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
14
|
+
3,
|
|
15
|
+
] as const;
|
|
16
|
+
const MAX_MATCH = 0x1004;
|
|
17
|
+
const MAX_FILTER_SIZE = 1 << 22;
|
|
18
|
+
|
|
19
|
+
class Bits {
|
|
20
|
+
readonly bytes: Uint8Array;
|
|
21
|
+
pos = 0;
|
|
22
|
+
bit = 0;
|
|
23
|
+
blockEnd = 0;
|
|
24
|
+
blockEndBit = 0;
|
|
25
|
+
|
|
26
|
+
constructor(bytes: Uint8Array) {
|
|
27
|
+
this.bytes = new Uint8Array(bytes.byteLength + 16);
|
|
28
|
+
this.bytes.set(bytes);
|
|
29
|
+
this.bytes.fill(0xff, bytes.byteLength);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
read(n: number): number {
|
|
33
|
+
if (n === 0) return 0;
|
|
34
|
+
if (n < 0 || n > 31) fail("invalid bit count");
|
|
35
|
+
let value = 0;
|
|
36
|
+
for (let left = n; left > 0; ) {
|
|
37
|
+
if (this.pos >= this.bytes.byteLength - 16) fail("truncated compressed data");
|
|
38
|
+
const take = Math.min(left, 8 - this.bit);
|
|
39
|
+
value = value * 2 ** take + ((this.bytes[this.pos]! >>> (8 - this.bit - take)) & (2 ** take - 1));
|
|
40
|
+
this.bit += take;
|
|
41
|
+
left -= take;
|
|
42
|
+
if (this.bit === 8) {
|
|
43
|
+
this.bit = 0;
|
|
44
|
+
this.pos++;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
peek15(): number {
|
|
51
|
+
const pos = this.pos;
|
|
52
|
+
const bit = this.bit;
|
|
53
|
+
const value = this.read(15);
|
|
54
|
+
this.pos = pos;
|
|
55
|
+
this.bit = bit;
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
align(): void {
|
|
60
|
+
if (this.bit !== 0) {
|
|
61
|
+
this.pos++;
|
|
62
|
+
this.bit = 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
byte(): number {
|
|
67
|
+
if (this.bit !== 0) fail("unaligned compressed data");
|
|
68
|
+
if (this.pos >= this.bytes.byteLength - 16) fail("truncated compressed data");
|
|
69
|
+
return this.bytes[this.pos++]!;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
atBlockEnd(): boolean {
|
|
73
|
+
return this.pos > this.blockEnd || (this.pos === this.blockEnd && this.bit >= this.blockEndBit);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class Huffman {
|
|
78
|
+
readonly #maxBits: number;
|
|
79
|
+
readonly #size: number;
|
|
80
|
+
#counts: Int32Array;
|
|
81
|
+
#firstCode: Int32Array;
|
|
82
|
+
#firstSymbol: Int32Array;
|
|
83
|
+
#symbols: Int32Array;
|
|
84
|
+
#ready = false;
|
|
85
|
+
|
|
86
|
+
constructor(size: number, maxBits = 15) {
|
|
87
|
+
this.#size = size;
|
|
88
|
+
this.#maxBits = maxBits;
|
|
89
|
+
this.#counts = new Int32Array(maxBits + 1);
|
|
90
|
+
this.#firstCode = new Int32Array(maxBits + 1);
|
|
91
|
+
this.#firstSymbol = new Int32Array(maxBits + 1);
|
|
92
|
+
this.#symbols = new Int32Array(size);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
build(lengths: Uint8Array, offset = 0): void {
|
|
96
|
+
this.#counts.fill(0);
|
|
97
|
+
for (let i = 0; i < this.#size; i++) {
|
|
98
|
+
const length = lengths[offset + i]!;
|
|
99
|
+
if (length > this.#maxBits) fail("invalid Huffman code length");
|
|
100
|
+
if (length !== 0) this.#counts[length]++;
|
|
101
|
+
}
|
|
102
|
+
let code = 0;
|
|
103
|
+
let symbols = 0;
|
|
104
|
+
for (let bits = 1; bits <= this.#maxBits; bits++) {
|
|
105
|
+
code = (code + this.#counts[bits - 1]!) << 1;
|
|
106
|
+
this.#firstCode[bits] = code;
|
|
107
|
+
this.#firstSymbol[bits] = symbols;
|
|
108
|
+
symbols += this.#counts[bits]!;
|
|
109
|
+
}
|
|
110
|
+
if (symbols !== 0 && code + this.#counts[this.#maxBits]! !== 1 << this.#maxBits) fail("incomplete Huffman table");
|
|
111
|
+
const next = this.#firstSymbol.slice();
|
|
112
|
+
for (let symbol = 0; symbol < this.#size; symbol++) {
|
|
113
|
+
const length = lengths[offset + symbol]!;
|
|
114
|
+
if (length !== 0) this.#symbols[next[length]++] = symbol;
|
|
115
|
+
}
|
|
116
|
+
this.#ready = symbols !== 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
decode(bits: Bits): number {
|
|
120
|
+
if (!this.#ready) fail("empty Huffman table");
|
|
121
|
+
let code = 0;
|
|
122
|
+
for (let length = 1; length <= this.#maxBits; length++) {
|
|
123
|
+
code = (code << 1) | bits.read(1);
|
|
124
|
+
const index = code - this.#firstCode[length]!;
|
|
125
|
+
if (index >= 0 && index < this.#counts[length]!) return this.#symbols[this.#firstSymbol[length]! + index]!;
|
|
126
|
+
}
|
|
127
|
+
fail("invalid Huffman symbol");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface Filter {
|
|
132
|
+
type: number;
|
|
133
|
+
channels: number;
|
|
134
|
+
start: number;
|
|
135
|
+
size: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Stateful RAR5 LZSS decoder; reuse one instance for members in a solid chain. */
|
|
139
|
+
export class Rar5Decoder {
|
|
140
|
+
#history = new Uint8Array(0);
|
|
141
|
+
#reps = [0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff];
|
|
142
|
+
#lastLen = 0;
|
|
143
|
+
#main = new Huffman(MAIN_SIZE);
|
|
144
|
+
#dist = new Huffman(DIST_SIZE);
|
|
145
|
+
#align = new Huffman(ALIGN_SIZE);
|
|
146
|
+
#length = new Huffman(LEN_SIZE);
|
|
147
|
+
#useAlign = false;
|
|
148
|
+
|
|
149
|
+
reset(): void {
|
|
150
|
+
this.#history = new Uint8Array(0);
|
|
151
|
+
this.#reps = [0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff];
|
|
152
|
+
this.#lastLen = 0;
|
|
153
|
+
this.#main = new Huffman(MAIN_SIZE);
|
|
154
|
+
this.#dist = new Huffman(DIST_SIZE);
|
|
155
|
+
this.#align = new Huffman(ALIGN_SIZE);
|
|
156
|
+
this.#length = new Huffman(LEN_SIZE);
|
|
157
|
+
this.#useAlign = false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
decode(
|
|
161
|
+
packed: Uint8Array,
|
|
162
|
+
unpackedSize: number,
|
|
163
|
+
dictionarySize: number,
|
|
164
|
+
solid: boolean,
|
|
165
|
+
version: number,
|
|
166
|
+
): Uint8Array {
|
|
167
|
+
if (version !== 0 && version !== 1) {
|
|
168
|
+
throw new ArchiveError(`Unsupported RAR5 compression algorithm version ${version}`);
|
|
169
|
+
}
|
|
170
|
+
if (!solid) this.reset();
|
|
171
|
+
if (!Number.isSafeInteger(dictionarySize) || dictionarySize < 128 * 1024) fail("invalid dictionary size");
|
|
172
|
+
const prior =
|
|
173
|
+
this.#history.byteLength > dictionarySize
|
|
174
|
+
? this.#history.subarray(this.#history.byteLength - dictionarySize)
|
|
175
|
+
: this.#history;
|
|
176
|
+
const data = new Uint8Array(prior.byteLength + unpackedSize + MAX_MATCH);
|
|
177
|
+
data.set(prior);
|
|
178
|
+
let outPos = prior.byteLength;
|
|
179
|
+
const outStart = outPos;
|
|
180
|
+
const outEnd = outStart + unpackedSize;
|
|
181
|
+
const filters: Filter[] = [];
|
|
182
|
+
const bits = new Bits(packed);
|
|
183
|
+
const main = this.#main;
|
|
184
|
+
const dist = this.#dist;
|
|
185
|
+
const align = this.#align;
|
|
186
|
+
const lenDecoder = this.#length;
|
|
187
|
+
let lastBlock = false;
|
|
188
|
+
|
|
189
|
+
const readTables = (): void => {
|
|
190
|
+
bits.align();
|
|
191
|
+
const flags = bits.byte();
|
|
192
|
+
let checksum = flags ^ bits.byte();
|
|
193
|
+
const sizeBytes = (flags >>> 3) & 3;
|
|
194
|
+
if (sizeBytes === 3) fail("invalid compressed block header");
|
|
195
|
+
let blockSize = bits.byte();
|
|
196
|
+
checksum ^= blockSize;
|
|
197
|
+
for (let i = 0; i < sizeBytes; i++) {
|
|
198
|
+
const b = bits.byte();
|
|
199
|
+
checksum ^= b;
|
|
200
|
+
blockSize += b * 2 ** (8 * (i + 1));
|
|
201
|
+
}
|
|
202
|
+
if (checksum !== 0x5a) fail("compressed block checksum mismatch");
|
|
203
|
+
let endBits = (flags & 7) + 1;
|
|
204
|
+
blockSize += endBits >>> 3;
|
|
205
|
+
if (blockSize === 0) fail("empty compressed block");
|
|
206
|
+
blockSize--;
|
|
207
|
+
endBits &= 7;
|
|
208
|
+
bits.blockEnd = bits.pos + blockSize;
|
|
209
|
+
bits.blockEndBit = endBits;
|
|
210
|
+
lastBlock = (flags & 0x40) !== 0;
|
|
211
|
+
if ((flags & 0x80) === 0) return;
|
|
212
|
+
|
|
213
|
+
const levelLengths = new Uint8Array(20);
|
|
214
|
+
for (let i = 0; i < 20; ) {
|
|
215
|
+
const length = bits.read(4);
|
|
216
|
+
if (length === 15) {
|
|
217
|
+
const zeros = bits.read(4);
|
|
218
|
+
if (zeros !== 0) {
|
|
219
|
+
i = Math.min(20, i + zeros + 2);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
levelLengths[i++] = length;
|
|
224
|
+
}
|
|
225
|
+
const level = new Huffman(20);
|
|
226
|
+
level.build(levelLengths);
|
|
227
|
+
const tableLength = version === 1 ? TABLE_SIZE : TABLE_SIZE - 16;
|
|
228
|
+
const lengthsCompact = new Uint8Array(tableLength);
|
|
229
|
+
for (let i = 0; i < tableLength; ) {
|
|
230
|
+
const symbol = level.decode(bits);
|
|
231
|
+
if (symbol < 16) {
|
|
232
|
+
lengthsCompact[i++] = symbol;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
let count = ((symbol - 16) & 1) * 4;
|
|
236
|
+
count += count + 3 + bits.read(count + 3);
|
|
237
|
+
const value = symbol < 18 ? (i === 0 ? fail("invalid Huffman repeat") : lengthsCompact[i - 1]!) : 0;
|
|
238
|
+
const end = Math.min(tableLength, i + count);
|
|
239
|
+
lengthsCompact.fill(value, i, end);
|
|
240
|
+
i = end;
|
|
241
|
+
}
|
|
242
|
+
const lengths = new Uint8Array(TABLE_SIZE);
|
|
243
|
+
if (version === 0) {
|
|
244
|
+
lengths.set(lengthsCompact.subarray(0, MAIN_SIZE + 64));
|
|
245
|
+
lengths.set(lengthsCompact.subarray(MAIN_SIZE + 64), MAIN_SIZE + DIST_SIZE);
|
|
246
|
+
} else {
|
|
247
|
+
lengths.set(lengthsCompact);
|
|
248
|
+
}
|
|
249
|
+
main.build(lengths);
|
|
250
|
+
dist.build(lengths, MAIN_SIZE);
|
|
251
|
+
align.build(lengths, MAIN_SIZE + DIST_SIZE);
|
|
252
|
+
lenDecoder.build(lengths, MAIN_SIZE + DIST_SIZE + ALIGN_SIZE);
|
|
253
|
+
this.#useAlign = false;
|
|
254
|
+
for (let i = 0; i < ALIGN_SIZE; i++) {
|
|
255
|
+
if (lengths[MAIN_SIZE + DIST_SIZE + i] !== 4) this.#useAlign = true;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
readTables();
|
|
260
|
+
while (outPos < outEnd) {
|
|
261
|
+
if (bits.atBlockEnd()) {
|
|
262
|
+
if (bits.pos > bits.blockEnd || (bits.pos === bits.blockEnd && bits.bit > bits.blockEndBit))
|
|
263
|
+
fail("compressed block overread");
|
|
264
|
+
bits.align();
|
|
265
|
+
if (lastBlock) break;
|
|
266
|
+
readTables();
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const symbol = main.decode(bits);
|
|
270
|
+
if (symbol < 256) {
|
|
271
|
+
data[outPos++] = symbol;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (symbol === 256) {
|
|
275
|
+
const bytes = (bits.read(2) + 1) * 8;
|
|
276
|
+
let start = 0;
|
|
277
|
+
for (let shift = 0; shift < bytes; shift += 8) start += bits.read(8) * 2 ** shift;
|
|
278
|
+
const sizeBytes = (bits.read(2) + 1) * 8;
|
|
279
|
+
let size = 0;
|
|
280
|
+
for (let shift = 0; shift < sizeBytes; shift += 8) size += bits.read(8) * 2 ** shift;
|
|
281
|
+
const type = bits.read(3);
|
|
282
|
+
const channels = type === 0 ? bits.read(5) + 1 : 0;
|
|
283
|
+
if (type > 3) throw new ArchiveError(`Unsupported RAR5 filter type ${type}`);
|
|
284
|
+
if (size > MAX_FILTER_SIZE || outPos - outStart + start + size > unpackedSize) fail("invalid filter range");
|
|
285
|
+
filters.push({ type, channels, start: outPos - outStart + start, size });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let length: number;
|
|
290
|
+
let distance = this.#reps[0]!;
|
|
291
|
+
if (symbol < 262) {
|
|
292
|
+
if (symbol >= 258) {
|
|
293
|
+
const repIndex = symbol - 258;
|
|
294
|
+
distance = this.#reps[repIndex]!;
|
|
295
|
+
for (let i = repIndex; i > 0; i--) this.#reps[i] = this.#reps[i - 1]!;
|
|
296
|
+
this.#reps[0] = distance;
|
|
297
|
+
const slot = lenDecoder.decode(bits);
|
|
298
|
+
length = slot >= 8 ? slotToLength(bits, slot) : slot;
|
|
299
|
+
length += 2;
|
|
300
|
+
this.#lastLen = length;
|
|
301
|
+
} else {
|
|
302
|
+
length = this.#lastLen;
|
|
303
|
+
if (length === 0) continue;
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
this.#reps[3] = this.#reps[2]!;
|
|
307
|
+
this.#reps[2] = this.#reps[1]!;
|
|
308
|
+
this.#reps[1] = this.#reps[0]!;
|
|
309
|
+
const slot = symbol - 262;
|
|
310
|
+
length = slot >= 8 ? slotToLength(bits, slot) : slot;
|
|
311
|
+
length += 2;
|
|
312
|
+
let distanceSlot = dist.decode(bits);
|
|
313
|
+
if (distanceSlot >= 4) {
|
|
314
|
+
const extraBits = (distanceSlot - 2) >>> 1;
|
|
315
|
+
distanceSlot = (2 | (distanceSlot & 1)) * 2 ** extraBits;
|
|
316
|
+
if (extraBits < 4) distanceSlot += bits.read(extraBits);
|
|
317
|
+
else {
|
|
318
|
+
length += LEN_PLUS[extraBits] ?? 3;
|
|
319
|
+
if (this.#useAlign) distanceSlot += bits.read(extraBits - 4) * 16 + align.decode(bits);
|
|
320
|
+
else distanceSlot += bits.read(extraBits);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
distance = distanceSlot + 1;
|
|
324
|
+
this.#reps[0] = distance;
|
|
325
|
+
this.#lastLen = length;
|
|
326
|
+
}
|
|
327
|
+
if (distance <= 0 || distance > dictionarySize || distance > outPos) fail("invalid LZ distance");
|
|
328
|
+
if (outPos + length > data.byteLength) fail("invalid LZ match length");
|
|
329
|
+
for (let i = 0; i < length; i++) data[outPos + i] = data[outPos + i - distance]!;
|
|
330
|
+
outPos += length;
|
|
331
|
+
}
|
|
332
|
+
if (outPos !== outEnd) fail(`decompressed size mismatch (${outPos - outStart} != ${unpackedSize})`);
|
|
333
|
+
const output = data.slice(outStart, outEnd);
|
|
334
|
+
applyFilters(output, filters);
|
|
335
|
+
const historyStart = Math.max(0, outEnd - dictionarySize);
|
|
336
|
+
this.#history = data.slice(historyStart, outEnd);
|
|
337
|
+
return output;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function slotToLength(bits: Bits, slot: number): number {
|
|
342
|
+
const count = (slot >>> 2) - 1;
|
|
343
|
+
return ((4 | (slot & 3)) << count) + bits.read(count);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function applyFilters(output: Uint8Array, filters: Filter[]): void {
|
|
347
|
+
for (const filter of filters) {
|
|
348
|
+
const data = output.subarray(filter.start, filter.start + filter.size);
|
|
349
|
+
if (filter.type === 0) {
|
|
350
|
+
const source = data.slice();
|
|
351
|
+
let sourcePos = 0;
|
|
352
|
+
for (let channel = 0; channel < filter.channels; channel++) {
|
|
353
|
+
let previous = 0;
|
|
354
|
+
for (let pos = channel; pos < data.byteLength; pos += filter.channels) {
|
|
355
|
+
previous = (previous - source[sourcePos++]!) & 0xff;
|
|
356
|
+
data[pos] = previous;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} else if (filter.type === 1 || filter.type === 2) {
|
|
360
|
+
const fileSize = 1 << 24;
|
|
361
|
+
for (let pos = 0; pos + 4 < data.byteLength; ) {
|
|
362
|
+
const opcode = data[pos++]!;
|
|
363
|
+
if (opcode !== 0xe8 && (filter.type === 1 || opcode !== 0xe9)) continue;
|
|
364
|
+
const offset = (filter.start + pos) & (fileSize - 1);
|
|
365
|
+
let address = readI32(data, pos);
|
|
366
|
+
if (address >>> 0 < fileSize) address -= offset;
|
|
367
|
+
else if (address >>> 0 >= -offset >>> 0) address += fileSize;
|
|
368
|
+
else {
|
|
369
|
+
pos += 4;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
writeI32(data, pos, address);
|
|
373
|
+
pos += 4;
|
|
374
|
+
}
|
|
375
|
+
} else {
|
|
376
|
+
for (let pos = 0; pos + 3 < data.byteLength; pos += 4) {
|
|
377
|
+
if (data[pos + 3] !== 0xeb) continue;
|
|
378
|
+
const instruction = readI32(data, pos);
|
|
379
|
+
const address =
|
|
380
|
+
((instruction & 0xff000000) | ((instruction - ((filter.start + pos) >> 2)) & 0xffffff)) >>> 0;
|
|
381
|
+
writeI32(data, pos, address);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function readI32(bytes: Uint8Array, offset: number): number {
|
|
388
|
+
return bytes[offset]! | (bytes[offset + 1]! << 8) | (bytes[offset + 2]! << 16) | (bytes[offset + 3]! << 24);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function writeI32(bytes: Uint8Array, offset: number, value: number): void {
|
|
392
|
+
bytes[offset] = value;
|
|
393
|
+
bytes[offset + 1] = value >>> 8;
|
|
394
|
+
bytes[offset + 2] = value >>> 16;
|
|
395
|
+
bytes[offset + 3] = value >>> 24;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function fail(reason: string): never {
|
|
399
|
+
throw new ArchiveError(`Invalid RAR5 archive: ${reason}`);
|
|
400
|
+
}
|