@d2runewizard/d2s 2.0.134 → 2.0.135
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/lib/binary/bitreader.d.ts +23 -23
- package/lib/binary/bitreader.js +107 -107
- package/lib/binary/bitwriter.d.ts +21 -21
- package/lib/binary/bitwriter.js +117 -117
- package/lib/binary/constants.d.ts +4 -4
- package/lib/binary/constants.js +8 -8
- package/lib/binary/seekToSequence.d.ts +2 -2
- package/lib/binary/seekToSequence.js +12 -12
- package/lib/d2/attribute_enhancer.d.ts +5 -5
- package/lib/d2/attribute_enhancer.js +598 -598
- package/lib/d2/attributes.d.ts +4 -4
- package/lib/d2/attributes.js +98 -98
- package/lib/d2/constants.d.ts +4 -4
- package/lib/d2/constants.js +15 -15
- package/lib/d2/d2s.d.ts +10 -10
- package/lib/d2/d2s.js +115 -115
- package/lib/d2/header.d.ts +8 -8
- package/lib/d2/header.js +94 -94
- package/lib/d2/items.d.ts +19 -19
- package/lib/d2/items.js +879 -870
- package/lib/d2/items.js.map +1 -1
- package/lib/d2/skills.d.ts +4 -4
- package/lib/d2/skills.js +45 -45
- package/lib/d2/stash.d.ts +3 -3
- package/lib/d2/stash.js +380 -380
- package/lib/d2/types.d.ts +467 -462
- package/lib/d2/types.js +14 -14
- package/lib/d2/versions/default_header.d.ts +5 -5
- package/lib/d2/versions/default_header.js +750 -750
- package/lib/data/parser.d.ts +3 -3
- package/lib/data/parser.js +626 -626
- package/lib/data/versions/105_constant_data.d.ts +16150 -16150
- package/lib/data/versions/105_constant_data.js +24316 -24316
- package/lib/data/versions/96_constant_data.d.ts +14427 -14427
- package/lib/data/versions/96_constant_data.js +4 -4
- package/lib/data/versions/99_constant_data.d.ts +15572 -15572
- package/lib/data/versions/99_constant_data.js +4 -4
- package/lib/index.d.ts +8 -8
- package/lib/index.js +46 -46
- package/package.json +50 -50
package/lib/d2/stash.js
CHANGED
|
@@ -1,381 +1,381 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.write = exports.read = void 0;
|
|
23
|
-
const types = __importStar(require("./types"));
|
|
24
|
-
const bitwriter_1 = require("../binary/bitwriter");
|
|
25
|
-
const items = __importStar(require("./items"));
|
|
26
|
-
const attribute_enhancer_1 = require("./attribute_enhancer");
|
|
27
|
-
const bitreader_1 = require("../binary/bitreader");
|
|
28
|
-
const constants_1 = require("./constants");
|
|
29
|
-
const defaultConfig = {
|
|
30
|
-
extendedStash: false,
|
|
31
|
-
};
|
|
32
|
-
async function read(buffer, constants, version, userConfig) {
|
|
33
|
-
const stash = {};
|
|
34
|
-
const reader = new bitreader_1.BitReader(buffer);
|
|
35
|
-
const config = Object.assign(defaultConfig, userConfig);
|
|
36
|
-
const firstHeader = reader.ReadUInt32();
|
|
37
|
-
reader.SeekByte(0);
|
|
38
|
-
if (firstHeader == 0xaa55aa55) {
|
|
39
|
-
stash.pages = [];
|
|
40
|
-
stash.sharedGold = 0;
|
|
41
|
-
let pageCount = 0;
|
|
42
|
-
while (reader.offset < reader.bits.length && pageCount < 6) {
|
|
43
|
-
const pageIndex = pageCount;
|
|
44
|
-
pageCount++;
|
|
45
|
-
await readStashHeader(stash, reader, pageIndex);
|
|
46
|
-
const saveVersion = version || parseInt(stash.version);
|
|
47
|
-
if (!constants) {
|
|
48
|
-
constants = constants_1.getConstantData(saveVersion);
|
|
49
|
-
}
|
|
50
|
-
await readStashPart(stash, reader, saveVersion, constants, pageIndex);
|
|
51
|
-
}
|
|
52
|
-
const saveVersion = version || parseInt(stash.version);
|
|
53
|
-
if (!constants) {
|
|
54
|
-
constants = constants_1.getConstantData(saveVersion);
|
|
55
|
-
}
|
|
56
|
-
await readChronicle(stash, reader, saveVersion, constants);
|
|
57
|
-
stash.pageCount = pageCount;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
await readStashHeader(stash, reader);
|
|
61
|
-
const saveVersion = version || parseInt(stash.version);
|
|
62
|
-
if (!constants) {
|
|
63
|
-
constants = constants_1.getConstantData(saveVersion);
|
|
64
|
-
}
|
|
65
|
-
await readStashPages(stash, reader, saveVersion, constants);
|
|
66
|
-
}
|
|
67
|
-
return stash;
|
|
68
|
-
}
|
|
69
|
-
exports.read = read;
|
|
70
|
-
async function readStashHeader(stash, reader, pageIndex) {
|
|
71
|
-
const header = reader.ReadUInt32();
|
|
72
|
-
switch (header) {
|
|
73
|
-
// Resurrected
|
|
74
|
-
case 0xaa55aa55:
|
|
75
|
-
stash.type = types.EStashType.shared;
|
|
76
|
-
stash.hardcore = reader.ReadUInt32() == 0;
|
|
77
|
-
stash.version = reader.ReadUInt32().toString();
|
|
78
|
-
const version = parseInt(stash.version);
|
|
79
|
-
stash.sharedGold += reader.ReadUInt32();
|
|
80
|
-
reader.ReadUInt32(); // size of the sector
|
|
81
|
-
if (version === 0x69 && pageIndex !== undefined) {
|
|
82
|
-
const isStackable = reader.ReadByte();
|
|
83
|
-
stash.pages[pageIndex] = {
|
|
84
|
-
items: [],
|
|
85
|
-
name: "",
|
|
86
|
-
type: 0,
|
|
87
|
-
isStackable,
|
|
88
|
-
};
|
|
89
|
-
reader.SkipBytes(43);
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
reader.SkipBytes(44); // empty
|
|
93
|
-
}
|
|
94
|
-
break;
|
|
95
|
-
// LoD
|
|
96
|
-
case 0x535353: // SSS
|
|
97
|
-
case 0x4d545343: // CSTM
|
|
98
|
-
stash.version = reader.ReadString(2);
|
|
99
|
-
if (stash.version !== "01" && stash.version !== "02") {
|
|
100
|
-
throw new Error(`unkown stash version ${stash.version} at position ${reader.offset - 2 * 8}`);
|
|
101
|
-
}
|
|
102
|
-
stash.type = header === 0x535353 ? types.EStashType.shared : types.EStashType.private;
|
|
103
|
-
if (stash.type === types.EStashType.shared && stash.version == "02") {
|
|
104
|
-
stash.sharedGold = reader.ReadUInt32();
|
|
105
|
-
}
|
|
106
|
-
if (stash.type === types.EStashType.private) {
|
|
107
|
-
reader.ReadUInt32();
|
|
108
|
-
stash.sharedGold = 0;
|
|
109
|
-
}
|
|
110
|
-
stash.pageCount = reader.ReadUInt32();
|
|
111
|
-
break;
|
|
112
|
-
default:
|
|
113
|
-
debugger;
|
|
114
|
-
throw new Error(`shared stash header 'SSS' / 0xAA55AA55 / private stash header 'CSTM' not found at position ${reader.offset - 3 * 8}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
async function readStashPages(stash, reader, version, constants) {
|
|
118
|
-
stash.pages = [];
|
|
119
|
-
for (let i = 0; i < stash.pageCount; i++) {
|
|
120
|
-
await readStashPage(stash, reader, version, constants);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
const CHRONICLE_SECTION_MAGIC = 0xaa55aa55;
|
|
124
|
-
const CHRONICLE_MAGIC = 0xc0eaedc0;
|
|
125
|
-
const CHRONICLE_VERSION = 1;
|
|
126
|
-
const emptyChronicle = (trailing = new Uint8Array(0)) => ({
|
|
127
|
-
setItems: [],
|
|
128
|
-
uniqueItems: [],
|
|
129
|
-
runewords: [],
|
|
130
|
-
_unknown_data: {
|
|
131
|
-
envelopePadding: new Uint8Array(44),
|
|
132
|
-
trailing,
|
|
133
|
-
originalCounts: { setItems: 0, uniqueItems: 0, runewords: 0 },
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
async function readChronicle(stash, reader, version, constants) {
|
|
137
|
-
const remainingBytes = (reader.bits.length - reader.offset) / 8;
|
|
138
|
-
// The chronicle section is wrapped in the same 64-byte sector envelope used by regular
|
|
139
|
-
// stash pages (magic, hardcore flag, save version, unused gold slot, size of sector, padding).
|
|
140
|
-
if (remainingBytes < 64 || reader.ReadUInt32() !== CHRONICLE_SECTION_MAGIC) {
|
|
141
|
-
stash.chronicle = emptyChronicle(reader.ReadBytes(Math.max(0, remainingBytes - 4)));
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
reader.ReadUInt32(); // hardcore flag, mirrors the stash header, unused here
|
|
145
|
-
reader.ReadUInt32(); // save version, mirrors stash.version
|
|
146
|
-
reader.ReadUInt32(); // unused (gold slot on regular pages)
|
|
147
|
-
reader.ReadUInt32(); // size of sector
|
|
148
|
-
// Not fully understood (mirrors the isStackable-byte + padding slot on real v0x69 pages); preserved verbatim rather than assumed to be zero.
|
|
149
|
-
const envelopePadding = reader.ReadBytes(44);
|
|
150
|
-
if (reader.ReadUInt32() !== CHRONICLE_MAGIC) {
|
|
151
|
-
stash.chronicle = emptyChronicle();
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
reader.ReadUInt16(); // chronicle format version
|
|
155
|
-
const setCount = reader.ReadUInt16();
|
|
156
|
-
const uniqueCount = reader.ReadUInt16();
|
|
157
|
-
const runewordCount = reader.ReadUInt16();
|
|
158
|
-
reader.SkipBytes(8); // reserved
|
|
159
|
-
const readRawEntries = (count) => {
|
|
160
|
-
const entries = [];
|
|
161
|
-
for (let i = 0; i < count; i++) {
|
|
162
|
-
const itemId = reader.ReadUInt32();
|
|
163
|
-
const monster = reader.ReadUInt16();
|
|
164
|
-
const foundAt = reader.ReadUInt32() * 60; // minutes since epoch -> unix seconds
|
|
165
|
-
entries.push({ itemId, monster, foundAt });
|
|
166
|
-
}
|
|
167
|
-
return entries;
|
|
168
|
-
};
|
|
169
|
-
const setEntries = readRawEntries(setCount);
|
|
170
|
-
const uniqueEntries = readRawEntries(uniqueCount);
|
|
171
|
-
const runewordEntries = readRawEntries(runewordCount);
|
|
172
|
-
// Not fully understood (its content shifts and changes as entries are added/removed, likely some
|
|
173
|
-
// kind of progress/summary cache); preserved verbatim so an unmodified read+write roundtrips exactly.
|
|
174
|
-
// Editing the entry lists and writing back will NOT correctly regenerate this region.
|
|
175
|
-
const trailing = reader.ReadBytes((reader.bits.length - reader.offset) / 8);
|
|
176
|
-
stash.chronicle = {
|
|
177
|
-
setItems: setEntries.map(({ itemId, monster, foundAt }) => ({
|
|
178
|
-
item: resolveChronicleItem(itemId, constants.set_items),
|
|
179
|
-
monster,
|
|
180
|
-
foundAt,
|
|
181
|
-
})),
|
|
182
|
-
uniqueItems: uniqueEntries.map(({ itemId, monster, foundAt }) => ({
|
|
183
|
-
item: resolveChronicleItem(itemId, constants.unq_items),
|
|
184
|
-
monster,
|
|
185
|
-
foundAt,
|
|
186
|
-
})),
|
|
187
|
-
runewords: runewordEntries.map(({ itemId, monster, foundAt }) => ({
|
|
188
|
-
item: resolveChronicleItem(itemId, constants.runewords, RUNEWORD_ID_OFFSET, RUNEWORD_ID_OVERRIDES),
|
|
189
|
-
monster,
|
|
190
|
-
foundAt,
|
|
191
|
-
})),
|
|
192
|
-
_unknown_data: {
|
|
193
|
-
envelopePadding,
|
|
194
|
-
trailing,
|
|
195
|
-
originalCounts: { setItems: setCount, uniqueItems: uniqueCount, runewords: runewordCount },
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
// Runeword chronicle ids don't map 1:1 onto constants.runewords' own indices. Most fall at a
|
|
200
|
-
// fixed offset (constants.runewords has leading null placeholders before the first real entry),
|
|
201
|
-
// but a small, patch-specific cluster of ids use unrelated ids entirely and have no known formula
|
|
202
|
-
// - those need to be hardcoded here as they're discovered/reported. As of 105_constant_data,
|
|
203
|
-
// the still-unmapped ids are: 10910, 27360-27367, 27650-27652, 27974.
|
|
204
|
-
const RUNEWORD_ID_OFFSET = 20480;
|
|
205
|
-
const RUNEWORD_ID_OVERRIDES = {
|
|
206
|
-
10910: 48,
|
|
207
|
-
27360: 196,
|
|
208
|
-
27361: 197,
|
|
209
|
-
27362: 198,
|
|
210
|
-
27363: 199,
|
|
211
|
-
27364: 200,
|
|
212
|
-
27365: 201,
|
|
213
|
-
27366: 202,
|
|
214
|
-
27367: 203,
|
|
215
|
-
27650: 204,
|
|
216
|
-
27651: 205,
|
|
217
|
-
27652: 206,
|
|
218
|
-
27974: 207,
|
|
219
|
-
};
|
|
220
|
-
function resolveChronicleItem(id, table, offset = 0, overrides = {}) {
|
|
221
|
-
const index = overrides[id] !== undefined ? overrides[id] : id - offset;
|
|
222
|
-
const data = table === null || table === void 0 ? void 0 : table[index];
|
|
223
|
-
return Object.assign({ id }, data);
|
|
224
|
-
}
|
|
225
|
-
async function writeChronicle(stash, version) {
|
|
226
|
-
var _a;
|
|
227
|
-
if (!stash.chronicle) {
|
|
228
|
-
return new Uint8Array();
|
|
229
|
-
}
|
|
230
|
-
const { setItems, uniqueItems, runewords, _unknown_data } = stash.chronicle;
|
|
231
|
-
const writer = new bitwriter_1.BitWriter();
|
|
232
|
-
writer.WriteUInt32(CHRONICLE_SECTION_MAGIC);
|
|
233
|
-
writer.WriteUInt32(stash.hardcore ? 0 : version === 0x69 ? 2 : 1);
|
|
234
|
-
writer.WriteUInt32(version);
|
|
235
|
-
writer.WriteUInt32(0); // unused (gold slot on regular pages)
|
|
236
|
-
writer.WriteUInt32(0); // size of the sector, fixed below
|
|
237
|
-
writer.WriteBytes((_a = _unknown_data === null || _unknown_data === void 0 ? void 0 : _unknown_data.envelopePadding) !== null && _a !== void 0 ? _a : new Uint8Array(44));
|
|
238
|
-
writer.WriteUInt32(CHRONICLE_MAGIC);
|
|
239
|
-
writer.WriteUInt16(CHRONICLE_VERSION);
|
|
240
|
-
writer.WriteUInt16(setItems.length);
|
|
241
|
-
writer.WriteUInt16(uniqueItems.length);
|
|
242
|
-
writer.WriteUInt16(runewords.length);
|
|
243
|
-
writer.WriteBytes(new Uint8Array(8).fill(0)); // reserved
|
|
244
|
-
const writeEntry = (itemId, monster, foundAt) => {
|
|
245
|
-
writer.WriteUInt32(itemId);
|
|
246
|
-
writer.WriteUInt16(monster);
|
|
247
|
-
writer.WriteUInt32(Math.floor(foundAt / 60)); // unix seconds -> minutes since epoch
|
|
248
|
-
};
|
|
249
|
-
// Entries are stored newest-first in real saves; enforce that order on write regardless of how
|
|
250
|
-
// the caller built the list, rather than relying on callers to insert/remove at the right spot.
|
|
251
|
-
const newestFirst = (entries) => [...entries].sort((a, b) => b.foundAt - a.foundAt);
|
|
252
|
-
for (const entry of newestFirst(setItems))
|
|
253
|
-
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
254
|
-
for (const entry of newestFirst(uniqueItems))
|
|
255
|
-
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
256
|
-
for (const entry of newestFirst(runewords))
|
|
257
|
-
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
258
|
-
// Not fully understood. If the entry lists are unchanged from what was read, re-emit the
|
|
259
|
-
// captured bytes verbatim so an unmodified roundtrip matches byte-for-byte. If entries were
|
|
260
|
-
// added/removed, those captured bytes are stale (this region's content shifts with entry count),
|
|
261
|
-
// so zero it instead - a 637-entry real save had this region fully zeroed, suggesting all-zero is
|
|
262
|
-
// an accepted state, whereas carrying forward stale bytes for a different entry count is not.
|
|
263
|
-
const countsUnchanged = (_unknown_data === null || _unknown_data === void 0 ? void 0 : _unknown_data.originalCounts) &&
|
|
264
|
-
_unknown_data.originalCounts.setItems === setItems.length &&
|
|
265
|
-
_unknown_data.originalCounts.uniqueItems === uniqueItems.length &&
|
|
266
|
-
_unknown_data.originalCounts.runewords === runewords.length;
|
|
267
|
-
writer.WriteBytes(countsUnchanged ? _unknown_data.trailing : new Uint8Array(64));
|
|
268
|
-
const size = writer.offset;
|
|
269
|
-
writer.SeekByte(16);
|
|
270
|
-
writer.WriteUInt32(Math.ceil(size / 8));
|
|
271
|
-
return writer.ToArray();
|
|
272
|
-
}
|
|
273
|
-
async function readStashPage(stash, reader, version, constants) {
|
|
274
|
-
const page = {
|
|
275
|
-
items: [],
|
|
276
|
-
name: "",
|
|
277
|
-
type: 0,
|
|
278
|
-
};
|
|
279
|
-
const header = reader.ReadString(2);
|
|
280
|
-
if (header !== "ST") {
|
|
281
|
-
throw new Error(`can not read stash page header ST at position ${reader.offset - 2 * 8}`);
|
|
282
|
-
}
|
|
283
|
-
page.type = reader.ReadUInt32();
|
|
284
|
-
page.name = reader.ReadNullTerminatedString();
|
|
285
|
-
page.items = await items.readItems(reader, version, constants, defaultConfig);
|
|
286
|
-
attribute_enhancer_1.enhanceItems(page.items, constants, 1);
|
|
287
|
-
stash.pages.push(page);
|
|
288
|
-
}
|
|
289
|
-
async function readStashPart(stash, reader, version, constants, pageIndex) {
|
|
290
|
-
const currentPage = pageIndex !== undefined ? stash.pages[pageIndex] : undefined;
|
|
291
|
-
const page = currentPage || {
|
|
292
|
-
items: [],
|
|
293
|
-
name: "",
|
|
294
|
-
type: 0,
|
|
295
|
-
};
|
|
296
|
-
page.items = await items.readItems(reader, version, constants, defaultConfig);
|
|
297
|
-
attribute_enhancer_1.enhanceItems(page.items, constants, 1);
|
|
298
|
-
if (!currentPage) {
|
|
299
|
-
stash.pages.push(page);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
async function write(data, constants, version, userConfig) {
|
|
303
|
-
const config = Object.assign(defaultConfig, userConfig);
|
|
304
|
-
const writer = new bitwriter_1.BitWriter();
|
|
305
|
-
if (!constants) {
|
|
306
|
-
constants = constants_1.getConstantData(version);
|
|
307
|
-
}
|
|
308
|
-
if (version > 0x61) {
|
|
309
|
-
for (const page of data.pages) {
|
|
310
|
-
writer.WriteArray(await writeStashSection(data, page, constants, config));
|
|
311
|
-
}
|
|
312
|
-
if (version === 0x69 && data.chronicle) {
|
|
313
|
-
writer.WriteArray(await writeChronicle(data, version));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
writer.WriteArray(await writeStashHeader(data));
|
|
318
|
-
writer.WriteArray(await writeStashPages(data, version, constants, config));
|
|
319
|
-
}
|
|
320
|
-
return writer.ToArray();
|
|
321
|
-
}
|
|
322
|
-
exports.write = write;
|
|
323
|
-
async function writeStashHeader(data) {
|
|
324
|
-
const writer = new bitwriter_1.BitWriter();
|
|
325
|
-
if (data.type === types.EStashType.private) {
|
|
326
|
-
writer.WriteString("CSTM", 4);
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
writer.WriteString("SSS", 4);
|
|
330
|
-
}
|
|
331
|
-
writer.WriteString(data.version, data.version.length);
|
|
332
|
-
if (data.type === types.EStashType.private) {
|
|
333
|
-
writer.WriteString("", 4);
|
|
334
|
-
}
|
|
335
|
-
else {
|
|
336
|
-
if (data.version == "02") {
|
|
337
|
-
writer.WriteUInt32(data.sharedGold);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
writer.WriteUInt32(data.pages.length);
|
|
341
|
-
return writer.ToArray();
|
|
342
|
-
}
|
|
343
|
-
async function writeStashSection(data, page, constants, userConfig) {
|
|
344
|
-
const writer = new bitwriter_1.BitWriter();
|
|
345
|
-
const version = parseInt(data.version);
|
|
346
|
-
writer.WriteUInt32(0xaa55aa55);
|
|
347
|
-
writer.WriteUInt32(data.hardcore ? 0 : version === 0x69 ? 2 : 1); // change to 0x02
|
|
348
|
-
writer.WriteUInt32(version);
|
|
349
|
-
const maxGold = Math.min(data.sharedGold, 2500000);
|
|
350
|
-
data.sharedGold = Math.max(0, data.sharedGold - maxGold);
|
|
351
|
-
writer.WriteUInt32(maxGold);
|
|
352
|
-
writer.WriteUInt32(0); // size of the sector, to be fixed later
|
|
353
|
-
if (version === 0x69) {
|
|
354
|
-
writer.WriteByte(page.isStackable || 0);
|
|
355
|
-
writer.WriteBytes(new Uint8Array(43).fill(0)); // empty
|
|
356
|
-
}
|
|
357
|
-
else {
|
|
358
|
-
writer.WriteBytes(new Uint8Array(44).fill(0)); // empty
|
|
359
|
-
}
|
|
360
|
-
writer.WriteArray(await items.writeItems(page.items, parseInt(data.version), constants, userConfig));
|
|
361
|
-
const size = writer.offset;
|
|
362
|
-
writer.SeekByte(16);
|
|
363
|
-
writer.WriteUInt32(Math.ceil(size / 8));
|
|
364
|
-
return writer.ToArray();
|
|
365
|
-
}
|
|
366
|
-
async function writeStashPages(data, version, constants, config) {
|
|
367
|
-
const writer = new bitwriter_1.BitWriter();
|
|
368
|
-
for (let i = 0; i < data.pages.length; i++) {
|
|
369
|
-
writer.WriteArray(await writeStashPage(data.pages[i], version, constants, config));
|
|
370
|
-
}
|
|
371
|
-
return writer.ToArray();
|
|
372
|
-
}
|
|
373
|
-
async function writeStashPage(data, version, constants, config) {
|
|
374
|
-
const writer = new bitwriter_1.BitWriter();
|
|
375
|
-
writer.WriteString("ST", 2);
|
|
376
|
-
writer.WriteUInt32(data.type);
|
|
377
|
-
writer.WriteString(data.name, data.name.length + 1);
|
|
378
|
-
writer.WriteArray(await items.writeItems(data.items, version, constants, config));
|
|
379
|
-
return writer.ToArray();
|
|
380
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.write = exports.read = void 0;
|
|
23
|
+
const types = __importStar(require("./types"));
|
|
24
|
+
const bitwriter_1 = require("../binary/bitwriter");
|
|
25
|
+
const items = __importStar(require("./items"));
|
|
26
|
+
const attribute_enhancer_1 = require("./attribute_enhancer");
|
|
27
|
+
const bitreader_1 = require("../binary/bitreader");
|
|
28
|
+
const constants_1 = require("./constants");
|
|
29
|
+
const defaultConfig = {
|
|
30
|
+
extendedStash: false,
|
|
31
|
+
};
|
|
32
|
+
async function read(buffer, constants, version, userConfig) {
|
|
33
|
+
const stash = {};
|
|
34
|
+
const reader = new bitreader_1.BitReader(buffer);
|
|
35
|
+
const config = Object.assign(defaultConfig, userConfig);
|
|
36
|
+
const firstHeader = reader.ReadUInt32();
|
|
37
|
+
reader.SeekByte(0);
|
|
38
|
+
if (firstHeader == 0xaa55aa55) {
|
|
39
|
+
stash.pages = [];
|
|
40
|
+
stash.sharedGold = 0;
|
|
41
|
+
let pageCount = 0;
|
|
42
|
+
while (reader.offset < reader.bits.length && pageCount < 6) {
|
|
43
|
+
const pageIndex = pageCount;
|
|
44
|
+
pageCount++;
|
|
45
|
+
await readStashHeader(stash, reader, pageIndex);
|
|
46
|
+
const saveVersion = version || parseInt(stash.version);
|
|
47
|
+
if (!constants) {
|
|
48
|
+
constants = constants_1.getConstantData(saveVersion);
|
|
49
|
+
}
|
|
50
|
+
await readStashPart(stash, reader, saveVersion, constants, pageIndex);
|
|
51
|
+
}
|
|
52
|
+
const saveVersion = version || parseInt(stash.version);
|
|
53
|
+
if (!constants) {
|
|
54
|
+
constants = constants_1.getConstantData(saveVersion);
|
|
55
|
+
}
|
|
56
|
+
await readChronicle(stash, reader, saveVersion, constants);
|
|
57
|
+
stash.pageCount = pageCount;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await readStashHeader(stash, reader);
|
|
61
|
+
const saveVersion = version || parseInt(stash.version);
|
|
62
|
+
if (!constants) {
|
|
63
|
+
constants = constants_1.getConstantData(saveVersion);
|
|
64
|
+
}
|
|
65
|
+
await readStashPages(stash, reader, saveVersion, constants);
|
|
66
|
+
}
|
|
67
|
+
return stash;
|
|
68
|
+
}
|
|
69
|
+
exports.read = read;
|
|
70
|
+
async function readStashHeader(stash, reader, pageIndex) {
|
|
71
|
+
const header = reader.ReadUInt32();
|
|
72
|
+
switch (header) {
|
|
73
|
+
// Resurrected
|
|
74
|
+
case 0xaa55aa55:
|
|
75
|
+
stash.type = types.EStashType.shared;
|
|
76
|
+
stash.hardcore = reader.ReadUInt32() == 0;
|
|
77
|
+
stash.version = reader.ReadUInt32().toString();
|
|
78
|
+
const version = parseInt(stash.version);
|
|
79
|
+
stash.sharedGold += reader.ReadUInt32();
|
|
80
|
+
reader.ReadUInt32(); // size of the sector
|
|
81
|
+
if (version === 0x69 && pageIndex !== undefined) {
|
|
82
|
+
const isStackable = reader.ReadByte();
|
|
83
|
+
stash.pages[pageIndex] = {
|
|
84
|
+
items: [],
|
|
85
|
+
name: "",
|
|
86
|
+
type: 0,
|
|
87
|
+
isStackable,
|
|
88
|
+
};
|
|
89
|
+
reader.SkipBytes(43);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
reader.SkipBytes(44); // empty
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
// LoD
|
|
96
|
+
case 0x535353: // SSS
|
|
97
|
+
case 0x4d545343: // CSTM
|
|
98
|
+
stash.version = reader.ReadString(2);
|
|
99
|
+
if (stash.version !== "01" && stash.version !== "02") {
|
|
100
|
+
throw new Error(`unkown stash version ${stash.version} at position ${reader.offset - 2 * 8}`);
|
|
101
|
+
}
|
|
102
|
+
stash.type = header === 0x535353 ? types.EStashType.shared : types.EStashType.private;
|
|
103
|
+
if (stash.type === types.EStashType.shared && stash.version == "02") {
|
|
104
|
+
stash.sharedGold = reader.ReadUInt32();
|
|
105
|
+
}
|
|
106
|
+
if (stash.type === types.EStashType.private) {
|
|
107
|
+
reader.ReadUInt32();
|
|
108
|
+
stash.sharedGold = 0;
|
|
109
|
+
}
|
|
110
|
+
stash.pageCount = reader.ReadUInt32();
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
debugger;
|
|
114
|
+
throw new Error(`shared stash header 'SSS' / 0xAA55AA55 / private stash header 'CSTM' not found at position ${reader.offset - 3 * 8}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function readStashPages(stash, reader, version, constants) {
|
|
118
|
+
stash.pages = [];
|
|
119
|
+
for (let i = 0; i < stash.pageCount; i++) {
|
|
120
|
+
await readStashPage(stash, reader, version, constants);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const CHRONICLE_SECTION_MAGIC = 0xaa55aa55;
|
|
124
|
+
const CHRONICLE_MAGIC = 0xc0eaedc0;
|
|
125
|
+
const CHRONICLE_VERSION = 1;
|
|
126
|
+
const emptyChronicle = (trailing = new Uint8Array(0)) => ({
|
|
127
|
+
setItems: [],
|
|
128
|
+
uniqueItems: [],
|
|
129
|
+
runewords: [],
|
|
130
|
+
_unknown_data: {
|
|
131
|
+
envelopePadding: new Uint8Array(44),
|
|
132
|
+
trailing,
|
|
133
|
+
originalCounts: { setItems: 0, uniqueItems: 0, runewords: 0 },
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
async function readChronicle(stash, reader, version, constants) {
|
|
137
|
+
const remainingBytes = (reader.bits.length - reader.offset) / 8;
|
|
138
|
+
// The chronicle section is wrapped in the same 64-byte sector envelope used by regular
|
|
139
|
+
// stash pages (magic, hardcore flag, save version, unused gold slot, size of sector, padding).
|
|
140
|
+
if (remainingBytes < 64 || reader.ReadUInt32() !== CHRONICLE_SECTION_MAGIC) {
|
|
141
|
+
stash.chronicle = emptyChronicle(reader.ReadBytes(Math.max(0, remainingBytes - 4)));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
reader.ReadUInt32(); // hardcore flag, mirrors the stash header, unused here
|
|
145
|
+
reader.ReadUInt32(); // save version, mirrors stash.version
|
|
146
|
+
reader.ReadUInt32(); // unused (gold slot on regular pages)
|
|
147
|
+
reader.ReadUInt32(); // size of sector
|
|
148
|
+
// Not fully understood (mirrors the isStackable-byte + padding slot on real v0x69 pages); preserved verbatim rather than assumed to be zero.
|
|
149
|
+
const envelopePadding = reader.ReadBytes(44);
|
|
150
|
+
if (reader.ReadUInt32() !== CHRONICLE_MAGIC) {
|
|
151
|
+
stash.chronicle = emptyChronicle();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
reader.ReadUInt16(); // chronicle format version
|
|
155
|
+
const setCount = reader.ReadUInt16();
|
|
156
|
+
const uniqueCount = reader.ReadUInt16();
|
|
157
|
+
const runewordCount = reader.ReadUInt16();
|
|
158
|
+
reader.SkipBytes(8); // reserved
|
|
159
|
+
const readRawEntries = (count) => {
|
|
160
|
+
const entries = [];
|
|
161
|
+
for (let i = 0; i < count; i++) {
|
|
162
|
+
const itemId = reader.ReadUInt32();
|
|
163
|
+
const monster = reader.ReadUInt16();
|
|
164
|
+
const foundAt = reader.ReadUInt32() * 60; // minutes since epoch -> unix seconds
|
|
165
|
+
entries.push({ itemId, monster, foundAt });
|
|
166
|
+
}
|
|
167
|
+
return entries;
|
|
168
|
+
};
|
|
169
|
+
const setEntries = readRawEntries(setCount);
|
|
170
|
+
const uniqueEntries = readRawEntries(uniqueCount);
|
|
171
|
+
const runewordEntries = readRawEntries(runewordCount);
|
|
172
|
+
// Not fully understood (its content shifts and changes as entries are added/removed, likely some
|
|
173
|
+
// kind of progress/summary cache); preserved verbatim so an unmodified read+write roundtrips exactly.
|
|
174
|
+
// Editing the entry lists and writing back will NOT correctly regenerate this region.
|
|
175
|
+
const trailing = reader.ReadBytes((reader.bits.length - reader.offset) / 8);
|
|
176
|
+
stash.chronicle = {
|
|
177
|
+
setItems: setEntries.map(({ itemId, monster, foundAt }) => ({
|
|
178
|
+
item: resolveChronicleItem(itemId, constants.set_items),
|
|
179
|
+
monster,
|
|
180
|
+
foundAt,
|
|
181
|
+
})),
|
|
182
|
+
uniqueItems: uniqueEntries.map(({ itemId, monster, foundAt }) => ({
|
|
183
|
+
item: resolveChronicleItem(itemId, constants.unq_items),
|
|
184
|
+
monster,
|
|
185
|
+
foundAt,
|
|
186
|
+
})),
|
|
187
|
+
runewords: runewordEntries.map(({ itemId, monster, foundAt }) => ({
|
|
188
|
+
item: resolveChronicleItem(itemId, constants.runewords, RUNEWORD_ID_OFFSET, RUNEWORD_ID_OVERRIDES),
|
|
189
|
+
monster,
|
|
190
|
+
foundAt,
|
|
191
|
+
})),
|
|
192
|
+
_unknown_data: {
|
|
193
|
+
envelopePadding,
|
|
194
|
+
trailing,
|
|
195
|
+
originalCounts: { setItems: setCount, uniqueItems: uniqueCount, runewords: runewordCount },
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// Runeword chronicle ids don't map 1:1 onto constants.runewords' own indices. Most fall at a
|
|
200
|
+
// fixed offset (constants.runewords has leading null placeholders before the first real entry),
|
|
201
|
+
// but a small, patch-specific cluster of ids use unrelated ids entirely and have no known formula
|
|
202
|
+
// - those need to be hardcoded here as they're discovered/reported. As of 105_constant_data,
|
|
203
|
+
// the still-unmapped ids are: 10910, 27360-27367, 27650-27652, 27974.
|
|
204
|
+
const RUNEWORD_ID_OFFSET = 20480;
|
|
205
|
+
const RUNEWORD_ID_OVERRIDES = {
|
|
206
|
+
10910: 48,
|
|
207
|
+
27360: 196,
|
|
208
|
+
27361: 197,
|
|
209
|
+
27362: 198,
|
|
210
|
+
27363: 199,
|
|
211
|
+
27364: 200,
|
|
212
|
+
27365: 201,
|
|
213
|
+
27366: 202,
|
|
214
|
+
27367: 203,
|
|
215
|
+
27650: 204,
|
|
216
|
+
27651: 205,
|
|
217
|
+
27652: 206,
|
|
218
|
+
27974: 207,
|
|
219
|
+
};
|
|
220
|
+
function resolveChronicleItem(id, table, offset = 0, overrides = {}) {
|
|
221
|
+
const index = overrides[id] !== undefined ? overrides[id] : id - offset;
|
|
222
|
+
const data = table === null || table === void 0 ? void 0 : table[index];
|
|
223
|
+
return Object.assign({ id }, data);
|
|
224
|
+
}
|
|
225
|
+
async function writeChronicle(stash, version) {
|
|
226
|
+
var _a;
|
|
227
|
+
if (!stash.chronicle) {
|
|
228
|
+
return new Uint8Array();
|
|
229
|
+
}
|
|
230
|
+
const { setItems, uniqueItems, runewords, _unknown_data } = stash.chronicle;
|
|
231
|
+
const writer = new bitwriter_1.BitWriter();
|
|
232
|
+
writer.WriteUInt32(CHRONICLE_SECTION_MAGIC);
|
|
233
|
+
writer.WriteUInt32(stash.hardcore ? 0 : version === 0x69 ? 2 : 1);
|
|
234
|
+
writer.WriteUInt32(version);
|
|
235
|
+
writer.WriteUInt32(0); // unused (gold slot on regular pages)
|
|
236
|
+
writer.WriteUInt32(0); // size of the sector, fixed below
|
|
237
|
+
writer.WriteBytes((_a = _unknown_data === null || _unknown_data === void 0 ? void 0 : _unknown_data.envelopePadding) !== null && _a !== void 0 ? _a : new Uint8Array(44));
|
|
238
|
+
writer.WriteUInt32(CHRONICLE_MAGIC);
|
|
239
|
+
writer.WriteUInt16(CHRONICLE_VERSION);
|
|
240
|
+
writer.WriteUInt16(setItems.length);
|
|
241
|
+
writer.WriteUInt16(uniqueItems.length);
|
|
242
|
+
writer.WriteUInt16(runewords.length);
|
|
243
|
+
writer.WriteBytes(new Uint8Array(8).fill(0)); // reserved
|
|
244
|
+
const writeEntry = (itemId, monster, foundAt) => {
|
|
245
|
+
writer.WriteUInt32(itemId);
|
|
246
|
+
writer.WriteUInt16(monster);
|
|
247
|
+
writer.WriteUInt32(Math.floor(foundAt / 60)); // unix seconds -> minutes since epoch
|
|
248
|
+
};
|
|
249
|
+
// Entries are stored newest-first in real saves; enforce that order on write regardless of how
|
|
250
|
+
// the caller built the list, rather than relying on callers to insert/remove at the right spot.
|
|
251
|
+
const newestFirst = (entries) => [...entries].sort((a, b) => b.foundAt - a.foundAt);
|
|
252
|
+
for (const entry of newestFirst(setItems))
|
|
253
|
+
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
254
|
+
for (const entry of newestFirst(uniqueItems))
|
|
255
|
+
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
256
|
+
for (const entry of newestFirst(runewords))
|
|
257
|
+
writeEntry(entry.item.id, entry.monster, entry.foundAt);
|
|
258
|
+
// Not fully understood. If the entry lists are unchanged from what was read, re-emit the
|
|
259
|
+
// captured bytes verbatim so an unmodified roundtrip matches byte-for-byte. If entries were
|
|
260
|
+
// added/removed, those captured bytes are stale (this region's content shifts with entry count),
|
|
261
|
+
// so zero it instead - a 637-entry real save had this region fully zeroed, suggesting all-zero is
|
|
262
|
+
// an accepted state, whereas carrying forward stale bytes for a different entry count is not.
|
|
263
|
+
const countsUnchanged = (_unknown_data === null || _unknown_data === void 0 ? void 0 : _unknown_data.originalCounts) &&
|
|
264
|
+
_unknown_data.originalCounts.setItems === setItems.length &&
|
|
265
|
+
_unknown_data.originalCounts.uniqueItems === uniqueItems.length &&
|
|
266
|
+
_unknown_data.originalCounts.runewords === runewords.length;
|
|
267
|
+
writer.WriteBytes(countsUnchanged ? _unknown_data.trailing : new Uint8Array(64));
|
|
268
|
+
const size = writer.offset;
|
|
269
|
+
writer.SeekByte(16);
|
|
270
|
+
writer.WriteUInt32(Math.ceil(size / 8));
|
|
271
|
+
return writer.ToArray();
|
|
272
|
+
}
|
|
273
|
+
async function readStashPage(stash, reader, version, constants) {
|
|
274
|
+
const page = {
|
|
275
|
+
items: [],
|
|
276
|
+
name: "",
|
|
277
|
+
type: 0,
|
|
278
|
+
};
|
|
279
|
+
const header = reader.ReadString(2);
|
|
280
|
+
if (header !== "ST") {
|
|
281
|
+
throw new Error(`can not read stash page header ST at position ${reader.offset - 2 * 8}`);
|
|
282
|
+
}
|
|
283
|
+
page.type = reader.ReadUInt32();
|
|
284
|
+
page.name = reader.ReadNullTerminatedString();
|
|
285
|
+
page.items = await items.readItems(reader, version, constants, defaultConfig);
|
|
286
|
+
attribute_enhancer_1.enhanceItems(page.items, constants, 1);
|
|
287
|
+
stash.pages.push(page);
|
|
288
|
+
}
|
|
289
|
+
async function readStashPart(stash, reader, version, constants, pageIndex) {
|
|
290
|
+
const currentPage = pageIndex !== undefined ? stash.pages[pageIndex] : undefined;
|
|
291
|
+
const page = currentPage || {
|
|
292
|
+
items: [],
|
|
293
|
+
name: "",
|
|
294
|
+
type: 0,
|
|
295
|
+
};
|
|
296
|
+
page.items = await items.readItems(reader, version, constants, defaultConfig);
|
|
297
|
+
attribute_enhancer_1.enhanceItems(page.items, constants, 1);
|
|
298
|
+
if (!currentPage) {
|
|
299
|
+
stash.pages.push(page);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async function write(data, constants, version, userConfig) {
|
|
303
|
+
const config = Object.assign(defaultConfig, userConfig);
|
|
304
|
+
const writer = new bitwriter_1.BitWriter();
|
|
305
|
+
if (!constants) {
|
|
306
|
+
constants = constants_1.getConstantData(version);
|
|
307
|
+
}
|
|
308
|
+
if (version > 0x61) {
|
|
309
|
+
for (const page of data.pages) {
|
|
310
|
+
writer.WriteArray(await writeStashSection(data, page, constants, config));
|
|
311
|
+
}
|
|
312
|
+
if (version === 0x69 && data.chronicle) {
|
|
313
|
+
writer.WriteArray(await writeChronicle(data, version));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
writer.WriteArray(await writeStashHeader(data));
|
|
318
|
+
writer.WriteArray(await writeStashPages(data, version, constants, config));
|
|
319
|
+
}
|
|
320
|
+
return writer.ToArray();
|
|
321
|
+
}
|
|
322
|
+
exports.write = write;
|
|
323
|
+
async function writeStashHeader(data) {
|
|
324
|
+
const writer = new bitwriter_1.BitWriter();
|
|
325
|
+
if (data.type === types.EStashType.private) {
|
|
326
|
+
writer.WriteString("CSTM", 4);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
writer.WriteString("SSS", 4);
|
|
330
|
+
}
|
|
331
|
+
writer.WriteString(data.version, data.version.length);
|
|
332
|
+
if (data.type === types.EStashType.private) {
|
|
333
|
+
writer.WriteString("", 4);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
if (data.version == "02") {
|
|
337
|
+
writer.WriteUInt32(data.sharedGold);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
writer.WriteUInt32(data.pages.length);
|
|
341
|
+
return writer.ToArray();
|
|
342
|
+
}
|
|
343
|
+
async function writeStashSection(data, page, constants, userConfig) {
|
|
344
|
+
const writer = new bitwriter_1.BitWriter();
|
|
345
|
+
const version = parseInt(data.version);
|
|
346
|
+
writer.WriteUInt32(0xaa55aa55);
|
|
347
|
+
writer.WriteUInt32(data.hardcore ? 0 : version === 0x69 ? 2 : 1); // change to 0x02
|
|
348
|
+
writer.WriteUInt32(version);
|
|
349
|
+
const maxGold = Math.min(data.sharedGold, 2500000);
|
|
350
|
+
data.sharedGold = Math.max(0, data.sharedGold - maxGold);
|
|
351
|
+
writer.WriteUInt32(maxGold);
|
|
352
|
+
writer.WriteUInt32(0); // size of the sector, to be fixed later
|
|
353
|
+
if (version === 0x69) {
|
|
354
|
+
writer.WriteByte(page.isStackable || 0);
|
|
355
|
+
writer.WriteBytes(new Uint8Array(43).fill(0)); // empty
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
writer.WriteBytes(new Uint8Array(44).fill(0)); // empty
|
|
359
|
+
}
|
|
360
|
+
writer.WriteArray(await items.writeItems(page.items, parseInt(data.version), constants, userConfig));
|
|
361
|
+
const size = writer.offset;
|
|
362
|
+
writer.SeekByte(16);
|
|
363
|
+
writer.WriteUInt32(Math.ceil(size / 8));
|
|
364
|
+
return writer.ToArray();
|
|
365
|
+
}
|
|
366
|
+
async function writeStashPages(data, version, constants, config) {
|
|
367
|
+
const writer = new bitwriter_1.BitWriter();
|
|
368
|
+
for (let i = 0; i < data.pages.length; i++) {
|
|
369
|
+
writer.WriteArray(await writeStashPage(data.pages[i], version, constants, config));
|
|
370
|
+
}
|
|
371
|
+
return writer.ToArray();
|
|
372
|
+
}
|
|
373
|
+
async function writeStashPage(data, version, constants, config) {
|
|
374
|
+
const writer = new bitwriter_1.BitWriter();
|
|
375
|
+
writer.WriteString("ST", 2);
|
|
376
|
+
writer.WriteUInt32(data.type);
|
|
377
|
+
writer.WriteString(data.name, data.name.length + 1);
|
|
378
|
+
writer.WriteArray(await items.writeItems(data.items, version, constants, config));
|
|
379
|
+
return writer.ToArray();
|
|
380
|
+
}
|
|
381
381
|
//# sourceMappingURL=stash.js.map
|