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,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2I (shared stash) reader — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/index.js → parseStash().
|
|
5
|
+
* Simplified: v105 only — all version branches removed.
|
|
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
|
+
/** Extended page item category. */
|
|
12
|
+
export type ExtendedPageType = 'gems' | 'runes' | 'misc';
|
|
13
|
+
/** A stash page as parsed from the binary d2i file. */
|
|
14
|
+
export interface D2IStashPage {
|
|
15
|
+
/** Zero-based page index in result.pages[]. */
|
|
16
|
+
index: number;
|
|
17
|
+
/**
|
|
18
|
+
* Page type:
|
|
19
|
+
* - 0 = normal stash page
|
|
20
|
+
* - 1 = extended page (the original binary page)
|
|
21
|
+
* - 'gems' | 'runes' | 'misc' = virtual sub-page split from an extended page
|
|
22
|
+
*/
|
|
23
|
+
pageType: number | ExtendedPageType;
|
|
24
|
+
/** Gold on this page (only the first virtual sub-page inherits gold from extended). */
|
|
25
|
+
gold: number;
|
|
26
|
+
/** Slot array: `stash[slot] = itemId`. Slots use `row * columns + col`. */
|
|
27
|
+
stash: (number | string | undefined)[];
|
|
28
|
+
/** Per-slot quantities for extended page items (keyed by slot index). */
|
|
29
|
+
quantities?: Record<number, number> | number[];
|
|
30
|
+
/** Number of rows for the misc virtual page. */
|
|
31
|
+
rows?: number;
|
|
32
|
+
/** Byte offset of the raw sector in the original d2i file. */
|
|
33
|
+
offset: number;
|
|
34
|
+
/** Byte size of the raw sector. */
|
|
35
|
+
sectorSize: number;
|
|
36
|
+
}
|
|
37
|
+
/** Result of parsing a .d2i shared stash file. */
|
|
38
|
+
export interface D2IReadResult {
|
|
39
|
+
/** Parsed stash pages (extended pages are split into virtual sub-pages). */
|
|
40
|
+
pages: D2IStashPage[];
|
|
41
|
+
/** All parsed items keyed by item ID. */
|
|
42
|
+
items: Record<number, BinaryParsedItem>;
|
|
43
|
+
/** Warnings from parsing (e.g. unknown stats, partial failures). */
|
|
44
|
+
warnings: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse a .d2i shared stash file.
|
|
48
|
+
*
|
|
49
|
+
* @param data Raw file bytes
|
|
50
|
+
* @param gd GameData instance
|
|
51
|
+
*/
|
|
52
|
+
export declare function readD2I(data: Uint8Array, gd: GameData): D2IReadResult;
|
|
53
|
+
//# sourceMappingURL=d2i-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d2i-reader.d.ts","sourceRoot":"","sources":["../../src/formats/d2i-reader.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,OAAO,EAAoB,KAAK,gBAAgB,EAAoB,MAAM,kBAAkB,CAAC;AAK7F,mCAAmC;AACnC,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzD,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACpC,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,KAAK,EAAE,CAAC,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IACvC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC;IAC/C,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAID;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,GAAG,aAAa,CAqDrE"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2I (shared stash) reader — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* Ported from d2planner/src/logic/binary/index.js → parseStash().
|
|
5
|
+
* Simplified: v105 only — all version branches removed.
|
|
6
|
+
*
|
|
7
|
+
* Uses GameData instead of global Data singleton.
|
|
8
|
+
*/
|
|
9
|
+
import { BinaryReader } from '../core/binary-reader.js';
|
|
10
|
+
import { detectFormat } from './detect.js';
|
|
11
|
+
import { createItemParser } from './item-parser.js';
|
|
12
|
+
import { isSubType } from '../items/item-types.js';
|
|
13
|
+
// ─── Reader ─────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Parse a .d2i shared stash file.
|
|
16
|
+
*
|
|
17
|
+
* @param data Raw file bytes
|
|
18
|
+
* @param gd GameData instance
|
|
19
|
+
*/
|
|
20
|
+
export function readD2I(data, gd) {
|
|
21
|
+
const buffer = new Uint8Array(data);
|
|
22
|
+
const pages = [];
|
|
23
|
+
const allItems = {};
|
|
24
|
+
const warnings = [];
|
|
25
|
+
let offset = 0;
|
|
26
|
+
let nextItemId = 1;
|
|
27
|
+
while (offset < buffer.length) {
|
|
28
|
+
if (offset + 64 > buffer.length)
|
|
29
|
+
break;
|
|
30
|
+
const pageData = buffer.subarray(offset);
|
|
31
|
+
const reader = new BinaryReader(pageData);
|
|
32
|
+
const fmt = detectFormat(pageData);
|
|
33
|
+
if (!fmt || fmt.type !== 'd2i')
|
|
34
|
+
break;
|
|
35
|
+
// D2I page header: magic(4) + ?(4) + version(4) + gold(4) + sectorSize(4) + pageType(1)
|
|
36
|
+
reader.seek(12);
|
|
37
|
+
const gold = reader.read32();
|
|
38
|
+
const sectorSize = reader.read32();
|
|
39
|
+
const pageType = reader.read8();
|
|
40
|
+
if (pageType === 2) {
|
|
41
|
+
// Metadata page — skip entirely
|
|
42
|
+
offset += sectorSize;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Items start at byte 64
|
|
46
|
+
reader.seek(64);
|
|
47
|
+
if (pageType === 1) {
|
|
48
|
+
// Extended page: split into 3 virtual tabs (gems, runes, materials)
|
|
49
|
+
parseExtendedPage(reader, gd, gold, offset, sectorSize, pages, allItems, warnings, nextItemId);
|
|
50
|
+
// Update nextItemId from the items that were just parsed
|
|
51
|
+
for (const key of Object.keys(allItems)) {
|
|
52
|
+
const id = Number(key);
|
|
53
|
+
if (id >= nextItemId)
|
|
54
|
+
nextItemId = id + 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Normal stash page
|
|
59
|
+
parseNormalPage(reader, gd, gold, pageType, offset, sectorSize, pages, allItems, warnings, nextItemId);
|
|
60
|
+
for (const key of Object.keys(allItems)) {
|
|
61
|
+
const id = Number(key);
|
|
62
|
+
if (id >= nextItemId)
|
|
63
|
+
nextItemId = id + 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
offset += sectorSize;
|
|
67
|
+
}
|
|
68
|
+
return { pages, items: allItems, warnings };
|
|
69
|
+
}
|
|
70
|
+
// ─── Normal page ────────────────────────────────────────────────
|
|
71
|
+
function parseNormalPage(reader, gd, gold, pageType, offset, sectorSize, pages, allItems, warnings, nextItemId) {
|
|
72
|
+
const stash = [];
|
|
73
|
+
const ctx = createItemParser(reader, gd, nextItemId);
|
|
74
|
+
try {
|
|
75
|
+
ctx.parseItemList((id, location, slot) => {
|
|
76
|
+
if (location === 'stash' && slot !== undefined) {
|
|
77
|
+
stash[slot] = id;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
83
|
+
const warning = `Stash page ${pages.length} parsing error: ${msg}`;
|
|
84
|
+
warnings.push(warning);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
Object.assign(allItems, ctx.items);
|
|
88
|
+
}
|
|
89
|
+
pages.push({
|
|
90
|
+
index: pages.length,
|
|
91
|
+
pageType,
|
|
92
|
+
gold,
|
|
93
|
+
stash,
|
|
94
|
+
offset,
|
|
95
|
+
sectorSize,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// ─── Extended page ──────────────────────────────────────────────
|
|
99
|
+
function parseExtendedPage(reader, gd, gold, offset, sectorSize, pages, allItems, warnings, nextItemId) {
|
|
100
|
+
const gems = [];
|
|
101
|
+
const runes = [];
|
|
102
|
+
const misc = [];
|
|
103
|
+
const gemQty = [];
|
|
104
|
+
const runeQty = [];
|
|
105
|
+
const ctx = createItemParser(reader, gd, nextItemId);
|
|
106
|
+
try {
|
|
107
|
+
const handler = (id, location, _slot, item) => {
|
|
108
|
+
if (location === 'stash') {
|
|
109
|
+
const base = item?.base || (typeof id === 'number' ? ctx.items[id]?.base : undefined);
|
|
110
|
+
if (!base)
|
|
111
|
+
return;
|
|
112
|
+
const itemEntry = gd.items[base];
|
|
113
|
+
const type = itemEntry?.type;
|
|
114
|
+
const qty = item?.quantity || ctx.items[id]?.quantity || 0;
|
|
115
|
+
if (!qty)
|
|
116
|
+
return;
|
|
117
|
+
if (type && isSubType(gd, type, 'gem')) {
|
|
118
|
+
gems.push(id);
|
|
119
|
+
gemQty.push(qty);
|
|
120
|
+
}
|
|
121
|
+
else if (type && isSubType(gd, type, 'rune')) {
|
|
122
|
+
runes.push(id);
|
|
123
|
+
runeQty.push(qty);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
misc.push({ id, qty });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
ctx.parseItemList(handler);
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
134
|
+
const warning = `Stash page ${pages.length} (extended) parsing error: ${msg}`;
|
|
135
|
+
warnings.push(warning);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
Object.assign(allItems, ctx.items);
|
|
139
|
+
}
|
|
140
|
+
// Pack misc items into a grid respecting actual item sizes
|
|
141
|
+
const miscStash = [];
|
|
142
|
+
const miscQty = {};
|
|
143
|
+
const miscCols = 10;
|
|
144
|
+
const occupied = new Set();
|
|
145
|
+
let miscMaxRow = 0;
|
|
146
|
+
for (const { id, qty } of misc) {
|
|
147
|
+
const baseCode = allItems[id]?.base;
|
|
148
|
+
const baseItem = baseCode
|
|
149
|
+
? gd.items[baseCode]
|
|
150
|
+
: undefined;
|
|
151
|
+
const w = baseItem?.invwidth || 1;
|
|
152
|
+
const h = baseItem?.invheight || 1;
|
|
153
|
+
for (let slot = 0;; slot++) {
|
|
154
|
+
const x = slot % miscCols;
|
|
155
|
+
const y = Math.floor(slot / miscCols);
|
|
156
|
+
if (x + w > miscCols)
|
|
157
|
+
continue;
|
|
158
|
+
let fits = true;
|
|
159
|
+
for (let dy = 0; dy < h && fits; dy++) {
|
|
160
|
+
for (let dx = 0; dx < w && fits; dx++) {
|
|
161
|
+
if (occupied.has((y + dy) * miscCols + (x + dx)))
|
|
162
|
+
fits = false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (fits) {
|
|
166
|
+
miscStash[slot] = id;
|
|
167
|
+
miscQty[slot] = qty;
|
|
168
|
+
for (let dy = 0; dy < h; dy++) {
|
|
169
|
+
for (let dx = 0; dx < w; dx++) {
|
|
170
|
+
occupied.add((y + dy) * miscCols + (x + dx));
|
|
171
|
+
miscMaxRow = Math.max(miscMaxRow, y + dy);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const miscRows = misc.length ? miscMaxRow + 1 : 1;
|
|
179
|
+
pages.push({
|
|
180
|
+
index: pages.length,
|
|
181
|
+
pageType: 'gems',
|
|
182
|
+
gold,
|
|
183
|
+
stash: gems,
|
|
184
|
+
quantities: gemQty,
|
|
185
|
+
offset,
|
|
186
|
+
sectorSize,
|
|
187
|
+
});
|
|
188
|
+
pages.push({
|
|
189
|
+
index: pages.length,
|
|
190
|
+
pageType: 'runes',
|
|
191
|
+
gold: 0,
|
|
192
|
+
stash: runes,
|
|
193
|
+
quantities: runeQty,
|
|
194
|
+
offset,
|
|
195
|
+
sectorSize,
|
|
196
|
+
});
|
|
197
|
+
pages.push({
|
|
198
|
+
index: pages.length,
|
|
199
|
+
pageType: 'misc',
|
|
200
|
+
gold: 0,
|
|
201
|
+
stash: miscStash,
|
|
202
|
+
quantities: miscQty,
|
|
203
|
+
rows: miscRows,
|
|
204
|
+
offset,
|
|
205
|
+
sectorSize,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=d2i-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d2i-reader.js","sourceRoot":"","sources":["../../src/formats/d2i-reader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2I (shared stash) writer — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* **New functionality** — d2planner has no writeStash() equivalent.
|
|
5
|
+
* Reverse-engineered from the parseStash() reader and the binary format:
|
|
6
|
+
*
|
|
7
|
+
* [0x00] Magic: 0xAA55AA55 (4 bytes)
|
|
8
|
+
* [0x04] Mode: uint32 = 2 (4 bytes)
|
|
9
|
+
* [0x08] Version: uint32 = 105 (4 bytes)
|
|
10
|
+
* [0x0C] Gold: uint32 (4 bytes)
|
|
11
|
+
* [0x10] SectorSize: uint32 (4 bytes) — patched after writing items
|
|
12
|
+
* [0x14] PageType: uint8 (1 byte)
|
|
13
|
+
* [0x15] Padding: zeros (43 bytes → total header = 64 bytes)
|
|
14
|
+
* [0x40] Items: JM header + items (via writeItemList)
|
|
15
|
+
*
|
|
16
|
+
* Supports:
|
|
17
|
+
* - writeStash() — full rewrite of all pages
|
|
18
|
+
* - patchStashPage() — incremental patch: replace one page, leave rest as-is
|
|
19
|
+
*/
|
|
20
|
+
import { GameData } from '../game-data/game-data.js';
|
|
21
|
+
import type { BinaryParsedItem } from './item-parser.js';
|
|
22
|
+
import type { D2IStashPage } from './d2i-reader.js';
|
|
23
|
+
import { type ItemWriteEntry } from './item-writer.js';
|
|
24
|
+
/** Options for a single stash page to be written. */
|
|
25
|
+
export interface WriteStashPage {
|
|
26
|
+
/** Page type for the binary header (0 = normal, 1 = extended). */
|
|
27
|
+
pageType: number;
|
|
28
|
+
/** Gold on this page. */
|
|
29
|
+
gold: number;
|
|
30
|
+
/** Item entries to write on this page. */
|
|
31
|
+
entries: ItemWriteEntry[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Write a complete .d2i file from an array of pages.
|
|
35
|
+
*
|
|
36
|
+
* @param pages Array of page descriptions with items.
|
|
37
|
+
* @param allItems All items (for socketed item lookups).
|
|
38
|
+
* @param gd GameData instance.
|
|
39
|
+
* @returns Raw file bytes.
|
|
40
|
+
*/
|
|
41
|
+
export declare function writeStash(pages: WriteStashPage[], allItems: Record<number | string, BinaryParsedItem>, gd: GameData): Uint8Array;
|
|
42
|
+
/**
|
|
43
|
+
* Build WriteStashPage entries from parsed D2IStashPage + items.
|
|
44
|
+
*
|
|
45
|
+
* Converts the reader's page model back to writable entries.
|
|
46
|
+
* Extended sub-pages (gems/runes/misc) are merged back into a single
|
|
47
|
+
* extended page with pageType=1.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildStashWritePages(pages: D2IStashPage[], allItems: Record<number | string, BinaryParsedItem>, gd: GameData): WriteStashPage[];
|
|
50
|
+
/**
|
|
51
|
+
* Incremental patch: replace one page in an existing .d2i file.
|
|
52
|
+
*
|
|
53
|
+
* Copies all sectors as-is, but re-writes the target page.
|
|
54
|
+
*
|
|
55
|
+
* @param originalBuffer The original .d2i file bytes.
|
|
56
|
+
* @param pageIndex The zero-based page index to replace (in raw sector order, ignoring pageType=2 skips).
|
|
57
|
+
* @param newPage The new page content.
|
|
58
|
+
* @param allItems All items (for socketed item lookups).
|
|
59
|
+
* @param gd GameData instance.
|
|
60
|
+
* @returns New file bytes with the patched page.
|
|
61
|
+
*/
|
|
62
|
+
export declare function patchStashPage(originalBuffer: Uint8Array, pageIndex: number, newPage: WriteStashPage, allItems: Record<number | string, BinaryParsedItem>, gd: GameData): Uint8Array;
|
|
63
|
+
//# sourceMappingURL=d2i-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d2i-writer.d.ts","sourceRoot":"","sources":["../../src/formats/d2i-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAiBtE,qDAAqD;AACrD,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,cAAc,EAAE,EACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,EACnD,EAAE,EAAE,QAAQ,GACX,UAAU,CAgBZ;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EAAE,EACrB,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,EACnD,EAAE,EAAE,QAAQ,GACX,cAAc,EAAE,CA6ClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,cAAc,EAAE,UAAU,EAC1B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,EACnD,EAAE,EAAE,QAAQ,GACX,UAAU,CAuCZ"}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2I (shared stash) writer — Blizzless v105 only.
|
|
3
|
+
*
|
|
4
|
+
* **New functionality** — d2planner has no writeStash() equivalent.
|
|
5
|
+
* Reverse-engineered from the parseStash() reader and the binary format:
|
|
6
|
+
*
|
|
7
|
+
* [0x00] Magic: 0xAA55AA55 (4 bytes)
|
|
8
|
+
* [0x04] Mode: uint32 = 2 (4 bytes)
|
|
9
|
+
* [0x08] Version: uint32 = 105 (4 bytes)
|
|
10
|
+
* [0x0C] Gold: uint32 (4 bytes)
|
|
11
|
+
* [0x10] SectorSize: uint32 (4 bytes) — patched after writing items
|
|
12
|
+
* [0x14] PageType: uint8 (1 byte)
|
|
13
|
+
* [0x15] Padding: zeros (43 bytes → total header = 64 bytes)
|
|
14
|
+
* [0x40] Items: JM header + items (via writeItemList)
|
|
15
|
+
*
|
|
16
|
+
* Supports:
|
|
17
|
+
* - writeStash() — full rewrite of all pages
|
|
18
|
+
* - patchStashPage() — incremental patch: replace one page, leave rest as-is
|
|
19
|
+
*/
|
|
20
|
+
import { BitWriter } from '../core/binary-writer.js';
|
|
21
|
+
import { writeItemList } from './item-writer.js';
|
|
22
|
+
// ─── Constants ──────────────────────────────────────────────────
|
|
23
|
+
/** D2I magic signature (same as D2S). */
|
|
24
|
+
const D2I_MAGIC = 0xaa55aa55;
|
|
25
|
+
/** D2I mode field (always 2 in real files). */
|
|
26
|
+
const D2I_MODE = 2;
|
|
27
|
+
/** Blizzless version. */
|
|
28
|
+
const D2I_VERSION = 105;
|
|
29
|
+
/** D2I page header size in bytes. */
|
|
30
|
+
const PAGE_HEADER_SIZE = 64;
|
|
31
|
+
/** Default stash columns. */
|
|
32
|
+
const STASH_COLUMNS = 16;
|
|
33
|
+
/**
|
|
34
|
+
* Write a complete .d2i file from an array of pages.
|
|
35
|
+
*
|
|
36
|
+
* @param pages Array of page descriptions with items.
|
|
37
|
+
* @param allItems All items (for socketed item lookups).
|
|
38
|
+
* @param gd GameData instance.
|
|
39
|
+
* @returns Raw file bytes.
|
|
40
|
+
*/
|
|
41
|
+
export function writeStash(pages, allItems, gd) {
|
|
42
|
+
const sectors = [];
|
|
43
|
+
for (const page of pages) {
|
|
44
|
+
sectors.push(writeStashSector(page, allItems, gd));
|
|
45
|
+
}
|
|
46
|
+
// Concatenate all sectors
|
|
47
|
+
const totalSize = sectors.reduce((sum, s) => sum + s.length, 0);
|
|
48
|
+
const result = new Uint8Array(totalSize);
|
|
49
|
+
let offset = 0;
|
|
50
|
+
for (const sector of sectors) {
|
|
51
|
+
result.set(sector, offset);
|
|
52
|
+
offset += sector.length;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build WriteStashPage entries from parsed D2IStashPage + items.
|
|
58
|
+
*
|
|
59
|
+
* Converts the reader's page model back to writable entries.
|
|
60
|
+
* Extended sub-pages (gems/runes/misc) are merged back into a single
|
|
61
|
+
* extended page with pageType=1.
|
|
62
|
+
*/
|
|
63
|
+
export function buildStashWritePages(pages, allItems, gd) {
|
|
64
|
+
const result = [];
|
|
65
|
+
let i = 0;
|
|
66
|
+
while (i < pages.length) {
|
|
67
|
+
const page = pages[i];
|
|
68
|
+
if (typeof page.pageType === 'number') {
|
|
69
|
+
// Normal page (type 0) or other numeric type
|
|
70
|
+
const entries = buildStashEntries(page.stash, allItems, gd);
|
|
71
|
+
result.push({
|
|
72
|
+
pageType: page.pageType,
|
|
73
|
+
gold: page.gold,
|
|
74
|
+
entries,
|
|
75
|
+
});
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Extended virtual sub-pages: collect all consecutive sub-pages
|
|
80
|
+
// that share the same original offset (gems, runes, misc)
|
|
81
|
+
const extEntries = [];
|
|
82
|
+
const baseOffset = page.offset;
|
|
83
|
+
let extGold = 0;
|
|
84
|
+
while (i < pages.length) {
|
|
85
|
+
const subPage = pages[i];
|
|
86
|
+
if (typeof subPage.pageType === 'number')
|
|
87
|
+
break;
|
|
88
|
+
if (subPage.offset !== baseOffset && extEntries.length > 0)
|
|
89
|
+
break;
|
|
90
|
+
if (subPage.gold > 0)
|
|
91
|
+
extGold = subPage.gold;
|
|
92
|
+
// Collect items from each sub-page
|
|
93
|
+
const entriesToAdd = buildExtendedEntries(subPage, allItems, gd);
|
|
94
|
+
extEntries.push(...entriesToAdd);
|
|
95
|
+
i++;
|
|
96
|
+
}
|
|
97
|
+
result.push({
|
|
98
|
+
pageType: 1,
|
|
99
|
+
gold: extGold,
|
|
100
|
+
entries: extEntries,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Incremental patch: replace one page in an existing .d2i file.
|
|
108
|
+
*
|
|
109
|
+
* Copies all sectors as-is, but re-writes the target page.
|
|
110
|
+
*
|
|
111
|
+
* @param originalBuffer The original .d2i file bytes.
|
|
112
|
+
* @param pageIndex The zero-based page index to replace (in raw sector order, ignoring pageType=2 skips).
|
|
113
|
+
* @param newPage The new page content.
|
|
114
|
+
* @param allItems All items (for socketed item lookups).
|
|
115
|
+
* @param gd GameData instance.
|
|
116
|
+
* @returns New file bytes with the patched page.
|
|
117
|
+
*/
|
|
118
|
+
export function patchStashPage(originalBuffer, pageIndex, newPage, allItems, gd) {
|
|
119
|
+
const sectors = [];
|
|
120
|
+
let offset = 0;
|
|
121
|
+
let sectorIndex = 0;
|
|
122
|
+
while (offset < originalBuffer.length) {
|
|
123
|
+
if (offset + PAGE_HEADER_SIZE > originalBuffer.length)
|
|
124
|
+
break;
|
|
125
|
+
const dv = new DataView(originalBuffer.buffer, originalBuffer.byteOffset + offset);
|
|
126
|
+
const magic = dv.getUint32(0, true);
|
|
127
|
+
if (magic !== D2I_MAGIC)
|
|
128
|
+
break;
|
|
129
|
+
const sectorSize = dv.getUint32(16, true);
|
|
130
|
+
if (sectorSize < PAGE_HEADER_SIZE)
|
|
131
|
+
break;
|
|
132
|
+
if (sectorIndex === pageIndex) {
|
|
133
|
+
// Replace this sector
|
|
134
|
+
sectors.push(writeStashSector(newPage, allItems, gd));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Copy original sector as-is
|
|
138
|
+
sectors.push(originalBuffer.slice(offset, offset + sectorSize));
|
|
139
|
+
}
|
|
140
|
+
offset += sectorSize;
|
|
141
|
+
sectorIndex++;
|
|
142
|
+
}
|
|
143
|
+
// Concatenate
|
|
144
|
+
const totalSize = sectors.reduce((sum, s) => sum + s.length, 0);
|
|
145
|
+
const result = new Uint8Array(totalSize);
|
|
146
|
+
let writeOffset = 0;
|
|
147
|
+
for (const sector of sectors) {
|
|
148
|
+
result.set(sector, writeOffset);
|
|
149
|
+
writeOffset += sector.length;
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
// ─── Internal ───────────────────────────────────────────────────
|
|
154
|
+
/**
|
|
155
|
+
* Write a single D2I page sector (header + items).
|
|
156
|
+
*/
|
|
157
|
+
function writeStashSector(page, allItems, gd) {
|
|
158
|
+
// Write item list first to know its size
|
|
159
|
+
const itemBytes = writeItemList(page.entries, allItems, gd);
|
|
160
|
+
const totalSize = PAGE_HEADER_SIZE + itemBytes.length;
|
|
161
|
+
const writer = new BitWriter();
|
|
162
|
+
// Header
|
|
163
|
+
writer.writeUInt32(D2I_MAGIC); // 0x00: magic
|
|
164
|
+
writer.writeUInt32(D2I_MODE); // 0x04: mode
|
|
165
|
+
writer.writeUInt32(D2I_VERSION); // 0x08: version
|
|
166
|
+
writer.writeUInt32(page.gold); // 0x0C: gold
|
|
167
|
+
writer.writeUInt32(totalSize); // 0x10: sectorSize
|
|
168
|
+
writer.writeUInt8(page.pageType); // 0x14: pageType
|
|
169
|
+
// Padding: 43 zero bytes (0x15 to 0x3F)
|
|
170
|
+
writer.writeBytes(new Uint8Array(43));
|
|
171
|
+
// Items (JM header + entries)
|
|
172
|
+
writer.writeBytes(itemBytes);
|
|
173
|
+
return writer.toArray();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build item write entries from a normal stash page's slot array.
|
|
177
|
+
*/
|
|
178
|
+
function buildStashEntries(stash, allItems, gd) {
|
|
179
|
+
const entries = [];
|
|
180
|
+
const columns = gd.info?.stash?.columns || STASH_COLUMNS;
|
|
181
|
+
for (let slot = 0; slot < stash.length; slot++) {
|
|
182
|
+
const id = stash[slot];
|
|
183
|
+
if (id == null)
|
|
184
|
+
continue;
|
|
185
|
+
const item = allItems[id];
|
|
186
|
+
if (!item)
|
|
187
|
+
continue;
|
|
188
|
+
const x = slot % columns;
|
|
189
|
+
const y = Math.floor(slot / columns);
|
|
190
|
+
entries.push({
|
|
191
|
+
item,
|
|
192
|
+
location: { loc: 0, equip: 0, x, y, storage: 5 },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return entries;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Build item write entries from an extended virtual sub-page.
|
|
199
|
+
*
|
|
200
|
+
* Extended pages store items at stash location with position (0,0).
|
|
201
|
+
* All items are just listed sequentially — position doesn't really matter
|
|
202
|
+
* for extended pages since the game unpacks them by type.
|
|
203
|
+
*/
|
|
204
|
+
function buildExtendedEntries(subPage, allItems, _gd) {
|
|
205
|
+
const entries = [];
|
|
206
|
+
const stash = subPage.stash;
|
|
207
|
+
const quantities = subPage.quantities;
|
|
208
|
+
for (let i = 0; i < stash.length; i++) {
|
|
209
|
+
const id = stash[i];
|
|
210
|
+
if (id == null)
|
|
211
|
+
continue;
|
|
212
|
+
const item = allItems[id];
|
|
213
|
+
if (!item)
|
|
214
|
+
continue;
|
|
215
|
+
// Get quantity from the sub-page
|
|
216
|
+
const qty = Array.isArray(quantities)
|
|
217
|
+
? quantities[i]
|
|
218
|
+
: quantities?.[i];
|
|
219
|
+
const entryItem = qty != null && qty !== item.quantity
|
|
220
|
+
? { ...item, quantity: qty }
|
|
221
|
+
: item;
|
|
222
|
+
entries.push({
|
|
223
|
+
item: entryItem,
|
|
224
|
+
location: { loc: 0, equip: 0, x: 0, y: 0, storage: 5 },
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return entries;
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=d2i-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d2i-writer.js","sourceRoot":"","sources":["../../src/formats/d2i-writer.ts"],"names":[],"mappings":"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"}
|