@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +22 -0
  3. package/THIRD-PARTY-NOTICES.txt +22909 -0
  4. package/dist/types/ar/arj.d.ts +5 -0
  5. package/dist/types/ar/asar.d.ts +7 -0
  6. package/dist/types/ar/bytes.d.ts +20 -0
  7. package/dist/types/ar/cab.d.ts +5 -0
  8. package/dist/types/ar/checksums.d.ts +10 -0
  9. package/dist/types/ar/codecs/bzip2.d.ts +4 -0
  10. package/dist/types/ar/codecs/gzip.d.ts +6 -0
  11. package/dist/types/ar/codecs/lzma.d.ts +6 -0
  12. package/dist/types/ar/codecs/lzw.d.ts +4 -0
  13. package/dist/types/ar/codecs/lzx.d.ts +7 -0
  14. package/dist/types/ar/codecs/xz.d.ts +4 -0
  15. package/dist/types/ar/codecs/zstd.d.ts +6 -0
  16. package/dist/types/ar/cpio.d.ts +7 -0
  17. package/dist/types/ar/deb.d.ts +5 -0
  18. package/dist/types/ar/entries.d.ts +22 -0
  19. package/dist/types/ar/error.d.ts +8 -0
  20. package/dist/types/ar/index.d.ts +11 -0
  21. package/dist/types/ar/iso.d.ts +5 -0
  22. package/dist/types/ar/limits.d.ts +33 -0
  23. package/dist/types/ar/lzh.d.ts +7 -0
  24. package/dist/types/ar/open.d.ts +45 -0
  25. package/dist/types/ar/paths.d.ts +18 -0
  26. package/dist/types/ar/rar/rar4-decoder.d.ts +6 -0
  27. package/dist/types/ar/rar/rar5-decoder.d.ts +6 -0
  28. package/dist/types/ar/rar.d.ts +5 -0
  29. package/dist/types/ar/reader.d.ts +28 -0
  30. package/dist/types/ar/registry.d.ts +17 -0
  31. package/dist/types/ar/rpm.d.ts +5 -0
  32. package/dist/types/ar/sevenzip/decode.d.ts +33 -0
  33. package/dist/types/ar/sevenzip.d.ts +5 -0
  34. package/dist/types/ar/source.d.ts +54 -0
  35. package/dist/types/ar/tar.d.ts +9 -0
  36. package/dist/types/ar/types.d.ts +99 -0
  37. package/dist/types/ar/unix-ar.d.ts +7 -0
  38. package/dist/types/ar/write.d.ts +10 -0
  39. package/dist/types/ar/zip.d.ts +10 -0
  40. package/dist/types/postmortem.d.ts +33 -0
  41. package/package.json +9 -3
  42. package/src/ar/arj.ts +314 -0
  43. package/src/ar/asar.ts +480 -0
  44. package/src/ar/bytes.ts +78 -0
  45. package/src/ar/cab.ts +377 -0
  46. package/src/ar/checksums.ts +62 -0
  47. package/src/ar/codecs/bzip2.ts +489 -0
  48. package/src/ar/codecs/gzip.ts +25 -0
  49. package/src/ar/codecs/lzma.ts +437 -0
  50. package/src/ar/codecs/lzw.ts +191 -0
  51. package/src/ar/codecs/lzx.ts +366 -0
  52. package/src/ar/codecs/xz.ts +522 -0
  53. package/src/ar/codecs/zstd.ts +25 -0
  54. package/src/ar/cpio.ts +389 -0
  55. package/src/ar/deb.ts +160 -0
  56. package/src/ar/entries.ts +97 -0
  57. package/src/ar/error.ts +11 -0
  58. package/src/ar/index.ts +16 -0
  59. package/src/ar/iso.ts +712 -0
  60. package/src/ar/limits.ts +80 -0
  61. package/src/ar/lzh.ts +659 -0
  62. package/src/ar/open.ts +225 -0
  63. package/src/ar/paths.ts +70 -0
  64. package/src/ar/rar/rar4-decoder.ts +459 -0
  65. package/src/ar/rar/rar5-decoder.ts +400 -0
  66. package/src/ar/rar.ts +735 -0
  67. package/src/ar/reader.ts +162 -0
  68. package/src/ar/registry.ts +208 -0
  69. package/src/ar/rpm.ts +320 -0
  70. package/src/ar/sevenzip/decode.ts +239 -0
  71. package/src/ar/sevenzip.ts +634 -0
  72. package/src/ar/source.ts +190 -0
  73. package/src/ar/tar.ts +771 -0
  74. package/src/ar/types.ts +131 -0
  75. package/src/ar/unix-ar.ts +312 -0
  76. package/src/ar/write.ts +55 -0
  77. package/src/ar/zip.ts +719 -0
  78. package/src/browsers.ts +2 -149
  79. package/src/docx/converter.ts +9 -9
  80. package/src/postmortem.ts +74 -0
  81. package/dist/types/docx/zip.d.ts +0 -6
  82. package/src/docx/zip.ts +0 -87
@@ -0,0 +1,366 @@
1
+ import { ArchiveError } from "../error";
2
+
3
+ const FRAME_SIZE = 32 * 1024;
4
+ const MIN_MATCH = 2;
5
+ const NUM_PRIMARY_LENGTHS = 7;
6
+ const NUM_SECONDARY_LENGTHS = 249;
7
+ const POSITION_SLOTS = [30, 32, 34, 36, 38, 42, 50] as const;
8
+
9
+ class LzxBitReader {
10
+ readonly #bytes: Uint8Array;
11
+ #offset = 0;
12
+ #word = 0;
13
+ #remaining = 0;
14
+
15
+ constructor(bytes: Uint8Array) {
16
+ this.#bytes = bytes;
17
+ }
18
+
19
+ readBits(count: number): number {
20
+ let value = 0;
21
+ let needed = count;
22
+ while (needed > 0) {
23
+ if (this.#remaining === 0) {
24
+ if (this.#offset + 2 > this.#bytes.byteLength) {
25
+ throw new ArchiveError("Invalid CAB archive: truncated LZX bitstream");
26
+ }
27
+ this.#word = this.#bytes[this.#offset]! | (this.#bytes[this.#offset + 1]! << 8);
28
+ this.#offset += 2;
29
+ this.#remaining = 16;
30
+ }
31
+ const take = Math.min(needed, this.#remaining);
32
+ value = value * 2 ** take + ((this.#word >>> (this.#remaining - take)) & (2 ** take - 1));
33
+ this.#remaining -= take;
34
+ needed -= take;
35
+ }
36
+ return value;
37
+ }
38
+
39
+ alignWord(): void {
40
+ this.#remaining = 0;
41
+ }
42
+
43
+ readByte(): number {
44
+ if (this.#remaining !== 0) {
45
+ throw new ArchiveError("Invalid CAB archive: misaligned LZX byte stream");
46
+ }
47
+ if (this.#offset >= this.#bytes.byteLength) {
48
+ throw new ArchiveError("Invalid CAB archive: truncated LZX data");
49
+ }
50
+ return this.#bytes[this.#offset++]!;
51
+ }
52
+
53
+ readUInt32LE(): number {
54
+ const b0 = this.readByte();
55
+ const b1 = this.readByte();
56
+ const b2 = this.readByte();
57
+ const b3 = this.readByte();
58
+ return (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;
59
+ }
60
+ }
61
+
62
+ class LzxHuffmanTable {
63
+ readonly #counts = new Uint32Array(17);
64
+ readonly #firstCodes = new Uint32Array(17);
65
+ readonly #firstSymbols = new Uint32Array(17);
66
+ readonly #symbols: Uint16Array;
67
+ readonly empty: boolean;
68
+
69
+ constructor(lengths: Uint8Array, allowEmpty = false) {
70
+ let symbolCount = 0;
71
+ for (const length of lengths) {
72
+ if (length > 16) throw new ArchiveError("Invalid CAB archive: invalid LZX Huffman code length");
73
+ if (length !== 0) {
74
+ this.#counts[length]++;
75
+ symbolCount++;
76
+ }
77
+ }
78
+ this.empty = symbolCount === 0;
79
+ if (this.empty && !allowEmpty) {
80
+ throw new ArchiveError("Invalid CAB archive: empty LZX Huffman tree");
81
+ }
82
+
83
+ let code = 0;
84
+ let symbolOffset = 0;
85
+ for (let length = 1; length <= 16; length++) {
86
+ code = (code + this.#counts[length - 1]!) * 2;
87
+ if (code + this.#counts[length]! > 2 ** length) {
88
+ throw new ArchiveError("Invalid CAB archive: oversubscribed LZX Huffman tree");
89
+ }
90
+ this.#firstCodes[length] = code;
91
+ this.#firstSymbols[length] = symbolOffset;
92
+ symbolOffset += this.#counts[length]!;
93
+ }
94
+
95
+ this.#symbols = new Uint16Array(symbolCount);
96
+ const next = this.#firstSymbols.slice();
97
+ for (let symbol = 0; symbol < lengths.byteLength; symbol++) {
98
+ const length = lengths[symbol]!;
99
+ if (length !== 0) this.#symbols[next[length]!] = symbol;
100
+ next[length]!++;
101
+ }
102
+ }
103
+
104
+ decode(reader: LzxBitReader): number {
105
+ if (this.empty) throw new ArchiveError("Invalid CAB archive: LZX stream uses an empty Huffman tree");
106
+ let code = 0;
107
+ for (let length = 1; length <= 16; length++) {
108
+ code = code * 2 + reader.readBits(1);
109
+ const relative = code - this.#firstCodes[length]!;
110
+ if (relative >= 0 && relative < this.#counts[length]!) {
111
+ return this.#symbols[this.#firstSymbols[length]! + relative]!;
112
+ }
113
+ }
114
+ throw new ArchiveError("Invalid CAB archive: invalid LZX Huffman symbol");
115
+ }
116
+ }
117
+
118
+ function readCodeLengths(reader: LzxBitReader, lengths: Uint8Array, first: number, last: number): void {
119
+ const pretreeLengths = new Uint8Array(20);
120
+ for (let index = 0; index < pretreeLengths.byteLength; index++) pretreeLengths[index] = reader.readBits(4);
121
+ const pretree = new LzxHuffmanTable(pretreeLengths);
122
+ let index = first;
123
+ while (index < last) {
124
+ const symbol = pretree.decode(reader);
125
+ if (symbol === 17 || symbol === 18) {
126
+ const run = reader.readBits(symbol === 17 ? 4 : 5) + (symbol === 17 ? 4 : 20);
127
+ if (index + run > last) throw new ArchiveError("Invalid CAB archive: LZX code-length run exceeds its tree");
128
+ lengths.fill(0, index, index + run);
129
+ index += run;
130
+ continue;
131
+ }
132
+ if (symbol === 19) {
133
+ const run = reader.readBits(1) + 4;
134
+ if (index + run > last) throw new ArchiveError("Invalid CAB archive: LZX code-length run exceeds its tree");
135
+ const delta = pretree.decode(reader);
136
+ const length = (lengths[index]! - delta + 17) % 17;
137
+ lengths.fill(length, index, index + run);
138
+ index += run;
139
+ continue;
140
+ }
141
+ lengths[index] = (lengths[index]! - symbol + 17) % 17;
142
+ index++;
143
+ }
144
+ }
145
+
146
+ function signedUInt32(value: number): number {
147
+ return value > 0x7fffffff ? value - 0x100000000 : value;
148
+ }
149
+
150
+ /** Stateful Microsoft LZX decoder used by CAB folders, retaining trees and window history between 32 KiB frames. */
151
+ export class LzxDecoder {
152
+ readonly #window: Uint8Array;
153
+ readonly #positionBase: Uint32Array;
154
+ readonly #extraBits: Uint8Array;
155
+ readonly #mainLengths: Uint8Array;
156
+ readonly #lengthLengths = new Uint8Array(NUM_SECONDARY_LENGTHS);
157
+ #mainTable?: LzxHuffmanTable;
158
+ #lengthTable?: LzxHuffmanTable;
159
+ #alignedTable?: LzxHuffmanTable;
160
+ #windowPosition = 0;
161
+ #decodedSize = 0;
162
+ #frame = 0;
163
+ #r0 = 1;
164
+ #r1 = 1;
165
+ #r2 = 1;
166
+ #headerRead = false;
167
+ #intelFileSize = 0;
168
+ #intelStarted = false;
169
+ #blockType = 0;
170
+ #blockLength = 0;
171
+ #blockRemaining = 0;
172
+ #uncompressedPadding = false;
173
+
174
+ constructor(windowBits: number) {
175
+ if (!Number.isInteger(windowBits) || windowBits < 15 || windowBits > 21) {
176
+ throw new ArchiveError(`Unsupported CAB LZX window size: ${windowBits} bits (expected 15-21)`);
177
+ }
178
+ this.#window = new Uint8Array(2 ** windowBits);
179
+ const slots = POSITION_SLOTS[windowBits - 15]!;
180
+ this.#mainLengths = new Uint8Array(256 + slots * 8);
181
+ this.#extraBits = new Uint8Array(slots);
182
+ this.#positionBase = new Uint32Array(slots);
183
+ for (let slot = 0; slot < slots; slot++) {
184
+ this.#extraBits[slot] = slot < 4 ? 0 : Math.min(17, Math.floor(slot / 2) - 1);
185
+ if (slot > 0) this.#positionBase[slot] = this.#positionBase[slot - 1]! + 2 ** this.#extraBits[slot - 1]!;
186
+ }
187
+ }
188
+
189
+ /** Decode one CAB CFDATA LZX frame while preserving the folder's dictionary and Huffman state. */
190
+ decompressFrame(bytes: Uint8Array, outputSize: number): Uint8Array {
191
+ if (!Number.isInteger(outputSize) || outputSize < 0 || outputSize > FRAME_SIZE) {
192
+ throw new ArchiveError(`Invalid CAB archive: LZX frame size ${outputSize} exceeds 32768 bytes`);
193
+ }
194
+ if (outputSize === 0) return new Uint8Array(0);
195
+ const reader = new LzxBitReader(bytes);
196
+ if (!this.#headerRead) {
197
+ if (reader.readBits(1) !== 0) {
198
+ const high = reader.readBits(16);
199
+ const low = reader.readBits(16);
200
+ this.#intelFileSize = signedUInt32((high * 0x10000 + low) >>> 0);
201
+ }
202
+ this.#headerRead = true;
203
+ }
204
+
205
+ const raw = new Uint8Array(outputSize);
206
+ let outputPosition = 0;
207
+ while (outputPosition < outputSize) {
208
+ if (this.#blockRemaining === 0) this.#readBlockHeader(reader);
209
+ const run = Math.min(this.#blockRemaining, outputSize - outputPosition);
210
+ const produced = this.#decodeRun(reader, raw, outputPosition, run);
211
+ if (produced !== run) throw new ArchiveError("Invalid CAB archive: LZX block produced the wrong byte count");
212
+ outputPosition += produced;
213
+ this.#blockRemaining -= produced;
214
+ }
215
+ if (this.#blockRemaining === 0 && this.#blockType === 3 && this.#uncompressedPadding) {
216
+ reader.readByte();
217
+ this.#uncompressedPadding = false;
218
+ }
219
+ reader.alignWord();
220
+
221
+ const translated = this.#translateE8(raw);
222
+ this.#frame++;
223
+ return translated;
224
+ }
225
+
226
+ #readBlockHeader(reader: LzxBitReader): void {
227
+ if (this.#blockType === 3 && this.#uncompressedPadding) reader.readByte();
228
+ this.#uncompressedPadding = false;
229
+ this.#blockType = reader.readBits(3);
230
+ this.#blockLength = reader.readBits(16) * 256 + reader.readBits(8);
231
+ if (this.#blockLength === 0) throw new ArchiveError("Invalid CAB archive: zero-length LZX block");
232
+ this.#blockRemaining = this.#blockLength;
233
+
234
+ if (this.#blockType === 1 || this.#blockType === 2) {
235
+ if (this.#blockType === 2) {
236
+ const alignedLengths = new Uint8Array(8);
237
+ for (let index = 0; index < alignedLengths.byteLength; index++) alignedLengths[index] = reader.readBits(3);
238
+ this.#alignedTable = new LzxHuffmanTable(alignedLengths);
239
+ }
240
+ readCodeLengths(reader, this.#mainLengths, 0, 256);
241
+ readCodeLengths(reader, this.#mainLengths, 256, this.#mainLengths.byteLength);
242
+ this.#mainTable = new LzxHuffmanTable(this.#mainLengths);
243
+ if (this.#mainLengths[0xe8] !== 0) this.#intelStarted = true;
244
+ readCodeLengths(reader, this.#lengthLengths, 0, this.#lengthLengths.byteLength);
245
+ this.#lengthTable = new LzxHuffmanTable(this.#lengthLengths, true);
246
+ return;
247
+ }
248
+ if (this.#blockType === 3) {
249
+ this.#intelStarted = true;
250
+ reader.alignWord();
251
+ this.#r0 = reader.readUInt32LE();
252
+ this.#r1 = reader.readUInt32LE();
253
+ this.#r2 = reader.readUInt32LE();
254
+ if (this.#r0 === 0 || this.#r1 === 0 || this.#r2 === 0) {
255
+ throw new ArchiveError("Invalid CAB archive: invalid LZX repeated offset");
256
+ }
257
+ this.#uncompressedPadding = (this.#blockLength & 1) !== 0;
258
+ return;
259
+ }
260
+ throw new ArchiveError(`Invalid CAB archive: unsupported LZX block type ${this.#blockType}`);
261
+ }
262
+
263
+ #decodeRun(reader: LzxBitReader, output: Uint8Array, outputStart: number, count: number): number {
264
+ if (this.#blockType === 3) {
265
+ for (let index = 0; index < count; index++) this.#writeByte(reader.readByte(), output, outputStart + index);
266
+ return count;
267
+ }
268
+ if (!this.#mainTable || !this.#lengthTable)
269
+ throw new ArchiveError("Invalid CAB archive: missing LZX decode trees");
270
+
271
+ let produced = 0;
272
+ while (produced < count) {
273
+ const element = this.#mainTable.decode(reader);
274
+ if (element < 256) {
275
+ this.#writeByte(element, output, outputStart + produced);
276
+ produced++;
277
+ continue;
278
+ }
279
+
280
+ const match = element - 256;
281
+ let matchLength = match & NUM_PRIMARY_LENGTHS;
282
+ if (matchLength === NUM_PRIMARY_LENGTHS) matchLength += this.#lengthTable.decode(reader);
283
+ matchLength += MIN_MATCH;
284
+ if (matchLength > count - produced || matchLength > this.#blockRemaining - produced) {
285
+ throw new ArchiveError("Invalid CAB archive: LZX match crosses a frame or block boundary");
286
+ }
287
+
288
+ const slot = match >>> 3;
289
+ let matchOffset: number;
290
+ if (slot === 0) {
291
+ matchOffset = this.#r0;
292
+ } else if (slot === 1) {
293
+ matchOffset = this.#r1;
294
+ this.#r1 = this.#r0;
295
+ this.#r0 = matchOffset;
296
+ } else if (slot === 2) {
297
+ matchOffset = this.#r2;
298
+ this.#r2 = this.#r0;
299
+ this.#r0 = matchOffset;
300
+ } else {
301
+ if (slot >= this.#positionBase.byteLength)
302
+ throw new ArchiveError("Invalid CAB archive: LZX position slot is out of range");
303
+ const extra = this.#extraBits[slot]!;
304
+ matchOffset = this.#positionBase[slot]! - 2;
305
+ if (this.#blockType === 2 && extra >= 3) {
306
+ if (extra > 3) matchOffset += reader.readBits(extra - 3) * 8;
307
+ if (!this.#alignedTable) throw new ArchiveError("Invalid CAB archive: missing LZX aligned tree");
308
+ matchOffset += this.#alignedTable.decode(reader);
309
+ } else if (extra !== 0) {
310
+ matchOffset += reader.readBits(extra);
311
+ }
312
+ this.#r2 = this.#r1;
313
+ this.#r1 = this.#r0;
314
+ this.#r0 = matchOffset;
315
+ }
316
+
317
+ if (matchOffset <= 0 || matchOffset > Math.min(this.#decodedSize, this.#window.byteLength)) {
318
+ throw new ArchiveError("Invalid CAB archive: LZX match offset exceeds available history");
319
+ }
320
+ for (let index = 0; index < matchLength; index++) {
321
+ const source = (this.#windowPosition - matchOffset + this.#window.byteLength) % this.#window.byteLength;
322
+ this.#writeByte(this.#window[source]!, output, outputStart + produced + index);
323
+ }
324
+ produced += matchLength;
325
+ }
326
+ return produced;
327
+ }
328
+
329
+ #writeByte(value: number, output: Uint8Array, outputPosition: number): void {
330
+ output[outputPosition] = value;
331
+ this.#window[this.#windowPosition] = value;
332
+ this.#windowPosition = (this.#windowPosition + 1) % this.#window.byteLength;
333
+ this.#decodedSize++;
334
+ }
335
+
336
+ #translateE8(raw: Uint8Array): Uint8Array {
337
+ if (!this.#intelStarted || this.#intelFileSize === 0 || this.#frame >= 32768 || raw.byteLength <= 10) return raw;
338
+ const output = raw.slice();
339
+ let position = 0;
340
+ let current = this.#decodedSize - raw.byteLength;
341
+ const end = output.byteLength - 10;
342
+ while (position < end) {
343
+ if (output[position++] !== 0xe8) {
344
+ current++;
345
+ continue;
346
+ }
347
+ const absolute = signedUInt32(
348
+ (output[position]! |
349
+ (output[position + 1]! << 8) |
350
+ (output[position + 2]! << 16) |
351
+ (output[position + 3]! << 24)) >>>
352
+ 0,
353
+ );
354
+ if (absolute >= -current && absolute < this.#intelFileSize) {
355
+ const relative = absolute >= 0 ? absolute - current : absolute + this.#intelFileSize;
356
+ output[position] = relative & 0xff;
357
+ output[position + 1] = (relative >>> 8) & 0xff;
358
+ output[position + 2] = (relative >>> 16) & 0xff;
359
+ output[position + 3] = (relative >>> 24) & 0xff;
360
+ }
361
+ position += 4;
362
+ current += 5;
363
+ }
364
+ return output;
365
+ }
366
+ }