ygopro-deck-encode 1.0.5 → 1.0.6
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/README.md +8 -0
- package/dist/index.cjs +196 -3
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +197 -3
- package/dist/index.mjs.map +3 -3
- package/dist/src/base64.d.ts +2 -0
- package/dist/src/ydke.d.ts +7 -0
- package/dist/src/ygom.d.ts +7 -0
- package/index.ts +45 -3
- package/package.json +5 -7
package/README.md
CHANGED
|
@@ -15,6 +15,14 @@ const anotherDeck = YGOProDeck.fromEncodedString(code); // decode it back
|
|
|
15
15
|
|
|
16
16
|
const ydk = deck.toYdkString(); // YDK format
|
|
17
17
|
const yetAnotherDeck = YGOProDeck.fromYdkString(ydk); // decode it back
|
|
18
|
+
|
|
19
|
+
const ydke = deck.toYdkeURL();
|
|
20
|
+
const yetAnotherDeck2 = YGOProDeck.fromYdkeURL(ydke); // decode it back
|
|
21
|
+
|
|
22
|
+
const ygomobileDeckUrl = deck.toYGOMobileDeckURL(); // YGOMobile Deck URL
|
|
23
|
+
const yetAnotherDeck3 = YGOProDeck.fromYGOMobileDeckURL(ygomobileDeckUrl); // decode it back
|
|
24
|
+
|
|
25
|
+
const updateDeckPayload = deck.toUpdateDeckPayload(); // payload for MSG_UPDATE_DECK
|
|
18
26
|
```
|
|
19
27
|
|
|
20
28
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,25 @@ __export(ygopro_deck_encode_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(ygopro_deck_encode_exports);
|
|
25
25
|
|
|
26
|
+
// src/base64.ts
|
|
27
|
+
function toBase64Url(bytes) {
|
|
28
|
+
const base64 = typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : btoa(String.fromCharCode(...bytes));
|
|
29
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
30
|
+
}
|
|
31
|
+
function fromBase64Url(encoded) {
|
|
32
|
+
const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
33
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
34
|
+
if (typeof Buffer !== "undefined") {
|
|
35
|
+
return Uint8Array.from(Buffer.from(padded, "base64"));
|
|
36
|
+
}
|
|
37
|
+
const binary = atob(padded);
|
|
38
|
+
const bytes = new Uint8Array(binary.length);
|
|
39
|
+
for (let i = 0; i < binary.length; i++) {
|
|
40
|
+
bytes[i] = binary.charCodeAt(i);
|
|
41
|
+
}
|
|
42
|
+
return bytes;
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
// src/utils.ts
|
|
27
46
|
function countItems(arr) {
|
|
28
47
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -72,8 +91,149 @@ var BufferWriter = class {
|
|
|
72
91
|
}
|
|
73
92
|
};
|
|
74
93
|
|
|
94
|
+
// src/ydke.ts
|
|
95
|
+
function base64ToUint32Array(base64) {
|
|
96
|
+
const binary = atob(base64);
|
|
97
|
+
const bytes = new Uint8Array(binary.length);
|
|
98
|
+
for (let i = 0; i < binary.length; i++) {
|
|
99
|
+
bytes[i] = binary.charCodeAt(i);
|
|
100
|
+
}
|
|
101
|
+
const view = new DataView(bytes.buffer);
|
|
102
|
+
const result = [];
|
|
103
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
104
|
+
result.push(view.getUint32(i, true));
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
function uint32ArrayToBase64(data) {
|
|
109
|
+
const buffer = new ArrayBuffer(data.length * 4);
|
|
110
|
+
const view = new DataView(buffer);
|
|
111
|
+
data.forEach((val, i) => view.setUint32(i * 4, val, true));
|
|
112
|
+
const bytes = new Uint8Array(buffer);
|
|
113
|
+
let binary = "";
|
|
114
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
115
|
+
binary += String.fromCharCode(bytes[i]);
|
|
116
|
+
}
|
|
117
|
+
return btoa(binary);
|
|
118
|
+
}
|
|
119
|
+
function toYdkeURL(deck) {
|
|
120
|
+
return "ydke://" + uint32ArrayToBase64(deck.main) + "!" + uint32ArrayToBase64(deck.extra) + "!" + uint32ArrayToBase64(deck.side) + "!";
|
|
121
|
+
}
|
|
122
|
+
function fromYdkeURL(ydke) {
|
|
123
|
+
if (!ydke.startsWith("ydke://")) {
|
|
124
|
+
throw new Error("Invalid ydke:// URI");
|
|
125
|
+
}
|
|
126
|
+
const [mainStr, extraStr, sideStr] = ydke.slice(7).split("!");
|
|
127
|
+
if (mainStr === void 0 || extraStr === void 0 || sideStr === void 0) {
|
|
128
|
+
throw new Error("Incomplete ydke:// URI");
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
main: base64ToUint32Array(mainStr),
|
|
132
|
+
extra: base64ToUint32Array(extraStr),
|
|
133
|
+
side: base64ToUint32Array(sideStr)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/ygom.ts
|
|
138
|
+
var QUERY_YGO_TYPE = "ygotype";
|
|
139
|
+
var QUERY_VERSION = "v";
|
|
140
|
+
var ARG_DECK = "deck";
|
|
141
|
+
var QUERY_DECK = "d";
|
|
142
|
+
var QUERY_NAME = "name";
|
|
143
|
+
var URL_SCHEME_HTTP = "http";
|
|
144
|
+
var URL_HOST_DECK = "deck.ourygo.top";
|
|
145
|
+
function toBinary(value, length) {
|
|
146
|
+
return value.toString(2).padStart(length, "0");
|
|
147
|
+
}
|
|
148
|
+
function encodeCards(cards) {
|
|
149
|
+
const bits = [];
|
|
150
|
+
for (let i = 0; i < cards.length; ) {
|
|
151
|
+
const id = cards[i];
|
|
152
|
+
let count = 1;
|
|
153
|
+
while (i + count < cards.length && cards[i + count] === id && count < 3)
|
|
154
|
+
count++;
|
|
155
|
+
const prefix = count === 2 ? "10" : count === 3 ? "11" : "01";
|
|
156
|
+
bits.push(prefix + toBinary(id, 27));
|
|
157
|
+
i += count;
|
|
158
|
+
}
|
|
159
|
+
return bits.join("");
|
|
160
|
+
}
|
|
161
|
+
function countUnique(cards) {
|
|
162
|
+
let num = 0;
|
|
163
|
+
for (let i = 0; i < cards.length; i++) {
|
|
164
|
+
const id = cards[i];
|
|
165
|
+
if (id > 0) {
|
|
166
|
+
num++;
|
|
167
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
168
|
+
i++;
|
|
169
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
170
|
+
i++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return num;
|
|
176
|
+
}
|
|
177
|
+
function toYGOMobileDeckURL(main, extra, side, customParams = {}) {
|
|
178
|
+
const mNum = countUnique(main);
|
|
179
|
+
const eNum = countUnique(extra);
|
|
180
|
+
const sNum = countUnique(side);
|
|
181
|
+
const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);
|
|
182
|
+
let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);
|
|
183
|
+
while (bitString.length % 8 !== 0)
|
|
184
|
+
bitString += "0";
|
|
185
|
+
const bytes = new Uint8Array(bitString.length / 8);
|
|
186
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
187
|
+
bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);
|
|
188
|
+
}
|
|
189
|
+
let encoded = toBase64Url(bytes);
|
|
190
|
+
const searchParams = new URLSearchParams();
|
|
191
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
192
|
+
searchParams.set(key, value);
|
|
193
|
+
}
|
|
194
|
+
searchParams.set(QUERY_YGO_TYPE, ARG_DECK);
|
|
195
|
+
searchParams.set(QUERY_VERSION, "1");
|
|
196
|
+
searchParams.set(QUERY_DECK, encoded);
|
|
197
|
+
return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;
|
|
198
|
+
}
|
|
199
|
+
function fromYGOMobileDeckURL(uri) {
|
|
200
|
+
const url = new URL(uri);
|
|
201
|
+
if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {
|
|
202
|
+
throw new Error("Not a YGO Mobile deck URI");
|
|
203
|
+
}
|
|
204
|
+
let encoded = url.searchParams.get(QUERY_DECK);
|
|
205
|
+
if (!encoded)
|
|
206
|
+
throw new Error("Missing deck data");
|
|
207
|
+
const bytes = fromBase64Url(encoded);
|
|
208
|
+
const bits = Array.from(bytes).map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
209
|
+
const mNum = parseInt(bits.slice(0, 8), 2);
|
|
210
|
+
const eNum = parseInt(bits.slice(8, 12), 2);
|
|
211
|
+
const sNum = parseInt(bits.slice(12, 16), 2);
|
|
212
|
+
const all = mNum + eNum + sNum;
|
|
213
|
+
const cards = [];
|
|
214
|
+
let pos = 16;
|
|
215
|
+
const res = {
|
|
216
|
+
main: [],
|
|
217
|
+
extra: [],
|
|
218
|
+
side: []
|
|
219
|
+
};
|
|
220
|
+
let i = 0;
|
|
221
|
+
while (pos + 29 <= bits.length && cards.length < all) {
|
|
222
|
+
const countBits = bits.slice(pos, pos + 2);
|
|
223
|
+
const count = countBits === "10" ? 2 : countBits === "11" ? 3 : 1;
|
|
224
|
+
const id = parseInt(bits.slice(pos + 2, pos + 29), 2);
|
|
225
|
+
const field = i < mNum ? "main" : i < mNum + eNum ? "extra" : "side";
|
|
226
|
+
res[field].push(...Array(count).fill(id));
|
|
227
|
+
pos += 29;
|
|
228
|
+
i++;
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
...res,
|
|
232
|
+
name: url.searchParams.get(QUERY_NAME)
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
75
236
|
// index.ts
|
|
76
|
-
var import_js_base64 = require("js-base64");
|
|
77
237
|
var YGOProDeck = class {
|
|
78
238
|
constructor() {
|
|
79
239
|
this.main = [];
|
|
@@ -102,7 +262,7 @@ var YGOProDeck = class {
|
|
|
102
262
|
return writer.buffer;
|
|
103
263
|
}
|
|
104
264
|
toEncodedString() {
|
|
105
|
-
return
|
|
265
|
+
return toBase64Url(this.toUint8Array());
|
|
106
266
|
}
|
|
107
267
|
toString() {
|
|
108
268
|
return this.toEncodedString();
|
|
@@ -124,7 +284,7 @@ var YGOProDeck = class {
|
|
|
124
284
|
return new YGOProDeck().fromUint8Array(buf);
|
|
125
285
|
}
|
|
126
286
|
fromEncodedString(str) {
|
|
127
|
-
return this.fromUint8Array(
|
|
287
|
+
return this.fromUint8Array(fromBase64Url(str));
|
|
128
288
|
}
|
|
129
289
|
static fromEncodedString(str) {
|
|
130
290
|
return new YGOProDeck().fromEncodedString(str);
|
|
@@ -169,6 +329,39 @@ var YGOProDeck = class {
|
|
|
169
329
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
170
330
|
return writer.buffer;
|
|
171
331
|
}
|
|
332
|
+
fromYGOMobileDeckURL(uri) {
|
|
333
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
334
|
+
this.main = parsed.main;
|
|
335
|
+
this.extra = parsed.extra;
|
|
336
|
+
this.side = parsed.side;
|
|
337
|
+
this.name = parsed.name;
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
static fromYGOMobileDeckURL(uri) {
|
|
341
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
342
|
+
}
|
|
343
|
+
toYGOMobileDeckURL() {
|
|
344
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
345
|
+
name: this.name
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
fromYdkeURL(uri) {
|
|
349
|
+
const parsed = fromYdkeURL(uri);
|
|
350
|
+
this.main = parsed.main;
|
|
351
|
+
this.extra = parsed.extra;
|
|
352
|
+
this.side = parsed.side;
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
static fromYdkeURL(uri) {
|
|
356
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
357
|
+
}
|
|
358
|
+
toYdkeURL() {
|
|
359
|
+
return toYdkeURL({
|
|
360
|
+
main: this.main,
|
|
361
|
+
extra: this.extra,
|
|
362
|
+
side: this.side
|
|
363
|
+
});
|
|
364
|
+
}
|
|
172
365
|
};
|
|
173
366
|
// Annotate the CommonJS export names for ESM import in node:
|
|
174
367
|
0 && (module.exports = {});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../index.ts", "../src/utils.ts"],
|
|
4
|
-
"sourcesContent": ["import { BufferWriter, countItems } from './src/utils';\nimport { Base64 } from 'js-base64';\n\nexport default class YGOProDeck {\n main: number[] = [];\n extra: number[] = [];\n side: number[] = [];\n\n bufferLength() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n return counted.reduce((a, b) => a + b.size * 4, 0);\n }\n\n toUint8Array() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n const writer = new BufferWriter(\n counted.reduce((a, b) => a + b.size * 4, 0),\n );\n const writeCards = (countMap: Map<number, number>, type: number) => {\n // each card: 28 bits for id, 2 bits(0, 1, 2, 3) for type(0: main, 1: extra, 2: side, 3: unknown), 2 bits for count (0: 1, 1: 2, 2: 3, 3: 4)\n for (const [id, count] of countMap.entries()) {\n if (count > 4) {\n throw new Error(`Too many cards: ${id}`);\n }\n const value = (id & 0xfffffff) | (type << 28) | ((count - 1) << 30);\n writer.writeUint32LE(value);\n }\n };\n counted.forEach(writeCards);\n return writer.buffer;\n }\n\n toEncodedString() {\n return Base64.fromUint8Array(this.toUint8Array(), true);\n }\n\n toString() {\n return this.toEncodedString();\n }\n\n fromUint8Array(buf: Uint8Array) {\n for (let i = 0; i < buf.length - 3; i += 4) {\n const value =\n buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);\n const id = value & 0xfffffff;\n const type = (value >>> 28) & 0x3;\n const count = ((value >>> 30) & 0x3) + 1;\n const cards = [this.main, this.extra, this.side][type];\n for (let j = 0; j < count; j++) {\n cards.push(id);\n }\n }\n return this;\n }\n\n static fromUint8Array(buf: Uint8Array) {\n return new YGOProDeck().fromUint8Array(buf);\n }\n\n fromEncodedString(str: string) {\n return this.fromUint8Array(Base64.toUint8Array(str));\n }\n\n static fromEncodedString(str: string) {\n return new YGOProDeck().fromEncodedString(str);\n }\n\n toYdkString() {\n return [\n '#created by ygopro-deck-encode',\n '#main',\n ...this.main.map((id) => id.toString()),\n '#extra',\n ...this.extra.map((id) => id.toString()),\n '!side',\n ...this.side.map((id) => id.toString()),\n ].join('\\n');\n }\n\n fromYdkString(str: string) {\n const lines = str.split(/\\r?\\n/);\n let current = this.main;\n for (const _line of lines) {\n const line = _line.trim();\n if (line === '#main') {\n current = this.main;\n }\n if (line === '#extra') {\n current = this.extra;\n }\n if (line === '!side') {\n current = this.side;\n }\n if (line.match(/^\\d+$/)) {\n current.push(parseInt(line, 10));\n }\n }\n return this;\n }\n\n static fromYdkString(str: string) {\n return new YGOProDeck().fromYdkString(str);\n }\n\n toUpdateDeckPayload() {\n const cards = [...this.main, ...this.extra, ...this.side];\n const writer = new BufferWriter(cards.length * 4 + 8)\n .writeUint32LE(this.main.length + this.extra.length)\n .writeUint32LE(this.side.length);\n cards.forEach((id) => writer.writeUint32LE(id));\n return writer.buffer;\n }\n}\n", "export function countItems<T>(arr: T[]) {\n const map = new Map<T, number>();\n for (const item of arr) {\n map.set(item, (map.get(item) || 0) + 1);\n }\n return map;\n}\n\nexport class BufferWriter {\n buffer: Uint8Array;\n pointer = 0;\n constructor(length: number) {\n this.buffer = new Uint8Array(length);\n }\n writeUint32LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n this.buffer[this.pointer++] = str.charCodeAt(i);\n }\n return this;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,SAAS,WAAc,KAAU;AACtC,QAAM,MAAM,oBAAI,IAAe;AAC/B,aAAW,QAAQ,KAAK;AACtB,QAAI,IAAI,OAAO,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAEO,IAAM,eAAN,MAAmB;AAAA,EAGxB,YAAY,QAAgB;AAD5B,mBAAU;AAER,SAAK,SAAS,IAAI,WAAW,MAAM;AAAA,EACrC;AAAA,EACA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,OAAe;AACxB,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,KAAa;AACvB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,WAAK,OAAO,KAAK,SAAS,IAAI,IAAI,WAAW,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACF;;;
|
|
3
|
+
"sources": ["../index.ts", "../src/base64.ts", "../src/utils.ts", "../src/ydke.ts", "../src/ygom.ts"],
|
|
4
|
+
"sourcesContent": ["import { fromBase64Url, toBase64Url } from './src/base64';\nimport { BufferWriter, countItems } from './src/utils';\nimport { fromYdkeURL, toYdkeURL } from './src/ydke';\nimport { fromYGOMobileDeckURL, toYGOMobileDeckURL } from './src/ygom';\n\nexport default class YGOProDeck {\n main: number[] = [];\n extra: number[] = [];\n side: number[] = [];\n name?: string;\n\n bufferLength() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n return counted.reduce((a, b) => a + b.size * 4, 0);\n }\n\n toUint8Array() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n const writer = new BufferWriter(\n counted.reduce((a, b) => a + b.size * 4, 0),\n );\n const writeCards = (countMap: Map<number, number>, type: number) => {\n // each card: 28 bits for id, 2 bits(0, 1, 2, 3) for type(0: main, 1: extra, 2: side, 3: unknown), 2 bits for count (0: 1, 1: 2, 2: 3, 3: 4)\n for (const [id, count] of countMap.entries()) {\n if (count > 4) {\n throw new Error(`Too many cards: ${id}`);\n }\n const value = (id & 0xfffffff) | (type << 28) | ((count - 1) << 30);\n writer.writeUint32LE(value);\n }\n };\n counted.forEach(writeCards);\n return writer.buffer;\n }\n\n toEncodedString() {\n return toBase64Url(this.toUint8Array());\n }\n\n toString() {\n return this.toEncodedString();\n }\n\n fromUint8Array(buf: Uint8Array) {\n for (let i = 0; i < buf.length - 3; i += 4) {\n const value =\n buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);\n const id = value & 0xfffffff;\n const type = (value >>> 28) & 0x3;\n const count = ((value >>> 30) & 0x3) + 1;\n const cards = [this.main, this.extra, this.side][type];\n for (let j = 0; j < count; j++) {\n cards.push(id);\n }\n }\n return this;\n }\n\n static fromUint8Array(buf: Uint8Array) {\n return new YGOProDeck().fromUint8Array(buf);\n }\n\n fromEncodedString(str: string) {\n return this.fromUint8Array(fromBase64Url(str));\n }\n\n static fromEncodedString(str: string) {\n return new YGOProDeck().fromEncodedString(str);\n }\n\n toYdkString() {\n return [\n '#created by ygopro-deck-encode',\n '#main',\n ...this.main.map((id) => id.toString()),\n '#extra',\n ...this.extra.map((id) => id.toString()),\n '!side',\n ...this.side.map((id) => id.toString()),\n ].join('\\n');\n }\n\n fromYdkString(str: string) {\n const lines = str.split(/\\r?\\n/);\n let current = this.main;\n for (const _line of lines) {\n const line = _line.trim();\n if (line === '#main') {\n current = this.main;\n }\n if (line === '#extra') {\n current = this.extra;\n }\n if (line === '!side') {\n current = this.side;\n }\n if (line.match(/^\\d+$/)) {\n current.push(parseInt(line, 10));\n }\n }\n return this;\n }\n\n static fromYdkString(str: string) {\n return new YGOProDeck().fromYdkString(str);\n }\n\n toUpdateDeckPayload() {\n const cards = [...this.main, ...this.extra, ...this.side];\n const writer = new BufferWriter(cards.length * 4 + 8)\n .writeUint32LE(this.main.length + this.extra.length)\n .writeUint32LE(this.side.length);\n cards.forEach((id) => writer.writeUint32LE(id));\n return writer.buffer;\n }\n\n fromYGOMobileDeckURL(uri: string): YGOProDeck {\n const parsed = fromYGOMobileDeckURL(uri);\n this.main = parsed.main;\n this.extra = parsed.extra;\n this.side = parsed.side;\n this.name = parsed.name;\n return this;\n }\n\n static fromYGOMobileDeckURL(uri: string): YGOProDeck {\n return new YGOProDeck().fromYGOMobileDeckURL(uri);\n }\n\n toYGOMobileDeckURL(): string {\n return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {\n name: this.name,\n });\n }\n\n fromYdkeURL(uri: string): YGOProDeck {\n const parsed = fromYdkeURL(uri);\n this.main = parsed.main;\n this.extra = parsed.extra;\n this.side = parsed.side;\n return this;\n }\n\n static fromYdkeURL(uri: string): YGOProDeck {\n return new YGOProDeck().fromYdkeURL(uri);\n }\n\n toYdkeURL(): string {\n return toYdkeURL({\n main: this.main,\n extra: this.extra,\n side: this.side,\n });\n }\n}\n", "export function toBase64Url(bytes: Uint8Array): string {\n const base64 = typeof Buffer !== 'undefined'\n ? Buffer.from(bytes).toString('base64')\n : btoa(String.fromCharCode(...bytes));\n\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function fromBase64Url(encoded: string): Uint8Array {\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=');\n\n if (typeof Buffer !== 'undefined') {\n return Uint8Array.from(Buffer.from(padded, 'base64'));\n }\n\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n", "export function countItems<T>(arr: T[]) {\n const map = new Map<T, number>();\n for (const item of arr) {\n map.set(item, (map.get(item) || 0) + 1);\n }\n return map;\n}\n\nexport class BufferWriter {\n buffer: Uint8Array;\n pointer = 0;\n constructor(length: number) {\n this.buffer = new Uint8Array(length);\n }\n writeUint32LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n this.buffer[this.pointer++] = str.charCodeAt(i);\n }\n return this;\n }\n}\n", "// ydke.ts\n\nexport interface TypedDeck {\n main: number[];\n extra: number[];\n side: number[];\n}\n\nfunction base64ToUint32Array(base64: string): number[] {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const view = new DataView(bytes.buffer);\n const result: number[] = [];\n for (let i = 0; i < bytes.length; i += 4) {\n result.push(view.getUint32(i, true)); // little-endian\n }\n return result;\n}\n\nfunction uint32ArrayToBase64(data: number[]): string {\n const buffer = new ArrayBuffer(data.length * 4);\n const view = new DataView(buffer);\n data.forEach((val, i) => view.setUint32(i * 4, val, true));\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nexport function toYdkeURL(deck: TypedDeck): string {\n return (\n 'ydke://' +\n uint32ArrayToBase64(deck.main) +\n '!' +\n uint32ArrayToBase64(deck.extra) +\n '!' +\n uint32ArrayToBase64(deck.side) +\n '!'\n );\n}\n\nexport function fromYdkeURL(ydke: string): TypedDeck {\n if (!ydke.startsWith('ydke://')) {\n throw new Error('Invalid ydke:// URI');\n }\n const [mainStr, extraStr, sideStr] = ydke.slice(7).split('!');\n if (mainStr === undefined || extraStr === undefined || sideStr === undefined) {\n throw new Error('Incomplete ydke:// URI');\n }\n return {\n main: base64ToUint32Array(mainStr),\n extra: base64ToUint32Array(extraStr),\n side: base64ToUint32Array(sideStr),\n };\n}\n", "import { fromBase64Url, toBase64Url } from \"./base64\";\n\n// === \u5E38\u91CF\u533A\u57DF ===\nconst QUERY_YGO_TYPE = 'ygotype';\nconst QUERY_VERSION = 'v';\nconst ARG_DECK = 'deck';\nconst QUERY_DECK = 'd';\nconst QUERY_NAME = 'name';\n\nconst URL_SCHEME_HTTP = 'http';\nconst URL_HOST_DECK = 'deck.ourygo.top';\n\n// === \u5DE5\u5177\u51FD\u6570 ===\nfunction toBinary(value: number, length: number): string {\n return value.toString(2).padStart(length, '0');\n}\n\nfunction encodeCards(cards: number[]): string {\n const bits: string[] = [];\n for (let i = 0; i < cards.length; ) {\n const id = cards[i];\n let count = 1;\n while (i + count < cards.length && cards[i + count] === id && count < 3) count++;\n const prefix = count === 2 ? '10' : count === 3 ? '11' : '01';\n bits.push(prefix + toBinary(id, 27));\n i += count;\n }\n return bits.join('');\n}\n\nfunction countUnique(cards: number[]): number {\n let num = 0;\n for (let i = 0; i < cards.length; i++) {\n const id = cards[i];\n if (id > 0) {\n num++;\n if (i < cards.length - 1 && cards[i + 1] === id) {\n i++;\n if (i < cards.length - 1 && cards[i + 1] === id) {\n i++;\n }\n }\n }\n }\n return num;\n}\n\n// === \u4E3B\u51FD\u6570 ===\n\nexport function toYGOMobileDeckURL(\n main: number[],\n extra: number[],\n side: number[],\n customParams: Record<string, string> = {},\n): string {\n const mNum = countUnique(main);\n const eNum = countUnique(extra);\n const sNum = countUnique(side);\n\n const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);\n let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);\n while (bitString.length % 8 !== 0) bitString += '0';\n\n const bytes = new Uint8Array(bitString.length / 8);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);\n }\n\n let encoded = toBase64Url(bytes)\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(customParams)) {\n searchParams.set(key, value);\n }\n searchParams.set(QUERY_YGO_TYPE, ARG_DECK);\n searchParams.set(QUERY_VERSION, '1');\n searchParams.set(QUERY_DECK, encoded);\n\n return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;\n}\n\n\nexport function fromYGOMobileDeckURL(uri: string): {\n main: number[];\n extra: number[];\n side: number[];\n name?: string;\n} {\n const url = new URL(uri);\n if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {\n throw new Error('Not a YGO Mobile deck URI');\n }\n\n let encoded = url.searchParams.get(QUERY_DECK);\n if (!encoded) throw new Error('Missing deck data');\n\n const bytes = fromBase64Url(encoded);\n\n const bits = Array.from(bytes)\n .map((b) => b.toString(2).padStart(8, '0'))\n .join('');\n\n const mNum = parseInt(bits.slice(0, 8), 2);\n const eNum = parseInt(bits.slice(8, 12), 2);\n const sNum = parseInt(bits.slice(12, 16), 2);\n\n const all = mNum + eNum + sNum;\n const cards: number[] = [];\n let pos = 16;\n \n const res = {\n main: [] as number[],\n extra: [] as number[],\n side: [] as number[],\n }\n\n let i = 0;\n\n while (pos + 29 <= bits.length && cards.length < all) {\n const countBits = bits.slice(pos, pos + 2);\n const count = countBits === '10' ? 2 : countBits === '11' ? 3 : 1;\n const id = parseInt(bits.slice(pos + 2, pos + 29), 2);\n const field = i < mNum ? 'main' : i < mNum + eNum ? 'extra' : 'side';\n res[field].push(...Array(count).fill(id));\n pos += 29;\n i++;\n }\n\n return {\n ...res,\n name: url.searchParams.get(QUERY_NAME),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export default class YGOProDeck {
|
|
|
2
2
|
main: number[];
|
|
3
3
|
extra: number[];
|
|
4
4
|
side: number[];
|
|
5
|
+
name?: string;
|
|
5
6
|
bufferLength(): number;
|
|
6
7
|
toUint8Array(): Uint8Array;
|
|
7
8
|
toEncodedString(): string;
|
|
@@ -14,4 +15,10 @@ export default class YGOProDeck {
|
|
|
14
15
|
fromYdkString(str: string): this;
|
|
15
16
|
static fromYdkString(str: string): YGOProDeck;
|
|
16
17
|
toUpdateDeckPayload(): Uint8Array;
|
|
18
|
+
fromYGOMobileDeckURL(uri: string): YGOProDeck;
|
|
19
|
+
static fromYGOMobileDeckURL(uri: string): YGOProDeck;
|
|
20
|
+
toYGOMobileDeckURL(): string;
|
|
21
|
+
fromYdkeURL(uri: string): YGOProDeck;
|
|
22
|
+
static fromYdkeURL(uri: string): YGOProDeck;
|
|
23
|
+
toYdkeURL(): string;
|
|
17
24
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
// src/base64.ts
|
|
2
|
+
function toBase64Url(bytes) {
|
|
3
|
+
const base64 = typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : btoa(String.fromCharCode(...bytes));
|
|
4
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
5
|
+
}
|
|
6
|
+
function fromBase64Url(encoded) {
|
|
7
|
+
const base64 = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
8
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
9
|
+
if (typeof Buffer !== "undefined") {
|
|
10
|
+
return Uint8Array.from(Buffer.from(padded, "base64"));
|
|
11
|
+
}
|
|
12
|
+
const binary = atob(padded);
|
|
13
|
+
const bytes = new Uint8Array(binary.length);
|
|
14
|
+
for (let i = 0; i < binary.length; i++) {
|
|
15
|
+
bytes[i] = binary.charCodeAt(i);
|
|
16
|
+
}
|
|
17
|
+
return bytes;
|
|
18
|
+
}
|
|
19
|
+
|
|
1
20
|
// src/utils.ts
|
|
2
21
|
function countItems(arr) {
|
|
3
22
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -48,12 +67,154 @@ var BufferWriter = class {
|
|
|
48
67
|
}
|
|
49
68
|
};
|
|
50
69
|
|
|
70
|
+
// src/ydke.ts
|
|
71
|
+
function base64ToUint32Array(base64) {
|
|
72
|
+
const binary = atob(base64);
|
|
73
|
+
const bytes = new Uint8Array(binary.length);
|
|
74
|
+
for (let i = 0; i < binary.length; i++) {
|
|
75
|
+
bytes[i] = binary.charCodeAt(i);
|
|
76
|
+
}
|
|
77
|
+
const view = new DataView(bytes.buffer);
|
|
78
|
+
const result = [];
|
|
79
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
80
|
+
result.push(view.getUint32(i, true));
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
function uint32ArrayToBase64(data) {
|
|
85
|
+
const buffer = new ArrayBuffer(data.length * 4);
|
|
86
|
+
const view = new DataView(buffer);
|
|
87
|
+
data.forEach((val, i) => view.setUint32(i * 4, val, true));
|
|
88
|
+
const bytes = new Uint8Array(buffer);
|
|
89
|
+
let binary = "";
|
|
90
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
91
|
+
binary += String.fromCharCode(bytes[i]);
|
|
92
|
+
}
|
|
93
|
+
return btoa(binary);
|
|
94
|
+
}
|
|
95
|
+
function toYdkeURL(deck) {
|
|
96
|
+
return "ydke://" + uint32ArrayToBase64(deck.main) + "!" + uint32ArrayToBase64(deck.extra) + "!" + uint32ArrayToBase64(deck.side) + "!";
|
|
97
|
+
}
|
|
98
|
+
function fromYdkeURL(ydke) {
|
|
99
|
+
if (!ydke.startsWith("ydke://")) {
|
|
100
|
+
throw new Error("Invalid ydke:// URI");
|
|
101
|
+
}
|
|
102
|
+
const [mainStr, extraStr, sideStr] = ydke.slice(7).split("!");
|
|
103
|
+
if (mainStr === void 0 || extraStr === void 0 || sideStr === void 0) {
|
|
104
|
+
throw new Error("Incomplete ydke:// URI");
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
main: base64ToUint32Array(mainStr),
|
|
108
|
+
extra: base64ToUint32Array(extraStr),
|
|
109
|
+
side: base64ToUint32Array(sideStr)
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/ygom.ts
|
|
114
|
+
var QUERY_YGO_TYPE = "ygotype";
|
|
115
|
+
var QUERY_VERSION = "v";
|
|
116
|
+
var ARG_DECK = "deck";
|
|
117
|
+
var QUERY_DECK = "d";
|
|
118
|
+
var QUERY_NAME = "name";
|
|
119
|
+
var URL_SCHEME_HTTP = "http";
|
|
120
|
+
var URL_HOST_DECK = "deck.ourygo.top";
|
|
121
|
+
function toBinary(value, length) {
|
|
122
|
+
return value.toString(2).padStart(length, "0");
|
|
123
|
+
}
|
|
124
|
+
function encodeCards(cards) {
|
|
125
|
+
const bits = [];
|
|
126
|
+
for (let i = 0; i < cards.length; ) {
|
|
127
|
+
const id = cards[i];
|
|
128
|
+
let count = 1;
|
|
129
|
+
while (i + count < cards.length && cards[i + count] === id && count < 3)
|
|
130
|
+
count++;
|
|
131
|
+
const prefix = count === 2 ? "10" : count === 3 ? "11" : "01";
|
|
132
|
+
bits.push(prefix + toBinary(id, 27));
|
|
133
|
+
i += count;
|
|
134
|
+
}
|
|
135
|
+
return bits.join("");
|
|
136
|
+
}
|
|
137
|
+
function countUnique(cards) {
|
|
138
|
+
let num = 0;
|
|
139
|
+
for (let i = 0; i < cards.length; i++) {
|
|
140
|
+
const id = cards[i];
|
|
141
|
+
if (id > 0) {
|
|
142
|
+
num++;
|
|
143
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
144
|
+
i++;
|
|
145
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return num;
|
|
152
|
+
}
|
|
153
|
+
function toYGOMobileDeckURL(main, extra, side, customParams = {}) {
|
|
154
|
+
const mNum = countUnique(main);
|
|
155
|
+
const eNum = countUnique(extra);
|
|
156
|
+
const sNum = countUnique(side);
|
|
157
|
+
const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);
|
|
158
|
+
let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);
|
|
159
|
+
while (bitString.length % 8 !== 0)
|
|
160
|
+
bitString += "0";
|
|
161
|
+
const bytes = new Uint8Array(bitString.length / 8);
|
|
162
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
163
|
+
bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);
|
|
164
|
+
}
|
|
165
|
+
let encoded = toBase64Url(bytes);
|
|
166
|
+
const searchParams = new URLSearchParams();
|
|
167
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
168
|
+
searchParams.set(key, value);
|
|
169
|
+
}
|
|
170
|
+
searchParams.set(QUERY_YGO_TYPE, ARG_DECK);
|
|
171
|
+
searchParams.set(QUERY_VERSION, "1");
|
|
172
|
+
searchParams.set(QUERY_DECK, encoded);
|
|
173
|
+
return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;
|
|
174
|
+
}
|
|
175
|
+
function fromYGOMobileDeckURL(uri) {
|
|
176
|
+
const url = new URL(uri);
|
|
177
|
+
if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {
|
|
178
|
+
throw new Error("Not a YGO Mobile deck URI");
|
|
179
|
+
}
|
|
180
|
+
let encoded = url.searchParams.get(QUERY_DECK);
|
|
181
|
+
if (!encoded)
|
|
182
|
+
throw new Error("Missing deck data");
|
|
183
|
+
const bytes = fromBase64Url(encoded);
|
|
184
|
+
const bits = Array.from(bytes).map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
185
|
+
const mNum = parseInt(bits.slice(0, 8), 2);
|
|
186
|
+
const eNum = parseInt(bits.slice(8, 12), 2);
|
|
187
|
+
const sNum = parseInt(bits.slice(12, 16), 2);
|
|
188
|
+
const all = mNum + eNum + sNum;
|
|
189
|
+
const cards = [];
|
|
190
|
+
let pos = 16;
|
|
191
|
+
const res = {
|
|
192
|
+
main: [],
|
|
193
|
+
extra: [],
|
|
194
|
+
side: []
|
|
195
|
+
};
|
|
196
|
+
let i = 0;
|
|
197
|
+
while (pos + 29 <= bits.length && cards.length < all) {
|
|
198
|
+
const countBits = bits.slice(pos, pos + 2);
|
|
199
|
+
const count = countBits === "10" ? 2 : countBits === "11" ? 3 : 1;
|
|
200
|
+
const id = parseInt(bits.slice(pos + 2, pos + 29), 2);
|
|
201
|
+
const field = i < mNum ? "main" : i < mNum + eNum ? "extra" : "side";
|
|
202
|
+
res[field].push(...Array(count).fill(id));
|
|
203
|
+
pos += 29;
|
|
204
|
+
i++;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
...res,
|
|
208
|
+
name: url.searchParams.get(QUERY_NAME)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
51
212
|
// index.ts
|
|
52
|
-
import { Base64 } from "js-base64";
|
|
53
213
|
var YGOProDeck = class {
|
|
54
214
|
main = [];
|
|
55
215
|
extra = [];
|
|
56
216
|
side = [];
|
|
217
|
+
name;
|
|
57
218
|
bufferLength() {
|
|
58
219
|
const counted = [this.main, this.extra, this.side].map(countItems);
|
|
59
220
|
return counted.reduce((a, b) => a + b.size * 4, 0);
|
|
@@ -76,7 +237,7 @@ var YGOProDeck = class {
|
|
|
76
237
|
return writer.buffer;
|
|
77
238
|
}
|
|
78
239
|
toEncodedString() {
|
|
79
|
-
return
|
|
240
|
+
return toBase64Url(this.toUint8Array());
|
|
80
241
|
}
|
|
81
242
|
toString() {
|
|
82
243
|
return this.toEncodedString();
|
|
@@ -98,7 +259,7 @@ var YGOProDeck = class {
|
|
|
98
259
|
return new YGOProDeck().fromUint8Array(buf);
|
|
99
260
|
}
|
|
100
261
|
fromEncodedString(str) {
|
|
101
|
-
return this.fromUint8Array(
|
|
262
|
+
return this.fromUint8Array(fromBase64Url(str));
|
|
102
263
|
}
|
|
103
264
|
static fromEncodedString(str) {
|
|
104
265
|
return new YGOProDeck().fromEncodedString(str);
|
|
@@ -143,6 +304,39 @@ var YGOProDeck = class {
|
|
|
143
304
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
144
305
|
return writer.buffer;
|
|
145
306
|
}
|
|
307
|
+
fromYGOMobileDeckURL(uri) {
|
|
308
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
309
|
+
this.main = parsed.main;
|
|
310
|
+
this.extra = parsed.extra;
|
|
311
|
+
this.side = parsed.side;
|
|
312
|
+
this.name = parsed.name;
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
static fromYGOMobileDeckURL(uri) {
|
|
316
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
317
|
+
}
|
|
318
|
+
toYGOMobileDeckURL() {
|
|
319
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
320
|
+
name: this.name
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
fromYdkeURL(uri) {
|
|
324
|
+
const parsed = fromYdkeURL(uri);
|
|
325
|
+
this.main = parsed.main;
|
|
326
|
+
this.extra = parsed.extra;
|
|
327
|
+
this.side = parsed.side;
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
static fromYdkeURL(uri) {
|
|
331
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
332
|
+
}
|
|
333
|
+
toYdkeURL() {
|
|
334
|
+
return toYdkeURL({
|
|
335
|
+
main: this.main,
|
|
336
|
+
extra: this.extra,
|
|
337
|
+
side: this.side
|
|
338
|
+
});
|
|
339
|
+
}
|
|
146
340
|
};
|
|
147
341
|
export {
|
|
148
342
|
YGOProDeck as default
|
package/dist/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/utils.ts", "../index.ts"],
|
|
4
|
-
"sourcesContent": ["export function countItems<T>(arr: T[]) {\n const map = new Map<T, number>();\n for (const item of arr) {\n map.set(item, (map.get(item) || 0) + 1);\n }\n return map;\n}\n\nexport class BufferWriter {\n buffer: Uint8Array;\n pointer = 0;\n constructor(length: number) {\n this.buffer = new Uint8Array(length);\n }\n writeUint32LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n this.buffer[this.pointer++] = str.charCodeAt(i);\n }\n return this;\n }\n}\n", "import { BufferWriter, countItems } from './src/utils';\nimport { Base64 } from 'js-base64';\n\nexport default class YGOProDeck {\n main: number[] = [];\n extra: number[] = [];\n side: number[] = [];\n\n bufferLength() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n return counted.reduce((a, b) => a + b.size * 4, 0);\n }\n\n toUint8Array() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n const writer = new BufferWriter(\n counted.reduce((a, b) => a + b.size * 4, 0),\n );\n const writeCards = (countMap: Map<number, number>, type: number) => {\n // each card: 28 bits for id, 2 bits(0, 1, 2, 3) for type(0: main, 1: extra, 2: side, 3: unknown), 2 bits for count (0: 1, 1: 2, 2: 3, 3: 4)\n for (const [id, count] of countMap.entries()) {\n if (count > 4) {\n throw new Error(`Too many cards: ${id}`);\n }\n const value = (id & 0xfffffff) | (type << 28) | ((count - 1) << 30);\n writer.writeUint32LE(value);\n }\n };\n counted.forEach(writeCards);\n return writer.buffer;\n }\n\n toEncodedString() {\n return Base64.fromUint8Array(this.toUint8Array(), true);\n }\n\n toString() {\n return this.toEncodedString();\n }\n\n fromUint8Array(buf: Uint8Array) {\n for (let i = 0; i < buf.length - 3; i += 4) {\n const value =\n buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);\n const id = value & 0xfffffff;\n const type = (value >>> 28) & 0x3;\n const count = ((value >>> 30) & 0x3) + 1;\n const cards = [this.main, this.extra, this.side][type];\n for (let j = 0; j < count; j++) {\n cards.push(id);\n }\n }\n return this;\n }\n\n static fromUint8Array(buf: Uint8Array) {\n return new YGOProDeck().fromUint8Array(buf);\n }\n\n fromEncodedString(str: string) {\n return this.fromUint8Array(Base64.toUint8Array(str));\n }\n\n static fromEncodedString(str: string) {\n return new YGOProDeck().fromEncodedString(str);\n }\n\n toYdkString() {\n return [\n '#created by ygopro-deck-encode',\n '#main',\n ...this.main.map((id) => id.toString()),\n '#extra',\n ...this.extra.map((id) => id.toString()),\n '!side',\n ...this.side.map((id) => id.toString()),\n ].join('\\n');\n }\n\n fromYdkString(str: string) {\n const lines = str.split(/\\r?\\n/);\n let current = this.main;\n for (const _line of lines) {\n const line = _line.trim();\n if (line === '#main') {\n current = this.main;\n }\n if (line === '#extra') {\n current = this.extra;\n }\n if (line === '!side') {\n current = this.side;\n }\n if (line.match(/^\\d+$/)) {\n current.push(parseInt(line, 10));\n }\n }\n return this;\n }\n\n static fromYdkString(str: string) {\n return new YGOProDeck().fromYdkString(str);\n }\n\n toUpdateDeckPayload() {\n const cards = [...this.main, ...this.extra, ...this.side];\n const writer = new BufferWriter(cards.length * 4 + 8)\n .writeUint32LE(this.main.length + this.extra.length)\n .writeUint32LE(this.side.length);\n cards.forEach((id) => writer.writeUint32LE(id));\n return writer.buffer;\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAAO,SAAS,WAAc,KAAU;AACtC,QAAM,MAAM,oBAAI,IAAe;AAC/B,aAAW,QAAQ,KAAK;AACtB,QAAI,IAAI,OAAO,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EACxC;AACA,SAAO;AACT;AAEO,IAAM,eAAN,MAAmB;AAAA,EACxB;AAAA,EACA,UAAU;AAAA,EACV,YAAY,QAAgB;AAC1B,SAAK,SAAS,IAAI,WAAW,MAAM;AAAA,EACrC;AAAA,EACA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,KAAM;AAC/C,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,SAAK,OAAO,KAAK,SAAS,IAAK,UAAU,IAAK;AAC9C,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,OAAe;AACxB,SAAK,OAAO,KAAK,SAAS,IAAI,QAAQ;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,KAAa;AACvB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,WAAK,OAAO,KAAK,SAAS,IAAI,IAAI,WAAW,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACF;;;
|
|
3
|
+
"sources": ["../src/base64.ts", "../src/utils.ts", "../src/ydke.ts", "../src/ygom.ts", "../index.ts"],
|
|
4
|
+
"sourcesContent": ["export function toBase64Url(bytes: Uint8Array): string {\n const base64 = typeof Buffer !== 'undefined'\n ? Buffer.from(bytes).toString('base64')\n : btoa(String.fromCharCode(...bytes));\n\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function fromBase64Url(encoded: string): Uint8Array {\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, '=');\n\n if (typeof Buffer !== 'undefined') {\n return Uint8Array.from(Buffer.from(padded, 'base64'));\n }\n\n const binary = atob(padded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n", "export function countItems<T>(arr: T[]) {\n const map = new Map<T, number>();\n for (const item of arr) {\n map.set(item, (map.get(item) || 0) + 1);\n }\n return map;\n}\n\nexport class BufferWriter {\n buffer: Uint8Array;\n pointer = 0;\n constructor(length: number) {\n this.buffer = new Uint8Array(length);\n }\n writeUint32LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 24) & 0xff;\n this.buffer[this.pointer++] = (value >>> 16) & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n this.buffer[this.pointer++] = (value >>> 8) & 0xff;\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n this.buffer[this.pointer++] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n this.buffer[this.pointer++] = str.charCodeAt(i);\n }\n return this;\n }\n}\n", "// ydke.ts\n\nexport interface TypedDeck {\n main: number[];\n extra: number[];\n side: number[];\n}\n\nfunction base64ToUint32Array(base64: string): number[] {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const view = new DataView(bytes.buffer);\n const result: number[] = [];\n for (let i = 0; i < bytes.length; i += 4) {\n result.push(view.getUint32(i, true)); // little-endian\n }\n return result;\n}\n\nfunction uint32ArrayToBase64(data: number[]): string {\n const buffer = new ArrayBuffer(data.length * 4);\n const view = new DataView(buffer);\n data.forEach((val, i) => view.setUint32(i * 4, val, true));\n const bytes = new Uint8Array(buffer);\n let binary = '';\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nexport function toYdkeURL(deck: TypedDeck): string {\n return (\n 'ydke://' +\n uint32ArrayToBase64(deck.main) +\n '!' +\n uint32ArrayToBase64(deck.extra) +\n '!' +\n uint32ArrayToBase64(deck.side) +\n '!'\n );\n}\n\nexport function fromYdkeURL(ydke: string): TypedDeck {\n if (!ydke.startsWith('ydke://')) {\n throw new Error('Invalid ydke:// URI');\n }\n const [mainStr, extraStr, sideStr] = ydke.slice(7).split('!');\n if (mainStr === undefined || extraStr === undefined || sideStr === undefined) {\n throw new Error('Incomplete ydke:// URI');\n }\n return {\n main: base64ToUint32Array(mainStr),\n extra: base64ToUint32Array(extraStr),\n side: base64ToUint32Array(sideStr),\n };\n}\n", "import { fromBase64Url, toBase64Url } from \"./base64\";\n\n// === \u5E38\u91CF\u533A\u57DF ===\nconst QUERY_YGO_TYPE = 'ygotype';\nconst QUERY_VERSION = 'v';\nconst ARG_DECK = 'deck';\nconst QUERY_DECK = 'd';\nconst QUERY_NAME = 'name';\n\nconst URL_SCHEME_HTTP = 'http';\nconst URL_HOST_DECK = 'deck.ourygo.top';\n\n// === \u5DE5\u5177\u51FD\u6570 ===\nfunction toBinary(value: number, length: number): string {\n return value.toString(2).padStart(length, '0');\n}\n\nfunction encodeCards(cards: number[]): string {\n const bits: string[] = [];\n for (let i = 0; i < cards.length; ) {\n const id = cards[i];\n let count = 1;\n while (i + count < cards.length && cards[i + count] === id && count < 3) count++;\n const prefix = count === 2 ? '10' : count === 3 ? '11' : '01';\n bits.push(prefix + toBinary(id, 27));\n i += count;\n }\n return bits.join('');\n}\n\nfunction countUnique(cards: number[]): number {\n let num = 0;\n for (let i = 0; i < cards.length; i++) {\n const id = cards[i];\n if (id > 0) {\n num++;\n if (i < cards.length - 1 && cards[i + 1] === id) {\n i++;\n if (i < cards.length - 1 && cards[i + 1] === id) {\n i++;\n }\n }\n }\n }\n return num;\n}\n\n// === \u4E3B\u51FD\u6570 ===\n\nexport function toYGOMobileDeckURL(\n main: number[],\n extra: number[],\n side: number[],\n customParams: Record<string, string> = {},\n): string {\n const mNum = countUnique(main);\n const eNum = countUnique(extra);\n const sNum = countUnique(side);\n\n const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);\n let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);\n while (bitString.length % 8 !== 0) bitString += '0';\n\n const bytes = new Uint8Array(bitString.length / 8);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);\n }\n\n let encoded = toBase64Url(bytes)\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(customParams)) {\n searchParams.set(key, value);\n }\n searchParams.set(QUERY_YGO_TYPE, ARG_DECK);\n searchParams.set(QUERY_VERSION, '1');\n searchParams.set(QUERY_DECK, encoded);\n\n return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;\n}\n\n\nexport function fromYGOMobileDeckURL(uri: string): {\n main: number[];\n extra: number[];\n side: number[];\n name?: string;\n} {\n const url = new URL(uri);\n if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {\n throw new Error('Not a YGO Mobile deck URI');\n }\n\n let encoded = url.searchParams.get(QUERY_DECK);\n if (!encoded) throw new Error('Missing deck data');\n\n const bytes = fromBase64Url(encoded);\n\n const bits = Array.from(bytes)\n .map((b) => b.toString(2).padStart(8, '0'))\n .join('');\n\n const mNum = parseInt(bits.slice(0, 8), 2);\n const eNum = parseInt(bits.slice(8, 12), 2);\n const sNum = parseInt(bits.slice(12, 16), 2);\n\n const all = mNum + eNum + sNum;\n const cards: number[] = [];\n let pos = 16;\n \n const res = {\n main: [] as number[],\n extra: [] as number[],\n side: [] as number[],\n }\n\n let i = 0;\n\n while (pos + 29 <= bits.length && cards.length < all) {\n const countBits = bits.slice(pos, pos + 2);\n const count = countBits === '10' ? 2 : countBits === '11' ? 3 : 1;\n const id = parseInt(bits.slice(pos + 2, pos + 29), 2);\n const field = i < mNum ? 'main' : i < mNum + eNum ? 'extra' : 'side';\n res[field].push(...Array(count).fill(id));\n pos += 29;\n i++;\n }\n\n return {\n ...res,\n name: url.searchParams.get(QUERY_NAME),\n };\n}\n", "import { fromBase64Url, toBase64Url } from './src/base64';\nimport { BufferWriter, countItems } from './src/utils';\nimport { fromYdkeURL, toYdkeURL } from './src/ydke';\nimport { fromYGOMobileDeckURL, toYGOMobileDeckURL } from './src/ygom';\n\nexport default class YGOProDeck {\n main: number[] = [];\n extra: number[] = [];\n side: number[] = [];\n name?: string;\n\n bufferLength() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n return counted.reduce((a, b) => a + b.size * 4, 0);\n }\n\n toUint8Array() {\n const counted = [this.main, this.extra, this.side].map(countItems);\n const writer = new BufferWriter(\n counted.reduce((a, b) => a + b.size * 4, 0),\n );\n const writeCards = (countMap: Map<number, number>, type: number) => {\n // each card: 28 bits for id, 2 bits(0, 1, 2, 3) for type(0: main, 1: extra, 2: side, 3: unknown), 2 bits for count (0: 1, 1: 2, 2: 3, 3: 4)\n for (const [id, count] of countMap.entries()) {\n if (count > 4) {\n throw new Error(`Too many cards: ${id}`);\n }\n const value = (id & 0xfffffff) | (type << 28) | ((count - 1) << 30);\n writer.writeUint32LE(value);\n }\n };\n counted.forEach(writeCards);\n return writer.buffer;\n }\n\n toEncodedString() {\n return toBase64Url(this.toUint8Array());\n }\n\n toString() {\n return this.toEncodedString();\n }\n\n fromUint8Array(buf: Uint8Array) {\n for (let i = 0; i < buf.length - 3; i += 4) {\n const value =\n buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);\n const id = value & 0xfffffff;\n const type = (value >>> 28) & 0x3;\n const count = ((value >>> 30) & 0x3) + 1;\n const cards = [this.main, this.extra, this.side][type];\n for (let j = 0; j < count; j++) {\n cards.push(id);\n }\n }\n return this;\n }\n\n static fromUint8Array(buf: Uint8Array) {\n return new YGOProDeck().fromUint8Array(buf);\n }\n\n fromEncodedString(str: string) {\n return this.fromUint8Array(fromBase64Url(str));\n }\n\n static fromEncodedString(str: string) {\n return new YGOProDeck().fromEncodedString(str);\n }\n\n toYdkString() {\n return [\n '#created by ygopro-deck-encode',\n '#main',\n ...this.main.map((id) => id.toString()),\n '#extra',\n ...this.extra.map((id) => id.toString()),\n '!side',\n ...this.side.map((id) => id.toString()),\n ].join('\\n');\n }\n\n fromYdkString(str: string) {\n const lines = str.split(/\\r?\\n/);\n let current = this.main;\n for (const _line of lines) {\n const line = _line.trim();\n if (line === '#main') {\n current = this.main;\n }\n if (line === '#extra') {\n current = this.extra;\n }\n if (line === '!side') {\n current = this.side;\n }\n if (line.match(/^\\d+$/)) {\n current.push(parseInt(line, 10));\n }\n }\n return this;\n }\n\n static fromYdkString(str: string) {\n return new YGOProDeck().fromYdkString(str);\n }\n\n toUpdateDeckPayload() {\n const cards = [...this.main, ...this.extra, ...this.side];\n const writer = new BufferWriter(cards.length * 4 + 8)\n .writeUint32LE(this.main.length + this.extra.length)\n .writeUint32LE(this.side.length);\n cards.forEach((id) => writer.writeUint32LE(id));\n return writer.buffer;\n }\n\n fromYGOMobileDeckURL(uri: string): YGOProDeck {\n const parsed = fromYGOMobileDeckURL(uri);\n this.main = parsed.main;\n this.extra = parsed.extra;\n this.side = parsed.side;\n this.name = parsed.name;\n return this;\n }\n\n static fromYGOMobileDeckURL(uri: string): YGOProDeck {\n return new YGOProDeck().fromYGOMobileDeckURL(uri);\n }\n\n toYGOMobileDeckURL(): string {\n return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {\n name: this.name,\n });\n }\n\n fromYdkeURL(uri: string): YGOProDeck {\n const parsed = fromYdkeURL(uri);\n this.main = parsed.main;\n this.extra = parsed.extra;\n this.side = parsed.side;\n return this;\n }\n\n static fromYdkeURL(uri: string): YGOProDeck {\n return new YGOProDeck().fromYdkeURL(uri);\n }\n\n toYdkeURL(): string {\n return toYdkeURL({\n main: this.main,\n extra: this.extra,\n side: this.side,\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function toYGOMobileDeckURL(main: number[], extra: number[], side: number[], customParams?: Record<string, string>): string;
|
|
2
|
+
export declare function fromYGOMobileDeckURL(uri: string): {
|
|
3
|
+
main: number[];
|
|
4
|
+
extra: number[];
|
|
5
|
+
side: number[];
|
|
6
|
+
name?: string;
|
|
7
|
+
};
|
package/index.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { fromBase64Url, toBase64Url } from './src/base64';
|
|
1
2
|
import { BufferWriter, countItems } from './src/utils';
|
|
2
|
-
import {
|
|
3
|
+
import { fromYdkeURL, toYdkeURL } from './src/ydke';
|
|
4
|
+
import { fromYGOMobileDeckURL, toYGOMobileDeckURL } from './src/ygom';
|
|
3
5
|
|
|
4
6
|
export default class YGOProDeck {
|
|
5
7
|
main: number[] = [];
|
|
6
8
|
extra: number[] = [];
|
|
7
9
|
side: number[] = [];
|
|
10
|
+
name?: string;
|
|
8
11
|
|
|
9
12
|
bufferLength() {
|
|
10
13
|
const counted = [this.main, this.extra, this.side].map(countItems);
|
|
@@ -31,7 +34,7 @@ export default class YGOProDeck {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
toEncodedString() {
|
|
34
|
-
return
|
|
37
|
+
return toBase64Url(this.toUint8Array());
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
toString() {
|
|
@@ -58,7 +61,7 @@ export default class YGOProDeck {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
fromEncodedString(str: string) {
|
|
61
|
-
return this.fromUint8Array(
|
|
64
|
+
return this.fromUint8Array(fromBase64Url(str));
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
static fromEncodedString(str: string) {
|
|
@@ -110,4 +113,43 @@ export default class YGOProDeck {
|
|
|
110
113
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
111
114
|
return writer.buffer;
|
|
112
115
|
}
|
|
116
|
+
|
|
117
|
+
fromYGOMobileDeckURL(uri: string): YGOProDeck {
|
|
118
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
119
|
+
this.main = parsed.main;
|
|
120
|
+
this.extra = parsed.extra;
|
|
121
|
+
this.side = parsed.side;
|
|
122
|
+
this.name = parsed.name;
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static fromYGOMobileDeckURL(uri: string): YGOProDeck {
|
|
127
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
toYGOMobileDeckURL(): string {
|
|
131
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
132
|
+
name: this.name,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fromYdkeURL(uri: string): YGOProDeck {
|
|
137
|
+
const parsed = fromYdkeURL(uri);
|
|
138
|
+
this.main = parsed.main;
|
|
139
|
+
this.extra = parsed.extra;
|
|
140
|
+
this.side = parsed.side;
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static fromYdkeURL(uri: string): YGOProDeck {
|
|
145
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toYdkeURL(): string {
|
|
149
|
+
return toYdkeURL({
|
|
150
|
+
main: this.main,
|
|
151
|
+
extra: this.extra,
|
|
152
|
+
side: this.side,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
113
155
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ygopro-deck-encode",
|
|
3
3
|
"description": "YGOPro Deck encode and decode, with well-browser support.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.6",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"lint": "eslint --fix .",
|
|
10
|
-
"compile:cjs": "esbuild index.ts --outfile=dist/index.cjs --bundle --sourcemap --platform=node --target=es2019
|
|
11
|
-
"compile:esm": "esbuild index.ts --outfile=dist/index.mjs --bundle --sourcemap --platform=neutral --target=esnext
|
|
10
|
+
"compile:cjs": "esbuild index.ts --outfile=dist/index.cjs --bundle --sourcemap --platform=node --target=es2019",
|
|
11
|
+
"compile:esm": "esbuild index.ts --outfile=dist/index.mjs --bundle --sourcemap --platform=neutral --target=esnext",
|
|
12
12
|
"compile:types": "tsc --emitDeclarationOnly --declaration",
|
|
13
13
|
"build": "rimraf dist && npm run compile:cjs && npm run compile:esm && npm run compile:types",
|
|
14
14
|
"test": "jest --passWithNoTests",
|
|
@@ -56,9 +56,7 @@
|
|
|
56
56
|
"prettier": "^2.8.4",
|
|
57
57
|
"rimraf": "^4.1.2",
|
|
58
58
|
"ts-jest": "^29.0.5",
|
|
59
|
-
"typescript": "^4.9.5"
|
|
60
|
-
|
|
61
|
-
"dependencies": {
|
|
62
|
-
"js-base64": "^3.7.5"
|
|
59
|
+
"typescript": "^4.9.5",
|
|
60
|
+
"ydke": "^1.1.0"
|
|
63
61
|
}
|
|
64
62
|
}
|