ygopro-deck-encode 1.0.5 → 1.0.7
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 +12 -0
- package/dist/index.cjs +311 -20
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +311 -19
- package/dist/index.mjs.map +3 -3
- package/dist/src/base64.d.ts +2 -0
- package/dist/src/utils.d.ts +22 -1
- package/dist/src/ydke.d.ts +7 -0
- package/dist/src/ygom.d.ts +7 -0
- package/index.ts +72 -4
- package/package.json +5 -7
package/README.md
CHANGED
|
@@ -15,6 +15,18 @@ 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
|
|
26
|
+
// decode it back, but a callback is needed for check if it's extra deck
|
|
27
|
+
// use SQLite3 is recommended
|
|
28
|
+
// by default, it would assume everything is main deck
|
|
29
|
+
const yetAnotherDeck4 = YGOProDeck.fromUpdateDeckPayload(updateDeckPayload, (code, i, mainc) => i >= mainc - 15);
|
|
18
30
|
```
|
|
19
31
|
|
|
20
32
|
|
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();
|
|
@@ -31,49 +50,264 @@ function countItems(arr) {
|
|
|
31
50
|
}
|
|
32
51
|
return map;
|
|
33
52
|
}
|
|
34
|
-
var
|
|
35
|
-
constructor(
|
|
53
|
+
var BufferCursor = class {
|
|
54
|
+
constructor(bufOrLength) {
|
|
36
55
|
this.pointer = 0;
|
|
37
|
-
|
|
56
|
+
if (typeof bufOrLength === "number") {
|
|
57
|
+
this.buffer = new Uint8Array(bufOrLength);
|
|
58
|
+
} else {
|
|
59
|
+
this.buffer = bufOrLength;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 安全地将 pointer 向前移动指定字节数。
|
|
64
|
+
* @param bytes 要移动的字节数
|
|
65
|
+
* @returns 原来 pointer 的值
|
|
66
|
+
* @throws RangeError 如果越界
|
|
67
|
+
*/
|
|
68
|
+
increasePointer(bytes) {
|
|
69
|
+
const old = this.pointer;
|
|
70
|
+
const next = old + bytes;
|
|
71
|
+
if (next > this.buffer.length) {
|
|
72
|
+
throw new RangeError(
|
|
73
|
+
`Pointer overflow: tried to move to ${next}, but buffer length is ${this.buffer.length}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
this.pointer = next;
|
|
77
|
+
return old;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var BufferWriter = class extends BufferCursor {
|
|
81
|
+
constructor(length) {
|
|
82
|
+
super(length);
|
|
38
83
|
}
|
|
39
84
|
writeUint32LE(value) {
|
|
40
|
-
|
|
41
|
-
this.buffer[
|
|
42
|
-
this.buffer[
|
|
43
|
-
this.buffer[
|
|
85
|
+
const idx = this.increasePointer(4);
|
|
86
|
+
this.buffer[idx] = value & 255;
|
|
87
|
+
this.buffer[idx + 1] = value >>> 8 & 255;
|
|
88
|
+
this.buffer[idx + 2] = value >>> 16 & 255;
|
|
89
|
+
this.buffer[idx + 3] = value >>> 24 & 255;
|
|
44
90
|
return this;
|
|
45
91
|
}
|
|
46
92
|
writeUint32BE(value) {
|
|
47
|
-
|
|
48
|
-
this.buffer[
|
|
49
|
-
this.buffer[
|
|
50
|
-
this.buffer[
|
|
93
|
+
const idx = this.increasePointer(4);
|
|
94
|
+
this.buffer[idx] = value >>> 24 & 255;
|
|
95
|
+
this.buffer[idx + 1] = value >>> 16 & 255;
|
|
96
|
+
this.buffer[idx + 2] = value >>> 8 & 255;
|
|
97
|
+
this.buffer[idx + 3] = value & 255;
|
|
51
98
|
return this;
|
|
52
99
|
}
|
|
53
100
|
writeUint16LE(value) {
|
|
54
|
-
|
|
55
|
-
this.buffer[
|
|
101
|
+
const idx = this.increasePointer(2);
|
|
102
|
+
this.buffer[idx] = value & 255;
|
|
103
|
+
this.buffer[idx + 1] = value >>> 8 & 255;
|
|
56
104
|
return this;
|
|
57
105
|
}
|
|
58
106
|
writeUint16BE(value) {
|
|
59
|
-
|
|
60
|
-
this.buffer[
|
|
107
|
+
const idx = this.increasePointer(2);
|
|
108
|
+
this.buffer[idx] = value >>> 8 & 255;
|
|
109
|
+
this.buffer[idx + 1] = value & 255;
|
|
61
110
|
return this;
|
|
62
111
|
}
|
|
63
112
|
writeUint8(value) {
|
|
64
|
-
|
|
113
|
+
const idx = this.increasePointer(1);
|
|
114
|
+
this.buffer[idx] = value & 255;
|
|
65
115
|
return this;
|
|
66
116
|
}
|
|
67
117
|
writeString(str) {
|
|
68
118
|
for (let i = 0; i < str.length; i++) {
|
|
69
|
-
|
|
119
|
+
const idx = this.increasePointer(1);
|
|
120
|
+
this.buffer[idx] = str.charCodeAt(i);
|
|
70
121
|
}
|
|
71
122
|
return this;
|
|
72
123
|
}
|
|
73
124
|
};
|
|
125
|
+
var BufferReader = class extends BufferCursor {
|
|
126
|
+
constructor(buf) {
|
|
127
|
+
super(buf);
|
|
128
|
+
}
|
|
129
|
+
readUint32LE() {
|
|
130
|
+
const idx = this.increasePointer(4);
|
|
131
|
+
const b = this.buffer;
|
|
132
|
+
return (b[idx] | b[idx + 1] << 8 | b[idx + 2] << 16 | b[idx + 3] << 24) >>> 0;
|
|
133
|
+
}
|
|
134
|
+
readUint32BE() {
|
|
135
|
+
const idx = this.increasePointer(4);
|
|
136
|
+
const b = this.buffer;
|
|
137
|
+
return (b[idx] << 24 | b[idx + 1] << 16 | b[idx + 2] << 8 | b[idx + 3]) >>> 0;
|
|
138
|
+
}
|
|
139
|
+
readUint16LE() {
|
|
140
|
+
const idx = this.increasePointer(2);
|
|
141
|
+
const b = this.buffer;
|
|
142
|
+
return b[idx] | b[idx + 1] << 8;
|
|
143
|
+
}
|
|
144
|
+
readUint16BE() {
|
|
145
|
+
const idx = this.increasePointer(2);
|
|
146
|
+
const b = this.buffer;
|
|
147
|
+
return b[idx] << 8 | b[idx + 1];
|
|
148
|
+
}
|
|
149
|
+
readUint8() {
|
|
150
|
+
const idx = this.increasePointer(1);
|
|
151
|
+
return this.buffer[idx];
|
|
152
|
+
}
|
|
153
|
+
readString(length) {
|
|
154
|
+
let s = "";
|
|
155
|
+
for (let i = 0; i < length; i++) {
|
|
156
|
+
const idx = this.increasePointer(1);
|
|
157
|
+
s += String.fromCharCode(this.buffer[idx]);
|
|
158
|
+
}
|
|
159
|
+
return s;
|
|
160
|
+
}
|
|
161
|
+
readRemaining() {
|
|
162
|
+
const remaining = this.buffer.length - this.pointer;
|
|
163
|
+
const idx = this.increasePointer(remaining);
|
|
164
|
+
return this.buffer.subarray(idx, this.buffer.length);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/ydke.ts
|
|
169
|
+
function base64ToUint32Array(base64) {
|
|
170
|
+
const binary = atob(base64);
|
|
171
|
+
const bytes = new Uint8Array(binary.length);
|
|
172
|
+
for (let i = 0; i < binary.length; i++) {
|
|
173
|
+
bytes[i] = binary.charCodeAt(i);
|
|
174
|
+
}
|
|
175
|
+
const view = new DataView(bytes.buffer);
|
|
176
|
+
const result = [];
|
|
177
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
178
|
+
result.push(view.getUint32(i, true));
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
function uint32ArrayToBase64(data) {
|
|
183
|
+
const buffer = new ArrayBuffer(data.length * 4);
|
|
184
|
+
const view = new DataView(buffer);
|
|
185
|
+
data.forEach((val, i) => view.setUint32(i * 4, val, true));
|
|
186
|
+
const bytes = new Uint8Array(buffer);
|
|
187
|
+
let binary = "";
|
|
188
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
189
|
+
binary += String.fromCharCode(bytes[i]);
|
|
190
|
+
}
|
|
191
|
+
return btoa(binary);
|
|
192
|
+
}
|
|
193
|
+
function toYdkeURL(deck) {
|
|
194
|
+
return "ydke://" + uint32ArrayToBase64(deck.main) + "!" + uint32ArrayToBase64(deck.extra) + "!" + uint32ArrayToBase64(deck.side) + "!";
|
|
195
|
+
}
|
|
196
|
+
function fromYdkeURL(ydke) {
|
|
197
|
+
if (!ydke.startsWith("ydke://")) {
|
|
198
|
+
throw new Error("Invalid ydke:// URI");
|
|
199
|
+
}
|
|
200
|
+
const [mainStr, extraStr, sideStr] = ydke.slice(7).split("!");
|
|
201
|
+
if (mainStr === void 0 || extraStr === void 0 || sideStr === void 0) {
|
|
202
|
+
throw new Error("Incomplete ydke:// URI");
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
main: base64ToUint32Array(mainStr),
|
|
206
|
+
extra: base64ToUint32Array(extraStr),
|
|
207
|
+
side: base64ToUint32Array(sideStr)
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/ygom.ts
|
|
212
|
+
var QUERY_YGO_TYPE = "ygotype";
|
|
213
|
+
var QUERY_VERSION = "v";
|
|
214
|
+
var ARG_DECK = "deck";
|
|
215
|
+
var QUERY_DECK = "d";
|
|
216
|
+
var QUERY_NAME = "name";
|
|
217
|
+
var URL_SCHEME_HTTP = "http";
|
|
218
|
+
var URL_HOST_DECK = "deck.ourygo.top";
|
|
219
|
+
function toBinary(value, length) {
|
|
220
|
+
return value.toString(2).padStart(length, "0");
|
|
221
|
+
}
|
|
222
|
+
function encodeCards(cards) {
|
|
223
|
+
const bits = [];
|
|
224
|
+
for (let i = 0; i < cards.length; ) {
|
|
225
|
+
const id = cards[i];
|
|
226
|
+
let count = 1;
|
|
227
|
+
while (i + count < cards.length && cards[i + count] === id && count < 3)
|
|
228
|
+
count++;
|
|
229
|
+
const prefix = count === 2 ? "10" : count === 3 ? "11" : "01";
|
|
230
|
+
bits.push(prefix + toBinary(id, 27));
|
|
231
|
+
i += count;
|
|
232
|
+
}
|
|
233
|
+
return bits.join("");
|
|
234
|
+
}
|
|
235
|
+
function countUnique(cards) {
|
|
236
|
+
let num = 0;
|
|
237
|
+
for (let i = 0; i < cards.length; i++) {
|
|
238
|
+
const id = cards[i];
|
|
239
|
+
if (id > 0) {
|
|
240
|
+
num++;
|
|
241
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
242
|
+
i++;
|
|
243
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
244
|
+
i++;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return num;
|
|
250
|
+
}
|
|
251
|
+
function toYGOMobileDeckURL(main, extra, side, customParams = {}) {
|
|
252
|
+
const mNum = countUnique(main);
|
|
253
|
+
const eNum = countUnique(extra);
|
|
254
|
+
const sNum = countUnique(side);
|
|
255
|
+
const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);
|
|
256
|
+
let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);
|
|
257
|
+
while (bitString.length % 8 !== 0)
|
|
258
|
+
bitString += "0";
|
|
259
|
+
const bytes = new Uint8Array(bitString.length / 8);
|
|
260
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
261
|
+
bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);
|
|
262
|
+
}
|
|
263
|
+
let encoded = toBase64Url(bytes);
|
|
264
|
+
const searchParams = new URLSearchParams();
|
|
265
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
266
|
+
searchParams.set(key, value);
|
|
267
|
+
}
|
|
268
|
+
searchParams.set(QUERY_YGO_TYPE, ARG_DECK);
|
|
269
|
+
searchParams.set(QUERY_VERSION, "1");
|
|
270
|
+
searchParams.set(QUERY_DECK, encoded);
|
|
271
|
+
return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;
|
|
272
|
+
}
|
|
273
|
+
function fromYGOMobileDeckURL(uri) {
|
|
274
|
+
const url = new URL(uri);
|
|
275
|
+
if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {
|
|
276
|
+
throw new Error("Not a YGO Mobile deck URI");
|
|
277
|
+
}
|
|
278
|
+
let encoded = url.searchParams.get(QUERY_DECK);
|
|
279
|
+
if (!encoded)
|
|
280
|
+
throw new Error("Missing deck data");
|
|
281
|
+
const bytes = fromBase64Url(encoded);
|
|
282
|
+
const bits = Array.from(bytes).map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
283
|
+
const mNum = parseInt(bits.slice(0, 8), 2);
|
|
284
|
+
const eNum = parseInt(bits.slice(8, 12), 2);
|
|
285
|
+
const sNum = parseInt(bits.slice(12, 16), 2);
|
|
286
|
+
const all = mNum + eNum + sNum;
|
|
287
|
+
const cards = [];
|
|
288
|
+
let pos = 16;
|
|
289
|
+
const res = {
|
|
290
|
+
main: [],
|
|
291
|
+
extra: [],
|
|
292
|
+
side: []
|
|
293
|
+
};
|
|
294
|
+
let i = 0;
|
|
295
|
+
while (pos + 29 <= bits.length && cards.length < all) {
|
|
296
|
+
const countBits = bits.slice(pos, pos + 2);
|
|
297
|
+
const count = countBits === "10" ? 2 : countBits === "11" ? 3 : 1;
|
|
298
|
+
const id = parseInt(bits.slice(pos + 2, pos + 29), 2);
|
|
299
|
+
const field = i < mNum ? "main" : i < mNum + eNum ? "extra" : "side";
|
|
300
|
+
res[field].push(...Array(count).fill(id));
|
|
301
|
+
pos += 29;
|
|
302
|
+
i++;
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
...res,
|
|
306
|
+
name: url.searchParams.get(QUERY_NAME)
|
|
307
|
+
};
|
|
308
|
+
}
|
|
74
309
|
|
|
75
310
|
// index.ts
|
|
76
|
-
var import_js_base64 = require("js-base64");
|
|
77
311
|
var YGOProDeck = class {
|
|
78
312
|
constructor() {
|
|
79
313
|
this.main = [];
|
|
@@ -102,7 +336,7 @@ var YGOProDeck = class {
|
|
|
102
336
|
return writer.buffer;
|
|
103
337
|
}
|
|
104
338
|
toEncodedString() {
|
|
105
|
-
return
|
|
339
|
+
return toBase64Url(this.toUint8Array());
|
|
106
340
|
}
|
|
107
341
|
toString() {
|
|
108
342
|
return this.toEncodedString();
|
|
@@ -124,7 +358,7 @@ var YGOProDeck = class {
|
|
|
124
358
|
return new YGOProDeck().fromUint8Array(buf);
|
|
125
359
|
}
|
|
126
360
|
fromEncodedString(str) {
|
|
127
|
-
return this.fromUint8Array(
|
|
361
|
+
return this.fromUint8Array(fromBase64Url(str));
|
|
128
362
|
}
|
|
129
363
|
static fromEncodedString(str) {
|
|
130
364
|
return new YGOProDeck().fromEncodedString(str);
|
|
@@ -163,12 +397,69 @@ var YGOProDeck = class {
|
|
|
163
397
|
static fromYdkString(str) {
|
|
164
398
|
return new YGOProDeck().fromYdkString(str);
|
|
165
399
|
}
|
|
400
|
+
fromUpdateDeckPayload(buf, isExtraDeckCard = () => false) {
|
|
401
|
+
const reader = new BufferReader(buf);
|
|
402
|
+
const mainc = reader.readUint32LE();
|
|
403
|
+
const sidec = reader.readUint32LE();
|
|
404
|
+
this.main = [];
|
|
405
|
+
this.extra = [];
|
|
406
|
+
this.side = [];
|
|
407
|
+
for (let i = 0; i < mainc; i++) {
|
|
408
|
+
const id = reader.readUint32LE();
|
|
409
|
+
if (isExtraDeckCard(id, i, mainc)) {
|
|
410
|
+
this.extra.push(id);
|
|
411
|
+
} else {
|
|
412
|
+
this.main.push(id);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
for (let i = 0; i < sidec; i++) {
|
|
416
|
+
const id = reader.readUint32LE();
|
|
417
|
+
this.side.push(id);
|
|
418
|
+
}
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
static fromUpdateDeckPayload(buf, isExtraDeckCard = () => false) {
|
|
422
|
+
return new YGOProDeck().fromUpdateDeckPayload(buf, isExtraDeckCard);
|
|
423
|
+
}
|
|
166
424
|
toUpdateDeckPayload() {
|
|
167
425
|
const cards = [...this.main, ...this.extra, ...this.side];
|
|
168
426
|
const writer = new BufferWriter(cards.length * 4 + 8).writeUint32LE(this.main.length + this.extra.length).writeUint32LE(this.side.length);
|
|
169
427
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
170
428
|
return writer.buffer;
|
|
171
429
|
}
|
|
430
|
+
fromYGOMobileDeckURL(uri) {
|
|
431
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
432
|
+
this.main = parsed.main;
|
|
433
|
+
this.extra = parsed.extra;
|
|
434
|
+
this.side = parsed.side;
|
|
435
|
+
this.name = parsed.name;
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
static fromYGOMobileDeckURL(uri) {
|
|
439
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
440
|
+
}
|
|
441
|
+
toYGOMobileDeckURL() {
|
|
442
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
443
|
+
name: this.name
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
fromYdkeURL(uri) {
|
|
447
|
+
const parsed = fromYdkeURL(uri);
|
|
448
|
+
this.main = parsed.main;
|
|
449
|
+
this.extra = parsed.extra;
|
|
450
|
+
this.side = parsed.side;
|
|
451
|
+
return this;
|
|
452
|
+
}
|
|
453
|
+
static fromYdkeURL(uri) {
|
|
454
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
455
|
+
}
|
|
456
|
+
toYdkeURL() {
|
|
457
|
+
return toYdkeURL({
|
|
458
|
+
main: this.main,
|
|
459
|
+
extra: this.extra,
|
|
460
|
+
side: this.side
|
|
461
|
+
});
|
|
462
|
+
}
|
|
172
463
|
};
|
|
173
464
|
// Annotate the CommonJS export names for ESM import in node:
|
|
174
465
|
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": "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",
|
|
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 { BufferReader, 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 fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number, i: number, mainc: number) => boolean = () => false) {\n const reader = new BufferReader(buf);\n const mainc = reader.readUint32LE();\n const sidec = reader.readUint32LE();\n this.main = [];\n this.extra = [];\n this.side = [];\n for(let i = 0; i < mainc; i++) {\n const id = reader.readUint32LE();\n if (isExtraDeckCard(id, i, mainc)) {\n this.extra.push(id);\n } else {\n this.main.push(id);\n }\n }\n for(let i = 0; i < sidec; i++) {\n const id = reader.readUint32LE();\n this.side.push(id);\n }\n return this;\n }\n\n static fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number) => boolean = () => false) { \n return new YGOProDeck().fromUpdateDeckPayload(buf, isExtraDeckCard);\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}\nabstract class BufferCursor {\n buffer: Uint8Array;\n pointer = 0;\n\n constructor(bufOrLength: Uint8Array | number) {\n if (typeof bufOrLength === 'number') {\n this.buffer = new Uint8Array(bufOrLength);\n } else {\n this.buffer = bufOrLength;\n }\n }\n\n /**\n * \u5B89\u5168\u5730\u5C06 pointer \u5411\u524D\u79FB\u52A8\u6307\u5B9A\u5B57\u8282\u6570\u3002\n * @param bytes \u8981\u79FB\u52A8\u7684\u5B57\u8282\u6570\n * @returns \u539F\u6765 pointer \u7684\u503C\n * @throws RangeError \u5982\u679C\u8D8A\u754C\n */\n protected increasePointer(bytes: number): number {\n const old = this.pointer;\n const next = old + bytes;\n if (next > this.buffer.length) {\n throw new RangeError(\n `Pointer overflow: tried to move to ${next}, but buffer length is ${this.buffer.length}`\n );\n }\n this.pointer = next;\n return old;\n }\n}\n\nexport class BufferWriter extends BufferCursor {\n constructor(length: number) {\n super(length);\n }\n\n writeUint32LE(value: number) {\n const idx = this.increasePointer(4);\n this.buffer[idx ] = value & 0xff;\n this.buffer[idx + 1] = (value >>> 8) & 0xff;\n this.buffer[idx + 2] = (value >>> 16) & 0xff;\n this.buffer[idx + 3] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n const idx = this.increasePointer(4);\n this.buffer[idx ] = (value >>> 24) & 0xff;\n this.buffer[idx + 1] = (value >>> 16) & 0xff;\n this.buffer[idx + 2] = (value >>> 8) & 0xff;\n this.buffer[idx + 3] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n const idx = this.increasePointer(2);\n this.buffer[idx ] = value & 0xff;\n this.buffer[idx + 1] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n const idx = this.increasePointer(2);\n this.buffer[idx ] = (value >>> 8) & 0xff;\n this.buffer[idx + 1] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n const idx = this.increasePointer(1);\n this.buffer[idx] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n const idx = this.increasePointer(1);\n this.buffer[idx] = str.charCodeAt(i);\n }\n return this;\n }\n}\n\nexport class BufferReader extends BufferCursor {\n constructor(buf: Uint8Array) {\n super(buf);\n }\n\n readUint32LE(): number {\n const idx = this.increasePointer(4);\n const b = this.buffer;\n // >>>0 \u4FDD\u8BC1\u65E0\u7B26\u53F7\u8F93\u51FA\n return (\n (b[idx ] ) |\n (b[idx + 1] << 8) |\n (b[idx + 2] << 16) |\n (b[idx + 3] << 24)\n ) >>> 0;\n }\n\n readUint32BE(): number {\n const idx = this.increasePointer(4);\n const b = this.buffer;\n return (\n (b[idx ] << 24) |\n (b[idx + 1] << 16) |\n (b[idx + 2] << 8) |\n (b[idx + 3] )\n ) >>> 0;\n }\n\n readUint16LE(): number {\n const idx = this.increasePointer(2);\n const b = this.buffer;\n return (b[idx] | (b[idx + 1] << 8));\n }\n\n readUint16BE(): number {\n const idx = this.increasePointer(2);\n const b = this.buffer;\n return ((b[idx] << 8) | b[idx + 1]);\n }\n\n readUint8(): number {\n const idx = this.increasePointer(1);\n return this.buffer[idx];\n }\n\n readString(length: number): string {\n let s = '';\n for (let i = 0; i < length; i++) {\n const idx = this.increasePointer(1);\n s += String.fromCharCode(this.buffer[idx]);\n }\n return s;\n }\n\n readRemaining(): Uint8Array {\n const remaining = this.buffer.length - this.pointer;\n const idx = this.increasePointer(remaining);\n return this.buffer.subarray(idx, this.buffer.length);\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;
|
|
@@ -13,5 +14,13 @@ export default class YGOProDeck {
|
|
|
13
14
|
toYdkString(): string;
|
|
14
15
|
fromYdkString(str: string): this;
|
|
15
16
|
static fromYdkString(str: string): YGOProDeck;
|
|
17
|
+
fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard?: (code: number, i: number, mainc: number) => boolean): this;
|
|
18
|
+
static fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard?: (code: number) => boolean): YGOProDeck;
|
|
16
19
|
toUpdateDeckPayload(): Uint8Array;
|
|
20
|
+
fromYGOMobileDeckURL(uri: string): YGOProDeck;
|
|
21
|
+
static fromYGOMobileDeckURL(uri: string): YGOProDeck;
|
|
22
|
+
toYGOMobileDeckURL(): string;
|
|
23
|
+
fromYdkeURL(uri: string): YGOProDeck;
|
|
24
|
+
static fromYdkeURL(uri: string): YGOProDeck;
|
|
25
|
+
toYdkeURL(): string;
|
|
17
26
|
}
|
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();
|
|
@@ -6,54 +25,270 @@ function countItems(arr) {
|
|
|
6
25
|
}
|
|
7
26
|
return map;
|
|
8
27
|
}
|
|
9
|
-
var
|
|
28
|
+
var BufferCursor = class {
|
|
10
29
|
buffer;
|
|
11
30
|
pointer = 0;
|
|
31
|
+
constructor(bufOrLength) {
|
|
32
|
+
if (typeof bufOrLength === "number") {
|
|
33
|
+
this.buffer = new Uint8Array(bufOrLength);
|
|
34
|
+
} else {
|
|
35
|
+
this.buffer = bufOrLength;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 安全地将 pointer 向前移动指定字节数。
|
|
40
|
+
* @param bytes 要移动的字节数
|
|
41
|
+
* @returns 原来 pointer 的值
|
|
42
|
+
* @throws RangeError 如果越界
|
|
43
|
+
*/
|
|
44
|
+
increasePointer(bytes) {
|
|
45
|
+
const old = this.pointer;
|
|
46
|
+
const next = old + bytes;
|
|
47
|
+
if (next > this.buffer.length) {
|
|
48
|
+
throw new RangeError(
|
|
49
|
+
`Pointer overflow: tried to move to ${next}, but buffer length is ${this.buffer.length}`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
this.pointer = next;
|
|
53
|
+
return old;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var BufferWriter = class extends BufferCursor {
|
|
12
57
|
constructor(length) {
|
|
13
|
-
|
|
58
|
+
super(length);
|
|
14
59
|
}
|
|
15
60
|
writeUint32LE(value) {
|
|
16
|
-
|
|
17
|
-
this.buffer[
|
|
18
|
-
this.buffer[
|
|
19
|
-
this.buffer[
|
|
61
|
+
const idx = this.increasePointer(4);
|
|
62
|
+
this.buffer[idx] = value & 255;
|
|
63
|
+
this.buffer[idx + 1] = value >>> 8 & 255;
|
|
64
|
+
this.buffer[idx + 2] = value >>> 16 & 255;
|
|
65
|
+
this.buffer[idx + 3] = value >>> 24 & 255;
|
|
20
66
|
return this;
|
|
21
67
|
}
|
|
22
68
|
writeUint32BE(value) {
|
|
23
|
-
|
|
24
|
-
this.buffer[
|
|
25
|
-
this.buffer[
|
|
26
|
-
this.buffer[
|
|
69
|
+
const idx = this.increasePointer(4);
|
|
70
|
+
this.buffer[idx] = value >>> 24 & 255;
|
|
71
|
+
this.buffer[idx + 1] = value >>> 16 & 255;
|
|
72
|
+
this.buffer[idx + 2] = value >>> 8 & 255;
|
|
73
|
+
this.buffer[idx + 3] = value & 255;
|
|
27
74
|
return this;
|
|
28
75
|
}
|
|
29
76
|
writeUint16LE(value) {
|
|
30
|
-
|
|
31
|
-
this.buffer[
|
|
77
|
+
const idx = this.increasePointer(2);
|
|
78
|
+
this.buffer[idx] = value & 255;
|
|
79
|
+
this.buffer[idx + 1] = value >>> 8 & 255;
|
|
32
80
|
return this;
|
|
33
81
|
}
|
|
34
82
|
writeUint16BE(value) {
|
|
35
|
-
|
|
36
|
-
this.buffer[
|
|
83
|
+
const idx = this.increasePointer(2);
|
|
84
|
+
this.buffer[idx] = value >>> 8 & 255;
|
|
85
|
+
this.buffer[idx + 1] = value & 255;
|
|
37
86
|
return this;
|
|
38
87
|
}
|
|
39
88
|
writeUint8(value) {
|
|
40
|
-
|
|
89
|
+
const idx = this.increasePointer(1);
|
|
90
|
+
this.buffer[idx] = value & 255;
|
|
41
91
|
return this;
|
|
42
92
|
}
|
|
43
93
|
writeString(str) {
|
|
44
94
|
for (let i = 0; i < str.length; i++) {
|
|
45
|
-
|
|
95
|
+
const idx = this.increasePointer(1);
|
|
96
|
+
this.buffer[idx] = str.charCodeAt(i);
|
|
46
97
|
}
|
|
47
98
|
return this;
|
|
48
99
|
}
|
|
49
100
|
};
|
|
101
|
+
var BufferReader = class extends BufferCursor {
|
|
102
|
+
constructor(buf) {
|
|
103
|
+
super(buf);
|
|
104
|
+
}
|
|
105
|
+
readUint32LE() {
|
|
106
|
+
const idx = this.increasePointer(4);
|
|
107
|
+
const b = this.buffer;
|
|
108
|
+
return (b[idx] | b[idx + 1] << 8 | b[idx + 2] << 16 | b[idx + 3] << 24) >>> 0;
|
|
109
|
+
}
|
|
110
|
+
readUint32BE() {
|
|
111
|
+
const idx = this.increasePointer(4);
|
|
112
|
+
const b = this.buffer;
|
|
113
|
+
return (b[idx] << 24 | b[idx + 1] << 16 | b[idx + 2] << 8 | b[idx + 3]) >>> 0;
|
|
114
|
+
}
|
|
115
|
+
readUint16LE() {
|
|
116
|
+
const idx = this.increasePointer(2);
|
|
117
|
+
const b = this.buffer;
|
|
118
|
+
return b[idx] | b[idx + 1] << 8;
|
|
119
|
+
}
|
|
120
|
+
readUint16BE() {
|
|
121
|
+
const idx = this.increasePointer(2);
|
|
122
|
+
const b = this.buffer;
|
|
123
|
+
return b[idx] << 8 | b[idx + 1];
|
|
124
|
+
}
|
|
125
|
+
readUint8() {
|
|
126
|
+
const idx = this.increasePointer(1);
|
|
127
|
+
return this.buffer[idx];
|
|
128
|
+
}
|
|
129
|
+
readString(length) {
|
|
130
|
+
let s = "";
|
|
131
|
+
for (let i = 0; i < length; i++) {
|
|
132
|
+
const idx = this.increasePointer(1);
|
|
133
|
+
s += String.fromCharCode(this.buffer[idx]);
|
|
134
|
+
}
|
|
135
|
+
return s;
|
|
136
|
+
}
|
|
137
|
+
readRemaining() {
|
|
138
|
+
const remaining = this.buffer.length - this.pointer;
|
|
139
|
+
const idx = this.increasePointer(remaining);
|
|
140
|
+
return this.buffer.subarray(idx, this.buffer.length);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/ydke.ts
|
|
145
|
+
function base64ToUint32Array(base64) {
|
|
146
|
+
const binary = atob(base64);
|
|
147
|
+
const bytes = new Uint8Array(binary.length);
|
|
148
|
+
for (let i = 0; i < binary.length; i++) {
|
|
149
|
+
bytes[i] = binary.charCodeAt(i);
|
|
150
|
+
}
|
|
151
|
+
const view = new DataView(bytes.buffer);
|
|
152
|
+
const result = [];
|
|
153
|
+
for (let i = 0; i < bytes.length; i += 4) {
|
|
154
|
+
result.push(view.getUint32(i, true));
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
function uint32ArrayToBase64(data) {
|
|
159
|
+
const buffer = new ArrayBuffer(data.length * 4);
|
|
160
|
+
const view = new DataView(buffer);
|
|
161
|
+
data.forEach((val, i) => view.setUint32(i * 4, val, true));
|
|
162
|
+
const bytes = new Uint8Array(buffer);
|
|
163
|
+
let binary = "";
|
|
164
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
165
|
+
binary += String.fromCharCode(bytes[i]);
|
|
166
|
+
}
|
|
167
|
+
return btoa(binary);
|
|
168
|
+
}
|
|
169
|
+
function toYdkeURL(deck) {
|
|
170
|
+
return "ydke://" + uint32ArrayToBase64(deck.main) + "!" + uint32ArrayToBase64(deck.extra) + "!" + uint32ArrayToBase64(deck.side) + "!";
|
|
171
|
+
}
|
|
172
|
+
function fromYdkeURL(ydke) {
|
|
173
|
+
if (!ydke.startsWith("ydke://")) {
|
|
174
|
+
throw new Error("Invalid ydke:// URI");
|
|
175
|
+
}
|
|
176
|
+
const [mainStr, extraStr, sideStr] = ydke.slice(7).split("!");
|
|
177
|
+
if (mainStr === void 0 || extraStr === void 0 || sideStr === void 0) {
|
|
178
|
+
throw new Error("Incomplete ydke:// URI");
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
main: base64ToUint32Array(mainStr),
|
|
182
|
+
extra: base64ToUint32Array(extraStr),
|
|
183
|
+
side: base64ToUint32Array(sideStr)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/ygom.ts
|
|
188
|
+
var QUERY_YGO_TYPE = "ygotype";
|
|
189
|
+
var QUERY_VERSION = "v";
|
|
190
|
+
var ARG_DECK = "deck";
|
|
191
|
+
var QUERY_DECK = "d";
|
|
192
|
+
var QUERY_NAME = "name";
|
|
193
|
+
var URL_SCHEME_HTTP = "http";
|
|
194
|
+
var URL_HOST_DECK = "deck.ourygo.top";
|
|
195
|
+
function toBinary(value, length) {
|
|
196
|
+
return value.toString(2).padStart(length, "0");
|
|
197
|
+
}
|
|
198
|
+
function encodeCards(cards) {
|
|
199
|
+
const bits = [];
|
|
200
|
+
for (let i = 0; i < cards.length; ) {
|
|
201
|
+
const id = cards[i];
|
|
202
|
+
let count = 1;
|
|
203
|
+
while (i + count < cards.length && cards[i + count] === id && count < 3)
|
|
204
|
+
count++;
|
|
205
|
+
const prefix = count === 2 ? "10" : count === 3 ? "11" : "01";
|
|
206
|
+
bits.push(prefix + toBinary(id, 27));
|
|
207
|
+
i += count;
|
|
208
|
+
}
|
|
209
|
+
return bits.join("");
|
|
210
|
+
}
|
|
211
|
+
function countUnique(cards) {
|
|
212
|
+
let num = 0;
|
|
213
|
+
for (let i = 0; i < cards.length; i++) {
|
|
214
|
+
const id = cards[i];
|
|
215
|
+
if (id > 0) {
|
|
216
|
+
num++;
|
|
217
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
218
|
+
i++;
|
|
219
|
+
if (i < cards.length - 1 && cards[i + 1] === id) {
|
|
220
|
+
i++;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return num;
|
|
226
|
+
}
|
|
227
|
+
function toYGOMobileDeckURL(main, extra, side, customParams = {}) {
|
|
228
|
+
const mNum = countUnique(main);
|
|
229
|
+
const eNum = countUnique(extra);
|
|
230
|
+
const sNum = countUnique(side);
|
|
231
|
+
const header = toBinary(mNum, 8) + toBinary(eNum, 4) + toBinary(sNum, 4);
|
|
232
|
+
let bitString = header + encodeCards(main) + encodeCards(extra) + encodeCards(side);
|
|
233
|
+
while (bitString.length % 8 !== 0)
|
|
234
|
+
bitString += "0";
|
|
235
|
+
const bytes = new Uint8Array(bitString.length / 8);
|
|
236
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
237
|
+
bytes[i] = parseInt(bitString.slice(i * 8, i * 8 + 8), 2);
|
|
238
|
+
}
|
|
239
|
+
let encoded = toBase64Url(bytes);
|
|
240
|
+
const searchParams = new URLSearchParams();
|
|
241
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
242
|
+
searchParams.set(key, value);
|
|
243
|
+
}
|
|
244
|
+
searchParams.set(QUERY_YGO_TYPE, ARG_DECK);
|
|
245
|
+
searchParams.set(QUERY_VERSION, "1");
|
|
246
|
+
searchParams.set(QUERY_DECK, encoded);
|
|
247
|
+
return `${URL_SCHEME_HTTP}://${URL_HOST_DECK}?${searchParams.toString()}`;
|
|
248
|
+
}
|
|
249
|
+
function fromYGOMobileDeckURL(uri) {
|
|
250
|
+
const url = new URL(uri);
|
|
251
|
+
if (url.searchParams.get(QUERY_YGO_TYPE) !== ARG_DECK) {
|
|
252
|
+
throw new Error("Not a YGO Mobile deck URI");
|
|
253
|
+
}
|
|
254
|
+
let encoded = url.searchParams.get(QUERY_DECK);
|
|
255
|
+
if (!encoded)
|
|
256
|
+
throw new Error("Missing deck data");
|
|
257
|
+
const bytes = fromBase64Url(encoded);
|
|
258
|
+
const bits = Array.from(bytes).map((b) => b.toString(2).padStart(8, "0")).join("");
|
|
259
|
+
const mNum = parseInt(bits.slice(0, 8), 2);
|
|
260
|
+
const eNum = parseInt(bits.slice(8, 12), 2);
|
|
261
|
+
const sNum = parseInt(bits.slice(12, 16), 2);
|
|
262
|
+
const all = mNum + eNum + sNum;
|
|
263
|
+
const cards = [];
|
|
264
|
+
let pos = 16;
|
|
265
|
+
const res = {
|
|
266
|
+
main: [],
|
|
267
|
+
extra: [],
|
|
268
|
+
side: []
|
|
269
|
+
};
|
|
270
|
+
let i = 0;
|
|
271
|
+
while (pos + 29 <= bits.length && cards.length < all) {
|
|
272
|
+
const countBits = bits.slice(pos, pos + 2);
|
|
273
|
+
const count = countBits === "10" ? 2 : countBits === "11" ? 3 : 1;
|
|
274
|
+
const id = parseInt(bits.slice(pos + 2, pos + 29), 2);
|
|
275
|
+
const field = i < mNum ? "main" : i < mNum + eNum ? "extra" : "side";
|
|
276
|
+
res[field].push(...Array(count).fill(id));
|
|
277
|
+
pos += 29;
|
|
278
|
+
i++;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
...res,
|
|
282
|
+
name: url.searchParams.get(QUERY_NAME)
|
|
283
|
+
};
|
|
284
|
+
}
|
|
50
285
|
|
|
51
286
|
// index.ts
|
|
52
|
-
import { Base64 } from "js-base64";
|
|
53
287
|
var YGOProDeck = class {
|
|
54
288
|
main = [];
|
|
55
289
|
extra = [];
|
|
56
290
|
side = [];
|
|
291
|
+
name;
|
|
57
292
|
bufferLength() {
|
|
58
293
|
const counted = [this.main, this.extra, this.side].map(countItems);
|
|
59
294
|
return counted.reduce((a, b) => a + b.size * 4, 0);
|
|
@@ -76,7 +311,7 @@ var YGOProDeck = class {
|
|
|
76
311
|
return writer.buffer;
|
|
77
312
|
}
|
|
78
313
|
toEncodedString() {
|
|
79
|
-
return
|
|
314
|
+
return toBase64Url(this.toUint8Array());
|
|
80
315
|
}
|
|
81
316
|
toString() {
|
|
82
317
|
return this.toEncodedString();
|
|
@@ -98,7 +333,7 @@ var YGOProDeck = class {
|
|
|
98
333
|
return new YGOProDeck().fromUint8Array(buf);
|
|
99
334
|
}
|
|
100
335
|
fromEncodedString(str) {
|
|
101
|
-
return this.fromUint8Array(
|
|
336
|
+
return this.fromUint8Array(fromBase64Url(str));
|
|
102
337
|
}
|
|
103
338
|
static fromEncodedString(str) {
|
|
104
339
|
return new YGOProDeck().fromEncodedString(str);
|
|
@@ -137,12 +372,69 @@ var YGOProDeck = class {
|
|
|
137
372
|
static fromYdkString(str) {
|
|
138
373
|
return new YGOProDeck().fromYdkString(str);
|
|
139
374
|
}
|
|
375
|
+
fromUpdateDeckPayload(buf, isExtraDeckCard = () => false) {
|
|
376
|
+
const reader = new BufferReader(buf);
|
|
377
|
+
const mainc = reader.readUint32LE();
|
|
378
|
+
const sidec = reader.readUint32LE();
|
|
379
|
+
this.main = [];
|
|
380
|
+
this.extra = [];
|
|
381
|
+
this.side = [];
|
|
382
|
+
for (let i = 0; i < mainc; i++) {
|
|
383
|
+
const id = reader.readUint32LE();
|
|
384
|
+
if (isExtraDeckCard(id, i, mainc)) {
|
|
385
|
+
this.extra.push(id);
|
|
386
|
+
} else {
|
|
387
|
+
this.main.push(id);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
for (let i = 0; i < sidec; i++) {
|
|
391
|
+
const id = reader.readUint32LE();
|
|
392
|
+
this.side.push(id);
|
|
393
|
+
}
|
|
394
|
+
return this;
|
|
395
|
+
}
|
|
396
|
+
static fromUpdateDeckPayload(buf, isExtraDeckCard = () => false) {
|
|
397
|
+
return new YGOProDeck().fromUpdateDeckPayload(buf, isExtraDeckCard);
|
|
398
|
+
}
|
|
140
399
|
toUpdateDeckPayload() {
|
|
141
400
|
const cards = [...this.main, ...this.extra, ...this.side];
|
|
142
401
|
const writer = new BufferWriter(cards.length * 4 + 8).writeUint32LE(this.main.length + this.extra.length).writeUint32LE(this.side.length);
|
|
143
402
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
144
403
|
return writer.buffer;
|
|
145
404
|
}
|
|
405
|
+
fromYGOMobileDeckURL(uri) {
|
|
406
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
407
|
+
this.main = parsed.main;
|
|
408
|
+
this.extra = parsed.extra;
|
|
409
|
+
this.side = parsed.side;
|
|
410
|
+
this.name = parsed.name;
|
|
411
|
+
return this;
|
|
412
|
+
}
|
|
413
|
+
static fromYGOMobileDeckURL(uri) {
|
|
414
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
415
|
+
}
|
|
416
|
+
toYGOMobileDeckURL() {
|
|
417
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
418
|
+
name: this.name
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
fromYdkeURL(uri) {
|
|
422
|
+
const parsed = fromYdkeURL(uri);
|
|
423
|
+
this.main = parsed.main;
|
|
424
|
+
this.extra = parsed.extra;
|
|
425
|
+
this.side = parsed.side;
|
|
426
|
+
return this;
|
|
427
|
+
}
|
|
428
|
+
static fromYdkeURL(uri) {
|
|
429
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
430
|
+
}
|
|
431
|
+
toYdkeURL() {
|
|
432
|
+
return toYdkeURL({
|
|
433
|
+
main: this.main,
|
|
434
|
+
extra: this.extra,
|
|
435
|
+
side: this.side
|
|
436
|
+
});
|
|
437
|
+
}
|
|
146
438
|
};
|
|
147
439
|
export {
|
|
148
440
|
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": "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",
|
|
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}\nabstract class BufferCursor {\n buffer: Uint8Array;\n pointer = 0;\n\n constructor(bufOrLength: Uint8Array | number) {\n if (typeof bufOrLength === 'number') {\n this.buffer = new Uint8Array(bufOrLength);\n } else {\n this.buffer = bufOrLength;\n }\n }\n\n /**\n * \u5B89\u5168\u5730\u5C06 pointer \u5411\u524D\u79FB\u52A8\u6307\u5B9A\u5B57\u8282\u6570\u3002\n * @param bytes \u8981\u79FB\u52A8\u7684\u5B57\u8282\u6570\n * @returns \u539F\u6765 pointer \u7684\u503C\n * @throws RangeError \u5982\u679C\u8D8A\u754C\n */\n protected increasePointer(bytes: number): number {\n const old = this.pointer;\n const next = old + bytes;\n if (next > this.buffer.length) {\n throw new RangeError(\n `Pointer overflow: tried to move to ${next}, but buffer length is ${this.buffer.length}`\n );\n }\n this.pointer = next;\n return old;\n }\n}\n\nexport class BufferWriter extends BufferCursor {\n constructor(length: number) {\n super(length);\n }\n\n writeUint32LE(value: number) {\n const idx = this.increasePointer(4);\n this.buffer[idx ] = value & 0xff;\n this.buffer[idx + 1] = (value >>> 8) & 0xff;\n this.buffer[idx + 2] = (value >>> 16) & 0xff;\n this.buffer[idx + 3] = (value >>> 24) & 0xff;\n return this;\n }\n\n writeUint32BE(value: number) {\n const idx = this.increasePointer(4);\n this.buffer[idx ] = (value >>> 24) & 0xff;\n this.buffer[idx + 1] = (value >>> 16) & 0xff;\n this.buffer[idx + 2] = (value >>> 8) & 0xff;\n this.buffer[idx + 3] = value & 0xff;\n return this;\n }\n\n writeUint16LE(value: number) {\n const idx = this.increasePointer(2);\n this.buffer[idx ] = value & 0xff;\n this.buffer[idx + 1] = (value >>> 8) & 0xff;\n return this;\n }\n\n writeUint16BE(value: number) {\n const idx = this.increasePointer(2);\n this.buffer[idx ] = (value >>> 8) & 0xff;\n this.buffer[idx + 1] = value & 0xff;\n return this;\n }\n\n writeUint8(value: number) {\n const idx = this.increasePointer(1);\n this.buffer[idx] = value & 0xff;\n return this;\n }\n\n writeString(str: string) {\n for (let i = 0; i < str.length; i++) {\n const idx = this.increasePointer(1);\n this.buffer[idx] = str.charCodeAt(i);\n }\n return this;\n }\n}\n\nexport class BufferReader extends BufferCursor {\n constructor(buf: Uint8Array) {\n super(buf);\n }\n\n readUint32LE(): number {\n const idx = this.increasePointer(4);\n const b = this.buffer;\n // >>>0 \u4FDD\u8BC1\u65E0\u7B26\u53F7\u8F93\u51FA\n return (\n (b[idx ] ) |\n (b[idx + 1] << 8) |\n (b[idx + 2] << 16) |\n (b[idx + 3] << 24)\n ) >>> 0;\n }\n\n readUint32BE(): number {\n const idx = this.increasePointer(4);\n const b = this.buffer;\n return (\n (b[idx ] << 24) |\n (b[idx + 1] << 16) |\n (b[idx + 2] << 8) |\n (b[idx + 3] )\n ) >>> 0;\n }\n\n readUint16LE(): number {\n const idx = this.increasePointer(2);\n const b = this.buffer;\n return (b[idx] | (b[idx + 1] << 8));\n }\n\n readUint16BE(): number {\n const idx = this.increasePointer(2);\n const b = this.buffer;\n return ((b[idx] << 8) | b[idx + 1]);\n }\n\n readUint8(): number {\n const idx = this.increasePointer(1);\n return this.buffer[idx];\n }\n\n readString(length: number): string {\n let s = '';\n for (let i = 0; i < length; i++) {\n const idx = this.increasePointer(1);\n s += String.fromCharCode(this.buffer[idx]);\n }\n return s;\n }\n\n readRemaining(): Uint8Array {\n const remaining = this.buffer.length - this.pointer;\n const idx = this.increasePointer(remaining);\n return this.buffer.subarray(idx, this.buffer.length);\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 { BufferReader, 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 fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number, i: number, mainc: number) => boolean = () => false) {\n const reader = new BufferReader(buf);\n const mainc = reader.readUint32LE();\n const sidec = reader.readUint32LE();\n this.main = [];\n this.extra = [];\n this.side = [];\n for(let i = 0; i < mainc; i++) {\n const id = reader.readUint32LE();\n if (isExtraDeckCard(id, i, mainc)) {\n this.extra.push(id);\n } else {\n this.main.push(id);\n }\n }\n for(let i = 0; i < sidec; i++) {\n const id = reader.readUint32LE();\n this.side.push(id);\n }\n return this;\n }\n\n static fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number) => boolean = () => false) { \n return new YGOProDeck().fromUpdateDeckPayload(buf, isExtraDeckCard);\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": ";AAAO,SAAS,YAAY,OAA2B;AACrD,QAAM,SAAS,OAAO,WAAW,cAC7B,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ,IACpC,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC;AAEtC,SAAO,OAAO,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACzE;AAEO,SAAS,cAAc,SAA6B;AACzD,QAAM,SAAS,QAAQ,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC3D,QAAM,SAAS,OAAO,OAAO,OAAO,UAAU,IAAI,OAAO,SAAS,KAAK,GAAG,GAAG;AAE7E,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,WAAW,KAAK,OAAO,KAAK,QAAQ,QAAQ,CAAC;AAAA,EACtD;AAEA,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;;;ACtBO,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;AACA,IAAe,eAAf,MAA4B;AAAA,EAC1B;AAAA,EACA,UAAU;AAAA,EAEV,YAAY,aAAkC;AAC5C,QAAI,OAAO,gBAAgB,UAAU;AACnC,WAAK,SAAS,IAAI,WAAW,WAAW;AAAA,IAC1C,OAAO;AACL,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,gBAAgB,OAAuB;AAC/C,UAAM,MAAM,KAAK;AACjB,UAAM,OAAO,MAAM;AACnB,QAAI,OAAO,KAAK,OAAO,QAAQ;AAC7B,YAAM,IAAI;AAAA,QACR,sCAAsC,8BAA8B,KAAK,OAAO;AAAA,MAClF;AAAA,IACF;AACA,SAAK,UAAU;AACf,WAAO;AAAA,EACT;AACF;AAEO,IAAM,eAAN,cAA2B,aAAa;AAAA,EAC7C,YAAY,QAAgB;AAC1B,UAAM,MAAM;AAAA,EACd;AAAA,EAEA,cAAc,OAAe;AAC3B,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,SAAK,OAAO,GAAO,IAAK,QAAgB;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,IAAM;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,KAAM;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,KAAM;AACxC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,SAAK,OAAO,GAAO,IAAK,UAAU,KAAM;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,KAAM;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,IAAM;AACxC,SAAK,OAAO,MAAM,CAAC,IAAK,QAAgB;AACxC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,SAAK,OAAO,GAAO,IAAK,QAAe;AACvC,SAAK,OAAO,MAAM,CAAC,IAAK,UAAU,IAAK;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,cAAc,OAAe;AAC3B,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,SAAK,OAAO,GAAO,IAAK,UAAU,IAAK;AACvC,SAAK,OAAO,MAAM,CAAC,IAAK,QAAe;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,OAAe;AACxB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,SAAK,OAAO,GAAG,IAAI,QAAQ;AAC3B,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,KAAa;AACvB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,YAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,WAAK,OAAO,GAAG,IAAI,IAAI,WAAW,CAAC;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,eAAN,cAA2B,aAAa;AAAA,EAC7C,YAAY,KAAiB;AAC3B,UAAM,GAAG;AAAA,EACX;AAAA,EAEA,eAAuB;AACrB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,UAAM,IAAI,KAAK;AAEf,YACG,EAAE,GAAO,IACT,EAAE,MAAM,CAAC,KAAM,IACf,EAAE,MAAM,CAAC,KAAK,KACd,EAAE,MAAM,CAAC,KAAK,QACX;AAAA,EACR;AAAA,EAEA,eAAuB;AACrB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,UAAM,IAAI,KAAK;AACf,YACG,EAAE,GAAO,KAAK,KACd,EAAE,MAAM,CAAC,KAAK,KACd,EAAE,MAAM,CAAC,KAAM,IACf,EAAE,MAAM,CAAC,OACN;AAAA,EACR;AAAA,EAEA,eAAuB;AACrB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,UAAM,IAAI,KAAK;AACf,WAAQ,EAAE,GAAG,IAAK,EAAE,MAAM,CAAC,KAAK;AAAA,EAClC;AAAA,EAEA,eAAuB;AACrB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,UAAM,IAAI,KAAK;AACf,WAAS,EAAE,GAAG,KAAK,IAAK,EAAE,MAAM,CAAC;AAAA,EACnC;AAAA,EAEA,YAAoB;AAClB,UAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,WAAO,KAAK,OAAO,GAAG;AAAA,EACxB;AAAA,EAEA,WAAW,QAAwB;AACjC,QAAI,IAAI;AACR,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,YAAM,MAAM,KAAK,gBAAgB,CAAC;AAClC,WAAK,OAAO,aAAa,KAAK,OAAO,GAAG,CAAC;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,gBAA4B;AAC1B,UAAM,YAAY,KAAK,OAAO,SAAS,KAAK;AAC5C,UAAM,MAAM,KAAK,gBAAgB,SAAS;AAC1C,WAAO,KAAK,OAAO,SAAS,KAAK,KAAK,OAAO,MAAM;AAAA,EACrD;AACF;;;AC7IA,SAAS,oBAAoB,QAA0B;AACrD,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,QAAM,OAAO,IAAI,SAAS,MAAM,MAAM;AACtC,QAAM,SAAmB,CAAC;AAC1B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,WAAO,KAAK,KAAK,UAAU,GAAG,IAAI,CAAC;AAAA,EACrC;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,MAAwB;AACnD,QAAM,SAAS,IAAI,YAAY,KAAK,SAAS,CAAC;AAC9C,QAAM,OAAO,IAAI,SAAS,MAAM;AAChC,OAAK,QAAQ,CAAC,KAAK,MAAM,KAAK,UAAU,IAAI,GAAG,KAAK,IAAI,CAAC;AACzD,QAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EACxC;AACA,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,UAAU,MAAyB;AACjD,SACE,YACA,oBAAoB,KAAK,IAAI,IAC7B,MACA,oBAAoB,KAAK,KAAK,IAC9B,MACA,oBAAoB,KAAK,IAAI,IAC7B;AAEJ;AAEO,SAAS,YAAY,MAAyB;AACnD,MAAI,CAAC,KAAK,WAAW,SAAS,GAAG;AAC/B,UAAM,IAAI,MAAM,qBAAqB;AAAA,EACvC;AACA,QAAM,CAAC,SAAS,UAAU,OAAO,IAAI,KAAK,MAAM,CAAC,EAAE,MAAM,GAAG;AAC5D,MAAI,YAAY,UAAa,aAAa,UAAa,YAAY,QAAW;AAC5E,UAAM,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AACA,SAAO;AAAA,IACL,MAAM,oBAAoB,OAAO;AAAA,IACjC,OAAO,oBAAoB,QAAQ;AAAA,IACnC,MAAM,oBAAoB,OAAO;AAAA,EACnC;AACF;;;ACxDA,IAAM,iBAAiB;AACvB,IAAM,gBAAgB;AACtB,IAAM,WAAW;AACjB,IAAM,aAAa;AACnB,IAAM,aAAa;AAEnB,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AAGtB,SAAS,SAAS,OAAe,QAAwB;AACvD,SAAO,MAAM,SAAS,CAAC,EAAE,SAAS,QAAQ,GAAG;AAC/C;AAEA,SAAS,YAAY,OAAyB;AAC5C,QAAM,OAAiB,CAAC;AACxB,WAAS,IAAI,GAAG,IAAI,MAAM,UAAU;AAClC,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,QAAQ;AACZ,WAAO,IAAI,QAAQ,MAAM,UAAU,MAAM,IAAI,KAAK,MAAM,MAAM,QAAQ;AAAG;AACzE,UAAM,SAAS,UAAU,IAAI,OAAO,UAAU,IAAI,OAAO;AACzD,SAAK,KAAK,SAAS,SAAS,IAAI,EAAE,CAAC;AACnC,SAAK;AAAA,EACP;AACA,SAAO,KAAK,KAAK,EAAE;AACrB;AAEA,SAAS,YAAY,OAAyB;AAC5C,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,KAAK,MAAM,CAAC;AAClB,QAAI,KAAK,GAAG;AACV;AACA,UAAI,IAAI,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI;AAC/C;AACA,YAAI,IAAI,MAAM,SAAS,KAAK,MAAM,IAAI,CAAC,MAAM,IAAI;AAC/C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAIO,SAAS,mBACd,MACA,OACA,MACA,eAAuC,CAAC,GAChC;AACR,QAAM,OAAO,YAAY,IAAI;AAC7B,QAAM,OAAO,YAAY,KAAK;AAC9B,QAAM,OAAO,YAAY,IAAI;AAE7B,QAAM,SAAS,SAAS,MAAM,CAAC,IAAI,SAAS,MAAM,CAAC,IAAI,SAAS,MAAM,CAAC;AACvE,MAAI,YAAY,SAAS,YAAY,IAAI,IAAI,YAAY,KAAK,IAAI,YAAY,IAAI;AAClF,SAAO,UAAU,SAAS,MAAM;AAAG,iBAAa;AAEhD,QAAM,QAAQ,IAAI,WAAW,UAAU,SAAS,CAAC;AACjD,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,CAAC,IAAI,SAAS,UAAU,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;AAAA,EAC1D;AAEA,MAAI,UAAU,YAAY,KAAK;AAE/B,QAAM,eAAe,IAAI,gBAAgB;AACzC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,iBAAa,IAAI,KAAK,KAAK;AAAA,EAC7B;AACA,eAAa,IAAI,gBAAgB,QAAQ;AACzC,eAAa,IAAI,eAAe,GAAG;AACnC,eAAa,IAAI,YAAY,OAAO;AAEpC,SAAO,GAAG,qBAAqB,iBAAiB,aAAa,SAAS;AACxE;AAGO,SAAS,qBAAqB,KAKnC;AACA,QAAM,MAAM,IAAI,IAAI,GAAG;AACvB,MAAI,IAAI,aAAa,IAAI,cAAc,MAAM,UAAU;AACrD,UAAM,IAAI,MAAM,2BAA2B;AAAA,EAC7C;AAEA,MAAI,UAAU,IAAI,aAAa,IAAI,UAAU;AAC7C,MAAI,CAAC;AAAS,UAAM,IAAI,MAAM,mBAAmB;AAEjD,QAAM,QAAQ,cAAc,OAAO;AAEnC,QAAM,OAAO,MAAM,KAAK,KAAK,EAC1B,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,EACzC,KAAK,EAAE;AAEV,QAAM,OAAO,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,CAAC;AACzC,QAAM,OAAO,SAAS,KAAK,MAAM,GAAG,EAAE,GAAG,CAAC;AAC1C,QAAM,OAAO,SAAS,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC;AAE3C,QAAM,MAAM,OAAO,OAAO;AAC1B,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAM;AAEV,QAAM,MAAM;AAAA,IACV,MAAM,CAAC;AAAA,IACP,OAAO,CAAC;AAAA,IACR,MAAM,CAAC;AAAA,EACT;AAEA,MAAI,IAAI;AAER,SAAO,MAAM,MAAM,KAAK,UAAU,MAAM,SAAS,KAAK;AACpD,UAAM,YAAY,KAAK,MAAM,KAAK,MAAM,CAAC;AACzC,UAAM,QAAQ,cAAc,OAAO,IAAI,cAAc,OAAO,IAAI;AAChE,UAAM,KAAK,SAAS,KAAK,MAAM,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC;AACpD,UAAM,QAAQ,IAAI,OAAO,SAAS,IAAI,OAAO,OAAO,UAAU;AAC9D,QAAI,KAAK,EAAE,KAAK,GAAG,MAAM,KAAK,EAAE,KAAK,EAAE,CAAC;AACxC,WAAO;AACP;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,IAAI,aAAa,IAAI,UAAU;AAAA,EACvC;AACF;;;AC/HA,IAAqB,aAArB,MAAgC;AAAA,EAC9B,OAAiB,CAAC;AAAA,EAClB,QAAkB,CAAC;AAAA,EACnB,OAAiB,CAAC;AAAA,EAClB;AAAA,EAEA,eAAe;AACb,UAAM,UAAU,CAAC,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE,IAAI,UAAU;AACjE,WAAO,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,GAAG,CAAC;AAAA,EACnD;AAAA,EAEA,eAAe;AACb,UAAM,UAAU,CAAC,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE,IAAI,UAAU;AACjE,UAAM,SAAS,IAAI;AAAA,MACjB,QAAQ,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,GAAG,CAAC;AAAA,IAC5C;AACA,UAAM,aAAa,CAAC,UAA+B,SAAiB;AAElE,iBAAW,CAAC,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;AAC5C,YAAI,QAAQ,GAAG;AACb,gBAAM,IAAI,MAAM,mBAAmB,IAAI;AAAA,QACzC;AACA,cAAM,QAAS,KAAK,YAAc,QAAQ,KAAQ,QAAQ,KAAM;AAChE,eAAO,cAAc,KAAK;AAAA,MAC5B;AAAA,IACF;AACA,YAAQ,QAAQ,UAAU;AAC1B,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,kBAAkB;AAChB,WAAO,YAAY,KAAK,aAAa,CAAC;AAAA,EACxC;AAAA,EAEA,WAAW;AACT,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA,EAEA,eAAe,KAAiB;AAC9B,aAAS,IAAI,GAAG,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG;AAC1C,YAAM,QACJ,IAAI,CAAC,IAAK,IAAI,IAAI,CAAC,KAAK,IAAM,IAAI,IAAI,CAAC,KAAK,KAAO,IAAI,IAAI,CAAC,KAAK;AACnE,YAAM,KAAK,QAAQ;AACnB,YAAM,OAAQ,UAAU,KAAM;AAC9B,YAAM,SAAU,UAAU,KAAM,KAAO;AACvC,YAAM,QAAQ,CAAC,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,EAAE,IAAI;AACrD,eAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,cAAM,KAAK,EAAE;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,eAAe,KAAiB;AACrC,WAAO,IAAI,WAAW,EAAE,eAAe,GAAG;AAAA,EAC5C;AAAA,EAEA,kBAAkB,KAAa;AAC7B,WAAO,KAAK,eAAe,cAAc,GAAG,CAAC;AAAA,EAC/C;AAAA,EAEA,OAAO,kBAAkB,KAAa;AACpC,WAAO,IAAI,WAAW,EAAE,kBAAkB,GAAG;AAAA,EAC/C;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;AAAA,MACtC;AAAA,MACA,GAAG,KAAK,MAAM,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;AAAA,MACvC;AAAA,MACA,GAAG,KAAK,KAAK,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;AAAA,IACxC,EAAE,KAAK,IAAI;AAAA,EACb;AAAA,EAEA,cAAc,KAAa;AACzB,UAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,QAAI,UAAU,KAAK;AACnB,eAAW,SAAS,OAAO;AACzB,YAAM,OAAO,MAAM,KAAK;AACxB,UAAI,SAAS,SAAS;AACpB,kBAAU,KAAK;AAAA,MACjB;AACA,UAAI,SAAS,UAAU;AACrB,kBAAU,KAAK;AAAA,MACjB;AACA,UAAI,SAAS,SAAS;AACpB,kBAAU,KAAK;AAAA,MACjB;AACA,UAAI,KAAK,MAAM,OAAO,GAAG;AACvB,gBAAQ,KAAK,SAAS,MAAM,EAAE,CAAC;AAAA,MACjC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,cAAc,KAAa;AAChC,WAAO,IAAI,WAAW,EAAE,cAAc,GAAG;AAAA,EAC3C;AAAA,EAEA,sBAAsB,KAAiB,kBAAuE,MAAM,OAAO;AACzH,UAAM,SAAS,IAAI,aAAa,GAAG;AACnC,UAAM,QAAQ,OAAO,aAAa;AAClC,UAAM,QAAQ,OAAO,aAAa;AAClC,SAAK,OAAO,CAAC;AACb,SAAK,QAAQ,CAAC;AACd,SAAK,OAAO,CAAC;AACb,aAAQ,IAAI,GAAG,IAAI,OAAO,KAAK;AAC7B,YAAM,KAAK,OAAO,aAAa;AAC/B,UAAI,gBAAgB,IAAI,GAAG,KAAK,GAAG;AACjC,aAAK,MAAM,KAAK,EAAE;AAAA,MACpB,OAAO;AACL,aAAK,KAAK,KAAK,EAAE;AAAA,MACnB;AAAA,IACF;AACA,aAAQ,IAAI,GAAG,IAAI,OAAO,KAAK;AAC7B,YAAM,KAAK,OAAO,aAAa;AAC/B,WAAK,KAAK,KAAK,EAAE;AAAA,IACnB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,sBAAsB,KAAiB,kBAA6C,MAAM,OAAO;AACtG,WAAO,IAAI,WAAW,EAAE,sBAAsB,KAAK,eAAe;AAAA,EACpE;AAAA,EAEA,sBAAsB;AACpB,UAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,GAAG,KAAK,OAAO,GAAG,KAAK,IAAI;AACxD,UAAM,SAAS,IAAI,aAAa,MAAM,SAAS,IAAI,CAAC,EACjD,cAAc,KAAK,KAAK,SAAS,KAAK,MAAM,MAAM,EAClD,cAAc,KAAK,KAAK,MAAM;AACjC,UAAM,QAAQ,CAAC,OAAO,OAAO,cAAc,EAAE,CAAC;AAC9C,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,qBAAqB,KAAyB;AAC5C,UAAM,SAAS,qBAAqB,GAAG;AACvC,SAAK,OAAO,OAAO;AACnB,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,OAAO;AACnB,SAAK,OAAO,OAAO;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,qBAAqB,KAAyB;AACnD,WAAO,IAAI,WAAW,EAAE,qBAAqB,GAAG;AAAA,EAClD;AAAA,EAEA,qBAA6B;AAC3B,WAAO,mBAAmB,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,KAAK,QAAQ;AAAA,MACvE,MAAM,KAAK;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,YAAY,KAAyB;AACnC,UAAM,SAAS,YAAY,GAAG;AAC9B,SAAK,OAAO,OAAO;AACnB,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,OAAO;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,YAAY,KAAyB;AAC1C,WAAO,IAAI,WAAW,EAAE,YAAY,GAAG;AAAA,EACzC;AAAA,EAEA,YAAoB;AAClB,WAAO,UAAU;AAAA,MACf,MAAM,KAAK;AAAA,MACX,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,IACb,CAAC;AAAA,EACH;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
export declare function countItems<T>(arr: T[]): Map<T, number>;
|
|
2
|
-
|
|
2
|
+
declare abstract class BufferCursor {
|
|
3
3
|
buffer: Uint8Array;
|
|
4
4
|
pointer: number;
|
|
5
|
+
constructor(bufOrLength: Uint8Array | number);
|
|
6
|
+
/**
|
|
7
|
+
* 安全地将 pointer 向前移动指定字节数。
|
|
8
|
+
* @param bytes 要移动的字节数
|
|
9
|
+
* @returns 原来 pointer 的值
|
|
10
|
+
* @throws RangeError 如果越界
|
|
11
|
+
*/
|
|
12
|
+
protected increasePointer(bytes: number): number;
|
|
13
|
+
}
|
|
14
|
+
export declare class BufferWriter extends BufferCursor {
|
|
5
15
|
constructor(length: number);
|
|
6
16
|
writeUint32LE(value: number): this;
|
|
7
17
|
writeUint32BE(value: number): this;
|
|
@@ -10,3 +20,14 @@ export declare class BufferWriter {
|
|
|
10
20
|
writeUint8(value: number): this;
|
|
11
21
|
writeString(str: string): this;
|
|
12
22
|
}
|
|
23
|
+
export declare class BufferReader extends BufferCursor {
|
|
24
|
+
constructor(buf: Uint8Array);
|
|
25
|
+
readUint32LE(): number;
|
|
26
|
+
readUint32BE(): number;
|
|
27
|
+
readUint16LE(): number;
|
|
28
|
+
readUint16BE(): number;
|
|
29
|
+
readUint8(): number;
|
|
30
|
+
readString(length: number): string;
|
|
31
|
+
readRemaining(): Uint8Array;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -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 {
|
|
2
|
-
import {
|
|
1
|
+
import { fromBase64Url, toBase64Url } from './src/base64';
|
|
2
|
+
import { BufferReader, BufferWriter, countItems } from './src/utils';
|
|
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) {
|
|
@@ -102,6 +105,32 @@ export default class YGOProDeck {
|
|
|
102
105
|
return new YGOProDeck().fromYdkString(str);
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number, i: number, mainc: number) => boolean = () => false) {
|
|
109
|
+
const reader = new BufferReader(buf);
|
|
110
|
+
const mainc = reader.readUint32LE();
|
|
111
|
+
const sidec = reader.readUint32LE();
|
|
112
|
+
this.main = [];
|
|
113
|
+
this.extra = [];
|
|
114
|
+
this.side = [];
|
|
115
|
+
for(let i = 0; i < mainc; i++) {
|
|
116
|
+
const id = reader.readUint32LE();
|
|
117
|
+
if (isExtraDeckCard(id, i, mainc)) {
|
|
118
|
+
this.extra.push(id);
|
|
119
|
+
} else {
|
|
120
|
+
this.main.push(id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for(let i = 0; i < sidec; i++) {
|
|
124
|
+
const id = reader.readUint32LE();
|
|
125
|
+
this.side.push(id);
|
|
126
|
+
}
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static fromUpdateDeckPayload(buf: Uint8Array, isExtraDeckCard: (code: number) => boolean = () => false) {
|
|
131
|
+
return new YGOProDeck().fromUpdateDeckPayload(buf, isExtraDeckCard);
|
|
132
|
+
}
|
|
133
|
+
|
|
105
134
|
toUpdateDeckPayload() {
|
|
106
135
|
const cards = [...this.main, ...this.extra, ...this.side];
|
|
107
136
|
const writer = new BufferWriter(cards.length * 4 + 8)
|
|
@@ -110,4 +139,43 @@ export default class YGOProDeck {
|
|
|
110
139
|
cards.forEach((id) => writer.writeUint32LE(id));
|
|
111
140
|
return writer.buffer;
|
|
112
141
|
}
|
|
142
|
+
|
|
143
|
+
fromYGOMobileDeckURL(uri: string): YGOProDeck {
|
|
144
|
+
const parsed = fromYGOMobileDeckURL(uri);
|
|
145
|
+
this.main = parsed.main;
|
|
146
|
+
this.extra = parsed.extra;
|
|
147
|
+
this.side = parsed.side;
|
|
148
|
+
this.name = parsed.name;
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static fromYGOMobileDeckURL(uri: string): YGOProDeck {
|
|
153
|
+
return new YGOProDeck().fromYGOMobileDeckURL(uri);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
toYGOMobileDeckURL(): string {
|
|
157
|
+
return toYGOMobileDeckURL(this.main, this.extra, this.side, this.name && {
|
|
158
|
+
name: this.name,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fromYdkeURL(uri: string): YGOProDeck {
|
|
163
|
+
const parsed = fromYdkeURL(uri);
|
|
164
|
+
this.main = parsed.main;
|
|
165
|
+
this.extra = parsed.extra;
|
|
166
|
+
this.side = parsed.side;
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static fromYdkeURL(uri: string): YGOProDeck {
|
|
171
|
+
return new YGOProDeck().fromYdkeURL(uri);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
toYdkeURL(): string {
|
|
175
|
+
return toYdkeURL({
|
|
176
|
+
main: this.main,
|
|
177
|
+
extra: this.extra,
|
|
178
|
+
side: this.side,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
113
181
|
}
|
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.7",
|
|
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
|
}
|