d2r-saver 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/data/data.json +1 -0
- package/data/strings.json +1 -0
- package/dist/core/binary-reader.d.ts +43 -0
- package/dist/core/binary-reader.d.ts.map +1 -0
- package/dist/core/binary-reader.js +130 -0
- package/dist/core/binary-reader.js.map +1 -0
- package/dist/core/binary-writer.d.ts +49 -0
- package/dist/core/binary-writer.d.ts.map +1 -0
- package/dist/core/binary-writer.js +156 -0
- package/dist/core/binary-writer.js.map +1 -0
- package/dist/core/checksum.d.ts +28 -0
- package/dist/core/checksum.d.ts.map +1 -0
- package/dist/core/checksum.js +62 -0
- package/dist/core/checksum.js.map +1 -0
- package/dist/core/huffman.d.ts +22 -0
- package/dist/core/huffman.d.ts.map +1 -0
- package/dist/core/huffman.js +66 -0
- package/dist/core/huffman.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/formats/d2i-reader.d.ts +53 -0
- package/dist/formats/d2i-reader.d.ts.map +1 -0
- package/dist/formats/d2i-reader.js +208 -0
- package/dist/formats/d2i-reader.js.map +1 -0
- package/dist/formats/d2i-writer.d.ts +63 -0
- package/dist/formats/d2i-writer.d.ts.map +1 -0
- package/dist/formats/d2i-writer.js +229 -0
- package/dist/formats/d2i-writer.js.map +1 -0
- package/dist/formats/d2s-reader.d.ts +104 -0
- package/dist/formats/d2s-reader.d.ts.map +1 -0
- package/dist/formats/d2s-reader.js +257 -0
- package/dist/formats/d2s-reader.js.map +1 -0
- package/dist/formats/d2s-writer.d.ts +52 -0
- package/dist/formats/d2s-writer.d.ts.map +1 -0
- package/dist/formats/d2s-writer.js +407 -0
- package/dist/formats/d2s-writer.js.map +1 -0
- package/dist/formats/detect.d.ts +18 -0
- package/dist/formats/detect.d.ts.map +1 -0
- package/dist/formats/detect.js +40 -0
- package/dist/formats/detect.js.map +1 -0
- package/dist/formats/index.d.ts +8 -0
- package/dist/formats/index.d.ts.map +1 -0
- package/dist/formats/index.js +8 -0
- package/dist/formats/index.js.map +1 -0
- package/dist/formats/item-parser.d.ts +97 -0
- package/dist/formats/item-parser.d.ts.map +1 -0
- package/dist/formats/item-parser.js +627 -0
- package/dist/formats/item-parser.js.map +1 -0
- package/dist/formats/item-writer.d.ts +55 -0
- package/dist/formats/item-writer.d.ts.map +1 -0
- package/dist/formats/item-writer.js +514 -0
- package/dist/formats/item-writer.js.map +1 -0
- package/dist/game-data/game-data.d.ts +90 -0
- package/dist/game-data/game-data.d.ts.map +1 -0
- package/dist/game-data/game-data.js +355 -0
- package/dist/game-data/game-data.js.map +1 -0
- package/dist/game-data/index.d.ts +4 -0
- package/dist/game-data/index.d.ts.map +1 -0
- package/dist/game-data/index.js +3 -0
- package/dist/game-data/index.js.map +1 -0
- package/dist/game-data/loader.d.ts +23 -0
- package/dist/game-data/loader.d.ts.map +1 -0
- package/dist/game-data/loader.js +32 -0
- package/dist/game-data/loader.js.map +1 -0
- package/dist/game-data/types.d.ts +428 -0
- package/dist/game-data/types.d.ts.map +1 -0
- package/dist/game-data/types.js +10 -0
- package/dist/game-data/types.js.map +1 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +197 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory/dimensions.d.ts +42 -0
- package/dist/inventory/dimensions.d.ts.map +1 -0
- package/dist/inventory/dimensions.js +30 -0
- package/dist/inventory/dimensions.js.map +1 -0
- package/dist/inventory/grid.d.ts +53 -0
- package/dist/inventory/grid.d.ts.map +1 -0
- package/dist/inventory/grid.js +117 -0
- package/dist/inventory/grid.js.map +1 -0
- package/dist/inventory/index.d.ts +4 -0
- package/dist/inventory/index.d.ts.map +1 -0
- package/dist/inventory/index.js +4 -0
- package/dist/inventory/index.js.map +1 -0
- package/dist/inventory/placement.d.ts +68 -0
- package/dist/inventory/placement.d.ts.map +1 -0
- package/dist/inventory/placement.js +90 -0
- package/dist/inventory/placement.js.map +1 -0
- package/dist/items/index.d.ts +7 -0
- package/dist/items/index.d.ts.map +1 -0
- package/dist/items/index.js +7 -0
- package/dist/items/index.js.map +1 -0
- package/dist/items/item-dto.d.ts +47 -0
- package/dist/items/item-dto.d.ts.map +1 -0
- package/dist/items/item-dto.js +124 -0
- package/dist/items/item-dto.js.map +1 -0
- package/dist/items/item-icon.d.ts +27 -0
- package/dist/items/item-icon.d.ts.map +1 -0
- package/dist/items/item-icon.js +103 -0
- package/dist/items/item-icon.js.map +1 -0
- package/dist/items/item-serializer.d.ts +38 -0
- package/dist/items/item-serializer.d.ts.map +1 -0
- package/dist/items/item-serializer.js +92 -0
- package/dist/items/item-serializer.js.map +1 -0
- package/dist/items/item-stats-parser.d.ts +52 -0
- package/dist/items/item-stats-parser.d.ts.map +1 -0
- package/dist/items/item-stats-parser.js +671 -0
- package/dist/items/item-stats-parser.js.map +1 -0
- package/dist/items/item-types.d.ts +40 -0
- package/dist/items/item-types.d.ts.map +1 -0
- package/dist/items/item-types.js +198 -0
- package/dist/items/item-types.js.map +1 -0
- package/dist/items/item.d.ts +61 -0
- package/dist/items/item.d.ts.map +1 -0
- package/dist/items/item.js +335 -0
- package/dist/items/item.js.map +1 -0
- package/dist/operations/extract-item.d.ts +39 -0
- package/dist/operations/extract-item.d.ts.map +1 -0
- package/dist/operations/extract-item.js +204 -0
- package/dist/operations/extract-item.js.map +1 -0
- package/dist/operations/index.d.ts +4 -0
- package/dist/operations/index.d.ts.map +1 -0
- package/dist/operations/index.js +4 -0
- package/dist/operations/index.js.map +1 -0
- package/dist/operations/insert-item.d.ts +59 -0
- package/dist/operations/insert-item.d.ts.map +1 -0
- package/dist/operations/insert-item.js +210 -0
- package/dist/operations/insert-item.js.map +1 -0
- package/dist/operations/read-save.d.ts +25 -0
- package/dist/operations/read-save.d.ts.map +1 -0
- package/dist/operations/read-save.js +27 -0
- package/dist/operations/read-save.js.map +1 -0
- package/dist/types/constants.d.ts +76 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/constants.js +96 -0
- package/dist/types/constants.js.map +1 -0
- package/dist/types/errors.d.ts +47 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +53 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/item.d.ts +112 -0
- package/dist/types/item.d.ts.map +1 -0
- package/dist/types/item.js +6 -0
- package/dist/types/item.js.map +1 -0
- package/dist/types/save-file.d.ts +74 -0
- package/dist/types/save-file.d.ts.map +1 -0
- package/dist/types/save-file.js +5 -0
- package/dist/types/save-file.js.map +1 -0
- package/dist/types/trade-item.d.ts +37 -0
- package/dist/types/trade-item.d.ts.map +1 -0
- package/dist/types/trade-item.js +5 -0
- package/dist/types/trade-item.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2S (character save) writer — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/writed2s.js → writeCharacter().
|
|
5
|
+
* Simplified: v105 only — all version conditionals removed.
|
|
6
|
+
*
|
|
7
|
+
* Uses GameData instead of global Data/Locale/Store singletons.
|
|
8
|
+
* Accepts an explicit profile + items dict instead of reading from state.
|
|
9
|
+
*/
|
|
10
|
+
import { BitWriter } from '../core/binary-writer.js';
|
|
11
|
+
import { fixHeader } from '../core/checksum.js';
|
|
12
|
+
import { GameData } from '../game-data/game-data.js';
|
|
13
|
+
import { buildWriteEntries, writeItemList } from './item-writer.js';
|
|
14
|
+
// ─── Constants ──────────────────────────────────────────────────
|
|
15
|
+
/** Experience thresholds indexed by (level − 1). xp[0] = 0 means level 1. */
|
|
16
|
+
const xp = [
|
|
17
|
+
0, 500, 1500, 3750, 7875, 14175, 22680, 32886, 44396, 57715,
|
|
18
|
+
72144, 90180, 112725, 140906, 176132, 220165, 275207, 344008, 430010, 537513,
|
|
19
|
+
671891, 839864, 1049830, 1312287, 1640359, 2050449, 2563061, 3203826, 3902260, 4663553,
|
|
20
|
+
5493363, 6397855, 7383752, 8458379, 9629723, 10906488, 12298162, 13815086, 15468534, 17270791,
|
|
21
|
+
19235252, 21376515, 23710491, 26254525, 29027522, 32050088, 35344686, 38935798, 42850109, 47116709,
|
|
22
|
+
51767302, 56836449, 62361819, 68384473, 74949165, 82104680, 89904191, 98405658, 107672256, 117772849,
|
|
23
|
+
128782495, 140783010, 153863570, 168121381, 183662396, 200602101, 219066380, 239192444, 261129853, 285041630,
|
|
24
|
+
311105466, 339515048, 370481492, 404234916, 441026148, 481128591, 524840254, 572485967, 624419793, 681027665,
|
|
25
|
+
742730244, 809986056, 883294891, 963201521, 1050299747, 1145236814, 1248718217, 1361512946, 1484459201, 1618470619,
|
|
26
|
+
1764543065, 1923762030, 2097310703, 2286478756, 2492671933, 2717422497, 2962400612, 3229426756, 3520485254, 3837739017,
|
|
27
|
+
];
|
|
28
|
+
/** Skill base offsets per class index (ama=0, sor=1, nec=2, pal=3, bar=4, dru=5, ass=6, war=7). */
|
|
29
|
+
const skillBases = [6, 36, 66, 96, 126, 221, 251, 373];
|
|
30
|
+
// ─── Public API ─────────────────────────────────────────────────
|
|
31
|
+
/**
|
|
32
|
+
* Write a complete .d2s character save file (Blizzless v105).
|
|
33
|
+
*
|
|
34
|
+
* @returns Raw file bytes ready to be saved.
|
|
35
|
+
*/
|
|
36
|
+
export function writeD2S(opts) {
|
|
37
|
+
const { profile, items, gd, name } = opts;
|
|
38
|
+
const writer = new BitWriter();
|
|
39
|
+
const classId = GameData.classes.indexOf(profile.class);
|
|
40
|
+
if (classId < 0)
|
|
41
|
+
throw new Error(`unknown class: ${profile.class}`);
|
|
42
|
+
// ═══════════ HEADER ═══════════
|
|
43
|
+
// Lossless path: copy the original fixed 833-byte v105 header verbatim when it
|
|
44
|
+
// was captured by the reader. This preserves name, class, merc header, quests,
|
|
45
|
+
// waypoints, appearance, difficulty, skill hotkeys, progression and timestamps
|
|
46
|
+
// exactly. filesize + checksum (offsets 8/12) are re-patched by fixHeader.
|
|
47
|
+
if (profile.rawHeader && profile.rawHeader.length >= 833) {
|
|
48
|
+
writer.writeBytes(new Uint8Array(profile.rawHeader.slice(0, 833)));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
writer.writeUInt32(0xaa55aa55); // 0x0000: magic
|
|
52
|
+
writer.writeUInt32(105); // 0x0004: version
|
|
53
|
+
writer.writeUInt32(0); // 0x0008: filesize (patched by fixHeader)
|
|
54
|
+
writer.writeUInt32(0); // 0x000C: checksum (patched by fixHeader)
|
|
55
|
+
writer.writeBytes(new Uint8Array(4)); // 0x0010: 4 zeros (v105 padding)
|
|
56
|
+
// Status byte — v105: expansion=false
|
|
57
|
+
const statusByte = writeStatus({
|
|
58
|
+
hardcore: false,
|
|
59
|
+
died: false,
|
|
60
|
+
expansion: false,
|
|
61
|
+
ladder: false,
|
|
62
|
+
});
|
|
63
|
+
writer.writeBytes(statusByte); // 0x0014
|
|
64
|
+
writer.writeUInt8(15); // 0x0015: progression (15 = all acts complete)
|
|
65
|
+
writer.writeUInt16(profile.weaponSet ?? 0); // 0x0016: active_arms
|
|
66
|
+
writer.writeUInt8(classId); // 0x0018: class
|
|
67
|
+
writer.writeBytes(new Uint8Array([0x10, 0x1e])); // 0x0019: unknown
|
|
68
|
+
writer.writeUInt8(profile.level || 1); // 0x001b: level
|
|
69
|
+
writer.writeBytes(new Uint8Array(4)); // 0x001c: unknown zeros
|
|
70
|
+
writer.writeUInt32(Math.floor(Date.now() / 1000)); // 0x0020: last_played (timestamp)
|
|
71
|
+
writer.writeBytes(new Uint8Array([0xff, 0xff, 0xff, 0xff])); // 0x0024: unknown
|
|
72
|
+
// Assigned skills (16 × 4 bytes = 64 bytes)
|
|
73
|
+
writer.writeBytes(writeAssignedSkills()); // 0x0028
|
|
74
|
+
// Left/right skills
|
|
75
|
+
writer.writeUInt32(0); // 0x0068: left_skill (Attack)
|
|
76
|
+
writer.writeUInt32(0); // 0x006c: right_skill (Attack)
|
|
77
|
+
writer.writeUInt32(0); // 0x0070: left_swap_skill
|
|
78
|
+
writer.writeUInt32(0); // 0x0074: right_swap_skill
|
|
79
|
+
// Char menu appearance (32 bytes)
|
|
80
|
+
writer.writeBytes(writeDefaultAppearance()); // 0x0078: 16 graphics + 16 tints
|
|
81
|
+
// Difficulty (3 bytes) — v105: {Normal:0, Nightmare:0, Hell:129}
|
|
82
|
+
writer.writeUInt8(0); // Normal
|
|
83
|
+
writer.writeUInt8(0); // Nightmare
|
|
84
|
+
writer.writeUInt8(129); // Hell (0x81 = active + completed)
|
|
85
|
+
// Map ID
|
|
86
|
+
writer.writeUInt32(0); // 0x009b: map_id
|
|
87
|
+
writer.writeBytes(new Uint8Array(2)); // 0x009f: unknown
|
|
88
|
+
// Merc section
|
|
89
|
+
writer.writeUInt16(0); // dead_merc
|
|
90
|
+
if (profile.merc) {
|
|
91
|
+
writer.writeUInt32(parseInt(profile.merc, 16) || 0); // merc_id
|
|
92
|
+
writer.writeUInt16(profile.mercName || 0); // merc_name_id
|
|
93
|
+
writer.writeUInt16(Number(profile.merc) || 0); // merc_type
|
|
94
|
+
writer.writeUInt32(xp[(profile.mercLevel || 1) - 1] || 0); // merc_experience
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
writer.writeUInt32(0); // merc_id
|
|
98
|
+
writer.writeUInt16(0); // merc_name_id
|
|
99
|
+
writer.writeUInt16(0); // merc_type
|
|
100
|
+
writer.writeUInt32(0); // merc_experience
|
|
101
|
+
}
|
|
102
|
+
// v105 extended block: 124 bytes + name(16) + 88 zeros
|
|
103
|
+
const extBlock1 = new Uint8Array(124);
|
|
104
|
+
extBlock1[73] = 0x03; // v105 expansion flag
|
|
105
|
+
writer.writeBytes(extBlock1);
|
|
106
|
+
writer.writeString(name || profile.name || '', 16);
|
|
107
|
+
writer.writeBytes(new Uint8Array(88));
|
|
108
|
+
// ═══════════ QUESTS ═══════════
|
|
109
|
+
writer.writeString('Woo!', 4);
|
|
110
|
+
writer.writeBytes(new Uint8Array([0x06, 0x00, 0x00, 0x00, 0x2a, 0x01]));
|
|
111
|
+
writer.writeBytes(writeQuestsForDifficulty(profile.quests?.[0]));
|
|
112
|
+
writer.writeBytes(writeQuestsForDifficulty(profile.quests?.[1]));
|
|
113
|
+
writer.writeBytes(writeQuestsForDifficulty(profile.quests?.[2]));
|
|
114
|
+
// ═══════════ WAYPOINTS ═══════════
|
|
115
|
+
writer.writeString('WS', 2);
|
|
116
|
+
writer.writeBytes(new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x50, 0x00]));
|
|
117
|
+
writer.writeBytes(writeWaypointData());
|
|
118
|
+
// ═══════════ NPC DATA ═══════════
|
|
119
|
+
writer.writeBytes(new Uint8Array([0x01, 0x77]));
|
|
120
|
+
writer.writeUInt16(0x34);
|
|
121
|
+
writer.writeBytes(new Uint8Array(0x30)); // blank NPC data
|
|
122
|
+
} // end synthesised-header fallback
|
|
123
|
+
// ═══════════ STATS ═══════════
|
|
124
|
+
const statsBytes = writeCharacterStats(profile, gd, opts.charStats);
|
|
125
|
+
writer.writeBytes(statsBytes);
|
|
126
|
+
// ═══════════ SKILLS ═══════════
|
|
127
|
+
writer.writeUInt16(0x6669);
|
|
128
|
+
const skillBase = skillBases[classId] ?? (classId < 5 ? classId * 30 + 6 : (classId - 5) * 30 + 221);
|
|
129
|
+
for (let i = 0; i < 30; i++) {
|
|
130
|
+
writer.writeUInt8(profile.skills?.[i + skillBase] || 0);
|
|
131
|
+
}
|
|
132
|
+
writer.align();
|
|
133
|
+
// ═══════════ ITEMS ═══════════
|
|
134
|
+
const playerEntries = buildWriteEntries(profile, items, gd, false);
|
|
135
|
+
const playerItemBytes = writeItemList(playerEntries, items, gd);
|
|
136
|
+
writer.writeBytes(playerItemBytes);
|
|
137
|
+
// ═══════════ CORPSE ═══════════
|
|
138
|
+
// Preserve corpse items verbatim when captured; otherwise emit "no corpse".
|
|
139
|
+
if (profile.rawCorpse && profile.rawCorpse.length) {
|
|
140
|
+
writer.writeBytes(new Uint8Array(profile.rawCorpse));
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
writer.writeUInt16(0x4d4a); // "JM"
|
|
144
|
+
writer.writeUInt16(0); // 0 corpses
|
|
145
|
+
}
|
|
146
|
+
// ═══════════ MERC ITEMS ═══════════
|
|
147
|
+
writer.writeUInt16(0x666a);
|
|
148
|
+
if (profile.merc) {
|
|
149
|
+
const mercEntries = buildWriteEntries(profile, items, gd, true);
|
|
150
|
+
const mercItemBytes = writeItemList(mercEntries, items, gd);
|
|
151
|
+
writer.writeBytes(mercItemBytes);
|
|
152
|
+
}
|
|
153
|
+
// ═══════════ IRON GOLEM ═══════════
|
|
154
|
+
// Preserve the iron golem verbatim when captured; otherwise emit "no golem".
|
|
155
|
+
if (profile.rawGolem && profile.rawGolem.length) {
|
|
156
|
+
writer.writeBytes(new Uint8Array(profile.rawGolem));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
writer.writeUInt16(0x666b);
|
|
160
|
+
writer.writeUInt8(0);
|
|
161
|
+
}
|
|
162
|
+
// ═══════════ V105 EXTRA SECTIONS ═══════════
|
|
163
|
+
// Preserve the source file's extra sections verbatim when they were captured
|
|
164
|
+
// by the reader (e.g. warlock 0x666c bind-demon block). Otherwise write an
|
|
165
|
+
// empty 0x666c marker.
|
|
166
|
+
if (profile.extraSections && profile.extraSections.length) {
|
|
167
|
+
writer.writeBytes(new Uint8Array(profile.extraSections));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
writer.writeUInt16(1); // unknown flag/count
|
|
171
|
+
writer.writeUInt16(0x666c); // v105 section marker
|
|
172
|
+
writer.writeUInt16(0); // empty section data
|
|
173
|
+
}
|
|
174
|
+
// ═══════════ FINALIZE ═══════════
|
|
175
|
+
fixHeader(writer);
|
|
176
|
+
return writer.toArray();
|
|
177
|
+
}
|
|
178
|
+
// ─── Character Stats ────────────────────────────────────────────
|
|
179
|
+
/**
|
|
180
|
+
* Build the binary stats section ("gf" header + stat entries + 0x1FF terminator).
|
|
181
|
+
*/
|
|
182
|
+
function writeCharacterStats(profile, gd, explicitStats) {
|
|
183
|
+
const writer = new BitWriter();
|
|
184
|
+
writer.writeString('gf', 2);
|
|
185
|
+
// ── Faithful path ──────────────────────────────────────────────
|
|
186
|
+
// When the parsed raw attribute map is available, re-emit every stat verbatim
|
|
187
|
+
// (same stat id, csvbits width, and raw value — including the 8.8 fixed-point
|
|
188
|
+
// life/mana/stamina) so gold, experience and all attributes round-trip exactly.
|
|
189
|
+
// Targeted edits are applied by mutating profile.attributes before writeD2S.
|
|
190
|
+
if (profile.attributes && Object.keys(profile.attributes).length) {
|
|
191
|
+
for (const key of Object.keys(profile.attributes)) {
|
|
192
|
+
const st = gd.itemStatCost[key];
|
|
193
|
+
if (!st)
|
|
194
|
+
continue;
|
|
195
|
+
writer.writeUInt16(st.id, 9);
|
|
196
|
+
writer.writeUInt32(profile.attributes[key] || 0, st.csvbits ?? 0);
|
|
197
|
+
}
|
|
198
|
+
writer.writeUInt16(0x1ff, 9);
|
|
199
|
+
writer.align();
|
|
200
|
+
return writer.toArray();
|
|
201
|
+
}
|
|
202
|
+
// ── Legacy synth path (brand-new characters without a parsed stat map) ──
|
|
203
|
+
const classId = GameData.classes.indexOf(profile.class);
|
|
204
|
+
const base = gd.charStats[profile.class];
|
|
205
|
+
// Compute final stat values (base class + allocated points)
|
|
206
|
+
const str = (base?.str ?? 0) + (profile.stats?.str ?? 0);
|
|
207
|
+
const dex = (base?.dex ?? 0) + (profile.stats?.dex ?? 0);
|
|
208
|
+
const eng = (base?.int ?? 0) + (profile.stats?.int ?? 0);
|
|
209
|
+
const vit = (base?.vit ?? 0) + (profile.stats?.vit ?? 0);
|
|
210
|
+
// Use explicit stats if provided, otherwise compute defaults
|
|
211
|
+
const hp = explicitStats?.maxhp ?? computeHP(base, profile);
|
|
212
|
+
const mana = explicitStats?.maxmana ?? computeMana(base, profile);
|
|
213
|
+
const stamina = explicitStats?.maxstamina ?? computeStamina(base, profile);
|
|
214
|
+
// Remaining stat/skill points
|
|
215
|
+
const spentStatPts = (profile.stats?.str ?? 0) + (profile.stats?.dex ?? 0) +
|
|
216
|
+
(profile.stats?.int ?? 0) + (profile.stats?.vit ?? 0);
|
|
217
|
+
const totalStatPts = (profile.level - 1) * 5 + countQuestStatPoints(profile.quests);
|
|
218
|
+
const statpts = explicitStats?.statpts ?? Math.max(0, totalStatPts - spentStatPts);
|
|
219
|
+
const spentSkillPts = Object.values(profile.skills || {}).reduce((s, v) => s + v, 0);
|
|
220
|
+
const totalSkillPts = (profile.level - 1) + countQuestSkillPoints(profile.quests);
|
|
221
|
+
const newskills = explicitStats?.newskills ?? Math.max(0, totalSkillPts - spentSkillPts);
|
|
222
|
+
const level = profile.level || 1;
|
|
223
|
+
const experience = xp[level - 1] || 0;
|
|
224
|
+
const statMap = [
|
|
225
|
+
{ key: 'strength', value: explicitStats?.strength ?? str },
|
|
226
|
+
{ key: 'energy', value: explicitStats?.energy ?? eng },
|
|
227
|
+
{ key: 'dexterity', value: explicitStats?.dexterity ?? dex },
|
|
228
|
+
{ key: 'vitality', value: explicitStats?.vitality ?? vit },
|
|
229
|
+
{ key: 'statpts', value: statpts },
|
|
230
|
+
{ key: 'newskills', value: newskills },
|
|
231
|
+
{ key: 'hitpoints', value: explicitStats?.hitpoints ?? hp },
|
|
232
|
+
{ key: 'maxhp', value: hp },
|
|
233
|
+
{ key: 'mana', value: explicitStats?.mana ?? mana },
|
|
234
|
+
{ key: 'maxmana', value: mana },
|
|
235
|
+
{ key: 'stamina', value: explicitStats?.stamina ?? stamina },
|
|
236
|
+
{ key: 'maxstamina', value: stamina },
|
|
237
|
+
{ key: 'level', value: level },
|
|
238
|
+
{ key: 'experience', value: experience },
|
|
239
|
+
{ key: 'gold', value: 990000 },
|
|
240
|
+
{ key: 'goldbank', value: 2500000 },
|
|
241
|
+
];
|
|
242
|
+
for (let i = 0; i < statMap.length; i++) {
|
|
243
|
+
const s = statMap[i];
|
|
244
|
+
const stat = gd.itemStatCost[s.key];
|
|
245
|
+
if (!stat)
|
|
246
|
+
continue;
|
|
247
|
+
let value = s.value || 0;
|
|
248
|
+
// HP, mana, stamina (indices 6-11) are stored shifted left by 8
|
|
249
|
+
if (i >= 6 && i <= 11)
|
|
250
|
+
value <<= 8;
|
|
251
|
+
writer.writeUInt16(stat.id, 9);
|
|
252
|
+
writer.writeUInt32(value, stat.csvbits ?? 0);
|
|
253
|
+
}
|
|
254
|
+
// Terminator
|
|
255
|
+
writer.writeUInt16(0x1ff, 9);
|
|
256
|
+
writer.align();
|
|
257
|
+
return writer.toArray();
|
|
258
|
+
}
|
|
259
|
+
// ─── Stat computation helpers ───────────────────────────────────
|
|
260
|
+
function computeHP(base, profile) {
|
|
261
|
+
// base HP + vit × hpPerVit
|
|
262
|
+
const baseHP = base?.hpl ?? 50;
|
|
263
|
+
const hpPerVit = base?.['life_per_vitality'] ?? 3;
|
|
264
|
+
return baseHP + (profile.stats?.vit ?? 0) * hpPerVit;
|
|
265
|
+
}
|
|
266
|
+
function computeMana(base, profile) {
|
|
267
|
+
const baseMana = base?.mnl ?? 15;
|
|
268
|
+
const manaPerEng = base?.['mana_per_magic'] ?? 2;
|
|
269
|
+
return baseMana + (profile.stats?.int ?? 0) * manaPerEng;
|
|
270
|
+
}
|
|
271
|
+
function computeStamina(base, profile) {
|
|
272
|
+
const baseStamina = base?.stl ?? 80;
|
|
273
|
+
const stamPerVit = base?.['stamina_per_vitality'] ?? 1;
|
|
274
|
+
return baseStamina + (profile.stats?.vit ?? 0) * stamPerVit;
|
|
275
|
+
}
|
|
276
|
+
/** Count stat points awarded by quests (Den of Evil × 3 difficulties). */
|
|
277
|
+
function countQuestStatPoints(quests) {
|
|
278
|
+
if (!quests)
|
|
279
|
+
return 0;
|
|
280
|
+
let pts = 0;
|
|
281
|
+
for (const diff of quests) {
|
|
282
|
+
// Den of Evil rewards 5 stat points per difficulty in LoD
|
|
283
|
+
// Lam Esen's Tome rewards 5 stat points per difficulty
|
|
284
|
+
if (diff?.denofevil)
|
|
285
|
+
pts += 5;
|
|
286
|
+
if (diff?.lamessenstome)
|
|
287
|
+
pts += 5;
|
|
288
|
+
}
|
|
289
|
+
return pts;
|
|
290
|
+
}
|
|
291
|
+
/** Count skill points awarded by quests. */
|
|
292
|
+
function countQuestSkillPoints(quests) {
|
|
293
|
+
if (!quests)
|
|
294
|
+
return 0;
|
|
295
|
+
let pts = 0;
|
|
296
|
+
for (const diff of quests) {
|
|
297
|
+
// Radament's Lair rewards 1 skill point per difficulty
|
|
298
|
+
// The Fallen Angel rewards 2 skill points per difficulty
|
|
299
|
+
if (diff?.radamentslair)
|
|
300
|
+
pts += 1;
|
|
301
|
+
if (diff?.thefallenangel)
|
|
302
|
+
pts += 2;
|
|
303
|
+
}
|
|
304
|
+
return pts;
|
|
305
|
+
}
|
|
306
|
+
// ─── Header helpers ─────────────────────────────────────────────
|
|
307
|
+
/** Encode status byte. */
|
|
308
|
+
function writeStatus(status) {
|
|
309
|
+
const arr = new Uint8Array(1);
|
|
310
|
+
if (status.hardcore)
|
|
311
|
+
arr[0] |= 1 << 2;
|
|
312
|
+
if (status.died)
|
|
313
|
+
arr[0] |= 1 << 3;
|
|
314
|
+
if (status.expansion)
|
|
315
|
+
arr[0] |= 1 << 5;
|
|
316
|
+
if (status.ladder)
|
|
317
|
+
arr[0] |= 1 << 6;
|
|
318
|
+
return arr;
|
|
319
|
+
}
|
|
320
|
+
/** 16 assigned skill slots × 4 bytes. Default: all empty (0xffff). */
|
|
321
|
+
function writeAssignedSkills() {
|
|
322
|
+
const w = new BitWriter();
|
|
323
|
+
for (let i = 0; i < 16; i++) {
|
|
324
|
+
w.writeUInt32(0xffff);
|
|
325
|
+
}
|
|
326
|
+
return w.toArray();
|
|
327
|
+
}
|
|
328
|
+
/** Default character menu appearance — all 0xff (invisible). */
|
|
329
|
+
function writeDefaultAppearance() {
|
|
330
|
+
const arr = new Uint8Array(32);
|
|
331
|
+
arr.fill(0xff);
|
|
332
|
+
return arr;
|
|
333
|
+
}
|
|
334
|
+
// ─── Quest helpers ──────────────────────────────────────────────
|
|
335
|
+
/**
|
|
336
|
+
* Write 96 bytes of quest data for one difficulty.
|
|
337
|
+
*
|
|
338
|
+
* We write a simplified version: only the quests the reader cares about
|
|
339
|
+
* (den_of_evil, radaments_lair, golden_bird, lam_esens_tome,
|
|
340
|
+
* the_fallen_angel, prison_of_ice) are flagged. Everything else is zeroed.
|
|
341
|
+
*
|
|
342
|
+
* The reader checks byte(2)&1 for den_of_evil, byte(18)&1 for radaments_lair, etc.
|
|
343
|
+
*/
|
|
344
|
+
function writeQuestsForDifficulty(q) {
|
|
345
|
+
const buf = new Uint8Array(96);
|
|
346
|
+
if (!q)
|
|
347
|
+
return buf;
|
|
348
|
+
// Act I
|
|
349
|
+
// introduced = 1 (16-bit at offset 0)
|
|
350
|
+
buf[0] = 1;
|
|
351
|
+
// den_of_evil: 2 bytes at offset 2 (quest 0) — byte(2) bit 0 = is_completed
|
|
352
|
+
if (q.denofevil)
|
|
353
|
+
buf[2] = 1;
|
|
354
|
+
// sisters_to_the_slaughter: offset 12, mark act I completed at offset 14
|
|
355
|
+
if (q.denofevil) {
|
|
356
|
+
// Mark act I completed + introduced Act II
|
|
357
|
+
buf[14] = 1; // act I completed
|
|
358
|
+
buf[16] = 1; // act II introduced
|
|
359
|
+
}
|
|
360
|
+
// Act II
|
|
361
|
+
// radaments_lair: 2 bytes at offset 18 (quest 0 of act II) — byte(18) bit 0
|
|
362
|
+
if (q.radamentslair)
|
|
363
|
+
buf[18] = 1;
|
|
364
|
+
// the_seven_tombs completed → act II done + act III intro
|
|
365
|
+
buf[30] = 0; // act II completed
|
|
366
|
+
buf[32] = 1; // act III introduced
|
|
367
|
+
// Act III
|
|
368
|
+
// lam_esens_tome: offset 34 — byte(34) bit 0
|
|
369
|
+
if (q.lamessenstome)
|
|
370
|
+
buf[34] = 1;
|
|
371
|
+
// the_golden_bird: offset 40 — byte(40) bit 0
|
|
372
|
+
if (q.thegoldenbird)
|
|
373
|
+
buf[40] = 1;
|
|
374
|
+
// Act IV
|
|
375
|
+
// introduced at offset 48
|
|
376
|
+
buf[48] = 1;
|
|
377
|
+
// the_fallen_angel: offset 50 — byte(50) bit 0
|
|
378
|
+
if (q.thefallenangel)
|
|
379
|
+
buf[50] = 1;
|
|
380
|
+
// Act V
|
|
381
|
+
// introduced
|
|
382
|
+
buf[62] = 1;
|
|
383
|
+
// prison_of_ice: offset 74 — byte(74) bit 0
|
|
384
|
+
if (q.prisonofice)
|
|
385
|
+
buf[74] = 1;
|
|
386
|
+
return buf;
|
|
387
|
+
}
|
|
388
|
+
// ─── Waypoint helpers ───────────────────────────────────────────
|
|
389
|
+
/** Write 72 bytes of waypoint data (3 difficulties × 24 bytes). */
|
|
390
|
+
function writeWaypointData() {
|
|
391
|
+
const w = new BitWriter();
|
|
392
|
+
for (let i = 0; i < 3; i++) {
|
|
393
|
+
w.writeBytes(writeWaypointsForDifficulty());
|
|
394
|
+
}
|
|
395
|
+
return w.toArray();
|
|
396
|
+
}
|
|
397
|
+
/** Write 24 bytes of waypoints for one difficulty — all waypoints enabled. */
|
|
398
|
+
function writeWaypointsForDifficulty() {
|
|
399
|
+
const w = new BitWriter();
|
|
400
|
+
w.writeBytes(new Uint8Array([0x02, 0x01])); // header
|
|
401
|
+
// 39 waypoint bits — all enabled
|
|
402
|
+
w.writeBytes(new Uint8Array([0xff, 0xff, 0xff, 0xff, 0x7f]));
|
|
403
|
+
w.align();
|
|
404
|
+
w.writeBytes(new Uint8Array(17)); // padding
|
|
405
|
+
return w.toArray();
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=d2s-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d2s-writer.js","sourceRoot":"","sources":["../../src/formats/d2s-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEpE,mEAAmE;AAEnE,6EAA6E;AAC7E,MAAM,EAAE,GAAG;IACT,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IAC3D,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAC5E,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO;IACtF,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;IAC7F,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ;IAClG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;IACpG,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IAC5G,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IAC5G,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;IAClH,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU;CACvH,CAAC;AAEF,mGAAmG;AACnG,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAsCvD,mEAAmE;AAEnE;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAqB;IAC5C,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;IAE/B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAwC,CAAC,CAAC;IAC3F,IAAI,OAAO,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAEpE,iCAAiC;IACjC,+EAA+E;IAC/E,+EAA+E;IAC/E,+EAA+E;IAC/E,2EAA2E;IAC3E,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACzD,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACR,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,CAAY,gBAAgB;QAC3D,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAmB,kBAAkB;QAC7D,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,0CAA0C;QACrF,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,0CAA0C;QACrF,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAM,iCAAiC;QAE5E,sCAAsC;QACtC,MAAM,UAAU,GAAG,WAAW,CAAC;YAC7B,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QACH,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAa,SAAS;QACpD,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAqB,+CAA+C;QAC1F,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAA,sBAAsB;QAEjE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAgB,gBAAgB;QAE3D,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB;QACnE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAK,gBAAgB;QAC3D,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAM,wBAAwB;QACnE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,kCAAkC;QACrF,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB;QAE/E,4CAA4C;QAC5C,MAAM,CAAC,UAAU,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAE,SAAS;QACpD,oBAAoB;QACpB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,+BAA+B;QAC1E,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,+BAA+B;QAC1E,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,0BAA0B;QACrE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,2BAA2B;QAEtE,kCAAkC;QAClC,MAAM,CAAC,UAAU,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,iCAAiC;QAE9E,iEAAiE;QACjE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAsB,SAAS;QACpD,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAsB,YAAY;QACvD,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAoB,mCAAmC;QAE9E,SAAS;QACT,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,iBAAiB;QAC5D,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAM,kBAAkB;QAE7D,eAAe;QACf,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAqB,YAAY;QACvD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU;YAC/D,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAW,eAAe;YACpE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAO,YAAY;YACjE,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB;QAC/E,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;YACjC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe;YACtC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;YACnC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB;QAC3C,CAAC;QAED,uDAAuD;QACvD,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;QACtC,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,sBAAsB;QAC5C,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC7B,MAAM,CAAC,WAAW,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAEtC,iCAAiC;QACjC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,UAAU,CAAC,wBAAwB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,UAAU,CAAC,wBAAwB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,CAAC,UAAU,CAAC,wBAAwB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjE,oCAAoC;QACpC,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5B,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,UAAU,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEvC,mCAAmC;QACnC,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACzB,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,iBAAiB;IAC1D,CAAC,CAAC,kCAAkC;IAEpC,gCAAgC;IAChC,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAE9B,iCAAiC;IACjC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC;IACrG,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,CAAC,KAAK,EAAE,CAAC;IAEf,gCAAgC;IAChC,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAEnC,iCAAiC;IACjC,4EAA4E;IAC5E,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QAClD,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;QACnC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAM,YAAY;IAC1C,CAAC;IAED,qCAAqC;IACrC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5D,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IAED,qCAAqC;IACrC,6EAA6E;IAC7E,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAChD,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IAED,8CAA8C;IAC9C,6EAA6E;IAC7E,2EAA2E;IAC3E,uBAAuB;IACvB,IAAI,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;QAC1D,MAAM,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAO,qBAAqB;QAClD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAE,sBAAsB;QACnD,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAO,qBAAqB;IACpD,CAAC;IAED,mCAAmC;IACnC,SAAS,CAAC,MAAM,CAAC,CAAC;IAElB,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED,mEAAmE;AAEnE;;GAEG;AACH,SAAS,mBAAmB,CAC1B,OAA4B,EAC5B,EAAY,EACZ,aAA4C;IAE5C,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;IAC/B,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAE5B,kEAAkE;IAClE,8EAA8E;IAC9E,8EAA8E;IAC9E,gFAAgF;IAChF,6EAA6E;IAC7E,IAAI,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC;QACjE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAClD,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,CAAiD,CAAC;YAChF,IAAI,CAAC,EAAE;gBAAE,SAAS;YAClB,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7B,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,2EAA2E;IAC3E,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAwC,CAAC,CAAC;IAC3F,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAuC,CAAC;IAE/E,4DAA4D;IAC5D,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAEzD,6DAA6D;IAC7D,MAAM,EAAE,GAAG,aAAa,EAAE,KAAK,IAAI,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,aAAa,EAAE,OAAO,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,aAAa,EAAE,UAAU,IAAI,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAE3E,8BAA8B;IAC9B,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;QACxE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,aAAa,EAAE,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,GAAG,YAAY,CAAC,CAAC;IAEnF,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,aAAa,GAAG,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,aAAa,EAAE,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,aAAa,CAAC,CAAC;IAEzF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;IACjC,MAAM,UAAU,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;IAEtC,MAAM,OAAO,GAA0C;QACrD,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,IAAI,GAAG,EAAE;QAC1D,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,GAAG,EAAE;QACtD,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,IAAI,GAAG,EAAE;QAC5D,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,IAAI,GAAG,EAAE;QAC1D,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE;QAClC,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE;QACtC,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,IAAI,EAAE,EAAE;QAC3D,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,IAAI,IAAI,EAAE;QACnD,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE;QAC/B,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,IAAI,OAAO,EAAE;QAC5D,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE;QACrC,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE;QAC9B,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE;QACxC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;QAC9B,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE;KACpC,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAiD,CAAC;QACpF,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;QACzB,gEAAgE;QAChE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,KAAK,KAAK,CAAC,CAAC;QACnC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/B,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,aAAa;IACb,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,KAAK,EAAE,CAAC;IAEf,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED,mEAAmE;AAEnE,SAAS,SAAS,CAChB,IAAwC,EACxC,OAA4B;IAE5B,2BAA2B;IAC3B,MAAM,MAAM,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,IAAI,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,MAAM,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC;AACvD,CAAC;AAED,SAAS,WAAW,CAClB,IAAwC,EACxC,OAA4B;IAE5B,MAAM,QAAQ,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,QAAQ,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,UAAU,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CACrB,IAAwC,EACxC,OAA4B;IAE5B,MAAM,WAAW,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,WAAW,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,UAAU,CAAC;AAC9D,CAAC;AAED,0EAA0E;AAC1E,SAAS,oBAAoB,CAC3B,MAAsC;IAEtC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACtB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,0DAA0D;QAC1D,uDAAuD;QACvD,IAAI,IAAI,EAAE,SAAS;YAAE,GAAG,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI,EAAE,aAAa;YAAE,GAAG,IAAI,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4CAA4C;AAC5C,SAAS,qBAAqB,CAC5B,MAAsC;IAEtC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC;IACtB,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,uDAAuD;QACvD,yDAAyD;QACzD,IAAI,IAAI,EAAE,aAAa;YAAE,GAAG,IAAI,CAAC,CAAC;QAClC,IAAI,IAAI,EAAE,cAAc;YAAE,GAAG,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AAEnE,0BAA0B;AAC1B,SAAS,WAAW,CAAC,MAKpB;IACC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,MAAM,CAAC,QAAQ;QAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,IAAI;QAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,SAAS;QAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM;QAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sEAAsE;AACtE,SAAS,mBAAmB;IAC1B,MAAM,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,gEAAgE;AAChE,SAAS,sBAAsB;IAC7B,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AAEnE;;;;;;;;GAQG;AACH,SAAS,wBAAwB,CAC/B,CAAoC;IAEpC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IAEnB,QAAQ;IACR,sCAAsC;IACtC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACX,4EAA4E;IAC5E,IAAI,CAAC,CAAC,SAAS;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5B,yEAAyE;IACzE,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;QAChB,2CAA2C;QAC3C,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB;QAC/B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,oBAAoB;IACnC,CAAC;IAED,SAAS;IACT,4EAA4E;IAC5E,IAAI,CAAC,CAAC,aAAa;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,mBAAmB;IAChC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,qBAAqB;IAElC,UAAU;IACV,6CAA6C;IAC7C,IAAI,CAAC,CAAC,aAAa;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjC,8CAA8C;IAC9C,IAAI,CAAC,CAAC,aAAa;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAEjC,SAAS;IACT,0BAA0B;IAC1B,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACZ,+CAA+C;IAC/C,IAAI,CAAC,CAAC,cAAc;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAElC,QAAQ;IACR,aAAa;IACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACZ,4CAA4C;IAC5C,IAAI,CAAC,CAAC,WAAW;QAAE,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAE/B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,mEAAmE;AAEnE,mEAAmE;AACnE,SAAS,iBAAiB;IACxB,MAAM,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,CAAC,CAAC,UAAU,CAAC,2BAA2B,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACrB,CAAC;AAED,8EAA8E;AAC9E,SAAS,2BAA2B;IAClC,MAAM,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC;IAC1B,CAAC,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;IACrD,iCAAiC;IACjC,CAAC,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,KAAK,EAAE,CAAC;IACV,CAAC,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU;IAC5C,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save file format detection — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/index.js → detectFormat().
|
|
5
|
+
* Simplified: only accepts version 105.
|
|
6
|
+
*/
|
|
7
|
+
/** Detected format type */
|
|
8
|
+
export interface DetectedFormat {
|
|
9
|
+
type: 'd2s' | 'd2i';
|
|
10
|
+
version: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Detect whether `data` is a valid Blizzless v105 .d2s or .d2i file.
|
|
14
|
+
*
|
|
15
|
+
* @returns Format info or `null` if not recognized / unsupported version.
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectFormat(data: Uint8Array): DetectedFormat | null;
|
|
18
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/formats/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAcD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAmBpE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save file format detection — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/index.js → detectFormat().
|
|
5
|
+
* Simplified: only accepts version 105.
|
|
6
|
+
*/
|
|
7
|
+
import { D2S_MAGIC, BLIZZLESS_VERSION } from '../types/constants.js';
|
|
8
|
+
/**
|
|
9
|
+
* Read a 32-bit LE unsigned int at the given byte offset.
|
|
10
|
+
*/
|
|
11
|
+
function readU32(data, offset) {
|
|
12
|
+
return (data[offset] |
|
|
13
|
+
(data[offset + 1] << 8) |
|
|
14
|
+
(data[offset + 2] << 16) |
|
|
15
|
+
(data[offset + 3] << 24)) >>> 0;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Detect whether `data` is a valid Blizzless v105 .d2s or .d2i file.
|
|
19
|
+
*
|
|
20
|
+
* @returns Format info or `null` if not recognized / unsupported version.
|
|
21
|
+
*/
|
|
22
|
+
export function detectFormat(data) {
|
|
23
|
+
if (data.length < 16)
|
|
24
|
+
return null;
|
|
25
|
+
const magic = readU32(data, 0);
|
|
26
|
+
if (magic !== D2S_MAGIC)
|
|
27
|
+
return null;
|
|
28
|
+
// Check d2s version at offset 4
|
|
29
|
+
const d2sVersion = readU32(data, 4);
|
|
30
|
+
if (d2sVersion === BLIZZLESS_VERSION) {
|
|
31
|
+
return { type: 'd2s', version: BLIZZLESS_VERSION };
|
|
32
|
+
}
|
|
33
|
+
// Check d2i version at offset 8
|
|
34
|
+
const d2iVersion = readU32(data, 8);
|
|
35
|
+
if (d2iVersion === BLIZZLESS_VERSION) {
|
|
36
|
+
return { type: 'd2i', version: BLIZZLESS_VERSION };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/formats/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAQrE;;GAEG;AACH,SAAS,OAAO,CAAC,IAAgB,EAAE,MAAc;IAC/C,OAAO,CACL,IAAI,CAAC,MAAM,CAAC;QACZ,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CACzB,KAAK,CAAC,CAAC;AACV,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAElC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAErC,gCAAgC;IAChC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpC,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IACrD,CAAC;IAED,gCAAgC;IAChC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpC,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IACrD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { detectFormat, type DetectedFormat } from './detect.js';
|
|
2
|
+
export { createItemParser, type BinaryParsedItem, type ItemHandler, type ItemParserContext, } from './item-parser.js';
|
|
3
|
+
export { readD2S, type D2SReadResult, type D2SCharacterProfile } from './d2s-reader.js';
|
|
4
|
+
export { readD2I, type D2IReadResult, type D2IStashPage, type ExtendedPageType, } from './d2i-reader.js';
|
|
5
|
+
export { writeItem, writeItemList, buildWriteEntries, type ItemWriteEntry, type ItemWriteLocation, } from './item-writer.js';
|
|
6
|
+
export { writeD2S, type WriteD2SOptions } from './d2s-writer.js';
|
|
7
|
+
export { writeStash, buildStashWritePages, patchStashPage, type WriteStashPage, } from './d2i-writer.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EACL,OAAO,EACP,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { detectFormat } from './detect.js';
|
|
2
|
+
export { createItemParser, } from './item-parser.js';
|
|
3
|
+
export { readD2S } from './d2s-reader.js';
|
|
4
|
+
export { readD2I, } from './d2i-reader.js';
|
|
5
|
+
export { writeItem, writeItemList, buildWriteEntries, } from './item-writer.js';
|
|
6
|
+
export { writeD2S } from './d2s-writer.js';
|
|
7
|
+
export { writeStash, buildStashWritePages, patchStashPage, } from './d2i-writer.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/formats/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,aAAa,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAgD,MAAM,iBAAiB,CAAC;AACxF,OAAO,EACL,OAAO,GAIR,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,SAAS,EACT,aAAa,EACb,iBAAiB,GAGlB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAwB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,cAAc,GAEf,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Item binary parser — reads items from D2R v105 save files.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/index.js → createItemParser().
|
|
5
|
+
* Simplified: Blizzless v105 only (D2R=true, V105=true, version=105).
|
|
6
|
+
*
|
|
7
|
+
* Uses GameData instead of global Data singleton.
|
|
8
|
+
*/
|
|
9
|
+
import type { BinaryReader } from '../core/binary-reader.js';
|
|
10
|
+
import type { GameData } from '../game-data/game-data.js';
|
|
11
|
+
/** Maps the 4-bit quality field from the binary format to our Quality enum values. */
|
|
12
|
+
export declare const gameQualityMap: readonly [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
|
13
|
+
/** Parsed item representation from the binary reader. */
|
|
14
|
+
export interface BinaryParsedItem {
|
|
15
|
+
itemId: number;
|
|
16
|
+
base: string;
|
|
17
|
+
quality: number;
|
|
18
|
+
ilvl: number;
|
|
19
|
+
unidentified: boolean;
|
|
20
|
+
ethereal: boolean;
|
|
21
|
+
/** Set when armor item has eth-bug (defense computed via inverted ethereal math). */
|
|
22
|
+
ethbugged?: boolean;
|
|
23
|
+
socketed: boolean;
|
|
24
|
+
sockets: number;
|
|
25
|
+
/**
|
|
26
|
+
* IDs of inserted socketed sub-items. Numeric for regular sub-items,
|
|
27
|
+
* string base-code for preset items (runes/gems/jewels) — matches
|
|
28
|
+
* d2planner's convention where preset items are keyed by their base code.
|
|
29
|
+
*/
|
|
30
|
+
socketedItems: (number | string)[];
|
|
31
|
+
stats: Record<string, number>;
|
|
32
|
+
unique?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
/** Raw magic affix codes (mp###/ms###) read straight from the binary. Kept on the item
|
|
35
|
+
* so the magic display name (prefix + base + suffix) can be rebuilt even when the stat
|
|
36
|
+
* solver fails on Blizzless data and clears the `mods` map. */
|
|
37
|
+
magicPrefixCode?: string;
|
|
38
|
+
magicSuffixCode?: string;
|
|
39
|
+
/** ALL prefix affix codes (mp###), in binary order. Magic items carry one; rare/crafted up to
|
|
40
|
+
* three. Persisted so the backend can fill the create-form Prefix slots (names only — the stats
|
|
41
|
+
* always live in the flat mod list, NEVER attributed back to an affix). */
|
|
42
|
+
prefixCodes?: string[];
|
|
43
|
+
/** ALL suffix affix codes (ms###), in binary order (magic: one; rare/crafted: up to three). */
|
|
44
|
+
suffixCodes?: string[];
|
|
45
|
+
uniqueValues?: number[];
|
|
46
|
+
/** Selected pg option index per property key (warlock property-group items). */
|
|
47
|
+
propertyIndex?: Record<string, number>;
|
|
48
|
+
mods?: Record<string, number[]>;
|
|
49
|
+
auto?: Record<string, number[]>;
|
|
50
|
+
staff?: Record<string, number[]>;
|
|
51
|
+
crafted?: Record<string, number[]>;
|
|
52
|
+
superior?: Record<string, number[]>;
|
|
53
|
+
defense?: number;
|
|
54
|
+
quantity?: number;
|
|
55
|
+
/** Personalized name string (when personalized flag set). */
|
|
56
|
+
personalized?: string;
|
|
57
|
+
/** Ear-item payload (when ear flag set). */
|
|
58
|
+
ear?: {
|
|
59
|
+
class: number;
|
|
60
|
+
level: number;
|
|
61
|
+
name: string;
|
|
62
|
+
};
|
|
63
|
+
iconIndex?: number;
|
|
64
|
+
custom?: boolean;
|
|
65
|
+
/** Active set-bonus property bits (5-bit field for SET quality items). */
|
|
66
|
+
setflags?: number;
|
|
67
|
+
/** Bit range in the original buffer. */
|
|
68
|
+
binaryOffset: {
|
|
69
|
+
start: number;
|
|
70
|
+
end: number;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Callback for item placement during parsing. */
|
|
74
|
+
export type ItemHandler = (id: number | string, location?: string, slot?: number | string, item?: BinaryParsedItem) => void;
|
|
75
|
+
/** Return type of createItemParser. */
|
|
76
|
+
export interface ItemParserContext {
|
|
77
|
+
parseItem: (itemId: number, handler: ItemHandler) => void;
|
|
78
|
+
parseItemList: (handler: ItemHandler) => void;
|
|
79
|
+
items: Record<number | string, BinaryParsedItem>;
|
|
80
|
+
nextId: () => number;
|
|
81
|
+
readonly currentId: number;
|
|
82
|
+
/** Non-fatal error from the last parseItemList call (set when an item mid-list throws). */
|
|
83
|
+
readonly error: string | null;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create a parser context for reading items from a BinaryReader.
|
|
87
|
+
* V105 only — no version branching.
|
|
88
|
+
*
|
|
89
|
+
* @param reader BinaryReader positioned before item data
|
|
90
|
+
* @param gd GameData instance
|
|
91
|
+
* @param initialId Starting item ID (default 0)
|
|
92
|
+
* @param options Parser options
|
|
93
|
+
*/
|
|
94
|
+
export declare function createItemParser(reader: BinaryReader, gd: GameData, initialId?: number, options?: {
|
|
95
|
+
forceStackable?: boolean;
|
|
96
|
+
}): ItemParserContext;
|
|
97
|
+
//# sourceMappingURL=item-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item-parser.d.ts","sourceRoot":"","sources":["../../src/formats/item-parser.ts"],"names":[],"mappings":"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"}
|