koikatu.js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +138 -0
- package/README.md +141 -0
- package/dist/index.cjs +541 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +498 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
type Input = ArrayBuffer | Uint8Array;
|
|
2
|
+
type ParseOptions = {
|
|
3
|
+
containsPng?: boolean;
|
|
4
|
+
strict?: boolean;
|
|
5
|
+
decodeBlocks?: boolean;
|
|
6
|
+
};
|
|
7
|
+
type CardHeader = {
|
|
8
|
+
productNo: number;
|
|
9
|
+
header: string;
|
|
10
|
+
version: string;
|
|
11
|
+
faceImage?: Uint8Array;
|
|
12
|
+
};
|
|
13
|
+
type BlockInfo = {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
pos: number;
|
|
17
|
+
size: number;
|
|
18
|
+
};
|
|
19
|
+
type ParseError = {
|
|
20
|
+
code: string;
|
|
21
|
+
message: string;
|
|
22
|
+
at?: string;
|
|
23
|
+
};
|
|
24
|
+
type Card = {
|
|
25
|
+
header: CardHeader;
|
|
26
|
+
blocks: Record<string, any>;
|
|
27
|
+
blockIndex: BlockInfo[];
|
|
28
|
+
rawBlockBytes?: Record<string, Uint8Array>;
|
|
29
|
+
errors?: ParseError[];
|
|
30
|
+
unsupportedHeader?: boolean;
|
|
31
|
+
};
|
|
32
|
+
type CardSummary = {
|
|
33
|
+
header: CardHeader;
|
|
34
|
+
product: string;
|
|
35
|
+
name?: string;
|
|
36
|
+
birthday?: {
|
|
37
|
+
month?: number;
|
|
38
|
+
day?: number;
|
|
39
|
+
};
|
|
40
|
+
sex?: number;
|
|
41
|
+
hasKKEx?: boolean;
|
|
42
|
+
blocks: string[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
declare const ERR_NO_PNG = "ERR_NO_PNG";
|
|
46
|
+
declare const ERR_NO_CARD_PAYLOAD = "ERR_NO_CARD_PAYLOAD";
|
|
47
|
+
declare const ERR_UNSUPPORTED_HEADER = "ERR_UNSUPPORTED_HEADER";
|
|
48
|
+
declare const ERR_PARSE_BLOCK = "ERR_PARSE_BLOCK";
|
|
49
|
+
declare class KoikatuError extends Error {
|
|
50
|
+
code: string;
|
|
51
|
+
at?: string;
|
|
52
|
+
constructor(code: string, message: string, at?: string);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare function decodeMsgpack(data: Uint8Array): any;
|
|
56
|
+
|
|
57
|
+
declare class BinaryReader {
|
|
58
|
+
private view;
|
|
59
|
+
private data;
|
|
60
|
+
private _offset;
|
|
61
|
+
private strict;
|
|
62
|
+
constructor(input: ArrayBuffer | Uint8Array, strict?: boolean);
|
|
63
|
+
get offset(): number;
|
|
64
|
+
set offset(value: number);
|
|
65
|
+
get remaining(): number;
|
|
66
|
+
private check;
|
|
67
|
+
readInt8(): number | undefined;
|
|
68
|
+
readUint8(): number | undefined;
|
|
69
|
+
readInt32LE(): number | undefined;
|
|
70
|
+
readUint32BE(): number | undefined;
|
|
71
|
+
readInt64LE(): bigint | undefined;
|
|
72
|
+
readBytes(len: number): Uint8Array | undefined;
|
|
73
|
+
readLengthPrefixed(sizeType: 'b' | 'i' | 'q'): Uint8Array | undefined;
|
|
74
|
+
readLengthPrefixedString(sizeType: 'b' | 'i'): string | undefined;
|
|
75
|
+
subarray(offset: number, length: number): Uint8Array;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare function scanPngIend$1(data: Uint8Array): number;
|
|
79
|
+
|
|
80
|
+
declare function decodeCustomBlock(data: Uint8Array): Record<string, any>;
|
|
81
|
+
declare function decodeCoordinateBlock(data: Uint8Array, version?: string): any;
|
|
82
|
+
declare function decodeParameterBlock(data: Uint8Array): Record<string, any>;
|
|
83
|
+
declare function decodeKKExBlock(data: Uint8Array): Record<string, any>;
|
|
84
|
+
|
|
85
|
+
declare function scanPngIend(input: Input): number;
|
|
86
|
+
declare function parseHeader(input: Input, options?: {
|
|
87
|
+
containsPng?: boolean;
|
|
88
|
+
strict?: boolean;
|
|
89
|
+
}): CardHeader;
|
|
90
|
+
declare function parseCard(input: Input, options?: ParseOptions): Card;
|
|
91
|
+
declare function parseCardSummary(input: Input, options?: ParseOptions): CardSummary;
|
|
92
|
+
declare function isCard(input: Input): boolean;
|
|
93
|
+
|
|
94
|
+
export { BinaryReader, type BlockInfo, type Card, type CardHeader, type CardSummary, ERR_NO_CARD_PAYLOAD, ERR_NO_PNG, ERR_PARSE_BLOCK, ERR_UNSUPPORTED_HEADER, type Input, KoikatuError, type ParseError, type ParseOptions, decodeCoordinateBlock, decodeCustomBlock, decodeKKExBlock, decodeMsgpack, decodeParameterBlock, isCard, parseCard, parseCardSummary, parseHeader, scanPngIend, scanPngIend$1 as scanPngIendRaw };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ERR_NO_PNG = "ERR_NO_PNG";
|
|
3
|
+
var ERR_NO_CARD_PAYLOAD = "ERR_NO_CARD_PAYLOAD";
|
|
4
|
+
var ERR_UNSUPPORTED_HEADER = "ERR_UNSUPPORTED_HEADER";
|
|
5
|
+
var ERR_PARSE_BLOCK = "ERR_PARSE_BLOCK";
|
|
6
|
+
var KoikatuError = class extends Error {
|
|
7
|
+
constructor(code, message, at) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "KoikatuError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.at = at;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/parse/msgpack.ts
|
|
16
|
+
import { decode } from "@msgpack/msgpack";
|
|
17
|
+
function decodeMsgpack(data) {
|
|
18
|
+
return decode(data, { useBigInt64: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/parse/reader.ts
|
|
22
|
+
var textDecoder = new TextDecoder("utf-8");
|
|
23
|
+
var BinaryReader = class {
|
|
24
|
+
constructor(input, strict = false) {
|
|
25
|
+
if (input instanceof Uint8Array) {
|
|
26
|
+
this.data = input;
|
|
27
|
+
this.view = new DataView(
|
|
28
|
+
input.buffer,
|
|
29
|
+
input.byteOffset,
|
|
30
|
+
input.byteLength
|
|
31
|
+
);
|
|
32
|
+
} else {
|
|
33
|
+
this.data = new Uint8Array(input);
|
|
34
|
+
this.view = new DataView(input);
|
|
35
|
+
}
|
|
36
|
+
this._offset = 0;
|
|
37
|
+
this.strict = strict;
|
|
38
|
+
}
|
|
39
|
+
get offset() {
|
|
40
|
+
return this._offset;
|
|
41
|
+
}
|
|
42
|
+
set offset(value) {
|
|
43
|
+
this._offset = value;
|
|
44
|
+
}
|
|
45
|
+
get remaining() {
|
|
46
|
+
return this.data.byteLength - this._offset;
|
|
47
|
+
}
|
|
48
|
+
check(bytes) {
|
|
49
|
+
if (this._offset + bytes > this.data.byteLength) {
|
|
50
|
+
if (this.strict) {
|
|
51
|
+
throw new RangeError(
|
|
52
|
+
`Read out of bounds: offset ${this._offset} + ${bytes} > ${this.data.byteLength}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
readInt8() {
|
|
60
|
+
if (!this.check(1)) return void 0;
|
|
61
|
+
const val = this.view.getInt8(this._offset);
|
|
62
|
+
this._offset += 1;
|
|
63
|
+
return val;
|
|
64
|
+
}
|
|
65
|
+
readUint8() {
|
|
66
|
+
if (!this.check(1)) return void 0;
|
|
67
|
+
const val = this.view.getUint8(this._offset);
|
|
68
|
+
this._offset += 1;
|
|
69
|
+
return val;
|
|
70
|
+
}
|
|
71
|
+
readInt32LE() {
|
|
72
|
+
if (!this.check(4)) return void 0;
|
|
73
|
+
const val = this.view.getInt32(this._offset, true);
|
|
74
|
+
this._offset += 4;
|
|
75
|
+
return val;
|
|
76
|
+
}
|
|
77
|
+
readUint32BE() {
|
|
78
|
+
if (!this.check(4)) return void 0;
|
|
79
|
+
const val = this.view.getUint32(this._offset, false);
|
|
80
|
+
this._offset += 4;
|
|
81
|
+
return val;
|
|
82
|
+
}
|
|
83
|
+
readInt64LE() {
|
|
84
|
+
if (!this.check(8)) return void 0;
|
|
85
|
+
const val = this.view.getBigInt64(this._offset, true);
|
|
86
|
+
this._offset += 8;
|
|
87
|
+
return val;
|
|
88
|
+
}
|
|
89
|
+
readBytes(len) {
|
|
90
|
+
if (len < 0) return void 0;
|
|
91
|
+
if (!this.check(len)) return void 0;
|
|
92
|
+
const slice = this.data.slice(this._offset, this._offset + len);
|
|
93
|
+
this._offset += len;
|
|
94
|
+
return slice;
|
|
95
|
+
}
|
|
96
|
+
readLengthPrefixed(sizeType) {
|
|
97
|
+
let len;
|
|
98
|
+
switch (sizeType) {
|
|
99
|
+
case "b":
|
|
100
|
+
len = this.readInt8();
|
|
101
|
+
break;
|
|
102
|
+
case "i":
|
|
103
|
+
len = this.readInt32LE();
|
|
104
|
+
break;
|
|
105
|
+
case "q":
|
|
106
|
+
len = this.readInt64LE();
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
if (len === void 0) return void 0;
|
|
110
|
+
return this.readBytes(Number(len));
|
|
111
|
+
}
|
|
112
|
+
readLengthPrefixedString(sizeType) {
|
|
113
|
+
const bytes = this.readLengthPrefixed(sizeType);
|
|
114
|
+
if (bytes === void 0) return void 0;
|
|
115
|
+
return textDecoder.decode(bytes);
|
|
116
|
+
}
|
|
117
|
+
subarray(offset, length) {
|
|
118
|
+
return this.data.slice(offset, offset + length);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/schema/blocks.ts
|
|
123
|
+
function decodeCustomBlock(data) {
|
|
124
|
+
const reader = new BinaryReader(data);
|
|
125
|
+
const result = {};
|
|
126
|
+
const faceBytes = reader.readLengthPrefixed("i");
|
|
127
|
+
if (faceBytes) {
|
|
128
|
+
result.face = decodeMsgpack(faceBytes);
|
|
129
|
+
}
|
|
130
|
+
const bodyBytes = reader.readLengthPrefixed("i");
|
|
131
|
+
if (bodyBytes) {
|
|
132
|
+
result.body = decodeMsgpack(bodyBytes);
|
|
133
|
+
}
|
|
134
|
+
const hairBytes = reader.readLengthPrefixed("i");
|
|
135
|
+
if (hairBytes) {
|
|
136
|
+
result.hair = decodeMsgpack(hairBytes);
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
function decodeCoordinateBlock(data, version) {
|
|
141
|
+
if (version === "0.0.1") {
|
|
142
|
+
const reader = new BinaryReader(data);
|
|
143
|
+
const coord = {};
|
|
144
|
+
const clothesBytes = reader.readLengthPrefixed("i");
|
|
145
|
+
if (clothesBytes) coord.clothes = decodeMsgpack(clothesBytes);
|
|
146
|
+
const accessoryBytes = reader.readLengthPrefixed("i");
|
|
147
|
+
if (accessoryBytes) coord.accessory = decodeMsgpack(accessoryBytes);
|
|
148
|
+
return coord;
|
|
149
|
+
}
|
|
150
|
+
const outerList = decodeMsgpack(data);
|
|
151
|
+
if (!Array.isArray(outerList)) return [];
|
|
152
|
+
const coords = [];
|
|
153
|
+
for (const entry of outerList) {
|
|
154
|
+
const entryBytes = entry instanceof Uint8Array ? entry : new Uint8Array(entry);
|
|
155
|
+
const reader = new BinaryReader(entryBytes);
|
|
156
|
+
const coord = {};
|
|
157
|
+
const clothesBytes = reader.readLengthPrefixed("i");
|
|
158
|
+
if (clothesBytes) coord.clothes = decodeMsgpack(clothesBytes);
|
|
159
|
+
const accessoryBytes = reader.readLengthPrefixed("i");
|
|
160
|
+
if (accessoryBytes) coord.accessory = decodeMsgpack(accessoryBytes);
|
|
161
|
+
const enableMakeup = reader.readUint8();
|
|
162
|
+
coord.enableMakeup = enableMakeup !== void 0 ? enableMakeup !== 0 : void 0;
|
|
163
|
+
const makeupBytes = reader.readLengthPrefixed("i");
|
|
164
|
+
if (makeupBytes) coord.makeup = decodeMsgpack(makeupBytes);
|
|
165
|
+
coords.push(coord);
|
|
166
|
+
}
|
|
167
|
+
return coords;
|
|
168
|
+
}
|
|
169
|
+
function decodeParameterBlock(data) {
|
|
170
|
+
return decodeMsgpack(data);
|
|
171
|
+
}
|
|
172
|
+
function decodeKKExBlock(data) {
|
|
173
|
+
const decoded = decodeMsgpack(data);
|
|
174
|
+
return expandNestedMsgpack(decoded);
|
|
175
|
+
}
|
|
176
|
+
function expandNestedMsgpack(obj) {
|
|
177
|
+
if (obj instanceof Uint8Array) {
|
|
178
|
+
try {
|
|
179
|
+
const inner = decodeMsgpack(obj);
|
|
180
|
+
return expandNestedMsgpack(inner);
|
|
181
|
+
} catch {
|
|
182
|
+
return obj;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (obj instanceof Map) {
|
|
186
|
+
const result = {};
|
|
187
|
+
for (const [key, value] of obj) {
|
|
188
|
+
result[String(key)] = expandNestedMsgpack(value);
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
if (Array.isArray(obj)) {
|
|
193
|
+
return obj.map(expandNestedMsgpack);
|
|
194
|
+
}
|
|
195
|
+
if (obj !== null && typeof obj === "object" && !(obj instanceof Uint8Array)) {
|
|
196
|
+
const result = {};
|
|
197
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
198
|
+
result[key] = expandNestedMsgpack(value);
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
return obj;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/parse/blocks.ts
|
|
206
|
+
function parseBlockIndex(reader) {
|
|
207
|
+
const indexData = reader.readLengthPrefixed("i");
|
|
208
|
+
if (!indexData) {
|
|
209
|
+
throw new KoikatuError(
|
|
210
|
+
ERR_NO_CARD_PAYLOAD,
|
|
211
|
+
"Failed to read block index data"
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const decoded = decodeMsgpack(indexData);
|
|
215
|
+
if (!decoded?.lstInfo || !Array.isArray(decoded.lstInfo)) {
|
|
216
|
+
throw new KoikatuError(
|
|
217
|
+
ERR_NO_CARD_PAYLOAD,
|
|
218
|
+
"Block index missing lstInfo array"
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const blockIndex = decoded.lstInfo.map((entry) => ({
|
|
222
|
+
name: String(entry.name ?? ""),
|
|
223
|
+
version: String(entry.version ?? ""),
|
|
224
|
+
pos: Number(entry.pos ?? 0),
|
|
225
|
+
size: Number(entry.size ?? 0)
|
|
226
|
+
}));
|
|
227
|
+
const rawBytes = reader.readLengthPrefixed("q");
|
|
228
|
+
if (!rawBytes) {
|
|
229
|
+
throw new KoikatuError(
|
|
230
|
+
ERR_NO_CARD_PAYLOAD,
|
|
231
|
+
"Failed to read raw block bytes"
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return { blockIndex, rawBytes };
|
|
235
|
+
}
|
|
236
|
+
function parseBlocks(blockIndex, rawBytes, options) {
|
|
237
|
+
const decodeBlocks = options?.decodeBlocks ?? true;
|
|
238
|
+
const strict = options?.strict ?? false;
|
|
239
|
+
const blocks = {};
|
|
240
|
+
const rawBlockBytes = {};
|
|
241
|
+
const errors = [];
|
|
242
|
+
for (const info of blockIndex) {
|
|
243
|
+
const end = info.pos + info.size;
|
|
244
|
+
if (end > rawBytes.length) {
|
|
245
|
+
const err = {
|
|
246
|
+
code: ERR_PARSE_BLOCK,
|
|
247
|
+
message: `Block "${info.name}" exceeds raw data bounds (pos=${info.pos}, size=${info.size}, total=${rawBytes.length})`,
|
|
248
|
+
at: info.name
|
|
249
|
+
};
|
|
250
|
+
if (strict) throw new KoikatuError(err.code, err.message, err.at);
|
|
251
|
+
errors.push(err);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const slice = rawBytes.slice(info.pos, end);
|
|
255
|
+
rawBlockBytes[info.name] = slice;
|
|
256
|
+
if (decodeBlocks) {
|
|
257
|
+
try {
|
|
258
|
+
blocks[info.name] = decodeBlock(info.name, info.version, slice);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
const err = {
|
|
261
|
+
code: ERR_PARSE_BLOCK,
|
|
262
|
+
message: `Failed to decode block "${info.name}": ${e instanceof Error ? e.message : String(e)}`,
|
|
263
|
+
at: info.name
|
|
264
|
+
};
|
|
265
|
+
if (strict) throw new KoikatuError(err.code, err.message, err.at);
|
|
266
|
+
errors.push(err);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return { blocks, rawBlockBytes, errors };
|
|
271
|
+
}
|
|
272
|
+
function decodeBlock(name, version, data) {
|
|
273
|
+
switch (name) {
|
|
274
|
+
case "Custom":
|
|
275
|
+
return decodeCustomBlock(data);
|
|
276
|
+
case "Coordinate":
|
|
277
|
+
return decodeCoordinateBlock(data, version);
|
|
278
|
+
case "KKEx":
|
|
279
|
+
return decodeKKExBlock(data);
|
|
280
|
+
default:
|
|
281
|
+
return decodeMsgpack(data);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/parse/header.ts
|
|
286
|
+
var SUPPORTED_HEADERS = /* @__PURE__ */ new Set([
|
|
287
|
+
"\u3010KoiKatuChara\u3011",
|
|
288
|
+
"\u3010KoiKatuCharaSun\u3011",
|
|
289
|
+
"\u3010KoiKatuCharaSP\u3011",
|
|
290
|
+
"\u3010Emocre\u3011",
|
|
291
|
+
"\u3010HCChara\u3011",
|
|
292
|
+
"\u3010HCPChara\u3011",
|
|
293
|
+
"\u3010DCChara\u3011",
|
|
294
|
+
"\u3010SVChara\u3011",
|
|
295
|
+
"\u3010ACChara\u3011"
|
|
296
|
+
]);
|
|
297
|
+
function parseHeader(reader, options) {
|
|
298
|
+
const strict = options?.strict ?? false;
|
|
299
|
+
const productNo = reader.readInt32LE();
|
|
300
|
+
if (productNo === void 0) {
|
|
301
|
+
throw new KoikatuError(
|
|
302
|
+
ERR_NO_CARD_PAYLOAD,
|
|
303
|
+
"Failed to read product number"
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const headerStr = reader.readLengthPrefixedString("b");
|
|
307
|
+
if (headerStr === void 0) {
|
|
308
|
+
throw new KoikatuError(ERR_NO_CARD_PAYLOAD, "Failed to read header string");
|
|
309
|
+
}
|
|
310
|
+
let unsupportedHeader;
|
|
311
|
+
if (!SUPPORTED_HEADERS.has(headerStr)) {
|
|
312
|
+
if (strict) {
|
|
313
|
+
throw new KoikatuError(
|
|
314
|
+
ERR_UNSUPPORTED_HEADER,
|
|
315
|
+
`Unsupported header: "${headerStr}"`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
unsupportedHeader = true;
|
|
319
|
+
}
|
|
320
|
+
const versionStr = reader.readLengthPrefixedString("b");
|
|
321
|
+
if (versionStr === void 0) {
|
|
322
|
+
throw new KoikatuError(
|
|
323
|
+
ERR_NO_CARD_PAYLOAD,
|
|
324
|
+
"Failed to read version string"
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const faceImage = reader.readLengthPrefixed("i");
|
|
328
|
+
const result = {
|
|
329
|
+
header: {
|
|
330
|
+
productNo,
|
|
331
|
+
header: headerStr,
|
|
332
|
+
version: versionStr,
|
|
333
|
+
faceImage: faceImage ?? void 0
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
if (unsupportedHeader) {
|
|
337
|
+
result.unsupportedHeader = true;
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/parse/scanPng.ts
|
|
343
|
+
var PNG_MAGIC = new Uint8Array([
|
|
344
|
+
137,
|
|
345
|
+
80,
|
|
346
|
+
78,
|
|
347
|
+
71,
|
|
348
|
+
13,
|
|
349
|
+
10,
|
|
350
|
+
26,
|
|
351
|
+
10
|
|
352
|
+
]);
|
|
353
|
+
function scanPngIend(data) {
|
|
354
|
+
if (data.length < 8) {
|
|
355
|
+
throw new KoikatuError(ERR_NO_PNG, "Data too short to contain PNG header");
|
|
356
|
+
}
|
|
357
|
+
for (let i = 0; i < 8; i++) {
|
|
358
|
+
if (data[i] !== PNG_MAGIC[i]) {
|
|
359
|
+
throw new KoikatuError(ERR_NO_PNG, "Invalid PNG magic bytes");
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
let offset = 8;
|
|
363
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
364
|
+
while (offset + 12 <= data.length) {
|
|
365
|
+
const chunkLen = view.getUint32(offset, false);
|
|
366
|
+
const chunkType = String.fromCharCode(data[offset + 4]) + String.fromCharCode(data[offset + 5]) + String.fromCharCode(data[offset + 6]) + String.fromCharCode(data[offset + 7]);
|
|
367
|
+
const chunkTotal = 4 + 4 + chunkLen + 4;
|
|
368
|
+
if (offset + chunkTotal > data.length) {
|
|
369
|
+
throw new KoikatuError(
|
|
370
|
+
ERR_NO_PNG,
|
|
371
|
+
`Truncated PNG chunk "${chunkType}" at offset ${offset}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
if (chunkType === "IEND") {
|
|
375
|
+
return offset + chunkTotal;
|
|
376
|
+
}
|
|
377
|
+
offset += chunkTotal;
|
|
378
|
+
}
|
|
379
|
+
throw new KoikatuError(ERR_NO_PNG, "PNG IEND chunk not found");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// src/schema/normalize.ts
|
|
383
|
+
function normalizeCard(header, blockIndex, blocks) {
|
|
384
|
+
const product = header.header;
|
|
385
|
+
const blockNames = blockIndex.map((b) => b.name);
|
|
386
|
+
const summary = {
|
|
387
|
+
header,
|
|
388
|
+
product,
|
|
389
|
+
blocks: blockNames
|
|
390
|
+
};
|
|
391
|
+
const param = blocks.Parameter;
|
|
392
|
+
if (param) {
|
|
393
|
+
const lastname = param.lastname ?? param.lastName ?? "";
|
|
394
|
+
const firstname = param.firstname ?? param.firstName ?? "";
|
|
395
|
+
const name = `${lastname} ${firstname}`.trim();
|
|
396
|
+
if (name) summary.name = name;
|
|
397
|
+
const birthMonth = param.birthMonth ?? param.birthday?.month;
|
|
398
|
+
const birthDay = param.birthDay ?? param.birthday?.day;
|
|
399
|
+
if (birthMonth !== void 0 || birthDay !== void 0) {
|
|
400
|
+
summary.birthday = {
|
|
401
|
+
month: birthMonth,
|
|
402
|
+
day: birthDay
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
if (param.sex !== void 0) {
|
|
406
|
+
summary.sex = param.sex;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
summary.hasKKEx = blockNames.includes("KKEx");
|
|
410
|
+
return summary;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/index.ts
|
|
414
|
+
function toUint8Array(input) {
|
|
415
|
+
if (input instanceof Uint8Array) return input;
|
|
416
|
+
return new Uint8Array(input);
|
|
417
|
+
}
|
|
418
|
+
function scanPngIend2(input) {
|
|
419
|
+
return scanPngIend(toUint8Array(input));
|
|
420
|
+
}
|
|
421
|
+
function parseHeader2(input, options) {
|
|
422
|
+
const data = toUint8Array(input);
|
|
423
|
+
let offset = 0;
|
|
424
|
+
if (options?.containsPng !== false) {
|
|
425
|
+
offset = scanPngIend(data);
|
|
426
|
+
}
|
|
427
|
+
const reader = new BinaryReader(data.subarray(offset), options?.strict);
|
|
428
|
+
const result = parseHeader(reader, { strict: options?.strict });
|
|
429
|
+
return result.header;
|
|
430
|
+
}
|
|
431
|
+
function parseCard(input, options) {
|
|
432
|
+
const data = toUint8Array(input);
|
|
433
|
+
const strict = options?.strict ?? false;
|
|
434
|
+
const decodeBlocks = options?.decodeBlocks ?? true;
|
|
435
|
+
let offset = 0;
|
|
436
|
+
if (options?.containsPng !== false) {
|
|
437
|
+
offset = scanPngIend(data);
|
|
438
|
+
}
|
|
439
|
+
const payload = data.subarray(offset);
|
|
440
|
+
if (payload.length === 0) {
|
|
441
|
+
throw new KoikatuError(
|
|
442
|
+
ERR_NO_CARD_PAYLOAD,
|
|
443
|
+
"No card payload found after PNG data"
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
const reader = new BinaryReader(payload, strict);
|
|
447
|
+
const headerResult = parseHeader(reader, { strict });
|
|
448
|
+
const { blockIndex, rawBytes } = parseBlockIndex(reader);
|
|
449
|
+
const { blocks, rawBlockBytes, errors } = parseBlocks(blockIndex, rawBytes, {
|
|
450
|
+
strict,
|
|
451
|
+
decodeBlocks
|
|
452
|
+
});
|
|
453
|
+
const card = {
|
|
454
|
+
header: headerResult.header,
|
|
455
|
+
blocks,
|
|
456
|
+
blockIndex,
|
|
457
|
+
rawBlockBytes
|
|
458
|
+
};
|
|
459
|
+
if (headerResult.unsupportedHeader) {
|
|
460
|
+
card.unsupportedHeader = true;
|
|
461
|
+
}
|
|
462
|
+
if (errors.length > 0) {
|
|
463
|
+
card.errors = errors;
|
|
464
|
+
}
|
|
465
|
+
return card;
|
|
466
|
+
}
|
|
467
|
+
function parseCardSummary(input, options) {
|
|
468
|
+
const card = parseCard(input, { ...options, decodeBlocks: true });
|
|
469
|
+
return normalizeCard(card.header, card.blockIndex, card.blocks);
|
|
470
|
+
}
|
|
471
|
+
function isCard(input) {
|
|
472
|
+
try {
|
|
473
|
+
parseHeader2(input);
|
|
474
|
+
return true;
|
|
475
|
+
} catch {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
export {
|
|
480
|
+
BinaryReader,
|
|
481
|
+
ERR_NO_CARD_PAYLOAD,
|
|
482
|
+
ERR_NO_PNG,
|
|
483
|
+
ERR_PARSE_BLOCK,
|
|
484
|
+
ERR_UNSUPPORTED_HEADER,
|
|
485
|
+
KoikatuError,
|
|
486
|
+
decodeCoordinateBlock,
|
|
487
|
+
decodeCustomBlock,
|
|
488
|
+
decodeKKExBlock,
|
|
489
|
+
decodeMsgpack,
|
|
490
|
+
decodeParameterBlock,
|
|
491
|
+
isCard,
|
|
492
|
+
parseCard,
|
|
493
|
+
parseCardSummary,
|
|
494
|
+
parseHeader2 as parseHeader,
|
|
495
|
+
scanPngIend2 as scanPngIend,
|
|
496
|
+
scanPngIend as scanPngIendRaw
|
|
497
|
+
};
|
|
498
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/parse/msgpack.ts","../src/parse/reader.ts","../src/schema/blocks.ts","../src/parse/blocks.ts","../src/parse/header.ts","../src/parse/scanPng.ts","../src/schema/normalize.ts","../src/index.ts"],"sourcesContent":["export const ERR_NO_PNG = 'ERR_NO_PNG';\nexport const ERR_NO_CARD_PAYLOAD = 'ERR_NO_CARD_PAYLOAD';\nexport const ERR_UNSUPPORTED_HEADER = 'ERR_UNSUPPORTED_HEADER';\nexport const ERR_PARSE_BLOCK = 'ERR_PARSE_BLOCK';\n\nexport class KoikatuError extends Error {\n code: string;\n at?: string;\n\n constructor(code: string, message: string, at?: string) {\n super(message);\n this.name = 'KoikatuError';\n this.code = code;\n this.at = at;\n }\n}\n","import { decode } from '@msgpack/msgpack';\n\nexport function decodeMsgpack(data: Uint8Array): any {\n return decode(data, { useBigInt64: true });\n}\n","const textDecoder = new TextDecoder('utf-8');\n\nexport class BinaryReader {\n private view: DataView;\n private data: Uint8Array;\n private _offset: number;\n private strict: boolean;\n\n constructor(input: ArrayBuffer | Uint8Array, strict = false) {\n if (input instanceof Uint8Array) {\n this.data = input;\n this.view = new DataView(\n input.buffer,\n input.byteOffset,\n input.byteLength,\n );\n } else {\n this.data = new Uint8Array(input);\n this.view = new DataView(input);\n }\n this._offset = 0;\n this.strict = strict;\n }\n\n get offset(): number {\n return this._offset;\n }\n\n set offset(value: number) {\n this._offset = value;\n }\n\n get remaining(): number {\n return this.data.byteLength - this._offset;\n }\n\n private check(bytes: number): boolean {\n if (this._offset + bytes > this.data.byteLength) {\n if (this.strict) {\n throw new RangeError(\n `Read out of bounds: offset ${this._offset} + ${bytes} > ${this.data.byteLength}`,\n );\n }\n return false;\n }\n return true;\n }\n\n readInt8(): number | undefined {\n if (!this.check(1)) return undefined;\n const val = this.view.getInt8(this._offset);\n this._offset += 1;\n return val;\n }\n\n readUint8(): number | undefined {\n if (!this.check(1)) return undefined;\n const val = this.view.getUint8(this._offset);\n this._offset += 1;\n return val;\n }\n\n readInt32LE(): number | undefined {\n if (!this.check(4)) return undefined;\n const val = this.view.getInt32(this._offset, true);\n this._offset += 4;\n return val;\n }\n\n readUint32BE(): number | undefined {\n if (!this.check(4)) return undefined;\n const val = this.view.getUint32(this._offset, false);\n this._offset += 4;\n return val;\n }\n\n readInt64LE(): bigint | undefined {\n if (!this.check(8)) return undefined;\n const val = this.view.getBigInt64(this._offset, true);\n this._offset += 8;\n return val;\n }\n\n readBytes(len: number): Uint8Array | undefined {\n if (len < 0) return undefined;\n if (!this.check(len)) return undefined;\n const slice = this.data.slice(this._offset, this._offset + len);\n this._offset += len;\n return slice;\n }\n\n readLengthPrefixed(sizeType: 'b' | 'i' | 'q'): Uint8Array | undefined {\n let len: number | bigint | undefined;\n switch (sizeType) {\n case 'b':\n len = this.readInt8();\n break;\n case 'i':\n len = this.readInt32LE();\n break;\n case 'q':\n len = this.readInt64LE();\n break;\n }\n if (len === undefined) return undefined;\n return this.readBytes(Number(len));\n }\n\n readLengthPrefixedString(sizeType: 'b' | 'i'): string | undefined {\n const bytes = this.readLengthPrefixed(sizeType);\n if (bytes === undefined) return undefined;\n return textDecoder.decode(bytes);\n }\n\n subarray(offset: number, length: number): Uint8Array {\n return this.data.slice(offset, offset + length);\n }\n}\n","import { decodeMsgpack } from '../parse/msgpack.js';\nimport { BinaryReader } from '../parse/reader.js';\n\nexport function decodeCustomBlock(data: Uint8Array): Record<string, any> {\n const reader = new BinaryReader(data);\n const result: Record<string, any> = {};\n\n const faceBytes = reader.readLengthPrefixed('i');\n if (faceBytes) {\n result.face = decodeMsgpack(faceBytes);\n }\n\n const bodyBytes = reader.readLengthPrefixed('i');\n if (bodyBytes) {\n result.body = decodeMsgpack(bodyBytes);\n }\n\n const hairBytes = reader.readLengthPrefixed('i');\n if (hairBytes) {\n result.hair = decodeMsgpack(hairBytes);\n }\n\n return result;\n}\n\nexport function decodeCoordinateBlock(data: Uint8Array, version?: string): any {\n if (version === '0.0.1') {\n // EmotionCreators: single coordinate, no list wrapping\n const reader = new BinaryReader(data);\n const coord: Record<string, any> = {};\n const clothesBytes = reader.readLengthPrefixed('i');\n if (clothesBytes) coord.clothes = decodeMsgpack(clothesBytes);\n const accessoryBytes = reader.readLengthPrefixed('i');\n if (accessoryBytes) coord.accessory = decodeMsgpack(accessoryBytes);\n return coord;\n }\n\n // v0.0.0 (Koikatu): outer msgpack unpack produces a list of raw byte arrays\n const outerList = decodeMsgpack(data) as Uint8Array[];\n if (!Array.isArray(outerList)) return [];\n\n const coords: any[] = [];\n for (const entry of outerList) {\n const entryBytes =\n entry instanceof Uint8Array ? entry : new Uint8Array(entry);\n const reader = new BinaryReader(entryBytes);\n const coord: Record<string, any> = {};\n\n const clothesBytes = reader.readLengthPrefixed('i');\n if (clothesBytes) coord.clothes = decodeMsgpack(clothesBytes);\n\n const accessoryBytes = reader.readLengthPrefixed('i');\n if (accessoryBytes) coord.accessory = decodeMsgpack(accessoryBytes);\n\n const enableMakeup = reader.readUint8();\n coord.enableMakeup =\n enableMakeup !== undefined ? enableMakeup !== 0 : undefined;\n\n const makeupBytes = reader.readLengthPrefixed('i');\n if (makeupBytes) coord.makeup = decodeMsgpack(makeupBytes);\n\n coords.push(coord);\n }\n\n return coords;\n}\n\nexport function decodeParameterBlock(data: Uint8Array): Record<string, any> {\n return decodeMsgpack(data) as Record<string, any>;\n}\n\nexport function decodeKKExBlock(data: Uint8Array): Record<string, any> {\n const decoded = decodeMsgpack(data) as Record<string, any>;\n return expandNestedMsgpack(decoded);\n}\n\nfunction expandNestedMsgpack(obj: any): any {\n if (obj instanceof Uint8Array) {\n try {\n const inner = decodeMsgpack(obj);\n return expandNestedMsgpack(inner);\n } catch {\n return obj;\n }\n }\n\n if (obj instanceof Map) {\n const result: Record<string, any> = {};\n for (const [key, value] of obj) {\n result[String(key)] = expandNestedMsgpack(value);\n }\n return result;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(expandNestedMsgpack);\n }\n\n if (obj !== null && typeof obj === 'object' && !(obj instanceof Uint8Array)) {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = expandNestedMsgpack(value);\n }\n return result;\n }\n\n return obj;\n}\n","import {\n ERR_NO_CARD_PAYLOAD,\n ERR_PARSE_BLOCK,\n KoikatuError,\n} from '../errors.js';\nimport {\n decodeCoordinateBlock,\n decodeCustomBlock,\n decodeKKExBlock,\n} from '../schema/blocks.js';\nimport type { BlockInfo, ParseError } from '../types.js';\nimport { decodeMsgpack } from './msgpack.js';\nimport type { BinaryReader } from './reader.js';\n\nexport interface BlockIndexResult {\n blockIndex: BlockInfo[];\n rawBytes: Uint8Array;\n}\n\nexport function parseBlockIndex(reader: BinaryReader): BlockIndexResult {\n const indexData = reader.readLengthPrefixed('i');\n if (!indexData) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'Failed to read block index data',\n );\n }\n\n const decoded = decodeMsgpack(indexData) as { lstInfo?: any[] };\n if (!decoded?.lstInfo || !Array.isArray(decoded.lstInfo)) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'Block index missing lstInfo array',\n );\n }\n\n const blockIndex: BlockInfo[] = decoded.lstInfo.map((entry: any) => ({\n name: String(entry.name ?? ''),\n version: String(entry.version ?? ''),\n pos: Number(entry.pos ?? 0),\n size: Number(entry.size ?? 0),\n }));\n\n const rawBytes = reader.readLengthPrefixed('q');\n if (!rawBytes) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'Failed to read raw block bytes',\n );\n }\n\n return { blockIndex, rawBytes };\n}\n\nexport function parseBlocks(\n blockIndex: BlockInfo[],\n rawBytes: Uint8Array,\n options?: { strict?: boolean; decodeBlocks?: boolean },\n): {\n blocks: Record<string, any>;\n rawBlockBytes: Record<string, Uint8Array>;\n errors: ParseError[];\n} {\n const decodeBlocks = options?.decodeBlocks ?? true;\n const strict = options?.strict ?? false;\n const blocks: Record<string, any> = {};\n const rawBlockBytes: Record<string, Uint8Array> = {};\n const errors: ParseError[] = [];\n\n for (const info of blockIndex) {\n const end = info.pos + info.size;\n if (end > rawBytes.length) {\n const err: ParseError = {\n code: ERR_PARSE_BLOCK,\n message: `Block \"${info.name}\" exceeds raw data bounds (pos=${info.pos}, size=${info.size}, total=${rawBytes.length})`,\n at: info.name,\n };\n if (strict) throw new KoikatuError(err.code, err.message, err.at);\n errors.push(err);\n continue;\n }\n\n const slice = rawBytes.slice(info.pos, end);\n rawBlockBytes[info.name] = slice;\n\n if (decodeBlocks) {\n try {\n blocks[info.name] = decodeBlock(info.name, info.version, slice);\n } catch (e) {\n const err: ParseError = {\n code: ERR_PARSE_BLOCK,\n message: `Failed to decode block \"${info.name}\": ${e instanceof Error ? e.message : String(e)}`,\n at: info.name,\n };\n if (strict) throw new KoikatuError(err.code, err.message, err.at);\n errors.push(err);\n }\n }\n }\n\n return { blocks, rawBlockBytes, errors };\n}\n\nfunction decodeBlock(name: string, version: string, data: Uint8Array): any {\n switch (name) {\n case 'Custom':\n return decodeCustomBlock(data);\n case 'Coordinate':\n return decodeCoordinateBlock(data, version);\n case 'KKEx':\n return decodeKKExBlock(data);\n default:\n return decodeMsgpack(data);\n }\n}\n","import {\n ERR_NO_CARD_PAYLOAD,\n ERR_UNSUPPORTED_HEADER,\n KoikatuError,\n} from '../errors.js';\nimport type { CardHeader } from '../types.js';\nimport type { BinaryReader } from './reader.js';\n\nconst SUPPORTED_HEADERS = new Set([\n '【KoiKatuChara】',\n '【KoiKatuCharaSun】',\n '【KoiKatuCharaSP】',\n '【Emocre】',\n '【HCChara】',\n '【HCPChara】',\n '【DCChara】',\n '【SVChara】',\n '【ACChara】',\n]);\n\nexport interface HeaderParseResult {\n header: CardHeader;\n unsupportedHeader?: boolean;\n}\n\nexport function parseHeader(\n reader: BinaryReader,\n options?: { strict?: boolean },\n): HeaderParseResult {\n const strict = options?.strict ?? false;\n\n const productNo = reader.readInt32LE();\n if (productNo === undefined) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'Failed to read product number',\n );\n }\n\n const headerStr = reader.readLengthPrefixedString('b');\n if (headerStr === undefined) {\n throw new KoikatuError(ERR_NO_CARD_PAYLOAD, 'Failed to read header string');\n }\n\n let unsupportedHeader: boolean | undefined;\n if (!SUPPORTED_HEADERS.has(headerStr)) {\n if (strict) {\n throw new KoikatuError(\n ERR_UNSUPPORTED_HEADER,\n `Unsupported header: \"${headerStr}\"`,\n );\n }\n unsupportedHeader = true;\n }\n\n const versionStr = reader.readLengthPrefixedString('b');\n if (versionStr === undefined) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'Failed to read version string',\n );\n }\n\n const faceImage = reader.readLengthPrefixed('i');\n\n const result: HeaderParseResult = {\n header: {\n productNo,\n header: headerStr,\n version: versionStr,\n faceImage: faceImage ?? undefined,\n },\n };\n\n if (unsupportedHeader) {\n result.unsupportedHeader = true;\n }\n\n return result;\n}\n","import { ERR_NO_PNG, KoikatuError } from '../errors.js';\n\nconst PNG_MAGIC = new Uint8Array([\n 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,\n]);\n\nexport function scanPngIend(data: Uint8Array): number {\n if (data.length < 8) {\n throw new KoikatuError(ERR_NO_PNG, 'Data too short to contain PNG header');\n }\n\n for (let i = 0; i < 8; i++) {\n if (data[i] !== PNG_MAGIC[i]) {\n throw new KoikatuError(ERR_NO_PNG, 'Invalid PNG magic bytes');\n }\n }\n\n let offset = 8;\n const view = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n while (offset + 12 <= data.length) {\n const chunkLen = view.getUint32(offset, false);\n const chunkType =\n String.fromCharCode(data[offset + 4]) +\n String.fromCharCode(data[offset + 5]) +\n String.fromCharCode(data[offset + 6]) +\n String.fromCharCode(data[offset + 7]);\n\n // chunk = 4 (length) + 4 (type) + chunkLen (data) + 4 (CRC)\n const chunkTotal = 4 + 4 + chunkLen + 4;\n\n if (offset + chunkTotal > data.length) {\n throw new KoikatuError(\n ERR_NO_PNG,\n `Truncated PNG chunk \"${chunkType}\" at offset ${offset}`,\n );\n }\n\n if (chunkType === 'IEND') {\n return offset + chunkTotal;\n }\n\n offset += chunkTotal;\n }\n\n throw new KoikatuError(ERR_NO_PNG, 'PNG IEND chunk not found');\n}\n","import type { BlockInfo, CardHeader, CardSummary } from '../types.js';\n\nexport function normalizeCard(\n header: CardHeader,\n blockIndex: BlockInfo[],\n blocks: Record<string, any>,\n): CardSummary {\n const product = header.header;\n const blockNames = blockIndex.map((b) => b.name);\n\n const summary: CardSummary = {\n header,\n product,\n blocks: blockNames,\n };\n\n const param = blocks.Parameter;\n if (param) {\n const lastname = param.lastname ?? param.lastName ?? '';\n const firstname = param.firstname ?? param.firstName ?? '';\n const name = `${lastname} ${firstname}`.trim();\n if (name) summary.name = name;\n\n const birthMonth = param.birthMonth ?? param.birthday?.month;\n const birthDay = param.birthDay ?? param.birthday?.day;\n if (birthMonth !== undefined || birthDay !== undefined) {\n summary.birthday = {\n month: birthMonth,\n day: birthDay,\n };\n }\n\n if (param.sex !== undefined) {\n summary.sex = param.sex;\n }\n }\n\n summary.hasKKEx = blockNames.includes('KKEx');\n\n return summary;\n}\n","import { ERR_NO_CARD_PAYLOAD, KoikatuError } from './errors.js';\nimport { parseBlockIndex, parseBlocks } from './parse/blocks.js';\nimport { parseHeader as parseHeaderImpl } from './parse/header.js';\nimport { BinaryReader } from './parse/reader.js';\nimport { scanPngIend as scanPngIendImpl } from './parse/scanPng.js';\nimport { normalizeCard } from './schema/normalize.js';\nimport type {\n Card,\n CardHeader,\n CardSummary,\n Input,\n ParseOptions,\n} from './types.js';\n\nexport {\n ERR_NO_CARD_PAYLOAD,\n ERR_NO_PNG,\n ERR_PARSE_BLOCK,\n ERR_UNSUPPORTED_HEADER,\n KoikatuError,\n} from './errors.js';\nexport { decodeMsgpack } from './parse/msgpack.js';\nexport { BinaryReader } from './parse/reader.js';\nexport { scanPngIend as scanPngIendRaw } from './parse/scanPng.js';\nexport {\n decodeCoordinateBlock,\n decodeCustomBlock,\n decodeKKExBlock,\n decodeParameterBlock,\n} from './schema/blocks.js';\nexport type {\n BlockInfo,\n Card,\n CardHeader,\n CardSummary,\n Input,\n ParseError,\n ParseOptions,\n} from './types.js';\n\nfunction toUint8Array(input: Input): Uint8Array {\n if (input instanceof Uint8Array) return input;\n return new Uint8Array(input);\n}\n\nexport function scanPngIend(input: Input): number {\n return scanPngIendImpl(toUint8Array(input));\n}\n\nexport function parseHeader(\n input: Input,\n options?: { containsPng?: boolean; strict?: boolean },\n): CardHeader {\n const data = toUint8Array(input);\n let offset = 0;\n\n if (options?.containsPng !== false) {\n offset = scanPngIendImpl(data);\n }\n\n const reader = new BinaryReader(data.subarray(offset), options?.strict);\n const result = parseHeaderImpl(reader, { strict: options?.strict });\n return result.header;\n}\n\nexport function parseCard(input: Input, options?: ParseOptions): Card {\n const data = toUint8Array(input);\n const strict = options?.strict ?? false;\n const decodeBlocks = options?.decodeBlocks ?? true;\n let offset = 0;\n\n if (options?.containsPng !== false) {\n offset = scanPngIendImpl(data);\n }\n\n const payload = data.subarray(offset);\n if (payload.length === 0) {\n throw new KoikatuError(\n ERR_NO_CARD_PAYLOAD,\n 'No card payload found after PNG data',\n );\n }\n\n const reader = new BinaryReader(payload, strict);\n\n const headerResult = parseHeaderImpl(reader, { strict });\n const { blockIndex, rawBytes } = parseBlockIndex(reader);\n const { blocks, rawBlockBytes, errors } = parseBlocks(blockIndex, rawBytes, {\n strict,\n decodeBlocks,\n });\n\n const card: Card = {\n header: headerResult.header,\n blocks,\n blockIndex,\n rawBlockBytes,\n };\n\n if (headerResult.unsupportedHeader) {\n card.unsupportedHeader = true;\n }\n\n if (errors.length > 0) {\n card.errors = errors;\n }\n\n return card;\n}\n\nexport function parseCardSummary(\n input: Input,\n options?: ParseOptions,\n): CardSummary {\n const card = parseCard(input, { ...options, decodeBlocks: true });\n return normalizeCard(card.header, card.blockIndex, card.blocks);\n}\n\nexport function isCard(input: Input): boolean {\n try {\n parseHeader(input);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["scanPngIend","parseHeader"]}
|