nitro-patcher 0.3.0 → 1.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/dist/browser.d.ts +37 -2
- package/dist/browser.js +11501 -101
- package/dist/browser.js.LEGAL.txt +664 -0
- package/dist/browser.js.map +4 -4
- package/dist/chunk-LK3JXA24.js +2067 -0
- package/dist/chunk-LK3JXA24.js.map +1 -0
- package/dist/{chunk-IGURDH5L.js → chunk-ZQM3LT5L.js} +362 -8
- package/dist/chunk-ZQM3LT5L.js.map +1 -0
- package/dist/cli.js +19 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -2
- package/dist/nitro-helper.d.ts +43 -134
- package/dist/nitro-helper.js +3 -1
- package/dist/nitro-patch-helper.d.ts +17 -1
- package/dist/nitro-patch-helper.js +4 -2
- package/dist/twl-complete-B2is30H0.d.ts +201 -0
- package/package.json +10 -3
- package/src/browser/buffer.ts +1 -0
- package/src/browser.ts +40 -2
- package/src/cli.ts +15 -2
- package/src/nitro/blz.ts +13 -4
- package/src/nitro/crypto.ts +29 -11
- package/src/nitro/filesystem.ts +88 -5
- package/src/nitro/header.ts +4 -0
- package/src/nitro/nds-file.ts +378 -30
- package/src/nitro/twl-complete.ts +665 -0
- package/src/nitro/twl.ts +53 -19
- package/src/nitro-helper.ts +2 -1
- package/src/nitro-patch-helper.ts +66 -3
- package/src/patch/create.ts +341 -0
- package/src/patch/xdelta3-LICENSE +201 -0
- package/src/patch/zip.ts +14 -4
- package/src/runtime/browser.ts +2 -2
- package/src/runtime/node.ts +2 -15
- package/src/runtime/rsa.ts +14 -0
- package/dist/chunk-GLK3K2HY.js +0 -1120
- package/dist/chunk-GLK3K2HY.js.map +0 -1
- package/dist/chunk-IGURDH5L.js.map +0 -1
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Header,
|
|
2
3
|
NDSFile,
|
|
4
|
+
OverlayTable,
|
|
3
5
|
bytes,
|
|
6
|
+
crc16,
|
|
4
7
|
crc32,
|
|
8
|
+
decryptSecureArea,
|
|
9
|
+
encryptSecureArea,
|
|
5
10
|
hashDigest,
|
|
6
11
|
inflateRawSync,
|
|
7
12
|
readFile
|
|
8
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LK3JXA24.js";
|
|
9
14
|
|
|
10
15
|
// src/patch/xdelta.ts
|
|
11
16
|
var UINT32_MAX = 4294967295;
|
|
@@ -17,6 +22,8 @@ var Reader = class {
|
|
|
17
22
|
this.bytes = bytes2;
|
|
18
23
|
this.label = label;
|
|
19
24
|
}
|
|
25
|
+
bytes;
|
|
26
|
+
label;
|
|
20
27
|
pos = 0;
|
|
21
28
|
get remaining() {
|
|
22
29
|
return this.bytes.length - this.pos;
|
|
@@ -227,7 +234,7 @@ function filename(raw, flags, fields) {
|
|
|
227
234
|
for (const byte of raw) result += byte < 128 ? String.fromCharCode(byte) : cp437[byte - 128];
|
|
228
235
|
return result;
|
|
229
236
|
}
|
|
230
|
-
var
|
|
237
|
+
var readZipEntries = (input, options = {}) => {
|
|
231
238
|
if (!(input instanceof Uint8Array)) throw new TypeError("ZIP input must be a Buffer or Uint8Array");
|
|
232
239
|
const bytes2 = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
|
|
233
240
|
const maxSize = boundOption(options.maxUncompressedSize, 1024 * 1024 * 1024, "maxUncompressedSize");
|
|
@@ -271,7 +278,8 @@ var readZip = (input, options = {}) => {
|
|
|
271
278
|
checkRange(bytes2, offset + 46, nameLength + extraLength + commentLength, eocd);
|
|
272
279
|
const rawName = bytes2.subarray(offset + 46, offset + 46 + nameLength);
|
|
273
280
|
const fields = extras(bytes2.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength));
|
|
274
|
-
const
|
|
281
|
+
const path = filename(rawName, flags, fields).replaceAll("\\", "/");
|
|
282
|
+
const name = path.toLowerCase();
|
|
275
283
|
offset += 46 + nameLength + extraLength + commentLength;
|
|
276
284
|
checkRange(bytes2, localOffset, 30, centralOffset);
|
|
277
285
|
if (bytes2.readUInt32LE(localOffset) !== 67324752) fail2("invalid local header signature");
|
|
@@ -311,7 +319,7 @@ var readZip = (input, options = {}) => {
|
|
|
311
319
|
}
|
|
312
320
|
if (data.length !== size) fail2(`uncompressed size mismatch for ${JSON.stringify(name)}`);
|
|
313
321
|
if (crc32(data) !== checksum) fail2(`CRC32 mismatch for ${JSON.stringify(name)}`);
|
|
314
|
-
if (name && !name.endsWith("/")) result.set(name, data);
|
|
322
|
+
if (name && !name.endsWith("/")) result.set(name, { path, data });
|
|
315
323
|
}
|
|
316
324
|
if (offset !== eocd) fail2("central directory entry count/length mismatch");
|
|
317
325
|
ranges.sort((a, b) => a.start - b.start);
|
|
@@ -319,6 +327,7 @@ var readZip = (input, options = {}) => {
|
|
|
319
327
|
if (ranges[i].start < ranges[i - 1].end) fail2("overlapping local entry ranges");
|
|
320
328
|
return result;
|
|
321
329
|
};
|
|
330
|
+
var readZip = (input, options = {}) => new Map([...readZipEntries(input, options)].map(([name, entry]) => [name, entry.data]));
|
|
322
331
|
var extractZipEntry = (input, entryName, options = {}) => {
|
|
323
332
|
if (typeof entryName !== "string" || !entryName) throw new TypeError("ZIP entry name must be a non-empty string");
|
|
324
333
|
const normalized = entryName.replaceAll("\\", "/").toLowerCase();
|
|
@@ -328,6 +337,293 @@ var extractZipEntry = (input, entryName, options = {}) => {
|
|
|
328
337
|
return entry;
|
|
329
338
|
};
|
|
330
339
|
|
|
340
|
+
// src/patch/create.ts
|
|
341
|
+
import { encode } from "@limrun/xdelta3-wasm";
|
|
342
|
+
import { zipSync } from "fflate";
|
|
343
|
+
var normalizePath = (path) => path.replaceAll("\\", "/").toLowerCase();
|
|
344
|
+
var listFiles = (rom) => new Map(rom.listFiles().map((file) => [normalizePath(file.path), file]));
|
|
345
|
+
var listDirectories = (folder, prefix = "data") => [prefix, ...folder.folders.flatMap((child) => listDirectories(child, `${prefix}/${child.name}`))].sort();
|
|
346
|
+
var getUnnamedFiles = (rom) => {
|
|
347
|
+
const namedIds = new Set(
|
|
348
|
+
rom.listFiles().filter((file) => /^(data|overlay)\//i.test(file.path)).map((file) => file.id)
|
|
349
|
+
);
|
|
350
|
+
return rom.fatTable.entries.filter((entry) => !namedIds.has(entry.id)).map((entry) => rom.original.subarray(entry.offset, entry.offset + entry.size));
|
|
351
|
+
};
|
|
352
|
+
var normalizeHeader = (rom) => {
|
|
353
|
+
const header = new Header(rom.getFile("header.bin"));
|
|
354
|
+
header.size = 131072;
|
|
355
|
+
for (const field of [
|
|
356
|
+
"ARM9romOffset",
|
|
357
|
+
"ARM9size",
|
|
358
|
+
"ARM7romOffset",
|
|
359
|
+
"ARM7size",
|
|
360
|
+
"FNToffset",
|
|
361
|
+
"FNTsize",
|
|
362
|
+
"FAToffset",
|
|
363
|
+
"FATsize",
|
|
364
|
+
"ARM9overlayOffset",
|
|
365
|
+
"ARM9overlaySize",
|
|
366
|
+
"ARM7overlayOffset",
|
|
367
|
+
"ARM7overlaySize",
|
|
368
|
+
"bannerOffset",
|
|
369
|
+
"secureCRC16",
|
|
370
|
+
"ROMsize",
|
|
371
|
+
"logoCRC16",
|
|
372
|
+
"headerCRC16"
|
|
373
|
+
])
|
|
374
|
+
header[field] = 0;
|
|
375
|
+
if (header.unitCode === 2) {
|
|
376
|
+
for (const field of [
|
|
377
|
+
"ntr_region_end",
|
|
378
|
+
"twl_region_start",
|
|
379
|
+
"dsi9_rom_offset",
|
|
380
|
+
"dsi7_rom_offset",
|
|
381
|
+
"digest_ntr_start",
|
|
382
|
+
"digest_ntr_size",
|
|
383
|
+
"digest_twl_start",
|
|
384
|
+
"digest_twl_size",
|
|
385
|
+
"sector_hashtable_start",
|
|
386
|
+
"sector_hashtable_size",
|
|
387
|
+
"block_hashtable_start",
|
|
388
|
+
"block_hashtable_size",
|
|
389
|
+
"total_rom_size",
|
|
390
|
+
"modcrypt1_start",
|
|
391
|
+
"modcrypt1_size",
|
|
392
|
+
"modcrypt2_start",
|
|
393
|
+
"modcrypt2_size"
|
|
394
|
+
])
|
|
395
|
+
header[field] = 0;
|
|
396
|
+
for (const field of [
|
|
397
|
+
"hmac_arm9",
|
|
398
|
+
"hmac_arm7",
|
|
399
|
+
"hmac_digest_master",
|
|
400
|
+
"hmac_icon_title",
|
|
401
|
+
"hmac_arm9i",
|
|
402
|
+
"hmac_arm7i",
|
|
403
|
+
"hmac_arm9_no_secure",
|
|
404
|
+
"rsa_signature"
|
|
405
|
+
])
|
|
406
|
+
header[field] = Buffer.alloc(header[field].length);
|
|
407
|
+
}
|
|
408
|
+
return header.raw;
|
|
409
|
+
};
|
|
410
|
+
var compareContent = (actual, expected, path, warnings) => {
|
|
411
|
+
if (!actual.equals(expected)) warnings.push(`\u91CD\u5EFA\u5185\u5BB9\u4E0E\u4FEE\u6539\u540E\u7684 ROM \u4E0D\u540C\uFF1A${path}`);
|
|
412
|
+
};
|
|
413
|
+
var errorMessage = (reason) => reason instanceof Error ? reason.message : String(reason);
|
|
414
|
+
var parseRom = (input, label, warnings) => {
|
|
415
|
+
try {
|
|
416
|
+
const rom = new NDSFile(input, { dsiMode: "complete" });
|
|
417
|
+
rom.listFiles();
|
|
418
|
+
return rom;
|
|
419
|
+
} catch (reason) {
|
|
420
|
+
try {
|
|
421
|
+
const rom = new NDSFile(input);
|
|
422
|
+
rom.listFiles();
|
|
423
|
+
warnings.push(`${label} \u4F7F\u7528\u5E38\u89C4\u6A21\u5F0F\u89E3\u6790\uFF1A${errorMessage(reason)}`);
|
|
424
|
+
return rom;
|
|
425
|
+
} catch (error) {
|
|
426
|
+
throw new Error(`\u65E0\u6CD5\u89E3\u6790${label}\uFF1A${errorMessage(error)}`, { cause: error });
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var createDelta = async (before, after) => {
|
|
431
|
+
const chunks = [];
|
|
432
|
+
async function* targetChunks() {
|
|
433
|
+
yield after;
|
|
434
|
+
}
|
|
435
|
+
for await (const chunk of encode(targetChunks(), {
|
|
436
|
+
size: before.length,
|
|
437
|
+
read(offset, into) {
|
|
438
|
+
const part = before.subarray(offset, offset + into.length);
|
|
439
|
+
into.set(part);
|
|
440
|
+
return part.length;
|
|
441
|
+
}
|
|
442
|
+
}))
|
|
443
|
+
chunks.push(Buffer.from(chunk));
|
|
444
|
+
return Buffer.concat(chunks);
|
|
445
|
+
};
|
|
446
|
+
var verifyContent = (actual, expected, arm9, warnings) => {
|
|
447
|
+
const actualFiles = listFiles(actual);
|
|
448
|
+
const expectedFiles = listFiles(expected);
|
|
449
|
+
if ([...actualFiles.keys()].sort().join("\n") !== [...expectedFiles.keys()].sort().join("\n"))
|
|
450
|
+
warnings.push("\u91CD\u5EFA ROM \u7684\u6587\u4EF6\u8DEF\u5F84\u96C6\u5408\u4E0E\u4FEE\u6539\u540E\u7684 ROM \u4E0D\u540C\u3002");
|
|
451
|
+
const actualDirectories = new Set(listDirectories(actual.data));
|
|
452
|
+
const expectedDirectories = new Set(listDirectories(expected.data));
|
|
453
|
+
for (const path of expectedDirectories)
|
|
454
|
+
if (!actualDirectories.has(path)) {
|
|
455
|
+
if ([...expectedFiles.keys()].some((name) => name.startsWith(normalizePath(`${path}/`))))
|
|
456
|
+
warnings.push(`\u91CD\u5EFA ROM \u7F3A\u5C11\u76EE\u5F55\uFF1A${path}`);
|
|
457
|
+
else warnings.push(`\u65B0\u589E\u7A7A\u76EE\u5F55\u5DF2\u5FFD\u7565\uFF1A${path}`);
|
|
458
|
+
}
|
|
459
|
+
for (const path of actualDirectories)
|
|
460
|
+
if (!expectedDirectories.has(path)) {
|
|
461
|
+
if ([...actualFiles.keys()].some((name) => name.startsWith(normalizePath(`${path}/`))))
|
|
462
|
+
warnings.push(`\u91CD\u5EFA ROM \u4FDD\u7559\u4E86\u539F\u59CB\u76EE\u5F55\uFF1A${path}`);
|
|
463
|
+
else warnings.push(`\u5DF2\u4FDD\u7559\u539F\u59CB ROM \u7684\u7A7A\u76EE\u5F55\uFF1A${path}`);
|
|
464
|
+
}
|
|
465
|
+
for (const [path, file] of expectedFiles) {
|
|
466
|
+
if (["header.bin", "fnt.bin", "fat.bin", "banner.bin", "overarm9.bin", "overarm7.bin"].includes(path)) continue;
|
|
467
|
+
if (!actualFiles.has(path)) continue;
|
|
468
|
+
let content = path === "arm9.bin" ? Buffer.from(arm9) : expected.getFile(file.path);
|
|
469
|
+
if (path === "arm9.bin" && expected.header.unitCode === 2) {
|
|
470
|
+
const plain = expected.header.decrypted ? content : decryptSecureArea(expected.header.gameCode, content);
|
|
471
|
+
plain.writeUInt16LE(crc16(plain.subarray(16, 2048)), 14);
|
|
472
|
+
content = expected.header.decrypted ? plain : encryptSecureArea(expected.header.gameCode, plain);
|
|
473
|
+
}
|
|
474
|
+
compareContent(actual.getFile(file.path), content, file.path, warnings);
|
|
475
|
+
}
|
|
476
|
+
compareContent(actual.banner.toBuffer(), expected.banner.toBuffer(), "banner.bin", warnings);
|
|
477
|
+
compareContent(normalizeHeader(actual), normalizeHeader(expected), "header.bin", warnings);
|
|
478
|
+
compareContent(actual.header.dlp_signature, expected.header.dlp_signature, "Download Play \u7B7E\u540D", warnings);
|
|
479
|
+
for (const [left, right] of [
|
|
480
|
+
[actual.overlay9Table, expected.overlay9Table],
|
|
481
|
+
[actual.overlay7Table, expected.overlay7Table]
|
|
482
|
+
]) {
|
|
483
|
+
if (left.entries.length !== right.entries.length) warnings.push("\u91CD\u5EFA ROM \u7684\u8986\u76D6\u6A21\u5757\u6570\u91CF\u4E0E\u4FEE\u6539\u540E\u7684 ROM \u4E0D\u540C\u3002");
|
|
484
|
+
for (const entry of right.entries) {
|
|
485
|
+
const other = left.entries.find((item) => item.overlayId === entry.overlayId);
|
|
486
|
+
if (!other) {
|
|
487
|
+
warnings.push(`\u91CD\u5EFA ROM \u7F3A\u5C11\u8986\u76D6\u6A21\u5757\uFF1A${entry.overlayId}`);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
for (const field of [
|
|
491
|
+
"ramAddress",
|
|
492
|
+
"ramSize",
|
|
493
|
+
"bssSize",
|
|
494
|
+
"staticInitialiserStartAddress",
|
|
495
|
+
"staticInitialiserEndAddress",
|
|
496
|
+
"reserved"
|
|
497
|
+
])
|
|
498
|
+
if (other[field] !== entry[field])
|
|
499
|
+
warnings.push(`\u91CD\u5EFA\u8986\u76D6\u6A21\u5757 ${entry.overlayId} \u7684 ${field} \u4E0E\u4FEE\u6539\u540E\u7684 ROM \u4E0D\u540C\u3002`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
const actualUnnamed = getUnnamedFiles(actual);
|
|
503
|
+
const expectedUnnamed = getUnnamedFiles(expected);
|
|
504
|
+
if (actualUnnamed.length !== expectedUnnamed.length)
|
|
505
|
+
warnings.push("\u91CD\u5EFA ROM \u7684\u672A\u547D\u540D FAT \u9879\u6570\u91CF\u4E0E\u4FEE\u6539\u540E\u7684 ROM \u4E0D\u540C\u3002");
|
|
506
|
+
actualUnnamed.forEach((content, index) => {
|
|
507
|
+
if (expectedUnnamed[index]) compareContent(content, expectedUnnamed[index], `\u672A\u547D\u540D FAT \u9879 ${index}`, warnings);
|
|
508
|
+
});
|
|
509
|
+
if (actual.header.unitCode === 2) {
|
|
510
|
+
for (const [name, check] of Object.entries(actual.verifyDsi()))
|
|
511
|
+
if (check.status === "invalid") warnings.push(`\u91CD\u5EFA ROM \u7684 DSi \u6821\u9A8C\u7ED3\u679C\u4E3A invalid\uFF1A${name}`);
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
var verifyPatch = (input, archive, target, arm9, options, warnings) => {
|
|
515
|
+
let output;
|
|
516
|
+
try {
|
|
517
|
+
output = patchBuffer(input, archive, options).buffer;
|
|
518
|
+
} catch (reason) {
|
|
519
|
+
warnings.push(`\u8865\u4E01\u5E94\u7528\u68C0\u67E5\u672A\u5B8C\u6210\uFF1A${errorMessage(reason)}`);
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
verifyContent(parseRom(output, "\u91CD\u5EFA ROM", warnings), target, arm9, warnings);
|
|
524
|
+
} catch (reason) {
|
|
525
|
+
warnings.push(`\u8865\u4E01\u6570\u636E\u6838\u5BF9\u672A\u5B8C\u6210\uFF1A${errorMessage(reason)}`);
|
|
526
|
+
}
|
|
527
|
+
return true;
|
|
528
|
+
};
|
|
529
|
+
var createPatch = async (original, modified, options = {}) => {
|
|
530
|
+
const input = bytes(original, "original ROM");
|
|
531
|
+
const targetBytes = bytes(modified, "modified ROM");
|
|
532
|
+
const warnings = [];
|
|
533
|
+
const source = parseRom(input, "\u539F\u59CB ROM", warnings);
|
|
534
|
+
const target = parseRom(targetBytes, "\u4FEE\u6539\u540E\u7684 ROM", warnings);
|
|
535
|
+
let preprocessing = false;
|
|
536
|
+
if (source.header.gameCode !== target.header.gameCode)
|
|
537
|
+
warnings.push(`\u4E24\u4E2A ROM \u7684\u6E38\u620F\u4EE3\u7801\u4E0D\u540C\uFF1A${source.header.gameCode} / ${target.header.gameCode}`);
|
|
538
|
+
if (source.header.unitCode !== target.header.unitCode)
|
|
539
|
+
warnings.push(`\u4E24\u4E2A ROM \u7684 unitCode \u4E0D\u540C\uFF1A${source.header.unitCode} / ${target.header.unitCode}`);
|
|
540
|
+
if (source.header.ROMversion !== target.header.ROMversion)
|
|
541
|
+
warnings.push(`\u4E24\u4E2A ROM \u7684\u4FEE\u8BA2\u7248\u672C\u4E0D\u540C\uFF1A${source.header.ROMversion} / ${target.header.ROMversion}`);
|
|
542
|
+
if (source.header.gameCode !== target.header.gameCode || source.header.unitCode !== target.header.unitCode)
|
|
543
|
+
preprocessing = true;
|
|
544
|
+
const sourceFiles = listFiles(source);
|
|
545
|
+
const targetFiles = listFiles(target);
|
|
546
|
+
const headerChanged = !normalizeHeader(source).equals(normalizeHeader(target));
|
|
547
|
+
const sourceArm9 = source.getFile("arm9.bin");
|
|
548
|
+
const targetArm9 = target.getFile("arm9.bin");
|
|
549
|
+
const sourcePostData = sourceArm9.subarray(source.header.ARM9size);
|
|
550
|
+
const targetPostData = targetArm9.subarray(target.header.ARM9size);
|
|
551
|
+
if (!sourcePostData.equals(targetPostData)) warnings.push("\u4E24\u4E2A ROM \u7684 ARM9 \u5C3E\u90E8\u8BB0\u5F55\u4E0D\u540C\u3002");
|
|
552
|
+
for (const [label, rom, entries2] of [
|
|
553
|
+
["\u539F\u59CB ROM", source, sourceFiles],
|
|
554
|
+
["\u4FEE\u6539\u540E\u7684 ROM", target, targetFiles]
|
|
555
|
+
]) {
|
|
556
|
+
const file = entries2.get("arm9.bin");
|
|
557
|
+
const end = file.offset + file.size;
|
|
558
|
+
if (rom.header.nitrocode && rom.original.subarray(end, end + 4).equals(Buffer.from("2106c0de", "hex")))
|
|
559
|
+
warnings.push(`${label} \u7684 ARM9 \u542B\u6709\u989D\u5916\u5C3E\u90E8\u8BB0\u5F55\u3002`);
|
|
560
|
+
}
|
|
561
|
+
const targetProgram = targetArm9.subarray(0, target.header.ARM9size);
|
|
562
|
+
const arm9 = sourcePostData.length && targetProgram.subarray(-sourcePostData.length).equals(sourcePostData) ? targetProgram : Buffer.concat([targetProgram, sourcePostData]);
|
|
563
|
+
const removed = [...sourceFiles.keys()].filter((path) => !targetFiles.has(path));
|
|
564
|
+
if (removed.length) {
|
|
565
|
+
warnings.push(`\u4FEE\u6539\u540E\u7684 ROM \u7F3A\u5C11\u539F\u59CB ROM \u4E2D\u7684 ${removed.length} \u4E2A\u6587\u4EF6\u3002`);
|
|
566
|
+
preprocessing = true;
|
|
567
|
+
}
|
|
568
|
+
const sourceUnnamed = getUnnamedFiles(source);
|
|
569
|
+
const targetUnnamed = getUnnamedFiles(target);
|
|
570
|
+
if (sourceUnnamed.length !== targetUnnamed.length || sourceUnnamed.some((content, index) => !content.equals(targetUnnamed[index] ?? Buffer.alloc(0)))) {
|
|
571
|
+
warnings.push("\u4E24\u4E2A ROM \u7684\u672A\u547D\u540D FAT \u6570\u636E\u4E0D\u540C\u3002");
|
|
572
|
+
preprocessing = true;
|
|
573
|
+
}
|
|
574
|
+
const entries = {};
|
|
575
|
+
let changedFiles = 0;
|
|
576
|
+
for (const [path, file] of targetFiles) {
|
|
577
|
+
if (path === "fnt.bin" || path === "fat.bin") continue;
|
|
578
|
+
if (path === "header.bin" && !headerChanged) continue;
|
|
579
|
+
const before = sourceFiles.has(path) ? source.getFile(file.path) : Buffer.alloc(0);
|
|
580
|
+
const after = path === "arm9.bin" ? arm9 : target.getFile(file.path);
|
|
581
|
+
if (sourceFiles.has(path) && before.equals(after)) continue;
|
|
582
|
+
if (!sourceFiles.has(path) && !/^(data|overlay)\//.test(path) && !/^overarm[97]\.bin$/.test(path))
|
|
583
|
+
warnings.push(`\u65B0\u589E\u7CFB\u7EDF\u6587\u4EF6\uFF1A${file.path}`);
|
|
584
|
+
try {
|
|
585
|
+
const delta = await createDelta(before, after);
|
|
586
|
+
if (decodeXdelta(before, delta, options).equals(after)) entries[`xdelta/${file.path}`] = delta;
|
|
587
|
+
else {
|
|
588
|
+
warnings.push(`\u6587\u4EF6\u5DEE\u5206\u89E3\u7801\u5185\u5BB9\u4E0D\u540C\uFF0C\u5DF2\u4FDD\u5B58\u5B8C\u6574\u66FF\u6362\u5185\u5BB9\uFF1A${file.path}`);
|
|
589
|
+
entries[file.path] = after;
|
|
590
|
+
}
|
|
591
|
+
} catch (reason) {
|
|
592
|
+
warnings.push(`\u6587\u4EF6\u5DEE\u5206\u68C0\u67E5\u672A\u5B8C\u6210\uFF0C\u5DF2\u4FDD\u5B58\u5B8C\u6574\u66FF\u6362\u5185\u5BB9\uFF1A${file.path}\uFF1A${errorMessage(reason)}`);
|
|
593
|
+
entries[file.path] = after;
|
|
594
|
+
}
|
|
595
|
+
changedFiles++;
|
|
596
|
+
}
|
|
597
|
+
const metadata = { ...options.metadata, formatVersion: 2 };
|
|
598
|
+
const inputMd5 = hashDigest("md5", input).toString("hex");
|
|
599
|
+
entries["metadata.json"] = Buffer.from(JSON.stringify(metadata, null, 2));
|
|
600
|
+
entries["md5.txt"] = Buffer.from(inputMd5);
|
|
601
|
+
entries["readme.md"] = Buffer.from(options.readme ?? "");
|
|
602
|
+
let archive = zipSync(entries, { level: 9 });
|
|
603
|
+
if (!preprocessing) preprocessing = !verifyPatch(input, archive, target, arm9, options, warnings);
|
|
604
|
+
if (preprocessing) {
|
|
605
|
+
try {
|
|
606
|
+
const delta = await createDelta(input, targetBytes);
|
|
607
|
+
const formatVersion = target.header.unitCode & 2 && !target.twl?.complete ? 1 : 2;
|
|
608
|
+
archive = zipSync(
|
|
609
|
+
{
|
|
610
|
+
[`preprocessing/${inputMd5}.xdelta`]: delta,
|
|
611
|
+
"metadata.json": Buffer.from(JSON.stringify({ ...metadata, formatVersion }, null, 2)),
|
|
612
|
+
"md5.txt": entries["md5.txt"],
|
|
613
|
+
"readme.md": entries["readme.md"]
|
|
614
|
+
},
|
|
615
|
+
{ level: 9 }
|
|
616
|
+
);
|
|
617
|
+
changedFiles = 1;
|
|
618
|
+
warnings.push("\u8865\u4E01\u4F7F\u7528\u6574\u4EFD ROM \u7684\u9884\u5904\u7406\u5DEE\u5206\u4FDD\u5B58\u4FEE\u6539\u3002");
|
|
619
|
+
verifyPatch(input, archive, target, targetArm9, options, warnings);
|
|
620
|
+
} catch (reason) {
|
|
621
|
+
warnings.push(`\u9884\u5904\u7406\u5DEE\u5206\u751F\u6210\u672A\u5B8C\u6210\uFF0C\u5DF2\u4FDD\u7559\u6309\u6587\u4EF6\u8865\u4E01\u5305\uFF1A${errorMessage(reason)}`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return { buffer: new Uint8Array(archive), changedFiles, warnings };
|
|
625
|
+
};
|
|
626
|
+
|
|
331
627
|
// src/nitro-patch-helper.ts
|
|
332
628
|
var PatchReturnValue = Object.freeze({
|
|
333
629
|
SUCCESS: "SUCCESS",
|
|
@@ -356,6 +652,13 @@ var readMetadata = (files) => {
|
|
|
356
652
|
return invalidMetadata("\u6839\u8282\u70B9\u5FC5\u987B\u662F\u5BF9\u8C61\u3002");
|
|
357
653
|
const value = metadata;
|
|
358
654
|
const result = {};
|
|
655
|
+
if (value.formatVersion !== void 0) {
|
|
656
|
+
const version = value.formatVersion;
|
|
657
|
+
if (typeof version !== "number" || !Number.isSafeInteger(version) || version < 1)
|
|
658
|
+
throw new Error("metadata.json formatVersion must be a positive safe integer");
|
|
659
|
+
if (version !== 1 && version !== 2) throw new Error(`Unsupported XZP format version: ${version}`);
|
|
660
|
+
result.formatVersion = version;
|
|
661
|
+
}
|
|
359
662
|
for (const field of ["id", "author", "name", "homepage", "version"]) {
|
|
360
663
|
const fieldValue = value[field];
|
|
361
664
|
if (fieldValue === void 0) continue;
|
|
@@ -399,10 +702,18 @@ var readPatchReadmeFile = async (patchPath, options = {}) => readPatchReadme(awa
|
|
|
399
702
|
var readPatchInfoFile = async (patchPath, options = {}) => readPatchInfo(await readFile(patchPath), options);
|
|
400
703
|
var patchBuffer = (original, patch, options = {}) => {
|
|
401
704
|
const input = bytes(original, "original ROM");
|
|
402
|
-
const
|
|
705
|
+
const entries = readZipEntries(patch, options);
|
|
706
|
+
const patches = new Map([...entries].map(([name, entry]) => [name, entry.data]));
|
|
707
|
+
const formatVersion = readMetadata(patches)?.formatVersion ?? 1;
|
|
403
708
|
const inputMd5 = md5(input);
|
|
404
709
|
const preprocessing = patches.get(`preprocessing/${inputMd5}.xdelta`);
|
|
405
|
-
const
|
|
710
|
+
const romOptions = { ...options, dsiMode: formatVersion === 2 ? "complete" : "legacy" };
|
|
711
|
+
const rom = new NDSFile(preprocessing ? decodeXdelta(input, preprocessing, options) : input, romOptions);
|
|
712
|
+
if (formatVersion === 2)
|
|
713
|
+
for (const name of ["arm9i.bin", "arm7i.bin"]) {
|
|
714
|
+
if ((patches.has(name) || patches.has(`xdelta/${name}`)) && !rom.hasFile(name))
|
|
715
|
+
throw new Error(`${name} requires a complete DSi-enhanced cartridge`);
|
|
716
|
+
}
|
|
406
717
|
let matched = true;
|
|
407
718
|
const md5File = patches.get("md5.txt");
|
|
408
719
|
if (md5File) {
|
|
@@ -423,12 +734,54 @@ var patchBuffer = (original, patch, options = {}) => {
|
|
|
423
734
|
const replacement = delta ? decodeXdelta(rom.getFile(path), delta, options) : patches.get(normalized);
|
|
424
735
|
if (replacement) rom.replaceFile(path, replacement);
|
|
425
736
|
}
|
|
737
|
+
if (formatVersion === 2) {
|
|
738
|
+
for (const name of ["overarm9.bin", "overarm7.bin"])
|
|
739
|
+
if (!rom.hasFile(name)) {
|
|
740
|
+
const delta = patches.get(`xdelta/${name}`);
|
|
741
|
+
const replacement = delta ? decodeXdelta(Buffer.alloc(0), delta, options) : patches.get(name);
|
|
742
|
+
if (replacement) rom.replaceFile(name, replacement);
|
|
743
|
+
}
|
|
744
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
745
|
+
for (const [name, entry] of entries) {
|
|
746
|
+
const delta = name.startsWith("xdelta/");
|
|
747
|
+
const target = delta ? name.slice(7) : name;
|
|
748
|
+
if (!target.startsWith("data/") && !target.startsWith("overlay/") || rom.hasFile(target)) continue;
|
|
749
|
+
if (!delta && entries.has(`xdelta/${target}`)) continue;
|
|
750
|
+
candidates.set(target, { path: delta ? entry.path.slice(7) : entry.path, data: entry.data, delta });
|
|
751
|
+
}
|
|
752
|
+
if (candidates.size && (patches.has("fnt.bin") || patches.has("xdelta/fnt.bin")))
|
|
753
|
+
throw new Error("Adding files conflicts with an fnt.bin replacement");
|
|
754
|
+
const overlayTables = /* @__PURE__ */ new Map();
|
|
755
|
+
for (const name of ["overarm9.bin", "overarm7.bin"])
|
|
756
|
+
if (rom.hasFile(name)) {
|
|
757
|
+
const raw = rom.getFile(name);
|
|
758
|
+
overlayTables.set(name, new OverlayTable(raw, 0, raw.length, name === "overarm9.bin"));
|
|
759
|
+
}
|
|
760
|
+
const additions = [...candidates.values()].sort((a, b) => Buffer.compare(Buffer.from(a.path), Buffer.from(b.path)));
|
|
761
|
+
for (const entry of additions) {
|
|
762
|
+
const content = entry.delta ? decodeXdelta(Buffer.alloc(0), entry.data, options) : entry.data;
|
|
763
|
+
if (entry.path.toLowerCase().startsWith("overlay/")) {
|
|
764
|
+
const match = /^overlay\/overlay(7)?_(\d{4,})\.bin$/i.exec(entry.path);
|
|
765
|
+
const overlayId = match?.[2];
|
|
766
|
+
const arm9 = !match?.[1];
|
|
767
|
+
if (!overlayId || String(Number(overlayId)).padStart(4, "0") !== overlayId)
|
|
768
|
+
throw new Error(`Invalid overlay path: ${entry.path}`);
|
|
769
|
+
const name = arm9 ? "overarm9.bin" : "overarm7.bin";
|
|
770
|
+
const table2 = overlayTables.get(name);
|
|
771
|
+
if (!table2) throw new Error(`New overlay requires ${name}: ${entry.path}`);
|
|
772
|
+
const metadata = table2.entries.find((item) => item.overlayId === Number(overlayId));
|
|
773
|
+
if (!metadata) throw new Error(`New overlay is missing its table entry: ${entry.path}`);
|
|
774
|
+
rom.addOverlay(arm9, metadata, content);
|
|
775
|
+
} else rom.addFile(entry.path, content);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
426
778
|
const buffer = rom.toBuffer();
|
|
427
779
|
return {
|
|
428
780
|
buffer,
|
|
429
781
|
returnValue: matched ? PatchReturnValue.SUCCESS : PatchReturnValue.MD5_MISMATCH,
|
|
430
782
|
inputMd5,
|
|
431
|
-
outputMd5: md5(buffer)
|
|
783
|
+
outputMd5: md5(buffer),
|
|
784
|
+
...formatVersion === 2 && rom.header.unitCode === 2 && { dsi: new NDSFile(buffer, romOptions).verifyDsi() }
|
|
432
785
|
};
|
|
433
786
|
};
|
|
434
787
|
var patchIt = async (originalPath, patchPath, options = {}) => {
|
|
@@ -449,6 +802,7 @@ var PatchHelper = Object.freeze({
|
|
|
449
802
|
|
|
450
803
|
export {
|
|
451
804
|
decodeXdelta,
|
|
805
|
+
createPatch,
|
|
452
806
|
PatchReturnValue,
|
|
453
807
|
readPatchMetadata,
|
|
454
808
|
readPatchReadme,
|
|
@@ -461,4 +815,4 @@ export {
|
|
|
461
815
|
patchIt,
|
|
462
816
|
PatchHelper
|
|
463
817
|
};
|
|
464
|
-
//# sourceMappingURL=chunk-
|
|
818
|
+
//# sourceMappingURL=chunk-ZQM3LT5L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/patch/xdelta.ts","../src/patch/zip.ts","../src/patch/create.ts","../src/nitro-patch-helper.ts"],"sourcesContent":["/** Native RFC 3284 VCDIFF decoder with the xdelta3 Adler32/app-header extensions.\n * Supports the same default-table, uncompressed format as PleOps.XdeltaSharp 1.3.0.\n * Secondary compression and custom code tables are explicitly unsupported there too.\n */\nconst UINT32_MAX = 0xffffffff;\nconst fail = (message: string): never => {\n throw new Error(`VCDIFF: ${message}`);\n};\n\nclass Reader {\n pos = 0;\n constructor(\n readonly bytes: Buffer,\n readonly label = \"patch\",\n ) {}\n get remaining() {\n return this.bytes.length - this.pos;\n }\n byte() {\n if (!this.remaining) fail(`truncated ${this.label}`);\n return this.bytes[this.pos++]!;\n }\n integer(): number {\n let value = 0;\n for (let count = 0; count < 5; count++) {\n const byte = this.byte();\n value = value * 128 + (byte & 127);\n if (value > UINT32_MAX) fail(`integer exceeds 32-bit limit in ${this.label}`);\n if (!(byte & 128)) return value;\n }\n return fail(`invalid variable-length integer in ${this.label}`);\n }\n take(size: number): Buffer {\n if (size > this.remaining) fail(`truncated ${this.label}: requested ${size} bytes, ${this.remaining} remain`);\n const result = this.bytes.subarray(this.pos, this.pos + size);\n this.pos += size;\n return result;\n }\n}\n\n// RFC 3284 section 5.6: (operation, size, COPY address mode), up to two per opcode.\ninterface Operation {\n type: \"ADD\" | \"RUN\" | \"COPY\";\n size: number;\n mode: number;\n}\nconst table: Operation[][] = [];\nconst op = (type: Operation[\"type\"], size: number, mode = 0): Operation => ({ type, size, mode });\ntable.push([op(\"RUN\", 0)]);\nfor (let size = 0; size <= 17; size++) table.push([op(\"ADD\", size)]);\nfor (let mode = 0; mode <= 8; mode++) {\n table.push([op(\"COPY\", 0, mode)]);\n for (let size = 4; size <= 18; size++) table.push([op(\"COPY\", size, mode)]);\n}\nfor (let mode = 0; mode <= 5; mode++) {\n for (let add = 1; add <= 4; add++) {\n for (let copy = 4; copy <= 6; copy++) table.push([op(\"ADD\", add), op(\"COPY\", copy, mode)]);\n }\n}\nfor (let mode = 6; mode <= 8; mode++) {\n for (let add = 1; add <= 4; add++) table.push([op(\"ADD\", add), op(\"COPY\", 4, mode)]);\n}\nfor (let mode = 0; mode <= 8; mode++) table.push([op(\"COPY\", 4, mode), op(\"ADD\", 1)]);\n\nfunction adler32(bytes: Uint8Array): number {\n let a = 1,\n b = 0;\n for (let start = 0; start < bytes.length; start += 5552) {\n const end = Math.min(start + 5552, bytes.length);\n for (let i = start; i < end; i++) {\n a += bytes[i]!;\n b += a;\n }\n a %= 65521;\n b %= 65521;\n }\n return (b * 65536 + a) >>> 0;\n}\n\nfunction asBuffer(value: Uint8Array, name: string): Buffer {\n if (!(value instanceof Uint8Array)) throw new TypeError(`${name} must be a Buffer or Uint8Array`);\n return Buffer.from(value.buffer, value.byteOffset, value.byteLength);\n}\nfunction limit(value: number | undefined, fallback: number, name: string): number {\n value ??= fallback;\n if (!Number.isSafeInteger(value) || value < 0 || value > UINT32_MAX)\n throw new RangeError(`${name} must be an integer between 0 and ${UINT32_MAX}`);\n return value;\n}\n\n/**\n * @param {Buffer|Uint8Array} source Original bytes.\n * @param {Buffer|Uint8Array} patch VCDIFF bytes.\n * @param {{maxOutputSize?: number, maxWindowSize?: number}} [options]\n * @returns {Buffer} Decoded target, only returned after all windows/checksums validate.\n */\nexport interface XdeltaOptions {\n maxOutputSize?: number;\n maxWindowSize?: number;\n}\n\nexport const decodeXdelta = (source: Uint8Array, patch: Uint8Array, options: XdeltaOptions = {}): Buffer => {\n const sourceBytes = asBuffer(source, \"source\");\n const reader = new Reader(asBuffer(patch, \"patch\"));\n const maxOutput = limit(options.maxOutputSize, 1024 * 1024 * 1024, \"maxOutputSize\");\n const maxWindow = limit(options.maxWindowSize, 16 * 1024 * 1024, \"maxWindowSize\");\n if (!reader.take(3).equals(Buffer.from([0xd6, 0xc3, 0xc4]))) fail(\"invalid file signature\");\n if (reader.byte() !== 0) fail(\"unsupported version (expected 0)\");\n const header = reader.byte();\n if (header & ~7) fail(\"unrecognized header indicator bits\");\n if (header & 1) fail(\"secondary compression is unsupported; recreate the patch using xdelta3 -S none\");\n if (header & 2) fail(\"custom code table is unsupported\");\n if (header & 4) reader.take(reader.integer());\n const windows: Buffer[] = [];\n let total = 0;\n\n while (reader.remaining) {\n const indicator = reader.byte();\n if (indicator & ~7 || (indicator & 3) === 3)\n fail(\"invalid window indicator: source and target are mutually exclusive\");\n let dictionary: Buffer = Buffer.alloc(0);\n if (indicator & 3) {\n const length = reader.integer();\n const offset = reader.integer();\n const available = indicator & 1 ? sourceBytes.length : total;\n if (offset > available || length > available - offset)\n fail(\"source segment exceeds available source/target bounds\");\n if (indicator & 1) dictionary = sourceBytes.subarray(offset, offset + length);\n else {\n dictionary = Buffer.allocUnsafe(length);\n let base = 0;\n for (const previous of windows) {\n const start = Math.max(offset, base),\n end = Math.min(offset + length, base + previous.length);\n if (end > start) previous.copy(dictionary, start - offset, start - base, end - base);\n base += previous.length;\n if (base >= offset + length) break;\n }\n }\n }\n const deltaLength = reader.integer();\n const delta = new Reader(reader.take(deltaLength), \"delta window\");\n const targetLength = delta.integer();\n if (targetLength > maxWindow) fail(`window size ${targetLength} exceeds maximum limit ${maxWindow}`);\n if (targetLength > maxOutput - total) fail(`output size exceeds maximum limit ${maxOutput}`);\n if (dictionary.length + targetLength > UINT32_MAX) fail(\"window address space exceeds 32-bit limit\");\n const compressed = delta.byte();\n if (compressed & ~7) fail(\"unrecognized delta indicator bits\");\n if (compressed) fail(\"secondary compression in delta sections is unsupported\");\n const dataLength = delta.integer(),\n instructionLength = delta.integer(),\n addressLength = delta.integer();\n const checksum = indicator & 4 ? delta.take(4).readUInt32BE() : null;\n const data = new Reader(delta.take(dataLength), \"data section\");\n const instructions = new Reader(delta.take(instructionLength), \"instruction section\");\n const addresses = new Reader(delta.take(addressLength), \"address section\");\n if (delta.remaining) fail(\"delta encoding length does not match section lengths\");\n const target = Buffer.allocUnsafe(targetLength);\n const near = new Uint32Array(4),\n same = new Uint32Array(768);\n let nearIndex = 0,\n position = 0;\n while (instructions.remaining) {\n const operations = table[instructions.byte()]!;\n for (const { type, size: fixedSize, mode } of operations) {\n const size = fixedSize || instructions.integer();\n if (size > targetLength - position) fail(`${type} size exceeds target window length`);\n if (type === \"ADD\") {\n data.take(size).copy(target, position);\n } else if (type === \"RUN\") {\n target.fill(data.byte(), position, position + size);\n } else {\n const here = dictionary.length + position;\n let address;\n if (mode === 0) address = addresses.integer();\n else if (mode === 1) address = here - addresses.integer();\n else if (mode < 6) address = near[mode - 2]! + addresses.integer();\n else address = same[(mode - 6) * 256 + addresses.byte()]!;\n if (address < 0 || address >= here) fail(`COPY address ${address} is outside decoded address space ${here}`);\n near[nearIndex] = address;\n nearIndex = (nearIndex + 1) % near.length;\n same[address % same.length] = address;\n let copied = 0;\n if (address < dictionary.length) {\n copied = Math.min(size, dictionary.length - address);\n dictionary.copy(target, position, address, address + copied);\n }\n // Copy only already available bytes per iteration. Doubling handles overlap\n // (including a copy crossing from dictionary to current target) efficiently.\n const from = Math.max(0, address - dictionary.length);\n while (copied < size) {\n const count = Math.min(size - copied, position + copied - from);\n if (count <= 0) fail(\"COPY references unavailable target bytes\");\n target.copy(target, position + copied, from, from + count);\n copied += count;\n }\n }\n position += size;\n }\n }\n if (position !== targetLength) fail(`target window length mismatch: decoded ${position}, expected ${targetLength}`);\n if (data.remaining || addresses.remaining) fail(\"unused bytes in data/address section\");\n if (checksum !== null && adler32(target) !== checksum) fail(\"Adler32 checksum mismatch\");\n windows.push(target);\n total += targetLength;\n }\n return Buffer.concat(windows, total);\n};\n","import { inflateRawSync } from \"#nitro-runtime\";\n\nimport { crc32 } from \"../nitro/crc\";\n\n/** Resource limits apply to every central-directory entry, including duplicates. */\nexport interface ZipOptions {\n maxUncompressedSize?: number;\n maxEntries?: number;\n}\n\nexport interface ZipEntry {\n path: string;\n data: Buffer;\n}\n\nconst fail = (message: string): never => {\n throw new Error(`ZIP: ${message}`);\n};\nconst utf8 = new TextDecoder(\"utf-8\", { fatal: true });\n// IBM code page 437, byte values 0x80..0xff. ASCII bytes retain their literal values.\nconst cp437 =\n \"ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ \";\n\nfunction decodeUtf8(bytes: Uint8Array): string {\n try {\n return utf8.decode(bytes);\n } catch {\n return fail(\"invalid UTF-8 filename\");\n }\n}\nfunction boundOption(value: number | undefined, fallback: number, label: string): number {\n value ??= fallback;\n if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${label} must be a non-negative safe integer`);\n return value;\n}\nfunction checkRange(bytes: Buffer, offset: number, length: number, end = bytes.length): void {\n if (offset < 0 || offset > end || length > end - offset)\n fail(`truncated or out-of-bounds record at ${offset} (length ${length})`);\n}\nfunction extras(bytes: Buffer): Map<number, Buffer> {\n const fields = new Map<number, Buffer>();\n let offset = 0;\n while (offset < bytes.length) {\n checkRange(bytes, offset, 4);\n const id = bytes.readUInt16LE(offset),\n length = bytes.readUInt16LE(offset + 2);\n offset += 4;\n checkRange(bytes, offset, length);\n if (id === 1) fail(\"ZIP64 archives are unsupported\");\n fields.set(id, bytes.subarray(offset, offset + length));\n offset += length;\n }\n return fields;\n}\nfunction filename(raw: Buffer, flags: number, fields: Map<number, Buffer>): string {\n if (flags & 0x800) return decodeUtf8(raw);\n const unicode = fields.get(0x7075);\n if (unicode && unicode.length >= 5 && unicode[0] === 1 && unicode.readUInt32LE(1) === crc32(raw))\n return decodeUtf8(unicode.subarray(5));\n let result = \"\";\n for (const byte of raw) result += byte < 128 ? String.fromCharCode(byte) : cp437[byte - 128]!;\n return result;\n}\n\n/**\n * Read stored/deflated, single-disk ZIPs entirely in memory. Central-directory\n * sizes support streamed entries with data descriptors. Paths match PatchHelper:\n * backslashes become slashes, names are lowercase, and the final duplicate wins.\n * ZIP64, encryption, split archives and other compression methods fail explicitly.\n * Format reference: https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT\n */\nexport const readZipEntries = (input: Uint8Array, options: ZipOptions = {}): Map<string, ZipEntry> => {\n if (!(input instanceof Uint8Array)) throw new TypeError(\"ZIP input must be a Buffer or Uint8Array\");\n const bytes = Buffer.from(input.buffer, input.byteOffset, input.byteLength);\n const maxSize = boundOption(options.maxUncompressedSize, 1024 * 1024 * 1024, \"maxUncompressedSize\");\n const maxEntries = boundOption(options.maxEntries, 100000, \"maxEntries\");\n let eocd = -1;\n for (let offset = bytes.length - 22; offset >= Math.max(0, bytes.length - 65557); offset--) {\n if (bytes.readUInt32LE(offset) === 0x06054b50 && offset + 22 + bytes.readUInt16LE(offset + 20) === bytes.length) {\n eocd = offset;\n break;\n }\n }\n if (eocd < 0) fail(\"end of central directory not found or truncated\");\n const disk = bytes.readUInt16LE(eocd + 4),\n centralDisk = bytes.readUInt16LE(eocd + 6);\n const diskEntries = bytes.readUInt16LE(eocd + 8),\n count = bytes.readUInt16LE(eocd + 10);\n const centralSize = bytes.readUInt32LE(eocd + 12),\n centralOffset = bytes.readUInt32LE(eocd + 16);\n if (\n count === 0xffff ||\n diskEntries === 0xffff ||\n centralSize === 0xffffffff ||\n centralOffset === 0xffffffff ||\n (eocd >= 20 && bytes.readUInt32LE(eocd - 20) === 0x07064b50)\n )\n fail(\"ZIP64 archives are unsupported\");\n if (disk || centralDisk || diskEntries !== count) fail(\"split or multiple-disk archives are unsupported\");\n if (count > maxEntries) fail(`entry count exceeds limit ${maxEntries}`);\n checkRange(bytes, centralOffset, centralSize, eocd);\n if (centralOffset + centralSize !== eocd) fail(\"central directory size/offset mismatch\");\n if (count > Math.floor(centralSize / 46)) fail(\"entry count exceeds central directory size\");\n const result = new Map<string, ZipEntry>();\n const ranges: { start: number; end: number }[] = [];\n let offset = centralOffset,\n totalSize = 0;\n for (let index = 0; index < count; index++) {\n checkRange(bytes, offset, 46, eocd);\n if (bytes.readUInt32LE(offset) !== 0x02014b50) fail(\"invalid central directory signature\");\n const flags = bytes.readUInt16LE(offset + 8),\n method = bytes.readUInt16LE(offset + 10);\n const checksum = bytes.readUInt32LE(offset + 16),\n compressedSize = bytes.readUInt32LE(offset + 20),\n size = bytes.readUInt32LE(offset + 24);\n const nameLength = bytes.readUInt16LE(offset + 28),\n extraLength = bytes.readUInt16LE(offset + 30),\n commentLength = bytes.readUInt16LE(offset + 32);\n const startDisk = bytes.readUInt16LE(offset + 34),\n localOffset = bytes.readUInt32LE(offset + 42);\n if (size === 0xffffffff || compressedSize === 0xffffffff || localOffset === 0xffffffff || startDisk === 0xffff)\n fail(\"ZIP64 entry is unsupported\");\n if (startDisk) fail(\"split or multiple-disk entry is unsupported\");\n if (flags & 0x2041) fail(\"encrypted entries are unsupported\");\n if (flags & ~0x80e) fail(\"unsupported general purpose flags\");\n if (method !== 0 && method !== 8) fail(`unsupported compression method ${method}`);\n if (size > maxSize - totalSize) fail(`uncompressed size exceeds limit ${maxSize}`);\n totalSize += size;\n checkRange(bytes, offset + 46, nameLength + extraLength + commentLength, eocd);\n const rawName = bytes.subarray(offset + 46, offset + 46 + nameLength);\n const fields = extras(bytes.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength));\n const path = filename(rawName, flags, fields).replaceAll(\"\\\\\", \"/\");\n const name = path.toLowerCase();\n offset += 46 + nameLength + extraLength + commentLength;\n\n checkRange(bytes, localOffset, 30, centralOffset);\n if (bytes.readUInt32LE(localOffset) !== 0x04034b50) fail(\"invalid local header signature\");\n if (bytes.readUInt16LE(localOffset + 6) !== flags || bytes.readUInt16LE(localOffset + 8) !== method)\n fail(\"local header flags/compression method mismatch\");\n const localNameLength = bytes.readUInt16LE(localOffset + 26),\n localExtraLength = bytes.readUInt16LE(localOffset + 28);\n const dataOffset = localOffset + 30 + localNameLength + localExtraLength;\n checkRange(bytes, localOffset + 30, localNameLength + localExtraLength, centralOffset);\n if (!rawName.equals(bytes.subarray(localOffset + 30, localOffset + 30 + localNameLength)))\n fail(\"local filename differs from central directory\");\n extras(bytes.subarray(localOffset + 30 + localNameLength, dataOffset));\n if (\n !(flags & 8) &&\n (bytes.readUInt32LE(localOffset + 14) !== checksum ||\n bytes.readUInt32LE(localOffset + 18) !== compressedSize ||\n bytes.readUInt32LE(localOffset + 22) !== size)\n )\n fail(\"local header CRC/size mismatch\");\n checkRange(bytes, dataOffset, compressedSize, centralOffset);\n let dataEnd = dataOffset + compressedSize;\n if (flags & 8) {\n // A descriptor may omit its signature. Use the CRC and both sizes to\n // disambiguate the rare case of an unsigned CRC equal to the signature.\n checkRange(bytes, dataEnd, 12, centralOffset);\n const matches = (at: number): boolean =>\n at + 12 <= centralOffset &&\n bytes.readUInt32LE(at) === checksum &&\n bytes.readUInt32LE(at + 4) === compressedSize &&\n bytes.readUInt32LE(at + 8) === size;\n if (matches(dataEnd)) dataEnd += 12;\n else if (bytes.readUInt32LE(dataEnd) === 0x08074b50 && matches(dataEnd + 4)) dataEnd += 16;\n else fail(\"data descriptor CRC/size mismatch\");\n }\n ranges.push({ start: localOffset, end: dataEnd });\n const compressed = bytes.subarray(dataOffset, dataOffset + compressedSize);\n let data: Buffer;\n if (method === 0) {\n if (compressedSize !== size) fail(\"stored entry compressed/uncompressed size mismatch\");\n data = Buffer.from(compressed);\n } else {\n try {\n data = inflateRawSync(compressed, { maxOutputLength: Math.max(1, size) });\n } catch (error) {\n return fail(\n `cannot inflate entry ${JSON.stringify(name)} within declared size: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n if (data.length !== size) fail(`uncompressed size mismatch for ${JSON.stringify(name)}`);\n if (crc32(data) !== checksum) fail(`CRC32 mismatch for ${JSON.stringify(name)}`);\n if (name && !name.endsWith(\"/\")) result.set(name, { path, data });\n }\n if (offset !== eocd) fail(\"central directory entry count/length mismatch\");\n ranges.sort((a, b) => a.start - b.start);\n for (let i = 1; i < ranges.length; i++)\n if (ranges[i]!.start < ranges[i - 1]!.end) fail(\"overlapping local entry ranges\");\n return result;\n};\n\n/** Read file contents using normalized paths, with the final duplicate winning. */\nexport const readZip = (input: Uint8Array, options: ZipOptions = {}): Map<string, Buffer> =>\n new Map([...readZipEntries(input, options)].map(([name, entry]) => [name, entry.data]));\n\n/** Extract one file using the same normalized, case-insensitive paths as readZip. */\nexport const extractZipEntry = (input: Uint8Array, entryName: string, options: ZipOptions = {}): Buffer => {\n if (typeof entryName !== \"string\" || !entryName) throw new TypeError(\"ZIP entry name must be a non-empty string\");\n const normalized = entryName.replaceAll(\"\\\\\", \"/\").toLowerCase();\n if (normalized.endsWith(\"/\")) fail(`entry ${JSON.stringify(entryName)} is a directory`);\n const entry = readZip(input, options).get(normalized);\n if (entry === undefined) return fail(`entry ${JSON.stringify(entryName)} not found`);\n return entry;\n};\n","import { encode } from \"@limrun/xdelta3-wasm\";\nimport { zipSync } from \"fflate\";\n\nimport { hashDigest } from \"#nitro-runtime\";\n\nimport { decodeXdelta } from \"./xdelta\";\nimport { bytes } from \"../nitro/binary\";\nimport { crc16 } from \"../nitro/crc\";\nimport { decryptSecureArea, encryptSecureArea } from \"../nitro/crypto\";\nimport type { NitroFolder } from \"../nitro/filesystem\";\nimport { Header } from \"../nitro/header\";\nimport { NDSFile } from \"../nitro/nds-file\";\nimport { patchBuffer } from \"../nitro-patch-helper\";\nimport type { PatchMetadata, PatchOptions } from \"../nitro-patch-helper\";\n\nexport interface CreatePatchOptions extends PatchOptions {\n metadata?: Omit<PatchMetadata, \"formatVersion\">;\n readme?: string;\n}\n\nexport interface CreatePatchResult {\n buffer: Uint8Array<ArrayBuffer>;\n changedFiles: number;\n warnings: string[];\n}\n\nconst normalizePath = (path: string): string => path.replaceAll(\"\\\\\", \"/\").toLowerCase();\nconst listFiles = (rom: NDSFile) => new Map(rom.listFiles().map((file) => [normalizePath(file.path), file]));\nconst listDirectories = (folder: NitroFolder, prefix = \"data\"): string[] =>\n [prefix, ...folder.folders.flatMap((child) => listDirectories(child, `${prefix}/${child.name}`))].sort();\nconst getUnnamedFiles = (rom: NDSFile): Buffer[] => {\n const namedIds = new Set(\n rom\n .listFiles()\n .filter((file) => /^(data|overlay)\\//i.test(file.path))\n .map((file) => file.id),\n );\n return rom.fatTable.entries\n .filter((entry) => !namedIds.has(entry.id))\n .map((entry) => rom.original.subarray(entry.offset, entry.offset + entry.size));\n};\n\n// Repacking assigns physical locations, capacities and derived checksums.\nconst normalizeHeader = (rom: NDSFile): Buffer => {\n const header = new Header(rom.getFile(\"header.bin\"));\n header.size = 0x20000;\n for (const field of [\n \"ARM9romOffset\",\n \"ARM9size\",\n \"ARM7romOffset\",\n \"ARM7size\",\n \"FNToffset\",\n \"FNTsize\",\n \"FAToffset\",\n \"FATsize\",\n \"ARM9overlayOffset\",\n \"ARM9overlaySize\",\n \"ARM7overlayOffset\",\n \"ARM7overlaySize\",\n \"bannerOffset\",\n \"secureCRC16\",\n \"ROMsize\",\n \"logoCRC16\",\n \"headerCRC16\",\n ] as const)\n header[field] = 0;\n if (header.unitCode === 2) {\n for (const field of [\n \"ntr_region_end\",\n \"twl_region_start\",\n \"dsi9_rom_offset\",\n \"dsi7_rom_offset\",\n \"digest_ntr_start\",\n \"digest_ntr_size\",\n \"digest_twl_start\",\n \"digest_twl_size\",\n \"sector_hashtable_start\",\n \"sector_hashtable_size\",\n \"block_hashtable_start\",\n \"block_hashtable_size\",\n \"total_rom_size\",\n \"modcrypt1_start\",\n \"modcrypt1_size\",\n \"modcrypt2_start\",\n \"modcrypt2_size\",\n ] as const)\n header[field] = 0;\n for (const field of [\n \"hmac_arm9\",\n \"hmac_arm7\",\n \"hmac_digest_master\",\n \"hmac_icon_title\",\n \"hmac_arm9i\",\n \"hmac_arm7i\",\n \"hmac_arm9_no_secure\",\n \"rsa_signature\",\n ] as const)\n header[field] = Buffer.alloc(header[field].length);\n }\n return header.raw;\n};\n\nconst compareContent = (actual: Buffer, expected: Buffer, path: string, warnings: string[]): void => {\n if (!actual.equals(expected)) warnings.push(`重建内容与修改后的 ROM 不同:${path}`);\n};\n\nconst errorMessage = (reason: unknown): string => (reason instanceof Error ? reason.message : String(reason));\n\nconst parseRom = (input: Buffer, label: string, warnings: string[]): NDSFile => {\n try {\n const rom = new NDSFile(input, { dsiMode: \"complete\" });\n rom.listFiles();\n return rom;\n } catch (reason) {\n try {\n const rom = new NDSFile(input);\n rom.listFiles();\n warnings.push(`${label} 使用常规模式解析:${errorMessage(reason)}`);\n return rom;\n } catch (error) {\n throw new Error(`无法解析${label}:${errorMessage(error)}`, { cause: error });\n }\n }\n};\n\nconst createDelta = async (before: Buffer, after: Buffer): Promise<Buffer> => {\n const chunks: Buffer[] = [];\n async function* targetChunks() {\n yield after;\n }\n for await (const chunk of encode(targetChunks(), {\n size: before.length,\n read(offset, into) {\n const part = before.subarray(offset, offset + into.length);\n into.set(part);\n return part.length;\n },\n }))\n chunks.push(Buffer.from(chunk));\n return Buffer.concat(chunks);\n};\n\nconst verifyContent = (actual: NDSFile, expected: NDSFile, arm9: Buffer, warnings: string[]): void => {\n const actualFiles = listFiles(actual);\n const expectedFiles = listFiles(expected);\n if ([...actualFiles.keys()].sort().join(\"\\n\") !== [...expectedFiles.keys()].sort().join(\"\\n\"))\n warnings.push(\"重建 ROM 的文件路径集合与修改后的 ROM 不同。\");\n const actualDirectories = new Set(listDirectories(actual.data));\n const expectedDirectories = new Set(listDirectories(expected.data));\n for (const path of expectedDirectories)\n if (!actualDirectories.has(path)) {\n if ([...expectedFiles.keys()].some((name) => name.startsWith(normalizePath(`${path}/`))))\n warnings.push(`重建 ROM 缺少目录:${path}`);\n else warnings.push(`新增空目录已忽略:${path}`);\n }\n for (const path of actualDirectories)\n if (!expectedDirectories.has(path)) {\n if ([...actualFiles.keys()].some((name) => name.startsWith(normalizePath(`${path}/`))))\n warnings.push(`重建 ROM 保留了原始目录:${path}`);\n else warnings.push(`已保留原始 ROM 的空目录:${path}`);\n }\n for (const [path, file] of expectedFiles) {\n if ([\"header.bin\", \"fnt.bin\", \"fat.bin\", \"banner.bin\", \"overarm9.bin\", \"overarm7.bin\"].includes(path)) continue;\n if (!actualFiles.has(path)) continue;\n let content = path === \"arm9.bin\" ? Buffer.from(arm9) : expected.getFile(file.path);\n if (path === \"arm9.bin\" && expected.header.unitCode === 2) {\n const plain = expected.header.decrypted ? content : decryptSecureArea(expected.header.gameCode, content);\n plain.writeUInt16LE(crc16(plain.subarray(0x10, 0x800)), 0xe);\n content = expected.header.decrypted ? plain : encryptSecureArea(expected.header.gameCode, plain);\n }\n compareContent(actual.getFile(file.path), content, file.path, warnings);\n }\n compareContent(actual.banner.toBuffer(), expected.banner.toBuffer(), \"banner.bin\", warnings);\n compareContent(normalizeHeader(actual), normalizeHeader(expected), \"header.bin\", warnings);\n compareContent(actual.header.dlp_signature, expected.header.dlp_signature, \"Download Play 签名\", warnings);\n for (const [left, right] of [\n [actual.overlay9Table, expected.overlay9Table],\n [actual.overlay7Table, expected.overlay7Table],\n ]) {\n if (left.entries.length !== right.entries.length) warnings.push(\"重建 ROM 的覆盖模块数量与修改后的 ROM 不同。\");\n for (const entry of right.entries) {\n const other = left.entries.find((item) => item.overlayId === entry.overlayId);\n if (!other) {\n warnings.push(`重建 ROM 缺少覆盖模块:${entry.overlayId}`);\n continue;\n }\n for (const field of [\n \"ramAddress\",\n \"ramSize\",\n \"bssSize\",\n \"staticInitialiserStartAddress\",\n \"staticInitialiserEndAddress\",\n \"reserved\",\n ] as const)\n if (other[field] !== entry[field])\n warnings.push(`重建覆盖模块 ${entry.overlayId} 的 ${field} 与修改后的 ROM 不同。`);\n }\n }\n const actualUnnamed = getUnnamedFiles(actual);\n const expectedUnnamed = getUnnamedFiles(expected);\n if (actualUnnamed.length !== expectedUnnamed.length)\n warnings.push(\"重建 ROM 的未命名 FAT 项数量与修改后的 ROM 不同。\");\n actualUnnamed.forEach((content, index) => {\n if (expectedUnnamed[index]) compareContent(content, expectedUnnamed[index], `未命名 FAT 项 ${index}`, warnings);\n });\n if (actual.header.unitCode === 2)\n for (const [name, check] of Object.entries(actual.verifyDsi()))\n if (check.status === \"invalid\") warnings.push(`重建 ROM 的 DSi 校验结果为 invalid:${name}`);\n};\n\nconst verifyPatch = (\n input: Buffer,\n archive: Uint8Array,\n target: NDSFile,\n arm9: Buffer,\n options: PatchOptions,\n warnings: string[],\n): boolean => {\n let output: Buffer;\n try {\n output = patchBuffer(input, archive, options).buffer;\n } catch (reason) {\n warnings.push(`补丁应用检查未完成:${errorMessage(reason)}`);\n return false;\n }\n try {\n verifyContent(parseRom(output, \"重建 ROM\", warnings), target, arm9, warnings);\n } catch (reason) {\n warnings.push(`补丁数据核对未完成:${errorMessage(reason)}`);\n }\n return true;\n};\n\n/** Create an XZP; differences and application checks are returned as warnings. */\nexport const createPatch = async (\n original: Uint8Array,\n modified: Uint8Array,\n options: CreatePatchOptions = {},\n): Promise<CreatePatchResult> => {\n const input = bytes(original, \"original ROM\");\n const targetBytes = bytes(modified, \"modified ROM\");\n const warnings: string[] = [];\n const source = parseRom(input, \"原始 ROM\", warnings);\n const target = parseRom(targetBytes, \"修改后的 ROM\", warnings);\n let preprocessing = false;\n if (source.header.gameCode !== target.header.gameCode)\n warnings.push(`两个 ROM 的游戏代码不同:${source.header.gameCode} / ${target.header.gameCode}`);\n if (source.header.unitCode !== target.header.unitCode)\n warnings.push(`两个 ROM 的 unitCode 不同:${source.header.unitCode} / ${target.header.unitCode}`);\n if (source.header.ROMversion !== target.header.ROMversion)\n warnings.push(`两个 ROM 的修订版本不同:${source.header.ROMversion} / ${target.header.ROMversion}`);\n if (source.header.gameCode !== target.header.gameCode || source.header.unitCode !== target.header.unitCode)\n preprocessing = true;\n const sourceFiles = listFiles(source);\n const targetFiles = listFiles(target);\n const headerChanged = !normalizeHeader(source).equals(normalizeHeader(target));\n const sourceArm9 = source.getFile(\"arm9.bin\");\n const targetArm9 = target.getFile(\"arm9.bin\");\n const sourcePostData = sourceArm9.subarray(source.header.ARM9size);\n const targetPostData = targetArm9.subarray(target.header.ARM9size);\n if (!sourcePostData.equals(targetPostData)) warnings.push(\"两个 ROM 的 ARM9 尾部记录不同。\");\n for (const [label, rom, entries] of [\n [\"原始 ROM\", source, sourceFiles],\n [\"修改后的 ROM\", target, targetFiles],\n ] as const) {\n const file = entries.get(\"arm9.bin\")!;\n const end = file.offset + file.size;\n if (rom.header.nitrocode && rom.original.subarray(end, end + 4).equals(Buffer.from(\"2106c0de\", \"hex\")))\n warnings.push(`${label} 的 ARM9 含有额外尾部记录。`);\n }\n const targetProgram = targetArm9.subarray(0, target.header.ARM9size);\n const arm9 =\n sourcePostData.length && targetProgram.subarray(-sourcePostData.length).equals(sourcePostData)\n ? targetProgram\n : Buffer.concat([targetProgram, sourcePostData]);\n const removed = [...sourceFiles.keys()].filter((path) => !targetFiles.has(path));\n if (removed.length) {\n warnings.push(`修改后的 ROM 缺少原始 ROM 中的 ${removed.length} 个文件。`);\n preprocessing = true;\n }\n const sourceUnnamed = getUnnamedFiles(source);\n const targetUnnamed = getUnnamedFiles(target);\n if (\n sourceUnnamed.length !== targetUnnamed.length ||\n sourceUnnamed.some((content, index) => !content.equals(targetUnnamed[index] ?? Buffer.alloc(0)))\n ) {\n warnings.push(\"两个 ROM 的未命名 FAT 数据不同。\");\n preprocessing = true;\n }\n const entries: Record<string, Uint8Array> = {};\n let changedFiles = 0;\n for (const [path, file] of targetFiles) {\n if (path === \"fnt.bin\" || path === \"fat.bin\") continue;\n if (path === \"header.bin\" && !headerChanged) continue;\n const before = sourceFiles.has(path) ? source.getFile(file.path) : Buffer.alloc(0);\n const after = path === \"arm9.bin\" ? arm9 : target.getFile(file.path);\n if (sourceFiles.has(path) && before.equals(after)) continue;\n if (!sourceFiles.has(path) && !/^(data|overlay)\\//.test(path) && !/^overarm[97]\\.bin$/.test(path))\n warnings.push(`新增系统文件:${file.path}`);\n try {\n const delta = await createDelta(before, after);\n if (decodeXdelta(before, delta, options).equals(after)) entries[`xdelta/${file.path}`] = delta;\n else {\n warnings.push(`文件差分解码内容不同,已保存完整替换内容:${file.path}`);\n entries[file.path] = after;\n }\n } catch (reason) {\n warnings.push(`文件差分检查未完成,已保存完整替换内容:${file.path}:${errorMessage(reason)}`);\n entries[file.path] = after;\n }\n changedFiles++;\n }\n const metadata: PatchMetadata = { ...options.metadata, formatVersion: 2 };\n const inputMd5 = hashDigest(\"md5\", input).toString(\"hex\");\n entries[\"metadata.json\"] = Buffer.from(JSON.stringify(metadata, null, 2));\n entries[\"md5.txt\"] = Buffer.from(inputMd5);\n entries[\"readme.md\"] = Buffer.from(options.readme ?? \"\");\n let archive = zipSync(entries, { level: 9 });\n if (!preprocessing) preprocessing = !verifyPatch(input, archive, target, arm9, options, warnings);\n if (preprocessing) {\n try {\n const delta = await createDelta(input, targetBytes);\n const formatVersion = target.header.unitCode & 2 && !target.twl?.complete ? 1 : 2;\n archive = zipSync(\n {\n [`preprocessing/${inputMd5}.xdelta`]: delta,\n \"metadata.json\": Buffer.from(JSON.stringify({ ...metadata, formatVersion }, null, 2)),\n \"md5.txt\": entries[\"md5.txt\"],\n \"readme.md\": entries[\"readme.md\"],\n },\n { level: 9 },\n );\n changedFiles = 1;\n warnings.push(\"补丁使用整份 ROM 的预处理差分保存修改。\");\n verifyPatch(input, archive, target, targetArm9, options, warnings);\n } catch (reason) {\n warnings.push(`预处理差分生成未完成,已保留按文件补丁包:${errorMessage(reason)}`);\n }\n }\n return { buffer: new Uint8Array(archive), changedFiles, warnings };\n};\n","// Native TypeScript port of NitroPatchHelper/PatchHelper.cs, GPL-3.0.\nimport { hashDigest, readFile } from \"#nitro-runtime\";\n\nimport { bytes } from \"./nitro/binary\";\nimport { OverlayTable } from \"./nitro/filesystem\";\nimport { NDSFile } from \"./nitro/nds-file\";\nimport type { DsiVerification } from \"./nitro/twl-complete\";\nimport { decodeXdelta } from \"./patch/xdelta\";\nimport { extractZipEntry as extractEntry, readZip, readZipEntries } from \"./patch/zip\";\n\nexport const PatchReturnValue = Object.freeze({\n SUCCESS: \"SUCCESS\",\n MD5_MISMATCH: \"MD5_MISMATCH\",\n} as const);\nexport type PatchReturnValue = (typeof PatchReturnValue)[keyof typeof PatchReturnValue];\n\nexport interface PatchResult {\n /** Generated ROM bytes. Also returned when the MD5 does not match, as in the original helper. */\n buffer: Buffer;\n returnValue: PatchReturnValue;\n /** MD5 of the original input, before preprocessing. */\n inputMd5: string;\n outputMd5: string;\n dsi?: DsiVerification;\n}\n\nexport interface PatchOptions {\n /** Maximum generated ROM/Xdelta target size in bytes, default 1 GiB. */\n maxOutputSize?: number;\n /** Maximum target size of an individual VCDIFF window, default 16 MiB. */\n maxWindowSize?: number;\n /** Maximum total uncompressed ZIP bytes, default 1 GiB. */\n maxUncompressedSize?: number;\n /** Maximum ZIP entry count. */\n maxEntries?: number;\n}\n\nexport interface PatchMetadata {\n formatVersion?: number;\n id?: string;\n author?: string;\n name?: string;\n homepage?: string;\n version?: string;\n isBeta?: boolean;\n}\n\nexport interface PatchReadme {\n format: \"markdown\" | \"plaintext\";\n content: string;\n}\n\nexport interface PatchInfo {\n metadata: PatchMetadata | null;\n readme: PatchReadme | null;\n}\n\nconst md5 = (data: Uint8Array): string => hashDigest(\"md5\", data).toString(\"hex\");\nconst invalidMetadata = (message: string): null => {\n console.warn(`忽略 metadata.json:${message}`);\n return null;\n};\nconst invalidMetadataField = (field: keyof PatchMetadata, message: string): void => {\n console.warn(`忽略 metadata.json 的 ${field}:${message}`);\n};\n\nconst readMetadata = (files: Map<string, Buffer>): PatchMetadata | null => {\n const file = files.get(\"metadata.json\");\n if (!file) return null;\n\n let metadata: unknown;\n try {\n metadata = JSON.parse(new TextDecoder(\"utf-8\", { fatal: true }).decode(file));\n } catch (error) {\n return invalidMetadata(\n `格式错误,可能是因为补丁包已损坏:${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (!metadata || typeof metadata !== \"object\" || Array.isArray(metadata))\n return invalidMetadata(\"根节点必须是对象。\");\n\n const value = metadata as Record<string, unknown>;\n const result: PatchMetadata = {};\n if (value.formatVersion !== undefined) {\n const version = value.formatVersion;\n if (typeof version !== \"number\" || !Number.isSafeInteger(version) || version < 1)\n throw new Error(\"metadata.json formatVersion must be a positive safe integer\");\n if (version !== 1 && version !== 2) throw new Error(`Unsupported XZP format version: ${version}`);\n result.formatVersion = version;\n }\n for (const field of [\"id\", \"author\", \"name\", \"homepage\", \"version\"] as const) {\n const fieldValue = value[field];\n if (fieldValue === undefined) continue;\n if (typeof fieldValue === \"string\") result[field] = fieldValue;\n else invalidMetadataField(field, \"必须是字符串。\");\n }\n if (value.isBeta === undefined) return result;\n if (typeof value.isBeta === \"boolean\") result.isBeta = value.isBeta;\n else invalidMetadataField(\"isBeta\", \"必须是布尔值。\");\n\n return result;\n};\n\nconst readReadme = (files: Map<string, Buffer>): PatchReadme | null => {\n for (const [filename, format] of [\n [\"README.md\", \"markdown\"],\n [\"README.txt\", \"plaintext\"],\n ] as const) {\n const file = files.get(filename.toLowerCase());\n if (!file) continue;\n try {\n return {\n format,\n content: new TextDecoder(\"utf-8\", { fatal: true }).decode(file),\n };\n } catch (error) {\n console.warn(\n `忽略 ${filename}:不是有效的 UTF-8 文本:${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n return null;\n};\n\n/** Read and validate metadata.json from a NitroPatcher ZIP package. */\nexport const readPatchMetadata = (patch: Uint8Array, options: PatchOptions = {}): PatchMetadata | null =>\n readMetadata(readZip(patch, options));\n\n/** Read a root README.md or README.txt from a NitroPatcher ZIP package. */\nexport const readPatchReadme = (patch: Uint8Array, options: PatchOptions = {}): PatchReadme | null =>\n readReadme(readZip(patch, options));\n\n/** Read metadata and README content while parsing the patch package only once. */\nexport const readPatchInfo = (patch: Uint8Array, options: PatchOptions = {}): PatchInfo => {\n const files = readZip(patch, options);\n return { metadata: readMetadata(files), readme: readReadme(files) };\n};\n\n/** Extract one file from a ZIP archive entirely in memory. */\nexport const extractZipEntry = (archive: Uint8Array, entryName: string, options: PatchOptions = {}): Buffer =>\n extractEntry(archive, entryName, options);\n\n/** Read metadata.json from a NitroPatcher ZIP package on disk. */\nexport const readPatchMetadataFile = async (\n patchPath: string,\n options: PatchOptions = {},\n): Promise<PatchMetadata | null> => readPatchMetadata(await readFile(patchPath), options);\n\n/** Read a patch README from disk. */\nexport const readPatchReadmeFile = async (patchPath: string, options: PatchOptions = {}): Promise<PatchReadme | null> =>\n readPatchReadme(await readFile(patchPath), options);\n\n/** Read patch metadata and README content from disk. */\nexport const readPatchInfoFile = async (patchPath: string, options: PatchOptions = {}): Promise<PatchInfo> =>\n readPatchInfo(await readFile(patchPath), options);\n\n/** Apply an existing NitroPatcher ZIP package entirely in memory. */\nexport const patchBuffer = (original: Uint8Array, patch: Uint8Array, options: PatchOptions = {}): PatchResult => {\n const input = bytes(original, \"original ROM\");\n const entries = readZipEntries(patch, options);\n const patches = new Map([...entries].map(([name, entry]) => [name, entry.data]));\n const formatVersion = readMetadata(patches)?.formatVersion ?? 1;\n const inputMd5 = md5(input);\n const preprocessing = patches.get(`preprocessing/${inputMd5}.xdelta`);\n const romOptions = { ...options, dsiMode: formatVersion === 2 ? (\"complete\" as const) : (\"legacy\" as const) };\n const rom = new NDSFile(preprocessing ? decodeXdelta(input, preprocessing, options) : input, romOptions);\n if (formatVersion === 2)\n for (const name of [\"arm9i.bin\", \"arm7i.bin\"]) {\n if ((patches.has(name) || patches.has(`xdelta/${name}`)) && !rom.hasFile(name))\n throw new Error(`${name} requires a complete DSi-enhanced cartridge`);\n }\n let matched = true;\n const md5File = patches.get(\"md5.txt\");\n if (md5File) {\n matched = false;\n for (const line of md5File.toString(\"utf8\").split(\"\\n\")) {\n const expected = line.trim();\n if (!expected || expected.startsWith(\"#\")) continue;\n // Keep the original format contract: exactly 32 characters, case-insensitive comparison.\n if (expected.length !== 32) throw new Error(\"md5.txt 格式错误,可能是因为补丁包已损坏。\");\n if (expected.toLowerCase() === inputMd5) {\n matched = true;\n break;\n }\n }\n }\n for (const { path } of rom.listFiles()) {\n const normalized = path.replaceAll(\"\\\\\", \"/\").toLowerCase();\n const delta = patches.get(`xdelta/${normalized}`);\n const replacement = delta ? decodeXdelta(rom.getFile(path), delta, options) : patches.get(normalized);\n if (replacement) rom.replaceFile(path, replacement);\n }\n if (formatVersion === 2) {\n for (const name of [\"overarm9.bin\", \"overarm7.bin\"])\n if (!rom.hasFile(name)) {\n const delta = patches.get(`xdelta/${name}`);\n const replacement = delta ? decodeXdelta(Buffer.alloc(0), delta, options) : patches.get(name);\n if (replacement) rom.replaceFile(name, replacement);\n }\n const candidates = new Map<string, { path: string; data: Buffer; delta: boolean }>();\n for (const [name, entry] of entries) {\n const delta = name.startsWith(\"xdelta/\");\n const target = delta ? name.slice(7) : name;\n if ((!target.startsWith(\"data/\") && !target.startsWith(\"overlay/\")) || rom.hasFile(target)) continue;\n if (!delta && entries.has(`xdelta/${target}`)) continue;\n candidates.set(target, { path: delta ? entry.path.slice(7) : entry.path, data: entry.data, delta });\n }\n if (candidates.size && (patches.has(\"fnt.bin\") || patches.has(\"xdelta/fnt.bin\")))\n throw new Error(\"Adding files conflicts with an fnt.bin replacement\");\n const overlayTables = new Map<string, OverlayTable>();\n for (const name of [\"overarm9.bin\", \"overarm7.bin\"])\n if (rom.hasFile(name)) {\n const raw = rom.getFile(name);\n overlayTables.set(name, new OverlayTable(raw, 0, raw.length, name === \"overarm9.bin\"));\n }\n const additions = [...candidates.values()].sort((a, b) => Buffer.compare(Buffer.from(a.path), Buffer.from(b.path)));\n for (const entry of additions) {\n const content = entry.delta ? decodeXdelta(Buffer.alloc(0), entry.data, options) : entry.data;\n if (entry.path.toLowerCase().startsWith(\"overlay/\")) {\n const match = /^overlay\\/overlay(7)?_(\\d{4,})\\.bin$/i.exec(entry.path);\n const overlayId = match?.[2];\n const arm9 = !match?.[1];\n if (!overlayId || String(Number(overlayId)).padStart(4, \"0\") !== overlayId)\n throw new Error(`Invalid overlay path: ${entry.path}`);\n const name = arm9 ? \"overarm9.bin\" : \"overarm7.bin\";\n const table = overlayTables.get(name);\n if (!table) throw new Error(`New overlay requires ${name}: ${entry.path}`);\n const metadata = table.entries.find((item) => item.overlayId === Number(overlayId));\n if (!metadata) throw new Error(`New overlay is missing its table entry: ${entry.path}`);\n rom.addOverlay(arm9, metadata, content);\n } else rom.addFile(entry.path, content);\n }\n }\n const buffer = rom.toBuffer();\n return {\n buffer,\n returnValue: matched ? PatchReturnValue.SUCCESS : PatchReturnValue.MD5_MISMATCH,\n inputMd5,\n outputMd5: md5(buffer),\n ...(formatVersion === 2 && rom.header.unitCode === 2 && { dsi: new NDSFile(buffer, romOptions).verifyDsi() }),\n };\n};\n\n/** Read input files and apply the patch. Does not write an output file. */\nexport const patchIt = async (\n originalPath: string,\n patchPath: string,\n options: PatchOptions = {},\n): Promise<PatchResult> => {\n const [original, patch] = await Promise.all([readFile(originalPath), readFile(patchPath)]);\n return patchBuffer(original, patch, options);\n};\n\n/** Namespaced counterpart of the original C# helper, sharing the same typed functions. */\nexport const PatchHelper = Object.freeze({\n extractZipEntry,\n patchBuffer,\n patchIt,\n readPatchInfo,\n readPatchInfoFile,\n readPatchMetadata,\n readPatchMetadataFile,\n readPatchReadme,\n readPatchReadmeFile,\n});\nexport { createPatch } from \"./patch/create\";\nexport type { CreatePatchOptions, CreatePatchResult } from \"./patch/create\";\nexport { decodeXdelta } from \"./patch/xdelta\";\nexport type { XdeltaOptions } from \"./patch/xdelta\";\n"],"mappings":"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","names":["bytes","fail","bytes","offset","entries","filename","extractZipEntry","table"]}
|