@d2runewizard/d2s 2.0.130 → 2.0.131
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 +596 -595
- package/lib/d2/attribute_enhancer.js.map +1 -1
- 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 +873 -873
- 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 +243 -243
- package/lib/d2/types.d.ts +439 -438
- package/lib/d2/types.js +14 -14
- package/lib/d2/types.js.map +1 -1
- 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 -46
package/lib/d2/items.js
CHANGED
|
@@ -1,874 +1,874 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._writeMagicProperties = exports._readMagicProperties = exports.writeItem = exports.readItem = exports.writeItems = exports.readItems = exports.writeCorpseItem = exports.readCorpseItems = exports.writeDemon = exports.readDemon = exports.writeGolemItems = exports.readGolemItems = exports.writeMercItems = exports.readMercItems = exports.writeCharItems = exports.readCharItems = void 0;
|
|
4
|
-
const bitreader_1 = require("../binary/bitreader");
|
|
5
|
-
const bitwriter_1 = require("../binary/bitwriter");
|
|
6
|
-
var ItemType;
|
|
7
|
-
(function (ItemType) {
|
|
8
|
-
ItemType[ItemType["Armor"] = 1] = "Armor";
|
|
9
|
-
ItemType[ItemType["Shield"] = 2] = "Shield";
|
|
10
|
-
ItemType[ItemType["Weapon"] = 3] = "Weapon";
|
|
11
|
-
ItemType[ItemType["Other"] = 4] = "Other";
|
|
12
|
-
})(ItemType || (ItemType = {}));
|
|
13
|
-
var Quality;
|
|
14
|
-
(function (Quality) {
|
|
15
|
-
Quality[Quality["Low"] = 1] = "Low";
|
|
16
|
-
Quality[Quality["Normal"] = 2] = "Normal";
|
|
17
|
-
Quality[Quality["Superior"] = 3] = "Superior";
|
|
18
|
-
Quality[Quality["Magic"] = 4] = "Magic";
|
|
19
|
-
Quality[Quality["Set"] = 5] = "Set";
|
|
20
|
-
Quality[Quality["Rare"] = 6] = "Rare";
|
|
21
|
-
Quality[Quality["Unique"] = 7] = "Unique";
|
|
22
|
-
Quality[Quality["Crafted"] = 8] = "Crafted";
|
|
23
|
-
})(Quality || (Quality = {}));
|
|
24
|
-
// prettier-ignore
|
|
25
|
-
//huffman tree
|
|
26
|
-
const HUFFMAN = [[[[["w", "u"], [["8", ["y", ["5", ["j", []]]]], "h"]], ["s", [["2", "n"], "x"]]], [[["c", ["k", "f"]], "b"], [["t", "m"], ["9", "7"]]]], [" ", [[[["e", "d"], "p"], ["g", [[["z", "q"], "3"], ["v", "6"]]]], [["r", "l"], ["a", [["1", ["4", "0"]], ["i", "o"]]]]]]];
|
|
27
|
-
// prettier-ignore
|
|
28
|
-
const HUFFMAN_LOOKUP = { "0": { "v": 223, "l": 8 }, "1": { "v": 31, "l": 7 }, "2": { "v": 12, "l": 6 }, "3": { "v": 91, "l": 7 }, "4": { "v": 95, "l": 8 }, "5": { "v": 104, "l": 8 }, "6": { "v": 123, "l": 7 }, "7": { "v": 30, "l": 5 }, "8": { "v": 8, "l": 6 }, "9": { "v": 14, "l": 5 }, " ": { "v": 1, "l": 2 }, "a": { "v": 15, "l": 5 }, "b": { "v": 10, "l": 4 }, "c": { "v": 2, "l": 5 }, "d": { "v": 35, "l": 6 }, "e": { "v": 3, "l": 6 }, "f": { "v": 50, "l": 6 }, "g": { "v": 11, "l": 5 }, "h": { "v": 24, "l": 5 }, "i": { "v": 63, "l": 7 }, "j": { "v": 232, "l": 9 }, "k": { "v": 18, "l": 6 }, "l": { "v": 23, "l": 5 }, "m": { "v": 22, "l": 5 }, "n": { "v": 44, "l": 6 }, "o": { "v": 127, "l": 7 }, "p": { "v": 19, "l": 5 }, "q": { "v": 155, "l": 8 }, "r": { "v": 7, "l": 5 }, "s": { "v": 4, "l": 4 }, "t": { "v": 6, "l": 5 }, "u": { "v": 16, "l": 5 }, "v": { "v": 59, "l": 7 }, "w": { "v": 0, "l": 5 }, "x": { "v": 28, "l": 5 }, "y": { "v": 40, "l": 7 }, "z": { "v": 27, "l": 8 } };
|
|
29
|
-
async function readCharItems(char, reader, constants, config) {
|
|
30
|
-
char.items = await readItems(reader, char.header.version, constants, config, char);
|
|
31
|
-
}
|
|
32
|
-
exports.readCharItems = readCharItems;
|
|
33
|
-
async function writeCharItems(char, constants, config) {
|
|
34
|
-
const writer = new bitwriter_1.BitWriter();
|
|
35
|
-
writer.WriteArray(await writeItems(char.items, char.header.version, constants, config));
|
|
36
|
-
return writer.ToArray();
|
|
37
|
-
}
|
|
38
|
-
exports.writeCharItems = writeCharItems;
|
|
39
|
-
async function readMercItems(char, reader, constants, config) {
|
|
40
|
-
char.merc_items = [];
|
|
41
|
-
const header = reader.ReadString(2); //0x0000 [merc item list header = "jf"]
|
|
42
|
-
if (header !== "jf") {
|
|
43
|
-
// header is not present in first save after char is created
|
|
44
|
-
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
throw new Error(`Mercenary header 'jf' not found at position ${reader.offset - 2 * 8}`);
|
|
48
|
-
}
|
|
49
|
-
if (char.header.merc_id && parseInt(char.header.merc_id, 16) !== 0) {
|
|
50
|
-
char.merc_items = await readItems(reader, char.header.version, constants, config, char);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
exports.readMercItems = readMercItems;
|
|
54
|
-
async function writeMercItems(char, constants, config) {
|
|
55
|
-
const writer = new bitwriter_1.BitWriter();
|
|
56
|
-
writer.WriteString("jf", 2);
|
|
57
|
-
if (char.header.merc_id && parseInt(char.header.merc_id, 16) !== 0) {
|
|
58
|
-
char.merc_items = char.merc_items || [];
|
|
59
|
-
writer.WriteArray(await writeItems(char.merc_items, char.header.version, constants, config));
|
|
60
|
-
}
|
|
61
|
-
return writer.ToArray();
|
|
62
|
-
}
|
|
63
|
-
exports.writeMercItems = writeMercItems;
|
|
64
|
-
async function readGolemItems(char, reader, constants, config) {
|
|
65
|
-
const header = reader.ReadString(2); //0x0000 [golem item list header = "kf"]
|
|
66
|
-
if (header !== "kf") {
|
|
67
|
-
// header is not present in first save after char is created
|
|
68
|
-
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
throw new Error(`Golem header 'kf' not found at position ${reader.offset - 2 * 8}`);
|
|
72
|
-
}
|
|
73
|
-
const has_golem = reader.ReadUInt8();
|
|
74
|
-
if (has_golem === 1) {
|
|
75
|
-
char.golem_item = await readItem(reader, char.header.version, constants, config);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
exports.readGolemItems = readGolemItems;
|
|
79
|
-
async function writeGolemItems(char, constants, config) {
|
|
80
|
-
const writer = new bitwriter_1.BitWriter();
|
|
81
|
-
writer.WriteString("kf", 2);
|
|
82
|
-
if (char.golem_item) {
|
|
83
|
-
writer.WriteUInt8(1);
|
|
84
|
-
writer.WriteArray(await writeItem(char.golem_item, char.header.version, constants, config));
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
writer.WriteUInt8(0);
|
|
88
|
-
}
|
|
89
|
-
return writer.ToArray();
|
|
90
|
-
}
|
|
91
|
-
exports.writeGolemItems = writeGolemItems;
|
|
92
|
-
async function readDemon(char, reader, constants, config) {
|
|
93
|
-
reader.SkipBytes(2);
|
|
94
|
-
const header = reader.ReadString(2); // 0x6C 0x66 lf Demon header
|
|
95
|
-
if (header === "lf") {
|
|
96
|
-
const hasDemon = reader.ReadUInt8();
|
|
97
|
-
if (!hasDemon)
|
|
98
|
-
return;
|
|
99
|
-
char.demon = {
|
|
100
|
-
_unknown_data: {},
|
|
101
|
-
mods: [],
|
|
102
|
-
};
|
|
103
|
-
char.demon._unknown_data.b1_4 = reader.ReadBytes(3);
|
|
104
|
-
char.demon.isSuperUnique = reader.ReadUInt16() === 2 ? 1 : 0;
|
|
105
|
-
char.demon.index = reader.ReadUInt16();
|
|
106
|
-
char.demon._unknown_data.b9_15 = reader.ReadBytes(6);
|
|
107
|
-
char.demon.difficulty = reader.ReadUInt8();
|
|
108
|
-
char.demon._unknown_data.b17_28 = reader.ReadBytes(11);
|
|
109
|
-
char.demon.levelId = reader.ReadUInt16();
|
|
110
|
-
char.demon._unknown_data.b31_32 = reader.ReadBytes(2);
|
|
111
|
-
char.demon.level = reader.ReadUInt8();
|
|
112
|
-
char.demon.isDesecrated = reader.ReadUInt8();
|
|
113
|
-
char.demon._unknown_data.b35_57 = reader.ReadBytes(22);
|
|
114
|
-
char.demon.difficulty2 = reader.ReadUInt8();
|
|
115
|
-
char.demon._unknown_data.b59_61 = reader.ReadBytes(3);
|
|
116
|
-
char.demon.difficulty3 = reader.ReadUInt8();
|
|
117
|
-
char.demon._unknown_data.b63_86 = reader.ReadBytes(23);
|
|
118
|
-
for (let i = 0; i < 9; i++) {
|
|
119
|
-
char.demon.mods.push(reader.ReadUInt8());
|
|
120
|
-
}
|
|
121
|
-
char.demon.stats = Array.from(reader.ReadBytes((reader.bits.length - reader.offset) / 8));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
exports.readDemon = readDemon;
|
|
125
|
-
async function writeDemon(char, constants, config) {
|
|
126
|
-
const writer = new bitwriter_1.BitWriter();
|
|
127
|
-
writer.WriteArray(new Uint8Array([0x01, 0x00]));
|
|
128
|
-
writer.WriteString("lf", 2);
|
|
129
|
-
if (char.demon) {
|
|
130
|
-
writer.WriteUInt8(1);
|
|
131
|
-
writer.WriteArray(char.demon._unknown_data.b1_4);
|
|
132
|
-
writer.WriteUInt16(char.demon.isSuperUnique ? 2 : 1);
|
|
133
|
-
writer.WriteUInt16(char.demon.index);
|
|
134
|
-
writer.WriteArray(char.demon._unknown_data.b9_15);
|
|
135
|
-
writer.WriteUInt8(char.demon.difficulty);
|
|
136
|
-
writer.WriteArray(char.demon._unknown_data.b17_28);
|
|
137
|
-
writer.WriteUInt16(char.demon.levelId);
|
|
138
|
-
writer.WriteArray(char.demon._unknown_data.b31_32);
|
|
139
|
-
writer.WriteUInt8(char.demon.level);
|
|
140
|
-
writer.WriteUInt8(char.demon.isDesecrated);
|
|
141
|
-
writer.WriteArray(char.demon._unknown_data.b35_57);
|
|
142
|
-
writer.WriteUInt8(char.demon.difficulty2);
|
|
143
|
-
writer.WriteArray(char.demon._unknown_data.b59_61);
|
|
144
|
-
writer.WriteUInt8(char.demon.difficulty3);
|
|
145
|
-
writer.WriteArray(char.demon._unknown_data.b63_86);
|
|
146
|
-
for (let i = 0; i < 9; i++) {
|
|
147
|
-
writer.WriteUInt8(char.demon.mods[i] || 0);
|
|
148
|
-
}
|
|
149
|
-
writer.WriteArray(new Uint8Array(char.demon.stats));
|
|
150
|
-
//writer.WriteBytes(new Uint8Array(char.demon.data));
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
writer.WriteArray(new Uint8Array([0x00, 0x00]));
|
|
154
|
-
}
|
|
155
|
-
return writer.ToArray();
|
|
156
|
-
}
|
|
157
|
-
exports.writeDemon = writeDemon;
|
|
158
|
-
async function readCorpseItems(char, reader, constants, config) {
|
|
159
|
-
char.corpse_items = [];
|
|
160
|
-
const header = reader.ReadString(2); //0x0000 [item list header = 0x4a, 0x4d "JM"]
|
|
161
|
-
if (header !== "JM") {
|
|
162
|
-
// header is not present in first save after char is created
|
|
163
|
-
if (char.header.level === 1) {
|
|
164
|
-
char.is_dead = 0;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
throw new Error(`Corpse header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
168
|
-
}
|
|
169
|
-
char.is_dead = reader.ReadUInt16(); //0x0002 [corpse count]
|
|
170
|
-
for (let i = 0; i < char.is_dead; i++) {
|
|
171
|
-
reader.SkipBytes(12); //0x0004 [unk4, x_pos, y_pos]
|
|
172
|
-
char.corpse_items = char.corpse_items.concat(await readItems(reader, char.header.version, constants, config, char));
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
exports.readCorpseItems = readCorpseItems;
|
|
176
|
-
async function writeCorpseItem(char, constants, config) {
|
|
177
|
-
const writer = new bitwriter_1.BitWriter();
|
|
178
|
-
writer.WriteString("JM", 2);
|
|
179
|
-
writer.WriteUInt16(char.is_dead);
|
|
180
|
-
//json struct doesnt support multiple corpses without modifying it
|
|
181
|
-
if (char.is_dead) {
|
|
182
|
-
writer.WriteArray(new Uint8Array(12));
|
|
183
|
-
char.corpse_items = char.corpse_items || [];
|
|
184
|
-
writer.WriteArray(await writeItems(char.corpse_items, char.header.version, constants, config));
|
|
185
|
-
}
|
|
186
|
-
return writer.ToArray();
|
|
187
|
-
}
|
|
188
|
-
exports.writeCorpseItem = writeCorpseItem;
|
|
189
|
-
async function readItems(reader, version, constants, config, char) {
|
|
190
|
-
const items = [];
|
|
191
|
-
const header = reader.ReadString(2); //0x0000 [item list header = 0x4a, 0x4d "JM"]
|
|
192
|
-
if (header !== "JM") {
|
|
193
|
-
// header is not present in first save after char is created
|
|
194
|
-
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
195
|
-
return []; // TODO: return starter items based on class
|
|
196
|
-
}
|
|
197
|
-
throw new Error(`Item list header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
198
|
-
}
|
|
199
|
-
const count = reader.ReadUInt16(); //0x0002
|
|
200
|
-
for (let i = 0; i < count; i++) {
|
|
201
|
-
/*if (version === 0x69) {
|
|
202
|
-
const bitStr = reader.bits.join("");
|
|
203
|
-
const searchStart = reader.offset;
|
|
204
|
-
const marker1 = "000000001 00001000 10100000".replace(/ /g, ""); // search for next item
|
|
205
|
-
const marker2 = "000000001 00000000 10100000".replace(/ /g, ""); // search for next item
|
|
206
|
-
const id1 = bitStr.indexOf(marker1, searchStart);
|
|
207
|
-
const id2 = bitStr.indexOf(marker2, searchStart);
|
|
208
|
-
const id = id1 !== -1 && id2 !== -1 ? Math.min(id1, id2) : Math.max(id1, id2);
|
|
209
|
-
reader.SeekBit(id - 15);
|
|
210
|
-
}*/
|
|
211
|
-
items.push(await readItem(reader, version, constants, config));
|
|
212
|
-
}
|
|
213
|
-
return items;
|
|
214
|
-
}
|
|
215
|
-
exports.readItems = readItems;
|
|
216
|
-
async function writeItems(items, version, constants, config) {
|
|
217
|
-
const writer = new bitwriter_1.BitWriter();
|
|
218
|
-
writer.WriteString("JM", 2);
|
|
219
|
-
writer.WriteUInt16(items.length);
|
|
220
|
-
for (let i = 0; i < items.length; i++) {
|
|
221
|
-
writer.WriteArray(await writeItem(items[i], version, constants, config));
|
|
222
|
-
}
|
|
223
|
-
return writer.ToArray();
|
|
224
|
-
}
|
|
225
|
-
exports.writeItems = writeItems;
|
|
226
|
-
async function readItem(reader, version, originalConstants, config, parent) {
|
|
227
|
-
if (version <= 0x60) {
|
|
228
|
-
const header = reader.ReadString(2); //0x0000 [item header = 0x4a, 0x4d "JM"]
|
|
229
|
-
if (header !== "JM") {
|
|
230
|
-
throw new Error(`Item header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const constants = originalConstants;
|
|
234
|
-
const item = {};
|
|
235
|
-
_readSimpleBits(item, reader, version, constants, config);
|
|
236
|
-
if (!item.simple_item) {
|
|
237
|
-
item.id = reader.ReadUInt32(32);
|
|
238
|
-
item.level = reader.ReadUInt8(7);
|
|
239
|
-
item.quality = reader.ReadUInt8(4);
|
|
240
|
-
item.multiple_pictures = reader.ReadBit();
|
|
241
|
-
if (item.multiple_pictures) {
|
|
242
|
-
item.picture_id = reader.ReadUInt8(3);
|
|
243
|
-
}
|
|
244
|
-
item.class_specific = reader.ReadBit();
|
|
245
|
-
if (item.class_specific) {
|
|
246
|
-
item.auto_affix_id = reader.ReadUInt16(11);
|
|
247
|
-
}
|
|
248
|
-
switch (item.quality) {
|
|
249
|
-
case Quality.Low:
|
|
250
|
-
item.low_quality_id = reader.ReadUInt8(3);
|
|
251
|
-
break;
|
|
252
|
-
case Quality.Normal:
|
|
253
|
-
break;
|
|
254
|
-
case Quality.Superior:
|
|
255
|
-
item.file_index = reader.ReadUInt8(3);
|
|
256
|
-
break;
|
|
257
|
-
case Quality.Magic:
|
|
258
|
-
item.magic_prefix = reader.ReadUInt16(11);
|
|
259
|
-
if (item.magic_prefix)
|
|
260
|
-
item.magic_prefix_name = constants.magic_prefixes[item.magic_prefix] ? constants.magic_prefixes[item.magic_prefix].n : null;
|
|
261
|
-
item.magic_suffix = reader.ReadUInt16(11);
|
|
262
|
-
if (item.magic_suffix)
|
|
263
|
-
item.magic_suffix_name = constants.magic_suffixes[item.magic_suffix] ? constants.magic_suffixes[item.magic_suffix].n : null;
|
|
264
|
-
break;
|
|
265
|
-
case Quality.Set:
|
|
266
|
-
item.set_id = reader.ReadUInt16(12);
|
|
267
|
-
item.set_name = constants.set_items[item.set_id] ? constants.set_items[item.set_id].n : null;
|
|
268
|
-
break;
|
|
269
|
-
case Quality.Unique:
|
|
270
|
-
item.unique_id = reader.ReadUInt16(12);
|
|
271
|
-
item.unique_name = constants.unq_items[item.unique_id] ? constants.unq_items[item.unique_id].n : null;
|
|
272
|
-
break;
|
|
273
|
-
case Quality.Rare:
|
|
274
|
-
case Quality.Crafted:
|
|
275
|
-
item.rare_name_id = reader.ReadUInt8(8);
|
|
276
|
-
if (item.rare_name_id)
|
|
277
|
-
item.rare_name = constants.rare_names[item.rare_name_id] ? constants.rare_names[item.rare_name_id].n : null;
|
|
278
|
-
item.rare_name_id2 = reader.ReadUInt8(8);
|
|
279
|
-
if (item.rare_name_id2)
|
|
280
|
-
item.rare_name2 = constants.rare_names[item.rare_name_id2] ? constants.rare_names[item.rare_name_id2].n : null;
|
|
281
|
-
item.magical_name_ids = [];
|
|
282
|
-
for (let i = 0; i < 6; i++) {
|
|
283
|
-
const prefix = reader.ReadBit();
|
|
284
|
-
if (prefix === 1) {
|
|
285
|
-
item.magical_name_ids[i] = reader.ReadUInt16(11);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
item.magical_name_ids[i] = null;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
break;
|
|
292
|
-
default:
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
if (item.given_runeword) {
|
|
296
|
-
item.runeword_id = reader.ReadUInt16(12);
|
|
297
|
-
//fix delerium on d2gs??? why is this a thing?
|
|
298
|
-
if (item.runeword_id == 2718) {
|
|
299
|
-
item.runeword_id = 48;
|
|
300
|
-
}
|
|
301
|
-
const mappedId = {
|
|
302
|
-
2784: 196,
|
|
303
|
-
2785: 197,
|
|
304
|
-
2786: 198,
|
|
305
|
-
2787: 199,
|
|
306
|
-
2788: 200,
|
|
307
|
-
2789: 201,
|
|
308
|
-
2790: 202,
|
|
309
|
-
2791: 203,
|
|
310
|
-
2792: 204,
|
|
311
|
-
3074: 205,
|
|
312
|
-
3075: 206,
|
|
313
|
-
3076: 207,
|
|
314
|
-
}[item.runeword_id] || item.runeword_id;
|
|
315
|
-
if (constants.runewords[mappedId]) {
|
|
316
|
-
item.runeword_name = constants.runewords[mappedId].n;
|
|
317
|
-
}
|
|
318
|
-
reader.ReadUInt8(4);
|
|
319
|
-
}
|
|
320
|
-
if (item.personalized) {
|
|
321
|
-
const arr = new Uint8Array(16);
|
|
322
|
-
for (let i = 0; i < arr.length; i++) {
|
|
323
|
-
if (version > 0x61) {
|
|
324
|
-
arr[i] = reader.ReadUInt8(8);
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
arr[i] = reader.ReadUInt8(7);
|
|
328
|
-
}
|
|
329
|
-
if (arr[i] === 0x00) {
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
item.personalized_name = new bitreader_1.BitReader(arr).ReadString(16).trim().replace(/\0/g, "");
|
|
334
|
-
}
|
|
335
|
-
//tomes
|
|
336
|
-
if (item.type === "tbk" || item.type == "ibk") {
|
|
337
|
-
reader.ReadUInt8(5);
|
|
338
|
-
}
|
|
339
|
-
//realm data
|
|
340
|
-
item.timestamp = reader.ReadUInt8(1);
|
|
341
|
-
if (item.type_id === ItemType.Armor) {
|
|
342
|
-
item.defense_rating = reader.ReadUInt16(constants.magical_properties[31].sB) - constants.magical_properties[31].sA;
|
|
343
|
-
}
|
|
344
|
-
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Weapon) {
|
|
345
|
-
item.max_durability = reader.ReadUInt16(constants.magical_properties[73].sB) - constants.magical_properties[73].sA;
|
|
346
|
-
if (item.max_durability > 0) {
|
|
347
|
-
item.current_durability = reader.ReadUInt16(constants.magical_properties[72].sB) - constants.magical_properties[72].sA;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (version === 0x69) {
|
|
351
|
-
reader.SkipBits(1); //unknown bit in RotW
|
|
352
|
-
}
|
|
353
|
-
if (constants.stackables[item.type]) {
|
|
354
|
-
item.quantity = reader.ReadUInt16(9);
|
|
355
|
-
}
|
|
356
|
-
if (item.socketed === 1) {
|
|
357
|
-
item.total_nr_of_sockets = reader.ReadUInt8(4);
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* 5 bits. any of the 5 bits can be set. if a bit is set that means
|
|
361
|
-
* means +1 to the set_list_count
|
|
362
|
-
*/
|
|
363
|
-
let plist_flag = 0;
|
|
364
|
-
if (item.quality === Quality.Set) {
|
|
365
|
-
plist_flag = reader.ReadUInt8(5);
|
|
366
|
-
item.set_list_count = 0;
|
|
367
|
-
item._unknown_data.plist_flag = plist_flag;
|
|
368
|
-
}
|
|
369
|
-
//magical properties
|
|
370
|
-
const skipMagicPeroperties = ["qll", "spe", "sol", "fng", "jaw", "scz", "flg", "tal"].includes(item.type);
|
|
371
|
-
let magic_attributes = _readMagicProperties(reader, constants, skipMagicPeroperties);
|
|
372
|
-
item.magic_attributes = magic_attributes;
|
|
373
|
-
while (plist_flag > 0) {
|
|
374
|
-
if (plist_flag & 1) {
|
|
375
|
-
item.set_list_count += 1;
|
|
376
|
-
magic_attributes = _readMagicProperties(reader, constants);
|
|
377
|
-
if (item.set_attributes) {
|
|
378
|
-
item.set_attributes.push(magic_attributes);
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
item.set_attributes = [magic_attributes];
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
plist_flag >>>= 1;
|
|
385
|
-
}
|
|
386
|
-
if (item.given_runeword === 1) {
|
|
387
|
-
magic_attributes = _readMagicProperties(reader, constants);
|
|
388
|
-
if (magic_attributes && magic_attributes.length > 0) {
|
|
389
|
-
item.runeword_attributes = magic_attributes;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
/*if (item._unknown_data.b27_31 && item._unknown_data.b27_31?.some((b) => b === 1)) {
|
|
394
|
-
reader.SkipBits(53); // skip 50 - 53
|
|
395
|
-
reader.Align();
|
|
396
|
-
}*/
|
|
397
|
-
if (version === 0x69) {
|
|
398
|
-
if (!item.identified && (item.quality === Quality.Unique || item.quality === Quality.Set)) {
|
|
399
|
-
item.chronicle = {
|
|
400
|
-
monsterId: reader.ReadUInt16(),
|
|
401
|
-
timestamp: reader.ReadUInt32(),
|
|
402
|
-
};
|
|
403
|
-
reader.SkipBits(4);
|
|
404
|
-
}
|
|
405
|
-
item._unknown_data.chest_stackable = reader.ReadBit(); //unknown bit in RotW (stackable in chest?)
|
|
406
|
-
if (item._unknown_data.chest_stackable) {
|
|
407
|
-
item.amount_in_shared_stash = reader.ReadUInt8();
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
reader.Align();
|
|
411
|
-
if (item.nr_of_items_in_sockets > 0 && item.simple_item === 0) {
|
|
412
|
-
item.socketed_items = [];
|
|
413
|
-
for (let i = 0; i < item.nr_of_items_in_sockets; i++) {
|
|
414
|
-
item.socketed_items.push(await readItem(reader, version, constants, config, item));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
//console.log(JSON.stringify(item));
|
|
418
|
-
return item;
|
|
419
|
-
}
|
|
420
|
-
exports.readItem = readItem;
|
|
421
|
-
async function writeItem(item, version, constants, config) {
|
|
422
|
-
var _a;
|
|
423
|
-
if (item._unknown_data === undefined) {
|
|
424
|
-
item._unknown_data = {};
|
|
425
|
-
}
|
|
426
|
-
if (item.categories === undefined) {
|
|
427
|
-
item.categories = (_a = _GetItemTXT(item, constants)) === null || _a === void 0 ? void 0 : _a.c;
|
|
428
|
-
}
|
|
429
|
-
const writer = new bitwriter_1.BitWriter();
|
|
430
|
-
if (version <= 0x60) {
|
|
431
|
-
writer.WriteString("JM", 2);
|
|
432
|
-
}
|
|
433
|
-
_writeSimpleBits(writer, version, item, constants, config);
|
|
434
|
-
if (!item.simple_item) {
|
|
435
|
-
writer.WriteUInt32(item.id, 32);
|
|
436
|
-
writer.WriteUInt8(item.level, 7);
|
|
437
|
-
writer.WriteUInt8(item.quality, 4);
|
|
438
|
-
writer.WriteUInt8(item.multiple_pictures, 1);
|
|
439
|
-
if (item.multiple_pictures) {
|
|
440
|
-
writer.WriteUInt8(item.picture_id, 3);
|
|
441
|
-
}
|
|
442
|
-
writer.WriteUInt8(item.class_specific, 1);
|
|
443
|
-
if (item.class_specific === 1) {
|
|
444
|
-
writer.WriteUInt16(item.auto_affix_id || 0, 11);
|
|
445
|
-
}
|
|
446
|
-
switch (item.quality) {
|
|
447
|
-
case Quality.Low:
|
|
448
|
-
writer.WriteUInt8(item.low_quality_id, 3);
|
|
449
|
-
break;
|
|
450
|
-
case Quality.Normal:
|
|
451
|
-
break;
|
|
452
|
-
case Quality.Superior:
|
|
453
|
-
writer.WriteUInt8(item.file_index || 0, 3);
|
|
454
|
-
break;
|
|
455
|
-
case Quality.Magic:
|
|
456
|
-
writer.WriteUInt16(item.magic_prefix, 11);
|
|
457
|
-
writer.WriteUInt16(item.magic_suffix, 11);
|
|
458
|
-
break;
|
|
459
|
-
case Quality.Set:
|
|
460
|
-
writer.WriteUInt16(item.set_id, 12);
|
|
461
|
-
break;
|
|
462
|
-
case Quality.Unique:
|
|
463
|
-
writer.WriteUInt16(item.unique_id, 12);
|
|
464
|
-
break;
|
|
465
|
-
case Quality.Rare:
|
|
466
|
-
case Quality.Crafted:
|
|
467
|
-
writer.WriteUInt8(item.rare_name_id !== undefined ? item.rare_name_id : _lookupRareId(item.rare_name, constants), 8);
|
|
468
|
-
writer.WriteUInt8(item.rare_name_id2 !== undefined ? item.rare_name_id2 : _lookupRareId(item.rare_name2, constants), 8);
|
|
469
|
-
for (let i = 0; i < 6; i++) {
|
|
470
|
-
const magical_name_id = item.magical_name_ids[i];
|
|
471
|
-
if (magical_name_id) {
|
|
472
|
-
writer.WriteBit(1);
|
|
473
|
-
writer.WriteUInt16(magical_name_id, 11);
|
|
474
|
-
}
|
|
475
|
-
else {
|
|
476
|
-
writer.WriteBit(0);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
break;
|
|
480
|
-
default:
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
if (item.given_runeword) {
|
|
484
|
-
//fix delerium on d2gs??? why is this a thing?
|
|
485
|
-
let runeword_id = item.runeword_id;
|
|
486
|
-
if (runeword_id == 2718) {
|
|
487
|
-
runeword_id = 48;
|
|
488
|
-
}
|
|
489
|
-
writer.WriteUInt16(runeword_id, 12);
|
|
490
|
-
writer.WriteUInt8(5, 4); //always 5?
|
|
491
|
-
}
|
|
492
|
-
if (item.personalized) {
|
|
493
|
-
const name = item.personalized_name.substring(0, 16);
|
|
494
|
-
for (let i = 0; i < name.length; i++) {
|
|
495
|
-
if (version > 0x61) {
|
|
496
|
-
writer.WriteUInt8(name.charCodeAt(i), 8);
|
|
497
|
-
}
|
|
498
|
-
else {
|
|
499
|
-
writer.WriteUInt8(name.charCodeAt(i) & 0x7f, 7);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
writer.WriteUInt8(0x00, version > 0x61 ? 8 : 7);
|
|
503
|
-
}
|
|
504
|
-
if (item.type === "tbk") {
|
|
505
|
-
writer.WriteUInt8(0, 5);
|
|
506
|
-
}
|
|
507
|
-
else if (item.type === "ibk") {
|
|
508
|
-
writer.WriteUInt8(1, 5);
|
|
509
|
-
}
|
|
510
|
-
writer.WriteUInt8(item.timestamp, 1);
|
|
511
|
-
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Shield) {
|
|
512
|
-
writer.WriteUInt16(item.defense_rating + constants.magical_properties[31].sA, constants.magical_properties[31].sB);
|
|
513
|
-
}
|
|
514
|
-
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Shield || item.type_id === ItemType.Weapon) {
|
|
515
|
-
writer.WriteUInt16(item.max_durability || 0, constants.magical_properties[73].sB);
|
|
516
|
-
if (item.max_durability > 0) {
|
|
517
|
-
writer.WriteUInt16(item.current_durability, constants.magical_properties[72].sB);
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
if (version === 0x69) {
|
|
521
|
-
writer.WriteUInt8(constants.stackables[item.type] ? 1 : 0, 1); //unknown bit in RotW
|
|
522
|
-
}
|
|
523
|
-
if (constants.stackables[item.type]) {
|
|
524
|
-
writer.WriteUInt16(item.quantity, 9);
|
|
525
|
-
}
|
|
526
|
-
if (item.socketed === 1) {
|
|
527
|
-
writer.WriteUInt8(item.total_nr_of_sockets, 4);
|
|
528
|
-
}
|
|
529
|
-
if (item.quality === Quality.Set) {
|
|
530
|
-
const set_attribute_count = item.set_attributes != null ? item.set_attributes.length : 0;
|
|
531
|
-
//reduced by -1 removed as this seems to be wrong
|
|
532
|
-
const plist_flag = (1 << set_attribute_count) - 1;
|
|
533
|
-
writer.WriteUInt8(item._unknown_data.plist_flag || plist_flag, 5);
|
|
534
|
-
}
|
|
535
|
-
_writeMagicProperties(writer, item.magic_attributes, constants);
|
|
536
|
-
if (item.set_attributes && item.set_attributes.length > 0) {
|
|
537
|
-
for (let i = 0; i < item.set_attributes.length; i++) {
|
|
538
|
-
_writeMagicProperties(writer, item.set_attributes[i], constants);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
if (item.given_runeword === 1) {
|
|
542
|
-
_writeMagicProperties(writer, item.runeword_attributes, constants);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (version === 0x69) {
|
|
546
|
-
if (!item.identified && (item.quality === Quality.Unique || item.quality === Quality.Set) && item.chronicle) {
|
|
547
|
-
writer.WriteUInt16(item.chronicle.monsterId);
|
|
548
|
-
writer.WriteUInt32(item.chronicle.timestamp);
|
|
549
|
-
writer.WriteUInt8(0, 4); //unknown bit in RotW
|
|
550
|
-
}
|
|
551
|
-
writer.WriteUInt8(item._unknown_data.chest_stackable || 0, 1);
|
|
552
|
-
if (item._unknown_data.chest_stackable) {
|
|
553
|
-
writer.WriteUInt8(item.amount_in_shared_stash || 0, 8); //unknown bit in RotW (stackable in chest?)
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
writer.Align();
|
|
557
|
-
if (item.nr_of_items_in_sockets > 0 && item.simple_item === 0) {
|
|
558
|
-
for (let i = 0; i < item.nr_of_items_in_sockets; i++) {
|
|
559
|
-
writer.WriteArray(await writeItem(item.socketed_items[i], version, constants, config));
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return writer.ToArray();
|
|
563
|
-
}
|
|
564
|
-
exports.writeItem = writeItem;
|
|
565
|
-
function _readSimpleBits(item, reader, version, constants, config) {
|
|
566
|
-
var _a, _b, _c;
|
|
567
|
-
//init so we do not have npe's
|
|
568
|
-
item._unknown_data = {};
|
|
569
|
-
//1.10-1.14d
|
|
570
|
-
//[flags:32][version:10][mode:3]([invloc:4][x:4][y:4][page:3])([itemcode:32])([sockets:3])
|
|
571
|
-
//1.15
|
|
572
|
-
//[flags:32][version:3][mode:3]([invloc:4][x:4][y:4][page:3])([itemcode:variable])([sockets:3])
|
|
573
|
-
item._unknown_data.b0_3 = reader.ReadBitArray(4); // 0 byte 1
|
|
574
|
-
item.identified = reader.ReadBit(); // 4
|
|
575
|
-
item._unknown_data.b5_10 = reader.ReadBitArray(6); // 5
|
|
576
|
-
item.socketed = reader.ReadBit(); // 11
|
|
577
|
-
item._unknown_data.b12 = reader.ReadBitArray(1); // 12
|
|
578
|
-
item.new = reader.ReadBit(); // 13
|
|
579
|
-
item._unknown_data.b14_15 = reader.ReadBitArray(2); // 14
|
|
580
|
-
item.is_ear = reader.ReadBit(); // 16 byte 3
|
|
581
|
-
item.starter_item = reader.ReadBit(); // 17
|
|
582
|
-
item._unknown_data.b18_20 = reader.ReadBitArray(3); // 18
|
|
583
|
-
item.simple_item = reader.ReadBit(); // 21
|
|
584
|
-
item.ethereal = reader.ReadBit(); // 22
|
|
585
|
-
item._unknown_data.b23 = reader.ReadBitArray(1); // 23
|
|
586
|
-
item.personalized = reader.ReadBit(); // 24 byte 4
|
|
587
|
-
item._unknown_data.b25 = reader.ReadBitArray(1); // 25
|
|
588
|
-
item.given_runeword = reader.ReadBit(); // 26
|
|
589
|
-
item._unknown_data.b27_31 = reader.ReadBitArray(5); // 27
|
|
590
|
-
if (version <= 0x60) {
|
|
591
|
-
item.version = reader.ReadUInt16(10).toString(10);
|
|
592
|
-
}
|
|
593
|
-
else if (version >= 0x61) {
|
|
594
|
-
item.version = reader.ReadUInt16(3).toString(2); // 32 byte 5
|
|
595
|
-
}
|
|
596
|
-
item.location_id = reader.ReadUInt8(3); // 34
|
|
597
|
-
item.equipped_id = reader.ReadUInt8(4); // 37
|
|
598
|
-
item.position_x = reader.ReadUInt8(4); // 41
|
|
599
|
-
item.position_y = reader.ReadUInt8(4); // 45
|
|
600
|
-
item.alt_position_id = reader.ReadUInt8(3); // 49
|
|
601
|
-
if (item.is_ear) {
|
|
602
|
-
const clazz = reader.ReadUInt8(3);
|
|
603
|
-
const level = reader.ReadUInt8(7);
|
|
604
|
-
const arr = new Uint8Array(15);
|
|
605
|
-
for (let i = 0; i < arr.length; i++) {
|
|
606
|
-
arr[i] = reader.ReadUInt8(7);
|
|
607
|
-
if (arr[i] === 0x00) {
|
|
608
|
-
break;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
const name = new bitreader_1.BitReader(arr).ReadString(15).trim().replace(/\0/g, "");
|
|
612
|
-
item.ear_attributes = {
|
|
613
|
-
class: clazz,
|
|
614
|
-
level: level,
|
|
615
|
-
name: name,
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
if (version <= 0x60) {
|
|
620
|
-
item.type = reader.ReadString(4);
|
|
621
|
-
}
|
|
622
|
-
else if (version >= 0x61) {
|
|
623
|
-
item.type = "";
|
|
624
|
-
//props to d07riv
|
|
625
|
-
//https://github.com/d07RiV/d07riv.github.io/blob/master/d2r.html#L11-L20
|
|
626
|
-
for (let i = 0; i < 4; i++) {
|
|
627
|
-
let node = HUFFMAN;
|
|
628
|
-
do {
|
|
629
|
-
node = node[reader.ReadBit()]; // 50
|
|
630
|
-
} while (Array.isArray(node));
|
|
631
|
-
item.type += node;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
item.type = item.type.trim().replace(/\0/g, "");
|
|
635
|
-
let details = _GetItemTXT(item, constants);
|
|
636
|
-
item.categories = details === null || details === void 0 ? void 0 : details.c;
|
|
637
|
-
if ((_a = item === null || item === void 0 ? void 0 : item.categories) === null || _a === void 0 ? void 0 : _a.includes("Any Armor")) {
|
|
638
|
-
item.type_id = ItemType.Armor;
|
|
639
|
-
}
|
|
640
|
-
else if ((_b = item === null || item === void 0 ? void 0 : item.categories) === null || _b === void 0 ? void 0 : _b.includes("Weapon")) {
|
|
641
|
-
item.type_id = ItemType.Weapon;
|
|
642
|
-
details = constants.weapon_items[item.type];
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
item.type_id = ItemType.Other;
|
|
646
|
-
}
|
|
647
|
-
let bits = item.simple_item ? 1 : 3;
|
|
648
|
-
if ((_c = item.categories) === null || _c === void 0 ? void 0 : _c.includes("Quest")) {
|
|
649
|
-
item.quest_difficulty = reader.ReadUInt16(constants.magical_properties[356].sB) - constants.magical_properties[356].sA;
|
|
650
|
-
bits = 1;
|
|
651
|
-
}
|
|
652
|
-
item.nr_of_items_in_sockets = reader.ReadUInt8(bits);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
function _lookupRareId(name, constants) {
|
|
656
|
-
//some inconsistencies with txt data and nokka. so have to hack it with startsWith
|
|
657
|
-
return constants.rare_names.findIndex((k) => k && (k.n.toLowerCase().startsWith(name.toLowerCase()) || name.toLowerCase().startsWith(k.n.toLowerCase())));
|
|
658
|
-
}
|
|
659
|
-
function _writeSimpleBits(writer, version, item, constants, config) {
|
|
660
|
-
var _a;
|
|
661
|
-
writer.WriteBits(item._unknown_data.b0_3 || new Uint8Array(4), 4);
|
|
662
|
-
writer.WriteBit(item.identified);
|
|
663
|
-
writer.WriteBits(item._unknown_data.b5_10 || new Uint8Array(6), 6);
|
|
664
|
-
writer.WriteBit(item.socketed);
|
|
665
|
-
writer.WriteBits(item._unknown_data.b12 || new Uint8Array(1), 1);
|
|
666
|
-
writer.WriteBit(item.new);
|
|
667
|
-
writer.WriteBits(item._unknown_data.b14_15 || new Uint8Array(2), 2);
|
|
668
|
-
writer.WriteBit(item.is_ear);
|
|
669
|
-
writer.WriteBit(item.starter_item);
|
|
670
|
-
writer.WriteBits(item._unknown_data.b18_20 || new Uint8Array(3), 3);
|
|
671
|
-
writer.WriteBit(item.simple_item);
|
|
672
|
-
writer.WriteBit(item.ethereal);
|
|
673
|
-
writer.WriteBits(item._unknown_data.b23 || new Uint8Array([1]), 1); //always 1? IFLAG_JUSTSAVED
|
|
674
|
-
writer.WriteBit(item.personalized);
|
|
675
|
-
writer.WriteBits(item._unknown_data.b25 || new Uint8Array(1), 1); //IFLAG_LOWQUALITY
|
|
676
|
-
writer.WriteBit(item.given_runeword);
|
|
677
|
-
writer.WriteBits(item._unknown_data.b27_31 || new Uint8Array(5), 5);
|
|
678
|
-
const itemVersion = item.version != null ? item.version : "101";
|
|
679
|
-
if (version <= 0x60) {
|
|
680
|
-
// 0 = pre-1.08; 1 = 1.08/1.09 normal; 2 = 1.10 normal; 100 = 1.08/1.09 expansion; 101 = 1.10 expansion
|
|
681
|
-
writer.WriteUInt16(parseInt(itemVersion, 10), 10);
|
|
682
|
-
}
|
|
683
|
-
else if (version >= 0x61) {
|
|
684
|
-
writer.WriteUInt16(parseInt(itemVersion, 2), 3);
|
|
685
|
-
}
|
|
686
|
-
writer.WriteUInt8(item.location_id, 3);
|
|
687
|
-
writer.WriteUInt8(item.equipped_id, 4);
|
|
688
|
-
writer.WriteUInt8(item.position_x, 4);
|
|
689
|
-
writer.WriteUInt8(item.position_y, 4);
|
|
690
|
-
writer.WriteUInt8(item.alt_position_id, 3);
|
|
691
|
-
if (item.is_ear) {
|
|
692
|
-
writer.WriteUInt8(item.ear_attributes.class, 3);
|
|
693
|
-
writer.WriteUInt8(item.ear_attributes.level, 7);
|
|
694
|
-
const name = item.ear_attributes.name.substring(0, 15);
|
|
695
|
-
for (let i = 0; i < name.length; i++) {
|
|
696
|
-
writer.WriteUInt8(name.charCodeAt(i) & 0x7f, 7);
|
|
697
|
-
}
|
|
698
|
-
writer.WriteUInt8(0x00, 7);
|
|
699
|
-
}
|
|
700
|
-
else {
|
|
701
|
-
const t = item.type.padEnd(4, " ");
|
|
702
|
-
if (version <= 0x60) {
|
|
703
|
-
writer.WriteString(t, 4);
|
|
704
|
-
}
|
|
705
|
-
else {
|
|
706
|
-
for (const c of t) {
|
|
707
|
-
const n = HUFFMAN_LOOKUP[c];
|
|
708
|
-
writer.WriteUInt16(n.v, n.l);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
let bits = item.simple_item ? 1 : 3;
|
|
712
|
-
if ((_a = item.categories) === null || _a === void 0 ? void 0 : _a.includes("Quest")) {
|
|
713
|
-
const difficulty = item.quest_difficulty || 0;
|
|
714
|
-
writer.WriteUInt16(difficulty + constants.magical_properties[356].sA, constants.magical_properties[356].sB);
|
|
715
|
-
bits = 1;
|
|
716
|
-
}
|
|
717
|
-
writer.WriteUInt8(item.nr_of_items_in_sockets, bits);
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
function _readMagicProperties(reader, constants, skipMagicProps) {
|
|
721
|
-
if (skipMagicProps) {
|
|
722
|
-
// Scan for the 0x1FF end marker (9 bits of 1)
|
|
723
|
-
const bitStr = reader.bits.join("");
|
|
724
|
-
const searchStart = reader.offset;
|
|
725
|
-
const marker = "111111111"; // 9-bit 0x1FF marker
|
|
726
|
-
const idx = bitStr.indexOf(marker, searchStart);
|
|
727
|
-
if (idx !== -1) {
|
|
728
|
-
reader.SeekBit(idx + 9); // Move reader past the 0x1FF marker
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
console.warn("0x1FF end marker not found, skipping failed");
|
|
732
|
-
}
|
|
733
|
-
return []; // Return empty list since we're skipping parsing
|
|
734
|
-
}
|
|
735
|
-
let id = reader.ReadUInt16(9);
|
|
736
|
-
const magic_attributes = [];
|
|
737
|
-
let zeroCounter = 0;
|
|
738
|
-
while (id != 0x1ff) {
|
|
739
|
-
if (id === 0) {
|
|
740
|
-
zeroCounter++;
|
|
741
|
-
}
|
|
742
|
-
if (zeroCounter > 10) {
|
|
743
|
-
throw new Error(`Possible malformed data at position ${reader.offset - 9}`);
|
|
744
|
-
}
|
|
745
|
-
const values = [];
|
|
746
|
-
if (id > constants.magical_properties.length) {
|
|
747
|
-
throw new Error(`Invalid Stat Id: ${id} at position ${reader.offset - 9}`);
|
|
748
|
-
}
|
|
749
|
-
const num_of_properties = constants.magical_properties[id].np || 1;
|
|
750
|
-
for (let i = 0; i < num_of_properties; i++) {
|
|
751
|
-
const prop = constants.magical_properties[id + i];
|
|
752
|
-
if (prop == null) {
|
|
753
|
-
throw new Error(`Cannot find Magical Property for id: ${id} at position ${reader.offset}`);
|
|
754
|
-
}
|
|
755
|
-
if (prop.sP) {
|
|
756
|
-
let param = reader.ReadUInt16(prop.sP);
|
|
757
|
-
switch (prop.dF) {
|
|
758
|
-
case 14: //+skill to skilltab
|
|
759
|
-
values.push(param & 0x7);
|
|
760
|
-
param = (param >> 3) & 0x1fff;
|
|
761
|
-
break;
|
|
762
|
-
default:
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
//encode
|
|
766
|
-
switch (prop.e) {
|
|
767
|
-
case 1:
|
|
768
|
-
//throw new Error(`Unimplemented encoding: ${prop.encode}`);
|
|
769
|
-
break;
|
|
770
|
-
case 2: //chance to cast
|
|
771
|
-
case 3: //charges
|
|
772
|
-
values.push(param & 0x3f); //skill level
|
|
773
|
-
param = (param >> 6) & 0x3ff; //skll id
|
|
774
|
-
break;
|
|
775
|
-
default:
|
|
776
|
-
break;
|
|
777
|
-
}
|
|
778
|
-
values.push(param);
|
|
779
|
-
}
|
|
780
|
-
if (!prop.sB) {
|
|
781
|
-
throw new Error(`Save Bits is undefined for stat: ${id}:${prop.s} at position ${reader.offset}`);
|
|
782
|
-
}
|
|
783
|
-
let v = reader.ReadUInt16(prop.sB);
|
|
784
|
-
if (prop.sA) {
|
|
785
|
-
v -= prop.sA;
|
|
786
|
-
}
|
|
787
|
-
switch (prop.e) {
|
|
788
|
-
case 3:
|
|
789
|
-
values.push(v & 0xff); // current charges
|
|
790
|
-
values.push((v >> 8) & 0xff); //max charges
|
|
791
|
-
break;
|
|
792
|
-
default:
|
|
793
|
-
values.push(v);
|
|
794
|
-
break;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
magic_attributes.push({
|
|
798
|
-
id: id,
|
|
799
|
-
values: values,
|
|
800
|
-
name: constants.magical_properties[id].s,
|
|
801
|
-
});
|
|
802
|
-
id = reader.ReadUInt16(9);
|
|
803
|
-
}
|
|
804
|
-
return magic_attributes;
|
|
805
|
-
}
|
|
806
|
-
exports._readMagicProperties = _readMagicProperties;
|
|
807
|
-
function _writeMagicProperties(writer, properties, constants) {
|
|
808
|
-
if (properties) {
|
|
809
|
-
for (let i = 0; i < properties.length; i++) {
|
|
810
|
-
const property = properties[i];
|
|
811
|
-
let valueIdx = 0;
|
|
812
|
-
writer.WriteUInt16(property.id, 9);
|
|
813
|
-
const num_of_properties = constants.magical_properties[property.id].np || 1;
|
|
814
|
-
for (let j = 0; j < num_of_properties; j++) {
|
|
815
|
-
const prop = constants.magical_properties[property.id + j];
|
|
816
|
-
if (prop == null) {
|
|
817
|
-
throw new Error(`Cannot find Magical Property for id: ${property.id}`);
|
|
818
|
-
}
|
|
819
|
-
if (prop.sP) {
|
|
820
|
-
let param = property.values[valueIdx++];
|
|
821
|
-
switch (prop.dF) {
|
|
822
|
-
case 14: //+skill to skilltab
|
|
823
|
-
param |= (property.values[valueIdx++] & 0x1fff) << 3;
|
|
824
|
-
break;
|
|
825
|
-
default:
|
|
826
|
-
break;
|
|
827
|
-
}
|
|
828
|
-
//encode
|
|
829
|
-
switch (prop.e) {
|
|
830
|
-
case 1:
|
|
831
|
-
//throw new Error(`Unimplemented encoding: ${prop.encode}`);
|
|
832
|
-
break;
|
|
833
|
-
case 2: //chance to cast
|
|
834
|
-
case 3: //charges
|
|
835
|
-
param |= (property.values[valueIdx++] & 0x3ff) << 6;
|
|
836
|
-
break;
|
|
837
|
-
default:
|
|
838
|
-
break;
|
|
839
|
-
}
|
|
840
|
-
writer.WriteUInt32(param, prop.sP);
|
|
841
|
-
}
|
|
842
|
-
let v = property.values[valueIdx++];
|
|
843
|
-
if (prop.sA) {
|
|
844
|
-
v += prop.sA;
|
|
845
|
-
}
|
|
846
|
-
switch (prop.e) {
|
|
847
|
-
case 3:
|
|
848
|
-
v |= (property.values[valueIdx++] & 0xff) << 8;
|
|
849
|
-
break;
|
|
850
|
-
default:
|
|
851
|
-
break;
|
|
852
|
-
}
|
|
853
|
-
if (!prop.sB) {
|
|
854
|
-
throw new Error(`Save Bits is undefined for stat: ${property.id}:${prop.s}`);
|
|
855
|
-
}
|
|
856
|
-
writer.WriteUInt32(v, prop.sB);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
writer.WriteUInt16(0x1ff, 9);
|
|
861
|
-
}
|
|
862
|
-
exports._writeMagicProperties = _writeMagicProperties;
|
|
863
|
-
function _GetItemTXT(item, constants) {
|
|
864
|
-
if (constants.armor_items[item.type]) {
|
|
865
|
-
return constants.armor_items[item.type];
|
|
866
|
-
}
|
|
867
|
-
else if (constants.weapon_items[item.type]) {
|
|
868
|
-
return constants.weapon_items[item.type];
|
|
869
|
-
}
|
|
870
|
-
else if (constants.other_items[item.type]) {
|
|
871
|
-
return constants.other_items[item.type];
|
|
872
|
-
}
|
|
873
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._writeMagicProperties = exports._readMagicProperties = exports.writeItem = exports.readItem = exports.writeItems = exports.readItems = exports.writeCorpseItem = exports.readCorpseItems = exports.writeDemon = exports.readDemon = exports.writeGolemItems = exports.readGolemItems = exports.writeMercItems = exports.readMercItems = exports.writeCharItems = exports.readCharItems = void 0;
|
|
4
|
+
const bitreader_1 = require("../binary/bitreader");
|
|
5
|
+
const bitwriter_1 = require("../binary/bitwriter");
|
|
6
|
+
var ItemType;
|
|
7
|
+
(function (ItemType) {
|
|
8
|
+
ItemType[ItemType["Armor"] = 1] = "Armor";
|
|
9
|
+
ItemType[ItemType["Shield"] = 2] = "Shield";
|
|
10
|
+
ItemType[ItemType["Weapon"] = 3] = "Weapon";
|
|
11
|
+
ItemType[ItemType["Other"] = 4] = "Other";
|
|
12
|
+
})(ItemType || (ItemType = {}));
|
|
13
|
+
var Quality;
|
|
14
|
+
(function (Quality) {
|
|
15
|
+
Quality[Quality["Low"] = 1] = "Low";
|
|
16
|
+
Quality[Quality["Normal"] = 2] = "Normal";
|
|
17
|
+
Quality[Quality["Superior"] = 3] = "Superior";
|
|
18
|
+
Quality[Quality["Magic"] = 4] = "Magic";
|
|
19
|
+
Quality[Quality["Set"] = 5] = "Set";
|
|
20
|
+
Quality[Quality["Rare"] = 6] = "Rare";
|
|
21
|
+
Quality[Quality["Unique"] = 7] = "Unique";
|
|
22
|
+
Quality[Quality["Crafted"] = 8] = "Crafted";
|
|
23
|
+
})(Quality || (Quality = {}));
|
|
24
|
+
// prettier-ignore
|
|
25
|
+
//huffman tree
|
|
26
|
+
const HUFFMAN = [[[[["w", "u"], [["8", ["y", ["5", ["j", []]]]], "h"]], ["s", [["2", "n"], "x"]]], [[["c", ["k", "f"]], "b"], [["t", "m"], ["9", "7"]]]], [" ", [[[["e", "d"], "p"], ["g", [[["z", "q"], "3"], ["v", "6"]]]], [["r", "l"], ["a", [["1", ["4", "0"]], ["i", "o"]]]]]]];
|
|
27
|
+
// prettier-ignore
|
|
28
|
+
const HUFFMAN_LOOKUP = { "0": { "v": 223, "l": 8 }, "1": { "v": 31, "l": 7 }, "2": { "v": 12, "l": 6 }, "3": { "v": 91, "l": 7 }, "4": { "v": 95, "l": 8 }, "5": { "v": 104, "l": 8 }, "6": { "v": 123, "l": 7 }, "7": { "v": 30, "l": 5 }, "8": { "v": 8, "l": 6 }, "9": { "v": 14, "l": 5 }, " ": { "v": 1, "l": 2 }, "a": { "v": 15, "l": 5 }, "b": { "v": 10, "l": 4 }, "c": { "v": 2, "l": 5 }, "d": { "v": 35, "l": 6 }, "e": { "v": 3, "l": 6 }, "f": { "v": 50, "l": 6 }, "g": { "v": 11, "l": 5 }, "h": { "v": 24, "l": 5 }, "i": { "v": 63, "l": 7 }, "j": { "v": 232, "l": 9 }, "k": { "v": 18, "l": 6 }, "l": { "v": 23, "l": 5 }, "m": { "v": 22, "l": 5 }, "n": { "v": 44, "l": 6 }, "o": { "v": 127, "l": 7 }, "p": { "v": 19, "l": 5 }, "q": { "v": 155, "l": 8 }, "r": { "v": 7, "l": 5 }, "s": { "v": 4, "l": 4 }, "t": { "v": 6, "l": 5 }, "u": { "v": 16, "l": 5 }, "v": { "v": 59, "l": 7 }, "w": { "v": 0, "l": 5 }, "x": { "v": 28, "l": 5 }, "y": { "v": 40, "l": 7 }, "z": { "v": 27, "l": 8 } };
|
|
29
|
+
async function readCharItems(char, reader, constants, config) {
|
|
30
|
+
char.items = await readItems(reader, char.header.version, constants, config, char);
|
|
31
|
+
}
|
|
32
|
+
exports.readCharItems = readCharItems;
|
|
33
|
+
async function writeCharItems(char, constants, config) {
|
|
34
|
+
const writer = new bitwriter_1.BitWriter();
|
|
35
|
+
writer.WriteArray(await writeItems(char.items, char.header.version, constants, config));
|
|
36
|
+
return writer.ToArray();
|
|
37
|
+
}
|
|
38
|
+
exports.writeCharItems = writeCharItems;
|
|
39
|
+
async function readMercItems(char, reader, constants, config) {
|
|
40
|
+
char.merc_items = [];
|
|
41
|
+
const header = reader.ReadString(2); //0x0000 [merc item list header = "jf"]
|
|
42
|
+
if (header !== "jf") {
|
|
43
|
+
// header is not present in first save after char is created
|
|
44
|
+
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(`Mercenary header 'jf' not found at position ${reader.offset - 2 * 8}`);
|
|
48
|
+
}
|
|
49
|
+
if (char.header.merc_id && parseInt(char.header.merc_id, 16) !== 0) {
|
|
50
|
+
char.merc_items = await readItems(reader, char.header.version, constants, config, char);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.readMercItems = readMercItems;
|
|
54
|
+
async function writeMercItems(char, constants, config) {
|
|
55
|
+
const writer = new bitwriter_1.BitWriter();
|
|
56
|
+
writer.WriteString("jf", 2);
|
|
57
|
+
if (char.header.merc_id && parseInt(char.header.merc_id, 16) !== 0) {
|
|
58
|
+
char.merc_items = char.merc_items || [];
|
|
59
|
+
writer.WriteArray(await writeItems(char.merc_items, char.header.version, constants, config));
|
|
60
|
+
}
|
|
61
|
+
return writer.ToArray();
|
|
62
|
+
}
|
|
63
|
+
exports.writeMercItems = writeMercItems;
|
|
64
|
+
async function readGolemItems(char, reader, constants, config) {
|
|
65
|
+
const header = reader.ReadString(2); //0x0000 [golem item list header = "kf"]
|
|
66
|
+
if (header !== "kf") {
|
|
67
|
+
// header is not present in first save after char is created
|
|
68
|
+
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Golem header 'kf' not found at position ${reader.offset - 2 * 8}`);
|
|
72
|
+
}
|
|
73
|
+
const has_golem = reader.ReadUInt8();
|
|
74
|
+
if (has_golem === 1) {
|
|
75
|
+
char.golem_item = await readItem(reader, char.header.version, constants, config);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.readGolemItems = readGolemItems;
|
|
79
|
+
async function writeGolemItems(char, constants, config) {
|
|
80
|
+
const writer = new bitwriter_1.BitWriter();
|
|
81
|
+
writer.WriteString("kf", 2);
|
|
82
|
+
if (char.golem_item) {
|
|
83
|
+
writer.WriteUInt8(1);
|
|
84
|
+
writer.WriteArray(await writeItem(char.golem_item, char.header.version, constants, config));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
writer.WriteUInt8(0);
|
|
88
|
+
}
|
|
89
|
+
return writer.ToArray();
|
|
90
|
+
}
|
|
91
|
+
exports.writeGolemItems = writeGolemItems;
|
|
92
|
+
async function readDemon(char, reader, constants, config) {
|
|
93
|
+
reader.SkipBytes(2);
|
|
94
|
+
const header = reader.ReadString(2); // 0x6C 0x66 lf Demon header
|
|
95
|
+
if (header === "lf") {
|
|
96
|
+
const hasDemon = reader.ReadUInt8();
|
|
97
|
+
if (!hasDemon)
|
|
98
|
+
return;
|
|
99
|
+
char.demon = {
|
|
100
|
+
_unknown_data: {},
|
|
101
|
+
mods: [],
|
|
102
|
+
};
|
|
103
|
+
char.demon._unknown_data.b1_4 = reader.ReadBytes(3);
|
|
104
|
+
char.demon.isSuperUnique = reader.ReadUInt16() === 2 ? 1 : 0;
|
|
105
|
+
char.demon.index = reader.ReadUInt16();
|
|
106
|
+
char.demon._unknown_data.b9_15 = reader.ReadBytes(6);
|
|
107
|
+
char.demon.difficulty = reader.ReadUInt8();
|
|
108
|
+
char.demon._unknown_data.b17_28 = reader.ReadBytes(11);
|
|
109
|
+
char.demon.levelId = reader.ReadUInt16();
|
|
110
|
+
char.demon._unknown_data.b31_32 = reader.ReadBytes(2);
|
|
111
|
+
char.demon.level = reader.ReadUInt8();
|
|
112
|
+
char.demon.isDesecrated = reader.ReadUInt8();
|
|
113
|
+
char.demon._unknown_data.b35_57 = reader.ReadBytes(22);
|
|
114
|
+
char.demon.difficulty2 = reader.ReadUInt8();
|
|
115
|
+
char.demon._unknown_data.b59_61 = reader.ReadBytes(3);
|
|
116
|
+
char.demon.difficulty3 = reader.ReadUInt8();
|
|
117
|
+
char.demon._unknown_data.b63_86 = reader.ReadBytes(23);
|
|
118
|
+
for (let i = 0; i < 9; i++) {
|
|
119
|
+
char.demon.mods.push(reader.ReadUInt8());
|
|
120
|
+
}
|
|
121
|
+
char.demon.stats = Array.from(reader.ReadBytes((reader.bits.length - reader.offset) / 8));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
exports.readDemon = readDemon;
|
|
125
|
+
async function writeDemon(char, constants, config) {
|
|
126
|
+
const writer = new bitwriter_1.BitWriter();
|
|
127
|
+
writer.WriteArray(new Uint8Array([0x01, 0x00]));
|
|
128
|
+
writer.WriteString("lf", 2);
|
|
129
|
+
if (char.demon) {
|
|
130
|
+
writer.WriteUInt8(1);
|
|
131
|
+
writer.WriteArray(char.demon._unknown_data.b1_4);
|
|
132
|
+
writer.WriteUInt16(char.demon.isSuperUnique ? 2 : 1);
|
|
133
|
+
writer.WriteUInt16(char.demon.index);
|
|
134
|
+
writer.WriteArray(char.demon._unknown_data.b9_15);
|
|
135
|
+
writer.WriteUInt8(char.demon.difficulty);
|
|
136
|
+
writer.WriteArray(char.demon._unknown_data.b17_28);
|
|
137
|
+
writer.WriteUInt16(char.demon.levelId);
|
|
138
|
+
writer.WriteArray(char.demon._unknown_data.b31_32);
|
|
139
|
+
writer.WriteUInt8(char.demon.level);
|
|
140
|
+
writer.WriteUInt8(char.demon.isDesecrated);
|
|
141
|
+
writer.WriteArray(char.demon._unknown_data.b35_57);
|
|
142
|
+
writer.WriteUInt8(char.demon.difficulty2);
|
|
143
|
+
writer.WriteArray(char.demon._unknown_data.b59_61);
|
|
144
|
+
writer.WriteUInt8(char.demon.difficulty3);
|
|
145
|
+
writer.WriteArray(char.demon._unknown_data.b63_86);
|
|
146
|
+
for (let i = 0; i < 9; i++) {
|
|
147
|
+
writer.WriteUInt8(char.demon.mods[i] || 0);
|
|
148
|
+
}
|
|
149
|
+
writer.WriteArray(new Uint8Array(char.demon.stats));
|
|
150
|
+
//writer.WriteBytes(new Uint8Array(char.demon.data));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
writer.WriteArray(new Uint8Array([0x00, 0x00]));
|
|
154
|
+
}
|
|
155
|
+
return writer.ToArray();
|
|
156
|
+
}
|
|
157
|
+
exports.writeDemon = writeDemon;
|
|
158
|
+
async function readCorpseItems(char, reader, constants, config) {
|
|
159
|
+
char.corpse_items = [];
|
|
160
|
+
const header = reader.ReadString(2); //0x0000 [item list header = 0x4a, 0x4d "JM"]
|
|
161
|
+
if (header !== "JM") {
|
|
162
|
+
// header is not present in first save after char is created
|
|
163
|
+
if (char.header.level === 1) {
|
|
164
|
+
char.is_dead = 0;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
throw new Error(`Corpse header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
168
|
+
}
|
|
169
|
+
char.is_dead = reader.ReadUInt16(); //0x0002 [corpse count]
|
|
170
|
+
for (let i = 0; i < char.is_dead; i++) {
|
|
171
|
+
reader.SkipBytes(12); //0x0004 [unk4, x_pos, y_pos]
|
|
172
|
+
char.corpse_items = char.corpse_items.concat(await readItems(reader, char.header.version, constants, config, char));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.readCorpseItems = readCorpseItems;
|
|
176
|
+
async function writeCorpseItem(char, constants, config) {
|
|
177
|
+
const writer = new bitwriter_1.BitWriter();
|
|
178
|
+
writer.WriteString("JM", 2);
|
|
179
|
+
writer.WriteUInt16(char.is_dead);
|
|
180
|
+
//json struct doesnt support multiple corpses without modifying it
|
|
181
|
+
if (char.is_dead) {
|
|
182
|
+
writer.WriteArray(new Uint8Array(12));
|
|
183
|
+
char.corpse_items = char.corpse_items || [];
|
|
184
|
+
writer.WriteArray(await writeItems(char.corpse_items, char.header.version, constants, config));
|
|
185
|
+
}
|
|
186
|
+
return writer.ToArray();
|
|
187
|
+
}
|
|
188
|
+
exports.writeCorpseItem = writeCorpseItem;
|
|
189
|
+
async function readItems(reader, version, constants, config, char) {
|
|
190
|
+
const items = [];
|
|
191
|
+
const header = reader.ReadString(2); //0x0000 [item list header = 0x4a, 0x4d "JM"]
|
|
192
|
+
if (header !== "JM") {
|
|
193
|
+
// header is not present in first save after char is created
|
|
194
|
+
if ((char === null || char === void 0 ? void 0 : char.header.level) === 1) {
|
|
195
|
+
return []; // TODO: return starter items based on class
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Item list header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
198
|
+
}
|
|
199
|
+
const count = reader.ReadUInt16(); //0x0002
|
|
200
|
+
for (let i = 0; i < count; i++) {
|
|
201
|
+
/*if (version === 0x69) {
|
|
202
|
+
const bitStr = reader.bits.join("");
|
|
203
|
+
const searchStart = reader.offset;
|
|
204
|
+
const marker1 = "000000001 00001000 10100000".replace(/ /g, ""); // search for next item
|
|
205
|
+
const marker2 = "000000001 00000000 10100000".replace(/ /g, ""); // search for next item
|
|
206
|
+
const id1 = bitStr.indexOf(marker1, searchStart);
|
|
207
|
+
const id2 = bitStr.indexOf(marker2, searchStart);
|
|
208
|
+
const id = id1 !== -1 && id2 !== -1 ? Math.min(id1, id2) : Math.max(id1, id2);
|
|
209
|
+
reader.SeekBit(id - 15);
|
|
210
|
+
}*/
|
|
211
|
+
items.push(await readItem(reader, version, constants, config));
|
|
212
|
+
}
|
|
213
|
+
return items;
|
|
214
|
+
}
|
|
215
|
+
exports.readItems = readItems;
|
|
216
|
+
async function writeItems(items, version, constants, config) {
|
|
217
|
+
const writer = new bitwriter_1.BitWriter();
|
|
218
|
+
writer.WriteString("JM", 2);
|
|
219
|
+
writer.WriteUInt16(items.length);
|
|
220
|
+
for (let i = 0; i < items.length; i++) {
|
|
221
|
+
writer.WriteArray(await writeItem(items[i], version, constants, config));
|
|
222
|
+
}
|
|
223
|
+
return writer.ToArray();
|
|
224
|
+
}
|
|
225
|
+
exports.writeItems = writeItems;
|
|
226
|
+
async function readItem(reader, version, originalConstants, config, parent) {
|
|
227
|
+
if (version <= 0x60) {
|
|
228
|
+
const header = reader.ReadString(2); //0x0000 [item header = 0x4a, 0x4d "JM"]
|
|
229
|
+
if (header !== "JM") {
|
|
230
|
+
throw new Error(`Item header 'JM' not found at position ${reader.offset - 2 * 8}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const constants = originalConstants;
|
|
234
|
+
const item = {};
|
|
235
|
+
_readSimpleBits(item, reader, version, constants, config);
|
|
236
|
+
if (!item.simple_item) {
|
|
237
|
+
item.id = reader.ReadUInt32(32);
|
|
238
|
+
item.level = reader.ReadUInt8(7);
|
|
239
|
+
item.quality = reader.ReadUInt8(4);
|
|
240
|
+
item.multiple_pictures = reader.ReadBit();
|
|
241
|
+
if (item.multiple_pictures) {
|
|
242
|
+
item.picture_id = reader.ReadUInt8(3);
|
|
243
|
+
}
|
|
244
|
+
item.class_specific = reader.ReadBit();
|
|
245
|
+
if (item.class_specific) {
|
|
246
|
+
item.auto_affix_id = reader.ReadUInt16(11);
|
|
247
|
+
}
|
|
248
|
+
switch (item.quality) {
|
|
249
|
+
case Quality.Low:
|
|
250
|
+
item.low_quality_id = reader.ReadUInt8(3);
|
|
251
|
+
break;
|
|
252
|
+
case Quality.Normal:
|
|
253
|
+
break;
|
|
254
|
+
case Quality.Superior:
|
|
255
|
+
item.file_index = reader.ReadUInt8(3);
|
|
256
|
+
break;
|
|
257
|
+
case Quality.Magic:
|
|
258
|
+
item.magic_prefix = reader.ReadUInt16(11);
|
|
259
|
+
if (item.magic_prefix)
|
|
260
|
+
item.magic_prefix_name = constants.magic_prefixes[item.magic_prefix] ? constants.magic_prefixes[item.magic_prefix].n : null;
|
|
261
|
+
item.magic_suffix = reader.ReadUInt16(11);
|
|
262
|
+
if (item.magic_suffix)
|
|
263
|
+
item.magic_suffix_name = constants.magic_suffixes[item.magic_suffix] ? constants.magic_suffixes[item.magic_suffix].n : null;
|
|
264
|
+
break;
|
|
265
|
+
case Quality.Set:
|
|
266
|
+
item.set_id = reader.ReadUInt16(12);
|
|
267
|
+
item.set_name = constants.set_items[item.set_id] ? constants.set_items[item.set_id].n : null;
|
|
268
|
+
break;
|
|
269
|
+
case Quality.Unique:
|
|
270
|
+
item.unique_id = reader.ReadUInt16(12);
|
|
271
|
+
item.unique_name = constants.unq_items[item.unique_id] ? constants.unq_items[item.unique_id].n : null;
|
|
272
|
+
break;
|
|
273
|
+
case Quality.Rare:
|
|
274
|
+
case Quality.Crafted:
|
|
275
|
+
item.rare_name_id = reader.ReadUInt8(8);
|
|
276
|
+
if (item.rare_name_id)
|
|
277
|
+
item.rare_name = constants.rare_names[item.rare_name_id] ? constants.rare_names[item.rare_name_id].n : null;
|
|
278
|
+
item.rare_name_id2 = reader.ReadUInt8(8);
|
|
279
|
+
if (item.rare_name_id2)
|
|
280
|
+
item.rare_name2 = constants.rare_names[item.rare_name_id2] ? constants.rare_names[item.rare_name_id2].n : null;
|
|
281
|
+
item.magical_name_ids = [];
|
|
282
|
+
for (let i = 0; i < 6; i++) {
|
|
283
|
+
const prefix = reader.ReadBit();
|
|
284
|
+
if (prefix === 1) {
|
|
285
|
+
item.magical_name_ids[i] = reader.ReadUInt16(11);
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
item.magical_name_ids[i] = null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
default:
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
if (item.given_runeword) {
|
|
296
|
+
item.runeword_id = reader.ReadUInt16(12);
|
|
297
|
+
//fix delerium on d2gs??? why is this a thing?
|
|
298
|
+
if (item.runeword_id == 2718) {
|
|
299
|
+
item.runeword_id = 48;
|
|
300
|
+
}
|
|
301
|
+
const mappedId = {
|
|
302
|
+
2784: 196,
|
|
303
|
+
2785: 197,
|
|
304
|
+
2786: 198,
|
|
305
|
+
2787: 199,
|
|
306
|
+
2788: 200,
|
|
307
|
+
2789: 201,
|
|
308
|
+
2790: 202,
|
|
309
|
+
2791: 203,
|
|
310
|
+
2792: 204,
|
|
311
|
+
3074: 205,
|
|
312
|
+
3075: 206,
|
|
313
|
+
3076: 207,
|
|
314
|
+
}[item.runeword_id] || item.runeword_id;
|
|
315
|
+
if (constants.runewords[mappedId]) {
|
|
316
|
+
item.runeword_name = constants.runewords[mappedId].n;
|
|
317
|
+
}
|
|
318
|
+
reader.ReadUInt8(4);
|
|
319
|
+
}
|
|
320
|
+
if (item.personalized) {
|
|
321
|
+
const arr = new Uint8Array(16);
|
|
322
|
+
for (let i = 0; i < arr.length; i++) {
|
|
323
|
+
if (version > 0x61) {
|
|
324
|
+
arr[i] = reader.ReadUInt8(8);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
arr[i] = reader.ReadUInt8(7);
|
|
328
|
+
}
|
|
329
|
+
if (arr[i] === 0x00) {
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
item.personalized_name = new bitreader_1.BitReader(arr).ReadString(16).trim().replace(/\0/g, "");
|
|
334
|
+
}
|
|
335
|
+
//tomes
|
|
336
|
+
if (item.type === "tbk" || item.type == "ibk") {
|
|
337
|
+
reader.ReadUInt8(5);
|
|
338
|
+
}
|
|
339
|
+
//realm data
|
|
340
|
+
item.timestamp = reader.ReadUInt8(1);
|
|
341
|
+
if (item.type_id === ItemType.Armor) {
|
|
342
|
+
item.defense_rating = reader.ReadUInt16(constants.magical_properties[31].sB) - constants.magical_properties[31].sA;
|
|
343
|
+
}
|
|
344
|
+
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Weapon) {
|
|
345
|
+
item.max_durability = reader.ReadUInt16(constants.magical_properties[73].sB) - constants.magical_properties[73].sA;
|
|
346
|
+
if (item.max_durability > 0) {
|
|
347
|
+
item.current_durability = reader.ReadUInt16(constants.magical_properties[72].sB) - constants.magical_properties[72].sA;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (version === 0x69) {
|
|
351
|
+
reader.SkipBits(1); //unknown bit in RotW
|
|
352
|
+
}
|
|
353
|
+
if (constants.stackables[item.type]) {
|
|
354
|
+
item.quantity = reader.ReadUInt16(9);
|
|
355
|
+
}
|
|
356
|
+
if (item.socketed === 1) {
|
|
357
|
+
item.total_nr_of_sockets = reader.ReadUInt8(4);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* 5 bits. any of the 5 bits can be set. if a bit is set that means
|
|
361
|
+
* means +1 to the set_list_count
|
|
362
|
+
*/
|
|
363
|
+
let plist_flag = 0;
|
|
364
|
+
if (item.quality === Quality.Set) {
|
|
365
|
+
plist_flag = reader.ReadUInt8(5);
|
|
366
|
+
item.set_list_count = 0;
|
|
367
|
+
item._unknown_data.plist_flag = plist_flag;
|
|
368
|
+
}
|
|
369
|
+
//magical properties
|
|
370
|
+
const skipMagicPeroperties = ["qll", "spe", "sol", "fng", "jaw", "scz", "flg", "tal"].includes(item.type);
|
|
371
|
+
let magic_attributes = _readMagicProperties(reader, constants, skipMagicPeroperties);
|
|
372
|
+
item.magic_attributes = magic_attributes;
|
|
373
|
+
while (plist_flag > 0) {
|
|
374
|
+
if (plist_flag & 1) {
|
|
375
|
+
item.set_list_count += 1;
|
|
376
|
+
magic_attributes = _readMagicProperties(reader, constants);
|
|
377
|
+
if (item.set_attributes) {
|
|
378
|
+
item.set_attributes.push(magic_attributes);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
item.set_attributes = [magic_attributes];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
plist_flag >>>= 1;
|
|
385
|
+
}
|
|
386
|
+
if (item.given_runeword === 1) {
|
|
387
|
+
magic_attributes = _readMagicProperties(reader, constants);
|
|
388
|
+
if (magic_attributes && magic_attributes.length > 0) {
|
|
389
|
+
item.runeword_attributes = magic_attributes;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/*if (item._unknown_data.b27_31 && item._unknown_data.b27_31?.some((b) => b === 1)) {
|
|
394
|
+
reader.SkipBits(53); // skip 50 - 53
|
|
395
|
+
reader.Align();
|
|
396
|
+
}*/
|
|
397
|
+
if (version === 0x69) {
|
|
398
|
+
if (!item.identified && (item.quality === Quality.Unique || item.quality === Quality.Set)) {
|
|
399
|
+
item.chronicle = {
|
|
400
|
+
monsterId: reader.ReadUInt16(),
|
|
401
|
+
timestamp: reader.ReadUInt32(),
|
|
402
|
+
};
|
|
403
|
+
reader.SkipBits(4);
|
|
404
|
+
}
|
|
405
|
+
item._unknown_data.chest_stackable = reader.ReadBit(); //unknown bit in RotW (stackable in chest?)
|
|
406
|
+
if (item._unknown_data.chest_stackable) {
|
|
407
|
+
item.amount_in_shared_stash = reader.ReadUInt8();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
reader.Align();
|
|
411
|
+
if (item.nr_of_items_in_sockets > 0 && item.simple_item === 0) {
|
|
412
|
+
item.socketed_items = [];
|
|
413
|
+
for (let i = 0; i < item.nr_of_items_in_sockets; i++) {
|
|
414
|
+
item.socketed_items.push(await readItem(reader, version, constants, config, item));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
//console.log(JSON.stringify(item));
|
|
418
|
+
return item;
|
|
419
|
+
}
|
|
420
|
+
exports.readItem = readItem;
|
|
421
|
+
async function writeItem(item, version, constants, config) {
|
|
422
|
+
var _a;
|
|
423
|
+
if (item._unknown_data === undefined) {
|
|
424
|
+
item._unknown_data = {};
|
|
425
|
+
}
|
|
426
|
+
if (item.categories === undefined) {
|
|
427
|
+
item.categories = (_a = _GetItemTXT(item, constants)) === null || _a === void 0 ? void 0 : _a.c;
|
|
428
|
+
}
|
|
429
|
+
const writer = new bitwriter_1.BitWriter();
|
|
430
|
+
if (version <= 0x60) {
|
|
431
|
+
writer.WriteString("JM", 2);
|
|
432
|
+
}
|
|
433
|
+
_writeSimpleBits(writer, version, item, constants, config);
|
|
434
|
+
if (!item.simple_item) {
|
|
435
|
+
writer.WriteUInt32(item.id, 32);
|
|
436
|
+
writer.WriteUInt8(item.level, 7);
|
|
437
|
+
writer.WriteUInt8(item.quality, 4);
|
|
438
|
+
writer.WriteUInt8(item.multiple_pictures, 1);
|
|
439
|
+
if (item.multiple_pictures) {
|
|
440
|
+
writer.WriteUInt8(item.picture_id, 3);
|
|
441
|
+
}
|
|
442
|
+
writer.WriteUInt8(item.class_specific, 1);
|
|
443
|
+
if (item.class_specific === 1) {
|
|
444
|
+
writer.WriteUInt16(item.auto_affix_id || 0, 11);
|
|
445
|
+
}
|
|
446
|
+
switch (item.quality) {
|
|
447
|
+
case Quality.Low:
|
|
448
|
+
writer.WriteUInt8(item.low_quality_id, 3);
|
|
449
|
+
break;
|
|
450
|
+
case Quality.Normal:
|
|
451
|
+
break;
|
|
452
|
+
case Quality.Superior:
|
|
453
|
+
writer.WriteUInt8(item.file_index || 0, 3);
|
|
454
|
+
break;
|
|
455
|
+
case Quality.Magic:
|
|
456
|
+
writer.WriteUInt16(item.magic_prefix, 11);
|
|
457
|
+
writer.WriteUInt16(item.magic_suffix, 11);
|
|
458
|
+
break;
|
|
459
|
+
case Quality.Set:
|
|
460
|
+
writer.WriteUInt16(item.set_id, 12);
|
|
461
|
+
break;
|
|
462
|
+
case Quality.Unique:
|
|
463
|
+
writer.WriteUInt16(item.unique_id, 12);
|
|
464
|
+
break;
|
|
465
|
+
case Quality.Rare:
|
|
466
|
+
case Quality.Crafted:
|
|
467
|
+
writer.WriteUInt8(item.rare_name_id !== undefined ? item.rare_name_id : _lookupRareId(item.rare_name, constants), 8);
|
|
468
|
+
writer.WriteUInt8(item.rare_name_id2 !== undefined ? item.rare_name_id2 : _lookupRareId(item.rare_name2, constants), 8);
|
|
469
|
+
for (let i = 0; i < 6; i++) {
|
|
470
|
+
const magical_name_id = item.magical_name_ids[i];
|
|
471
|
+
if (magical_name_id) {
|
|
472
|
+
writer.WriteBit(1);
|
|
473
|
+
writer.WriteUInt16(magical_name_id, 11);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
writer.WriteBit(0);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
break;
|
|
480
|
+
default:
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
if (item.given_runeword) {
|
|
484
|
+
//fix delerium on d2gs??? why is this a thing?
|
|
485
|
+
let runeword_id = item.runeword_id;
|
|
486
|
+
if (runeword_id == 2718) {
|
|
487
|
+
runeword_id = 48;
|
|
488
|
+
}
|
|
489
|
+
writer.WriteUInt16(runeword_id, 12);
|
|
490
|
+
writer.WriteUInt8(5, 4); //always 5?
|
|
491
|
+
}
|
|
492
|
+
if (item.personalized) {
|
|
493
|
+
const name = item.personalized_name.substring(0, 16);
|
|
494
|
+
for (let i = 0; i < name.length; i++) {
|
|
495
|
+
if (version > 0x61) {
|
|
496
|
+
writer.WriteUInt8(name.charCodeAt(i), 8);
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
writer.WriteUInt8(name.charCodeAt(i) & 0x7f, 7);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
writer.WriteUInt8(0x00, version > 0x61 ? 8 : 7);
|
|
503
|
+
}
|
|
504
|
+
if (item.type === "tbk") {
|
|
505
|
+
writer.WriteUInt8(0, 5);
|
|
506
|
+
}
|
|
507
|
+
else if (item.type === "ibk") {
|
|
508
|
+
writer.WriteUInt8(1, 5);
|
|
509
|
+
}
|
|
510
|
+
writer.WriteUInt8(item.timestamp, 1);
|
|
511
|
+
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Shield) {
|
|
512
|
+
writer.WriteUInt16(item.defense_rating + constants.magical_properties[31].sA, constants.magical_properties[31].sB);
|
|
513
|
+
}
|
|
514
|
+
if (item.type_id === ItemType.Armor || item.type_id === ItemType.Shield || item.type_id === ItemType.Weapon) {
|
|
515
|
+
writer.WriteUInt16(item.max_durability || 0, constants.magical_properties[73].sB);
|
|
516
|
+
if (item.max_durability > 0) {
|
|
517
|
+
writer.WriteUInt16(item.current_durability, constants.magical_properties[72].sB);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (version === 0x69) {
|
|
521
|
+
writer.WriteUInt8(constants.stackables[item.type] ? 1 : 0, 1); //unknown bit in RotW
|
|
522
|
+
}
|
|
523
|
+
if (constants.stackables[item.type]) {
|
|
524
|
+
writer.WriteUInt16(item.quantity, 9);
|
|
525
|
+
}
|
|
526
|
+
if (item.socketed === 1) {
|
|
527
|
+
writer.WriteUInt8(item.total_nr_of_sockets, 4);
|
|
528
|
+
}
|
|
529
|
+
if (item.quality === Quality.Set) {
|
|
530
|
+
const set_attribute_count = item.set_attributes != null ? item.set_attributes.length : 0;
|
|
531
|
+
//reduced by -1 removed as this seems to be wrong
|
|
532
|
+
const plist_flag = (1 << set_attribute_count) - 1;
|
|
533
|
+
writer.WriteUInt8(item._unknown_data.plist_flag || plist_flag, 5);
|
|
534
|
+
}
|
|
535
|
+
_writeMagicProperties(writer, item.magic_attributes, constants);
|
|
536
|
+
if (item.set_attributes && item.set_attributes.length > 0) {
|
|
537
|
+
for (let i = 0; i < item.set_attributes.length; i++) {
|
|
538
|
+
_writeMagicProperties(writer, item.set_attributes[i], constants);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (item.given_runeword === 1) {
|
|
542
|
+
_writeMagicProperties(writer, item.runeword_attributes, constants);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (version === 0x69) {
|
|
546
|
+
if (!item.identified && (item.quality === Quality.Unique || item.quality === Quality.Set) && item.chronicle) {
|
|
547
|
+
writer.WriteUInt16(item.chronicle.monsterId);
|
|
548
|
+
writer.WriteUInt32(item.chronicle.timestamp);
|
|
549
|
+
writer.WriteUInt8(0, 4); //unknown bit in RotW
|
|
550
|
+
}
|
|
551
|
+
writer.WriteUInt8(item._unknown_data.chest_stackable || 0, 1);
|
|
552
|
+
if (item._unknown_data.chest_stackable) {
|
|
553
|
+
writer.WriteUInt8(item.amount_in_shared_stash || 0, 8); //unknown bit in RotW (stackable in chest?)
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
writer.Align();
|
|
557
|
+
if (item.nr_of_items_in_sockets > 0 && item.simple_item === 0) {
|
|
558
|
+
for (let i = 0; i < item.nr_of_items_in_sockets; i++) {
|
|
559
|
+
writer.WriteArray(await writeItem(item.socketed_items[i], version, constants, config));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return writer.ToArray();
|
|
563
|
+
}
|
|
564
|
+
exports.writeItem = writeItem;
|
|
565
|
+
function _readSimpleBits(item, reader, version, constants, config) {
|
|
566
|
+
var _a, _b, _c;
|
|
567
|
+
//init so we do not have npe's
|
|
568
|
+
item._unknown_data = {};
|
|
569
|
+
//1.10-1.14d
|
|
570
|
+
//[flags:32][version:10][mode:3]([invloc:4][x:4][y:4][page:3])([itemcode:32])([sockets:3])
|
|
571
|
+
//1.15
|
|
572
|
+
//[flags:32][version:3][mode:3]([invloc:4][x:4][y:4][page:3])([itemcode:variable])([sockets:3])
|
|
573
|
+
item._unknown_data.b0_3 = reader.ReadBitArray(4); // 0 byte 1
|
|
574
|
+
item.identified = reader.ReadBit(); // 4
|
|
575
|
+
item._unknown_data.b5_10 = reader.ReadBitArray(6); // 5
|
|
576
|
+
item.socketed = reader.ReadBit(); // 11
|
|
577
|
+
item._unknown_data.b12 = reader.ReadBitArray(1); // 12
|
|
578
|
+
item.new = reader.ReadBit(); // 13
|
|
579
|
+
item._unknown_data.b14_15 = reader.ReadBitArray(2); // 14
|
|
580
|
+
item.is_ear = reader.ReadBit(); // 16 byte 3
|
|
581
|
+
item.starter_item = reader.ReadBit(); // 17
|
|
582
|
+
item._unknown_data.b18_20 = reader.ReadBitArray(3); // 18
|
|
583
|
+
item.simple_item = reader.ReadBit(); // 21
|
|
584
|
+
item.ethereal = reader.ReadBit(); // 22
|
|
585
|
+
item._unknown_data.b23 = reader.ReadBitArray(1); // 23
|
|
586
|
+
item.personalized = reader.ReadBit(); // 24 byte 4
|
|
587
|
+
item._unknown_data.b25 = reader.ReadBitArray(1); // 25
|
|
588
|
+
item.given_runeword = reader.ReadBit(); // 26
|
|
589
|
+
item._unknown_data.b27_31 = reader.ReadBitArray(5); // 27
|
|
590
|
+
if (version <= 0x60) {
|
|
591
|
+
item.version = reader.ReadUInt16(10).toString(10);
|
|
592
|
+
}
|
|
593
|
+
else if (version >= 0x61) {
|
|
594
|
+
item.version = reader.ReadUInt16(3).toString(2); // 32 byte 5
|
|
595
|
+
}
|
|
596
|
+
item.location_id = reader.ReadUInt8(3); // 34
|
|
597
|
+
item.equipped_id = reader.ReadUInt8(4); // 37
|
|
598
|
+
item.position_x = reader.ReadUInt8(4); // 41
|
|
599
|
+
item.position_y = reader.ReadUInt8(4); // 45
|
|
600
|
+
item.alt_position_id = reader.ReadUInt8(3); // 49
|
|
601
|
+
if (item.is_ear) {
|
|
602
|
+
const clazz = reader.ReadUInt8(3);
|
|
603
|
+
const level = reader.ReadUInt8(7);
|
|
604
|
+
const arr = new Uint8Array(15);
|
|
605
|
+
for (let i = 0; i < arr.length; i++) {
|
|
606
|
+
arr[i] = reader.ReadUInt8(7);
|
|
607
|
+
if (arr[i] === 0x00) {
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const name = new bitreader_1.BitReader(arr).ReadString(15).trim().replace(/\0/g, "");
|
|
612
|
+
item.ear_attributes = {
|
|
613
|
+
class: clazz,
|
|
614
|
+
level: level,
|
|
615
|
+
name: name,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
if (version <= 0x60) {
|
|
620
|
+
item.type = reader.ReadString(4);
|
|
621
|
+
}
|
|
622
|
+
else if (version >= 0x61) {
|
|
623
|
+
item.type = "";
|
|
624
|
+
//props to d07riv
|
|
625
|
+
//https://github.com/d07RiV/d07riv.github.io/blob/master/d2r.html#L11-L20
|
|
626
|
+
for (let i = 0; i < 4; i++) {
|
|
627
|
+
let node = HUFFMAN;
|
|
628
|
+
do {
|
|
629
|
+
node = node[reader.ReadBit()]; // 50
|
|
630
|
+
} while (Array.isArray(node));
|
|
631
|
+
item.type += node;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
item.type = item.type.trim().replace(/\0/g, "");
|
|
635
|
+
let details = _GetItemTXT(item, constants);
|
|
636
|
+
item.categories = details === null || details === void 0 ? void 0 : details.c;
|
|
637
|
+
if ((_a = item === null || item === void 0 ? void 0 : item.categories) === null || _a === void 0 ? void 0 : _a.includes("Any Armor")) {
|
|
638
|
+
item.type_id = ItemType.Armor;
|
|
639
|
+
}
|
|
640
|
+
else if ((_b = item === null || item === void 0 ? void 0 : item.categories) === null || _b === void 0 ? void 0 : _b.includes("Weapon")) {
|
|
641
|
+
item.type_id = ItemType.Weapon;
|
|
642
|
+
details = constants.weapon_items[item.type];
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
item.type_id = ItemType.Other;
|
|
646
|
+
}
|
|
647
|
+
let bits = item.simple_item ? 1 : 3;
|
|
648
|
+
if ((_c = item.categories) === null || _c === void 0 ? void 0 : _c.includes("Quest")) {
|
|
649
|
+
item.quest_difficulty = reader.ReadUInt16(constants.magical_properties[356].sB) - constants.magical_properties[356].sA;
|
|
650
|
+
bits = 1;
|
|
651
|
+
}
|
|
652
|
+
item.nr_of_items_in_sockets = reader.ReadUInt8(bits);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
function _lookupRareId(name, constants) {
|
|
656
|
+
//some inconsistencies with txt data and nokka. so have to hack it with startsWith
|
|
657
|
+
return constants.rare_names.findIndex((k) => k && (k.n.toLowerCase().startsWith(name.toLowerCase()) || name.toLowerCase().startsWith(k.n.toLowerCase())));
|
|
658
|
+
}
|
|
659
|
+
function _writeSimpleBits(writer, version, item, constants, config) {
|
|
660
|
+
var _a;
|
|
661
|
+
writer.WriteBits(item._unknown_data.b0_3 || new Uint8Array(4), 4);
|
|
662
|
+
writer.WriteBit(item.identified);
|
|
663
|
+
writer.WriteBits(item._unknown_data.b5_10 || new Uint8Array(6), 6);
|
|
664
|
+
writer.WriteBit(item.socketed);
|
|
665
|
+
writer.WriteBits(item._unknown_data.b12 || new Uint8Array(1), 1);
|
|
666
|
+
writer.WriteBit(item.new);
|
|
667
|
+
writer.WriteBits(item._unknown_data.b14_15 || new Uint8Array(2), 2);
|
|
668
|
+
writer.WriteBit(item.is_ear);
|
|
669
|
+
writer.WriteBit(item.starter_item);
|
|
670
|
+
writer.WriteBits(item._unknown_data.b18_20 || new Uint8Array(3), 3);
|
|
671
|
+
writer.WriteBit(item.simple_item);
|
|
672
|
+
writer.WriteBit(item.ethereal);
|
|
673
|
+
writer.WriteBits(item._unknown_data.b23 || new Uint8Array([1]), 1); //always 1? IFLAG_JUSTSAVED
|
|
674
|
+
writer.WriteBit(item.personalized);
|
|
675
|
+
writer.WriteBits(item._unknown_data.b25 || new Uint8Array(1), 1); //IFLAG_LOWQUALITY
|
|
676
|
+
writer.WriteBit(item.given_runeword);
|
|
677
|
+
writer.WriteBits(item._unknown_data.b27_31 || new Uint8Array(5), 5);
|
|
678
|
+
const itemVersion = item.version != null ? item.version : "101";
|
|
679
|
+
if (version <= 0x60) {
|
|
680
|
+
// 0 = pre-1.08; 1 = 1.08/1.09 normal; 2 = 1.10 normal; 100 = 1.08/1.09 expansion; 101 = 1.10 expansion
|
|
681
|
+
writer.WriteUInt16(parseInt(itemVersion, 10), 10);
|
|
682
|
+
}
|
|
683
|
+
else if (version >= 0x61) {
|
|
684
|
+
writer.WriteUInt16(parseInt(itemVersion, 2), 3);
|
|
685
|
+
}
|
|
686
|
+
writer.WriteUInt8(item.location_id, 3);
|
|
687
|
+
writer.WriteUInt8(item.equipped_id, 4);
|
|
688
|
+
writer.WriteUInt8(item.position_x, 4);
|
|
689
|
+
writer.WriteUInt8(item.position_y, 4);
|
|
690
|
+
writer.WriteUInt8(item.alt_position_id, 3);
|
|
691
|
+
if (item.is_ear) {
|
|
692
|
+
writer.WriteUInt8(item.ear_attributes.class, 3);
|
|
693
|
+
writer.WriteUInt8(item.ear_attributes.level, 7);
|
|
694
|
+
const name = item.ear_attributes.name.substring(0, 15);
|
|
695
|
+
for (let i = 0; i < name.length; i++) {
|
|
696
|
+
writer.WriteUInt8(name.charCodeAt(i) & 0x7f, 7);
|
|
697
|
+
}
|
|
698
|
+
writer.WriteUInt8(0x00, 7);
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
const t = item.type.padEnd(4, " ");
|
|
702
|
+
if (version <= 0x60) {
|
|
703
|
+
writer.WriteString(t, 4);
|
|
704
|
+
}
|
|
705
|
+
else {
|
|
706
|
+
for (const c of t) {
|
|
707
|
+
const n = HUFFMAN_LOOKUP[c];
|
|
708
|
+
writer.WriteUInt16(n.v, n.l);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
let bits = item.simple_item ? 1 : 3;
|
|
712
|
+
if ((_a = item.categories) === null || _a === void 0 ? void 0 : _a.includes("Quest")) {
|
|
713
|
+
const difficulty = item.quest_difficulty || 0;
|
|
714
|
+
writer.WriteUInt16(difficulty + constants.magical_properties[356].sA, constants.magical_properties[356].sB);
|
|
715
|
+
bits = 1;
|
|
716
|
+
}
|
|
717
|
+
writer.WriteUInt8(item.nr_of_items_in_sockets, bits);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
function _readMagicProperties(reader, constants, skipMagicProps) {
|
|
721
|
+
if (skipMagicProps) {
|
|
722
|
+
// Scan for the 0x1FF end marker (9 bits of 1)
|
|
723
|
+
const bitStr = reader.bits.join("");
|
|
724
|
+
const searchStart = reader.offset;
|
|
725
|
+
const marker = "111111111"; // 9-bit 0x1FF marker
|
|
726
|
+
const idx = bitStr.indexOf(marker, searchStart);
|
|
727
|
+
if (idx !== -1) {
|
|
728
|
+
reader.SeekBit(idx + 9); // Move reader past the 0x1FF marker
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
console.warn("0x1FF end marker not found, skipping failed");
|
|
732
|
+
}
|
|
733
|
+
return []; // Return empty list since we're skipping parsing
|
|
734
|
+
}
|
|
735
|
+
let id = reader.ReadUInt16(9);
|
|
736
|
+
const magic_attributes = [];
|
|
737
|
+
let zeroCounter = 0;
|
|
738
|
+
while (id != 0x1ff) {
|
|
739
|
+
if (id === 0) {
|
|
740
|
+
zeroCounter++;
|
|
741
|
+
}
|
|
742
|
+
if (zeroCounter > 10) {
|
|
743
|
+
throw new Error(`Possible malformed data at position ${reader.offset - 9}`);
|
|
744
|
+
}
|
|
745
|
+
const values = [];
|
|
746
|
+
if (id > constants.magical_properties.length) {
|
|
747
|
+
throw new Error(`Invalid Stat Id: ${id} at position ${reader.offset - 9}`);
|
|
748
|
+
}
|
|
749
|
+
const num_of_properties = constants.magical_properties[id].np || 1;
|
|
750
|
+
for (let i = 0; i < num_of_properties; i++) {
|
|
751
|
+
const prop = constants.magical_properties[id + i];
|
|
752
|
+
if (prop == null) {
|
|
753
|
+
throw new Error(`Cannot find Magical Property for id: ${id} at position ${reader.offset}`);
|
|
754
|
+
}
|
|
755
|
+
if (prop.sP) {
|
|
756
|
+
let param = reader.ReadUInt16(prop.sP);
|
|
757
|
+
switch (prop.dF) {
|
|
758
|
+
case 14: //+skill to skilltab
|
|
759
|
+
values.push(param & 0x7);
|
|
760
|
+
param = (param >> 3) & 0x1fff;
|
|
761
|
+
break;
|
|
762
|
+
default:
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
//encode
|
|
766
|
+
switch (prop.e) {
|
|
767
|
+
case 1:
|
|
768
|
+
//throw new Error(`Unimplemented encoding: ${prop.encode}`);
|
|
769
|
+
break;
|
|
770
|
+
case 2: //chance to cast
|
|
771
|
+
case 3: //charges
|
|
772
|
+
values.push(param & 0x3f); //skill level
|
|
773
|
+
param = (param >> 6) & 0x3ff; //skll id
|
|
774
|
+
break;
|
|
775
|
+
default:
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
values.push(param);
|
|
779
|
+
}
|
|
780
|
+
if (!prop.sB) {
|
|
781
|
+
throw new Error(`Save Bits is undefined for stat: ${id}:${prop.s} at position ${reader.offset}`);
|
|
782
|
+
}
|
|
783
|
+
let v = reader.ReadUInt16(prop.sB);
|
|
784
|
+
if (prop.sA) {
|
|
785
|
+
v -= prop.sA;
|
|
786
|
+
}
|
|
787
|
+
switch (prop.e) {
|
|
788
|
+
case 3:
|
|
789
|
+
values.push(v & 0xff); // current charges
|
|
790
|
+
values.push((v >> 8) & 0xff); //max charges
|
|
791
|
+
break;
|
|
792
|
+
default:
|
|
793
|
+
values.push(v);
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
magic_attributes.push({
|
|
798
|
+
id: id,
|
|
799
|
+
values: values,
|
|
800
|
+
name: constants.magical_properties[id].s,
|
|
801
|
+
});
|
|
802
|
+
id = reader.ReadUInt16(9);
|
|
803
|
+
}
|
|
804
|
+
return magic_attributes;
|
|
805
|
+
}
|
|
806
|
+
exports._readMagicProperties = _readMagicProperties;
|
|
807
|
+
function _writeMagicProperties(writer, properties, constants) {
|
|
808
|
+
if (properties) {
|
|
809
|
+
for (let i = 0; i < properties.length; i++) {
|
|
810
|
+
const property = properties[i];
|
|
811
|
+
let valueIdx = 0;
|
|
812
|
+
writer.WriteUInt16(property.id, 9);
|
|
813
|
+
const num_of_properties = constants.magical_properties[property.id].np || 1;
|
|
814
|
+
for (let j = 0; j < num_of_properties; j++) {
|
|
815
|
+
const prop = constants.magical_properties[property.id + j];
|
|
816
|
+
if (prop == null) {
|
|
817
|
+
throw new Error(`Cannot find Magical Property for id: ${property.id}`);
|
|
818
|
+
}
|
|
819
|
+
if (prop.sP) {
|
|
820
|
+
let param = property.values[valueIdx++];
|
|
821
|
+
switch (prop.dF) {
|
|
822
|
+
case 14: //+skill to skilltab
|
|
823
|
+
param |= (property.values[valueIdx++] & 0x1fff) << 3;
|
|
824
|
+
break;
|
|
825
|
+
default:
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
//encode
|
|
829
|
+
switch (prop.e) {
|
|
830
|
+
case 1:
|
|
831
|
+
//throw new Error(`Unimplemented encoding: ${prop.encode}`);
|
|
832
|
+
break;
|
|
833
|
+
case 2: //chance to cast
|
|
834
|
+
case 3: //charges
|
|
835
|
+
param |= (property.values[valueIdx++] & 0x3ff) << 6;
|
|
836
|
+
break;
|
|
837
|
+
default:
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
writer.WriteUInt32(param, prop.sP);
|
|
841
|
+
}
|
|
842
|
+
let v = property.values[valueIdx++];
|
|
843
|
+
if (prop.sA) {
|
|
844
|
+
v += prop.sA;
|
|
845
|
+
}
|
|
846
|
+
switch (prop.e) {
|
|
847
|
+
case 3:
|
|
848
|
+
v |= (property.values[valueIdx++] & 0xff) << 8;
|
|
849
|
+
break;
|
|
850
|
+
default:
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
if (!prop.sB) {
|
|
854
|
+
throw new Error(`Save Bits is undefined for stat: ${property.id}:${prop.s}`);
|
|
855
|
+
}
|
|
856
|
+
writer.WriteUInt32(v, prop.sB);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
writer.WriteUInt16(0x1ff, 9);
|
|
861
|
+
}
|
|
862
|
+
exports._writeMagicProperties = _writeMagicProperties;
|
|
863
|
+
function _GetItemTXT(item, constants) {
|
|
864
|
+
if (constants.armor_items[item.type]) {
|
|
865
|
+
return constants.armor_items[item.type];
|
|
866
|
+
}
|
|
867
|
+
else if (constants.weapon_items[item.type]) {
|
|
868
|
+
return constants.weapon_items[item.type];
|
|
869
|
+
}
|
|
870
|
+
else if (constants.other_items[item.type]) {
|
|
871
|
+
return constants.other_items[item.type];
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
874
|
//# sourceMappingURL=items.js.map
|