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,104 @@
1
+ /**
2
+ * D2S (character save) reader — Blizzless v105 only.
3
+ *
4
+ * Ported from d2planner/src/logic/binary/index.js → parseCharacter().
5
+ * Simplified: v105 only — all offsets use v105 values.
6
+ *
7
+ * Uses GameData instead of global Data singleton.
8
+ */
9
+ import { GameData } from '../game-data/game-data.js';
10
+ import { type BinaryParsedItem } from './item-parser.js';
11
+ export interface D2SCharacterProfile {
12
+ name: string;
13
+ class: string;
14
+ level: number;
15
+ stats: {
16
+ str: number;
17
+ dex: number;
18
+ int: number;
19
+ vit: number;
20
+ };
21
+ skills: Record<number, number>;
22
+ quests: Array<{
23
+ denofevil: boolean;
24
+ radamentslair: boolean;
25
+ thegoldenbird: boolean;
26
+ lamessenstome: boolean;
27
+ thefallenangel: boolean;
28
+ prisonofice: boolean;
29
+ }>;
30
+ weaponSet?: number;
31
+ mercName?: number;
32
+ merc?: string;
33
+ mercLevel?: number;
34
+ /**
35
+ * Slot maps and arrays carry either numeric item IDs OR string base codes
36
+ * (for preset items like runes / gems / potions, which are keyed by base
37
+ * code in the items dict).
38
+ */
39
+ items: Record<string, number | string>;
40
+ mercItems: Record<string, number | string>;
41
+ inventory: (number | string | undefined)[];
42
+ cube: (number | string | undefined)[];
43
+ stash: (number | string | undefined)[];
44
+ belt: (number | string | undefined)[];
45
+ /** Gold carried in the character's inventory (the `gold` stat). */
46
+ gold?: number;
47
+ /** Gold in the character's personal stash (the `goldbank` stat). */
48
+ goldStash?: number;
49
+ /**
50
+ * Raw character stat map straight from the save (stat name → value), e.g.
51
+ * strength, dexterity, vitality, energy, level, experience, maxhp, maxmana,
52
+ * maxstamina, statpts, newskills. Life/mana/stamina are 8.8 fixed-point
53
+ * (divide by 256 for the displayed integer).
54
+ */
55
+ attributes?: Record<string, number>;
56
+ ironGolem?: number | string;
57
+ /**
58
+ * Raw bytes of the v105 "extra sections" tail (typically the 0x666c warlock
59
+ * demon block). Preserved verbatim from the source file so the writer can
60
+ * round-trip them unchanged.
61
+ */
62
+ extraSections?: number[];
63
+ /**
64
+ * Raw bytes [0, 833) of the fixed v105 header, captured verbatim. Lets the
65
+ * writer round-trip every header field (name, class, merc header, quests,
66
+ * waypoints, appearance, difficulty, skill hotkeys, timestamps, progression)
67
+ * without recomputing them. Only filesize + checksum are re-derived on write.
68
+ */
69
+ rawHeader?: number[];
70
+ /** Raw bytes of the corpse section (0x4d4a marker + corpse item lists). */
71
+ rawCorpse?: number[];
72
+ /** Raw bytes of the iron-golem section (0x666b marker + optional golem item). */
73
+ rawGolem?: number[];
74
+ /**
75
+ * Parsed warlock bind-demon descriptor when the 0x666c extras block
76
+ * decodes successfully. Optional and best-effort.
77
+ */
78
+ bindDemon?: {
79
+ revive: 'bind';
80
+ id: string;
81
+ monsterType: string;
82
+ monster: string;
83
+ area: number;
84
+ level: number;
85
+ unit: string;
86
+ group: string;
87
+ minion: boolean;
88
+ skill: string;
89
+ mods?: string[];
90
+ };
91
+ }
92
+ export interface D2SReadResult {
93
+ profile: D2SCharacterProfile;
94
+ items: Record<number, BinaryParsedItem>;
95
+ warnings: string[];
96
+ }
97
+ /**
98
+ * Parse a .d2s character file.
99
+ *
100
+ * @param data Raw file bytes
101
+ * @param gd GameData instance
102
+ */
103
+ export declare function readD2S(data: Uint8Array, gd: GameData): D2SReadResult;
104
+ //# sourceMappingURL=d2s-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d2s-reader.d.ts","sourceRoot":"","sources":["../../src/formats/d2s-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * D2S (character save) reader — Blizzless v105 only.
3
+ *
4
+ * Ported from d2planner/src/logic/binary/index.js → parseCharacter().
5
+ * Simplified: v105 only — all offsets use v105 values.
6
+ *
7
+ * Uses GameData instead of global Data singleton.
8
+ */
9
+ import { BinaryReader } from '../core/binary-reader.js';
10
+ import { GameData } from '../game-data/game-data.js';
11
+ import { detectFormat } from './detect.js';
12
+ import { createItemParser } from './item-parser.js';
13
+ // ─── Reader ─────────────────────────────────────────────────────
14
+ /**
15
+ * Parse a .d2s character file.
16
+ *
17
+ * @param data Raw file bytes
18
+ * @param gd GameData instance
19
+ */
20
+ export function readD2S(data, gd) {
21
+ const warnings = [];
22
+ const fmt = detectFormat(data);
23
+ if (!fmt || fmt.type !== 'd2s')
24
+ throw Error('invalid d2s header');
25
+ const reader = new BinaryReader(data);
26
+ reader.seek(16);
27
+ const result = {};
28
+ // Preserve the fixed 833-byte v105 header verbatim for lossless round-tripping.
29
+ // (The stats section always starts at offset 833 in this format.)
30
+ result.rawHeader = Array.from(data.subarray(0, 833));
31
+ // v105: 4 zero bytes at offset 0x10
32
+ reader.skip(4);
33
+ // Name might be at offset 299 (v105)
34
+ if (!result.name) {
35
+ const savedPos = reader.bitpos;
36
+ reader.seek(299);
37
+ result.name = reader.utf8(16);
38
+ reader.bitpos = savedPos;
39
+ }
40
+ // Class at offset 24 (v105)
41
+ reader.seek(24);
42
+ const classId = reader.read8();
43
+ result.class = GameData.classes[classId] || `unknown_${classId}`;
44
+ // Merc data at offset 163 (v105)
45
+ reader.seek(163);
46
+ const mercId = reader.read32();
47
+ if (mercId) {
48
+ result.mercName = reader.read16();
49
+ result.merc = reader.read16().toString();
50
+ const mercXP = reader.read32();
51
+ const mercData = gd.hireling[result.merc]?.[0];
52
+ if (mercData) {
53
+ let L = mercData.level;
54
+ const M = mercData.explvl;
55
+ while (L < 98 && M * (L + 1) * (L + 1) * (L + 2) <= mercXP) {
56
+ L += 1;
57
+ }
58
+ result.mercLevel = L;
59
+ }
60
+ }
61
+ // Quests
62
+ function readQuestTable() {
63
+ return {
64
+ denofevil: (reader.byte(2) & 1) !== 0,
65
+ radamentslair: (reader.byte(18) & 1) !== 0,
66
+ thegoldenbird: (reader.byte(40) & 1) !== 0,
67
+ lamessenstome: (reader.byte(34) & 1) !== 0,
68
+ thefallenangel: (reader.byte(50) & 1) !== 0,
69
+ prisonofice: (reader.byte(74) & 1) !== 0,
70
+ };
71
+ }
72
+ result.quests = [];
73
+ reader.seek(413); // v105 quest offset normal
74
+ result.quests.push(readQuestTable());
75
+ reader.seek(509); // nightmare
76
+ result.quests.push(readQuestTable());
77
+ reader.seek(605); // hell
78
+ result.quests.push(readQuestTable());
79
+ // Character stats at offset 833 (v105)
80
+ reader.seek(833);
81
+ if (reader.read16() !== 0x6667)
82
+ throw Error('invalid stats header');
83
+ // Build stat map
84
+ const statMap = new Map();
85
+ for (const key in gd.itemStatCost) {
86
+ statMap.set(gd.itemStatCost[key].id, key);
87
+ }
88
+ const charStats = {};
89
+ while (true) {
90
+ const statId = reader.bits(9);
91
+ if (statId === 511)
92
+ break;
93
+ const stat = statMap.get(statId);
94
+ if (!stat)
95
+ throw Error(`unknown stat code ${statId}`);
96
+ charStats[stat] = reader.bits(gd.itemStatCost[stat].csvbits ?? 0);
97
+ }
98
+ reader.align();
99
+ const classStats = gd.charStats[result.class];
100
+ result.stats = {
101
+ str: charStats.strength - (classStats?.str || 0),
102
+ dex: charStats.dexterity - (classStats?.dex || 0),
103
+ int: charStats.energy - (classStats?.int || 0),
104
+ vit: charStats.vitality - (classStats?.vit || 0),
105
+ };
106
+ result.level = charStats.level;
107
+ result.gold = charStats.gold ?? 0;
108
+ result.goldStash = charStats.goldbank ?? 0;
109
+ result.attributes = charStats;
110
+ // Skills
111
+ if (reader.read16() !== 0x6669)
112
+ throw Error('invalid skills header');
113
+ result.skills = {};
114
+ const skillBases = [6, 36, 66, 96, 126, 221, 251, 373];
115
+ const skillBase = skillBases[classId] ?? (classId < 5 ? classId * 30 + 6 : (classId - 5) * 30 + 221);
116
+ for (let i = 0; i < 30; ++i) {
117
+ const level = reader.read8();
118
+ if (level)
119
+ result.skills[i + skillBase] = level;
120
+ }
121
+ // Items
122
+ const ctx = createItemParser(reader, gd);
123
+ result.items = {};
124
+ result.inventory = [];
125
+ result.cube = [];
126
+ result.stash = [];
127
+ result.belt = [];
128
+ try {
129
+ ctx.parseItemList((id, location, slot) => {
130
+ if (location === 'body')
131
+ result.items[slot] = id;
132
+ if (location === 'inventory')
133
+ result.inventory[slot] = id;
134
+ if (location === 'cube')
135
+ result.cube[slot] = id;
136
+ if (location === 'stash')
137
+ result.stash[slot] = id;
138
+ if (location === 'belt')
139
+ result.belt[slot] = id;
140
+ });
141
+ // Corpses — captured verbatim so the writer preserves corpse items.
142
+ const corpseStart = reader.bitpos >> 3;
143
+ if (reader.read16() !== 0x4d4a)
144
+ throw Error('invalid item table header');
145
+ const corpses = reader.read16();
146
+ reader.skip(corpses * 12);
147
+ for (let i = 0; i < corpses; ++i) {
148
+ ctx.parseItemList(() => null);
149
+ }
150
+ result.rawCorpse = Array.from(reader.buffer.subarray(corpseStart, reader.bitpos >> 3));
151
+ // Hireling items
152
+ if (!reader.eof()) {
153
+ if (reader.read16() !== 0x666a)
154
+ throw Error('invalid hireling header');
155
+ result.mercItems = {};
156
+ if (reader.byte(0) === 0x4a && reader.byte(1) === 0x4d) {
157
+ ctx.parseItemList((id, location, slot) => {
158
+ if (mercId && location === 'body')
159
+ result.mercItems[slot] = id;
160
+ });
161
+ }
162
+ }
163
+ // Iron golem — captured verbatim so the writer preserves the golem item.
164
+ if (!reader.eof()) {
165
+ const golemStart = reader.bitpos >> 3;
166
+ if (reader.read16() !== 0x666b)
167
+ throw Error('invalid iron golem header');
168
+ if (reader.read8()) {
169
+ ctx.parseItem(ctx.nextId(), id => { result.ironGolem = typeof id === 'number' ? id : 0; });
170
+ }
171
+ result.rawGolem = Array.from(reader.buffer.subarray(golemStart, reader.bitpos >> 3));
172
+ }
173
+ // Preserve extra sections (0x666c warlock demon data) as raw bytes,
174
+ // and best-effort parse the bind-demon descriptor.
175
+ if (!reader.eof()) {
176
+ const bytePos = reader.bitpos >> 3;
177
+ result.extraSections = Array.from(reader.buffer.subarray(bytePos));
178
+ try {
179
+ if (reader.read16() === 1 && reader.read16() === 0x666c) {
180
+ const count = reader.read16();
181
+ if (count > 0) {
182
+ reader.read16(); // entrySize
183
+ const idType = reader.read16(); // 1=normal monster by hcidx, 2=super unique
184
+ const binaryId = reader.read16();
185
+ const monId = binaryId - 1; // hcidx (1-indexed in binary)
186
+ reader.read16(); // skip
187
+ const monType = reader.read16(); // 8=unique, 12=champion
188
+ reader.read16(); // skip
189
+ reader.read8(); // diff
190
+ reader.skip(11);
191
+ const area = reader.read32();
192
+ const level = reader.read32();
193
+ reader.skip(48);
194
+ let monClass;
195
+ let demonId;
196
+ let monsterType;
197
+ if (idType === 2) {
198
+ const su = gd.superUniques?.[String(monId)];
199
+ monClass = su?.class;
200
+ demonId = String(monId);
201
+ monsterType = 'super';
202
+ }
203
+ else if (idType === 1) {
204
+ const entry = Object.entries(gd.monsters).find(([, v]) => v?.hcidx === monId);
205
+ monClass = entry?.[0];
206
+ demonId = monClass;
207
+ monsterType = monType === 12 ? 'champion' : 'unique';
208
+ }
209
+ if (monClass && demonId && monsterType) {
210
+ result.bindDemon = {
211
+ revive: 'bind',
212
+ id: demonId,
213
+ monsterType,
214
+ monster: monClass,
215
+ area,
216
+ level,
217
+ unit: `bind_${monClass}`,
218
+ group: 'binddemon',
219
+ minion: monType !== 16,
220
+ skill: '382',
221
+ };
222
+ const presetMods = idType === 2
223
+ ? [
224
+ gd.uniqueMods?.[gd.superUniques?.[String(monId)]?.mod1 ?? '']?.uniquemod,
225
+ gd.uniqueMods?.[gd.superUniques?.[String(monId)]?.mod2 ?? '']?.uniquemod,
226
+ gd.uniqueMods?.[gd.superUniques?.[String(monId)]?.mod3 ?? '']?.uniquemod,
227
+ ].filter(Boolean)
228
+ : [];
229
+ const allMods = new Set();
230
+ for (let m = 0; m < 8; m++) {
231
+ const um = gd.uniqueMods?.[String(reader.read8())]?.uniquemod;
232
+ if (um)
233
+ allMods.add(um);
234
+ }
235
+ const demonMods = [...allMods].filter(m => !presetMods.includes(m));
236
+ if (demonMods.length)
237
+ result.bindDemon.mods = demonMods;
238
+ }
239
+ }
240
+ }
241
+ }
242
+ catch {
243
+ // best-effort; non-critical
244
+ }
245
+ }
246
+ }
247
+ catch (e) {
248
+ const warning = `Item parsing error (some items may be missing): ${e.message}`;
249
+ warnings.push(warning);
250
+ }
251
+ return {
252
+ profile: result,
253
+ items: ctx.items,
254
+ warnings,
255
+ };
256
+ }
257
+ //# sourceMappingURL=d2s-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d2s-reader.js","sourceRoot":"","sources":["../../src/formats/d2s-reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
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 { GameData } from '../game-data/game-data.js';
11
+ import type { D2SCharacterProfile } from './d2s-reader.js';
12
+ import type { BinaryParsedItem } from './item-parser.js';
13
+ /**
14
+ * Options for writeD2S.
15
+ * Only `profile`, `items`, and `gd` are required.
16
+ * Everything else has sensible defaults for a Blizzless v105 character.
17
+ */
18
+ export interface WriteD2SOptions {
19
+ /** Character profile (as returned by readD2S). */
20
+ profile: D2SCharacterProfile;
21
+ /** All items keyed by item ID. */
22
+ items: Record<number | string, BinaryParsedItem>;
23
+ /** GameData instance for itemStatCost, charStats, etc. */
24
+ gd: GameData;
25
+ /** Override character name (uses profile.name by default). */
26
+ name?: string;
27
+ /**
28
+ * Explicit character stats (base totals = base class + allocated).
29
+ * If not supplied, stats are computed from profile.stats + charStats base.
30
+ */
31
+ charStats?: {
32
+ strength: number;
33
+ energy: number;
34
+ dexterity: number;
35
+ vitality: number;
36
+ hitpoints: number;
37
+ maxhp: number;
38
+ mana: number;
39
+ maxmana: number;
40
+ stamina: number;
41
+ maxstamina: number;
42
+ statpts?: number;
43
+ newskills?: number;
44
+ };
45
+ }
46
+ /**
47
+ * Write a complete .d2s character save file (Blizzless v105).
48
+ *
49
+ * @returns Raw file bytes ready to be saved.
50
+ */
51
+ export declare function writeD2S(opts: WriteD2SOptions): Uint8Array;
52
+ //# sourceMappingURL=d2s-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d2s-writer.d.ts","sourceRoot":"","sources":["../../src/formats/d2s-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAwBzD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACjD,0DAA0D;IAC1D,EAAE,EAAE,QAAQ,CAAC;IACb,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAID;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CA8J1D"}