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,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"}
|