@rhyster/wow-casc-dbc 2.5.0 → 2.6.0
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 +1 -1
- package/dist/index.cjs +777 -729
- package/dist/index.d.cts +66 -67
- package/dist/index.d.mts +66 -67
- package/dist/index.d.ts +66 -67
- package/dist/index.mjs +804 -756
- package/package.json +12 -18
package/dist/index.mjs
CHANGED
|
@@ -2,10 +2,10 @@ import assert from 'node:assert';
|
|
|
2
2
|
import crypto from 'node:crypto';
|
|
3
3
|
import { mapLimit, retry } from 'async';
|
|
4
4
|
import cliProgress from 'cli-progress';
|
|
5
|
+
import zlib from 'node:zlib';
|
|
5
6
|
import fs from 'node:fs/promises';
|
|
6
|
-
import path from 'node:path';
|
|
7
7
|
import http from 'node:http';
|
|
8
|
-
import
|
|
8
|
+
import path from 'node:path';
|
|
9
9
|
|
|
10
10
|
var __defProp$6 = Object.defineProperty;
|
|
11
11
|
var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -13,544 +13,684 @@ var __publicField$6 = (obj, key, value) => {
|
|
|
13
13
|
__defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
14
14
|
return value;
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
__publicField$6(this, "
|
|
20
|
-
__publicField$6(this, "
|
|
21
|
-
this
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
16
|
+
const ADB_MAGIC = 1481004104;
|
|
17
|
+
class ADBReader {
|
|
18
|
+
constructor(buffer) {
|
|
19
|
+
__publicField$6(this, "build");
|
|
20
|
+
__publicField$6(this, "entries", []);
|
|
21
|
+
__publicField$6(this, "tableEntries", /* @__PURE__ */ new Map());
|
|
22
|
+
const magic = buffer.readUInt32BE(0);
|
|
23
|
+
assert(magic === ADB_MAGIC, `[ADB]: Invalid magic: ${magic.toString(16).padStart(8, "0")}`);
|
|
24
|
+
const version = buffer.readUInt32LE(4);
|
|
25
|
+
assert(version === 9, `[ADB]: Invalid version: ${version.toString()}`);
|
|
26
|
+
const build = buffer.readUInt32LE(8);
|
|
27
|
+
this.build = build;
|
|
28
|
+
let pointer = 44;
|
|
29
|
+
while (pointer < buffer.byteLength) {
|
|
30
|
+
const offset = pointer;
|
|
31
|
+
const entryMagic = buffer.readUInt32BE(offset);
|
|
32
|
+
assert(entryMagic === ADB_MAGIC, `[ADB]: Invalid entry magic: ${magic.toString(16).padStart(8, "0")}`);
|
|
33
|
+
const regionID = buffer.readInt32LE(offset + 4);
|
|
34
|
+
const pushID = buffer.readInt32LE(offset + 8);
|
|
35
|
+
const uniqueID = buffer.readUInt32LE(offset + 12);
|
|
36
|
+
const tableHash = buffer.readUInt32LE(offset + 16);
|
|
37
|
+
const recordID = buffer.readUInt32LE(offset + 20);
|
|
38
|
+
const dataSize = buffer.readUInt32LE(offset + 24);
|
|
39
|
+
const recordState = buffer.readUInt32LE(offset + 28);
|
|
40
|
+
const data = buffer.subarray(offset + 32, offset + 32 + dataSize);
|
|
41
|
+
const entry = {
|
|
42
|
+
regionID,
|
|
43
|
+
pushID,
|
|
44
|
+
uniqueID,
|
|
45
|
+
tableHash,
|
|
46
|
+
recordID,
|
|
47
|
+
dataSize,
|
|
48
|
+
recordState,
|
|
49
|
+
data
|
|
50
|
+
};
|
|
51
|
+
this.entries.push(entry);
|
|
52
|
+
if (!this.tableEntries.has(tableHash)) {
|
|
53
|
+
this.tableEntries.set(tableHash, []);
|
|
54
|
+
}
|
|
55
|
+
this.tableEntries.get(tableHash)?.push(entry);
|
|
56
|
+
pointer += 32 + dataSize;
|
|
57
|
+
}
|
|
40
58
|
}
|
|
41
59
|
}
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
data: "data",
|
|
49
|
-
dbd: "dbd"
|
|
61
|
+
var __defProp$5 = Object.defineProperty;
|
|
62
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
63
|
+
var __publicField$5 = (obj, key, value) => {
|
|
64
|
+
__defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
65
|
+
return value;
|
|
50
66
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (res.headers.location) {
|
|
68
|
-
requestData(res.headers.location, { partialOffset, partialLength, showProgress }).then(resolve).catch((err) => {
|
|
69
|
-
throw err;
|
|
70
|
-
});
|
|
71
|
-
} else {
|
|
72
|
-
reject(new Error(`Failed to request ${url}, Status Code: ${res.statusCode.toString()}`));
|
|
67
|
+
class Salsa20 {
|
|
68
|
+
constructor(key, nonce) {
|
|
69
|
+
__publicField$5(this, "fixed");
|
|
70
|
+
__publicField$5(this, "key");
|
|
71
|
+
__publicField$5(this, "nonce");
|
|
72
|
+
__publicField$5(this, "counter", new Uint32Array([0, 0]));
|
|
73
|
+
__publicField$5(this, "state", new Uint32Array(16));
|
|
74
|
+
__publicField$5(this, "block", new Uint8Array(64));
|
|
75
|
+
__publicField$5(this, "position", 0);
|
|
76
|
+
assert(key.length === 32 || key.length === 16, "Salsa20 requires 128-bit or 256-bit key");
|
|
77
|
+
assert(nonce.length === 8, "Salsa20 requires 64-bit nonce");
|
|
78
|
+
this.key = new Uint32Array(8);
|
|
79
|
+
const keyView = new DataView(key.buffer);
|
|
80
|
+
if (key.length === 32) {
|
|
81
|
+
for (let i = 0; i < 8; i += 1) {
|
|
82
|
+
this.key[i] = keyView.getUint32(i * 4, true);
|
|
73
83
|
}
|
|
74
|
-
|
|
84
|
+
this.fixed = new Uint32Array([
|
|
85
|
+
1634760805,
|
|
86
|
+
857760878,
|
|
87
|
+
2036477234,
|
|
88
|
+
1797285236
|
|
89
|
+
]);
|
|
90
|
+
} else {
|
|
91
|
+
for (let i = 0; i < 4; i += 1) {
|
|
92
|
+
const word = keyView.getUint32(i * 4, true);
|
|
93
|
+
this.key[i] = word;
|
|
94
|
+
this.key[i + 4] = word;
|
|
95
|
+
}
|
|
96
|
+
this.fixed = new Uint32Array([
|
|
97
|
+
1634760805,
|
|
98
|
+
824206446,
|
|
99
|
+
2036477238,
|
|
100
|
+
1797285236
|
|
101
|
+
]);
|
|
75
102
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
103
|
+
this.nonce = new Uint32Array(2);
|
|
104
|
+
const nonceView = new DataView(nonce.buffer);
|
|
105
|
+
for (let i = 0; i < 2; i += 1) {
|
|
106
|
+
this.nonce[i] = nonceView.getUint32(i * 4, true);
|
|
79
107
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
108
|
+
this.generateBlock();
|
|
109
|
+
}
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
111
|
+
QR(a, b, c, d) {
|
|
112
|
+
let t;
|
|
113
|
+
t = this.state[a] + this.state[d] & 4294967295;
|
|
114
|
+
this.state[b] ^= t << 7 | t >>> 25;
|
|
115
|
+
t = this.state[b] + this.state[a] & 4294967295;
|
|
116
|
+
this.state[c] ^= t << 9 | t >>> 23;
|
|
117
|
+
t = this.state[c] + this.state[b] & 4294967295;
|
|
118
|
+
this.state[d] ^= t << 13 | t >>> 19;
|
|
119
|
+
t = this.state[d] + this.state[c] & 4294967295;
|
|
120
|
+
this.state[a] ^= t << 18 | t >>> 14;
|
|
121
|
+
}
|
|
122
|
+
generateBlock() {
|
|
123
|
+
const init = new Uint32Array([
|
|
124
|
+
this.fixed[0],
|
|
125
|
+
this.key[0],
|
|
126
|
+
this.key[1],
|
|
127
|
+
this.key[2],
|
|
128
|
+
this.key[3],
|
|
129
|
+
this.fixed[1],
|
|
130
|
+
this.nonce[0],
|
|
131
|
+
this.nonce[1],
|
|
132
|
+
this.counter[0],
|
|
133
|
+
this.counter[1],
|
|
134
|
+
this.fixed[2],
|
|
135
|
+
this.key[4],
|
|
136
|
+
this.key[5],
|
|
137
|
+
this.key[6],
|
|
138
|
+
this.key[7],
|
|
139
|
+
this.fixed[3]
|
|
140
|
+
]);
|
|
141
|
+
this.state = new Uint32Array(init);
|
|
142
|
+
for (let i = 0; i < 20; i += 2) {
|
|
143
|
+
this.QR(0, 4, 8, 12);
|
|
144
|
+
this.QR(5, 9, 13, 1);
|
|
145
|
+
this.QR(10, 14, 2, 6);
|
|
146
|
+
this.QR(15, 3, 7, 11);
|
|
147
|
+
this.QR(0, 1, 2, 3);
|
|
148
|
+
this.QR(5, 6, 7, 4);
|
|
149
|
+
this.QR(10, 11, 8, 9);
|
|
150
|
+
this.QR(15, 12, 13, 14);
|
|
151
|
+
}
|
|
152
|
+
for (let i = 0; i < 16; i += 1) {
|
|
153
|
+
const word = this.state[i] + init[i] & 4294967295;
|
|
154
|
+
this.block[i * 4] = word & 255;
|
|
155
|
+
this.block[i * 4 + 1] = word >>> 8 & 255;
|
|
156
|
+
this.block[i * 4 + 2] = word >>> 16 & 255;
|
|
157
|
+
this.block[i * 4 + 3] = word >>> 24 & 255;
|
|
158
|
+
}
|
|
159
|
+
this.counter[0] = this.counter[0] + 1 & 4294967295;
|
|
160
|
+
if (this.counter[0] === 0) {
|
|
161
|
+
this.counter[1] = this.counter[1] + 1 & 4294967295;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
process(input) {
|
|
165
|
+
const { length } = input;
|
|
166
|
+
const result = new Uint8Array(length);
|
|
167
|
+
for (let i = 0; i < length; i += 1) {
|
|
168
|
+
if (this.position === 64) {
|
|
169
|
+
this.generateBlock();
|
|
170
|
+
this.position = 0;
|
|
124
171
|
}
|
|
125
|
-
|
|
172
|
+
result[i] = input[i] ^ this.block[this.position];
|
|
173
|
+
this.position += 1;
|
|
126
174
|
}
|
|
175
|
+
return result;
|
|
127
176
|
}
|
|
128
|
-
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
var __defProp$4 = Object.defineProperty;
|
|
180
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
181
|
+
var __publicField$4 = (obj, key, value) => {
|
|
182
|
+
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
183
|
+
return value;
|
|
129
184
|
};
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
185
|
+
const BLTE_MAGIC = 1112298565;
|
|
186
|
+
const ENC_TYPE_SALSA20 = 83;
|
|
187
|
+
const EMPTY_HASH = "00000000000000000000000000000000";
|
|
188
|
+
class BLTEReader {
|
|
189
|
+
constructor(buffer, eKey, keys = /* @__PURE__ */ new Map()) {
|
|
190
|
+
__publicField$4(this, "buffer");
|
|
191
|
+
__publicField$4(this, "blte");
|
|
192
|
+
__publicField$4(this, "blocks", []);
|
|
193
|
+
__publicField$4(this, "keys");
|
|
194
|
+
__publicField$4(this, "processedBlock", 0);
|
|
195
|
+
__publicField$4(this, "processedOffset", 0);
|
|
196
|
+
this.blte = buffer;
|
|
197
|
+
this.buffer = Buffer.alloc(0);
|
|
198
|
+
this.keys = keys;
|
|
199
|
+
const size = buffer.byteLength;
|
|
200
|
+
assert(size >= 8, `[BLTE]: Invalid size: ${size.toString()} < 8`);
|
|
201
|
+
const magic = buffer.readUInt32BE(0);
|
|
202
|
+
assert(magic === BLTE_MAGIC, `[BLTE]: Invalid magic: ${magic.toString(16).padStart(8, "0")}`);
|
|
203
|
+
const headerSize = buffer.readUInt32BE(4);
|
|
204
|
+
if (headerSize === 0) {
|
|
205
|
+
const blteHash2 = crypto.createHash("md5").update(buffer).digest("hex");
|
|
206
|
+
assert(blteHash2 === eKey, `[BLTE]: Invalid hash: expected ${eKey}, got ${blteHash2}`);
|
|
207
|
+
this.blocks.push({
|
|
208
|
+
compressedSize: size - 8,
|
|
209
|
+
decompressedSize: size - 9,
|
|
210
|
+
hash: EMPTY_HASH
|
|
211
|
+
});
|
|
212
|
+
this.processedOffset = 8;
|
|
213
|
+
return;
|
|
143
214
|
}
|
|
144
|
-
|
|
215
|
+
const blteHash = crypto.createHash("md5").update(buffer.subarray(0, headerSize)).digest("hex");
|
|
216
|
+
assert(blteHash === eKey, `[BLTE]: Invalid hash: expected ${eKey}, got ${blteHash}`);
|
|
217
|
+
assert(size >= 12, `[BLTE]: Invalid size: ${size.toString()} < 12`);
|
|
218
|
+
const flag = buffer.readUInt8(8);
|
|
219
|
+
const numBlocks = buffer.readIntBE(9, 3);
|
|
220
|
+
assert(numBlocks > 0, `[BLTE]: Invalid number of blocks: ${numBlocks.toString()}`);
|
|
221
|
+
assert(flag === 15, `[BLTE]: Invalid flag: ${flag.toString(16).padStart(2, "0")}`);
|
|
222
|
+
const blockHeaderSize = numBlocks * 24;
|
|
223
|
+
assert(headerSize === blockHeaderSize + 12, `[BLTE]: Invalid header size: header size ${headerSize.toString()} != block header size ${blockHeaderSize.toString()} + 12`);
|
|
224
|
+
assert(size >= headerSize, `[BLTE]: Invalid size: ${size.toString()} < ${headerSize.toString()}`);
|
|
225
|
+
for (let i = 0; i < numBlocks; i += 1) {
|
|
226
|
+
const offset = 12 + i * 24;
|
|
227
|
+
const compressedSize = buffer.readUInt32BE(offset);
|
|
228
|
+
const decompressedSize = buffer.readUInt32BE(offset + 4);
|
|
229
|
+
const hash = buffer.toString("hex", offset + 8, offset + 24);
|
|
230
|
+
this.blocks.push({
|
|
231
|
+
compressedSize,
|
|
232
|
+
decompressedSize,
|
|
233
|
+
hash
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
this.processedOffset = headerSize;
|
|
145
237
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
238
|
+
processBlock(buffer, index, allowMissingKey) {
|
|
239
|
+
const flag = buffer.readUInt8(0);
|
|
240
|
+
switch (flag) {
|
|
241
|
+
case 69: {
|
|
242
|
+
let offset = 1;
|
|
243
|
+
const keyNameLength = buffer.readUInt8(offset);
|
|
244
|
+
offset += 1;
|
|
245
|
+
const keyNameBE = buffer.toString("hex", offset, offset + keyNameLength);
|
|
246
|
+
offset += keyNameLength;
|
|
247
|
+
const ivLength = buffer.readUInt8(offset);
|
|
248
|
+
offset += 1;
|
|
249
|
+
const ivBuffer = buffer.subarray(offset, offset + ivLength);
|
|
250
|
+
offset += ivLength;
|
|
251
|
+
const encryptType = buffer.readUInt8(offset);
|
|
252
|
+
offset += 1;
|
|
253
|
+
assert(encryptType === ENC_TYPE_SALSA20, `[BLTE]: Invalid encrypt type: ${encryptType.toString(16).padStart(2, "0")} at block ${index.toString()}`);
|
|
254
|
+
const keyName = [...keyNameBE.matchAll(/.{2}/g)].map((v) => v[0]).reverse().join("").toLowerCase();
|
|
255
|
+
const key = this.keys.get(keyName);
|
|
256
|
+
if (!key) {
|
|
257
|
+
if (allowMissingKey) {
|
|
258
|
+
return keyName;
|
|
259
|
+
}
|
|
260
|
+
throw new Error(`[BLTE]: Missing key: ${keyName} at block ${index.toString()}`);
|
|
261
|
+
}
|
|
262
|
+
const iv = new Uint8Array(8);
|
|
263
|
+
for (let i = 0; i < 8; i += 1) {
|
|
264
|
+
if (i < ivLength) {
|
|
265
|
+
iv[i] = ivBuffer.readUInt8(i) ^ index >>> 8 * i & 255;
|
|
266
|
+
} else {
|
|
267
|
+
iv[i] = 0;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const handler = new Salsa20(key, iv);
|
|
271
|
+
const decrypted = handler.process(buffer.subarray(offset));
|
|
272
|
+
if (allowMissingKey) {
|
|
273
|
+
return this.processBlock(Buffer.from(decrypted.buffer), index, true);
|
|
274
|
+
}
|
|
275
|
+
return this.processBlock(Buffer.from(decrypted.buffer), index, false);
|
|
276
|
+
}
|
|
277
|
+
case 70:
|
|
278
|
+
throw new Error(`[BLTE]: Frame (Recursive) block not supported at block ${index.toString()}`);
|
|
279
|
+
case 78:
|
|
280
|
+
return buffer.subarray(1);
|
|
281
|
+
case 90:
|
|
282
|
+
return zlib.inflateSync(buffer.subarray(1));
|
|
283
|
+
default:
|
|
284
|
+
throw new Error(`[BLTE]: Invalid block flag: ${flag.toString(16).padStart(2, "0")} at block ${index.toString()}`);
|
|
285
|
+
}
|
|
157
286
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
287
|
+
processBytes(allowMissingKey = false, size = Infinity) {
|
|
288
|
+
const missingKeyBlocks = [];
|
|
289
|
+
while (this.processedBlock < this.blocks.length && size > this.buffer.byteLength) {
|
|
290
|
+
const blockIndex = this.processedBlock;
|
|
291
|
+
const block = this.blocks[blockIndex];
|
|
292
|
+
const blockBuffer = this.blte.subarray(
|
|
293
|
+
this.processedOffset,
|
|
294
|
+
this.processedOffset + block.compressedSize
|
|
295
|
+
);
|
|
296
|
+
if (block.hash !== EMPTY_HASH) {
|
|
297
|
+
const blockHash = crypto.createHash("md5").update(blockBuffer).digest("hex");
|
|
298
|
+
assert(blockHash === block.hash, `[BLTE]: Invalid block hash: expected ${block.hash}, got ${blockHash}`);
|
|
299
|
+
}
|
|
300
|
+
if (allowMissingKey) {
|
|
301
|
+
const buffer = this.processBlock(blockBuffer, blockIndex, allowMissingKey);
|
|
302
|
+
if (buffer instanceof Buffer) {
|
|
303
|
+
assert(
|
|
304
|
+
buffer.byteLength === block.decompressedSize,
|
|
305
|
+
`[BLTE]: Invalid decompressed size: expected ${block.decompressedSize.toString()}, got ${buffer.byteLength.toString()}`
|
|
306
|
+
);
|
|
307
|
+
this.buffer = Buffer.concat([this.buffer, buffer]);
|
|
308
|
+
} else {
|
|
309
|
+
missingKeyBlocks.push({
|
|
310
|
+
offset: this.buffer.byteLength,
|
|
311
|
+
size: block.decompressedSize,
|
|
312
|
+
blockIndex,
|
|
313
|
+
keyName: buffer
|
|
314
|
+
});
|
|
315
|
+
this.buffer = Buffer.concat([
|
|
316
|
+
this.buffer,
|
|
317
|
+
Buffer.alloc(block.decompressedSize)
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
const buffer = this.processBlock(blockBuffer, blockIndex, allowMissingKey);
|
|
322
|
+
assert(
|
|
323
|
+
buffer.byteLength === block.decompressedSize,
|
|
324
|
+
`[BLTE]: Invalid decompressed size: expected ${block.decompressedSize.toString()}, got ${buffer.byteLength.toString()}`
|
|
325
|
+
);
|
|
326
|
+
this.buffer = Buffer.concat([this.buffer, buffer]);
|
|
327
|
+
}
|
|
328
|
+
this.processedBlock += 1;
|
|
329
|
+
this.processedOffset += block.compressedSize;
|
|
192
330
|
}
|
|
193
|
-
return
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
};
|
|
197
|
-
const parseProductVersions = (text) => parseProductConfig(text);
|
|
198
|
-
const parseProductCDNs = (text) => parseProductConfig(text);
|
|
331
|
+
return allowMissingKey ? missingKeyBlocks : void 0;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
199
334
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
assert(match !== null, "Invalid token encountered parsing CDN config");
|
|
206
|
-
const [key, value] = match.slice(1);
|
|
207
|
-
entries[normalizeKey(key)] = value;
|
|
208
|
-
});
|
|
209
|
-
return entries;
|
|
335
|
+
var __defProp$3 = Object.defineProperty;
|
|
336
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
337
|
+
var __publicField$3 = (obj, key, value) => {
|
|
338
|
+
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
339
|
+
return value;
|
|
210
340
|
};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
);
|
|
227
|
-
if (res.length === 1) {
|
|
228
|
-
return res[0];
|
|
341
|
+
class Store {
|
|
342
|
+
constructor(dataFile) {
|
|
343
|
+
__publicField$3(this, "data");
|
|
344
|
+
__publicField$3(this, "dataFile");
|
|
345
|
+
__publicField$3(this, "promise");
|
|
346
|
+
this.dataFile = dataFile;
|
|
347
|
+
this.data = {};
|
|
348
|
+
this.promise = new Promise((resolve) => {
|
|
349
|
+
fs.readFile(dataFile, "utf-8").then((file) => {
|
|
350
|
+
this.data = JSON.parse(file);
|
|
351
|
+
resolve();
|
|
352
|
+
}).catch(() => {
|
|
353
|
+
resolve();
|
|
354
|
+
});
|
|
355
|
+
});
|
|
229
356
|
}
|
|
230
|
-
|
|
357
|
+
async get(key) {
|
|
358
|
+
await this.promise;
|
|
359
|
+
return this.data[key];
|
|
360
|
+
}
|
|
361
|
+
async set(key, value) {
|
|
362
|
+
await this.promise;
|
|
363
|
+
this.data[key] = value;
|
|
364
|
+
await fs.writeFile(this.dataFile, JSON.stringify(this.data), "utf-8");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const USER_AGENT = "node-wow-casc-dbc";
|
|
369
|
+
const CACHE_ROOT = path.resolve("cache");
|
|
370
|
+
const CACHE_DIRS = {
|
|
371
|
+
build: "builds",
|
|
372
|
+
indexes: "indices",
|
|
373
|
+
data: "data",
|
|
374
|
+
dbd: "dbd"
|
|
231
375
|
};
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
assert(hash === cKey, `Invalid footer hash in ${cKey}: expected ${cKey}, got ${hash}`);
|
|
257
|
-
const footerHash = crypto.createHash("md5").update(footerCheckBuffer).digest("hex").slice(0, checksumSize * 2);
|
|
258
|
-
assert(footerHash === footerChecksum, `Invalid footer checksum in ${cKey}: expected ${footerChecksum}, got ${footerHash}`);
|
|
259
|
-
const tocHash = crypto.createHash("md5").update(toc).digest("hex").slice(0, checksumSize * 2);
|
|
260
|
-
assert(tocHash === tocChecksum, `Invalid toc checksum in ${cKey}: expected ${tocChecksum}, got ${tocHash}`);
|
|
261
|
-
const result = /* @__PURE__ */ new Map();
|
|
262
|
-
for (let i = 0; i < numBlocks; i += 1) {
|
|
263
|
-
const lastEkey = toc.toString("hex", i * keySize, (i + 1) * keySize);
|
|
264
|
-
const blockChecksum = toc.toString("hex", numBlocks * keySize + i * checksumSize, numBlocks * keySize + (i + 1) * checksumSize);
|
|
265
|
-
const blockOffset = i * blockSize;
|
|
266
|
-
const blockHash = crypto.createHash("md5").update(buffer.subarray(i * blockSize, (i + 1) * blockSize)).digest("hex").slice(0, checksumSize * 2);
|
|
267
|
-
assert(blockChecksum === blockHash, `Invalid block hash in ${cKey} at ${i.toString()}: expected ${blockChecksum}, got ${blockHash}`);
|
|
268
|
-
let length = 0;
|
|
269
|
-
while (length < blockSize) {
|
|
270
|
-
const entryOffset = blockOffset + length * entrySize;
|
|
271
|
-
const eKey = buffer.toString("hex", entryOffset, entryOffset + keySize);
|
|
272
|
-
const size = buffer.readUIntBE(entryOffset + keySize, sizeBytes);
|
|
273
|
-
const offset = buffer.readUIntBE(entryOffset + keySize + sizeBytes, offsetBytes);
|
|
274
|
-
result.set(eKey, { key: cKey, size, offset });
|
|
275
|
-
length += 1;
|
|
276
|
-
if (eKey === lastEkey) {
|
|
277
|
-
break;
|
|
376
|
+
const CACHE_INTEGRITY_FILE = path.resolve(CACHE_ROOT, "integrity.json");
|
|
377
|
+
const cacheIntegrity = new Store(CACHE_INTEGRITY_FILE);
|
|
378
|
+
const formatCDNKey = (key) => `${key.substring(0, 2)}/${key.substring(2, 4)}/${key}`;
|
|
379
|
+
const requestData = async (url, {
|
|
380
|
+
partialOffset,
|
|
381
|
+
partialLength,
|
|
382
|
+
showProgress
|
|
383
|
+
} = {}) => new Promise((resolve, reject) => {
|
|
384
|
+
const options = {
|
|
385
|
+
headers: {
|
|
386
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
387
|
+
"User-Agent": USER_AGENT,
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
389
|
+
Range: partialOffset !== void 0 && partialLength !== void 0 ? `bytes=${partialOffset.toString()}-${(partialOffset + partialLength - 1).toString()}` : "bytes=0-"
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
http.get(url, options, (res) => {
|
|
393
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
394
|
+
if (res.headers.location !== void 0) {
|
|
395
|
+
requestData(res.headers.location, { partialOffset, partialLength, showProgress }).then(resolve).catch((err) => {
|
|
396
|
+
throw err;
|
|
397
|
+
});
|
|
398
|
+
} else {
|
|
399
|
+
reject(new Error(`Failed to request ${url}, Status Code: ${res.statusCode.toString()}`));
|
|
278
400
|
}
|
|
401
|
+
return;
|
|
279
402
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
403
|
+
if (res.statusCode === void 0 || res.statusCode < 200 || res.statusCode > 302) {
|
|
404
|
+
reject(new Error(`Failed to request ${url}, Status Code: ${res.statusCode?.toString() ?? "undefined"}`));
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const lengthText = res.headers["content-length"];
|
|
408
|
+
const length = lengthText !== void 0 ? parseInt(lengthText, 10) : 0;
|
|
409
|
+
const bar = showProgress === true && !Number.isNaN(length) && length >= 10485760 ? new cliProgress.SingleBar({ etaBuffer: 10240 }, cliProgress.Presets.shades_classic) : void 0;
|
|
410
|
+
bar?.start(length, 0);
|
|
411
|
+
const chunks = [];
|
|
412
|
+
res.on("data", (chunk) => {
|
|
413
|
+
bar?.increment(chunk.length);
|
|
414
|
+
chunks.push(chunk);
|
|
415
|
+
});
|
|
416
|
+
res.on("end", () => {
|
|
417
|
+
bar?.stop();
|
|
418
|
+
resolve(Buffer.concat(chunks));
|
|
419
|
+
});
|
|
420
|
+
res.on("error", (err) => {
|
|
421
|
+
bar?.stop();
|
|
422
|
+
reject(err);
|
|
423
|
+
});
|
|
424
|
+
}).on("error", reject).end();
|
|
425
|
+
});
|
|
426
|
+
const downloadFile = (prefixes, type, key, {
|
|
427
|
+
partialOffset,
|
|
428
|
+
partialLength,
|
|
429
|
+
showProgress,
|
|
430
|
+
showAttemptFail
|
|
431
|
+
} = {}) => {
|
|
432
|
+
const urls = prefixes.map((prefix) => `${prefix}/${type}/${formatCDNKey(key)}`);
|
|
433
|
+
return urls.reduce(
|
|
434
|
+
(prev, url, index) => prev.catch((err) => {
|
|
435
|
+
if (showAttemptFail === true && index > 0 && err instanceof Error) {
|
|
436
|
+
console.warn(`${( new Date()).toISOString()} [WARN]:`, err.message);
|
|
307
437
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
438
|
+
return requestData(url, { partialOffset, partialLength, showProgress });
|
|
439
|
+
}),
|
|
440
|
+
Promise.reject(new Error(""))
|
|
441
|
+
);
|
|
442
|
+
};
|
|
443
|
+
const getFileCache = async (file) => {
|
|
444
|
+
const integrity = await cacheIntegrity.get(file);
|
|
445
|
+
if (integrity !== void 0) {
|
|
446
|
+
try {
|
|
447
|
+
const buffer = await fs.readFile(path.resolve(CACHE_ROOT, file));
|
|
448
|
+
const hash = crypto.createHash("sha256").update(buffer).digest("hex");
|
|
449
|
+
if (hash === integrity) {
|
|
450
|
+
return buffer;
|
|
319
451
|
}
|
|
320
|
-
|
|
321
|
-
1634760805,
|
|
322
|
-
824206446,
|
|
323
|
-
2036477238,
|
|
324
|
-
1797285236
|
|
325
|
-
]);
|
|
452
|
+
} catch {
|
|
326
453
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
454
|
+
}
|
|
455
|
+
return void 0;
|
|
456
|
+
};
|
|
457
|
+
const getDataFile = async (prefixes, key, type, buildCKey, {
|
|
458
|
+
name,
|
|
459
|
+
partialOffset,
|
|
460
|
+
partialLength,
|
|
461
|
+
showProgress,
|
|
462
|
+
showAttemptFail
|
|
463
|
+
} = {}) => {
|
|
464
|
+
const dir = type === "build" ? path.join(CACHE_DIRS[type], buildCKey) : CACHE_DIRS[type];
|
|
465
|
+
const file = name !== void 0 ? path.join(dir, name) : path.join(dir, key);
|
|
466
|
+
const cacheBuffer = await getFileCache(file);
|
|
467
|
+
if (cacheBuffer) {
|
|
468
|
+
if (name === void 0 && partialOffset !== void 0 && partialLength !== void 0) {
|
|
469
|
+
return cacheBuffer.subarray(partialOffset, partialOffset + partialLength);
|
|
331
470
|
}
|
|
332
|
-
|
|
471
|
+
return cacheBuffer;
|
|
333
472
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
473
|
+
const downloadBuffer = await downloadFile(prefixes, "data", key, {
|
|
474
|
+
partialOffset,
|
|
475
|
+
partialLength,
|
|
476
|
+
showProgress,
|
|
477
|
+
showAttemptFail
|
|
478
|
+
});
|
|
479
|
+
if (partialOffset === void 0 && partialLength === void 0 || name !== void 0) {
|
|
480
|
+
await fs.mkdir(path.resolve(CACHE_ROOT, dir), { recursive: true });
|
|
481
|
+
await fs.writeFile(path.resolve(CACHE_ROOT, file), downloadBuffer);
|
|
482
|
+
const hash = crypto.createHash("sha256").update(downloadBuffer).digest("hex");
|
|
483
|
+
await cacheIntegrity.set(file, hash);
|
|
344
484
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
485
|
+
return downloadBuffer;
|
|
486
|
+
};
|
|
487
|
+
const getConfigFile = async (prefixes, key, {
|
|
488
|
+
showProgress,
|
|
489
|
+
showAttemptFail
|
|
490
|
+
} = {}) => {
|
|
491
|
+
const downloadBuffer = await downloadFile(prefixes, "config", key, { showProgress, showAttemptFail });
|
|
492
|
+
return downloadBuffer.toString("utf-8");
|
|
493
|
+
};
|
|
494
|
+
const getProductVersions = async (region, product) => {
|
|
495
|
+
const url = `http://${region}.patch.battle.net:1119/${product}/versions`;
|
|
496
|
+
const headers = new Headers();
|
|
497
|
+
headers.set("User-Agent", USER_AGENT);
|
|
498
|
+
const res = await fetch(url, { headers });
|
|
499
|
+
return res.text();
|
|
500
|
+
};
|
|
501
|
+
const getProductCDNs = async (region, product) => {
|
|
502
|
+
const url = `http://${region}.patch.battle.net:1119/${product}/cdns`;
|
|
503
|
+
const headers = new Headers();
|
|
504
|
+
headers.set("User-Agent", USER_AGENT);
|
|
505
|
+
const res = await fetch(url, { headers });
|
|
506
|
+
return res.text();
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
const hashlittle2 = (key, pc = 0, pb = 0) => {
|
|
510
|
+
const { length } = key;
|
|
511
|
+
let offset = 0;
|
|
512
|
+
let a = 3735928559 + length + pc | 0;
|
|
513
|
+
let b = 3735928559 + length + pc | 0;
|
|
514
|
+
let c = 3735928559 + length + pc + pb | 0;
|
|
515
|
+
while (length - offset > 12) {
|
|
516
|
+
a += key.charCodeAt(offset + 0);
|
|
517
|
+
a += key.charCodeAt(offset + 1) << 8;
|
|
518
|
+
a += key.charCodeAt(offset + 2) << 16;
|
|
519
|
+
a += key.charCodeAt(offset + 3) << 24;
|
|
520
|
+
b += key.charCodeAt(offset + 4);
|
|
521
|
+
b += key.charCodeAt(offset + 5) << 8;
|
|
522
|
+
b += key.charCodeAt(offset + 6) << 16;
|
|
523
|
+
b += key.charCodeAt(offset + 7) << 24;
|
|
524
|
+
c += key.charCodeAt(offset + 8);
|
|
525
|
+
c += key.charCodeAt(offset + 9) << 8;
|
|
526
|
+
c += key.charCodeAt(offset + 10) << 16;
|
|
527
|
+
c += key.charCodeAt(offset + 11) << 24;
|
|
528
|
+
a -= c;
|
|
529
|
+
a ^= c << 4 | c >>> 28;
|
|
530
|
+
c = c + b | 0;
|
|
531
|
+
b -= a;
|
|
532
|
+
b ^= a << 6 | a >>> 26;
|
|
533
|
+
a = a + c | 0;
|
|
534
|
+
c -= b;
|
|
535
|
+
c ^= b << 8 | b >>> 24;
|
|
536
|
+
b = b + a | 0;
|
|
537
|
+
a -= c;
|
|
538
|
+
a ^= c << 16 | c >>> 16;
|
|
539
|
+
c = c + b | 0;
|
|
540
|
+
b -= a;
|
|
541
|
+
b ^= a << 19 | a >>> 13;
|
|
542
|
+
a = a + c | 0;
|
|
543
|
+
c -= b;
|
|
544
|
+
c ^= b << 4 | b >>> 28;
|
|
545
|
+
b = b + a | 0;
|
|
546
|
+
offset += 12;
|
|
386
547
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
548
|
+
if (length - offset > 0) {
|
|
549
|
+
switch (length - offset) {
|
|
550
|
+
case 12:
|
|
551
|
+
c += key.charCodeAt(offset + 11) << 24;
|
|
552
|
+
case 11:
|
|
553
|
+
c += key.charCodeAt(offset + 10) << 16;
|
|
554
|
+
case 10:
|
|
555
|
+
c += key.charCodeAt(offset + 9) << 8;
|
|
556
|
+
case 9:
|
|
557
|
+
c += key.charCodeAt(offset + 8);
|
|
558
|
+
case 8:
|
|
559
|
+
b += key.charCodeAt(offset + 7) << 24;
|
|
560
|
+
case 7:
|
|
561
|
+
b += key.charCodeAt(offset + 6) << 16;
|
|
562
|
+
case 6:
|
|
563
|
+
b += key.charCodeAt(offset + 5) << 8;
|
|
564
|
+
case 5:
|
|
565
|
+
b += key.charCodeAt(offset + 4);
|
|
566
|
+
case 4:
|
|
567
|
+
a += key.charCodeAt(offset + 3) << 24;
|
|
568
|
+
case 3:
|
|
569
|
+
a += key.charCodeAt(offset + 2) << 16;
|
|
570
|
+
case 2:
|
|
571
|
+
a += key.charCodeAt(offset + 1) << 8;
|
|
572
|
+
case 1:
|
|
573
|
+
a += key.charCodeAt(offset + 0);
|
|
397
574
|
}
|
|
398
|
-
|
|
575
|
+
c ^= b;
|
|
576
|
+
c -= b << 14 | b >>> 18;
|
|
577
|
+
a ^= c;
|
|
578
|
+
a -= c << 11 | c >>> 21;
|
|
579
|
+
b ^= a;
|
|
580
|
+
b -= a << 25 | a >>> 7;
|
|
581
|
+
c ^= b;
|
|
582
|
+
c -= b << 16 | b >>> 16;
|
|
583
|
+
a ^= c;
|
|
584
|
+
a -= c << 4 | c >>> 28;
|
|
585
|
+
b ^= a;
|
|
586
|
+
b -= a << 14 | a >>> 18;
|
|
587
|
+
c ^= b;
|
|
588
|
+
c -= b << 24 | b >>> 8;
|
|
399
589
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
var __defProp$4 = Object.defineProperty;
|
|
403
|
-
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
404
|
-
var __publicField$4 = (obj, key, value) => {
|
|
405
|
-
__defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
406
|
-
return value;
|
|
590
|
+
return [c >>> 0, b >>> 0];
|
|
407
591
|
};
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const flag = buffer.readUInt8(8);
|
|
442
|
-
const numBlocks = buffer.readIntBE(9, 3);
|
|
443
|
-
assert(numBlocks > 0, `[BLTE]: Invalid number of blocks: ${numBlocks.toString()}`);
|
|
444
|
-
assert(flag === 15, `[BLTE]: Invalid flag: ${flag.toString(16).padStart(2, "0")}`);
|
|
445
|
-
const blockHeaderSize = numBlocks * 24;
|
|
446
|
-
assert(headerSize === blockHeaderSize + 12, `[BLTE]: Invalid header size: header size ${headerSize.toString()} != block header size ${blockHeaderSize.toString()} + 12`);
|
|
447
|
-
assert(size >= headerSize, `[BLTE]: Invalid size: ${size.toString()} < ${headerSize.toString()}`);
|
|
448
|
-
for (let i = 0; i < numBlocks; i += 1) {
|
|
449
|
-
const offset = 12 + i * 24;
|
|
450
|
-
const compressedSize = buffer.readUInt32BE(offset);
|
|
451
|
-
const decompressedSize = buffer.readUInt32BE(offset + 4);
|
|
452
|
-
const hash = buffer.toString("hex", offset + 8, offset + 24);
|
|
453
|
-
this.blocks.push({
|
|
454
|
-
compressedSize,
|
|
455
|
-
decompressedSize,
|
|
456
|
-
hash
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
this.processedOffset = headerSize;
|
|
460
|
-
}
|
|
461
|
-
processBlock(buffer, index, allowMissingKey) {
|
|
462
|
-
const flag = buffer.readUInt8(0);
|
|
463
|
-
switch (flag) {
|
|
464
|
-
case 69: {
|
|
465
|
-
let offset = 1;
|
|
466
|
-
const keyNameLength = buffer.readUInt8(offset);
|
|
467
|
-
offset += 1;
|
|
468
|
-
const keyNameBE = buffer.toString("hex", offset, offset + keyNameLength);
|
|
469
|
-
offset += keyNameLength;
|
|
470
|
-
const ivLength = buffer.readUInt8(offset);
|
|
471
|
-
offset += 1;
|
|
472
|
-
const ivBuffer = buffer.subarray(offset, offset + ivLength);
|
|
473
|
-
offset += ivLength;
|
|
474
|
-
const encryptType = buffer.readUInt8(offset);
|
|
475
|
-
offset += 1;
|
|
476
|
-
assert(encryptType === ENC_TYPE_SALSA20, `[BLTE]: Invalid encrypt type: ${encryptType.toString(16).padStart(2, "0")}`);
|
|
477
|
-
const keyName = [...keyNameBE.matchAll(/.{2}/g)].map((v) => v[0]).reverse().join("").toLowerCase();
|
|
478
|
-
const key = this.keys.get(keyName);
|
|
479
|
-
if (!key) {
|
|
480
|
-
if (allowMissingKey) {
|
|
481
|
-
return keyName;
|
|
482
|
-
}
|
|
483
|
-
throw new Error(`[BLTE]: Missing key: ${keyName}`);
|
|
484
|
-
}
|
|
485
|
-
const iv = new Uint8Array(8);
|
|
486
|
-
for (let i = 0; i < 8; i += 1) {
|
|
487
|
-
const byte = ivBuffer.byteLength > i ? ivBuffer.readUInt8(i) : void 0;
|
|
488
|
-
iv[i] = byte ? byte ^ index >> 8 * i & 255 : 0;
|
|
489
|
-
}
|
|
490
|
-
const handler = new Salsa20(key, iv);
|
|
491
|
-
const decrypted = handler.process(buffer.subarray(offset));
|
|
492
|
-
if (allowMissingKey) {
|
|
493
|
-
return this.processBlock(Buffer.from(decrypted.buffer), index, true);
|
|
494
|
-
}
|
|
495
|
-
return this.processBlock(Buffer.from(decrypted.buffer), index, false);
|
|
496
|
-
}
|
|
497
|
-
case 70:
|
|
498
|
-
throw new Error("[BLTE]: Frame (Recursive) block not supported");
|
|
499
|
-
case 78:
|
|
500
|
-
return buffer.subarray(1);
|
|
501
|
-
case 90:
|
|
502
|
-
return zlib.inflateSync(buffer.subarray(1));
|
|
503
|
-
default:
|
|
504
|
-
throw new Error(`[BLTE]: Invalid block flag: ${flag.toString(16).padStart(2, "0")}`);
|
|
505
|
-
}
|
|
592
|
+
const getNameHash = (name) => {
|
|
593
|
+
const normalized = name.replace(/\//g, "\\").toUpperCase();
|
|
594
|
+
const [pc, pb] = hashlittle2(normalized);
|
|
595
|
+
return `${pc.toString(16).padStart(8, "0")}${pb.toString(16).padStart(8, "0")}`;
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const VERSION_SUB_OFFSET = -12;
|
|
599
|
+
const CHECKSUM_SIZE_SUB_OFFSET = -5;
|
|
600
|
+
const BLOCK_SIZE_OFFSET = 3;
|
|
601
|
+
const OFFSET_BYTES_OFFSET = 4;
|
|
602
|
+
const SIZE_BYTES_OFFSET = 5;
|
|
603
|
+
const KEY_SIZE_OFFSET = 6;
|
|
604
|
+
const NUM_ELEMENTS_OFFSET = 8;
|
|
605
|
+
const CHECKSUM_OFFSET = 12;
|
|
606
|
+
const CHECKSUM_TRIES = [
|
|
607
|
+
10,
|
|
608
|
+
9,
|
|
609
|
+
8,
|
|
610
|
+
7,
|
|
611
|
+
6,
|
|
612
|
+
5,
|
|
613
|
+
4,
|
|
614
|
+
3,
|
|
615
|
+
2,
|
|
616
|
+
1,
|
|
617
|
+
0
|
|
618
|
+
];
|
|
619
|
+
const tryArchiveIndexChecksumSize = (buffer, cKey) => {
|
|
620
|
+
const res = CHECKSUM_TRIES.filter(
|
|
621
|
+
(index) => buffer.readUInt8(buffer.byteLength - index + CHECKSUM_SIZE_SUB_OFFSET) === index && buffer.readUInt8(buffer.byteLength - index + VERSION_SUB_OFFSET) === 1
|
|
622
|
+
);
|
|
623
|
+
if (res.length === 1) {
|
|
624
|
+
return res[0];
|
|
506
625
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
626
|
+
throw new Error(`Invalid checksum size: ${res.join(", ")} in ${cKey}`);
|
|
627
|
+
};
|
|
628
|
+
const parseArchiveIndex = (buffer, cKey) => {
|
|
629
|
+
const checksumSize = tryArchiveIndexChecksumSize(buffer, cKey);
|
|
630
|
+
const versionOffset = buffer.byteLength - checksumSize + VERSION_SUB_OFFSET;
|
|
631
|
+
const footerOffset = versionOffset - checksumSize;
|
|
632
|
+
const tocChecksum = buffer.toString("hex", footerOffset, versionOffset);
|
|
633
|
+
const version = buffer.readUInt8(versionOffset);
|
|
634
|
+
const blockSizeKB = buffer.readUInt8(versionOffset + BLOCK_SIZE_OFFSET);
|
|
635
|
+
const offsetBytes = buffer.readUInt8(versionOffset + OFFSET_BYTES_OFFSET);
|
|
636
|
+
const sizeBytes = buffer.readUInt8(versionOffset + SIZE_BYTES_OFFSET);
|
|
637
|
+
const keySize = buffer.readUInt8(versionOffset + KEY_SIZE_OFFSET);
|
|
638
|
+
const numElements = buffer.readUInt32LE(versionOffset + NUM_ELEMENTS_OFFSET);
|
|
639
|
+
const footerChecksum = buffer.toString("hex", versionOffset + CHECKSUM_OFFSET);
|
|
640
|
+
assert(version === 1, `Invalid version: ${version.toString()} in ${cKey}`);
|
|
641
|
+
const entrySize = keySize + offsetBytes + sizeBytes;
|
|
642
|
+
const blockSize = blockSizeKB * 1024;
|
|
643
|
+
const numBlocks = footerOffset / (blockSize + keySize + checksumSize);
|
|
644
|
+
const tocSize = (keySize + checksumSize) * numBlocks;
|
|
645
|
+
const toc = buffer.subarray(footerOffset - tocSize, footerOffset);
|
|
646
|
+
const footer = buffer.subarray(footerOffset);
|
|
647
|
+
const footerCheckBuffer = Buffer.concat([
|
|
648
|
+
buffer.subarray(versionOffset, buffer.byteLength - checksumSize),
|
|
649
|
+
Buffer.alloc(checksumSize)
|
|
650
|
+
]);
|
|
651
|
+
const hash = crypto.createHash("md5").update(footer).digest("hex");
|
|
652
|
+
assert(hash === cKey, `Invalid footer hash in ${cKey}: expected ${cKey}, got ${hash}`);
|
|
653
|
+
const footerHash = crypto.createHash("md5").update(footerCheckBuffer).digest("hex").slice(0, checksumSize * 2);
|
|
654
|
+
assert(footerHash === footerChecksum, `Invalid footer checksum in ${cKey}: expected ${footerChecksum}, got ${footerHash}`);
|
|
655
|
+
const tocHash = crypto.createHash("md5").update(toc).digest("hex").slice(0, checksumSize * 2);
|
|
656
|
+
assert(tocHash === tocChecksum, `Invalid toc checksum in ${cKey}: expected ${tocChecksum}, got ${tocHash}`);
|
|
657
|
+
const result = /* @__PURE__ */ new Map();
|
|
658
|
+
for (let i = 0; i < numBlocks; i += 1) {
|
|
659
|
+
const lastEkey = toc.toString("hex", i * keySize, (i + 1) * keySize);
|
|
660
|
+
const blockChecksum = toc.toString("hex", numBlocks * keySize + i * checksumSize, numBlocks * keySize + (i + 1) * checksumSize);
|
|
661
|
+
const blockOffset = i * blockSize;
|
|
662
|
+
const blockHash = crypto.createHash("md5").update(buffer.subarray(i * blockSize, (i + 1) * blockSize)).digest("hex").slice(0, checksumSize * 2);
|
|
663
|
+
assert(blockChecksum === blockHash, `Invalid block hash in ${cKey} at ${i.toString()}: expected ${blockChecksum}, got ${blockHash}`);
|
|
664
|
+
let length = 0;
|
|
665
|
+
while (length < blockSize) {
|
|
666
|
+
const entryOffset = blockOffset + length * entrySize;
|
|
667
|
+
const eKey = buffer.toString("hex", entryOffset, entryOffset + keySize);
|
|
668
|
+
const size = buffer.readUIntBE(entryOffset + keySize, sizeBytes);
|
|
669
|
+
const offset = buffer.readUIntBE(entryOffset + keySize + sizeBytes, offsetBytes);
|
|
670
|
+
result.set(eKey, { key: cKey, size, offset });
|
|
671
|
+
length += 1;
|
|
672
|
+
if (eKey === lastEkey) {
|
|
673
|
+
break;
|
|
547
674
|
}
|
|
548
|
-
this.processedBlock += 1;
|
|
549
|
-
this.processedOffset += block.compressedSize;
|
|
550
675
|
}
|
|
551
|
-
return allowMissingKey ? missingKeyBlocks : void 0;
|
|
552
676
|
}
|
|
553
|
-
}
|
|
677
|
+
assert(result.size === numElements, `Invalid number of elements: ${result.size.toString()} != ${numElements.toString()} in ${cKey}`);
|
|
678
|
+
return result;
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
const normalizeKey = (key) => key.split("-").map((part, index) => index === 0 ? part : `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("");
|
|
682
|
+
const parseConfig = (text) => {
|
|
683
|
+
const entries = {};
|
|
684
|
+
text.split(/\r?\n/).filter((line) => line.trim().length !== 0 && !line.startsWith("#")).forEach((line) => {
|
|
685
|
+
const match = /([^\s]+)\s?=\s?(.*)/.exec(line);
|
|
686
|
+
assert(match !== null, "Invalid token encountered parsing CDN config");
|
|
687
|
+
const [key, value] = match.slice(1);
|
|
688
|
+
entries[normalizeKey(key)] = value;
|
|
689
|
+
});
|
|
690
|
+
return entries;
|
|
691
|
+
};
|
|
692
|
+
const parseCDNConfig = (text) => parseConfig(text);
|
|
693
|
+
const parseBuildConfig = (text) => parseConfig(text);
|
|
554
694
|
|
|
555
695
|
const ENC_MAGIC = 17742;
|
|
556
696
|
const MAGIC_OFFSET = 0;
|
|
@@ -669,6 +809,22 @@ const parseEncodingFile = (inputBuffer, eKey, cKey) => {
|
|
|
669
809
|
};
|
|
670
810
|
};
|
|
671
811
|
|
|
812
|
+
const parseProductConfig = (text) => {
|
|
813
|
+
const lines = text.split(/\r?\n/);
|
|
814
|
+
const headers = lines[0].split("|").map((header) => header.split("!")[0].replace(" ", ""));
|
|
815
|
+
const entries = lines.filter((line, index) => index > 0 && line.trim().length !== 0 && !line.startsWith("#")).map((line) => {
|
|
816
|
+
const node = {};
|
|
817
|
+
const entryFields = line.split("|");
|
|
818
|
+
for (let i = 0, n = entryFields.length; i < n; i += 1) {
|
|
819
|
+
node[headers[i]] = entryFields[i];
|
|
820
|
+
}
|
|
821
|
+
return node;
|
|
822
|
+
});
|
|
823
|
+
return entries;
|
|
824
|
+
};
|
|
825
|
+
const parseProductVersions = (text) => parseProductConfig(text);
|
|
826
|
+
const parseProductCDNs = (text) => parseProductConfig(text);
|
|
827
|
+
|
|
672
828
|
const MFST_MAGIC = 1296454484;
|
|
673
829
|
const ContentFlags = {
|
|
674
830
|
Install: 4,
|
|
@@ -794,99 +950,66 @@ const parseRootFile = (inputBuffer, eKey, cKey) => {
|
|
|
794
950
|
return { fileDataID2CKey, nameHash2FileDataID };
|
|
795
951
|
};
|
|
796
952
|
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
a = a + c | 0;
|
|
831
|
-
c -= b;
|
|
832
|
-
c ^= b << 4 | b >>> 28;
|
|
833
|
-
b = b + a | 0;
|
|
834
|
-
offset += 12;
|
|
835
|
-
}
|
|
836
|
-
if (length - offset > 0) {
|
|
837
|
-
switch (length - offset) {
|
|
838
|
-
case 12:
|
|
839
|
-
c += key.charCodeAt(offset + 11) << 24;
|
|
840
|
-
case 11:
|
|
841
|
-
c += key.charCodeAt(offset + 10) << 16;
|
|
842
|
-
case 10:
|
|
843
|
-
c += key.charCodeAt(offset + 9) << 8;
|
|
844
|
-
case 9:
|
|
845
|
-
c += key.charCodeAt(offset + 8);
|
|
846
|
-
case 8:
|
|
847
|
-
b += key.charCodeAt(offset + 7) << 24;
|
|
848
|
-
case 7:
|
|
849
|
-
b += key.charCodeAt(offset + 6) << 16;
|
|
850
|
-
case 6:
|
|
851
|
-
b += key.charCodeAt(offset + 5) << 8;
|
|
852
|
-
case 5:
|
|
853
|
-
b += key.charCodeAt(offset + 4);
|
|
854
|
-
case 4:
|
|
855
|
-
a += key.charCodeAt(offset + 3) << 24;
|
|
856
|
-
case 3:
|
|
857
|
-
a += key.charCodeAt(offset + 2) << 16;
|
|
858
|
-
case 2:
|
|
859
|
-
a += key.charCodeAt(offset + 1) << 8;
|
|
860
|
-
case 1:
|
|
861
|
-
a += key.charCodeAt(offset + 0);
|
|
953
|
+
const JEDEC = [
|
|
954
|
+
"B",
|
|
955
|
+
"KB",
|
|
956
|
+
"MB",
|
|
957
|
+
"GB",
|
|
958
|
+
"TB",
|
|
959
|
+
"PB",
|
|
960
|
+
"EB",
|
|
961
|
+
"ZB",
|
|
962
|
+
"YB"
|
|
963
|
+
];
|
|
964
|
+
const formatFileSize = (input) => {
|
|
965
|
+
if (Number.isNaN(input))
|
|
966
|
+
return "";
|
|
967
|
+
let size = Number(input);
|
|
968
|
+
const isNegative = size < 0;
|
|
969
|
+
const result = [];
|
|
970
|
+
if (isNegative)
|
|
971
|
+
size = -size;
|
|
972
|
+
let exponent = Math.floor(Math.log(size) / Math.log(1024));
|
|
973
|
+
if (exponent < 0)
|
|
974
|
+
exponent = 0;
|
|
975
|
+
if (exponent > 8)
|
|
976
|
+
exponent = 8;
|
|
977
|
+
if (size === 0) {
|
|
978
|
+
result[0] = 0;
|
|
979
|
+
result[1] = JEDEC[exponent];
|
|
980
|
+
} else {
|
|
981
|
+
const val = size / 2 ** (exponent * 10);
|
|
982
|
+
result[0] = Number(val.toFixed(exponent > 0 ? 2 : 0));
|
|
983
|
+
if (result[0] === 1024 && exponent < 8) {
|
|
984
|
+
result[0] = 1;
|
|
985
|
+
exponent += 1;
|
|
862
986
|
}
|
|
863
|
-
|
|
864
|
-
c -= b << 14 | b >>> 18;
|
|
865
|
-
a ^= c;
|
|
866
|
-
a -= c << 11 | c >>> 21;
|
|
867
|
-
b ^= a;
|
|
868
|
-
b -= a << 25 | a >>> 7;
|
|
869
|
-
c ^= b;
|
|
870
|
-
c -= b << 16 | b >>> 16;
|
|
871
|
-
a ^= c;
|
|
872
|
-
a -= c << 4 | c >>> 28;
|
|
873
|
-
b ^= a;
|
|
874
|
-
b -= a << 14 | a >>> 18;
|
|
875
|
-
c ^= b;
|
|
876
|
-
c -= b << 24 | b >>> 8;
|
|
987
|
+
result[1] = JEDEC[exponent];
|
|
877
988
|
}
|
|
878
|
-
|
|
989
|
+
if (isNegative)
|
|
990
|
+
result[0] = -result[0];
|
|
991
|
+
return result.join(" ");
|
|
879
992
|
};
|
|
880
|
-
const
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
|
|
993
|
+
const resolveCDNHost = async (hosts, path) => {
|
|
994
|
+
const latencies = await Promise.allSettled(
|
|
995
|
+
hosts.map(async (host) => {
|
|
996
|
+
const start = Date.now();
|
|
997
|
+
await fetch(`http://${host}/`);
|
|
998
|
+
const end = Date.now();
|
|
999
|
+
return {
|
|
1000
|
+
host,
|
|
1001
|
+
latency: end - start
|
|
1002
|
+
};
|
|
1003
|
+
})
|
|
1004
|
+
);
|
|
1005
|
+
const resolved = latencies.filter((result) => result.status === "fulfilled").map((result) => result.value).sort((a, b) => a.latency - b.latency);
|
|
1006
|
+
return resolved.map((result) => `http://${result.host}/${path}`);
|
|
884
1007
|
};
|
|
885
1008
|
|
|
886
|
-
var __defProp$
|
|
887
|
-
var __defNormalProp$
|
|
888
|
-
var __publicField$
|
|
889
|
-
__defNormalProp$
|
|
1009
|
+
var __defProp$2 = Object.defineProperty;
|
|
1010
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1011
|
+
var __publicField$2 = (obj, key, value) => {
|
|
1012
|
+
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
890
1013
|
return value;
|
|
891
1014
|
};
|
|
892
1015
|
const WDC5_MAGIC = 1464091445;
|
|
@@ -909,17 +1032,17 @@ const readBitpackedValue = (buffer, fieldOffsetBits, fieldSizeBits, signed = fal
|
|
|
909
1032
|
};
|
|
910
1033
|
class WDCReader {
|
|
911
1034
|
constructor(buffer, blocks = [], adb) {
|
|
912
|
-
__publicField$
|
|
913
|
-
__publicField$
|
|
914
|
-
__publicField$
|
|
915
|
-
__publicField$
|
|
916
|
-
__publicField$
|
|
917
|
-
__publicField$
|
|
918
|
-
__publicField$
|
|
919
|
-
__publicField$
|
|
920
|
-
__publicField$
|
|
921
|
-
__publicField$
|
|
922
|
-
__publicField$
|
|
1035
|
+
__publicField$2(this, "tableHash");
|
|
1036
|
+
__publicField$2(this, "layoutHash");
|
|
1037
|
+
__publicField$2(this, "locale");
|
|
1038
|
+
__publicField$2(this, "isNormal");
|
|
1039
|
+
__publicField$2(this, "hasRelationshipData");
|
|
1040
|
+
__publicField$2(this, "fields");
|
|
1041
|
+
__publicField$2(this, "fieldsInfo");
|
|
1042
|
+
__publicField$2(this, "rows", /* @__PURE__ */ new Map());
|
|
1043
|
+
__publicField$2(this, "relationships", /* @__PURE__ */ new Map());
|
|
1044
|
+
__publicField$2(this, "copyTable", /* @__PURE__ */ new Map());
|
|
1045
|
+
__publicField$2(this, "hotfixes", /* @__PURE__ */ new Map());
|
|
923
1046
|
const magic = buffer.readUInt32BE(0);
|
|
924
1047
|
const fieldCount = buffer.readUInt32LE(140);
|
|
925
1048
|
const recordSize = buffer.readUInt32LE(144);
|
|
@@ -1245,7 +1368,7 @@ class WDCReader {
|
|
|
1245
1368
|
data: value
|
|
1246
1369
|
};
|
|
1247
1370
|
}
|
|
1248
|
-
if (
|
|
1371
|
+
if (recordID === void 0 && fieldIndex === idIndex) {
|
|
1249
1372
|
recordID = value;
|
|
1250
1373
|
}
|
|
1251
1374
|
const fieldOffset = fieldInfo.fieldOffsetBits >>> 3;
|
|
@@ -1257,7 +1380,7 @@ class WDCReader {
|
|
|
1257
1380
|
};
|
|
1258
1381
|
}
|
|
1259
1382
|
case "commonData": {
|
|
1260
|
-
const value = (recordID ? commonData.get(fieldIndex)?.get(recordID) : void 0) ?? fieldInfo.defaultValue;
|
|
1383
|
+
const value = (recordID !== void 0 ? commonData.get(fieldIndex)?.get(recordID) : void 0) ?? fieldInfo.defaultValue;
|
|
1261
1384
|
return {
|
|
1262
1385
|
type: "commonData",
|
|
1263
1386
|
data: value
|
|
@@ -1292,7 +1415,7 @@ class WDCReader {
|
|
|
1292
1415
|
assert(fieldPalletData, `No pallet data for field ${fieldIndex.toString()}`);
|
|
1293
1416
|
value = fieldPalletData[value];
|
|
1294
1417
|
}
|
|
1295
|
-
if (
|
|
1418
|
+
if (recordID === void 0 && fieldIndex === idIndex) {
|
|
1296
1419
|
recordID = value;
|
|
1297
1420
|
}
|
|
1298
1421
|
return {
|
|
@@ -1307,7 +1430,7 @@ class WDCReader {
|
|
|
1307
1430
|
assert(recordID !== void 0, "No record ID found");
|
|
1308
1431
|
this.rows.set(recordID, recordData);
|
|
1309
1432
|
const foreignID = relationshipMap.get(recordIndex);
|
|
1310
|
-
if (foreignID) {
|
|
1433
|
+
if (foreignID !== void 0) {
|
|
1311
1434
|
this.relationships.set(recordID, foreignID);
|
|
1312
1435
|
}
|
|
1313
1436
|
} else {
|
|
@@ -1318,7 +1441,7 @@ class WDCReader {
|
|
|
1318
1441
|
assert(recordID !== void 0, "No record ID found");
|
|
1319
1442
|
this.rows.set(recordID, recordData);
|
|
1320
1443
|
const foreignID = relationshipMap.get(recordIndex);
|
|
1321
|
-
if (foreignID) {
|
|
1444
|
+
if (foreignID !== void 0) {
|
|
1322
1445
|
this.relationships.set(recordID, foreignID);
|
|
1323
1446
|
}
|
|
1324
1447
|
}
|
|
@@ -1362,70 +1485,24 @@ class WDCReader {
|
|
|
1362
1485
|
}
|
|
1363
1486
|
}
|
|
1364
1487
|
const dst = this.copyTable.get(id);
|
|
1365
|
-
if (dst) {
|
|
1488
|
+
if (dst !== void 0) {
|
|
1366
1489
|
return this.rows.get(dst);
|
|
1367
1490
|
}
|
|
1368
1491
|
return this.rows.get(id);
|
|
1369
1492
|
}
|
|
1370
1493
|
getRowRelationship(id) {
|
|
1371
1494
|
const dst = this.copyTable.get(id);
|
|
1372
|
-
if (dst) {
|
|
1495
|
+
if (dst !== void 0) {
|
|
1373
1496
|
return this.relationships.get(dst);
|
|
1374
1497
|
}
|
|
1375
1498
|
return this.relationships.get(id);
|
|
1376
1499
|
}
|
|
1377
1500
|
}
|
|
1378
1501
|
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
await fetch(`http://${host}/`);
|
|
1384
|
-
const end = Date.now();
|
|
1385
|
-
return {
|
|
1386
|
-
host,
|
|
1387
|
-
latency: end - start
|
|
1388
|
-
};
|
|
1389
|
-
})
|
|
1390
|
-
);
|
|
1391
|
-
const resolved = latencies.filter((result) => result.status === "fulfilled").map((result) => result.value).sort((a, b) => a.latency - b.latency);
|
|
1392
|
-
return resolved.map((result) => `http://${result.host}/${path}`);
|
|
1393
|
-
};
|
|
1394
|
-
const JEDEC = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
1395
|
-
const formatFileSize = (input) => {
|
|
1396
|
-
if (Number.isNaN(input))
|
|
1397
|
-
return "";
|
|
1398
|
-
let size = Number(input);
|
|
1399
|
-
const isNegative = size < 0;
|
|
1400
|
-
const result = [];
|
|
1401
|
-
if (isNegative)
|
|
1402
|
-
size = -size;
|
|
1403
|
-
let exponent = Math.floor(Math.log(size) / Math.log(1024));
|
|
1404
|
-
if (exponent < 0)
|
|
1405
|
-
exponent = 0;
|
|
1406
|
-
if (exponent > 8)
|
|
1407
|
-
exponent = 8;
|
|
1408
|
-
if (size === 0) {
|
|
1409
|
-
result[0] = 0;
|
|
1410
|
-
result[1] = JEDEC[exponent];
|
|
1411
|
-
} else {
|
|
1412
|
-
const val = size / 2 ** (exponent * 10);
|
|
1413
|
-
result[0] = Number(val.toFixed(exponent > 0 ? 2 : 0));
|
|
1414
|
-
if (result[0] === 1024 && exponent < 8) {
|
|
1415
|
-
result[0] = 1;
|
|
1416
|
-
exponent += 1;
|
|
1417
|
-
}
|
|
1418
|
-
result[1] = JEDEC[exponent];
|
|
1419
|
-
}
|
|
1420
|
-
if (isNegative)
|
|
1421
|
-
result[0] = -result[0];
|
|
1422
|
-
return result.join(" ");
|
|
1423
|
-
};
|
|
1424
|
-
|
|
1425
|
-
var __defProp$2 = Object.defineProperty;
|
|
1426
|
-
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1427
|
-
var __publicField$2 = (obj, key, value) => {
|
|
1428
|
-
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1502
|
+
var __defProp$1 = Object.defineProperty;
|
|
1503
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1504
|
+
var __publicField$1 = (obj, key, value) => {
|
|
1505
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1429
1506
|
return value;
|
|
1430
1507
|
};
|
|
1431
1508
|
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
@@ -1435,16 +1512,21 @@ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
|
1435
1512
|
LogLevel2[LogLevel2["debug"] = 3] = "debug";
|
|
1436
1513
|
return LogLevel2;
|
|
1437
1514
|
})(LogLevel || {});
|
|
1438
|
-
const textLogLevel = [
|
|
1515
|
+
const textLogLevel = [
|
|
1516
|
+
"ERROR",
|
|
1517
|
+
"WARN",
|
|
1518
|
+
"INFO",
|
|
1519
|
+
"DEBUG"
|
|
1520
|
+
];
|
|
1439
1521
|
class CASCClient {
|
|
1440
1522
|
constructor(region, product, version, logLevel = 2 /* info */) {
|
|
1441
|
-
__publicField$
|
|
1442
|
-
__publicField$
|
|
1443
|
-
__publicField$
|
|
1444
|
-
__publicField$
|
|
1445
|
-
__publicField$
|
|
1446
|
-
__publicField$
|
|
1447
|
-
__publicField$
|
|
1523
|
+
__publicField$1(this, "region");
|
|
1524
|
+
__publicField$1(this, "product");
|
|
1525
|
+
__publicField$1(this, "version");
|
|
1526
|
+
__publicField$1(this, "name2FileDataID", /* @__PURE__ */ new Map());
|
|
1527
|
+
__publicField$1(this, "keys", /* @__PURE__ */ new Map());
|
|
1528
|
+
__publicField$1(this, "preload");
|
|
1529
|
+
__publicField$1(this, "logLevel");
|
|
1448
1530
|
this.region = region;
|
|
1449
1531
|
this.product = product;
|
|
1450
1532
|
this.version = version;
|
|
@@ -1460,7 +1542,7 @@ class CASCClient {
|
|
|
1460
1542
|
if (level <= 0 /* error */) {
|
|
1461
1543
|
console.error(`${( new Date()).toISOString()} [${textLogLevel[level]}]:`, message);
|
|
1462
1544
|
} else {
|
|
1463
|
-
console.
|
|
1545
|
+
console.info(`${( new Date()).toISOString()} [${textLogLevel[level]}]:`, message);
|
|
1464
1546
|
}
|
|
1465
1547
|
}
|
|
1466
1548
|
}
|
|
@@ -1539,7 +1621,7 @@ class CASCClient {
|
|
|
1539
1621
|
this.log(2 /* info */, "Loading root table...");
|
|
1540
1622
|
const rootCKey = buildConfig.root;
|
|
1541
1623
|
const rootEKeys = encoding.cKey2EKey.get(rootCKey);
|
|
1542
|
-
assert(rootEKeys, "Failing to find EKey for root table.");
|
|
1624
|
+
assert(rootEKeys !== void 0, "Failing to find EKey for root table.");
|
|
1543
1625
|
const rootEKey = typeof rootEKeys === "string" ? rootEKeys : rootEKeys[0];
|
|
1544
1626
|
const rootBuffer = await getDataFile(prefixes, rootEKey, "build", this.version.BuildConfig, {
|
|
1545
1627
|
name: "root",
|
|
@@ -1593,8 +1675,8 @@ class CASCClient {
|
|
|
1593
1675
|
const lookupRow = lookupReader.rows.get(keyID);
|
|
1594
1676
|
const keyRow = keysReader.rows.get(keyID);
|
|
1595
1677
|
if (keyRow) {
|
|
1596
|
-
assert(Array.isArray(lookupRow) && lookupRow
|
|
1597
|
-
assert(Array.isArray(keyRow) && keyRow
|
|
1678
|
+
assert(Array.isArray(lookupRow) && lookupRow.length > 0, `Invalid TACTKeyLookup table row at id ${keyID.toString()}`);
|
|
1679
|
+
assert(Array.isArray(keyRow) && keyRow.length > 0, `Invalid TACTKey table row at id ${keyID.toString()}`);
|
|
1598
1680
|
const keyName = lookupRow[0].data.toString(16).padStart(16, "0");
|
|
1599
1681
|
const keyHexLE = keyRow[0].data.toString(16).padStart(32, "0");
|
|
1600
1682
|
assert(keyName.length === 16, `Invalid keyName length: ${keyName.length.toString()}`);
|
|
@@ -1646,7 +1728,7 @@ class CASCClient {
|
|
|
1646
1728
|
assert(this.preload, "Client not initialized");
|
|
1647
1729
|
const { prefixes, encoding, archives } = this.preload;
|
|
1648
1730
|
const eKeys = encoding.cKey2EKey.get(cKey);
|
|
1649
|
-
assert(eKeys, `Failing to find encoding key for ${cKey}`);
|
|
1731
|
+
assert(eKeys !== void 0, `Failing to find encoding key for ${cKey}`);
|
|
1650
1732
|
const eKey = typeof eKeys === "string" ? eKeys : eKeys[0];
|
|
1651
1733
|
const archive = archives.get(eKey);
|
|
1652
1734
|
const blte = archive ? await getDataFile(prefixes, archive.key, "data", this.version.BuildConfig, {
|
|
@@ -1662,9 +1744,12 @@ class CASCClient {
|
|
|
1662
1744
|
const reader = new BLTEReader(blte, eKey, this.keys);
|
|
1663
1745
|
if (!allowMissingKey) {
|
|
1664
1746
|
reader.processBytes(allowMissingKey);
|
|
1747
|
+
const hash = crypto.createHash("md5").update(reader.buffer).digest("hex");
|
|
1748
|
+
assert(hash === cKey, `Invalid hash: expected ${cKey}, got ${hash}`);
|
|
1665
1749
|
return {
|
|
1666
1750
|
type: "full",
|
|
1667
|
-
buffer: reader.buffer
|
|
1751
|
+
buffer: reader.buffer,
|
|
1752
|
+
blocks: void 0
|
|
1668
1753
|
};
|
|
1669
1754
|
}
|
|
1670
1755
|
const blocks = reader.processBytes(allowMissingKey);
|
|
@@ -1673,7 +1758,8 @@ class CASCClient {
|
|
|
1673
1758
|
assert(hash === cKey, `Invalid hash: expected ${cKey}, got ${hash}`);
|
|
1674
1759
|
return {
|
|
1675
1760
|
type: "full",
|
|
1676
|
-
buffer: reader.buffer
|
|
1761
|
+
buffer: reader.buffer,
|
|
1762
|
+
blocks: void 0
|
|
1677
1763
|
};
|
|
1678
1764
|
}
|
|
1679
1765
|
return {
|
|
@@ -1683,14 +1769,17 @@ class CASCClient {
|
|
|
1683
1769
|
};
|
|
1684
1770
|
}
|
|
1685
1771
|
}
|
|
1686
|
-
|
|
1687
|
-
__publicField$
|
|
1688
|
-
|
|
1772
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1773
|
+
__publicField$1(CASCClient, "LocaleFlags", LocaleFlags);
|
|
1774
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1775
|
+
__publicField$1(CASCClient, "ContentFlags", ContentFlags);
|
|
1776
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1777
|
+
__publicField$1(CASCClient, "LogLevel", LogLevel);
|
|
1689
1778
|
|
|
1690
|
-
var __defProp
|
|
1691
|
-
var __defNormalProp
|
|
1692
|
-
var __publicField
|
|
1693
|
-
__defNormalProp
|
|
1779
|
+
var __defProp = Object.defineProperty;
|
|
1780
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1781
|
+
var __publicField = (obj, key, value) => {
|
|
1782
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1694
1783
|
return value;
|
|
1695
1784
|
};
|
|
1696
1785
|
const PATTERN_COLUMN = /^(int|float|locstring|string)(<[^:]+::[^>]+>)?\s([^\s]+)/;
|
|
@@ -1735,10 +1824,10 @@ const getCastBuffer = (value, srcSize, dstSize) => {
|
|
|
1735
1824
|
};
|
|
1736
1825
|
class DBDParser {
|
|
1737
1826
|
constructor(wdc) {
|
|
1738
|
-
__publicField
|
|
1739
|
-
__publicField
|
|
1740
|
-
__publicField
|
|
1741
|
-
__publicField
|
|
1827
|
+
__publicField(this, "wdc");
|
|
1828
|
+
__publicField(this, "definitions", /* @__PURE__ */ new Map());
|
|
1829
|
+
__publicField(this, "columns", []);
|
|
1830
|
+
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
1742
1831
|
this.wdc = wdc;
|
|
1743
1832
|
}
|
|
1744
1833
|
async init() {
|
|
@@ -1746,7 +1835,7 @@ class DBDParser {
|
|
|
1746
1835
|
const manifests = await (await fetch(manifestsURL)).json();
|
|
1747
1836
|
const tableHashHex = this.wdc.tableHash.toString(16).padStart(8, "0").toLowerCase();
|
|
1748
1837
|
const manifest = manifests.find((v) => v.tableHash.toLowerCase() === tableHashHex);
|
|
1749
|
-
assert(manifest?.tableName, `No manifest found for table hash ${tableHashHex}`);
|
|
1838
|
+
assert(manifest?.tableName !== void 0, `No manifest found for table hash ${tableHashHex}`);
|
|
1750
1839
|
const url = `https://raw.githubusercontent.com/wowdev/WoWDBDefs/master/definitions/${manifest.tableName}.dbd`;
|
|
1751
1840
|
const text = await (await fetch(url)).text();
|
|
1752
1841
|
const lines = text.split("\n").map((v) => v.trim());
|
|
@@ -1762,7 +1851,7 @@ class DBDParser {
|
|
|
1762
1851
|
assert(columnsChunk?.[0] === "COLUMNS", "No column definitions found");
|
|
1763
1852
|
columnsChunk.shift();
|
|
1764
1853
|
columnsChunk.forEach((line) => {
|
|
1765
|
-
const match =
|
|
1854
|
+
const match = PATTERN_COLUMN.exec(line);
|
|
1766
1855
|
if (match) {
|
|
1767
1856
|
const [, type, , name] = match;
|
|
1768
1857
|
this.definitions.set(name.replace("?", ""), type);
|
|
@@ -1770,7 +1859,7 @@ class DBDParser {
|
|
|
1770
1859
|
});
|
|
1771
1860
|
const layoutHashHex = this.wdc.layoutHash.toString(16).padStart(8, "0").toLowerCase();
|
|
1772
1861
|
const versionChunk = chunks.find((chunk) => chunk.find((line) => {
|
|
1773
|
-
const layoutsMatch =
|
|
1862
|
+
const layoutsMatch = PATTERN_LAYOUT.exec(line);
|
|
1774
1863
|
const layouts = layoutsMatch?.[1].split(",").map((v) => v.trim().toLowerCase());
|
|
1775
1864
|
return layouts?.includes(layoutHashHex);
|
|
1776
1865
|
}));
|
|
@@ -1779,17 +1868,27 @@ class DBDParser {
|
|
|
1779
1868
|
if (line.startsWith("LAYOUT") || line.startsWith("BUILD") || line.startsWith("COMMENT")) {
|
|
1780
1869
|
return;
|
|
1781
1870
|
}
|
|
1782
|
-
const match =
|
|
1871
|
+
const match = PATTERN_FIELD.exec(line);
|
|
1783
1872
|
if (match) {
|
|
1784
|
-
const [
|
|
1873
|
+
const [
|
|
1874
|
+
,
|
|
1875
|
+
,
|
|
1876
|
+
annotationsText,
|
|
1877
|
+
name,
|
|
1878
|
+
,
|
|
1879
|
+
unsigned,
|
|
1880
|
+
sizeText,
|
|
1881
|
+
,
|
|
1882
|
+
arraySizeText
|
|
1883
|
+
] = match;
|
|
1785
1884
|
const type = this.definitions.get(name);
|
|
1786
|
-
assert(type, `No type found for column ${name}`);
|
|
1787
|
-
const annotations = annotationsText ? annotationsText.split(",").map((v) => v.trim()) :
|
|
1885
|
+
assert(type !== void 0, `No type found for column ${name}`);
|
|
1886
|
+
const annotations = annotationsText ? annotationsText.split(",").map((v) => v.trim()) : [];
|
|
1788
1887
|
const size = sizeText ? parseInt(sizeText, 10) : void 0;
|
|
1789
1888
|
const arraySize = arraySizeText ? parseInt(arraySizeText, 10) : void 0;
|
|
1790
|
-
const isID = !!annotations
|
|
1791
|
-
const isInline = !annotations
|
|
1792
|
-
const isRelation = !!annotations
|
|
1889
|
+
const isID = !!annotations.includes("id");
|
|
1890
|
+
const isInline = !annotations.includes("noninline");
|
|
1891
|
+
const isRelation = !!annotations.includes("relation");
|
|
1793
1892
|
const isSigned = !unsigned;
|
|
1794
1893
|
this.columns.push({
|
|
1795
1894
|
name,
|
|
@@ -1830,18 +1929,18 @@ class DBDParser {
|
|
|
1830
1929
|
fieldIndex += 1;
|
|
1831
1930
|
}
|
|
1832
1931
|
} else if (column.isInline) {
|
|
1932
|
+
assert(row.length > fieldIndex, `No value found for column ${column.name}`);
|
|
1833
1933
|
const cell = row[fieldIndex];
|
|
1834
|
-
assert(cell, `No value found for column ${column.name}`);
|
|
1835
1934
|
const fieldInfo = this.wdc.fieldsInfo[fieldIndex];
|
|
1836
1935
|
const srcSigned = fieldInfo.storageType === "bitpackedSigned";
|
|
1837
1936
|
const srcSize = fieldInfo.storageType === "none" || fieldInfo.storageType === "bitpacked" || fieldInfo.storageType === "bitpackedSigned" ? Math.ceil(fieldInfo.fieldSizeBits / 8) : 4;
|
|
1838
|
-
const dstSize = column.size ? Math.ceil(column.size / 8) : void 0;
|
|
1937
|
+
const dstSize = column.size !== void 0 ? Math.ceil(column.size / 8) : void 0;
|
|
1839
1938
|
if (cell.type === "bitpackedArray") {
|
|
1840
1939
|
data[column.name] = cell.data.map((v) => {
|
|
1841
1940
|
if (column.type === "float") {
|
|
1842
1941
|
return castFloat(v, srcSize, srcSigned);
|
|
1843
1942
|
}
|
|
1844
|
-
if (dstSize) {
|
|
1943
|
+
if (dstSize !== void 0) {
|
|
1845
1944
|
return castIntegerBySize(
|
|
1846
1945
|
v,
|
|
1847
1946
|
srcSize,
|
|
@@ -1859,7 +1958,7 @@ class DBDParser {
|
|
|
1859
1958
|
data[column.name] = cell.string;
|
|
1860
1959
|
}
|
|
1861
1960
|
} else if (column.type === "float") {
|
|
1862
|
-
if (column.arraySize) {
|
|
1961
|
+
if (column.arraySize !== void 0) {
|
|
1863
1962
|
const castBuffer = getCastBuffer(
|
|
1864
1963
|
typeof cell.data === "number" ? BigInt(cell.data) : cell.data,
|
|
1865
1964
|
srcSize,
|
|
@@ -1876,8 +1975,8 @@ class DBDParser {
|
|
|
1876
1975
|
data[column.name] = castFloat(cell.data, srcSize, srcSigned);
|
|
1877
1976
|
}
|
|
1878
1977
|
} else if (column.type === "int") {
|
|
1879
|
-
if (column.arraySize) {
|
|
1880
|
-
assert(dstSize, `Missing size for int array column ${column.name}`);
|
|
1978
|
+
if (column.arraySize !== void 0) {
|
|
1979
|
+
assert(dstSize !== void 0, `Missing size for int array column ${column.name}`);
|
|
1881
1980
|
const castBuffer = getCastBuffer(
|
|
1882
1981
|
typeof cell.data === "number" ? BigInt(cell.data) : cell.data,
|
|
1883
1982
|
srcSize,
|
|
@@ -1905,7 +2004,7 @@ class DBDParser {
|
|
|
1905
2004
|
column.isSigned
|
|
1906
2005
|
);
|
|
1907
2006
|
} else {
|
|
1908
|
-
assert(
|
|
2007
|
+
assert(column.size === void 0 || column.size === 64, `Unexpected size ${column.size?.toString() ?? ""} for column ${column.name}`);
|
|
1909
2008
|
if (srcSigned !== column.isSigned) {
|
|
1910
2009
|
data[column.name] = castBigInt64(
|
|
1911
2010
|
cell.data,
|
|
@@ -1959,7 +2058,7 @@ class DBDParser {
|
|
|
1959
2058
|
if (fieldIndex + 1 < this.wdc.fields.length) {
|
|
1960
2059
|
count = Math.max((nextField.position - currField.position) / size, 1);
|
|
1961
2060
|
} else {
|
|
1962
|
-
count = column.arraySize ? (buffer.byteLength - offset) / size : 1;
|
|
2061
|
+
count = column.arraySize !== void 0 ? (buffer.byteLength - offset) / size : 1;
|
|
1963
2062
|
}
|
|
1964
2063
|
for (let i = 0; i < count; i += 1) {
|
|
1965
2064
|
if (column.type === "float") {
|
|
@@ -1991,55 +2090,4 @@ class DBDParser {
|
|
|
1991
2090
|
}
|
|
1992
2091
|
}
|
|
1993
2092
|
|
|
1994
|
-
var __defProp = Object.defineProperty;
|
|
1995
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1996
|
-
var __publicField = (obj, key, value) => {
|
|
1997
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1998
|
-
return value;
|
|
1999
|
-
};
|
|
2000
|
-
const ADB_MAGIC = 1481004104;
|
|
2001
|
-
class ADBReader {
|
|
2002
|
-
constructor(buffer) {
|
|
2003
|
-
__publicField(this, "build");
|
|
2004
|
-
__publicField(this, "entries", []);
|
|
2005
|
-
__publicField(this, "tableEntries", /* @__PURE__ */ new Map());
|
|
2006
|
-
const magic = buffer.readUInt32BE(0);
|
|
2007
|
-
assert(magic === ADB_MAGIC, `[ADB]: Invalid magic: ${magic.toString(16).padStart(8, "0")}`);
|
|
2008
|
-
const version = buffer.readUInt32LE(4);
|
|
2009
|
-
assert(version === 9, `[ADB]: Invalid version: ${version.toString()}`);
|
|
2010
|
-
const build = buffer.readUInt32LE(8);
|
|
2011
|
-
this.build = build;
|
|
2012
|
-
let pointer = 44;
|
|
2013
|
-
while (pointer < buffer.byteLength) {
|
|
2014
|
-
const offset = pointer;
|
|
2015
|
-
const entryMagic = buffer.readUInt32BE(offset);
|
|
2016
|
-
assert(entryMagic === ADB_MAGIC, `[ADB]: Invalid entry magic: ${magic.toString(16).padStart(8, "0")}`);
|
|
2017
|
-
const regionID = buffer.readInt32LE(offset + 4);
|
|
2018
|
-
const pushID = buffer.readInt32LE(offset + 8);
|
|
2019
|
-
const uniqueID = buffer.readUInt32LE(offset + 12);
|
|
2020
|
-
const tableHash = buffer.readUInt32LE(offset + 16);
|
|
2021
|
-
const recordID = buffer.readUInt32LE(offset + 20);
|
|
2022
|
-
const dataSize = buffer.readUInt32LE(offset + 24);
|
|
2023
|
-
const recordState = buffer.readUInt32LE(offset + 28);
|
|
2024
|
-
const data = buffer.subarray(offset + 32, offset + 32 + dataSize);
|
|
2025
|
-
const entry = {
|
|
2026
|
-
regionID,
|
|
2027
|
-
pushID,
|
|
2028
|
-
uniqueID,
|
|
2029
|
-
tableHash,
|
|
2030
|
-
recordID,
|
|
2031
|
-
dataSize,
|
|
2032
|
-
recordState,
|
|
2033
|
-
data
|
|
2034
|
-
};
|
|
2035
|
-
this.entries.push(entry);
|
|
2036
|
-
if (!this.tableEntries.has(tableHash)) {
|
|
2037
|
-
this.tableEntries.set(tableHash, []);
|
|
2038
|
-
}
|
|
2039
|
-
this.tableEntries.get(tableHash)?.push(entry);
|
|
2040
|
-
pointer += 32 + dataSize;
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
2093
|
export { ADBReader, CASCClient, DBDParser, WDCReader };
|