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 @@
1
+ {"version":3,"file":"placement.js","sourceRoot":"","sources":["../../src/inventory/placement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { isSubType, itemGetTypes, itemGetType, presetItemIds, itemCheckMod, itemMaxSockets, } from './item-types.js';
2
+ export { formatStr, addValues, addProp, modsToStats, uniqueStats, runewordStats, gemStats, type StatValue, } from './item.js';
3
+ export { parseParamStats, parseUniqueStats, parseRunewordStats, parseModStats, } from './item-stats-parser.js';
4
+ export { serializeItem, deserializeItem, type DeserializedItem, } from './item-serializer.js';
5
+ export { getItemIconPath, getItemIconSD, } from './item-icon.js';
6
+ export { toTradeDTO, type TradeItemDTO, type ItemQuality, } from './item-dto.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/items/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,SAAS,EACT,SAAS,EACT,OAAO,EACP,WAAW,EACX,WAAW,EACX,aAAa,EACb,QAAQ,EACR,KAAK,SAAS,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,aAAa,EACb,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { isSubType, itemGetTypes, itemGetType, presetItemIds, itemCheckMod, itemMaxSockets, } from './item-types.js';
2
+ export { formatStr, addValues, addProp, modsToStats, uniqueStats, runewordStats, gemStats, } from './item.js';
3
+ export { parseParamStats, parseUniqueStats, parseRunewordStats, parseModStats, } from './item-stats-parser.js';
4
+ export { serializeItem, deserializeItem, } from './item-serializer.js';
5
+ export { getItemIconPath, getItemIconSD, } from './item-icon.js';
6
+ export { toTradeDTO, } from './item-dto.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/items/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,SAAS,EACT,SAAS,EACT,OAAO,EACP,WAAW,EACX,WAAW,EACX,aAAa,EACb,QAAQ,GAET,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,aAAa,EACb,eAAe,GAEhB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,UAAU,GAGX,MAAM,eAAe,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Item DTO — converts internal ParsedItem to a portable trade DTO.
3
+ *
4
+ * TradeItemDTO is the format sent to the backend for the trade system.
5
+ */
6
+ import type { GameData } from '../game-data/game-data.js';
7
+ import type { BinaryParsedItem } from '../formats/item-parser.js';
8
+ /** Quality enum for trade DTO (matches internal quality values). */
9
+ export type ItemQuality = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
10
+ /** Trade item DTO — portable representation for the backend. */
11
+ export interface TradeItemDTO {
12
+ /** Base64 token for full item reconstruction: `d2r1:<base64>` */
13
+ token: string;
14
+ /** 3-character item base code. */
15
+ baseCode: string;
16
+ /** Human-readable display name. */
17
+ displayName: string;
18
+ /** Item quality (1-9). */
19
+ quality: ItemQuality;
20
+ /** Item level. */
21
+ ilvl: number;
22
+ /** Is ethereal? */
23
+ ethereal: boolean;
24
+ /** Number of sockets (total, including filled). */
25
+ sockets: number;
26
+ /** Unique/set/runeword identifier. */
27
+ uniqueId?: string;
28
+ /** Width in grid cells. */
29
+ width: number;
30
+ /** Height in grid cells. */
31
+ height: number;
32
+ /** HD icon path key (for hditemlib.json lookup). */
33
+ iconPath: string | null;
34
+ /** Key stats for search/filtering. */
35
+ stats: Record<string, number>;
36
+ /** Socketed sub-items (runes/gems). */
37
+ socketedItems: TradeItemDTO[];
38
+ }
39
+ /**
40
+ * Convert a parsed item to a TradeItemDTO.
41
+ *
42
+ * @param item The parsed item.
43
+ * @param allItems All items dict (for socketed sub-item lookup and serialization).
44
+ * @param gd GameData instance.
45
+ */
46
+ export declare function toTradeDTO(item: BinaryParsedItem, allItems: Record<number | string, BinaryParsedItem>, gd: GameData): TradeItemDTO;
47
+ //# sourceMappingURL=item-dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-dto.d.ts","sourceRoot":"","sources":["../../src/items/item-dto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAMlE,oEAAoE;AACpE,MAAM,MAAM,WAAW,GACnB,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,GACD,CAAC,CAAC;AAEN,gEAAgE;AAChE,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uCAAuC;IACvC,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAID;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,EACnD,EAAE,EAAE,QAAQ,GACX,YAAY,CAuCd"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Item DTO — converts internal ParsedItem to a portable trade DTO.
3
+ *
4
+ * TradeItemDTO is the format sent to the backend for the trade system.
5
+ */
6
+ import { getItemIconPath } from './item-icon.js';
7
+ import { serializeItem } from './item-serializer.js';
8
+ // ─── Public API ─────────────────────────────────────────────────
9
+ /**
10
+ * Convert a parsed item to a TradeItemDTO.
11
+ *
12
+ * @param item The parsed item.
13
+ * @param allItems All items dict (for socketed sub-item lookup and serialization).
14
+ * @param gd GameData instance.
15
+ */
16
+ export function toTradeDTO(item, allItems, gd) {
17
+ const baseEntry = gd.items[item.base];
18
+ const base = baseEntry ? baseEntry : undefined;
19
+ const { w, h } = getItemSize(base);
20
+ // Resolve display name
21
+ const displayName = resolveDisplayName(item, gd);
22
+ // Build socketed sub-DTOs
23
+ const socketedDTOs = [];
24
+ if (item.socketedItems) {
25
+ for (const sid of item.socketedItems) {
26
+ const sockItem = allItems[sid];
27
+ if (sockItem) {
28
+ socketedDTOs.push(toTradeDTO(sockItem, allItems, gd));
29
+ }
30
+ }
31
+ }
32
+ // Serialize token
33
+ const token = serializeItem(item, allItems, gd);
34
+ return {
35
+ token,
36
+ baseCode: item.base,
37
+ displayName,
38
+ quality: item.quality,
39
+ // Simple items (gems, runes, potions, scrolls) carry no ilvl in the binary
40
+ // format — surface 0 in the DTO so downstream consumers can rely on `number`.
41
+ ilvl: item.ilvl ?? 0,
42
+ ethereal: item.ethereal,
43
+ sockets: item.sockets,
44
+ uniqueId: item.unique,
45
+ width: w,
46
+ height: h,
47
+ iconPath: getItemIconPath(item, gd),
48
+ stats: { ...item.stats },
49
+ socketedItems: socketedDTOs,
50
+ };
51
+ }
52
+ // ─── Helpers ────────────────────────────────────────────────────
53
+ /** Get item dimensions from base data. */
54
+ function getItemSize(base) {
55
+ const w = base?.invwidth || 1;
56
+ const h = base?.invheight || 1;
57
+ return { w, h };
58
+ }
59
+ /** Resolve human-readable display name. */
60
+ function resolveDisplayName(item, gd) {
61
+ // If item has an explicit name (rare/crafted)
62
+ if (item.name)
63
+ return item.name;
64
+ // Unique/set/runeword items: look up in GameData
65
+ if (item.unique) {
66
+ const name = lookupName(item.unique, gd);
67
+ if (name)
68
+ return name;
69
+ }
70
+ const base = localizedBaseName(item, gd);
71
+ // Magic items: in-game name is "<prefix> <base> <suffix>" (e.g. "Glowing Ring of the
72
+ // Whale"). The affix codes are read straight from the binary, so this works even when
73
+ // the stat solver fails to reconstruct the actual mods.
74
+ if (item.magicPrefixCode || item.magicSuffixCode) {
75
+ const prefix = affixWord(gd.magicPrefix?.[item.magicPrefixCode ?? ''], gd);
76
+ const suffix = affixWord(gd.magicSuffix?.[item.magicSuffixCode ?? ''], gd);
77
+ const full = [prefix, base, suffix].filter(Boolean).join(' ').trim();
78
+ if (full)
79
+ return full;
80
+ }
81
+ return base;
82
+ }
83
+ /** Resolve a magic affix entry's localized display word (e.g. "Glowing", "of the Whale"). */
84
+ function affixWord(entry, gd) {
85
+ const key = entry?.name;
86
+ if (!key)
87
+ return '';
88
+ return gd.locale.strings[key] || key;
89
+ }
90
+ /** Localized base-item name from the locale, falling back to the raw base code. */
91
+ function localizedBaseName(item, gd) {
92
+ const baseEntry = gd.items[item.base];
93
+ const base = baseEntry ? baseEntry : undefined;
94
+ const namestr = base?.namestr;
95
+ if (namestr) {
96
+ return gd.locale.strings[namestr] || namestr;
97
+ }
98
+ return item.base;
99
+ }
100
+ /** Look up an item name from the locale by its unique/set/runeword key. */
101
+ function lookupName(key, gd) {
102
+ // Try direct locale lookup
103
+ const direct = gd.locale.strings[key];
104
+ if (direct)
105
+ return direct;
106
+ // Try uniqueItems / setItems tables if available. The name key lives in the `index`
107
+ // column (e.g. unique117.index = "Nokozan Relic"), NOT `namestr` — without this every
108
+ // unique/set fell through to its base name ("Amulet", "Ring", …).
109
+ const tables = ['uniqueItems', 'setItems'];
110
+ for (const table of tables) {
111
+ const dict = gd[table];
112
+ if (dict?.[key]) {
113
+ const nameStr = (dict[key].index ?? dict[key].namestr ?? dict[key].name);
114
+ if (nameStr) {
115
+ const localized = gd.locale.strings[nameStr];
116
+ if (localized)
117
+ return localized;
118
+ return nameStr;
119
+ }
120
+ }
121
+ }
122
+ return null;
123
+ }
124
+ //# sourceMappingURL=item-dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-dto.js","sourceRoot":"","sources":["../../src/items/item-dto.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA8CrD,mEAAmE;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CACxB,IAAsB,EACtB,QAAmD,EACnD,EAAY;IAEZ,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,SAA+C,CAAC,CAAC,CAAC,SAAS,CAAC;IACrF,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAEnC,uBAAuB;IACvB,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEjD,0BAA0B;IAC1B,MAAM,YAAY,GAAmB,EAAE,CAAC;IACxC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,QAAQ,EAAE,CAAC;gBACb,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEhD,OAAO;QACL,KAAK;QACL,QAAQ,EAAE,IAAI,CAAC,IAAI;QACnB,WAAW;QACX,OAAO,EAAE,IAAI,CAAC,OAAsB;QACpC,2EAA2E;QAC3E,8EAA8E;QAC9E,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC;QACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ,EAAE,IAAI,CAAC,MAAM;QACrB,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;QACT,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,EAAE,CAAC;QACnC,KAAK,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,EAA4B;QAClD,aAAa,EAAE,YAAY;KAC5B,CAAC;AACJ,CAAC;AAED,mEAAmE;AAEnE,0CAA0C;AAC1C,SAAS,WAAW,CAAC,IAAyC;IAC5D,MAAM,CAAC,GAAI,IAAI,EAAE,QAAmB,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAI,IAAI,EAAE,SAAoB,IAAI,CAAC,CAAC;IAC3C,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;AAClB,CAAC;AAED,2CAA2C;AAC3C,SAAS,kBAAkB,CAAC,IAAsB,EAAE,EAAY;IAC9D,8CAA8C;IAC9C,IAAI,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAEhC,iDAAiD;IACjD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzC,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAED,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEzC,qFAAqF;IACrF,sFAAsF;IACtF,wDAAwD;IACxD,IAAI,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3E,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACxB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6FAA6F;AAC7F,SAAS,SAAS,CAAC,KAAoC,EAAE,EAAY;IACnE,MAAM,GAAG,GAAG,KAAK,EAAE,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AACvC,CAAC;AAED,mFAAmF;AACnF,SAAS,iBAAiB,CAAC,IAAsB,EAAE,EAAY;IAC7D,MAAM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAE,SAAgD,CAAC,CAAC,CAAC,SAAS,CAAC;IACvF,MAAM,OAAO,GAAG,IAAI,EAAE,OAA6B,CAAC;IACpD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC;AAED,2EAA2E;AAC3E,SAAS,UAAU,CAAC,GAAW,EAAE,EAAY;IAC3C,2BAA2B;IAC3B,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,oFAAoF;IACpF,sFAAsF;IACtF,kEAAkE;IAClE,MAAM,MAAM,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAC;IACpD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAI,EAAyE,CAAC,KAAK,CAAC,CAAC;QAC/F,IAAI,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAuB,CAAC;YAC/F,IAAI,OAAO,EAAE,CAAC;gBACZ,MAAM,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,SAAS;oBAAE,OAAO,SAAS,CAAC;gBAChC,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Item icon path resolution — determines the HD icon key for an item.
3
+ *
4
+ * Ported from d2planner/src/logic/item.js → itemGetIconHD().
5
+ */
6
+ import type { GameData } from '../game-data/game-data.js';
7
+ import type { BinaryParsedItem } from '../formats/item-parser.js';
8
+ /**
9
+ * Get the HD icon path key for an item.
10
+ *
11
+ * Returns the icon identifier used in hditemlib.json for HD rendering.
12
+ * The result can be passed to the frontend for icon display.
13
+ *
14
+ * @param item The parsed item.
15
+ * @param gd GameData instance.
16
+ * @returns Icon path string, or `null` if no icon is available.
17
+ */
18
+ export declare function getItemIconPath(item: BinaryParsedItem, gd: GameData): string | null;
19
+ /**
20
+ * Get the inventory file icon name for an item (SD, non-HD).
21
+ *
22
+ * @param item The parsed item.
23
+ * @param gd GameData instance.
24
+ * @returns Icon name string, or `null` if not found.
25
+ */
26
+ export declare function getItemIconSD(item: BinaryParsedItem, gd: GameData): string | null;
27
+ //# sourceMappingURL=item-icon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-icon.d.ts","sourceRoot":"","sources":["../../src/items/item-icon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIlE;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,QAAQ,GACX,MAAM,GAAG,IAAI,CAgDf;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,QAAQ,GACX,MAAM,GAAG,IAAI,CA8Bf"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Item icon path resolution — determines the HD icon key for an item.
3
+ *
4
+ * Ported from d2planner/src/logic/item.js → itemGetIconHD().
5
+ */
6
+ // ─── Public API ─────────────────────────────────────────────────
7
+ /**
8
+ * Get the HD icon path key for an item.
9
+ *
10
+ * Returns the icon identifier used in hditemlib.json for HD rendering.
11
+ * The result can be passed to the frontend for icon display.
12
+ *
13
+ * @param item The parsed item.
14
+ * @param gd GameData instance.
15
+ * @returns Icon path string, or `null` if no icon is available.
16
+ */
17
+ export function getItemIconPath(item, gd) {
18
+ const baseEntry = gd.items[item.base];
19
+ if (!baseEntry)
20
+ return null;
21
+ const base = baseEntry;
22
+ let icon = base.hd;
23
+ if (!icon)
24
+ return null;
25
+ // Unique/set items may have specific HD icons
26
+ if (item.quality === 7 || item.quality === 5) {
27
+ // quality 7 = UNIQUE, 5 = SET
28
+ const specialItem = item.unique
29
+ ? gd.uniqueItems[item.unique]
30
+ ?? gd.setItems[item.unique]
31
+ : undefined;
32
+ const specialHd = specialItem?.hd;
33
+ if (specialHd?.length) {
34
+ const normcode = base.normcode;
35
+ const ubercode = base.ubercode;
36
+ const ultracode = base.ultracode;
37
+ if (!normcode || normcode === item.base) {
38
+ icon = specialHd[0];
39
+ }
40
+ else if (ubercode === item.base) {
41
+ icon = specialHd[1] ?? specialHd[0];
42
+ }
43
+ else if (ultracode === item.base) {
44
+ icon = specialHd[2] ?? specialHd[0];
45
+ }
46
+ else {
47
+ icon = specialHd[0];
48
+ }
49
+ }
50
+ }
51
+ if (!icon)
52
+ return null;
53
+ // Variable inventory graphics (armor/weapons with multiple visual variants)
54
+ const baseType = base.type;
55
+ if (baseType) {
56
+ const typeData = gd.itemTypes[baseType];
57
+ if (typeData?.varinvgfx && icon === base.hd) {
58
+ const varCount = typeData.varinvgfx;
59
+ const index = ((item.iconIndex || 0) % varCount) + 1;
60
+ icon = icon + index;
61
+ }
62
+ }
63
+ return icon;
64
+ }
65
+ /**
66
+ * Get the inventory file icon name for an item (SD, non-HD).
67
+ *
68
+ * @param item The parsed item.
69
+ * @param gd GameData instance.
70
+ * @returns Icon name string, or `null` if not found.
71
+ */
72
+ export function getItemIconSD(item, gd) {
73
+ const baseEntry2 = gd.items[item.base];
74
+ if (!baseEntry2)
75
+ return null;
76
+ const base = baseEntry2;
77
+ let icon = base.invfile;
78
+ if (!icon)
79
+ return null;
80
+ // Variable inventory graphics
81
+ const baseType = base.type;
82
+ if (baseType) {
83
+ const typeData = gd.itemTypes[baseType];
84
+ if (typeData?.varinvgfx) {
85
+ const varCount = typeData.varinvgfx;
86
+ const index = ((item.iconIndex || 0) % varCount) + 1;
87
+ const varIcon = typeData[`invgfx${index}`];
88
+ if (varIcon)
89
+ icon = varIcon;
90
+ }
91
+ }
92
+ // Unique/set overrides
93
+ if (item.quality === 7) {
94
+ const uniq = item.unique ? gd.uniqueItems[item.unique] : undefined;
95
+ icon = uniq?.invfile || base.uniqueinvfile || icon;
96
+ }
97
+ else if (item.quality === 5) {
98
+ const setItem = item.unique ? gd.setItems[item.unique] : undefined;
99
+ icon = setItem?.invfile || base.setinvfile || icon;
100
+ }
101
+ return icon || null;
102
+ }
103
+ //# sourceMappingURL=item-icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-icon.js","sourceRoot":"","sources":["../../src/items/item-icon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Item serializer — portable base64 token for item transfer.
3
+ *
4
+ * Format: `d2r1:<base64>` where the payload is writeItem() output
5
+ * with a zeroed-out location (loc=0, equip=0, x=0, y=0, storage=0).
6
+ *
7
+ * This allows items to be extracted from a save, serialized to a string token,
8
+ * transmitted (e.g. via trade API), and deserialized back into binary form
9
+ * for insertion into any save file.
10
+ */
11
+ import { GameData } from '../game-data/game-data.js';
12
+ import type { BinaryParsedItem } from '../formats/item-parser.js';
13
+ /**
14
+ * Serialize an item (and its socketed sub-items) to a portable token string.
15
+ *
16
+ * @param item The item to serialize.
17
+ * @param allItems All items dict (needed to look up socketed items).
18
+ * @param gd GameData instance.
19
+ * @returns Token string: `d2r1:<base64>`
20
+ */
21
+ export declare function serializeItem(item: BinaryParsedItem, allItems: Record<number | string, BinaryParsedItem>, gd: GameData): string;
22
+ /**
23
+ * Deserialize a token string back to a parsed item.
24
+ *
25
+ * @param token Token string (must start with `d2r1:`).
26
+ * @param gd GameData instance.
27
+ * @returns The parsed item and any socketed sub-items.
28
+ * @throws If the token is invalid.
29
+ */
30
+ export declare function deserializeItem(token: string, gd: GameData): DeserializedItem;
31
+ /** Result of deserializing a token. */
32
+ export interface DeserializedItem {
33
+ /** The main item. */
34
+ item: BinaryParsedItem;
35
+ /** All items including socketed sub-items, keyed by ID. */
36
+ allItems: Record<number, BinaryParsedItem>;
37
+ }
38
+ //# sourceMappingURL=item-serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-serializer.d.ts","sourceRoot":"","sources":["../../src/items/item-serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAMlE;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,EACnD,EAAE,EAAE,QAAQ,GACX,MAAM,CAMR;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,GACX,gBAAgB,CAyClB;AAID,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,IAAI,EAAE,gBAAgB,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC5C"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Item serializer — portable base64 token for item transfer.
3
+ *
4
+ * Format: `d2r1:<base64>` where the payload is writeItem() output
5
+ * with a zeroed-out location (loc=0, equip=0, x=0, y=0, storage=0).
6
+ *
7
+ * This allows items to be extracted from a save, serialized to a string token,
8
+ * transmitted (e.g. via trade API), and deserialized back into binary form
9
+ * for insertion into any save file.
10
+ */
11
+ import { BinaryReader } from '../core/binary-reader.js';
12
+ import { TOKEN_PREFIX } from '../types/constants.js';
13
+ import { writeItem } from '../formats/item-writer.js';
14
+ import { createItemParser } from '../formats/item-parser.js';
15
+ // ─── Public API ─────────────────────────────────────────────────
16
+ /**
17
+ * Serialize an item (and its socketed sub-items) to a portable token string.
18
+ *
19
+ * @param item The item to serialize.
20
+ * @param allItems All items dict (needed to look up socketed items).
21
+ * @param gd GameData instance.
22
+ * @returns Token string: `d2r1:<base64>`
23
+ */
24
+ export function serializeItem(item, allItems, gd) {
25
+ // Write item with zero location
26
+ const zeroLoc = { loc: 0, equip: 0, x: 0, y: 0, storage: 0 };
27
+ const bytes = writeItem(item, zeroLoc, allItems, gd);
28
+ const base64 = uint8ToBase64(bytes);
29
+ return TOKEN_PREFIX + base64;
30
+ }
31
+ /**
32
+ * Deserialize a token string back to a parsed item.
33
+ *
34
+ * @param token Token string (must start with `d2r1:`).
35
+ * @param gd GameData instance.
36
+ * @returns The parsed item and any socketed sub-items.
37
+ * @throws If the token is invalid.
38
+ */
39
+ export function deserializeItem(token, gd) {
40
+ if (!token.startsWith(TOKEN_PREFIX)) {
41
+ throw new Error(`INVALID_TOKEN: token must start with '${TOKEN_PREFIX}'`);
42
+ }
43
+ const base64 = token.slice(TOKEN_PREFIX.length);
44
+ const bytes = base64ToUint8(base64);
45
+ if (bytes.length < 4) {
46
+ throw new Error('INVALID_TOKEN: payload too short');
47
+ }
48
+ // Parse item using a JM-wrapped buffer so createItemParser works
49
+ // We wrap: JM header (2 bytes) + count=1 (2 bytes) + item bytes
50
+ const wrapped = new Uint8Array(4 + bytes.length);
51
+ wrapped[0] = 0x4a; // 'J'
52
+ wrapped[1] = 0x4d; // 'M'
53
+ wrapped[2] = 1; // count = 1
54
+ wrapped[3] = 0;
55
+ wrapped.set(bytes, 4);
56
+ const reader = new BinaryReader(wrapped);
57
+ const ctx = createItemParser(reader, gd);
58
+ // Parse items — the handler may not fire because we use a zeroed location
59
+ // (location=0, storage=0), which doesn't match any known slot.
60
+ // Instead, we collect items directly from ctx.items after parsing.
61
+ ctx.parseItemList(() => { });
62
+ const allParsed = Object.values(ctx.items);
63
+ if (allParsed.length === 0) {
64
+ throw new Error('INVALID_TOKEN: no item parsed from token');
65
+ }
66
+ // The first item in the dict is the main item
67
+ const mainItem = allParsed[0];
68
+ return {
69
+ item: mainItem,
70
+ allItems: ctx.items,
71
+ };
72
+ }
73
+ // ─── Base64 helpers ─────────────────────────────────────────────
74
+ // Browser-safe base64 (no node:buffer). btoa/atob operate on binary strings;
75
+ // items are at most a few hundred bytes so the char-by-char loop is fine.
76
+ // atob/btoa are globals in the Tauri webview and in Node 18+.
77
+ function uint8ToBase64(bytes) {
78
+ let binary = '';
79
+ for (let i = 0; i < bytes.length; i++) {
80
+ binary += String.fromCharCode(bytes[i]);
81
+ }
82
+ return btoa(binary);
83
+ }
84
+ function base64ToUint8(base64) {
85
+ const binary = atob(base64);
86
+ const out = new Uint8Array(binary.length);
87
+ for (let i = 0; i < binary.length; i++) {
88
+ out[i] = binary.charCodeAt(i);
89
+ }
90
+ return out;
91
+ }
92
+ //# sourceMappingURL=item-serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-serializer.js","sourceRoot":"","sources":["../../src/items/item-serializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Item stat parser — determines mod sources for parsed items.
3
+ *
4
+ * Ported from d2planner/src/logic/parser.js.
5
+ * Provides: parseModStats, parseUniqueStats, parseRunewordStats.
6
+ *
7
+ * These functions analyze item stats read from the binary save file
8
+ * and determine which affixes (mods) produced those stats, along with
9
+ * the variable values (uniqueValues) for unique/set/runeword items.
10
+ *
11
+ * Uses GameData instead of global Data singleton.
12
+ */
13
+ import { GameData } from '../game-data/game-data.js';
14
+ import type { StatValue } from './item.js';
15
+ interface ParsedItem {
16
+ base: string;
17
+ quality: number;
18
+ unique?: string;
19
+ [key: string]: unknown;
20
+ }
21
+ type StatsGetter = (src: Record<string, StatValue>) => Record<string, StatValue> | null;
22
+ /**
23
+ * Post-process binary stats into a format suitable for mod matching.
24
+ * Handles descfunc 13/27 stats (class skill bonuses, single-skill bonuses).
25
+ * Ported from d2planner binary/index.js → parseParamStats().
26
+ */
27
+ export declare function parseParamStats(gd: GameData, stats: Record<string, number>, src: Record<string, StatValue>): Record<string, StatValue> | null;
28
+ /**
29
+ * Determine the variable values for a unique/set item by analyzing binary stats.
30
+ * Returns `{uniqueValues, propertyIndex}` (matching d2planner HEAD) or null
31
+ * when the stats don't match the definition.
32
+ *
33
+ * `propertyIndex` maps each propertyGroup property name to the picked option
34
+ * index (warlock items use this for "pick a skill tab" style rolls).
35
+ */
36
+ export declare function parseUniqueStats(gd: GameData, item: ParsedItem, getStats: StatsGetter): {
37
+ uniqueValues: number[];
38
+ propertyIndex: Record<string, number>;
39
+ } | null;
40
+ /**
41
+ * Determine the mod sources and uniqueValues for a runeword item.
42
+ */
43
+ export declare function parseRunewordStats(gd: GameData, item: ParsedItem, socketStats: Record<string, StatValue> | null, getStats: StatsGetter, presetMods?: string[]): Record<string, unknown> | null;
44
+ /**
45
+ * Determine the mod sources for a magic/rare/crafted/superior item.
46
+ */
47
+ export declare function parseModStats(gd: GameData, item: ParsedItem, magic: {
48
+ prefix?: string;
49
+ suffix?: string;
50
+ } | null, socketStats: Record<string, StatValue> | null, levelreq: number | null, getStats: StatsGetter, presetMods?: string[]): Record<string, unknown> | null;
51
+ export {};
52
+ //# sourceMappingURL=item-stats-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-stats-parser.d.ts","sourceRoot":"","sources":["../../src/items/item-stats-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAM3C,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;AAsaxF;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,CAoElC;AAID;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,WAAW,GACpB;IAAE,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CAmF1E;AAID;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,EAC7C,QAAQ,EAAE,WAAW,EACrB,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA2DhC;AAID;;GAEG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAClD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,EAC7C,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,QAAQ,EAAE,WAAW,EACrB,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CA4ChC"}