hxdec 1.0.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 +2 -0
- package/eslint.config.js +13 -0
- package/lib/index.js +559 -0
- package/lib/index.js.map +1 -0
- package/package.json +22 -0
- package/src/index.ts +589 -0
- package/tsconfig.json +16 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harun Eggleton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/eslint.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import js from '@eslint/js';
|
|
3
|
+
import tseslint from 'typescript-eslint';
|
|
4
|
+
|
|
5
|
+
export default tseslint.config(
|
|
6
|
+
js.configs.recommended,
|
|
7
|
+
...tseslint.configs.recommended,
|
|
8
|
+
{
|
|
9
|
+
rules: {
|
|
10
|
+
"@typescript-eslint/no-explicit-any": "off"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
);
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const hxdec = {
|
|
4
|
+
setData: [], // to be populated with set data from scryfall on init
|
|
5
|
+
encode: (list, foil = false, tags = false, cat = false) => {
|
|
6
|
+
if (hxdec.setData.length === 0) {
|
|
7
|
+
hxdec.error("Set data not ready yet, cannot encode");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const digestList = () => {
|
|
11
|
+
if (list.length === 0) {
|
|
12
|
+
hxdec.error("No deck list provided");
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const sections = list.split("\n\n");
|
|
16
|
+
const listData = {
|
|
17
|
+
name: "",
|
|
18
|
+
targetSection: "",
|
|
19
|
+
commander: [],
|
|
20
|
+
mainboard: [],
|
|
21
|
+
sideboard: [],
|
|
22
|
+
maybeboard: [],
|
|
23
|
+
companion: [],
|
|
24
|
+
prefetch: [], // cards that need to be fetched from scryfall to get set and collector number info for encoding
|
|
25
|
+
};
|
|
26
|
+
let targetSection = "mainboard"; // default section target
|
|
27
|
+
sections.forEach((section) => {
|
|
28
|
+
const sec = section.split("\n");
|
|
29
|
+
sec.forEach((line, index) => {
|
|
30
|
+
line = line.trim();
|
|
31
|
+
// if line doesnt start with a number followed by " " or "x ", It is not a card line. Check to see if its a section title and then, assign the targetSection based on the title. If the title is "About" and it is the first line of the first section, check to see if the second line starts with "Name " and if so, capture the deck name from that line and assign it to listData.name. If the line is not a card line or a section header, skip it.
|
|
32
|
+
if (!/^\d+x?\s/.test(line)) {
|
|
33
|
+
const titleLine = line.toLowerCase().replace(":", "");
|
|
34
|
+
console.log(`Processing section title: ${titleLine}`);
|
|
35
|
+
if (titleLine === "commander" || titleLine === "companion" || titleLine === "sideboard" || titleLine === "maybeboard" || titleLine === "mainboard") {
|
|
36
|
+
targetSection = titleLine;
|
|
37
|
+
}
|
|
38
|
+
if (titleLine === "deck") {
|
|
39
|
+
targetSection = "mainboard";
|
|
40
|
+
}
|
|
41
|
+
if (titleLine === "about" && index === 0 && /^Name\s/.test(sec[1])) {
|
|
42
|
+
listData.name = sec[1].split("Name")[1].trim();
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
else if (sec.length < 3 && index === 0 && listData.commander.length === 0) {
|
|
47
|
+
// 1-2 separate lines with no section headerde notes commanders for some formats
|
|
48
|
+
targetSection = "commander";
|
|
49
|
+
}
|
|
50
|
+
const newCard = hxdec.digestCard(line);
|
|
51
|
+
if (typeof newCard.hxnumber === "undefined" || typeof newCard.hxcode === "undefined") {
|
|
52
|
+
listData.prefetch = listData.prefetch || [];
|
|
53
|
+
listData.prefetch.push(Object.assign(Object.assign({}, newCard), { targetSection }));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
else if (typeof newCard.cat !== "undefined" && newCard.cat.includes("Commander")) {
|
|
57
|
+
listData.commander = listData.commander || [];
|
|
58
|
+
listData.commander.push(Object.assign(Object.assign({}, newCard), { targetSection }));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
listData[targetSection] = listData[targetSection] || [];
|
|
62
|
+
listData[targetSection].push(Object.assign(Object.assign({}, newCard), { targetSection }));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
if (typeof listData.prefetch !== "undefined") {
|
|
67
|
+
hxdec.getCardData(listData.prefetch, ["set", "collector_number"], (cards) => {
|
|
68
|
+
cards.forEach((card) => {
|
|
69
|
+
var _a;
|
|
70
|
+
// add each card to its target section and update the hxcode and hxnumber based on the set and collector number
|
|
71
|
+
if (typeof card.set !== "undefined" && typeof card.collector_number !== "undefined" && typeof hxdec.setData !== "undefined") {
|
|
72
|
+
card.hxcode = ((_a = hxdec.setData.find(s => s.code.toLowerCase() === card.set.toLowerCase())) === null || _a === void 0 ? void 0 : _a.hxcode) || null;
|
|
73
|
+
if (card.hxcode) {
|
|
74
|
+
const collectorNumber = card.collector_number;
|
|
75
|
+
if (/^\d+$/.test(collectorNumber)) {
|
|
76
|
+
card.hxnumber = Number(collectorNumber).toString(16);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
card.hxnumber = `~${collectorNumber}~`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
hxdec.warning(`No set data found for set: ${card.set} on card: ${card.name}`);
|
|
84
|
+
}
|
|
85
|
+
listData[card.targetSection] = listData[card.targetSection] || [];
|
|
86
|
+
listData[card.targetSection].push(card);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
console.log("Decoded list data and fetched missing card info from scryfall:", listData);
|
|
90
|
+
buildHxdec(listData);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
console.log("Decoded list data:", listData);
|
|
95
|
+
buildHxdec(listData);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
};
|
|
99
|
+
const buildHxdec = (listData) => {
|
|
100
|
+
// build the hxdec string based on the listData and setData
|
|
101
|
+
const outputCards = (rawCards) => {
|
|
102
|
+
let newCards = "";
|
|
103
|
+
rawCards.forEach(card => {
|
|
104
|
+
const qtyChars = ["u", "v", "w", "x", "y", "z"];
|
|
105
|
+
let cardString = "";
|
|
106
|
+
if (card.qty >= 1 && card.qty <= 4) {
|
|
107
|
+
cardString += qtyChars[card.qty - 1];
|
|
108
|
+
}
|
|
109
|
+
else if (card.qty > 4 && card.qty < 16) {
|
|
110
|
+
const qtyHex = Number(card.qty).toString(16);
|
|
111
|
+
cardString += `y${qtyHex}`;
|
|
112
|
+
}
|
|
113
|
+
else if (card.qty >= 16 && card.qty < 256) {
|
|
114
|
+
const qtyHex = Number(card.qty).toString(16);
|
|
115
|
+
cardString += `z${qtyHex}`;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
cardString += "zff";
|
|
119
|
+
}
|
|
120
|
+
cardString += card.hxcode;
|
|
121
|
+
cardString += card.hxnumber;
|
|
122
|
+
if (foil && typeof card.foil !== "undefined") {
|
|
123
|
+
cardString += `*${card.foil}*`;
|
|
124
|
+
}
|
|
125
|
+
if (tags && typeof card.tags !== "undefined") {
|
|
126
|
+
cardString += `^${card.tags}^`;
|
|
127
|
+
}
|
|
128
|
+
if (cat && typeof card.cat !== "undefined") {
|
|
129
|
+
cardString += `[${card.cat}]`;
|
|
130
|
+
}
|
|
131
|
+
newCards += cardString;
|
|
132
|
+
});
|
|
133
|
+
return newCards;
|
|
134
|
+
};
|
|
135
|
+
if (typeof listData.mainboard === "undefined" || listData.mainboard.length === 0) {
|
|
136
|
+
hxdec.error("No mainboard cards found, cannot build HXDEC");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// If this is a type of format that lists a commander as the first card in the mainboard section without a section header, move it to the commander section and remove it from the mainboard section
|
|
140
|
+
if (typeof listData.mainboard !== "undefined" && typeof listData.commander === "undefined") {
|
|
141
|
+
// count the number of cards in the mainboard
|
|
142
|
+
let mainboardCount = 0;
|
|
143
|
+
listData.mainboard.forEach((mainboardCard) => {
|
|
144
|
+
mainboardCount += mainboardCard.qty;
|
|
145
|
+
});
|
|
146
|
+
if (mainboardCount > 98) {
|
|
147
|
+
// Mainboard has more than 98 cards and no commander specified, treating first card as commander
|
|
148
|
+
listData.commander = [listData.mainboard[0]];
|
|
149
|
+
listData.mainboard = listData.mainboard.slice(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let deck = `h${outputCards(listData.mainboard)}`;
|
|
153
|
+
if (typeof listData.commander !== "undefined") {
|
|
154
|
+
deck += `k${outputCards(listData.commander)}`;
|
|
155
|
+
}
|
|
156
|
+
if (typeof listData.sideboard !== "undefined") {
|
|
157
|
+
deck += `s${outputCards(listData.sideboard)}`;
|
|
158
|
+
}
|
|
159
|
+
if (typeof listData.maybeboard !== "undefined") {
|
|
160
|
+
deck += `m${outputCards(listData.maybeboard)}`;
|
|
161
|
+
}
|
|
162
|
+
if (typeof listData.companion !== "undefined") {
|
|
163
|
+
deck += `p${outputCards(listData.companion)}`;
|
|
164
|
+
}
|
|
165
|
+
if (typeof listData.name !== "undefined") {
|
|
166
|
+
deck += `+${encodeURIComponent(listData.name)}+`;
|
|
167
|
+
}
|
|
168
|
+
hxdec.ready();
|
|
169
|
+
hxdec.encoded(deck);
|
|
170
|
+
return;
|
|
171
|
+
};
|
|
172
|
+
hxdec.loading("Processing deck list");
|
|
173
|
+
digestList();
|
|
174
|
+
},
|
|
175
|
+
decode: (list, format = "Archideckt") => {
|
|
176
|
+
if (hxdec.setData.length === 0) {
|
|
177
|
+
console.log("Set data not ready yet, cannot decode");
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
hxdec.loading("Decoding HXDEC string");
|
|
181
|
+
// Decoding logic to be implemented
|
|
182
|
+
// remove values wrapped and store them in an array, replace them in the deck string with their index in the array wrapped in thier original wrap symbol so they can be reinserted after splitting the string into sections
|
|
183
|
+
const captureWrappedValues = (wrap1, wrap2, regex) => {
|
|
184
|
+
const capturedValues = [];
|
|
185
|
+
const matches = list.match(regex) || [];
|
|
186
|
+
// for each match push the value without the wrapSymbol to capturedValues and replace the match in list with a placeholder of the index wrapped
|
|
187
|
+
matches.forEach((match, index) => {
|
|
188
|
+
// console.log(`Captured value for ${wrap1}${wrap2}:`, match);
|
|
189
|
+
capturedValues.push(match.replace(wrap1, "").replace(wrap2, ""));
|
|
190
|
+
list = list.replace(match, `${wrap1}${index}${wrap2}`);
|
|
191
|
+
});
|
|
192
|
+
return capturedValues;
|
|
193
|
+
};
|
|
194
|
+
const categoryValues = captureWrappedValues("[", "]", /\[(.*?)\]/g);
|
|
195
|
+
const textSets = captureWrappedValues("~", "~", /~(.*?)~/g);
|
|
196
|
+
const foilValues = captureWrappedValues("*", "*", /\*(.*?)\*/g);
|
|
197
|
+
const tagValues = captureWrappedValues("^", "^", /\^(.*?)\^/g);
|
|
198
|
+
const nameValue = captureWrappedValues("+", "", /\+(.*)\+/g);
|
|
199
|
+
const decodedData = { name: "", prefetch: [] };
|
|
200
|
+
if (nameValue.length > 0) {
|
|
201
|
+
decodedData.name = decodeURIComponent(nameValue[0]);
|
|
202
|
+
}
|
|
203
|
+
list = list.replace(/\+(.*)\+/g, ""); // remove the name from the list string so it doesn't interfere with section splitting
|
|
204
|
+
const sections = list.split(/(?=[hksmp])/);
|
|
205
|
+
sections.forEach(section => {
|
|
206
|
+
const type = section[0];
|
|
207
|
+
const data = section.substring(1);
|
|
208
|
+
const cardCodes = data.split(/(?=[uvwxyz])/);
|
|
209
|
+
const targetSection = type === "h" ? "mainboard" : type === "k" ? "commander" : type === "s" ? "sideboard" : type === "m" ? "maybeboard" : type === "p" ? "companion" : null;
|
|
210
|
+
if (targetSection) {
|
|
211
|
+
const cards = [];
|
|
212
|
+
cardCodes.forEach(code => {
|
|
213
|
+
// get the first character and remove it
|
|
214
|
+
const qtyChar = code[0];
|
|
215
|
+
code = code.substring(1);
|
|
216
|
+
let qty = ["u", "v", "w", "x", "y", "z"].indexOf(qtyChar) + 1;
|
|
217
|
+
if (qtyChar === "y") {
|
|
218
|
+
qty = parseInt(code.substring(0, 1), 16);
|
|
219
|
+
code = code.substring(1);
|
|
220
|
+
}
|
|
221
|
+
if (qtyChar === "z") {
|
|
222
|
+
qty = parseInt(code.substring(0, 2), 16);
|
|
223
|
+
code = code.substring(2);
|
|
224
|
+
}
|
|
225
|
+
const setHx = code.substring(0, 3);
|
|
226
|
+
// if code contains * , extract the foil value and remove it from the code
|
|
227
|
+
let foil = null;
|
|
228
|
+
if (code.includes("*")) {
|
|
229
|
+
const foilMatch = code.match(/\*(.*?)\*/);
|
|
230
|
+
if (foilMatch) {
|
|
231
|
+
foil = foilValues[parseInt(foilMatch[1])];
|
|
232
|
+
code = code.replace(foilMatch[0], "");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// if code contains ^, extract the tag value and remove it from the code
|
|
236
|
+
let tags = null;
|
|
237
|
+
if (code.includes("^")) {
|
|
238
|
+
const tagMatch = code.match(/\^(.*?)\^/);
|
|
239
|
+
if (tagMatch) {
|
|
240
|
+
tags = tagValues[parseInt(tagMatch[1])];
|
|
241
|
+
code = code.replace(tagMatch[0], "");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// if code contains [ ], extract the category value and remove it from the code
|
|
245
|
+
let category = null;
|
|
246
|
+
if (code.includes("[")) {
|
|
247
|
+
const categoryMatch = code.match(/\[(.*?)\]/);
|
|
248
|
+
if (categoryMatch) {
|
|
249
|
+
category = categoryValues[parseInt(categoryMatch[1])];
|
|
250
|
+
code = code.replace(categoryMatch[0], "");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const numberHx = code.substring(3);
|
|
254
|
+
let number = Number(parseInt(numberHx, 16)).toString();
|
|
255
|
+
if (isNaN(parseInt(number)) && numberHx.includes("~")) {
|
|
256
|
+
number = textSets[parseInt(numberHx.replace("~", ""))];
|
|
257
|
+
}
|
|
258
|
+
else if (isNaN(parseInt(number))) {
|
|
259
|
+
console.log(`Invalid collector number hex: ${numberHx} for code: ${code}`);
|
|
260
|
+
}
|
|
261
|
+
let set = null;
|
|
262
|
+
// find set in set data
|
|
263
|
+
const setInfo = hxdec.setData.find(s => s.hxcode === setHx);
|
|
264
|
+
if (setInfo) {
|
|
265
|
+
set = setInfo.code;
|
|
266
|
+
}
|
|
267
|
+
const newCard = { code, qty, set, number, foil, tags, category, targetSection };
|
|
268
|
+
cards.push(newCard);
|
|
269
|
+
});
|
|
270
|
+
decodedData.prefetch.push(...cards);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
console.log(`Unknown section type: ${type} in section: ${section}`);
|
|
274
|
+
return;
|
|
275
|
+
});
|
|
276
|
+
hxdec.getCardData(decodedData.prefetch, ["name"], (cards) => {
|
|
277
|
+
cards.forEach(card => {
|
|
278
|
+
decodedData[card.targetSection] = decodedData[card.targetSection] || [];
|
|
279
|
+
decodedData[card.targetSection].push(card);
|
|
280
|
+
});
|
|
281
|
+
buildList(decodedData);
|
|
282
|
+
console.log("Decoded data with card names:", decodedData);
|
|
283
|
+
});
|
|
284
|
+
const buildList = (decodedData) => {
|
|
285
|
+
let formatOptions = {
|
|
286
|
+
mainboardTitle: "",
|
|
287
|
+
commanderTitle: "",
|
|
288
|
+
sideboardTitle: "",
|
|
289
|
+
maybeboardTitle: "",
|
|
290
|
+
companionTitle: "",
|
|
291
|
+
nameTitle: "",
|
|
292
|
+
name: false,
|
|
293
|
+
sets: false,
|
|
294
|
+
collectorNumber: false,
|
|
295
|
+
foil: false,
|
|
296
|
+
cat: false,
|
|
297
|
+
tags: false,
|
|
298
|
+
order: ["commander", "mainboard", "companion", "sideboard", "maybeboard"]
|
|
299
|
+
};
|
|
300
|
+
if (format === "Archideckt") {
|
|
301
|
+
formatOptions = Object.assign(Object.assign({}, formatOptions), { mainboardTitle: "\nMainboard\n", commanderTitle: "\nCommander\n", sideboardTitle: "\nSideboard\n", maybeboardTitle: "\nMaybeboard\n", companionTitle: "\nCompanion\n", sets: true, collectorNumber: true, foil: true, cat: true, tags: true });
|
|
302
|
+
}
|
|
303
|
+
if (format === "MTGO") {
|
|
304
|
+
formatOptions = Object.assign(Object.assign({}, formatOptions), { sideboardTitle: "\nSIDEBOARD:\n", commanderTitle: "\n", order: ["mainboard", "companion", "sideboard", "commander", "maybeboard"] });
|
|
305
|
+
}
|
|
306
|
+
if (format === "Arena") {
|
|
307
|
+
formatOptions = Object.assign(Object.assign({}, formatOptions), { mainboardTitle: "\nDeck\n", commanderTitle: "\nCommander\n", sideboardTitle: "\nSideboard\n", nameTitle: "About\nName ", name: true });
|
|
308
|
+
}
|
|
309
|
+
if (format === "Moxfield") {
|
|
310
|
+
formatOptions = Object.assign(Object.assign({}, formatOptions), { sideboardTitle: "\nSIDEBOARD:\n", sets: true, collectorNumber: true, foil: true });
|
|
311
|
+
}
|
|
312
|
+
let newList = "";
|
|
313
|
+
if (formatOptions.name && decodedData.name) {
|
|
314
|
+
newList += `${formatOptions.nameTitle}${decodedData.name}\n`;
|
|
315
|
+
}
|
|
316
|
+
const buildCardLine = (card) => {
|
|
317
|
+
let line = `${card.qty} ${card.name}`;
|
|
318
|
+
if (formatOptions.sets && card.set) {
|
|
319
|
+
line += ` (${card.set})`;
|
|
320
|
+
}
|
|
321
|
+
if (formatOptions.collectorNumber && card.number) {
|
|
322
|
+
line += ` ${card.number}`;
|
|
323
|
+
}
|
|
324
|
+
if (formatOptions.foil && card.foil) {
|
|
325
|
+
line += ` *${card.foil}*`;
|
|
326
|
+
}
|
|
327
|
+
if (formatOptions.cat && card.category) {
|
|
328
|
+
line += ` [${card.category}]`;
|
|
329
|
+
}
|
|
330
|
+
if (formatOptions.tags && card.tags) {
|
|
331
|
+
line += ` ^${card.tags}^`;
|
|
332
|
+
}
|
|
333
|
+
return line;
|
|
334
|
+
};
|
|
335
|
+
const renderSection = (section, title) => {
|
|
336
|
+
if (section && section.length > 0) {
|
|
337
|
+
newList += title;
|
|
338
|
+
section.forEach(card => {
|
|
339
|
+
newList += buildCardLine(card) + "\n";
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
formatOptions.order.forEach((sectionKey) => {
|
|
344
|
+
if (decodedData[sectionKey]) {
|
|
345
|
+
const title = formatOptions[`${sectionKey}Title`] || "";
|
|
346
|
+
renderSection(decodedData[sectionKey], title);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
// trim the final newList and set it to the output
|
|
350
|
+
newList = newList.trim();
|
|
351
|
+
hxdec.ready();
|
|
352
|
+
hxdec.decoded(newList);
|
|
353
|
+
};
|
|
354
|
+
return;
|
|
355
|
+
},
|
|
356
|
+
init: () => {
|
|
357
|
+
// fetch all sets from scryfall and prepare them for initial firebase upload
|
|
358
|
+
if (typeof hxdec.setData !== "undefined") {
|
|
359
|
+
console.log("Set data already processed, skipping fetch");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
hxdec.loading("Fetching set data from scryfall");
|
|
363
|
+
fetch("https://api.scryfall.com/sets")
|
|
364
|
+
.then((resp) => resp.json())
|
|
365
|
+
.then((resp) => {
|
|
366
|
+
if (typeof resp.data !== "undefined" && resp.data.length > 0) {
|
|
367
|
+
const sets = [];
|
|
368
|
+
const initSetsData = resp.data;
|
|
369
|
+
// sort initSetsData by release date ascending
|
|
370
|
+
initSetsData.sort((a, b) => {
|
|
371
|
+
const dateA = new Date(a.released_at).getTime();
|
|
372
|
+
const dateB = new Date(b.released_at).getTime();
|
|
373
|
+
return dateA - dateB;
|
|
374
|
+
});
|
|
375
|
+
let setCounter = 1;
|
|
376
|
+
initSetsData.forEach((set) => {
|
|
377
|
+
if (set.set_type !== "token") {
|
|
378
|
+
const hxcode = Number(setCounter).toString(16).padStart(3, "0");
|
|
379
|
+
const newSet = {
|
|
380
|
+
date: new Date(set.released_at).getTime(),
|
|
381
|
+
code: set.code,
|
|
382
|
+
hxcode
|
|
383
|
+
};
|
|
384
|
+
sets.push(newSet);
|
|
385
|
+
setCounter++;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
if (sets.length > 0) {
|
|
389
|
+
hxdec.setData = sets;
|
|
390
|
+
hxdec.ready();
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
hxdec.error("No set data found from scryfall");
|
|
394
|
+
console.log("No set data found from scryfall", resp);
|
|
395
|
+
}
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
hxdec.error("No set data found from scryfall");
|
|
399
|
+
console.log("No set data found from scryfall", resp);
|
|
400
|
+
return;
|
|
401
|
+
})
|
|
402
|
+
.catch((err) => {
|
|
403
|
+
hxdec.error("Error fetching set data from scryfall");
|
|
404
|
+
console.error("Error fetching set data from scryfall", err);
|
|
405
|
+
return;
|
|
406
|
+
});
|
|
407
|
+
},
|
|
408
|
+
getCardData: async (cards, properties, callback) => {
|
|
409
|
+
// fetch all cards from scryfall
|
|
410
|
+
const batchSize = 75; // scryfall allows up to 75 identifiers per request
|
|
411
|
+
const delay = 500; // delay in ms between requests to avoid hitting rate limits
|
|
412
|
+
const cardInfos = [];
|
|
413
|
+
//delay each request by the specified delay to avoid hitting rate limits
|
|
414
|
+
for (let i = 0; i < cards.length; i += batchSize) {
|
|
415
|
+
const chunk = cards.slice(i, i + batchSize);
|
|
416
|
+
const identifiers = chunk.map(card => {
|
|
417
|
+
if (card.set && card.number) {
|
|
418
|
+
return {
|
|
419
|
+
set: card.set,
|
|
420
|
+
collector_number: String(card.number)
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
else if (card.name) {
|
|
424
|
+
return {
|
|
425
|
+
name: card.name
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
}).filter(id => id !== null);
|
|
432
|
+
if (identifiers.length === 0) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
fetch("https://api.scryfall.com/cards/collection", {
|
|
436
|
+
method: "POST",
|
|
437
|
+
headers: {
|
|
438
|
+
"Content-Type": "application/json"
|
|
439
|
+
},
|
|
440
|
+
body: JSON.stringify({ identifiers })
|
|
441
|
+
})
|
|
442
|
+
.then(resp => resp.json())
|
|
443
|
+
.then(resp => {
|
|
444
|
+
if (resp.data && resp.data.length > 0) {
|
|
445
|
+
cardInfos.push(...resp.data);
|
|
446
|
+
if (cardInfos.length >= cards.length) {
|
|
447
|
+
// all card info has been fetched, add the card names to the cards in the decoded data
|
|
448
|
+
cards.forEach((card, index) => {
|
|
449
|
+
let cardInfo = {};
|
|
450
|
+
if (typeof card.name !== "undefined" && (typeof card.set === "undefined" || typeof card.number === "undefined")) {
|
|
451
|
+
cardInfo = cardInfos.find(info => info.name.toLowerCase() === card.name.toLowerCase()) || {};
|
|
452
|
+
}
|
|
453
|
+
else if ((typeof card.set !== "undefined" && typeof card.number !== "undefined") && typeof card.name === "undefined") {
|
|
454
|
+
cardInfo = cardInfos.find(info => info.set.toLowerCase() === card.set.toLowerCase() && info.collector_number === String(card.number)) || {};
|
|
455
|
+
}
|
|
456
|
+
if (cardInfo && Object.keys(cardInfo).length !== 0) {
|
|
457
|
+
properties.forEach(prop => {
|
|
458
|
+
if (typeof cardInfo[prop] !== "undefined") {
|
|
459
|
+
cards[index][prop] = cardInfo[prop];
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
console.log(`No card info found for card: ${card.name} with set: ${card.set} and number: ${card.number}`);
|
|
465
|
+
}
|
|
466
|
+
// console.log("card", card, cardInfo);
|
|
467
|
+
});
|
|
468
|
+
// if all batches have been processed, call the callback with the updated cards
|
|
469
|
+
if (cardInfos.length >= cards.length) {
|
|
470
|
+
callback(cards);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// console.log("Fetched card info from scryfall:", resp.data);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
console.log("No card info found from scryfall for identifiers:", identifiers, resp);
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
.catch(err => {
|
|
480
|
+
console.error("Error fetching card info from scryfall for identifiers:", identifiers, err);
|
|
481
|
+
});
|
|
482
|
+
// wait 500ms before next request
|
|
483
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
484
|
+
}
|
|
485
|
+
return;
|
|
486
|
+
},
|
|
487
|
+
digestCard: (line) => {
|
|
488
|
+
var _a;
|
|
489
|
+
const splitIndex = line.indexOf(" ");
|
|
490
|
+
let name = line.substring(splitIndex + 1).trim();
|
|
491
|
+
const newCard = {
|
|
492
|
+
name,
|
|
493
|
+
qty: Number(line.substring(0, splitIndex).trim().replace("x", "")),
|
|
494
|
+
set: "",
|
|
495
|
+
foil: "",
|
|
496
|
+
cat: "",
|
|
497
|
+
tags: "",
|
|
498
|
+
collectorNumber: "",
|
|
499
|
+
hxcode: "",
|
|
500
|
+
hxnumber: "",
|
|
501
|
+
};
|
|
502
|
+
const trimValue = (property, start, end) => {
|
|
503
|
+
const regex = new RegExp(`\\${start}([^\\${end}]*)\\${end}`);
|
|
504
|
+
const match = name.match(regex);
|
|
505
|
+
let replaceSymbol = "";
|
|
506
|
+
if (start === "(")
|
|
507
|
+
replaceSymbol = `::::`;
|
|
508
|
+
// get the matched value without the start and end symbols and assign it to newCard with the property name as the key
|
|
509
|
+
if (match && match[1]) {
|
|
510
|
+
newCard[property] = match[1].trim();
|
|
511
|
+
name = name.replace(regex, replaceSymbol);
|
|
512
|
+
// console.log('newCard:', newCard, match[1]);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
trimValue("set", "(", ")");
|
|
516
|
+
trimValue("foil", "*", "*");
|
|
517
|
+
trimValue("cat", "[", "]");
|
|
518
|
+
trimValue("tags", "^", "^");
|
|
519
|
+
const nameSplit = name.split("::::");
|
|
520
|
+
name = nameSplit[0].trim();
|
|
521
|
+
if (nameSplit.length > 1) {
|
|
522
|
+
newCard.collectorNumber = nameSplit[1].trim();
|
|
523
|
+
}
|
|
524
|
+
if (typeof newCard.set !== "undefined") {
|
|
525
|
+
newCard.hxcode = ((_a = hxdec.setData.find(s => s.code.toLowerCase() === newCard.set.toLowerCase())) === null || _a === void 0 ? void 0 : _a.hxcode) || null;
|
|
526
|
+
}
|
|
527
|
+
if (typeof newCard.collectorNumber !== "undefined") {
|
|
528
|
+
// if collector number is just a number, convert to hex and pad to 3 digits
|
|
529
|
+
if (/^\d+$/.test(newCard.collectorNumber)) {
|
|
530
|
+
const hxnumber = Number(newCard.collectorNumber).toString(16);
|
|
531
|
+
newCard.hxnumber = hxnumber;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
newCard.hxnumber = `~${newCard.collectorNumber}~`;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return newCard;
|
|
538
|
+
},
|
|
539
|
+
encoded: (list) => {
|
|
540
|
+
console.log("hxdec.encoded: ", list);
|
|
541
|
+
},
|
|
542
|
+
decoded: (list) => {
|
|
543
|
+
console.log("hxdec.decoded: ", list);
|
|
544
|
+
},
|
|
545
|
+
ready: () => {
|
|
546
|
+
console.log("hxdec.ready: ready to encode and decode");
|
|
547
|
+
},
|
|
548
|
+
loading: (status) => {
|
|
549
|
+
console.log("hxdec.loading: ", status);
|
|
550
|
+
},
|
|
551
|
+
warning: (message) => {
|
|
552
|
+
console.log("HXDEC Warning:", message);
|
|
553
|
+
},
|
|
554
|
+
error: (message) => {
|
|
555
|
+
console.log("HXDEC Error:", message);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
exports.default = hxdec;
|
|
559
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hxdec",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Hexidecimal Magic the Gathering Decklist format.",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"lint": "eslint --ext .js,.ts .",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"build:watch": "tsc --watch"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [],
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@eslint/js": "^10.0.1",
|
|
17
|
+
"eslint": "^10.5.0",
|
|
18
|
+
"globals": "^17.6.0",
|
|
19
|
+
"typescript": "^6.0.3",
|
|
20
|
+
"typescript-eslint": "^8.61.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
|
|
2
|
+
type Obj = {
|
|
3
|
+
[key: string]: any;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const hxdec = {
|
|
7
|
+
setData: [] as Obj[], // to be populated with set data from scryfall on init
|
|
8
|
+
encode: (list: string, foil = false, tags = false, cat = false) => {
|
|
9
|
+
if (hxdec.setData.length === 0) {
|
|
10
|
+
hxdec.error("Set data not ready yet, cannot encode");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const digestList = () => {
|
|
14
|
+
if (list.length === 0) {
|
|
15
|
+
hxdec.error("No deck list provided");
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const sections = list.split("\n\n");
|
|
19
|
+
const listData = {
|
|
20
|
+
name: "",
|
|
21
|
+
targetSection: "",
|
|
22
|
+
commander: [] as Obj[],
|
|
23
|
+
mainboard: [] as Obj[],
|
|
24
|
+
sideboard: [] as Obj[],
|
|
25
|
+
maybeboard: [] as Obj[],
|
|
26
|
+
companion: [] as Obj[],
|
|
27
|
+
prefetch: [] as Obj[], // cards that need to be fetched from scryfall to get set and collector number info for encoding
|
|
28
|
+
} as Obj;
|
|
29
|
+
let targetSection = "mainboard"; // default section target
|
|
30
|
+
sections.forEach((section) => {
|
|
31
|
+
const sec = section.split("\n");
|
|
32
|
+
sec.forEach((line, index) => {
|
|
33
|
+
line = line.trim();
|
|
34
|
+
|
|
35
|
+
// if line doesnt start with a number followed by " " or "x ", It is not a card line. Check to see if its a section title and then, assign the targetSection based on the title. If the title is "About" and it is the first line of the first section, check to see if the second line starts with "Name " and if so, capture the deck name from that line and assign it to listData.name. If the line is not a card line or a section header, skip it.
|
|
36
|
+
if (!/^\d+x?\s/.test(line)) {
|
|
37
|
+
const titleLine = line.toLowerCase().replace(":", "");
|
|
38
|
+
console.log(`Processing section title: ${titleLine}`);
|
|
39
|
+
if (titleLine === "commander" || titleLine === "companion" || titleLine === "sideboard" || titleLine === "maybeboard" || titleLine === "mainboard") {
|
|
40
|
+
targetSection = titleLine;
|
|
41
|
+
}
|
|
42
|
+
if (titleLine === "deck") {
|
|
43
|
+
targetSection = "mainboard";
|
|
44
|
+
}
|
|
45
|
+
if (titleLine === "about" && index === 0 && /^Name\s/.test(sec[1])) {
|
|
46
|
+
listData.name = sec[1].split("Name")[1].trim();
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
} else if (sec.length < 3 && index === 0 && listData.commander.length === 0) {
|
|
50
|
+
// 1-2 separate lines with no section headerde notes commanders for some formats
|
|
51
|
+
targetSection = "commander";
|
|
52
|
+
}
|
|
53
|
+
const newCard = hxdec.digestCard(line);
|
|
54
|
+
if (typeof newCard.hxnumber === "undefined" || typeof newCard.hxcode === "undefined") {
|
|
55
|
+
listData.prefetch = listData.prefetch || [];
|
|
56
|
+
listData.prefetch.push({ ...newCard, targetSection });
|
|
57
|
+
return;
|
|
58
|
+
} else if (typeof newCard.cat !== "undefined" && newCard.cat.includes("Commander")) {
|
|
59
|
+
listData.commander = listData.commander || [];
|
|
60
|
+
listData.commander.push({ ...newCard, targetSection });
|
|
61
|
+
} else {
|
|
62
|
+
listData[targetSection] = listData[targetSection] || [];
|
|
63
|
+
listData[targetSection].push({ ...newCard, targetSection });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
});
|
|
68
|
+
if (typeof listData.prefetch !== "undefined") {
|
|
69
|
+
hxdec.getCardData(listData.prefetch, ["set", "collector_number"], (cards: Obj[]) => {
|
|
70
|
+
cards.forEach((card: Obj) => {
|
|
71
|
+
// add each card to its target section and update the hxcode and hxnumber based on the set and collector number
|
|
72
|
+
if (typeof card.set !== "undefined" && typeof card.collector_number !== "undefined" && typeof hxdec.setData !== "undefined") {
|
|
73
|
+
card.hxcode = hxdec.setData.find(s => s.code.toLowerCase() === card.set.toLowerCase())?.hxcode || null;
|
|
74
|
+
if (card.hxcode) {
|
|
75
|
+
const collectorNumber = card.collector_number;
|
|
76
|
+
if (/^\d+$/.test(collectorNumber)) {
|
|
77
|
+
card.hxnumber = Number(collectorNumber).toString(16);
|
|
78
|
+
} else {
|
|
79
|
+
card.hxnumber = `~${collectorNumber}~`;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
hxdec.warning(`No set data found for set: ${card.set} on card: ${card.name}`);
|
|
83
|
+
}
|
|
84
|
+
listData[card.targetSection] = listData[card.targetSection] || [];
|
|
85
|
+
listData[card.targetSection].push(card);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
console.log("Decoded list data and fetched missing card info from scryfall:", listData);
|
|
89
|
+
buildHxdec(listData);
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
console.log("Decoded list data:", listData);
|
|
93
|
+
buildHxdec(listData);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const buildHxdec = (listData: Obj) => {
|
|
98
|
+
// build the hxdec string based on the listData and setData
|
|
99
|
+
const outputCards = (rawCards: Obj[]) => {
|
|
100
|
+
let newCards = ""
|
|
101
|
+
rawCards.forEach(card => {
|
|
102
|
+
const qtyChars = ["u", "v", "w", "x", "y", "z"];
|
|
103
|
+
let cardString = "";
|
|
104
|
+
if (card.qty >= 1 && card.qty <= 4) {
|
|
105
|
+
cardString += qtyChars[card.qty - 1];
|
|
106
|
+
} else if (card.qty > 4 && card.qty < 16) {
|
|
107
|
+
const qtyHex = Number(card.qty).toString(16);
|
|
108
|
+
cardString += `y${qtyHex}`;
|
|
109
|
+
} else if (card.qty >= 16 && card.qty < 256) {
|
|
110
|
+
const qtyHex = Number(card.qty).toString(16);
|
|
111
|
+
cardString += `z${qtyHex}`;
|
|
112
|
+
} else {
|
|
113
|
+
cardString += "zff";
|
|
114
|
+
}
|
|
115
|
+
cardString += card.hxcode;
|
|
116
|
+
cardString += card.hxnumber;
|
|
117
|
+
if (foil && typeof card.foil !== "undefined") {
|
|
118
|
+
cardString += `*${card.foil}*`;
|
|
119
|
+
}
|
|
120
|
+
if (tags && typeof card.tags !== "undefined") {
|
|
121
|
+
cardString += `^${card.tags}^`;
|
|
122
|
+
}
|
|
123
|
+
if (cat && typeof card.cat !== "undefined") {
|
|
124
|
+
cardString += `[${card.cat}]`;
|
|
125
|
+
}
|
|
126
|
+
newCards += cardString;
|
|
127
|
+
});
|
|
128
|
+
return newCards;
|
|
129
|
+
}
|
|
130
|
+
if (typeof listData.mainboard === "undefined" || listData.mainboard.length === 0) {
|
|
131
|
+
hxdec.error("No mainboard cards found, cannot build HXDEC");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// If this is a type of format that lists a commander as the first card in the mainboard section without a section header, move it to the commander section and remove it from the mainboard section
|
|
135
|
+
if (typeof listData.mainboard !== "undefined" && typeof listData.commander === "undefined") {
|
|
136
|
+
// count the number of cards in the mainboard
|
|
137
|
+
let mainboardCount = 0;
|
|
138
|
+
listData.mainboard.forEach((mainboardCard: Obj) => {
|
|
139
|
+
mainboardCount += mainboardCard.qty;
|
|
140
|
+
});
|
|
141
|
+
if (mainboardCount > 98) {
|
|
142
|
+
// Mainboard has more than 98 cards and no commander specified, treating first card as commander
|
|
143
|
+
listData.commander = [listData.mainboard[0]];
|
|
144
|
+
listData.mainboard = listData.mainboard.slice(1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
let deck = `h${outputCards(listData.mainboard)}`;
|
|
148
|
+
if (typeof listData.commander !== "undefined") {
|
|
149
|
+
deck += `k${outputCards(listData.commander)}`;
|
|
150
|
+
}
|
|
151
|
+
if (typeof listData.sideboard !== "undefined") {
|
|
152
|
+
deck += `s${outputCards(listData.sideboard)}`;
|
|
153
|
+
}
|
|
154
|
+
if (typeof listData.maybeboard !== "undefined") {
|
|
155
|
+
deck += `m${outputCards(listData.maybeboard)}`;
|
|
156
|
+
}
|
|
157
|
+
if (typeof listData.companion !== "undefined") {
|
|
158
|
+
deck += `p${outputCards(listData.companion)}`;
|
|
159
|
+
}
|
|
160
|
+
if (typeof listData.name !== "undefined") {
|
|
161
|
+
deck += `+${encodeURIComponent(listData.name)}+`;
|
|
162
|
+
}
|
|
163
|
+
hxdec.ready();
|
|
164
|
+
hxdec.encoded(deck);
|
|
165
|
+
return;
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
hxdec.loading("Processing deck list");
|
|
170
|
+
digestList();
|
|
171
|
+
},
|
|
172
|
+
decode: (list: string, format = "Archideckt") => {
|
|
173
|
+
if (hxdec.setData.length === 0) {
|
|
174
|
+
console.log("Set data not ready yet, cannot decode");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
hxdec.loading("Decoding HXDEC string");
|
|
178
|
+
// Decoding logic to be implemented
|
|
179
|
+
// remove values wrapped and store them in an array, replace them in the deck string with their index in the array wrapped in thier original wrap symbol so they can be reinserted after splitting the string into sections
|
|
180
|
+
const captureWrappedValues = (wrap1: string, wrap2: string, regex: RegExp) => {
|
|
181
|
+
const capturedValues = [] as string[];
|
|
182
|
+
const matches = list.match(regex) || [];
|
|
183
|
+
// for each match push the value without the wrapSymbol to capturedValues and replace the match in list with a placeholder of the index wrapped
|
|
184
|
+
matches.forEach((match, index) => {
|
|
185
|
+
// console.log(`Captured value for ${wrap1}${wrap2}:`, match);
|
|
186
|
+
capturedValues.push(match.replace(wrap1, "").replace(wrap2, ""));
|
|
187
|
+
list = list.replace(match, `${wrap1}${index}${wrap2}`);
|
|
188
|
+
});
|
|
189
|
+
return capturedValues;
|
|
190
|
+
};
|
|
191
|
+
const categoryValues = captureWrappedValues("[", "]", /\[(.*?)\]/g);
|
|
192
|
+
const textSets = captureWrappedValues("~", "~", /~(.*?)~/g);
|
|
193
|
+
const foilValues = captureWrappedValues("*", "*", /\*(.*?)\*/g);
|
|
194
|
+
const tagValues = captureWrappedValues("^", "^", /\^(.*?)\^/g);
|
|
195
|
+
const nameValue = captureWrappedValues("+", "", /\+(.*)\+/g);
|
|
196
|
+
const decodedData = { name: "", prefetch: [] } as Obj;
|
|
197
|
+
if (nameValue.length > 0) {
|
|
198
|
+
decodedData.name = decodeURIComponent(nameValue[0]);
|
|
199
|
+
}
|
|
200
|
+
list = list.replace(/\+(.*)\+/g, ""); // remove the name from the list string so it doesn't interfere with section splitting
|
|
201
|
+
|
|
202
|
+
const sections = list.split(/(?=[hksmp])/);
|
|
203
|
+
sections.forEach(section => {
|
|
204
|
+
const type = section[0];
|
|
205
|
+
const data = section.substring(1);
|
|
206
|
+
const cardCodes = data.split(/(?=[uvwxyz])/);
|
|
207
|
+
const targetSection = type === "h" ? "mainboard" : type === "k" ? "commander" : type === "s" ? "sideboard" : type === "m" ? "maybeboard" : type === "p" ? "companion" : null;
|
|
208
|
+
if (targetSection) {
|
|
209
|
+
const cards = [] as Obj[];
|
|
210
|
+
cardCodes.forEach(code => {
|
|
211
|
+
// get the first character and remove it
|
|
212
|
+
const qtyChar = code[0];
|
|
213
|
+
code = code.substring(1);
|
|
214
|
+
let qty = ["u", "v", "w", "x", "y", "z"].indexOf(qtyChar) + 1;
|
|
215
|
+
if (qtyChar === "y") {
|
|
216
|
+
qty = parseInt(code.substring(0, 1), 16);
|
|
217
|
+
code = code.substring(1);
|
|
218
|
+
}
|
|
219
|
+
if (qtyChar === "z") {
|
|
220
|
+
qty = parseInt(code.substring(0, 2), 16);
|
|
221
|
+
code = code.substring(2);
|
|
222
|
+
}
|
|
223
|
+
const setHx = code.substring(0, 3);
|
|
224
|
+
// if code contains * , extract the foil value and remove it from the code
|
|
225
|
+
let foil = null;
|
|
226
|
+
if (code.includes("*")) {
|
|
227
|
+
const foilMatch = code.match(/\*(.*?)\*/);
|
|
228
|
+
if (foilMatch) {
|
|
229
|
+
foil = foilValues[parseInt(foilMatch[1])];
|
|
230
|
+
code = code.replace(foilMatch[0], "");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// if code contains ^, extract the tag value and remove it from the code
|
|
234
|
+
let tags = null;
|
|
235
|
+
if (code.includes("^")) {
|
|
236
|
+
const tagMatch = code.match(/\^(.*?)\^/);
|
|
237
|
+
if (tagMatch) {
|
|
238
|
+
tags = tagValues[parseInt(tagMatch[1])];
|
|
239
|
+
code = code.replace(tagMatch[0], "");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// if code contains [ ], extract the category value and remove it from the code
|
|
243
|
+
let category = null;
|
|
244
|
+
if (code.includes("[")) {
|
|
245
|
+
const categoryMatch = code.match(/\[(.*?)\]/);
|
|
246
|
+
if (categoryMatch) {
|
|
247
|
+
category = categoryValues[parseInt(categoryMatch[1])];
|
|
248
|
+
code = code.replace(categoryMatch[0], "");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const numberHx = code.substring(3);
|
|
253
|
+
|
|
254
|
+
let number: string = Number(parseInt(numberHx, 16)).toString();
|
|
255
|
+
if (isNaN(parseInt(number)) && numberHx.includes("~")) {
|
|
256
|
+
number = textSets[parseInt(numberHx.replace("~", ""))];
|
|
257
|
+
} else if (isNaN(parseInt(number))) {
|
|
258
|
+
console.log(`Invalid collector number hex: ${numberHx} for code: ${code}`);
|
|
259
|
+
}
|
|
260
|
+
let set = null;
|
|
261
|
+
|
|
262
|
+
// find set in set data
|
|
263
|
+
const setInfo = hxdec.setData.find(s => s.hxcode === setHx);
|
|
264
|
+
if (setInfo) {
|
|
265
|
+
set = setInfo.code;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const newCard = { code, qty, set, number, foil, tags, category, targetSection } as Obj;
|
|
269
|
+
|
|
270
|
+
cards.push(newCard);
|
|
271
|
+
});
|
|
272
|
+
decodedData.prefetch.push(...cards);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
console.log(`Unknown section type: ${type} in section: ${section}`);
|
|
276
|
+
return;
|
|
277
|
+
});
|
|
278
|
+
hxdec.getCardData(decodedData.prefetch, ["name"], (cards: Obj[]) => {
|
|
279
|
+
cards.forEach(card => {
|
|
280
|
+
decodedData[card.targetSection] = decodedData[card.targetSection] || [];
|
|
281
|
+
decodedData[card.targetSection].push(card);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
buildList(decodedData);
|
|
285
|
+
console.log("Decoded data with card names:", decodedData);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const buildList = (decodedData: Obj) => {
|
|
289
|
+
let formatOptions = {
|
|
290
|
+
mainboardTitle: "",
|
|
291
|
+
commanderTitle: "",
|
|
292
|
+
sideboardTitle: "",
|
|
293
|
+
maybeboardTitle: "",
|
|
294
|
+
companionTitle: "",
|
|
295
|
+
nameTitle: "",
|
|
296
|
+
name: false,
|
|
297
|
+
sets: false,
|
|
298
|
+
collectorNumber: false,
|
|
299
|
+
foil: false,
|
|
300
|
+
cat: false,
|
|
301
|
+
tags: false,
|
|
302
|
+
order: ["commander", "mainboard", "companion", "sideboard", "maybeboard"]
|
|
303
|
+
} as Obj;
|
|
304
|
+
if (format === "Archideckt") {
|
|
305
|
+
formatOptions = {
|
|
306
|
+
...formatOptions,
|
|
307
|
+
mainboardTitle: "\nMainboard\n",
|
|
308
|
+
commanderTitle: "\nCommander\n",
|
|
309
|
+
sideboardTitle: "\nSideboard\n",
|
|
310
|
+
maybeboardTitle: "\nMaybeboard\n",
|
|
311
|
+
companionTitle: "\nCompanion\n",
|
|
312
|
+
sets: true,
|
|
313
|
+
collectorNumber: true,
|
|
314
|
+
foil: true,
|
|
315
|
+
cat: true,
|
|
316
|
+
tags: true,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (format === "MTGO") {
|
|
320
|
+
formatOptions = {
|
|
321
|
+
...formatOptions,
|
|
322
|
+
sideboardTitle: "\nSIDEBOARD:\n",
|
|
323
|
+
commanderTitle: "\n",
|
|
324
|
+
order: ["mainboard", "companion", "sideboard", "commander", "maybeboard"]
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (format === "Arena") {
|
|
328
|
+
formatOptions = {
|
|
329
|
+
...formatOptions,
|
|
330
|
+
mainboardTitle: "\nDeck\n",
|
|
331
|
+
commanderTitle: "\nCommander\n",
|
|
332
|
+
sideboardTitle: "\nSideboard\n",
|
|
333
|
+
nameTitle: "About\nName ",
|
|
334
|
+
name: true,
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (format === "Moxfield") {
|
|
338
|
+
formatOptions = {
|
|
339
|
+
...formatOptions,
|
|
340
|
+
sideboardTitle: "\nSIDEBOARD:\n",
|
|
341
|
+
sets: true,
|
|
342
|
+
collectorNumber: true,
|
|
343
|
+
foil: true,
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let newList = "";
|
|
348
|
+
if (formatOptions.name && decodedData.name) {
|
|
349
|
+
newList += `${formatOptions.nameTitle}${decodedData.name}\n`;
|
|
350
|
+
}
|
|
351
|
+
const buildCardLine = (card: Obj) => {
|
|
352
|
+
let line = `${card.qty} ${card.name}`;
|
|
353
|
+
if (formatOptions.sets && card.set) {
|
|
354
|
+
line += ` (${card.set})`;
|
|
355
|
+
}
|
|
356
|
+
if (formatOptions.collectorNumber && card.number) {
|
|
357
|
+
line += ` ${card.number}`;
|
|
358
|
+
}
|
|
359
|
+
if (formatOptions.foil && card.foil) {
|
|
360
|
+
line += ` *${card.foil}*`;
|
|
361
|
+
}
|
|
362
|
+
if (formatOptions.cat && card.category) {
|
|
363
|
+
line += ` [${card.category}]`;
|
|
364
|
+
}
|
|
365
|
+
if (formatOptions.tags && card.tags) {
|
|
366
|
+
line += ` ^${card.tags}^`;
|
|
367
|
+
}
|
|
368
|
+
return line;
|
|
369
|
+
}
|
|
370
|
+
const renderSection = (section: Obj[], title: string) => {
|
|
371
|
+
if (section && section.length > 0) {
|
|
372
|
+
newList += title;
|
|
373
|
+
section.forEach(card => {
|
|
374
|
+
newList += buildCardLine(card) + "\n";
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
formatOptions.order.forEach((sectionKey: string) => {
|
|
379
|
+
if (decodedData[sectionKey]) {
|
|
380
|
+
const title = formatOptions[`${sectionKey}Title`] || "";
|
|
381
|
+
renderSection(decodedData[sectionKey], title);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// trim the final newList and set it to the output
|
|
386
|
+
newList = newList.trim();
|
|
387
|
+
hxdec.ready();
|
|
388
|
+
hxdec.decoded(newList);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return;
|
|
392
|
+
},
|
|
393
|
+
init: () => {
|
|
394
|
+
// fetch all sets from scryfall and prepare them for initial firebase upload
|
|
395
|
+
if (typeof hxdec.setData !== "undefined") {
|
|
396
|
+
console.log("Set data already processed, skipping fetch");
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
hxdec.loading("Fetching set data from scryfall");
|
|
400
|
+
fetch("https://api.scryfall.com/sets")
|
|
401
|
+
.then((resp) => resp.json())
|
|
402
|
+
.then((resp) => {
|
|
403
|
+
if (typeof resp.data !== "undefined" && resp.data.length > 0) {
|
|
404
|
+
const sets = [] as Obj[];
|
|
405
|
+
const initSetsData = resp.data;
|
|
406
|
+
// sort initSetsData by release date ascending
|
|
407
|
+
initSetsData.sort((a: Obj, b: Obj) => {
|
|
408
|
+
const dateA = new Date(a.released_at).getTime();
|
|
409
|
+
const dateB = new Date(b.released_at).getTime();
|
|
410
|
+
return dateA - dateB;
|
|
411
|
+
});
|
|
412
|
+
let setCounter = 1;
|
|
413
|
+
initSetsData.forEach((set: Obj) => {
|
|
414
|
+
if (set.set_type !== "token") {
|
|
415
|
+
const hxcode = Number(setCounter).toString(16).padStart(3, "0");
|
|
416
|
+
const newSet = {
|
|
417
|
+
date: new Date(set.released_at).getTime(),
|
|
418
|
+
code: set.code,
|
|
419
|
+
hxcode
|
|
420
|
+
};
|
|
421
|
+
sets.push(newSet);
|
|
422
|
+
setCounter++;
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
if (sets.length > 0) {
|
|
426
|
+
hxdec.setData = sets;
|
|
427
|
+
hxdec.ready();
|
|
428
|
+
} else {
|
|
429
|
+
hxdec.error("No set data found from scryfall");
|
|
430
|
+
console.log("No set data found from scryfall", resp);
|
|
431
|
+
}
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
hxdec.error("No set data found from scryfall");
|
|
435
|
+
console.log("No set data found from scryfall", resp);
|
|
436
|
+
return;
|
|
437
|
+
})
|
|
438
|
+
.catch((err) => {
|
|
439
|
+
hxdec.error("Error fetching set data from scryfall");
|
|
440
|
+
console.error("Error fetching set data from scryfall", err);
|
|
441
|
+
return;
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
getCardData: async (cards: Obj[], properties: string[], callback: (cardInfos: Obj[]) => void) => {
|
|
445
|
+
// fetch all cards from scryfall
|
|
446
|
+
const batchSize = 75; // scryfall allows up to 75 identifiers per request
|
|
447
|
+
const delay = 500; // delay in ms between requests to avoid hitting rate limits
|
|
448
|
+
const cardInfos = [] as Obj[];
|
|
449
|
+
//delay each request by the specified delay to avoid hitting rate limits
|
|
450
|
+
for (let i = 0; i < cards.length; i += batchSize) {
|
|
451
|
+
const chunk = cards.slice(i, i + batchSize);
|
|
452
|
+
const identifiers = chunk.map(card => {
|
|
453
|
+
if (card.set && card.number) {
|
|
454
|
+
return {
|
|
455
|
+
set: card.set,
|
|
456
|
+
collector_number: String(card.number)
|
|
457
|
+
}
|
|
458
|
+
} else if (card.name) {
|
|
459
|
+
return {
|
|
460
|
+
name: card.name
|
|
461
|
+
};
|
|
462
|
+
} else {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
}).filter(id => id !== null);
|
|
466
|
+
if (identifiers.length === 0) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
fetch("https://api.scryfall.com/cards/collection", {
|
|
470
|
+
method: "POST",
|
|
471
|
+
headers: {
|
|
472
|
+
"Content-Type": "application/json"
|
|
473
|
+
},
|
|
474
|
+
body: JSON.stringify({ identifiers })
|
|
475
|
+
})
|
|
476
|
+
.then(resp => resp.json())
|
|
477
|
+
.then(resp => {
|
|
478
|
+
if (resp.data && resp.data.length > 0) {
|
|
479
|
+
cardInfos.push(...resp.data);
|
|
480
|
+
if (cardInfos.length >= cards.length) {
|
|
481
|
+
// all card info has been fetched, add the card names to the cards in the decoded data
|
|
482
|
+
cards.forEach((card, index) => {
|
|
483
|
+
let cardInfo = {} as Obj;
|
|
484
|
+
if (typeof card.name !== "undefined" && (typeof card.set === "undefined" || typeof card.number === "undefined")) {
|
|
485
|
+
cardInfo = cardInfos.find(info => info.name.toLowerCase() === card.name.toLowerCase()) || {} as Obj;
|
|
486
|
+
} else if ((typeof card.set !== "undefined" && typeof card.number !== "undefined") && typeof card.name === "undefined") {
|
|
487
|
+
cardInfo = cardInfos.find(info => info.set.toLowerCase() === card.set.toLowerCase() && info.collector_number === String(card.number)) || {} as Obj;
|
|
488
|
+
}
|
|
489
|
+
if (cardInfo && Object.keys(cardInfo).length !== 0) {
|
|
490
|
+
properties.forEach(prop => {
|
|
491
|
+
if (typeof cardInfo[prop] !== "undefined") {
|
|
492
|
+
cards[index][prop] = cardInfo[prop];
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
} else {
|
|
496
|
+
console.log(`No card info found for card: ${card.name} with set: ${card.set} and number: ${card.number}`);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// console.log("card", card, cardInfo);
|
|
500
|
+
});
|
|
501
|
+
// if all batches have been processed, call the callback with the updated cards
|
|
502
|
+
if (cardInfos.length >= cards.length) {
|
|
503
|
+
callback(cards);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
}
|
|
507
|
+
// console.log("Fetched card info from scryfall:", resp.data);
|
|
508
|
+
} else {
|
|
509
|
+
console.log("No card info found from scryfall for identifiers:", identifiers, resp);
|
|
510
|
+
}
|
|
511
|
+
})
|
|
512
|
+
.catch(err => {
|
|
513
|
+
console.error("Error fetching card info from scryfall for identifiers:", identifiers, err);
|
|
514
|
+
});
|
|
515
|
+
// wait 500ms before next request
|
|
516
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
517
|
+
}
|
|
518
|
+
return;
|
|
519
|
+
},
|
|
520
|
+
digestCard: (line: string) => { //split at first space
|
|
521
|
+
const splitIndex = line.indexOf(" ");
|
|
522
|
+
let name = line.substring(splitIndex + 1).trim();
|
|
523
|
+
const newCard = {
|
|
524
|
+
name,
|
|
525
|
+
qty: Number(line.substring(0, splitIndex).trim().replace("x", "")),
|
|
526
|
+
set: "",
|
|
527
|
+
foil: "",
|
|
528
|
+
cat: "",
|
|
529
|
+
tags: "",
|
|
530
|
+
collectorNumber: "",
|
|
531
|
+
hxcode: "",
|
|
532
|
+
hxnumber: "",
|
|
533
|
+
} as Obj;
|
|
534
|
+
const trimValue = (property: string, start: string, end: string) => {
|
|
535
|
+
const regex = new RegExp(`\\${start}([^\\${end}]*)\\${end}`);
|
|
536
|
+
const match = name.match(regex);
|
|
537
|
+
let replaceSymbol = "";
|
|
538
|
+
if (start === "(") replaceSymbol = `::::`;
|
|
539
|
+
// get the matched value without the start and end symbols and assign it to newCard with the property name as the key
|
|
540
|
+
if (match && match[1]) {
|
|
541
|
+
newCard[property] = match[1].trim();
|
|
542
|
+
name = name.replace(regex, replaceSymbol);
|
|
543
|
+
// console.log('newCard:', newCard, match[1]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
trimValue("set", "(", ")");
|
|
547
|
+
trimValue("foil", "*", "*");
|
|
548
|
+
trimValue("cat", "[", "]");
|
|
549
|
+
trimValue("tags", "^", "^");
|
|
550
|
+
const nameSplit = name.split("::::");
|
|
551
|
+
name = nameSplit[0].trim();
|
|
552
|
+
if (nameSplit.length > 1) {
|
|
553
|
+
newCard.collectorNumber = nameSplit[1].trim();
|
|
554
|
+
}
|
|
555
|
+
if (typeof newCard.set !== "undefined") {
|
|
556
|
+
newCard.hxcode = hxdec.setData.find(s => s.code.toLowerCase() === newCard.set.toLowerCase())?.hxcode || null;
|
|
557
|
+
}
|
|
558
|
+
if (typeof newCard.collectorNumber !== "undefined") {
|
|
559
|
+
// if collector number is just a number, convert to hex and pad to 3 digits
|
|
560
|
+
if (/^\d+$/.test(newCard.collectorNumber)) {
|
|
561
|
+
const hxnumber = Number(newCard.collectorNumber).toString(16);
|
|
562
|
+
newCard.hxnumber = hxnumber;
|
|
563
|
+
} else {
|
|
564
|
+
newCard.hxnumber = `~${newCard.collectorNumber}~`;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return newCard;
|
|
568
|
+
},
|
|
569
|
+
encoded: (list: string) => {
|
|
570
|
+
console.log("hxdec.encoded: ", list);
|
|
571
|
+
},
|
|
572
|
+
decoded: (list: string) => {
|
|
573
|
+
console.log("hxdec.decoded: ", list);
|
|
574
|
+
},
|
|
575
|
+
ready: () => {
|
|
576
|
+
console.log("hxdec.ready: ready to encode and decode");
|
|
577
|
+
},
|
|
578
|
+
loading: (status: string) => {
|
|
579
|
+
console.log("hxdec.loading: ", status);
|
|
580
|
+
},
|
|
581
|
+
warning: (message: string) => {
|
|
582
|
+
console.log("HXDEC Warning:", message);
|
|
583
|
+
},
|
|
584
|
+
error: (message: string) => {
|
|
585
|
+
console.log("HXDEC Error:", message);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
export default hxdec;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"noImplicitReturns": true,
|
|
5
|
+
"noUnusedLocals": true,
|
|
6
|
+
"outDir": "lib",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"target": "es2017"
|
|
11
|
+
},
|
|
12
|
+
"compileOnSave": true,
|
|
13
|
+
"include": [
|
|
14
|
+
"src"
|
|
15
|
+
]
|
|
16
|
+
}
|