nitro-patcher 1.1.0 → 1.2.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.
@@ -338,7 +338,7 @@ var extractZipEntry = (input, entryName, options = {}) => {
338
338
  };
339
339
 
340
340
  // src/patch/create.ts
341
- import { encode } from "@limrun/xdelta3-wasm";
341
+ import { init, xd3_encode_memory, xd3_smatch_cfg } from "@xzonn/xdelta3-wasm";
342
342
  import { zipSync } from "fflate";
343
343
  var normalizePath = (path) => path.replaceAll("\\", "/").toLowerCase();
344
344
  var listFiles = (rom) => new Map(rom.listFiles().map((file) => [normalizePath(file.path), file]));
@@ -428,20 +428,12 @@ var parseRom = (input, label, warnings) => {
428
428
  }
429
429
  };
430
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);
431
+ await init();
432
+ const windowSize = 64 * 1024;
433
+ const maxSize = after.length * 2 + Math.ceil(after.length / windowSize) * 64 + 1024;
434
+ const result = xd3_encode_memory(after, before, maxSize, xd3_smatch_cfg.DEFAULT, windowSize);
435
+ if (result.ret !== 0) throw new Error(`Cannot encode VCDIFF: ${result.str}`);
436
+ return Buffer.from(result.output);
445
437
  };
446
438
  var verifyContent = (actual, expected, arm9, warnings) => {
447
439
  const actualFiles = listFiles(actual);
@@ -581,9 +573,15 @@ var createPatch = async (original, modified, options = {}) => {
581
573
  if (sourceFiles.has(path) && before.equals(after)) continue;
582
574
  if (!sourceFiles.has(path) && !/^(data|overlay)\//.test(path) && !/^overarm[97]\.bin$/.test(path))
583
575
  warnings.push(`\u65B0\u589E\u7CFB\u7EDF\u6587\u4EF6\uFF1A${file.path}`);
576
+ changedFiles++;
577
+ if (!sourceFiles.has(path) || after.length < 128 * 1024) {
578
+ entries[file.path] = after;
579
+ continue;
580
+ }
584
581
  try {
585
582
  const delta = await createDelta(before, after);
586
- if (decodeXdelta(before, delta, options).equals(after)) entries[`xdelta/${file.path}`] = delta;
583
+ if (delta.length > after.length * 0.8) entries[file.path] = after;
584
+ else if (decodeXdelta(before, delta, options).equals(after)) entries[`xdelta/${file.path}`] = delta;
587
585
  else {
588
586
  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
587
  entries[file.path] = after;
@@ -592,7 +590,6 @@ var createPatch = async (original, modified, options = {}) => {
592
590
  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
591
  entries[file.path] = after;
594
592
  }
595
- changedFiles++;
596
593
  }
597
594
  const metadata = { ...options.metadata, formatVersion: 2 };
598
595
  const inputMd5 = hashDigest("md5", input).toString("hex");
@@ -815,4 +812,4 @@ export {
815
812
  patchIt,
816
813
  PatchHelper
817
814
  };
818
- //# sourceMappingURL=chunk-ZQM3LT5L.js.map
815
+ //# sourceMappingURL=chunk-PALHUY7B.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 { init, xd3_encode_memory, xd3_smatch_cfg } from \"@xzonn/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 await init();\n const windowSize = 64 * 1024;\n const maxSize = after.length * 2 + Math.ceil(after.length / windowSize) * 64 + 1024;\n const result = xd3_encode_memory(after, before, maxSize, xd3_smatch_cfg.DEFAULT, windowSize);\n if (result.ret !== 0) throw new Error(`Cannot encode VCDIFF: ${result.str}`);\n return Buffer.from(result.output);\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 changedFiles++;\n if (!sourceFiles.has(path) || after.length < 128 * 1024) {\n entries[file.path] = after;\n continue;\n }\n try {\n const delta = await createDelta(before, after);\n if (delta.length > after.length * 0.8) entries[file.path] = after;\n else 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 }\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"]}
package/dist/cli.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ createPatch,
3
4
  patchBuffer,
4
5
  readPatchInfo
5
- } from "./chunk-ZQM3LT5L.js";
6
+ } from "./chunk-PALHUY7B.js";
6
7
  import {
7
8
  NDSFile
8
9
  } from "./chunk-LK3JXA24.js";
@@ -17,15 +18,17 @@ var help = `nitro-patcher \u2014 NDS ROM \u8865\u4E01\u5DE5\u5177
17
18
  \u7528\u6CD5\uFF1A
18
19
  nitro-patcher [--json] [--dry-run] <\u539F\u59CBROM> <\u8865\u4E01ZIP> [\u8F93\u51FAROM]
19
20
  nitro-patcher patch [--json] [--dry-run] --input <\u539F\u59CBROM> --patch <\u8865\u4E01ZIP> [--output <\u8F93\u51FAROM>]
21
+ nitro-patcher create [--json] [--dry-run] --input <\u539F\u59CBROM> --modified <\u4FEE\u6539\u540EROM> [--output <\u8F93\u51FAXZP>]
20
22
  nitro-patcher extract [--json] --input <ROM> --output <\u76EE\u5F55>
21
23
  nitro-patcher inspect [--json] --input <ROM>
22
24
 
23
25
  --json \u7ED3\u679C\u8F93\u51FA\u4E3A JSON \u683C\u5F0F
24
- --dry-run \u5B8C\u6574\u6267\u884C\u8865\u4E01\u6D41\u7A0B\uFF0C\u4F46\u4E0D\u5199\u5165\u8F93\u51FA ROM
26
+ --dry-run \u5B8C\u6574\u6267\u884C\u8865\u4E01\u6D41\u7A0B\uFF0C\u4F46\u4E0D\u5199\u5165\u8F93\u51FA\u6587\u4EF6
25
27
  --dsi-mode extract/inspect \u7684 DSi \u5904\u7406\u6A21\u5F0F\uFF1Alegacy \u6216 complete
26
28
  -i, --input \u8F93\u5165 ROM \u8DEF\u5F84
27
29
  -p, --patch \u8865\u4E01 ZIP \u8DEF\u5F84
28
- -o, --output \u8F93\u51FA ROM \u6216\u63D0\u53D6\u76EE\u5F55\uFF1B\u8BD5\u8FD0\u884C\u65F6\u53EF\u7701\u7565
30
+ -m, --modified \u4FEE\u6539\u540E\u7684 ROM \u8DEF\u5F84
31
+ -o, --output \u8F93\u51FA ROM\u3001\u8865\u4E01 XZP \u6216\u63D0\u53D6\u76EE\u5F55\uFF1B\u8BD5\u8FD0\u884C\u65F6\u53EF\u7701\u7565
29
32
  -h, --help \u663E\u793A\u5E2E\u52A9
30
33
  -v, --version \u663E\u793A\u7248\u672C`;
31
34
  var sameFile = async (input, output) => {
@@ -137,6 +140,7 @@ var main = async () => {
137
140
  "dsi-mode": { type: "string" },
138
141
  input: { type: "string", short: "i" },
139
142
  patch: { type: "string", short: "p" },
143
+ modified: { type: "string", short: "m" },
140
144
  output: { type: "string", short: "o" }
141
145
  }
142
146
  });
@@ -159,13 +163,16 @@ var main = async () => {
159
163
  dryRun: values["dry-run"] ?? false,
160
164
  input: values.input,
161
165
  patch: values.patch,
166
+ modified: values.modified,
162
167
  output: values.output,
163
168
  dsiMode
164
169
  };
165
170
  const command = positionals.length === 1 ? positionals[0] : void 0;
166
171
  if (positionals.length !== 0 && positionals.length !== 1 && positionals.length !== 3) throw new Error(help);
167
- if (command !== void 0 && command !== "patch" && command !== "extract" && command !== "inspect")
172
+ if (command !== void 0 && command !== "patch" && command !== "create" && command !== "extract" && command !== "inspect")
168
173
  throw new Error(help);
174
+ const creating = command === "create";
175
+ if (!creating && options.modified !== void 0) throw new Error(help);
169
176
  if (command === "extract") {
170
177
  await extractRom(options);
171
178
  return;
@@ -175,33 +182,49 @@ var main = async () => {
175
182
  return;
176
183
  }
177
184
  const dryRun = options.dryRun;
178
- if (options.dsiMode !== void 0) throw new Error("patch selects DSi processing from metadata.json formatVersion");
185
+ if (options.dsiMode !== void 0)
186
+ throw new Error(
187
+ creating ? "create selects DSi processing from the input ROMs" : "patch selects DSi processing from metadata.json formatVersion"
188
+ );
189
+ if (creating && (!options.modified || options.patch !== void 0)) throw new Error(help);
179
190
  const usesLegacyPaths = positionals.length === 3;
180
191
  if (usesLegacyPaths && (values.input !== void 0 || values.patch !== void 0 || values.output !== void 0))
181
192
  throw new Error(help);
182
- if (!usesLegacyPaths && (!values.input || !values.patch || !dryRun && !values.output)) throw new Error(help);
183
- const [original, patch, output] = usesLegacyPaths ? positionals : [values.input, values.patch, values.output];
184
- if (!original || !patch) throw new Error(help);
193
+ if (!usesLegacyPaths && (!values.input || !(creating ? values.modified : values.patch) || !dryRun && !values.output))
194
+ throw new Error(help);
195
+ const [original, secondInput, output] = usesLegacyPaths ? positionals : [values.input, creating ? values.modified : values.patch, values.output];
196
+ if (!original || !secondInput) throw new Error(help);
185
197
  if (!dryRun) {
186
198
  if (!output) throw new Error(help);
187
- if (await sameFile(original, output) || await sameFile(patch, output))
188
- throw new Error("\u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u8986\u76D6\u539F\u59CB ROM \u6216\u8865\u4E01\u5305\u3002");
199
+ if (await sameFile(original, output) || await sameFile(secondInput, output))
200
+ throw new Error(creating ? "\u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u8986\u76D6\u539F\u59CB ROM \u6216\u4FEE\u6539\u540E\u7684 ROM\u3002" : "\u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u8986\u76D6\u539F\u59CB ROM \u6216\u8865\u4E01\u5305\u3002");
189
201
  }
190
- const [originalBytes, patchBytes] = await Promise.all([readFile(original), readFile(patch)]);
191
- const info = readPatchInfo(patchBytes);
192
- const result = patchBuffer(originalBytes, patchBytes);
202
+ const [originalBytes, secondInputBytes] = await Promise.all([readFile(original), readFile(secondInput)]);
203
+ const info = creating ? void 0 : readPatchInfo(secondInputBytes);
204
+ const result = creating ? await createPatch(originalBytes, secondInputBytes) : patchBuffer(originalBytes, secondInputBytes);
193
205
  if (!dryRun) {
194
206
  if (!output) throw new Error(help);
195
207
  const destination = join(await realpath(dirname(resolve(output))), basename(output));
196
208
  const temporary = await mkdtemp(join(dirname(destination), ".nitro-patcher-"));
197
209
  try {
198
- const file = join(temporary, "output.nds");
210
+ const file = join(temporary, creating ? "patch.xzp" : "output.nds");
199
211
  await writeFile(file, result.buffer);
200
212
  await rename(file, destination);
201
213
  } finally {
202
214
  await rm(temporary, { recursive: true, force: true });
203
215
  }
204
216
  }
217
+ if ("changedFiles" in result) {
218
+ const { changedFiles, warnings } = result;
219
+ if (options.json) {
220
+ console.log(JSON.stringify({ changedFiles, warnings, ...dryRun && { dryRun: true } }));
221
+ } else {
222
+ if (dryRun) console.log("\u8BD5\u8FD0\u884C\u5B8C\u6210\uFF0C\u672A\u5199\u5165\u8F93\u51FA\u8865\u4E01\u3002");
223
+ console.log(`\u5DF2\u521B\u5EFA\u8865\u4E01\uFF0C\u5305\u542B ${changedFiles} \u9879\u6587\u4EF6\u4FEE\u6539\u3002`);
224
+ for (const warning of warnings) console.warn(`\u8B66\u544A\uFF1A${safeTerminalText(warning)}`);
225
+ }
226
+ return;
227
+ }
205
228
  const { returnValue, inputMd5, outputMd5 } = result;
206
229
  if (options.json) {
207
230
  console.log(
@@ -217,13 +240,13 @@ var main = async () => {
217
240
  return;
218
241
  }
219
242
  const sections = [];
220
- if (info.metadata) {
243
+ if (info?.metadata) {
221
244
  const metadata = formatMetadata(info.metadata);
222
245
  if (metadata) sections.push(`\u8865\u4E01\u4FE1\u606F\uFF1A
223
246
 
224
247
  ${metadata}`);
225
248
  }
226
- if (info.readme) {
249
+ if (info?.readme) {
227
250
  const content = safeTerminalText(info.readme.content);
228
251
  const rendered = info.readme.format === "markdown" ? render(content, {
229
252
  width: process.stdout.columns ?? 80,
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { render } from \"markdansi\";\nimport { mkdir, mkdtemp, readFile, realpath, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve, sep } from \"node:path\";\nimport { parseArgs } from \"node:util\";\n\nimport { NDSFile } from \"./nitro/nds-file\";\nimport { type PatchMetadata, patchBuffer, readPatchInfo } from \"./nitro-patch-helper\";\n\nconst help = `nitro-patcher — NDS ROM 补丁工具\n\n用法:\n nitro-patcher [--json] [--dry-run] <原始ROM> <补丁ZIP> [输出ROM]\n nitro-patcher patch [--json] [--dry-run] --input <原始ROM> --patch <补丁ZIP> [--output <输出ROM>]\n nitro-patcher extract [--json] --input <ROM> --output <目录>\n nitro-patcher inspect [--json] --input <ROM>\n\n --json 结果输出为 JSON 格式\n --dry-run 完整执行补丁流程,但不写入输出 ROM\n --dsi-mode extract/inspect 的 DSi 处理模式:legacy 或 complete\n -i, --input 输入 ROM 路径\n -p, --patch 补丁 ZIP 路径\n -o, --output 输出 ROM 或提取目录;试运行时可省略\n -h, --help 显示帮助\n -v, --version 显示版本`;\n\ninterface CommandOptions {\n json: boolean;\n dryRun: boolean;\n input: string | undefined;\n patch: string | undefined;\n output: string | undefined;\n dsiMode: \"legacy\" | \"complete\" | undefined;\n}\n\nconst sameFile = async (input: string, output: string): Promise<boolean> => {\n if (resolve(input) === resolve(output)) return true;\n const original = await stat(input);\n try {\n const destination = await stat(output);\n return original.dev === destination.dev && original.ino === destination.ino;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n return false;\n }\n};\n\nconst safeTerminalText = (value: string): string => {\n const characters: string[] = [];\n for (const character of value.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\")) {\n const code = character.codePointAt(0)!;\n if (\n (code < 0x20 && code !== 0x09 && code !== 0x0a) ||\n (code >= 0x7f && code <= 0x9f) ||\n (code >= 0x202a && code <= 0x202e) ||\n (code >= 0x2066 && code <= 0x2069)\n )\n continue;\n characters.push(character);\n }\n return characters.join(\"\");\n};\n\nconst formatMetadata = (metadata: PatchMetadata): string => {\n const labels = {\n author: \"作者\",\n name: \"名称\",\n homepage: \"主页\",\n version: \"版本\",\n } as const;\n const lines: string[] = [];\n for (const field of [\"author\", \"name\", \"homepage\", \"version\"] as const) {\n const value = metadata[field];\n if (value === undefined) continue;\n const suffix = field === \"version\" && metadata.isBeta ? \"(测试版)\" : \"\";\n lines.push(`- ${labels[field]}:${safeTerminalText(value).replaceAll(\"\\n\", \" \")}${suffix}`);\n }\n return lines.join(\"\\n\");\n};\n\nconst extractRom = async (options: CommandOptions): Promise<void> => {\n if (!options.input || !options.output || options.patch !== undefined || options.dryRun) throw new Error(help);\n const destination = join(await realpath(dirname(resolve(options.output))), basename(resolve(options.output)));\n const rom = new NDSFile(await readFile(options.input), { dsiMode: options.dsiMode ?? \"legacy\" });\n const files = rom.listFiles();\n await mkdir(destination, { recursive: true });\n for (const { path } of files) {\n const output = resolve(destination, path);\n if (!output.startsWith(`${destination}${sep}`)) throw new Error(`ROM 文件路径超出输出目录:${path}`);\n await mkdir(dirname(output), { recursive: true });\n await writeFile(output, rom.getFile(path));\n }\n\n if (options.json) {\n console.log(\n JSON.stringify({\n output: destination,\n fileCount: files.length,\n files: files.map(({ path }) => path),\n }),\n );\n } else {\n console.log(`已提取 ${files.length} 个文件到:${destination}`);\n }\n};\n\nconst inspectRom = async (options: CommandOptions): Promise<void> => {\n if (!options.input || options.patch !== undefined || options.output !== undefined || options.dryRun)\n throw new Error(help);\n const input = await readFile(options.input);\n const rom = new NDSFile(input, { dsiMode: options.dsiMode ?? \"legacy\" });\n const info = {\n gameTitle: rom.header.gameTitle.replace(/\\0+$/, \"\"),\n gameCode: rom.header.gameCode,\n size: input.length,\n fileCount: rom.listFiles().length,\n checksums: {\n header: rom.header.headerCRC,\n logo: rom.header.logoCRC,\n secureArea: rom.header.secureCRC,\n banner: rom.banner.bannerCRC,\n },\n ...(options.dsiMode === \"complete\" && { dsi: rom.verifyDsi() }),\n };\n if (options.json) {\n console.log(JSON.stringify(info));\n return;\n }\n console.log(`ROM 信息:\n\n标题:${safeTerminalText(info.gameTitle).replaceAll(\"\\n\", \" \")}\n游戏代码:${safeTerminalText(info.gameCode).replaceAll(\"\\n\", \" \")}\n大小:${info.size} 字节\n文件数量:${info.fileCount}\n校验:\n- Header:${info.checksums.header ? \"通过\" : \"失败\"}\n- Logo:${info.checksums.logo ? \"通过\" : \"失败\"}\n- Secure Area:${info.checksums.secureArea ? \"通过\" : \"失败\"}\n- Banner:${info.checksums.banner ? \"通过\" : \"失败\"}`);\n if (info.dsi)\n for (const [name, result] of Object.entries(info.dsi))\n console.log(`- DSi ${name}:${result.status}${result.reason ? ` (${result.reason})` : \"\"}`);\n};\n\nconst main = async (): Promise<void> => {\n const { values, positionals } = parseArgs({\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n version: { type: \"boolean\", short: \"v\" },\n json: { type: \"boolean\" },\n \"dry-run\": { type: \"boolean\" },\n \"dsi-mode\": { type: \"string\" },\n input: { type: \"string\", short: \"i\" },\n patch: { type: \"string\", short: \"p\" },\n output: { type: \"string\", short: \"o\" },\n },\n });\n if (values.help) {\n process.stdout.write(help);\n return;\n }\n if (values.version) {\n const metadata: unknown = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\"));\n if (!metadata || typeof metadata !== \"object\" || !(\"version\" in metadata) || typeof metadata.version !== \"string\")\n throw new Error(\"Invalid package version metadata\");\n console.log(metadata.version);\n return;\n }\n const dsiMode = values[\"dsi-mode\"];\n if (dsiMode !== undefined && dsiMode !== \"legacy\" && dsiMode !== \"complete\")\n throw new Error(\"Invalid --dsi-mode; expected legacy or complete\");\n const options: CommandOptions = {\n json: values.json ?? false,\n dryRun: values[\"dry-run\"] ?? false,\n input: values.input,\n patch: values.patch,\n output: values.output,\n dsiMode,\n };\n const command = positionals.length === 1 ? positionals[0] : undefined;\n if (positionals.length !== 0 && positionals.length !== 1 && positionals.length !== 3) throw new Error(help);\n if (command !== undefined && command !== \"patch\" && command !== \"extract\" && command !== \"inspect\")\n throw new Error(help);\n if (command === \"extract\") {\n await extractRom(options);\n return;\n }\n if (command === \"inspect\") {\n await inspectRom(options);\n return;\n }\n\n const dryRun = options.dryRun;\n if (options.dsiMode !== undefined) throw new Error(\"patch selects DSi processing from metadata.json formatVersion\");\n const usesLegacyPaths = positionals.length === 3;\n if (usesLegacyPaths && (values.input !== undefined || values.patch !== undefined || values.output !== undefined))\n throw new Error(help);\n if (!usesLegacyPaths && (!values.input || !values.patch || (!dryRun && !values.output))) throw new Error(help);\n const [original, patch, output] = usesLegacyPaths ? positionals : [values.input, values.patch, values.output];\n if (!original || !patch) throw new Error(help);\n if (!dryRun) {\n if (!output) throw new Error(help);\n if ((await sameFile(original, output)) || (await sameFile(patch, output)))\n throw new Error(\"输出路径不能覆盖原始 ROM 或补丁包。\");\n }\n const [originalBytes, patchBytes] = await Promise.all([readFile(original), readFile(patch)]);\n const info = readPatchInfo(patchBytes);\n const result = patchBuffer(originalBytes, patchBytes);\n if (!dryRun) {\n if (!output) throw new Error(help);\n const destination = join(await realpath(dirname(resolve(output))), basename(output));\n const temporary = await mkdtemp(join(dirname(destination), \".nitro-patcher-\"));\n try {\n const file = join(temporary, \"output.nds\");\n await writeFile(file, result.buffer);\n await rename(file, destination);\n } finally {\n await rm(temporary, { recursive: true, force: true });\n }\n }\n const { returnValue, inputMd5, outputMd5 } = result;\n if (options.json) {\n console.log(\n JSON.stringify({\n returnValue,\n inputMd5,\n outputMd5,\n ...info,\n ...(dryRun && { dryRun: true }),\n ...(result.dsi && { dsi: result.dsi }),\n }),\n );\n return;\n }\n\n const sections: string[] = [];\n if (info.metadata) {\n const metadata = formatMetadata(info.metadata);\n if (metadata) sections.push(`补丁信息:\\n\\n${metadata}`);\n }\n if (info.readme) {\n const content = safeTerminalText(info.readme.content);\n const rendered =\n info.readme.format === \"markdown\"\n ? render(content, {\n width: process.stdout.columns ?? 80,\n color: Boolean(process.stdout.isTTY),\n hyperlinks: false,\n })\n : content;\n sections.push(`补丁说明:\\n\\n${rendered.trim()}`);\n }\n if (dryRun) sections.push(\"试运行完成,未写入输出 ROM。\");\n sections.push(`${returnValue}\\n\\n原始 ROM 的 MD5:${inputMd5}\\n生成 ROM 的 MD5:${outputMd5}`);\n console.log(sections.join(\"\\n\\n\"));\n};\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { render } from \"markdansi\";\nimport { mkdir, mkdtemp, readFile, realpath, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve, sep } from \"node:path\";\nimport { parseArgs } from \"node:util\";\n\nimport { NDSFile } from \"./nitro/nds-file\";\nimport { type PatchMetadata, createPatch, patchBuffer, readPatchInfo } from \"./nitro-patch-helper\";\n\nconst help = `nitro-patcher — NDS ROM 补丁工具\n\n用法:\n nitro-patcher [--json] [--dry-run] <原始ROM> <补丁ZIP> [输出ROM]\n nitro-patcher patch [--json] [--dry-run] --input <原始ROM> --patch <补丁ZIP> [--output <输出ROM>]\n nitro-patcher create [--json] [--dry-run] --input <原始ROM> --modified <修改后ROM> [--output <输出XZP>]\n nitro-patcher extract [--json] --input <ROM> --output <目录>\n nitro-patcher inspect [--json] --input <ROM>\n\n --json 结果输出为 JSON 格式\n --dry-run 完整执行补丁流程,但不写入输出文件\n --dsi-mode extract/inspect 的 DSi 处理模式:legacy 或 complete\n -i, --input 输入 ROM 路径\n -p, --patch 补丁 ZIP 路径\n -m, --modified 修改后的 ROM 路径\n -o, --output 输出 ROM、补丁 XZP 或提取目录;试运行时可省略\n -h, --help 显示帮助\n -v, --version 显示版本`;\n\ninterface CommandOptions {\n json: boolean;\n dryRun: boolean;\n input: string | undefined;\n patch: string | undefined;\n modified: string | undefined;\n output: string | undefined;\n dsiMode: \"legacy\" | \"complete\" | undefined;\n}\n\nconst sameFile = async (input: string, output: string): Promise<boolean> => {\n if (resolve(input) === resolve(output)) return true;\n const original = await stat(input);\n try {\n const destination = await stat(output);\n return original.dev === destination.dev && original.ino === destination.ino;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n return false;\n }\n};\n\nconst safeTerminalText = (value: string): string => {\n const characters: string[] = [];\n for (const character of value.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\")) {\n const code = character.codePointAt(0)!;\n if (\n (code < 0x20 && code !== 0x09 && code !== 0x0a) ||\n (code >= 0x7f && code <= 0x9f) ||\n (code >= 0x202a && code <= 0x202e) ||\n (code >= 0x2066 && code <= 0x2069)\n )\n continue;\n characters.push(character);\n }\n return characters.join(\"\");\n};\n\nconst formatMetadata = (metadata: PatchMetadata): string => {\n const labels = {\n author: \"作者\",\n name: \"名称\",\n homepage: \"主页\",\n version: \"版本\",\n } as const;\n const lines: string[] = [];\n for (const field of [\"author\", \"name\", \"homepage\", \"version\"] as const) {\n const value = metadata[field];\n if (value === undefined) continue;\n const suffix = field === \"version\" && metadata.isBeta ? \"(测试版)\" : \"\";\n lines.push(`- ${labels[field]}:${safeTerminalText(value).replaceAll(\"\\n\", \" \")}${suffix}`);\n }\n return lines.join(\"\\n\");\n};\n\nconst extractRom = async (options: CommandOptions): Promise<void> => {\n if (!options.input || !options.output || options.patch !== undefined || options.dryRun) throw new Error(help);\n const destination = join(await realpath(dirname(resolve(options.output))), basename(resolve(options.output)));\n const rom = new NDSFile(await readFile(options.input), { dsiMode: options.dsiMode ?? \"legacy\" });\n const files = rom.listFiles();\n await mkdir(destination, { recursive: true });\n for (const { path } of files) {\n const output = resolve(destination, path);\n if (!output.startsWith(`${destination}${sep}`)) throw new Error(`ROM 文件路径超出输出目录:${path}`);\n await mkdir(dirname(output), { recursive: true });\n await writeFile(output, rom.getFile(path));\n }\n\n if (options.json) {\n console.log(\n JSON.stringify({\n output: destination,\n fileCount: files.length,\n files: files.map(({ path }) => path),\n }),\n );\n } else {\n console.log(`已提取 ${files.length} 个文件到:${destination}`);\n }\n};\n\nconst inspectRom = async (options: CommandOptions): Promise<void> => {\n if (!options.input || options.patch !== undefined || options.output !== undefined || options.dryRun)\n throw new Error(help);\n const input = await readFile(options.input);\n const rom = new NDSFile(input, { dsiMode: options.dsiMode ?? \"legacy\" });\n const info = {\n gameTitle: rom.header.gameTitle.replace(/\\0+$/, \"\"),\n gameCode: rom.header.gameCode,\n size: input.length,\n fileCount: rom.listFiles().length,\n checksums: {\n header: rom.header.headerCRC,\n logo: rom.header.logoCRC,\n secureArea: rom.header.secureCRC,\n banner: rom.banner.bannerCRC,\n },\n ...(options.dsiMode === \"complete\" && { dsi: rom.verifyDsi() }),\n };\n if (options.json) {\n console.log(JSON.stringify(info));\n return;\n }\n console.log(`ROM 信息:\n\n标题:${safeTerminalText(info.gameTitle).replaceAll(\"\\n\", \" \")}\n游戏代码:${safeTerminalText(info.gameCode).replaceAll(\"\\n\", \" \")}\n大小:${info.size} 字节\n文件数量:${info.fileCount}\n校验:\n- Header:${info.checksums.header ? \"通过\" : \"失败\"}\n- Logo:${info.checksums.logo ? \"通过\" : \"失败\"}\n- Secure Area:${info.checksums.secureArea ? \"通过\" : \"失败\"}\n- Banner:${info.checksums.banner ? \"通过\" : \"失败\"}`);\n if (info.dsi)\n for (const [name, result] of Object.entries(info.dsi))\n console.log(`- DSi ${name}:${result.status}${result.reason ? ` (${result.reason})` : \"\"}`);\n};\n\nconst main = async (): Promise<void> => {\n const { values, positionals } = parseArgs({\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n version: { type: \"boolean\", short: \"v\" },\n json: { type: \"boolean\" },\n \"dry-run\": { type: \"boolean\" },\n \"dsi-mode\": { type: \"string\" },\n input: { type: \"string\", short: \"i\" },\n patch: { type: \"string\", short: \"p\" },\n modified: { type: \"string\", short: \"m\" },\n output: { type: \"string\", short: \"o\" },\n },\n });\n if (values.help) {\n process.stdout.write(help);\n return;\n }\n if (values.version) {\n const metadata: unknown = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\"));\n if (!metadata || typeof metadata !== \"object\" || !(\"version\" in metadata) || typeof metadata.version !== \"string\")\n throw new Error(\"Invalid package version metadata\");\n console.log(metadata.version);\n return;\n }\n const dsiMode = values[\"dsi-mode\"];\n if (dsiMode !== undefined && dsiMode !== \"legacy\" && dsiMode !== \"complete\")\n throw new Error(\"Invalid --dsi-mode; expected legacy or complete\");\n const options: CommandOptions = {\n json: values.json ?? false,\n dryRun: values[\"dry-run\"] ?? false,\n input: values.input,\n patch: values.patch,\n modified: values.modified,\n output: values.output,\n dsiMode,\n };\n const command = positionals.length === 1 ? positionals[0] : undefined;\n if (positionals.length !== 0 && positionals.length !== 1 && positionals.length !== 3) throw new Error(help);\n if (\n command !== undefined &&\n command !== \"patch\" &&\n command !== \"create\" &&\n command !== \"extract\" &&\n command !== \"inspect\"\n )\n throw new Error(help);\n const creating = command === \"create\";\n if (!creating && options.modified !== undefined) throw new Error(help);\n if (command === \"extract\") {\n await extractRom(options);\n return;\n }\n if (command === \"inspect\") {\n await inspectRom(options);\n return;\n }\n\n const dryRun = options.dryRun;\n if (options.dsiMode !== undefined)\n throw new Error(\n creating\n ? \"create selects DSi processing from the input ROMs\"\n : \"patch selects DSi processing from metadata.json formatVersion\",\n );\n if (creating && (!options.modified || options.patch !== undefined)) throw new Error(help);\n const usesLegacyPaths = positionals.length === 3;\n if (usesLegacyPaths && (values.input !== undefined || values.patch !== undefined || values.output !== undefined))\n throw new Error(help);\n if (\n !usesLegacyPaths &&\n (!values.input || !(creating ? values.modified : values.patch) || (!dryRun && !values.output))\n )\n throw new Error(help);\n const [original, secondInput, output] = usesLegacyPaths\n ? positionals\n : [values.input, creating ? values.modified : values.patch, values.output];\n if (!original || !secondInput) throw new Error(help);\n if (!dryRun) {\n if (!output) throw new Error(help);\n if ((await sameFile(original, output)) || (await sameFile(secondInput, output)))\n throw new Error(creating ? \"输出路径不能覆盖原始 ROM 或修改后的 ROM。\" : \"输出路径不能覆盖原始 ROM 或补丁包。\");\n }\n const [originalBytes, secondInputBytes] = await Promise.all([readFile(original), readFile(secondInput)]);\n const info = creating ? undefined : readPatchInfo(secondInputBytes);\n const result = creating\n ? await createPatch(originalBytes, secondInputBytes)\n : patchBuffer(originalBytes, secondInputBytes);\n if (!dryRun) {\n if (!output) throw new Error(help);\n const destination = join(await realpath(dirname(resolve(output))), basename(output));\n const temporary = await mkdtemp(join(dirname(destination), \".nitro-patcher-\"));\n try {\n const file = join(temporary, creating ? \"patch.xzp\" : \"output.nds\");\n await writeFile(file, result.buffer);\n await rename(file, destination);\n } finally {\n await rm(temporary, { recursive: true, force: true });\n }\n }\n if (\"changedFiles\" in result) {\n const { changedFiles, warnings } = result;\n if (options.json) {\n console.log(JSON.stringify({ changedFiles, warnings, ...(dryRun && { dryRun: true }) }));\n } else {\n if (dryRun) console.log(\"试运行完成,未写入输出补丁。\");\n console.log(`已创建补丁,包含 ${changedFiles} 项文件修改。`);\n for (const warning of warnings) console.warn(`警告:${safeTerminalText(warning)}`);\n }\n return;\n }\n const { returnValue, inputMd5, outputMd5 } = result;\n if (options.json) {\n console.log(\n JSON.stringify({\n returnValue,\n inputMd5,\n outputMd5,\n ...info,\n ...(dryRun && { dryRun: true }),\n ...(result.dsi && { dsi: result.dsi }),\n }),\n );\n return;\n }\n\n const sections: string[] = [];\n if (info?.metadata) {\n const metadata = formatMetadata(info.metadata);\n if (metadata) sections.push(`补丁信息:\\n\\n${metadata}`);\n }\n if (info?.readme) {\n const content = safeTerminalText(info.readme.content);\n const rendered =\n info.readme.format === \"markdown\"\n ? render(content, {\n width: process.stdout.columns ?? 80,\n color: Boolean(process.stdout.isTTY),\n hyperlinks: false,\n })\n : content;\n sections.push(`补丁说明:\\n\\n${rendered.trim()}`);\n }\n if (dryRun) sections.push(\"试运行完成,未写入输出 ROM。\");\n sections.push(`${returnValue}\\n\\n原始 ROM 的 MD5:${inputMd5}\\n生成 ROM 的 MD5:${outputMd5}`);\n console.log(sections.join(\"\\n\\n\"));\n};\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  readPatchMetadataFile,
14
14
  readPatchReadme,
15
15
  readPatchReadmeFile
16
- } from "./chunk-ZQM3LT5L.js";
16
+ } from "./chunk-PALHUY7B.js";
17
17
  import {
18
18
  Banner,
19
19
  BinaryWriter,
@@ -12,7 +12,7 @@ import {
12
12
  readPatchMetadataFile,
13
13
  readPatchReadme,
14
14
  readPatchReadmeFile
15
- } from "./chunk-ZQM3LT5L.js";
15
+ } from "./chunk-PALHUY7B.js";
16
16
  import "./chunk-LK3JXA24.js";
17
17
  export {
18
18
  PatchHelper,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nitro-patcher",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Native Node.js Nintendo DS ROM reader, repacker and ZIP/Xdelta patcher",
5
5
  "type": "module",
6
6
  "engines": {
@@ -75,11 +75,11 @@
75
75
  "#browser-aes": "@noble/ciphers/aes.js"
76
76
  },
77
77
  "dependencies": {
78
- "@limrun/xdelta3-wasm": "0.2.0",
78
+ "@xzonn/xdelta3-wasm": "1.1.2",
79
79
  "fflate": "0.8.3",
80
80
  "iconv-lite": "0.7.3",
81
81
  "markdansi": "0.3.3",
82
- "node-forge": "1.4.0"
82
+ "node-forge": "1.4.2"
83
83
  },
84
84
  "scripts": {
85
85
  "clean": "tsx scripts/clean.ts",