stormlib-js 0.1.0 → 0.1.1
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/README.md +29 -0
- package/dist/chunk-RRBXXQVG.mjs +2410 -0
- package/dist/chunk-RRBXXQVG.mjs.map +1 -0
- package/dist/index.browser.d.mts +195 -0
- package/dist/index.browser.d.ts +195 -0
- package/dist/index.browser.js +3002 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.browser.mjs +693 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.d.mts +25 -241
- package/dist/index.d.ts +25 -241
- package/dist/index.js +87 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -2338
- package/dist/index.mjs.map +1 -1
- package/dist/storm-buffer-nHXHoPVG.d.mts +242 -0
- package/dist/storm-buffer-nHXHoPVG.d.ts +242 -0
- package/package.json +20 -3
package/dist/index.mjs
CHANGED
|
@@ -1,681 +1,133 @@
|
|
|
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
|
-
if (compressionType === 0) {
|
|
70
|
-
output[outPos++] = reader.readBits(8);
|
|
71
|
-
} else {
|
|
72
|
-
let value = 0;
|
|
73
|
-
const bits = reader.peekBits(8);
|
|
74
|
-
let found = false;
|
|
75
|
-
for (let i = 0; i < 256; i++) {
|
|
76
|
-
const numBits = CH_BITS_ASC[i];
|
|
77
|
-
const peek = reader.peekBits(numBits);
|
|
78
|
-
if (false) {
|
|
79
|
-
value = i;
|
|
80
|
-
reader.skipBits(numBits);
|
|
81
|
-
found = true;
|
|
82
|
-
break;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (!found) {
|
|
86
|
-
output[outPos++] = reader.readBits(8);
|
|
87
|
-
} else {
|
|
88
|
-
output[outPos++] = value;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
const distBits8 = reader.peekBits(8);
|
|
93
|
-
let distCode = -1;
|
|
94
|
-
for (let i = 0; i < DIST_CODES.length; i++) {
|
|
95
|
-
if (DIST_CODES[i] === (distBits8 & (1 << DIST_BITS[i]) - 1)) {
|
|
96
|
-
distCode = i;
|
|
97
|
-
reader.skipBits(DIST_BITS[i]);
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (distCode === -1) {
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
const distance = distCode << dictSizeBits | reader.readBits(dictSizeBits);
|
|
105
|
-
const length = decodeLength(reader);
|
|
106
|
-
if (length < 0) break;
|
|
107
|
-
const srcPos = outPos - distance - 1;
|
|
108
|
-
if (srcPos < 0) break;
|
|
109
|
-
for (let i = 0; i < length && outPos < outSize; i++) {
|
|
110
|
-
output[outPos] = output[srcPos + i];
|
|
111
|
-
outPos++;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return output.subarray(0, outPos);
|
|
116
|
-
}
|
|
117
|
-
var DIST_CODES, DIST_BITS, LENGTH_CODES, LENGTH_BITS, CH_BITS_ASC, PkBitReader;
|
|
118
|
-
var init_pkware = __esm({
|
|
119
|
-
"src/compression/pkware.ts"() {
|
|
120
|
-
"use strict";
|
|
121
|
-
DIST_CODES = [
|
|
122
|
-
3,
|
|
123
|
-
13,
|
|
124
|
-
5,
|
|
125
|
-
25,
|
|
126
|
-
9,
|
|
127
|
-
17,
|
|
128
|
-
1,
|
|
129
|
-
62,
|
|
130
|
-
30,
|
|
131
|
-
46,
|
|
132
|
-
14,
|
|
133
|
-
54,
|
|
134
|
-
22,
|
|
135
|
-
38,
|
|
136
|
-
6,
|
|
137
|
-
58,
|
|
138
|
-
26,
|
|
139
|
-
42,
|
|
140
|
-
10,
|
|
141
|
-
50,
|
|
142
|
-
18,
|
|
143
|
-
34,
|
|
144
|
-
66,
|
|
145
|
-
2,
|
|
146
|
-
124,
|
|
147
|
-
60,
|
|
148
|
-
92,
|
|
149
|
-
28,
|
|
150
|
-
108,
|
|
151
|
-
44,
|
|
152
|
-
76,
|
|
153
|
-
12,
|
|
154
|
-
116,
|
|
155
|
-
52,
|
|
156
|
-
84,
|
|
157
|
-
20,
|
|
158
|
-
100,
|
|
159
|
-
36,
|
|
160
|
-
68,
|
|
161
|
-
4,
|
|
162
|
-
120,
|
|
163
|
-
56,
|
|
164
|
-
88,
|
|
165
|
-
24,
|
|
166
|
-
104,
|
|
167
|
-
40,
|
|
168
|
-
72,
|
|
169
|
-
8,
|
|
170
|
-
240,
|
|
171
|
-
112,
|
|
172
|
-
176,
|
|
173
|
-
48,
|
|
174
|
-
208,
|
|
175
|
-
80,
|
|
176
|
-
144,
|
|
177
|
-
16,
|
|
178
|
-
224,
|
|
179
|
-
96,
|
|
180
|
-
160,
|
|
181
|
-
32,
|
|
182
|
-
192,
|
|
183
|
-
64,
|
|
184
|
-
128,
|
|
185
|
-
0
|
|
186
|
-
];
|
|
187
|
-
DIST_BITS = [
|
|
188
|
-
2,
|
|
189
|
-
4,
|
|
190
|
-
4,
|
|
191
|
-
5,
|
|
192
|
-
5,
|
|
193
|
-
5,
|
|
194
|
-
5,
|
|
195
|
-
6,
|
|
196
|
-
6,
|
|
197
|
-
6,
|
|
198
|
-
6,
|
|
199
|
-
6,
|
|
200
|
-
6,
|
|
201
|
-
6,
|
|
202
|
-
6,
|
|
203
|
-
6,
|
|
204
|
-
6,
|
|
205
|
-
6,
|
|
206
|
-
6,
|
|
207
|
-
6,
|
|
208
|
-
6,
|
|
209
|
-
6,
|
|
210
|
-
7,
|
|
211
|
-
7,
|
|
212
|
-
7,
|
|
213
|
-
7,
|
|
214
|
-
7,
|
|
215
|
-
7,
|
|
216
|
-
7,
|
|
217
|
-
7,
|
|
218
|
-
7,
|
|
219
|
-
7,
|
|
220
|
-
7,
|
|
221
|
-
7,
|
|
222
|
-
7,
|
|
223
|
-
7,
|
|
224
|
-
7,
|
|
225
|
-
7,
|
|
226
|
-
7,
|
|
227
|
-
7,
|
|
228
|
-
7,
|
|
229
|
-
7,
|
|
230
|
-
7,
|
|
231
|
-
7,
|
|
232
|
-
7,
|
|
233
|
-
7,
|
|
234
|
-
7,
|
|
235
|
-
7,
|
|
236
|
-
8,
|
|
237
|
-
8,
|
|
238
|
-
8,
|
|
239
|
-
8,
|
|
240
|
-
8,
|
|
241
|
-
8,
|
|
242
|
-
8,
|
|
243
|
-
8,
|
|
244
|
-
8,
|
|
245
|
-
8,
|
|
246
|
-
8,
|
|
247
|
-
8,
|
|
248
|
-
8,
|
|
249
|
-
8,
|
|
250
|
-
8,
|
|
251
|
-
8
|
|
252
|
-
];
|
|
253
|
-
LENGTH_CODES = [
|
|
254
|
-
5,
|
|
255
|
-
3,
|
|
256
|
-
1,
|
|
257
|
-
6,
|
|
258
|
-
10,
|
|
259
|
-
2,
|
|
260
|
-
12,
|
|
261
|
-
20,
|
|
262
|
-
4,
|
|
263
|
-
24,
|
|
264
|
-
8,
|
|
265
|
-
48,
|
|
266
|
-
16,
|
|
267
|
-
32,
|
|
268
|
-
64,
|
|
269
|
-
0
|
|
270
|
-
];
|
|
271
|
-
LENGTH_BITS = [
|
|
272
|
-
3,
|
|
273
|
-
2,
|
|
274
|
-
3,
|
|
275
|
-
3,
|
|
276
|
-
4,
|
|
277
|
-
4,
|
|
278
|
-
4,
|
|
279
|
-
5,
|
|
280
|
-
5,
|
|
281
|
-
5,
|
|
282
|
-
5,
|
|
283
|
-
6,
|
|
284
|
-
6,
|
|
285
|
-
6,
|
|
286
|
-
7,
|
|
287
|
-
7
|
|
288
|
-
];
|
|
289
|
-
CH_BITS_ASC = [
|
|
290
|
-
11,
|
|
291
|
-
12,
|
|
292
|
-
12,
|
|
293
|
-
12,
|
|
294
|
-
12,
|
|
295
|
-
12,
|
|
296
|
-
12,
|
|
297
|
-
12,
|
|
298
|
-
12,
|
|
299
|
-
8,
|
|
300
|
-
7,
|
|
301
|
-
12,
|
|
302
|
-
12,
|
|
303
|
-
7,
|
|
304
|
-
12,
|
|
305
|
-
12,
|
|
306
|
-
12,
|
|
307
|
-
12,
|
|
308
|
-
12,
|
|
309
|
-
12,
|
|
310
|
-
12,
|
|
311
|
-
12,
|
|
312
|
-
12,
|
|
313
|
-
12,
|
|
314
|
-
12,
|
|
315
|
-
12,
|
|
316
|
-
13,
|
|
317
|
-
12,
|
|
318
|
-
12,
|
|
319
|
-
12,
|
|
320
|
-
12,
|
|
321
|
-
12,
|
|
322
|
-
4,
|
|
323
|
-
10,
|
|
324
|
-
8,
|
|
325
|
-
12,
|
|
326
|
-
10,
|
|
327
|
-
12,
|
|
328
|
-
10,
|
|
329
|
-
8,
|
|
330
|
-
7,
|
|
331
|
-
7,
|
|
332
|
-
8,
|
|
333
|
-
9,
|
|
334
|
-
7,
|
|
335
|
-
6,
|
|
336
|
-
7,
|
|
337
|
-
8,
|
|
338
|
-
7,
|
|
339
|
-
6,
|
|
340
|
-
7,
|
|
341
|
-
7,
|
|
342
|
-
7,
|
|
343
|
-
7,
|
|
344
|
-
8,
|
|
345
|
-
7,
|
|
346
|
-
7,
|
|
347
|
-
8,
|
|
348
|
-
8,
|
|
349
|
-
12,
|
|
350
|
-
11,
|
|
351
|
-
7,
|
|
352
|
-
9,
|
|
353
|
-
11,
|
|
354
|
-
12,
|
|
355
|
-
6,
|
|
356
|
-
7,
|
|
357
|
-
6,
|
|
358
|
-
6,
|
|
359
|
-
5,
|
|
360
|
-
7,
|
|
361
|
-
8,
|
|
362
|
-
8,
|
|
363
|
-
6,
|
|
364
|
-
11,
|
|
365
|
-
9,
|
|
366
|
-
6,
|
|
367
|
-
7,
|
|
368
|
-
6,
|
|
369
|
-
6,
|
|
370
|
-
7,
|
|
371
|
-
11,
|
|
372
|
-
6,
|
|
373
|
-
6,
|
|
374
|
-
6,
|
|
375
|
-
7,
|
|
376
|
-
9,
|
|
377
|
-
8,
|
|
378
|
-
9,
|
|
379
|
-
9,
|
|
380
|
-
11,
|
|
381
|
-
8,
|
|
382
|
-
11,
|
|
383
|
-
9,
|
|
384
|
-
12,
|
|
385
|
-
8,
|
|
386
|
-
12,
|
|
387
|
-
5,
|
|
388
|
-
6,
|
|
389
|
-
6,
|
|
390
|
-
6,
|
|
391
|
-
5,
|
|
392
|
-
6,
|
|
393
|
-
6,
|
|
394
|
-
6,
|
|
395
|
-
5,
|
|
396
|
-
11,
|
|
397
|
-
7,
|
|
398
|
-
5,
|
|
399
|
-
6,
|
|
400
|
-
5,
|
|
401
|
-
5,
|
|
402
|
-
6,
|
|
403
|
-
10,
|
|
404
|
-
5,
|
|
405
|
-
5,
|
|
406
|
-
5,
|
|
407
|
-
5,
|
|
408
|
-
8,
|
|
409
|
-
7,
|
|
410
|
-
8,
|
|
411
|
-
8,
|
|
412
|
-
10,
|
|
413
|
-
11,
|
|
414
|
-
11,
|
|
415
|
-
12,
|
|
416
|
-
12,
|
|
417
|
-
12,
|
|
418
|
-
13,
|
|
419
|
-
13,
|
|
420
|
-
13,
|
|
421
|
-
13,
|
|
422
|
-
13,
|
|
423
|
-
13,
|
|
424
|
-
13,
|
|
425
|
-
13,
|
|
426
|
-
13,
|
|
427
|
-
13,
|
|
428
|
-
13,
|
|
429
|
-
13,
|
|
430
|
-
13,
|
|
431
|
-
13,
|
|
432
|
-
13,
|
|
433
|
-
13,
|
|
434
|
-
13,
|
|
435
|
-
13,
|
|
436
|
-
13,
|
|
437
|
-
13,
|
|
438
|
-
13,
|
|
439
|
-
13,
|
|
440
|
-
13,
|
|
441
|
-
13,
|
|
442
|
-
13,
|
|
443
|
-
13,
|
|
444
|
-
13,
|
|
445
|
-
13,
|
|
446
|
-
13,
|
|
447
|
-
13,
|
|
448
|
-
13,
|
|
449
|
-
13,
|
|
450
|
-
13,
|
|
451
|
-
13,
|
|
452
|
-
13,
|
|
453
|
-
13,
|
|
454
|
-
13,
|
|
455
|
-
13,
|
|
456
|
-
13,
|
|
457
|
-
13,
|
|
458
|
-
13,
|
|
459
|
-
13,
|
|
460
|
-
13,
|
|
461
|
-
13,
|
|
462
|
-
13,
|
|
463
|
-
13,
|
|
464
|
-
13,
|
|
465
|
-
13,
|
|
466
|
-
12,
|
|
467
|
-
12,
|
|
468
|
-
12,
|
|
469
|
-
12,
|
|
470
|
-
12,
|
|
471
|
-
12,
|
|
472
|
-
12,
|
|
473
|
-
12,
|
|
474
|
-
12,
|
|
475
|
-
12,
|
|
476
|
-
12,
|
|
477
|
-
12,
|
|
478
|
-
12,
|
|
479
|
-
12,
|
|
480
|
-
12,
|
|
481
|
-
12,
|
|
482
|
-
12,
|
|
483
|
-
12,
|
|
484
|
-
12,
|
|
485
|
-
12,
|
|
486
|
-
12,
|
|
487
|
-
12,
|
|
488
|
-
12,
|
|
489
|
-
12,
|
|
490
|
-
12,
|
|
491
|
-
12,
|
|
492
|
-
12,
|
|
493
|
-
12,
|
|
494
|
-
12,
|
|
495
|
-
12,
|
|
496
|
-
12,
|
|
497
|
-
12,
|
|
498
|
-
12,
|
|
499
|
-
12,
|
|
500
|
-
12,
|
|
501
|
-
12,
|
|
502
|
-
12,
|
|
503
|
-
12,
|
|
504
|
-
12,
|
|
505
|
-
12,
|
|
506
|
-
12,
|
|
507
|
-
12,
|
|
508
|
-
12,
|
|
509
|
-
12,
|
|
510
|
-
12,
|
|
511
|
-
12,
|
|
512
|
-
12,
|
|
513
|
-
12,
|
|
514
|
-
13,
|
|
515
|
-
12,
|
|
516
|
-
13,
|
|
517
|
-
13,
|
|
518
|
-
13,
|
|
519
|
-
12,
|
|
520
|
-
13,
|
|
521
|
-
13,
|
|
522
|
-
13,
|
|
523
|
-
12,
|
|
524
|
-
13,
|
|
525
|
-
13,
|
|
526
|
-
13,
|
|
527
|
-
13,
|
|
528
|
-
12,
|
|
529
|
-
13,
|
|
530
|
-
13,
|
|
531
|
-
13,
|
|
532
|
-
12,
|
|
533
|
-
12,
|
|
534
|
-
12,
|
|
535
|
-
13,
|
|
536
|
-
13,
|
|
537
|
-
13,
|
|
538
|
-
13,
|
|
539
|
-
13,
|
|
540
|
-
13,
|
|
541
|
-
13,
|
|
542
|
-
13,
|
|
543
|
-
13,
|
|
544
|
-
13,
|
|
545
|
-
13
|
|
546
|
-
];
|
|
547
|
-
PkBitReader = class {
|
|
548
|
-
constructor(data) {
|
|
549
|
-
this.bytePos = 0;
|
|
550
|
-
this.bitBuf = 0;
|
|
551
|
-
this.bitsAvail = 0;
|
|
552
|
-
this.data = data;
|
|
553
|
-
}
|
|
554
|
-
fillBuffer() {
|
|
555
|
-
while (this.bitsAvail <= 24 && this.bytePos < this.data.length) {
|
|
556
|
-
this.bitBuf |= this.data[this.bytePos++] << this.bitsAvail;
|
|
557
|
-
this.bitsAvail += 8;
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
peekBits(n) {
|
|
561
|
-
this.fillBuffer();
|
|
562
|
-
return this.bitBuf & (1 << n) - 1;
|
|
563
|
-
}
|
|
564
|
-
skipBits(n) {
|
|
565
|
-
this.fillBuffer();
|
|
566
|
-
this.bitBuf >>>= n;
|
|
567
|
-
this.bitsAvail -= n;
|
|
568
|
-
}
|
|
569
|
-
readBits(n) {
|
|
570
|
-
const val = this.peekBits(n);
|
|
571
|
-
this.skipBits(n);
|
|
572
|
-
return val;
|
|
573
|
-
}
|
|
574
|
-
get isEof() {
|
|
575
|
-
return this.bytePos >= this.data.length && this.bitsAvail === 0;
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
// src/constants.ts
|
|
582
|
-
var ID_MPQ = 441536589;
|
|
583
|
-
var ID_MPQ_USERDATA = 458313805;
|
|
584
|
-
var MPQ_FORMAT_VERSION_1 = 0;
|
|
585
|
-
var MPQ_FORMAT_VERSION_2 = 1;
|
|
586
|
-
var MPQ_FORMAT_VERSION_3 = 2;
|
|
587
|
-
var MPQ_FORMAT_VERSION_4 = 3;
|
|
588
|
-
var MPQ_HEADER_SIZE_V1 = 32;
|
|
589
|
-
var MPQ_HEADER_SIZE_V2 = 44;
|
|
590
|
-
var MPQ_HEADER_SIZE_V3 = 68;
|
|
591
|
-
var MPQ_HEADER_SIZE_V4 = 208;
|
|
592
|
-
var HASH_ENTRY_FREE = 4294967295;
|
|
593
|
-
var HET_ENTRY_FREE = 0;
|
|
594
|
-
var MPQ_HASH_TABLE_INDEX = 0;
|
|
595
|
-
var MPQ_HASH_NAME_A = 256;
|
|
596
|
-
var MPQ_HASH_NAME_B = 512;
|
|
597
|
-
var MPQ_HASH_FILE_KEY = 768;
|
|
598
|
-
var MPQ_HASH_KEY2_MIX = 1024;
|
|
599
|
-
var STORM_BUFFER_SIZE = 1280;
|
|
600
|
-
var MPQ_KEY_HASH_TABLE = 3283040112;
|
|
601
|
-
var MPQ_KEY_BLOCK_TABLE = 3968054179;
|
|
602
|
-
var MPQ_FILE_IMPLODE = 256;
|
|
603
|
-
var MPQ_FILE_COMPRESS = 512;
|
|
604
|
-
var MPQ_FILE_ENCRYPTED = 65536;
|
|
605
|
-
var MPQ_FILE_KEY_V2 = 131072;
|
|
606
|
-
var MPQ_FILE_SINGLE_UNIT = 16777216;
|
|
607
|
-
var MPQ_FILE_SECTOR_CRC = 67108864;
|
|
608
|
-
var MPQ_FILE_EXISTS = 2147483648;
|
|
609
|
-
var MPQ_COMPRESSION_HUFFMANN = 1;
|
|
610
|
-
var MPQ_COMPRESSION_ZLIB = 2;
|
|
611
|
-
var MPQ_COMPRESSION_PKWARE = 8;
|
|
612
|
-
var MPQ_COMPRESSION_BZIP2 = 16;
|
|
613
|
-
var MPQ_COMPRESSION_LZMA = 18;
|
|
614
|
-
var MPQ_COMPRESSION_SPARSE = 32;
|
|
615
|
-
var MPQ_COMPRESSION_ADPCM_MONO = 64;
|
|
616
|
-
var MPQ_COMPRESSION_ADPCM_STEREO = 128;
|
|
617
|
-
var HET_TABLE_SIGNATURE = 441730376;
|
|
618
|
-
var BET_TABLE_SIGNATURE = 441730370;
|
|
619
|
-
var BLOCK_INDEX_MASK = 268435455;
|
|
620
|
-
var LISTFILE_NAME = "(listfile)";
|
|
621
|
-
var SIGNATURE_NAME = "(signature)";
|
|
622
|
-
var ATTRIBUTES_NAME = "(attributes)";
|
|
623
|
-
var MPQ_HEADER_SEARCH_ALIGNMENT = 512;
|
|
624
|
-
|
|
625
|
-
// src/errors.ts
|
|
626
|
-
var MpqError = class extends Error {
|
|
627
|
-
constructor(message) {
|
|
628
|
-
super(message);
|
|
629
|
-
this.name = "MpqError";
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
var MpqNotFoundError = class extends MpqError {
|
|
633
|
-
constructor(message = "File not found in archive") {
|
|
634
|
-
super(message);
|
|
635
|
-
this.name = "MpqNotFoundError";
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
var MpqCorruptError = class extends MpqError {
|
|
639
|
-
constructor(message = "Archive data is corrupt") {
|
|
640
|
-
super(message);
|
|
641
|
-
this.name = "MpqCorruptError";
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
var MpqUnsupportedError = class extends MpqError {
|
|
645
|
-
constructor(message = "Unsupported feature") {
|
|
646
|
-
super(message);
|
|
647
|
-
this.name = "MpqUnsupportedError";
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
var MpqEncryptionError = class extends MpqError {
|
|
651
|
-
constructor(message = "Unable to decrypt data") {
|
|
652
|
-
super(message);
|
|
653
|
-
this.name = "MpqEncryptionError";
|
|
654
|
-
}
|
|
655
|
-
};
|
|
656
|
-
var MpqCompressionError = class extends MpqError {
|
|
657
|
-
constructor(message = "Decompression failed") {
|
|
658
|
-
super(message);
|
|
659
|
-
this.name = "MpqCompressionError";
|
|
660
|
-
}
|
|
661
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
ATTRIBUTES_NAME,
|
|
3
|
+
BLOCK_INDEX_MASK,
|
|
4
|
+
ID_MPQ,
|
|
5
|
+
ID_MPQ_USERDATA,
|
|
6
|
+
LISTFILE_NAME,
|
|
7
|
+
MPQ_COMPRESSION_ADPCM_MONO,
|
|
8
|
+
MPQ_COMPRESSION_ADPCM_STEREO,
|
|
9
|
+
MPQ_COMPRESSION_BZIP2,
|
|
10
|
+
MPQ_COMPRESSION_HUFFMANN,
|
|
11
|
+
MPQ_COMPRESSION_LZMA,
|
|
12
|
+
MPQ_COMPRESSION_PKWARE,
|
|
13
|
+
MPQ_COMPRESSION_SPARSE,
|
|
14
|
+
MPQ_COMPRESSION_ZLIB,
|
|
15
|
+
MPQ_FILE_COMPRESS,
|
|
16
|
+
MPQ_FILE_ENCRYPTED,
|
|
17
|
+
MPQ_FILE_EXISTS,
|
|
18
|
+
MPQ_FILE_IMPLODE,
|
|
19
|
+
MPQ_FILE_KEY_V2,
|
|
20
|
+
MPQ_FILE_SECTOR_CRC,
|
|
21
|
+
MPQ_FILE_SINGLE_UNIT,
|
|
22
|
+
MPQ_FORMAT_VERSION_1,
|
|
23
|
+
MPQ_FORMAT_VERSION_2,
|
|
24
|
+
MPQ_FORMAT_VERSION_3,
|
|
25
|
+
MPQ_FORMAT_VERSION_4,
|
|
26
|
+
MpqCompressionError,
|
|
27
|
+
MpqCorruptError,
|
|
28
|
+
MpqEncryptionError,
|
|
29
|
+
MpqError,
|
|
30
|
+
MpqNotFoundError,
|
|
31
|
+
MpqUnsupportedError,
|
|
32
|
+
SIGNATURE_NAME,
|
|
33
|
+
__require,
|
|
34
|
+
__toCommonJS,
|
|
35
|
+
applyFileNames,
|
|
36
|
+
buildFileTable,
|
|
37
|
+
buildFileTableFromHetBet,
|
|
38
|
+
decompress,
|
|
39
|
+
decompressPkware,
|
|
40
|
+
decryptBlock,
|
|
41
|
+
decryptFileKey,
|
|
42
|
+
encryptBlock,
|
|
43
|
+
findHashEntry,
|
|
44
|
+
findHeader,
|
|
45
|
+
findInHetTable,
|
|
46
|
+
getBetTableOffset,
|
|
47
|
+
getBlockTableOffset,
|
|
48
|
+
getHashTableOffset,
|
|
49
|
+
getHetTableOffset,
|
|
50
|
+
getHiBlockTableOffset,
|
|
51
|
+
getSectorSize,
|
|
52
|
+
getStormBuffer,
|
|
53
|
+
hashFileKey,
|
|
54
|
+
hashNameA,
|
|
55
|
+
hashNameB,
|
|
56
|
+
hashString,
|
|
57
|
+
hashTableIndex,
|
|
58
|
+
init_pkware,
|
|
59
|
+
jenkinsHash,
|
|
60
|
+
loadBetTable,
|
|
61
|
+
loadBlockTable,
|
|
62
|
+
loadHashTable,
|
|
63
|
+
loadHetTable,
|
|
64
|
+
loadHiBlockTable,
|
|
65
|
+
parseAttributes,
|
|
66
|
+
parseListfile,
|
|
67
|
+
pkware_exports
|
|
68
|
+
} from "./chunk-RRBXXQVG.mjs";
|
|
662
69
|
|
|
663
70
|
// src/stream/file-stream.ts
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
constructor(fd, fileSize, filePath) {
|
|
71
|
+
var _FileStream = class _FileStream {
|
|
72
|
+
constructor(fd, fileSize, filePath, memoryData = null) {
|
|
667
73
|
this.fd = fd;
|
|
668
74
|
this.fileSize = fileSize;
|
|
669
75
|
this.filePath = filePath;
|
|
76
|
+
this.memoryData = memoryData;
|
|
670
77
|
}
|
|
671
78
|
/**
|
|
672
79
|
* Open a file for reading.
|
|
673
80
|
*/
|
|
674
81
|
static open(path) {
|
|
82
|
+
const fs = _FileStream.getFs();
|
|
675
83
|
const fd = fs.openSync(path, "r");
|
|
676
84
|
const stat = fs.fstatSync(fd);
|
|
677
85
|
return new _FileStream(fd, BigInt(stat.size), path);
|
|
678
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Create an in-memory stream from binary data.
|
|
89
|
+
*/
|
|
90
|
+
static fromBuffer(data, filePath = "[buffer]") {
|
|
91
|
+
const buffer = _FileStream.normalizeBuffer(data);
|
|
92
|
+
return new _FileStream(-1, BigInt(buffer.length), filePath, buffer);
|
|
93
|
+
}
|
|
94
|
+
static normalizeBuffer(data) {
|
|
95
|
+
if (Buffer.isBuffer(data)) {
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
if (data instanceof Uint8Array) {
|
|
99
|
+
return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
100
|
+
}
|
|
101
|
+
return Buffer.from(data);
|
|
102
|
+
}
|
|
103
|
+
static getRequire() {
|
|
104
|
+
try {
|
|
105
|
+
if (typeof __require === "function") {
|
|
106
|
+
return __require;
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
return Function('return typeof require === "function" ? require : null;')();
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
static getFs() {
|
|
117
|
+
if (_FileStream.fsModule) {
|
|
118
|
+
return _FileStream.fsModule;
|
|
119
|
+
}
|
|
120
|
+
const req = _FileStream.getRequire();
|
|
121
|
+
if (!req) {
|
|
122
|
+
throw new Error("Node.js fs module is not available in this runtime. Use MpqArchive.openFromBuffer().");
|
|
123
|
+
}
|
|
124
|
+
const fs = req("fs");
|
|
125
|
+
if (!fs) {
|
|
126
|
+
throw new Error("Failed to load Node.js fs module.");
|
|
127
|
+
}
|
|
128
|
+
_FileStream.fsModule = fs;
|
|
129
|
+
return _FileStream.fsModule;
|
|
130
|
+
}
|
|
679
131
|
/**
|
|
680
132
|
* Read bytes at a given offset.
|
|
681
133
|
*
|
|
@@ -684,6 +136,15 @@ var FileStream = class _FileStream {
|
|
|
684
136
|
* @returns Buffer containing the read data
|
|
685
137
|
*/
|
|
686
138
|
read(offset, length) {
|
|
139
|
+
if (this.memoryData) {
|
|
140
|
+
const start = Number(offset);
|
|
141
|
+
if (start >= this.memoryData.length || length <= 0) {
|
|
142
|
+
return Buffer.alloc(0);
|
|
143
|
+
}
|
|
144
|
+
const end = Math.min(start + length, this.memoryData.length);
|
|
145
|
+
return this.memoryData.subarray(start, end);
|
|
146
|
+
}
|
|
147
|
+
const fs = _FileStream.getFs();
|
|
687
148
|
const buffer = Buffer.alloc(length);
|
|
688
149
|
const bytesRead = fs.readSync(this.fd, buffer, 0, length, Number(offset));
|
|
689
150
|
if (bytesRead < length) {
|
|
@@ -695,6 +156,15 @@ var FileStream = class _FileStream {
|
|
|
695
156
|
* Read bytes into an existing buffer.
|
|
696
157
|
*/
|
|
697
158
|
readInto(offset, buffer, bufOffset, length) {
|
|
159
|
+
if (this.memoryData) {
|
|
160
|
+
const start = Number(offset);
|
|
161
|
+
if (start >= this.memoryData.length || length <= 0) {
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
const end = Math.min(start + length, this.memoryData.length);
|
|
165
|
+
return this.memoryData.copy(buffer, bufOffset, start, end);
|
|
166
|
+
}
|
|
167
|
+
const fs = _FileStream.getFs();
|
|
698
168
|
return fs.readSync(this.fd, buffer, bufOffset, length, Number(offset));
|
|
699
169
|
}
|
|
700
170
|
/**
|
|
@@ -713,1685 +183,16 @@ var FileStream = class _FileStream {
|
|
|
713
183
|
* Close the file stream.
|
|
714
184
|
*/
|
|
715
185
|
close() {
|
|
716
|
-
if (this.fd >= 0) {
|
|
186
|
+
if (this.fd >= 0 && !this.memoryData) {
|
|
187
|
+
const fs = _FileStream.getFs();
|
|
717
188
|
fs.closeSync(this.fd);
|
|
718
189
|
this.fd = -1;
|
|
719
190
|
}
|
|
191
|
+
this.memoryData = null;
|
|
720
192
|
}
|
|
721
193
|
};
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
function createDefaultHeader() {
|
|
725
|
-
return {
|
|
726
|
-
dwID: 0,
|
|
727
|
-
dwHeaderSize: 0,
|
|
728
|
-
dwArchiveSize: 0,
|
|
729
|
-
wFormatVersion: 0,
|
|
730
|
-
wSectorSize: 0,
|
|
731
|
-
dwHashTablePos: 0,
|
|
732
|
-
dwBlockTablePos: 0,
|
|
733
|
-
dwHashTableSize: 0,
|
|
734
|
-
dwBlockTableSize: 0,
|
|
735
|
-
hiBlockTablePos64: 0n,
|
|
736
|
-
wHashTablePosHi: 0,
|
|
737
|
-
wBlockTablePosHi: 0,
|
|
738
|
-
archiveSize64: 0n,
|
|
739
|
-
betTablePos64: 0n,
|
|
740
|
-
hetTablePos64: 0n,
|
|
741
|
-
hashTableSize64: 0n,
|
|
742
|
-
blockTableSize64: 0n,
|
|
743
|
-
hiBlockTableSize64: 0n,
|
|
744
|
-
hetTableSize64: 0n,
|
|
745
|
-
betTableSize64: 0n,
|
|
746
|
-
dwRawChunkSize: 0,
|
|
747
|
-
md5BlockTable: new Uint8Array(16),
|
|
748
|
-
md5HashTable: new Uint8Array(16),
|
|
749
|
-
md5HiBlockTable: new Uint8Array(16),
|
|
750
|
-
md5BetTable: new Uint8Array(16),
|
|
751
|
-
md5HetTable: new Uint8Array(16),
|
|
752
|
-
md5MpqHeader: new Uint8Array(16)
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
function parseHeaderV1(data, header) {
|
|
756
|
-
header.dwID = data.readUInt32LE(0);
|
|
757
|
-
header.dwHeaderSize = data.readUInt32LE(4);
|
|
758
|
-
header.dwArchiveSize = data.readUInt32LE(8);
|
|
759
|
-
header.wFormatVersion = data.readUInt16LE(12);
|
|
760
|
-
header.wSectorSize = data.readUInt16LE(14);
|
|
761
|
-
header.dwHashTablePos = data.readUInt32LE(16);
|
|
762
|
-
header.dwBlockTablePos = data.readUInt32LE(20);
|
|
763
|
-
header.dwHashTableSize = data.readUInt32LE(24);
|
|
764
|
-
header.dwBlockTableSize = data.readUInt32LE(28);
|
|
765
|
-
header.archiveSize64 = BigInt(header.dwArchiveSize);
|
|
766
|
-
header.hashTableSize64 = BigInt(header.dwHashTableSize * 16);
|
|
767
|
-
header.blockTableSize64 = BigInt(header.dwBlockTableSize * 16);
|
|
768
|
-
}
|
|
769
|
-
function parseHeaderV2(data, header) {
|
|
770
|
-
if (data.length < MPQ_HEADER_SIZE_V2) return;
|
|
771
|
-
header.hiBlockTablePos64 = data.readBigUInt64LE(32);
|
|
772
|
-
header.wHashTablePosHi = data.readUInt16LE(40);
|
|
773
|
-
header.wBlockTablePosHi = data.readUInt16LE(42);
|
|
774
|
-
}
|
|
775
|
-
function parseHeaderV3(data, header) {
|
|
776
|
-
if (data.length < MPQ_HEADER_SIZE_V3) return;
|
|
777
|
-
header.archiveSize64 = data.readBigUInt64LE(44);
|
|
778
|
-
header.betTablePos64 = data.readBigUInt64LE(52);
|
|
779
|
-
header.hetTablePos64 = data.readBigUInt64LE(60);
|
|
780
|
-
}
|
|
781
|
-
function parseHeaderV4(data, header) {
|
|
782
|
-
if (data.length < MPQ_HEADER_SIZE_V4) return;
|
|
783
|
-
header.hashTableSize64 = data.readBigUInt64LE(68);
|
|
784
|
-
header.blockTableSize64 = data.readBigUInt64LE(76);
|
|
785
|
-
header.hiBlockTableSize64 = data.readBigUInt64LE(84);
|
|
786
|
-
header.hetTableSize64 = data.readBigUInt64LE(92);
|
|
787
|
-
header.betTableSize64 = data.readBigUInt64LE(100);
|
|
788
|
-
header.dwRawChunkSize = data.readUInt32LE(108);
|
|
789
|
-
header.md5BlockTable = new Uint8Array(data.subarray(112, 128));
|
|
790
|
-
header.md5HashTable = new Uint8Array(data.subarray(128, 144));
|
|
791
|
-
header.md5HiBlockTable = new Uint8Array(data.subarray(144, 160));
|
|
792
|
-
header.md5BetTable = new Uint8Array(data.subarray(160, 176));
|
|
793
|
-
header.md5HetTable = new Uint8Array(data.subarray(176, 192));
|
|
794
|
-
header.md5MpqHeader = new Uint8Array(data.subarray(192, 208));
|
|
795
|
-
}
|
|
796
|
-
function parseHeader(data) {
|
|
797
|
-
if (data.length < MPQ_HEADER_SIZE_V1) {
|
|
798
|
-
throw new MpqCorruptError("Header data too small");
|
|
799
|
-
}
|
|
800
|
-
const header = createDefaultHeader();
|
|
801
|
-
parseHeaderV1(data, header);
|
|
802
|
-
if (header.dwID !== ID_MPQ) {
|
|
803
|
-
throw new MpqCorruptError(`Invalid MPQ signature: 0x${header.dwID.toString(16)}`);
|
|
804
|
-
}
|
|
805
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_2) {
|
|
806
|
-
parseHeaderV2(data, header);
|
|
807
|
-
}
|
|
808
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_3) {
|
|
809
|
-
parseHeaderV3(data, header);
|
|
810
|
-
}
|
|
811
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_4) {
|
|
812
|
-
parseHeaderV4(data, header);
|
|
813
|
-
}
|
|
814
|
-
return header;
|
|
815
|
-
}
|
|
816
|
-
function parseUserData(data) {
|
|
817
|
-
return {
|
|
818
|
-
dwID: data.readUInt32LE(0),
|
|
819
|
-
cbUserDataSize: data.readUInt32LE(4),
|
|
820
|
-
dwHeaderOffs: data.readUInt32LE(8),
|
|
821
|
-
cbUserDataHeader: data.readUInt32LE(12)
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
function findHeader(stream, noHeaderSearch = false) {
|
|
825
|
-
const fileSize = stream.getSize();
|
|
826
|
-
const searchLimit = noHeaderSearch ? 0n : fileSize;
|
|
827
|
-
let userData;
|
|
828
|
-
let userDataOffset;
|
|
829
|
-
for (let offset = 0n; offset <= searchLimit; offset += BigInt(MPQ_HEADER_SEARCH_ALIGNMENT)) {
|
|
830
|
-
const sigBuf = stream.read(offset, 4);
|
|
831
|
-
if (sigBuf.length < 4) break;
|
|
832
|
-
const signature = sigBuf.readUInt32LE(0);
|
|
833
|
-
if (signature === ID_MPQ_USERDATA && !noHeaderSearch) {
|
|
834
|
-
const udBuf = stream.read(offset, 16);
|
|
835
|
-
if (udBuf.length >= 16) {
|
|
836
|
-
userData = parseUserData(udBuf);
|
|
837
|
-
userDataOffset = offset;
|
|
838
|
-
const mpqOffset = offset + BigInt(userData.dwHeaderOffs);
|
|
839
|
-
const headerBuf = stream.read(mpqOffset, MPQ_HEADER_SIZE_V4);
|
|
840
|
-
if (headerBuf.length >= MPQ_HEADER_SIZE_V1) {
|
|
841
|
-
const headerSig = headerBuf.readUInt32LE(0);
|
|
842
|
-
if (headerSig === ID_MPQ) {
|
|
843
|
-
const header = parseHeader(headerBuf);
|
|
844
|
-
return { header, headerOffset: mpqOffset, userData, userDataOffset };
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
} else if (signature === ID_MPQ) {
|
|
849
|
-
const headerBuf = stream.read(offset, MPQ_HEADER_SIZE_V4);
|
|
850
|
-
if (headerBuf.length >= MPQ_HEADER_SIZE_V1) {
|
|
851
|
-
const header = parseHeader(headerBuf);
|
|
852
|
-
return { header, headerOffset: offset, userData, userDataOffset };
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
if (noHeaderSearch) break;
|
|
856
|
-
}
|
|
857
|
-
throw new MpqNotFoundError("MPQ header not found in file");
|
|
858
|
-
}
|
|
859
|
-
function getHashTableOffset(header, archiveOffset) {
|
|
860
|
-
let offset = BigInt(header.dwHashTablePos);
|
|
861
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_2) {
|
|
862
|
-
offset |= BigInt(header.wHashTablePosHi) << 32n;
|
|
863
|
-
}
|
|
864
|
-
return archiveOffset + offset;
|
|
865
|
-
}
|
|
866
|
-
function getBlockTableOffset(header, archiveOffset) {
|
|
867
|
-
let offset = BigInt(header.dwBlockTablePos);
|
|
868
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_2) {
|
|
869
|
-
offset |= BigInt(header.wBlockTablePosHi) << 32n;
|
|
870
|
-
}
|
|
871
|
-
return archiveOffset + offset;
|
|
872
|
-
}
|
|
873
|
-
function getHiBlockTableOffset(header, archiveOffset) {
|
|
874
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_2 && header.hiBlockTablePos64 !== 0n) {
|
|
875
|
-
return archiveOffset + header.hiBlockTablePos64;
|
|
876
|
-
}
|
|
877
|
-
return 0n;
|
|
878
|
-
}
|
|
879
|
-
function getHetTableOffset(header, archiveOffset) {
|
|
880
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_3 && header.hetTablePos64 !== 0n) {
|
|
881
|
-
return archiveOffset + header.hetTablePos64;
|
|
882
|
-
}
|
|
883
|
-
return 0n;
|
|
884
|
-
}
|
|
885
|
-
function getBetTableOffset(header, archiveOffset) {
|
|
886
|
-
if (header.wFormatVersion >= MPQ_FORMAT_VERSION_3 && header.betTablePos64 !== 0n) {
|
|
887
|
-
return archiveOffset + header.betTablePos64;
|
|
888
|
-
}
|
|
889
|
-
return 0n;
|
|
890
|
-
}
|
|
891
|
-
function getSectorSize(header) {
|
|
892
|
-
return 512 << header.wSectorSize;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
// src/crypto/storm-buffer.ts
|
|
896
|
-
var stormBuffer = new Uint32Array(STORM_BUFFER_SIZE);
|
|
897
|
-
var initialized = false;
|
|
898
|
-
function initializeStormBuffer() {
|
|
899
|
-
let seed = 1048577;
|
|
900
|
-
for (let index1 = 0; index1 < 256; index1++) {
|
|
901
|
-
let index2 = index1;
|
|
902
|
-
for (let i = 0; i < 5; i++, index2 += 256) {
|
|
903
|
-
seed = (seed * 125 + 3) % 2796203;
|
|
904
|
-
const temp1 = (seed & 65535) << 16;
|
|
905
|
-
seed = (seed * 125 + 3) % 2796203;
|
|
906
|
-
const temp2 = seed & 65535;
|
|
907
|
-
stormBuffer[index2] = (temp1 | temp2) >>> 0;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
initialized = true;
|
|
911
|
-
}
|
|
912
|
-
function getStormBuffer() {
|
|
913
|
-
if (!initialized) {
|
|
914
|
-
initializeStormBuffer();
|
|
915
|
-
}
|
|
916
|
-
return stormBuffer;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
// src/crypto/hash.ts
|
|
920
|
-
var AsciiToUpperTable = new Uint8Array([
|
|
921
|
-
0,
|
|
922
|
-
1,
|
|
923
|
-
2,
|
|
924
|
-
3,
|
|
925
|
-
4,
|
|
926
|
-
5,
|
|
927
|
-
6,
|
|
928
|
-
7,
|
|
929
|
-
8,
|
|
930
|
-
9,
|
|
931
|
-
10,
|
|
932
|
-
11,
|
|
933
|
-
12,
|
|
934
|
-
13,
|
|
935
|
-
14,
|
|
936
|
-
15,
|
|
937
|
-
16,
|
|
938
|
-
17,
|
|
939
|
-
18,
|
|
940
|
-
19,
|
|
941
|
-
20,
|
|
942
|
-
21,
|
|
943
|
-
22,
|
|
944
|
-
23,
|
|
945
|
-
24,
|
|
946
|
-
25,
|
|
947
|
-
26,
|
|
948
|
-
27,
|
|
949
|
-
28,
|
|
950
|
-
29,
|
|
951
|
-
30,
|
|
952
|
-
31,
|
|
953
|
-
32,
|
|
954
|
-
33,
|
|
955
|
-
34,
|
|
956
|
-
35,
|
|
957
|
-
36,
|
|
958
|
-
37,
|
|
959
|
-
38,
|
|
960
|
-
39,
|
|
961
|
-
40,
|
|
962
|
-
41,
|
|
963
|
-
42,
|
|
964
|
-
43,
|
|
965
|
-
44,
|
|
966
|
-
45,
|
|
967
|
-
46,
|
|
968
|
-
92,
|
|
969
|
-
// '/' -> '\'
|
|
970
|
-
48,
|
|
971
|
-
49,
|
|
972
|
-
50,
|
|
973
|
-
51,
|
|
974
|
-
52,
|
|
975
|
-
53,
|
|
976
|
-
54,
|
|
977
|
-
55,
|
|
978
|
-
56,
|
|
979
|
-
57,
|
|
980
|
-
58,
|
|
981
|
-
59,
|
|
982
|
-
60,
|
|
983
|
-
61,
|
|
984
|
-
62,
|
|
985
|
-
63,
|
|
986
|
-
64,
|
|
987
|
-
65,
|
|
988
|
-
66,
|
|
989
|
-
67,
|
|
990
|
-
68,
|
|
991
|
-
69,
|
|
992
|
-
70,
|
|
993
|
-
71,
|
|
994
|
-
72,
|
|
995
|
-
73,
|
|
996
|
-
74,
|
|
997
|
-
75,
|
|
998
|
-
76,
|
|
999
|
-
77,
|
|
1000
|
-
78,
|
|
1001
|
-
79,
|
|
1002
|
-
80,
|
|
1003
|
-
81,
|
|
1004
|
-
82,
|
|
1005
|
-
83,
|
|
1006
|
-
84,
|
|
1007
|
-
85,
|
|
1008
|
-
86,
|
|
1009
|
-
87,
|
|
1010
|
-
88,
|
|
1011
|
-
89,
|
|
1012
|
-
90,
|
|
1013
|
-
91,
|
|
1014
|
-
92,
|
|
1015
|
-
93,
|
|
1016
|
-
94,
|
|
1017
|
-
95,
|
|
1018
|
-
96,
|
|
1019
|
-
65,
|
|
1020
|
-
66,
|
|
1021
|
-
67,
|
|
1022
|
-
68,
|
|
1023
|
-
69,
|
|
1024
|
-
70,
|
|
1025
|
-
71,
|
|
1026
|
-
72,
|
|
1027
|
-
73,
|
|
1028
|
-
74,
|
|
1029
|
-
75,
|
|
1030
|
-
76,
|
|
1031
|
-
77,
|
|
1032
|
-
78,
|
|
1033
|
-
79,
|
|
1034
|
-
80,
|
|
1035
|
-
81,
|
|
1036
|
-
82,
|
|
1037
|
-
83,
|
|
1038
|
-
84,
|
|
1039
|
-
85,
|
|
1040
|
-
86,
|
|
1041
|
-
87,
|
|
1042
|
-
88,
|
|
1043
|
-
89,
|
|
1044
|
-
90,
|
|
1045
|
-
123,
|
|
1046
|
-
124,
|
|
1047
|
-
125,
|
|
1048
|
-
126,
|
|
1049
|
-
127,
|
|
1050
|
-
128,
|
|
1051
|
-
129,
|
|
1052
|
-
130,
|
|
1053
|
-
131,
|
|
1054
|
-
132,
|
|
1055
|
-
133,
|
|
1056
|
-
134,
|
|
1057
|
-
135,
|
|
1058
|
-
136,
|
|
1059
|
-
137,
|
|
1060
|
-
138,
|
|
1061
|
-
139,
|
|
1062
|
-
140,
|
|
1063
|
-
141,
|
|
1064
|
-
142,
|
|
1065
|
-
143,
|
|
1066
|
-
144,
|
|
1067
|
-
145,
|
|
1068
|
-
146,
|
|
1069
|
-
147,
|
|
1070
|
-
148,
|
|
1071
|
-
149,
|
|
1072
|
-
150,
|
|
1073
|
-
151,
|
|
1074
|
-
152,
|
|
1075
|
-
153,
|
|
1076
|
-
154,
|
|
1077
|
-
155,
|
|
1078
|
-
156,
|
|
1079
|
-
157,
|
|
1080
|
-
158,
|
|
1081
|
-
159,
|
|
1082
|
-
160,
|
|
1083
|
-
161,
|
|
1084
|
-
162,
|
|
1085
|
-
163,
|
|
1086
|
-
164,
|
|
1087
|
-
165,
|
|
1088
|
-
166,
|
|
1089
|
-
167,
|
|
1090
|
-
168,
|
|
1091
|
-
169,
|
|
1092
|
-
170,
|
|
1093
|
-
171,
|
|
1094
|
-
172,
|
|
1095
|
-
173,
|
|
1096
|
-
174,
|
|
1097
|
-
175,
|
|
1098
|
-
176,
|
|
1099
|
-
177,
|
|
1100
|
-
178,
|
|
1101
|
-
179,
|
|
1102
|
-
180,
|
|
1103
|
-
181,
|
|
1104
|
-
182,
|
|
1105
|
-
183,
|
|
1106
|
-
184,
|
|
1107
|
-
185,
|
|
1108
|
-
186,
|
|
1109
|
-
187,
|
|
1110
|
-
188,
|
|
1111
|
-
189,
|
|
1112
|
-
190,
|
|
1113
|
-
191,
|
|
1114
|
-
192,
|
|
1115
|
-
193,
|
|
1116
|
-
194,
|
|
1117
|
-
195,
|
|
1118
|
-
196,
|
|
1119
|
-
197,
|
|
1120
|
-
198,
|
|
1121
|
-
199,
|
|
1122
|
-
200,
|
|
1123
|
-
201,
|
|
1124
|
-
202,
|
|
1125
|
-
203,
|
|
1126
|
-
204,
|
|
1127
|
-
205,
|
|
1128
|
-
206,
|
|
1129
|
-
207,
|
|
1130
|
-
208,
|
|
1131
|
-
209,
|
|
1132
|
-
210,
|
|
1133
|
-
211,
|
|
1134
|
-
212,
|
|
1135
|
-
213,
|
|
1136
|
-
214,
|
|
1137
|
-
215,
|
|
1138
|
-
216,
|
|
1139
|
-
217,
|
|
1140
|
-
218,
|
|
1141
|
-
219,
|
|
1142
|
-
220,
|
|
1143
|
-
221,
|
|
1144
|
-
222,
|
|
1145
|
-
223,
|
|
1146
|
-
224,
|
|
1147
|
-
225,
|
|
1148
|
-
226,
|
|
1149
|
-
227,
|
|
1150
|
-
228,
|
|
1151
|
-
229,
|
|
1152
|
-
230,
|
|
1153
|
-
231,
|
|
1154
|
-
232,
|
|
1155
|
-
233,
|
|
1156
|
-
234,
|
|
1157
|
-
235,
|
|
1158
|
-
236,
|
|
1159
|
-
237,
|
|
1160
|
-
238,
|
|
1161
|
-
239,
|
|
1162
|
-
240,
|
|
1163
|
-
241,
|
|
1164
|
-
242,
|
|
1165
|
-
243,
|
|
1166
|
-
244,
|
|
1167
|
-
245,
|
|
1168
|
-
246,
|
|
1169
|
-
247,
|
|
1170
|
-
248,
|
|
1171
|
-
249,
|
|
1172
|
-
250,
|
|
1173
|
-
251,
|
|
1174
|
-
252,
|
|
1175
|
-
253,
|
|
1176
|
-
254,
|
|
1177
|
-
255
|
|
1178
|
-
]);
|
|
1179
|
-
function hashString(str, hashType) {
|
|
1180
|
-
const buffer = getStormBuffer();
|
|
1181
|
-
let seed1 = 2146271213;
|
|
1182
|
-
let seed2 = 4008636142;
|
|
1183
|
-
for (let i = 0; i < str.length; i++) {
|
|
1184
|
-
const ch = AsciiToUpperTable[str.charCodeAt(i) & 255];
|
|
1185
|
-
seed1 = (buffer[hashType + ch] ^ seed1 + seed2 >>> 0) >>> 0;
|
|
1186
|
-
seed2 = (ch + seed1 + seed2 + (seed2 << 5) + 3 & 4294967295) >>> 0;
|
|
1187
|
-
}
|
|
1188
|
-
return seed1 >>> 0;
|
|
1189
|
-
}
|
|
1190
|
-
function hashTableIndex(fileName) {
|
|
1191
|
-
return hashString(fileName, MPQ_HASH_TABLE_INDEX);
|
|
1192
|
-
}
|
|
1193
|
-
function hashNameA(fileName) {
|
|
1194
|
-
return hashString(fileName, MPQ_HASH_NAME_A);
|
|
1195
|
-
}
|
|
1196
|
-
function hashNameB(fileName) {
|
|
1197
|
-
return hashString(fileName, MPQ_HASH_NAME_B);
|
|
1198
|
-
}
|
|
1199
|
-
function hashFileKey(fileName) {
|
|
1200
|
-
return hashString(fileName, MPQ_HASH_FILE_KEY);
|
|
1201
|
-
}
|
|
1202
|
-
function jenkinsHash(str) {
|
|
1203
|
-
let a = 3735928559 + str.length;
|
|
1204
|
-
let b = a;
|
|
1205
|
-
let c = a;
|
|
1206
|
-
let i = 0;
|
|
1207
|
-
while (i + 12 <= str.length) {
|
|
1208
|
-
a = a + str.charCodeAt(i) >>> 0;
|
|
1209
|
-
a = a + (str.charCodeAt(i + 1) << 8) >>> 0;
|
|
1210
|
-
a = a + (str.charCodeAt(i + 2) << 16) >>> 0;
|
|
1211
|
-
a = a + (str.charCodeAt(i + 3) << 24 >>> 0) >>> 0;
|
|
1212
|
-
b = b + str.charCodeAt(i + 4) >>> 0;
|
|
1213
|
-
b = b + (str.charCodeAt(i + 5) << 8) >>> 0;
|
|
1214
|
-
b = b + (str.charCodeAt(i + 6) << 16) >>> 0;
|
|
1215
|
-
b = b + (str.charCodeAt(i + 7) << 24 >>> 0) >>> 0;
|
|
1216
|
-
c = c + str.charCodeAt(i + 8) >>> 0;
|
|
1217
|
-
c = c + (str.charCodeAt(i + 9) << 8) >>> 0;
|
|
1218
|
-
c = c + (str.charCodeAt(i + 10) << 16) >>> 0;
|
|
1219
|
-
c = c + (str.charCodeAt(i + 11) << 24 >>> 0) >>> 0;
|
|
1220
|
-
a = a - c >>> 0;
|
|
1221
|
-
a = (a ^ (c << 4 | c >>> 28)) >>> 0;
|
|
1222
|
-
c = c + b >>> 0;
|
|
1223
|
-
b = b - a >>> 0;
|
|
1224
|
-
b = (b ^ (a << 6 | a >>> 26)) >>> 0;
|
|
1225
|
-
a = a + c >>> 0;
|
|
1226
|
-
c = c - b >>> 0;
|
|
1227
|
-
c = (c ^ (b << 8 | b >>> 24)) >>> 0;
|
|
1228
|
-
b = b + a >>> 0;
|
|
1229
|
-
a = a - c >>> 0;
|
|
1230
|
-
a = (a ^ (c << 16 | c >>> 16)) >>> 0;
|
|
1231
|
-
c = c + b >>> 0;
|
|
1232
|
-
b = b - a >>> 0;
|
|
1233
|
-
b = (b ^ (a << 19 | a >>> 13)) >>> 0;
|
|
1234
|
-
a = a + c >>> 0;
|
|
1235
|
-
c = c - b >>> 0;
|
|
1236
|
-
c = (c ^ (b << 4 | b >>> 28)) >>> 0;
|
|
1237
|
-
b = b + a >>> 0;
|
|
1238
|
-
i += 12;
|
|
1239
|
-
}
|
|
1240
|
-
const remaining = str.length - i;
|
|
1241
|
-
switch (remaining) {
|
|
1242
|
-
case 12:
|
|
1243
|
-
c = c + (str.charCodeAt(i + 11) << 24 >>> 0) >>> 0;
|
|
1244
|
-
// fallthrough
|
|
1245
|
-
case 11:
|
|
1246
|
-
c = c + (str.charCodeAt(i + 10) << 16) >>> 0;
|
|
1247
|
-
case 10:
|
|
1248
|
-
c = c + (str.charCodeAt(i + 9) << 8) >>> 0;
|
|
1249
|
-
case 9:
|
|
1250
|
-
c = c + str.charCodeAt(i + 8) >>> 0;
|
|
1251
|
-
case 8:
|
|
1252
|
-
b = b + (str.charCodeAt(i + 7) << 24 >>> 0) >>> 0;
|
|
1253
|
-
case 7:
|
|
1254
|
-
b = b + (str.charCodeAt(i + 6) << 16) >>> 0;
|
|
1255
|
-
case 6:
|
|
1256
|
-
b = b + (str.charCodeAt(i + 5) << 8) >>> 0;
|
|
1257
|
-
case 5:
|
|
1258
|
-
b = b + str.charCodeAt(i + 4) >>> 0;
|
|
1259
|
-
case 4:
|
|
1260
|
-
a = a + (str.charCodeAt(i + 3) << 24 >>> 0) >>> 0;
|
|
1261
|
-
case 3:
|
|
1262
|
-
a = a + (str.charCodeAt(i + 2) << 16) >>> 0;
|
|
1263
|
-
case 2:
|
|
1264
|
-
a = a + (str.charCodeAt(i + 1) << 8) >>> 0;
|
|
1265
|
-
case 1:
|
|
1266
|
-
a = a + str.charCodeAt(i) >>> 0;
|
|
1267
|
-
break;
|
|
1268
|
-
case 0:
|
|
1269
|
-
return BigInt(c >>> 0) << 32n | BigInt(b >>> 0);
|
|
1270
|
-
}
|
|
1271
|
-
c = (c ^ b) >>> 0;
|
|
1272
|
-
c = c - (b << 14 | b >>> 18) >>> 0;
|
|
1273
|
-
a = (a ^ c) >>> 0;
|
|
1274
|
-
a = a - (c << 11 | c >>> 21) >>> 0;
|
|
1275
|
-
b = (b ^ a) >>> 0;
|
|
1276
|
-
b = b - (a << 25 | a >>> 7) >>> 0;
|
|
1277
|
-
c = (c ^ b) >>> 0;
|
|
1278
|
-
c = c - (b << 16 | b >>> 16) >>> 0;
|
|
1279
|
-
a = (a ^ c) >>> 0;
|
|
1280
|
-
a = a - (c << 4 | c >>> 28) >>> 0;
|
|
1281
|
-
b = (b ^ a) >>> 0;
|
|
1282
|
-
b = b - (a << 14 | a >>> 18) >>> 0;
|
|
1283
|
-
c = (c ^ b) >>> 0;
|
|
1284
|
-
c = c - (b << 24 | b >>> 8) >>> 0;
|
|
1285
|
-
return BigInt(c >>> 0) << 32n | BigInt(b >>> 0);
|
|
1286
|
-
}
|
|
1287
|
-
function getPlainFileName(filePath) {
|
|
1288
|
-
let plainStart = 0;
|
|
1289
|
-
for (let i = 0; i < filePath.length; i++) {
|
|
1290
|
-
const ch = filePath.charCodeAt(i);
|
|
1291
|
-
if (ch === 92 || ch === 47) {
|
|
1292
|
-
plainStart = i + 1;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
return filePath.substring(plainStart);
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
// src/crypto/cipher.ts
|
|
1299
|
-
function decryptBlock(data, key) {
|
|
1300
|
-
const buffer = getStormBuffer();
|
|
1301
|
-
const dwCount = data.length >>> 2;
|
|
1302
|
-
let dwKey1 = key >>> 0;
|
|
1303
|
-
let dwKey2 = 4008636142;
|
|
1304
|
-
for (let i = 0; i < dwCount; i++) {
|
|
1305
|
-
dwKey2 = (dwKey2 + buffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 255)] & 4294967295) >>> 0;
|
|
1306
|
-
const encrypted = data.readUInt32LE(i * 4);
|
|
1307
|
-
const decrypted = (encrypted ^ dwKey1 + dwKey2 >>> 0) >>> 0;
|
|
1308
|
-
data.writeUInt32LE(decrypted, i * 4);
|
|
1309
|
-
dwKey1 = ((~dwKey1 << 21) + 286331153 | dwKey1 >>> 11) >>> 0;
|
|
1310
|
-
dwKey2 = (decrypted + dwKey2 + (dwKey2 << 5) + 3 & 4294967295) >>> 0;
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
function encryptBlock(data, key) {
|
|
1314
|
-
const buffer = getStormBuffer();
|
|
1315
|
-
const dwCount = data.length >>> 2;
|
|
1316
|
-
let dwKey1 = key >>> 0;
|
|
1317
|
-
let dwKey2 = 4008636142;
|
|
1318
|
-
for (let i = 0; i < dwCount; i++) {
|
|
1319
|
-
dwKey2 = (dwKey2 + buffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 255)] & 4294967295) >>> 0;
|
|
1320
|
-
const plaintext = data.readUInt32LE(i * 4);
|
|
1321
|
-
const encrypted = (plaintext ^ dwKey1 + dwKey2 >>> 0) >>> 0;
|
|
1322
|
-
data.writeUInt32LE(encrypted, i * 4);
|
|
1323
|
-
dwKey1 = ((~dwKey1 << 21) + 286331153 | dwKey1 >>> 11) >>> 0;
|
|
1324
|
-
dwKey2 = (plaintext + dwKey2 + (dwKey2 << 5) + 3 & 4294967295) >>> 0;
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
function decryptFileKey(fileName, byteOffset, fileSize, flags) {
|
|
1328
|
-
const plainName = getPlainFileName(fileName);
|
|
1329
|
-
let key = hashString(plainName, MPQ_HASH_FILE_KEY);
|
|
1330
|
-
if (flags & MPQ_FILE_KEY_V2) {
|
|
1331
|
-
key = (key + Number(byteOffset & 0xFFFFFFFFn) ^ fileSize) >>> 0;
|
|
1332
|
-
}
|
|
1333
|
-
return key >>> 0;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
// src/tables/hash-table.ts
|
|
1337
|
-
function loadHashTable(stream, offset, count) {
|
|
1338
|
-
const dataSize = count * 16;
|
|
1339
|
-
const data = stream.read(offset, dataSize);
|
|
1340
|
-
if (data.length < dataSize) {
|
|
1341
|
-
count = Math.floor(data.length / 16);
|
|
1342
|
-
}
|
|
1343
|
-
decryptBlock(data, MPQ_KEY_HASH_TABLE);
|
|
1344
|
-
const entries = new Array(count);
|
|
1345
|
-
for (let i = 0; i < count; i++) {
|
|
1346
|
-
const off = i * 16;
|
|
1347
|
-
entries[i] = {
|
|
1348
|
-
dwName1: data.readUInt32LE(off + 0),
|
|
1349
|
-
dwName2: data.readUInt32LE(off + 4),
|
|
1350
|
-
lcLocale: data.readUInt16LE(off + 8),
|
|
1351
|
-
platform: data[off + 10],
|
|
1352
|
-
dwBlockIndex: data.readUInt32LE(off + 12)
|
|
1353
|
-
};
|
|
1354
|
-
}
|
|
1355
|
-
return entries;
|
|
1356
|
-
}
|
|
1357
|
-
function findHashEntry(hashTable, fileName, locale, maxBlockIndex) {
|
|
1358
|
-
if (hashTable.length === 0) return null;
|
|
1359
|
-
const tableSize = hashTable.length;
|
|
1360
|
-
const mask = tableSize - 1;
|
|
1361
|
-
const startIndex = hashString(fileName, MPQ_HASH_TABLE_INDEX) & mask;
|
|
1362
|
-
const hashCheck1 = hashString(fileName, MPQ_HASH_NAME_A);
|
|
1363
|
-
const hashCheck2 = hashString(fileName, MPQ_HASH_NAME_B);
|
|
1364
|
-
let index = startIndex;
|
|
1365
|
-
for (; ; ) {
|
|
1366
|
-
const entry = hashTable[index];
|
|
1367
|
-
if (entry.dwName1 === hashCheck1 && entry.dwName2 === hashCheck2 && (entry.dwBlockIndex & BLOCK_INDEX_MASK) < maxBlockIndex) {
|
|
1368
|
-
if (locale === 0 || entry.lcLocale === locale || entry.lcLocale === 0) {
|
|
1369
|
-
return entry;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
if (entry.dwBlockIndex === HASH_ENTRY_FREE) {
|
|
1373
|
-
return null;
|
|
1374
|
-
}
|
|
1375
|
-
index = index + 1 & mask;
|
|
1376
|
-
if (index === startIndex) {
|
|
1377
|
-
return null;
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// src/tables/block-table.ts
|
|
1383
|
-
function loadBlockTable(stream, offset, count) {
|
|
1384
|
-
const dataSize = count * 16;
|
|
1385
|
-
const data = stream.read(offset, dataSize);
|
|
1386
|
-
if (data.length < dataSize) {
|
|
1387
|
-
count = Math.floor(data.length / 16);
|
|
1388
|
-
}
|
|
1389
|
-
decryptBlock(data, MPQ_KEY_BLOCK_TABLE);
|
|
1390
|
-
const entries = new Array(count);
|
|
1391
|
-
for (let i = 0; i < count; i++) {
|
|
1392
|
-
const off = i * 16;
|
|
1393
|
-
entries[i] = {
|
|
1394
|
-
dwFilePos: data.readUInt32LE(off + 0),
|
|
1395
|
-
dwCSize: data.readUInt32LE(off + 4),
|
|
1396
|
-
dwFSize: data.readUInt32LE(off + 8),
|
|
1397
|
-
dwFlags: data.readUInt32LE(off + 12)
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return entries;
|
|
1401
|
-
}
|
|
1402
|
-
function loadHiBlockTable(stream, offset, count) {
|
|
1403
|
-
const dataSize = count * 2;
|
|
1404
|
-
const data = stream.read(offset, dataSize);
|
|
1405
|
-
const hiOffsets = new Uint16Array(count);
|
|
1406
|
-
const actualCount = Math.min(count, Math.floor(data.length / 2));
|
|
1407
|
-
for (let i = 0; i < actualCount; i++) {
|
|
1408
|
-
hiOffsets[i] = data.readUInt16LE(i * 2);
|
|
1409
|
-
}
|
|
1410
|
-
return hiOffsets;
|
|
1411
|
-
}
|
|
1412
|
-
|
|
1413
|
-
// src/tables/het-table.ts
|
|
1414
|
-
function readBits(data, bitOffset, numBits) {
|
|
1415
|
-
let result = 0;
|
|
1416
|
-
for (let i = 0; i < numBits; i++) {
|
|
1417
|
-
const byteIdx = bitOffset + i >>> 3;
|
|
1418
|
-
const bitIdx = bitOffset + i & 7;
|
|
1419
|
-
if (byteIdx < data.length && data[byteIdx] & 1 << bitIdx) {
|
|
1420
|
-
result |= 1 << i;
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
return result;
|
|
1424
|
-
}
|
|
1425
|
-
function loadHetTable(stream, offset, compressedSize) {
|
|
1426
|
-
if (offset === 0n) return null;
|
|
1427
|
-
const extHeaderBuf = stream.read(offset, 12);
|
|
1428
|
-
if (extHeaderBuf.length < 12) return null;
|
|
1429
|
-
const signature = extHeaderBuf.readUInt32LE(0);
|
|
1430
|
-
if (signature !== HET_TABLE_SIGNATURE) return null;
|
|
1431
|
-
const version = extHeaderBuf.readUInt32LE(4);
|
|
1432
|
-
const dataSize = extHeaderBuf.readUInt32LE(8);
|
|
1433
|
-
const dataBuf = stream.read(offset + 12n, dataSize);
|
|
1434
|
-
if (dataBuf.length < dataSize) return null;
|
|
1435
|
-
let pos = 0;
|
|
1436
|
-
const hashTableSize = dataBuf.readUInt32LE(pos);
|
|
1437
|
-
pos += 4;
|
|
1438
|
-
const maxFileCount = dataBuf.readUInt32LE(pos);
|
|
1439
|
-
pos += 4;
|
|
1440
|
-
const hetHashTableSize = dataBuf.readUInt32LE(pos);
|
|
1441
|
-
pos += 4;
|
|
1442
|
-
const hashEntrySize = dataBuf.readUInt32LE(pos);
|
|
1443
|
-
pos += 4;
|
|
1444
|
-
const totalIndexSize = dataBuf.readUInt32LE(pos);
|
|
1445
|
-
pos += 4;
|
|
1446
|
-
const indexSizeExtra = dataBuf.readUInt32LE(pos);
|
|
1447
|
-
pos += 4;
|
|
1448
|
-
const indexSize = dataBuf.readUInt32LE(pos);
|
|
1449
|
-
pos += 4;
|
|
1450
|
-
const blockTableSize = dataBuf.readUInt32LE(pos);
|
|
1451
|
-
pos += 4;
|
|
1452
|
-
const hetHashes = new Uint8Array(dataBuf.subarray(pos, pos + hetHashTableSize));
|
|
1453
|
-
pos += hetHashTableSize;
|
|
1454
|
-
const betIndexBits = new Uint8Array(dataBuf.subarray(pos));
|
|
1455
|
-
const betIndices = new Array(hetHashTableSize);
|
|
1456
|
-
for (let i = 0; i < hetHashTableSize; i++) {
|
|
1457
|
-
betIndices[i] = readBits(betIndexBits, i * totalIndexSize, totalIndexSize);
|
|
1458
|
-
}
|
|
1459
|
-
return {
|
|
1460
|
-
hashTableSize: hetHashTableSize,
|
|
1461
|
-
maxFileCount,
|
|
1462
|
-
hashEntrySize,
|
|
1463
|
-
totalIndexSize,
|
|
1464
|
-
indexSizeExtra,
|
|
1465
|
-
indexSize,
|
|
1466
|
-
hetHashes,
|
|
1467
|
-
betIndices
|
|
1468
|
-
};
|
|
1469
|
-
}
|
|
1470
|
-
function findInHetTable(het, fileName) {
|
|
1471
|
-
if (!het || het.hashTableSize === 0) return -1;
|
|
1472
|
-
const fullHash = jenkinsHash(fileName);
|
|
1473
|
-
const hashByte = Number(fullHash >> 56n & 0xFFn) | 128;
|
|
1474
|
-
const startIndex = Number(fullHash % BigInt(het.hashTableSize));
|
|
1475
|
-
let index = startIndex;
|
|
1476
|
-
for (; ; ) {
|
|
1477
|
-
const hetHash = het.hetHashes[index];
|
|
1478
|
-
if (hetHash === HET_ENTRY_FREE) {
|
|
1479
|
-
return -1;
|
|
1480
|
-
}
|
|
1481
|
-
if (hetHash === hashByte) {
|
|
1482
|
-
return het.betIndices[index];
|
|
1483
|
-
}
|
|
1484
|
-
index = (index + 1) % het.hashTableSize;
|
|
1485
|
-
if (index === startIndex) return -1;
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// src/tables/bet-table.ts
|
|
1490
|
-
function readBits2(data, bitOffset, numBits) {
|
|
1491
|
-
let result = 0;
|
|
1492
|
-
for (let i = 0; i < numBits; i++) {
|
|
1493
|
-
const byteIdx = bitOffset + i >>> 3;
|
|
1494
|
-
const bitIdx = bitOffset + i & 7;
|
|
1495
|
-
if (byteIdx < data.length && data[byteIdx] & 1 << bitIdx) {
|
|
1496
|
-
result |= 1 << i;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
return result;
|
|
1500
|
-
}
|
|
1501
|
-
function readBits64(data, bitOffset, numBits) {
|
|
1502
|
-
let result = 0n;
|
|
1503
|
-
for (let i = 0; i < numBits; i++) {
|
|
1504
|
-
const byteIdx = bitOffset + i >>> 3;
|
|
1505
|
-
const bitIdx = bitOffset + i & 7;
|
|
1506
|
-
if (byteIdx < data.length && data[byteIdx] & 1 << bitIdx) {
|
|
1507
|
-
result |= 1n << BigInt(i);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
return result;
|
|
1511
|
-
}
|
|
1512
|
-
function loadBetTable(stream, offset, compressedSize) {
|
|
1513
|
-
if (offset === 0n) return null;
|
|
1514
|
-
const extHeaderBuf = stream.read(offset, 12);
|
|
1515
|
-
if (extHeaderBuf.length < 12) return null;
|
|
1516
|
-
const signature = extHeaderBuf.readUInt32LE(0);
|
|
1517
|
-
if (signature !== BET_TABLE_SIGNATURE) return null;
|
|
1518
|
-
const version = extHeaderBuf.readUInt32LE(4);
|
|
1519
|
-
const dataSize = extHeaderBuf.readUInt32LE(8);
|
|
1520
|
-
const dataBuf = stream.read(offset + 12n, dataSize);
|
|
1521
|
-
if (dataBuf.length < dataSize) return null;
|
|
1522
|
-
let pos = 0;
|
|
1523
|
-
const tableSize = dataBuf.readUInt32LE(pos);
|
|
1524
|
-
pos += 4;
|
|
1525
|
-
const maxFileCount = dataBuf.readUInt32LE(pos);
|
|
1526
|
-
pos += 4;
|
|
1527
|
-
const flagCount = dataBuf.readUInt32LE(pos);
|
|
1528
|
-
pos += 4;
|
|
1529
|
-
const filePosBits = dataBuf.readUInt32LE(pos);
|
|
1530
|
-
pos += 4;
|
|
1531
|
-
const fileSizeBits = dataBuf.readUInt32LE(pos);
|
|
1532
|
-
pos += 4;
|
|
1533
|
-
const cmpSizeBits = dataBuf.readUInt32LE(pos);
|
|
1534
|
-
pos += 4;
|
|
1535
|
-
const flagIndexBits = dataBuf.readUInt32LE(pos);
|
|
1536
|
-
pos += 4;
|
|
1537
|
-
const unknownBits = dataBuf.readUInt32LE(pos);
|
|
1538
|
-
pos += 4;
|
|
1539
|
-
const hashSizeBits = dataBuf.readUInt32LE(pos);
|
|
1540
|
-
pos += 4;
|
|
1541
|
-
const hashSizeTotal = dataBuf.readUInt32LE(pos);
|
|
1542
|
-
pos += 4;
|
|
1543
|
-
const hashSizeExtra = dataBuf.readUInt32LE(pos);
|
|
1544
|
-
pos += 4;
|
|
1545
|
-
const hashArraySize = dataBuf.readUInt32LE(pos);
|
|
1546
|
-
pos += 4;
|
|
1547
|
-
const flags = [];
|
|
1548
|
-
for (let i = 0; i < flagCount; i++) {
|
|
1549
|
-
flags.push(dataBuf.readUInt32LE(pos));
|
|
1550
|
-
pos += 4;
|
|
1551
|
-
}
|
|
1552
|
-
const bitsPerEntry = filePosBits + fileSizeBits + cmpSizeBits + flagIndexBits + unknownBits;
|
|
1553
|
-
const entryData = new Uint8Array(dataBuf.subarray(pos));
|
|
1554
|
-
const entries = [];
|
|
1555
|
-
for (let i = 0; i < maxFileCount; i++) {
|
|
1556
|
-
let bitOff = i * bitsPerEntry;
|
|
1557
|
-
const byteOffset = readBits64(entryData, bitOff, filePosBits);
|
|
1558
|
-
bitOff += filePosBits;
|
|
1559
|
-
const fileSize = readBits2(entryData, bitOff, fileSizeBits);
|
|
1560
|
-
bitOff += fileSizeBits;
|
|
1561
|
-
const cmpSize = readBits2(entryData, bitOff, cmpSizeBits);
|
|
1562
|
-
bitOff += cmpSizeBits;
|
|
1563
|
-
const flagIndex = readBits2(entryData, bitOff, flagIndexBits);
|
|
1564
|
-
bitOff += flagIndexBits;
|
|
1565
|
-
const hashBitOffset = maxFileCount * bitsPerEntry + i * hashSizeTotal;
|
|
1566
|
-
const fileNameHash = readBits64(entryData, hashBitOffset, hashSizeTotal);
|
|
1567
|
-
entries.push({
|
|
1568
|
-
fileNameHash,
|
|
1569
|
-
byteOffset,
|
|
1570
|
-
fileSize,
|
|
1571
|
-
cmpSize,
|
|
1572
|
-
flagIndex
|
|
1573
|
-
});
|
|
1574
|
-
}
|
|
1575
|
-
return { maxFileCount, flagCount, flags, entries };
|
|
1576
|
-
}
|
|
1577
|
-
function getBetEntry(bet, index) {
|
|
1578
|
-
if (index < 0 || index >= bet.entries.length) return null;
|
|
1579
|
-
const entry = bet.entries[index];
|
|
1580
|
-
const flags = entry.flagIndex < bet.flags.length ? bet.flags[entry.flagIndex] : 0;
|
|
1581
|
-
return {
|
|
1582
|
-
fileNameHash: entry.fileNameHash,
|
|
1583
|
-
byteOffset: entry.byteOffset,
|
|
1584
|
-
fileTime: 0n,
|
|
1585
|
-
fileSize: entry.fileSize,
|
|
1586
|
-
cmpSize: entry.cmpSize,
|
|
1587
|
-
flags,
|
|
1588
|
-
crc32: 0,
|
|
1589
|
-
md5: new Uint8Array(16),
|
|
1590
|
-
fileName: ""
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
// src/tables/file-table.ts
|
|
1595
|
-
function buildFileTable(hashTable, blockTable, hiBlockTable) {
|
|
1596
|
-
const fileCount = blockTable.length;
|
|
1597
|
-
const entries = new Array(fileCount);
|
|
1598
|
-
for (let i = 0; i < fileCount; i++) {
|
|
1599
|
-
const block = blockTable[i];
|
|
1600
|
-
let byteOffset = BigInt(block.dwFilePos);
|
|
1601
|
-
if (hiBlockTable && i < hiBlockTable.length) {
|
|
1602
|
-
byteOffset |= BigInt(hiBlockTable[i]) << 32n;
|
|
1603
|
-
}
|
|
1604
|
-
entries[i] = {
|
|
1605
|
-
fileNameHash: 0n,
|
|
1606
|
-
byteOffset,
|
|
1607
|
-
fileTime: 0n,
|
|
1608
|
-
fileSize: block.dwFSize,
|
|
1609
|
-
cmpSize: block.dwCSize,
|
|
1610
|
-
flags: block.dwFlags,
|
|
1611
|
-
crc32: 0,
|
|
1612
|
-
md5: new Uint8Array(16),
|
|
1613
|
-
fileName: ""
|
|
1614
|
-
};
|
|
1615
|
-
}
|
|
1616
|
-
return entries;
|
|
1617
|
-
}
|
|
1618
|
-
function buildFileTableFromHetBet(het, bet) {
|
|
1619
|
-
const entries = [];
|
|
1620
|
-
for (let i = 0; i < bet.entries.length; i++) {
|
|
1621
|
-
const entry = getBetEntry(bet, i);
|
|
1622
|
-
if (entry) {
|
|
1623
|
-
entries.push(entry);
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
return entries;
|
|
1627
|
-
}
|
|
1628
|
-
function applyFileNames(fileEntries, hashTable, names) {
|
|
1629
|
-
const tableSize = hashTable.length;
|
|
1630
|
-
if (tableSize === 0) return;
|
|
1631
|
-
const mask = tableSize - 1;
|
|
1632
|
-
for (const name of names) {
|
|
1633
|
-
if (!name) continue;
|
|
1634
|
-
const startIndex = hashString(name, MPQ_HASH_TABLE_INDEX) & mask;
|
|
1635
|
-
const check1 = hashString(name, MPQ_HASH_NAME_A);
|
|
1636
|
-
const check2 = hashString(name, MPQ_HASH_NAME_B);
|
|
1637
|
-
let index = startIndex;
|
|
1638
|
-
for (; ; ) {
|
|
1639
|
-
const entry = hashTable[index];
|
|
1640
|
-
if (entry.dwName1 === check1 && entry.dwName2 === check2 && (entry.dwBlockIndex & BLOCK_INDEX_MASK) < fileEntries.length) {
|
|
1641
|
-
const blockIndex = entry.dwBlockIndex & BLOCK_INDEX_MASK;
|
|
1642
|
-
if (!fileEntries[blockIndex].fileName) {
|
|
1643
|
-
fileEntries[blockIndex].fileName = name;
|
|
1644
|
-
}
|
|
1645
|
-
break;
|
|
1646
|
-
}
|
|
1647
|
-
if (entry.dwBlockIndex === HASH_ENTRY_FREE) break;
|
|
1648
|
-
index = index + 1 & mask;
|
|
1649
|
-
if (index === startIndex) break;
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
// src/archive/listfile.ts
|
|
1655
|
-
function parseListfile(data) {
|
|
1656
|
-
const text = data.toString("utf8");
|
|
1657
|
-
const names = [];
|
|
1658
|
-
for (const line of text.split(/\r?\n/)) {
|
|
1659
|
-
const trimmed = line.trim();
|
|
1660
|
-
if (trimmed.length > 0) {
|
|
1661
|
-
names.push(trimmed);
|
|
1662
|
-
}
|
|
1663
|
-
}
|
|
1664
|
-
return names;
|
|
1665
|
-
}
|
|
1666
|
-
function parseAttributes(data, fileCount) {
|
|
1667
|
-
if (data.length < 8) return null;
|
|
1668
|
-
const version = data.readUInt32LE(0);
|
|
1669
|
-
const flags = data.readUInt32LE(4);
|
|
1670
|
-
if (version !== 100) return null;
|
|
1671
|
-
const result = {
|
|
1672
|
-
crc32s: [],
|
|
1673
|
-
fileTimes: [],
|
|
1674
|
-
md5s: []
|
|
1675
|
-
};
|
|
1676
|
-
let offset = 8;
|
|
1677
|
-
if (flags & 1) {
|
|
1678
|
-
for (let i = 0; i < fileCount && offset + 4 <= data.length; i++) {
|
|
1679
|
-
result.crc32s.push(data.readUInt32LE(offset));
|
|
1680
|
-
offset += 4;
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
if (flags & 2) {
|
|
1684
|
-
for (let i = 0; i < fileCount && offset + 8 <= data.length; i++) {
|
|
1685
|
-
result.fileTimes.push(data.readBigUInt64LE(offset));
|
|
1686
|
-
offset += 8;
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
if (flags & 4) {
|
|
1690
|
-
for (let i = 0; i < fileCount && offset + 16 <= data.length; i++) {
|
|
1691
|
-
result.md5s.push(new Uint8Array(data.subarray(offset, offset + 16)));
|
|
1692
|
-
offset += 16;
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
return result;
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/compression/huffman.ts
|
|
1699
|
-
var WEIGHT_TABLES = {
|
|
1700
|
-
// Generic text compression (type 0)
|
|
1701
|
-
0: [
|
|
1702
|
-
2571,
|
|
1703
|
-
2568,
|
|
1704
|
-
2567,
|
|
1705
|
-
2566,
|
|
1706
|
-
2570,
|
|
1707
|
-
2569,
|
|
1708
|
-
2565,
|
|
1709
|
-
2564,
|
|
1710
|
-
2563,
|
|
1711
|
-
2562,
|
|
1712
|
-
2561,
|
|
1713
|
-
2560,
|
|
1714
|
-
2319,
|
|
1715
|
-
2318,
|
|
1716
|
-
2317,
|
|
1717
|
-
2316,
|
|
1718
|
-
2315,
|
|
1719
|
-
2314,
|
|
1720
|
-
2313,
|
|
1721
|
-
2312,
|
|
1722
|
-
2311,
|
|
1723
|
-
2310,
|
|
1724
|
-
2309,
|
|
1725
|
-
2308,
|
|
1726
|
-
2307,
|
|
1727
|
-
2306,
|
|
1728
|
-
2305,
|
|
1729
|
-
2304,
|
|
1730
|
-
2063,
|
|
1731
|
-
2062,
|
|
1732
|
-
2061,
|
|
1733
|
-
2060,
|
|
1734
|
-
2059,
|
|
1735
|
-
2058,
|
|
1736
|
-
2057,
|
|
1737
|
-
2056,
|
|
1738
|
-
2055,
|
|
1739
|
-
2054,
|
|
1740
|
-
2053,
|
|
1741
|
-
2052,
|
|
1742
|
-
2051,
|
|
1743
|
-
2050,
|
|
1744
|
-
2049,
|
|
1745
|
-
2048,
|
|
1746
|
-
1807,
|
|
1747
|
-
1806,
|
|
1748
|
-
1805,
|
|
1749
|
-
1804,
|
|
1750
|
-
1803,
|
|
1751
|
-
1802,
|
|
1752
|
-
1801,
|
|
1753
|
-
1800,
|
|
1754
|
-
1799,
|
|
1755
|
-
1798,
|
|
1756
|
-
1797,
|
|
1757
|
-
1796,
|
|
1758
|
-
1795,
|
|
1759
|
-
1794,
|
|
1760
|
-
1793,
|
|
1761
|
-
1792,
|
|
1762
|
-
1551,
|
|
1763
|
-
1550,
|
|
1764
|
-
1549,
|
|
1765
|
-
1548,
|
|
1766
|
-
1547,
|
|
1767
|
-
1546,
|
|
1768
|
-
1545,
|
|
1769
|
-
1544,
|
|
1770
|
-
1543,
|
|
1771
|
-
1542,
|
|
1772
|
-
1541,
|
|
1773
|
-
1540,
|
|
1774
|
-
1539,
|
|
1775
|
-
1538,
|
|
1776
|
-
1537,
|
|
1777
|
-
1536,
|
|
1778
|
-
1295,
|
|
1779
|
-
1294,
|
|
1780
|
-
1293,
|
|
1781
|
-
1292,
|
|
1782
|
-
1291,
|
|
1783
|
-
1290,
|
|
1784
|
-
1289,
|
|
1785
|
-
1288,
|
|
1786
|
-
1287,
|
|
1787
|
-
1286,
|
|
1788
|
-
1285,
|
|
1789
|
-
1284,
|
|
1790
|
-
1283,
|
|
1791
|
-
1282,
|
|
1792
|
-
1281,
|
|
1793
|
-
1280,
|
|
1794
|
-
1039,
|
|
1795
|
-
1038,
|
|
1796
|
-
1037,
|
|
1797
|
-
1036,
|
|
1798
|
-
1035,
|
|
1799
|
-
1034,
|
|
1800
|
-
1033,
|
|
1801
|
-
1032,
|
|
1802
|
-
1031,
|
|
1803
|
-
1030,
|
|
1804
|
-
1029,
|
|
1805
|
-
1028,
|
|
1806
|
-
1027,
|
|
1807
|
-
1026,
|
|
1808
|
-
1025,
|
|
1809
|
-
1024,
|
|
1810
|
-
783,
|
|
1811
|
-
782,
|
|
1812
|
-
781,
|
|
1813
|
-
780,
|
|
1814
|
-
779,
|
|
1815
|
-
778,
|
|
1816
|
-
777,
|
|
1817
|
-
776,
|
|
1818
|
-
775,
|
|
1819
|
-
774,
|
|
1820
|
-
773,
|
|
1821
|
-
772,
|
|
1822
|
-
771,
|
|
1823
|
-
770,
|
|
1824
|
-
769,
|
|
1825
|
-
768,
|
|
1826
|
-
527,
|
|
1827
|
-
526,
|
|
1828
|
-
525,
|
|
1829
|
-
524,
|
|
1830
|
-
523,
|
|
1831
|
-
522,
|
|
1832
|
-
521,
|
|
1833
|
-
520,
|
|
1834
|
-
519,
|
|
1835
|
-
518,
|
|
1836
|
-
517,
|
|
1837
|
-
516,
|
|
1838
|
-
515,
|
|
1839
|
-
514,
|
|
1840
|
-
513,
|
|
1841
|
-
512,
|
|
1842
|
-
271,
|
|
1843
|
-
270,
|
|
1844
|
-
269,
|
|
1845
|
-
268,
|
|
1846
|
-
267,
|
|
1847
|
-
266,
|
|
1848
|
-
265,
|
|
1849
|
-
264,
|
|
1850
|
-
263,
|
|
1851
|
-
262,
|
|
1852
|
-
261,
|
|
1853
|
-
260,
|
|
1854
|
-
259,
|
|
1855
|
-
258,
|
|
1856
|
-
257,
|
|
1857
|
-
256,
|
|
1858
|
-
15,
|
|
1859
|
-
14,
|
|
1860
|
-
13,
|
|
1861
|
-
12,
|
|
1862
|
-
11,
|
|
1863
|
-
10,
|
|
1864
|
-
9,
|
|
1865
|
-
8,
|
|
1866
|
-
7,
|
|
1867
|
-
6,
|
|
1868
|
-
5,
|
|
1869
|
-
4,
|
|
1870
|
-
3,
|
|
1871
|
-
2,
|
|
1872
|
-
1,
|
|
1873
|
-
0,
|
|
1874
|
-
0,
|
|
1875
|
-
0,
|
|
1876
|
-
0,
|
|
1877
|
-
0,
|
|
1878
|
-
0,
|
|
1879
|
-
0,
|
|
1880
|
-
0,
|
|
1881
|
-
0,
|
|
1882
|
-
0,
|
|
1883
|
-
0,
|
|
1884
|
-
0,
|
|
1885
|
-
0,
|
|
1886
|
-
0,
|
|
1887
|
-
0,
|
|
1888
|
-
0,
|
|
1889
|
-
0,
|
|
1890
|
-
0,
|
|
1891
|
-
0,
|
|
1892
|
-
0,
|
|
1893
|
-
0,
|
|
1894
|
-
0,
|
|
1895
|
-
0,
|
|
1896
|
-
0,
|
|
1897
|
-
0,
|
|
1898
|
-
0,
|
|
1899
|
-
0,
|
|
1900
|
-
0,
|
|
1901
|
-
0,
|
|
1902
|
-
0,
|
|
1903
|
-
0,
|
|
1904
|
-
0,
|
|
1905
|
-
0,
|
|
1906
|
-
0,
|
|
1907
|
-
0,
|
|
1908
|
-
0,
|
|
1909
|
-
0,
|
|
1910
|
-
0,
|
|
1911
|
-
0,
|
|
1912
|
-
0,
|
|
1913
|
-
0,
|
|
1914
|
-
0,
|
|
1915
|
-
0,
|
|
1916
|
-
0,
|
|
1917
|
-
0,
|
|
1918
|
-
0,
|
|
1919
|
-
0,
|
|
1920
|
-
0,
|
|
1921
|
-
0,
|
|
1922
|
-
0,
|
|
1923
|
-
0,
|
|
1924
|
-
0,
|
|
1925
|
-
0,
|
|
1926
|
-
0,
|
|
1927
|
-
0,
|
|
1928
|
-
0,
|
|
1929
|
-
0,
|
|
1930
|
-
0,
|
|
1931
|
-
0,
|
|
1932
|
-
0,
|
|
1933
|
-
0,
|
|
1934
|
-
0,
|
|
1935
|
-
0,
|
|
1936
|
-
0,
|
|
1937
|
-
0,
|
|
1938
|
-
0,
|
|
1939
|
-
0,
|
|
1940
|
-
0,
|
|
1941
|
-
0,
|
|
1942
|
-
0,
|
|
1943
|
-
0,
|
|
1944
|
-
0,
|
|
1945
|
-
0,
|
|
1946
|
-
0,
|
|
1947
|
-
0,
|
|
1948
|
-
0,
|
|
1949
|
-
0,
|
|
1950
|
-
0,
|
|
1951
|
-
0,
|
|
1952
|
-
0,
|
|
1953
|
-
0,
|
|
1954
|
-
0,
|
|
1955
|
-
0,
|
|
1956
|
-
0,
|
|
1957
|
-
2
|
|
1958
|
-
]
|
|
1959
|
-
};
|
|
1960
|
-
var BitReader = class {
|
|
1961
|
-
constructor(data) {
|
|
1962
|
-
this.bitPos = 0;
|
|
1963
|
-
this.data = data;
|
|
1964
|
-
}
|
|
1965
|
-
readBits(numBits) {
|
|
1966
|
-
let result = 0;
|
|
1967
|
-
for (let i = 0; i < numBits; i++) {
|
|
1968
|
-
const byteIndex = this.bitPos >>> 3;
|
|
1969
|
-
const bitIndex = this.bitPos & 7;
|
|
1970
|
-
if (byteIndex >= this.data.length) return result;
|
|
1971
|
-
if (this.data[byteIndex] & 1 << bitIndex) {
|
|
1972
|
-
result |= 1 << i;
|
|
1973
|
-
}
|
|
1974
|
-
this.bitPos++;
|
|
1975
|
-
}
|
|
1976
|
-
return result;
|
|
1977
|
-
}
|
|
1978
|
-
readBit() {
|
|
1979
|
-
const byteIndex = this.bitPos >>> 3;
|
|
1980
|
-
const bitIndex = this.bitPos & 7;
|
|
1981
|
-
if (byteIndex >= this.data.length) return 0;
|
|
1982
|
-
const bit = this.data[byteIndex] >>> bitIndex & 1;
|
|
1983
|
-
this.bitPos++;
|
|
1984
|
-
return bit;
|
|
1985
|
-
}
|
|
1986
|
-
get isEof() {
|
|
1987
|
-
return this.bitPos >>> 3 >= this.data.length;
|
|
1988
|
-
}
|
|
1989
|
-
};
|
|
1990
|
-
var HuffmanTree = class {
|
|
1991
|
-
constructor(compressionType) {
|
|
1992
|
-
this.nodes = [];
|
|
1993
|
-
this.root = -1;
|
|
1994
|
-
this.items256 = new Array(256).fill(-1);
|
|
1995
|
-
this.buildTree(compressionType);
|
|
1996
|
-
}
|
|
1997
|
-
buildTree(compressionType) {
|
|
1998
|
-
const weights = WEIGHT_TABLES[compressionType] || WEIGHT_TABLES[0];
|
|
1999
|
-
for (let i = 0; i < 257; i++) {
|
|
2000
|
-
this.nodes.push({
|
|
2001
|
-
weight: i < 256 ? weights ? weights[i] || 0 : 0 : 0,
|
|
2002
|
-
child0: -1,
|
|
2003
|
-
child1: -1,
|
|
2004
|
-
parent: -1,
|
|
2005
|
-
value: i
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
|
-
const leafIndices = Array.from({ length: 257 }, (_, i) => i).filter((i) => this.nodes[i].weight > 0 || i === 256);
|
|
2009
|
-
leafIndices.sort((a, b) => this.nodes[a].weight - this.nodes[b].weight);
|
|
2010
|
-
const active = [...leafIndices];
|
|
2011
|
-
while (active.length > 1) {
|
|
2012
|
-
const idx0 = active.shift();
|
|
2013
|
-
const idx1 = active.shift();
|
|
2014
|
-
const parentIdx = this.nodes.length;
|
|
2015
|
-
this.nodes.push({
|
|
2016
|
-
weight: this.nodes[idx0].weight + this.nodes[idx1].weight,
|
|
2017
|
-
child0: idx0,
|
|
2018
|
-
child1: idx1,
|
|
2019
|
-
parent: -1,
|
|
2020
|
-
value: -1
|
|
2021
|
-
});
|
|
2022
|
-
this.nodes[idx0].parent = parentIdx;
|
|
2023
|
-
this.nodes[idx1].parent = parentIdx;
|
|
2024
|
-
let insertAt = active.length;
|
|
2025
|
-
for (let j = 0; j < active.length; j++) {
|
|
2026
|
-
if (this.nodes[parentIdx].weight <= this.nodes[active[j]].weight) {
|
|
2027
|
-
insertAt = j;
|
|
2028
|
-
break;
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
active.splice(insertAt, 0, parentIdx);
|
|
2032
|
-
}
|
|
2033
|
-
if (active.length > 0) {
|
|
2034
|
-
this.root = active[0];
|
|
2035
|
-
}
|
|
2036
|
-
for (let i = 0; i < 256; i++) {
|
|
2037
|
-
if (this.nodes[i].weight > 0) {
|
|
2038
|
-
this.items256[i] = i;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
decodeOne(reader) {
|
|
2043
|
-
if (this.root === -1) return -1;
|
|
2044
|
-
let current = this.root;
|
|
2045
|
-
while (this.nodes[current].value === -1) {
|
|
2046
|
-
const bit = reader.readBit();
|
|
2047
|
-
if (bit === 0) {
|
|
2048
|
-
current = this.nodes[current].child0;
|
|
2049
|
-
} else {
|
|
2050
|
-
current = this.nodes[current].child1;
|
|
2051
|
-
}
|
|
2052
|
-
if (current === -1) return -1;
|
|
2053
|
-
}
|
|
2054
|
-
return this.nodes[current].value;
|
|
2055
|
-
}
|
|
2056
|
-
};
|
|
2057
|
-
function decompressHuffman(inBuffer, outSize) {
|
|
2058
|
-
if (inBuffer.length === 0) {
|
|
2059
|
-
return Buffer.alloc(outSize);
|
|
2060
|
-
}
|
|
2061
|
-
const compressionType = inBuffer[0];
|
|
2062
|
-
const reader = new BitReader(inBuffer.subarray(1));
|
|
2063
|
-
const tree = new HuffmanTree(compressionType);
|
|
2064
|
-
const output = Buffer.alloc(outSize);
|
|
2065
|
-
let outPos = 0;
|
|
2066
|
-
while (outPos < outSize && !reader.isEof) {
|
|
2067
|
-
const value = tree.decodeOne(reader);
|
|
2068
|
-
if (value < 0 || value === 256) break;
|
|
2069
|
-
output[outPos++] = value & 255;
|
|
2070
|
-
}
|
|
2071
|
-
return output;
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
// src/compression/index.ts
|
|
2075
|
-
init_pkware();
|
|
2076
|
-
|
|
2077
|
-
// src/compression/adpcm.ts
|
|
2078
|
-
var STEP_TABLE = [
|
|
2079
|
-
7,
|
|
2080
|
-
8,
|
|
2081
|
-
9,
|
|
2082
|
-
10,
|
|
2083
|
-
11,
|
|
2084
|
-
12,
|
|
2085
|
-
13,
|
|
2086
|
-
14,
|
|
2087
|
-
16,
|
|
2088
|
-
17,
|
|
2089
|
-
19,
|
|
2090
|
-
21,
|
|
2091
|
-
23,
|
|
2092
|
-
25,
|
|
2093
|
-
28,
|
|
2094
|
-
31,
|
|
2095
|
-
34,
|
|
2096
|
-
37,
|
|
2097
|
-
41,
|
|
2098
|
-
45,
|
|
2099
|
-
50,
|
|
2100
|
-
55,
|
|
2101
|
-
60,
|
|
2102
|
-
66,
|
|
2103
|
-
73,
|
|
2104
|
-
80,
|
|
2105
|
-
88,
|
|
2106
|
-
97,
|
|
2107
|
-
107,
|
|
2108
|
-
118,
|
|
2109
|
-
130,
|
|
2110
|
-
143,
|
|
2111
|
-
157,
|
|
2112
|
-
173,
|
|
2113
|
-
190,
|
|
2114
|
-
209,
|
|
2115
|
-
230,
|
|
2116
|
-
253,
|
|
2117
|
-
279,
|
|
2118
|
-
307,
|
|
2119
|
-
337,
|
|
2120
|
-
371,
|
|
2121
|
-
408,
|
|
2122
|
-
449,
|
|
2123
|
-
494,
|
|
2124
|
-
544,
|
|
2125
|
-
598,
|
|
2126
|
-
658,
|
|
2127
|
-
724,
|
|
2128
|
-
796,
|
|
2129
|
-
876,
|
|
2130
|
-
963,
|
|
2131
|
-
1060,
|
|
2132
|
-
1166,
|
|
2133
|
-
1282,
|
|
2134
|
-
1411,
|
|
2135
|
-
1552,
|
|
2136
|
-
1707,
|
|
2137
|
-
1878,
|
|
2138
|
-
2066,
|
|
2139
|
-
2272,
|
|
2140
|
-
2499,
|
|
2141
|
-
2749,
|
|
2142
|
-
3024,
|
|
2143
|
-
3327,
|
|
2144
|
-
3660,
|
|
2145
|
-
4026,
|
|
2146
|
-
4428,
|
|
2147
|
-
4871,
|
|
2148
|
-
5358,
|
|
2149
|
-
5894,
|
|
2150
|
-
6484,
|
|
2151
|
-
7132,
|
|
2152
|
-
7845,
|
|
2153
|
-
8630,
|
|
2154
|
-
9493,
|
|
2155
|
-
10442,
|
|
2156
|
-
11487,
|
|
2157
|
-
12635,
|
|
2158
|
-
13899,
|
|
2159
|
-
15289,
|
|
2160
|
-
16818,
|
|
2161
|
-
18500,
|
|
2162
|
-
20350,
|
|
2163
|
-
22385,
|
|
2164
|
-
24623,
|
|
2165
|
-
27086,
|
|
2166
|
-
29794,
|
|
2167
|
-
32767
|
|
2168
|
-
];
|
|
2169
|
-
var INDEX_TABLE = [
|
|
2170
|
-
-1,
|
|
2171
|
-
0,
|
|
2172
|
-
-1,
|
|
2173
|
-
4,
|
|
2174
|
-
-1,
|
|
2175
|
-
2,
|
|
2176
|
-
-1,
|
|
2177
|
-
6,
|
|
2178
|
-
-1,
|
|
2179
|
-
1,
|
|
2180
|
-
-1,
|
|
2181
|
-
5,
|
|
2182
|
-
-1,
|
|
2183
|
-
3,
|
|
2184
|
-
-1,
|
|
2185
|
-
7,
|
|
2186
|
-
-1,
|
|
2187
|
-
1,
|
|
2188
|
-
-1,
|
|
2189
|
-
5,
|
|
2190
|
-
-1,
|
|
2191
|
-
3,
|
|
2192
|
-
-1,
|
|
2193
|
-
7,
|
|
2194
|
-
-1,
|
|
2195
|
-
2,
|
|
2196
|
-
-1,
|
|
2197
|
-
4,
|
|
2198
|
-
-1,
|
|
2199
|
-
6,
|
|
2200
|
-
-1,
|
|
2201
|
-
8
|
|
2202
|
-
];
|
|
2203
|
-
function clampStepIndex(index) {
|
|
2204
|
-
if (index < 0) return 0;
|
|
2205
|
-
if (index > 88) return 88;
|
|
2206
|
-
return index;
|
|
2207
|
-
}
|
|
2208
|
-
function clampSample(sample) {
|
|
2209
|
-
if (sample > 32767) return 32767;
|
|
2210
|
-
if (sample < -32768) return -32768;
|
|
2211
|
-
return sample;
|
|
2212
|
-
}
|
|
2213
|
-
function decodeSample(state, code) {
|
|
2214
|
-
const step = STEP_TABLE[state.stepIndex];
|
|
2215
|
-
let diff = step >>> 3;
|
|
2216
|
-
if (code & 4) diff += step;
|
|
2217
|
-
if (code & 2) diff += step >>> 1;
|
|
2218
|
-
if (code & 1) diff += step >>> 2;
|
|
2219
|
-
if (code & 8) {
|
|
2220
|
-
state.predSample = clampSample(state.predSample - diff);
|
|
2221
|
-
} else {
|
|
2222
|
-
state.predSample = clampSample(state.predSample + diff);
|
|
2223
|
-
}
|
|
2224
|
-
state.stepIndex = clampStepIndex(state.stepIndex + INDEX_TABLE[code & 31]);
|
|
2225
|
-
return state.predSample;
|
|
2226
|
-
}
|
|
2227
|
-
function decompressAdpcmMono(inBuffer, outSize) {
|
|
2228
|
-
if (inBuffer.length < 4) {
|
|
2229
|
-
return Buffer.alloc(outSize);
|
|
2230
|
-
}
|
|
2231
|
-
const output = Buffer.alloc(outSize);
|
|
2232
|
-
let inPos = 0;
|
|
2233
|
-
let outPos = 0;
|
|
2234
|
-
const shift = inBuffer[inPos++];
|
|
2235
|
-
const initialSample = inBuffer.readInt16LE(inPos);
|
|
2236
|
-
inPos += 2;
|
|
2237
|
-
const state = {
|
|
2238
|
-
predSample: initialSample,
|
|
2239
|
-
stepIndex: STEP_TABLE.indexOf(Math.abs(initialSample)) || 0
|
|
2240
|
-
};
|
|
2241
|
-
if (outPos + 2 <= outSize) {
|
|
2242
|
-
output.writeInt16LE(state.predSample, outPos);
|
|
2243
|
-
outPos += 2;
|
|
2244
|
-
}
|
|
2245
|
-
while (inPos < inBuffer.length && outPos + 2 <= outSize) {
|
|
2246
|
-
const byte = inBuffer[inPos++];
|
|
2247
|
-
const sample1 = decodeSample(state, byte & 15);
|
|
2248
|
-
if (outPos + 2 <= outSize) {
|
|
2249
|
-
output.writeInt16LE(sample1, outPos);
|
|
2250
|
-
outPos += 2;
|
|
2251
|
-
}
|
|
2252
|
-
if (outPos + 2 <= outSize) {
|
|
2253
|
-
const sample2 = decodeSample(state, byte >>> 4 & 15);
|
|
2254
|
-
output.writeInt16LE(sample2, outPos);
|
|
2255
|
-
outPos += 2;
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
return output;
|
|
2259
|
-
}
|
|
2260
|
-
function decompressAdpcmStereo(inBuffer, outSize) {
|
|
2261
|
-
if (inBuffer.length < 8) {
|
|
2262
|
-
return Buffer.alloc(outSize);
|
|
2263
|
-
}
|
|
2264
|
-
const output = Buffer.alloc(outSize);
|
|
2265
|
-
let inPos = 0;
|
|
2266
|
-
let outPos = 0;
|
|
2267
|
-
const shift = inBuffer[inPos++];
|
|
2268
|
-
const initialLeft = inBuffer.readInt16LE(inPos);
|
|
2269
|
-
inPos += 2;
|
|
2270
|
-
const initialRight = inBuffer.readInt16LE(inPos);
|
|
2271
|
-
inPos += 2;
|
|
2272
|
-
const stateL = {
|
|
2273
|
-
predSample: initialLeft,
|
|
2274
|
-
stepIndex: 0
|
|
2275
|
-
};
|
|
2276
|
-
const stateR = {
|
|
2277
|
-
predSample: initialRight,
|
|
2278
|
-
stepIndex: 0
|
|
2279
|
-
};
|
|
2280
|
-
if (outPos + 4 <= outSize) {
|
|
2281
|
-
output.writeInt16LE(stateL.predSample, outPos);
|
|
2282
|
-
outPos += 2;
|
|
2283
|
-
output.writeInt16LE(stateR.predSample, outPos);
|
|
2284
|
-
outPos += 2;
|
|
2285
|
-
}
|
|
2286
|
-
let channel = 0;
|
|
2287
|
-
while (inPos < inBuffer.length && outPos + 2 <= outSize) {
|
|
2288
|
-
const byte = inBuffer[inPos++];
|
|
2289
|
-
const state = channel === 0 ? stateL : stateR;
|
|
2290
|
-
const sample1 = decodeSample(state, byte & 15);
|
|
2291
|
-
if (outPos + 2 <= outSize) {
|
|
2292
|
-
output.writeInt16LE(sample1, outPos);
|
|
2293
|
-
outPos += 2;
|
|
2294
|
-
}
|
|
2295
|
-
channel ^= 1;
|
|
2296
|
-
if (outPos + 2 <= outSize) {
|
|
2297
|
-
const state2 = channel === 0 ? stateL : stateR;
|
|
2298
|
-
const sample2 = decodeSample(state2, byte >>> 4 & 15);
|
|
2299
|
-
output.writeInt16LE(sample2, outPos);
|
|
2300
|
-
outPos += 2;
|
|
2301
|
-
}
|
|
2302
|
-
channel ^= 1;
|
|
2303
|
-
}
|
|
2304
|
-
return output;
|
|
2305
|
-
}
|
|
2306
|
-
|
|
2307
|
-
// src/compression/sparse.ts
|
|
2308
|
-
function decompressSparse(inBuffer, outSize) {
|
|
2309
|
-
const output = Buffer.alloc(outSize);
|
|
2310
|
-
let inPos = 0;
|
|
2311
|
-
let outPos = 0;
|
|
2312
|
-
while (inPos < inBuffer.length && outPos < outSize) {
|
|
2313
|
-
const byte = inBuffer[inPos++];
|
|
2314
|
-
if (byte !== 0) {
|
|
2315
|
-
output[outPos++] = byte;
|
|
2316
|
-
} else {
|
|
2317
|
-
if (inPos >= inBuffer.length) break;
|
|
2318
|
-
const count = inBuffer[inPos++];
|
|
2319
|
-
if (count === 0) {
|
|
2320
|
-
output[outPos++] = 0;
|
|
2321
|
-
} else {
|
|
2322
|
-
const end = Math.min(outPos + count, outSize);
|
|
2323
|
-
outPos = end;
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
return output;
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
// src/compression/index.ts
|
|
2331
|
-
import pako from "pako";
|
|
2332
|
-
function decompressZlib(inBuffer, outSize) {
|
|
2333
|
-
const result = pako.inflate(inBuffer);
|
|
2334
|
-
return Buffer.from(result.buffer, result.byteOffset, result.byteLength);
|
|
2335
|
-
}
|
|
2336
|
-
function decompressBzip2(_inBuffer, _outSize) {
|
|
2337
|
-
throw new MpqUnsupportedError("BZIP2 decompression is not supported. Install a bzip2 package if needed.");
|
|
2338
|
-
}
|
|
2339
|
-
function decompressLzma(_inBuffer, _outSize) {
|
|
2340
|
-
throw new MpqUnsupportedError("LZMA decompression is not supported. Install an LZMA package if needed.");
|
|
2341
|
-
}
|
|
2342
|
-
var DECOMPRESS_TABLE = [
|
|
2343
|
-
{ mask: MPQ_COMPRESSION_BZIP2, decompress: decompressBzip2 },
|
|
2344
|
-
{ mask: MPQ_COMPRESSION_PKWARE, decompress: decompressPkware },
|
|
2345
|
-
{ mask: MPQ_COMPRESSION_ZLIB, decompress: decompressZlib },
|
|
2346
|
-
{ mask: MPQ_COMPRESSION_HUFFMANN, decompress: decompressHuffman },
|
|
2347
|
-
{ mask: MPQ_COMPRESSION_ADPCM_STEREO, decompress: decompressAdpcmStereo },
|
|
2348
|
-
{ mask: MPQ_COMPRESSION_ADPCM_MONO, decompress: decompressAdpcmMono },
|
|
2349
|
-
{ mask: MPQ_COMPRESSION_SPARSE, decompress: decompressSparse }
|
|
2350
|
-
];
|
|
2351
|
-
function decompress(inBuffer, outSize) {
|
|
2352
|
-
if (inBuffer.length === outSize) {
|
|
2353
|
-
return Buffer.from(inBuffer);
|
|
2354
|
-
}
|
|
2355
|
-
if (inBuffer.length === 0) {
|
|
2356
|
-
throw new MpqCompressionError("Empty input buffer");
|
|
2357
|
-
}
|
|
2358
|
-
const compressionMask = inBuffer[0];
|
|
2359
|
-
let data = inBuffer.subarray(1);
|
|
2360
|
-
if (compressionMask === MPQ_COMPRESSION_LZMA) {
|
|
2361
|
-
return decompressLzma(data, outSize);
|
|
2362
|
-
}
|
|
2363
|
-
const stages = [];
|
|
2364
|
-
let remainingMask = compressionMask;
|
|
2365
|
-
for (const entry of DECOMPRESS_TABLE) {
|
|
2366
|
-
if (remainingMask & entry.mask) {
|
|
2367
|
-
stages.push(entry);
|
|
2368
|
-
remainingMask &= ~entry.mask;
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
if (stages.length === 0 || remainingMask !== 0) {
|
|
2372
|
-
throw new MpqCompressionError(
|
|
2373
|
-
`Unsupported compression mask: 0x${compressionMask.toString(16).padStart(2, "0")}`
|
|
2374
|
-
);
|
|
2375
|
-
}
|
|
2376
|
-
let currentData = data;
|
|
2377
|
-
let currentSize = outSize;
|
|
2378
|
-
for (let i = 0; i < stages.length; i++) {
|
|
2379
|
-
const isLast = i === stages.length - 1;
|
|
2380
|
-
const targetSize = isLast ? outSize : currentData.length * 4;
|
|
2381
|
-
try {
|
|
2382
|
-
currentData = stages[i].decompress(
|
|
2383
|
-
Buffer.isBuffer(currentData) ? currentData : Buffer.from(currentData),
|
|
2384
|
-
isLast ? outSize : targetSize
|
|
2385
|
-
);
|
|
2386
|
-
} catch (err) {
|
|
2387
|
-
if (err instanceof MpqUnsupportedError) throw err;
|
|
2388
|
-
throw new MpqCompressionError(
|
|
2389
|
-
`Decompression stage 0x${stages[i].mask.toString(16)} failed: ${err}`
|
|
2390
|
-
);
|
|
2391
|
-
}
|
|
2392
|
-
}
|
|
2393
|
-
return Buffer.isBuffer(currentData) ? currentData : Buffer.from(currentData);
|
|
2394
|
-
}
|
|
194
|
+
_FileStream.fsModule = null;
|
|
195
|
+
var FileStream = _FileStream;
|
|
2395
196
|
|
|
2396
197
|
// src/file/sector-reader.ts
|
|
2397
198
|
init_pkware();
|
|
@@ -2581,6 +382,22 @@ var MpqArchive = class _MpqArchive {
|
|
|
2581
382
|
*/
|
|
2582
383
|
static open(path, options) {
|
|
2583
384
|
const stream = FileStream.open(path);
|
|
385
|
+
return _MpqArchive.openFromStream(stream, options);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Open an MPQ archive from in-memory binary data.
|
|
389
|
+
*
|
|
390
|
+
* This API works in both Node.js and browser runtimes.
|
|
391
|
+
*
|
|
392
|
+
* @param data - MPQ binary contents
|
|
393
|
+
* @param options - Open options
|
|
394
|
+
* @returns MpqArchive instance
|
|
395
|
+
*/
|
|
396
|
+
static openFromBuffer(data, options) {
|
|
397
|
+
const stream = FileStream.fromBuffer(data);
|
|
398
|
+
return _MpqArchive.openFromStream(stream, options);
|
|
399
|
+
}
|
|
400
|
+
static openFromStream(stream, options) {
|
|
2584
401
|
try {
|
|
2585
402
|
const noHeaderSearch = options?.noHeaderSearch ?? false;
|
|
2586
403
|
const { header, headerOffset } = findHeader(stream, noHeaderSearch);
|