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.
Files changed (161) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -0
  3. package/data/data.json +1 -0
  4. package/data/strings.json +1 -0
  5. package/dist/core/binary-reader.d.ts +43 -0
  6. package/dist/core/binary-reader.d.ts.map +1 -0
  7. package/dist/core/binary-reader.js +130 -0
  8. package/dist/core/binary-reader.js.map +1 -0
  9. package/dist/core/binary-writer.d.ts +49 -0
  10. package/dist/core/binary-writer.d.ts.map +1 -0
  11. package/dist/core/binary-writer.js +156 -0
  12. package/dist/core/binary-writer.js.map +1 -0
  13. package/dist/core/checksum.d.ts +28 -0
  14. package/dist/core/checksum.d.ts.map +1 -0
  15. package/dist/core/checksum.js +62 -0
  16. package/dist/core/checksum.js.map +1 -0
  17. package/dist/core/huffman.d.ts +22 -0
  18. package/dist/core/huffman.d.ts.map +1 -0
  19. package/dist/core/huffman.js +66 -0
  20. package/dist/core/huffman.js.map +1 -0
  21. package/dist/core/index.d.ts +5 -0
  22. package/dist/core/index.d.ts.map +1 -0
  23. package/dist/core/index.js +5 -0
  24. package/dist/core/index.js.map +1 -0
  25. package/dist/formats/d2i-reader.d.ts +53 -0
  26. package/dist/formats/d2i-reader.d.ts.map +1 -0
  27. package/dist/formats/d2i-reader.js +208 -0
  28. package/dist/formats/d2i-reader.js.map +1 -0
  29. package/dist/formats/d2i-writer.d.ts +63 -0
  30. package/dist/formats/d2i-writer.d.ts.map +1 -0
  31. package/dist/formats/d2i-writer.js +229 -0
  32. package/dist/formats/d2i-writer.js.map +1 -0
  33. package/dist/formats/d2s-reader.d.ts +104 -0
  34. package/dist/formats/d2s-reader.d.ts.map +1 -0
  35. package/dist/formats/d2s-reader.js +257 -0
  36. package/dist/formats/d2s-reader.js.map +1 -0
  37. package/dist/formats/d2s-writer.d.ts +52 -0
  38. package/dist/formats/d2s-writer.d.ts.map +1 -0
  39. package/dist/formats/d2s-writer.js +407 -0
  40. package/dist/formats/d2s-writer.js.map +1 -0
  41. package/dist/formats/detect.d.ts +18 -0
  42. package/dist/formats/detect.d.ts.map +1 -0
  43. package/dist/formats/detect.js +40 -0
  44. package/dist/formats/detect.js.map +1 -0
  45. package/dist/formats/index.d.ts +8 -0
  46. package/dist/formats/index.d.ts.map +1 -0
  47. package/dist/formats/index.js +8 -0
  48. package/dist/formats/index.js.map +1 -0
  49. package/dist/formats/item-parser.d.ts +97 -0
  50. package/dist/formats/item-parser.d.ts.map +1 -0
  51. package/dist/formats/item-parser.js +627 -0
  52. package/dist/formats/item-parser.js.map +1 -0
  53. package/dist/formats/item-writer.d.ts +55 -0
  54. package/dist/formats/item-writer.d.ts.map +1 -0
  55. package/dist/formats/item-writer.js +514 -0
  56. package/dist/formats/item-writer.js.map +1 -0
  57. package/dist/game-data/game-data.d.ts +90 -0
  58. package/dist/game-data/game-data.d.ts.map +1 -0
  59. package/dist/game-data/game-data.js +355 -0
  60. package/dist/game-data/game-data.js.map +1 -0
  61. package/dist/game-data/index.d.ts +4 -0
  62. package/dist/game-data/index.d.ts.map +1 -0
  63. package/dist/game-data/index.js +3 -0
  64. package/dist/game-data/index.js.map +1 -0
  65. package/dist/game-data/loader.d.ts +23 -0
  66. package/dist/game-data/loader.d.ts.map +1 -0
  67. package/dist/game-data/loader.js +32 -0
  68. package/dist/game-data/loader.js.map +1 -0
  69. package/dist/game-data/types.d.ts +428 -0
  70. package/dist/game-data/types.d.ts.map +1 -0
  71. package/dist/game-data/types.js +10 -0
  72. package/dist/game-data/types.js.map +1 -0
  73. package/dist/index.d.ts +166 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +197 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/inventory/dimensions.d.ts +42 -0
  78. package/dist/inventory/dimensions.d.ts.map +1 -0
  79. package/dist/inventory/dimensions.js +30 -0
  80. package/dist/inventory/dimensions.js.map +1 -0
  81. package/dist/inventory/grid.d.ts +53 -0
  82. package/dist/inventory/grid.d.ts.map +1 -0
  83. package/dist/inventory/grid.js +117 -0
  84. package/dist/inventory/grid.js.map +1 -0
  85. package/dist/inventory/index.d.ts +4 -0
  86. package/dist/inventory/index.d.ts.map +1 -0
  87. package/dist/inventory/index.js +4 -0
  88. package/dist/inventory/index.js.map +1 -0
  89. package/dist/inventory/placement.d.ts +68 -0
  90. package/dist/inventory/placement.d.ts.map +1 -0
  91. package/dist/inventory/placement.js +90 -0
  92. package/dist/inventory/placement.js.map +1 -0
  93. package/dist/items/index.d.ts +7 -0
  94. package/dist/items/index.d.ts.map +1 -0
  95. package/dist/items/index.js +7 -0
  96. package/dist/items/index.js.map +1 -0
  97. package/dist/items/item-dto.d.ts +47 -0
  98. package/dist/items/item-dto.d.ts.map +1 -0
  99. package/dist/items/item-dto.js +124 -0
  100. package/dist/items/item-dto.js.map +1 -0
  101. package/dist/items/item-icon.d.ts +27 -0
  102. package/dist/items/item-icon.d.ts.map +1 -0
  103. package/dist/items/item-icon.js +103 -0
  104. package/dist/items/item-icon.js.map +1 -0
  105. package/dist/items/item-serializer.d.ts +38 -0
  106. package/dist/items/item-serializer.d.ts.map +1 -0
  107. package/dist/items/item-serializer.js +92 -0
  108. package/dist/items/item-serializer.js.map +1 -0
  109. package/dist/items/item-stats-parser.d.ts +52 -0
  110. package/dist/items/item-stats-parser.d.ts.map +1 -0
  111. package/dist/items/item-stats-parser.js +671 -0
  112. package/dist/items/item-stats-parser.js.map +1 -0
  113. package/dist/items/item-types.d.ts +40 -0
  114. package/dist/items/item-types.d.ts.map +1 -0
  115. package/dist/items/item-types.js +198 -0
  116. package/dist/items/item-types.js.map +1 -0
  117. package/dist/items/item.d.ts +61 -0
  118. package/dist/items/item.d.ts.map +1 -0
  119. package/dist/items/item.js +335 -0
  120. package/dist/items/item.js.map +1 -0
  121. package/dist/operations/extract-item.d.ts +39 -0
  122. package/dist/operations/extract-item.d.ts.map +1 -0
  123. package/dist/operations/extract-item.js +204 -0
  124. package/dist/operations/extract-item.js.map +1 -0
  125. package/dist/operations/index.d.ts +4 -0
  126. package/dist/operations/index.d.ts.map +1 -0
  127. package/dist/operations/index.js +4 -0
  128. package/dist/operations/index.js.map +1 -0
  129. package/dist/operations/insert-item.d.ts +59 -0
  130. package/dist/operations/insert-item.d.ts.map +1 -0
  131. package/dist/operations/insert-item.js +210 -0
  132. package/dist/operations/insert-item.js.map +1 -0
  133. package/dist/operations/read-save.d.ts +25 -0
  134. package/dist/operations/read-save.d.ts.map +1 -0
  135. package/dist/operations/read-save.js +27 -0
  136. package/dist/operations/read-save.js.map +1 -0
  137. package/dist/types/constants.d.ts +76 -0
  138. package/dist/types/constants.d.ts.map +1 -0
  139. package/dist/types/constants.js +96 -0
  140. package/dist/types/constants.js.map +1 -0
  141. package/dist/types/errors.d.ts +47 -0
  142. package/dist/types/errors.d.ts.map +1 -0
  143. package/dist/types/errors.js +53 -0
  144. package/dist/types/errors.js.map +1 -0
  145. package/dist/types/index.d.ts +5 -0
  146. package/dist/types/index.d.ts.map +1 -0
  147. package/dist/types/index.js +3 -0
  148. package/dist/types/index.js.map +1 -0
  149. package/dist/types/item.d.ts +112 -0
  150. package/dist/types/item.d.ts.map +1 -0
  151. package/dist/types/item.js +6 -0
  152. package/dist/types/item.js.map +1 -0
  153. package/dist/types/save-file.d.ts +74 -0
  154. package/dist/types/save-file.d.ts.map +1 -0
  155. package/dist/types/save-file.js +5 -0
  156. package/dist/types/save-file.js.map +1 -0
  157. package/dist/types/trade-item.d.ts +37 -0
  158. package/dist/types/trade-item.d.ts.map +1 -0
  159. package/dist/types/trade-item.js +5 -0
  160. package/dist/types/trade-item.js.map +1 -0
  161. 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":"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"}
@@ -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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"}