nitro-patcher 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +22 -0
- package/dist/browser.d.ts +34 -0
- package/dist/browser.js +4616 -0
- package/dist/browser.js.LEGAL.txt +147 -0
- package/dist/browser.js.map +7 -0
- package/dist/chunk-ANN47R34.js +1 -0
- package/dist/chunk-ANN47R34.js.map +1 -0
- package/dist/chunk-GFF67HX4.js +1181 -0
- package/dist/chunk-GFF67HX4.js.map +1 -0
- package/dist/chunk-L7ZM4LLP.js +396 -0
- package/dist/chunk-L7ZM4LLP.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +76 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/nitro-helper.d.ts +315 -0
- package/dist/nitro-helper.js +44 -0
- package/dist/nitro-helper.js.map +1 -0
- package/dist/nitro-patch-helper.d.ts +46 -0
- package/dist/nitro-patch-helper.js +16 -0
- package/dist/nitro-patch-helper.js.map +1 -0
- package/package.json +86 -0
- package/src/browser/buffer.ts +1 -0
- package/src/browser.ts +60 -0
- package/src/cli.ts +76 -0
- package/src/index.ts +2 -0
- package/src/nitro/banner.ts +77 -0
- package/src/nitro/binary.ts +80 -0
- package/src/nitro/blz.ts +163 -0
- package/src/nitro/crc.ts +23 -0
- package/src/nitro/crypto.ts +178 -0
- package/src/nitro/filesystem.ts +183 -0
- package/src/nitro/header.ts +254 -0
- package/src/nitro/nds-file.ts +285 -0
- package/src/nitro/twl.ts +338 -0
- package/src/nitro-helper.ts +14 -0
- package/src/nitro-patch-helper.ts +95 -0
- package/src/patch/xdelta.ts +214 -0
- package/src/patch/zip.ts +206 -0
- package/src/runtime/browser.ts +40 -0
- package/src/runtime/node.ts +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/patch/zip.ts","../src/patch/xdelta.ts","../src/nitro-patch-helper.ts"],"sourcesContent":["import { inflateRawSync } from '#nitro-runtime';\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\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)\n 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 readZip = (input: Uint8Array, options: ZipOptions = {}): Map<string, Buffer> => {\n if (!(input instanceof Uint8Array))\n 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(\n options.maxUncompressedSize,\n 1024 * 1024 * 1024,\n 'maxUncompressedSize',\n );\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 (\n bytes.readUInt32LE(offset) === 0x06054b50 &&\n offset + 22 + bytes.readUInt16LE(offset + 20) === bytes.length\n ) {\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)\n 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, Buffer>();\n const ranges: Array<{ 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 (\n size === 0xffffffff ||\n compressedSize === 0xffffffff ||\n localOffset === 0xffffffff ||\n startDisk === 0xffff\n )\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(\n bytes.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength),\n );\n const name = filename(rawName, flags, fields).replaceAll('\\\\', '/').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 (\n bytes.readUInt16LE(localOffset + 6) !== flags ||\n bytes.readUInt16LE(localOffset + 8) !== method\n )\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, 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","/** 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)\n 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.\ntype Operation = { type: 'ADD' | 'RUN' | 'COPY'; size: number; mode: number };\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 = (\n source: Uint8Array,\n patch: Uint8Array,\n options: XdeltaOptions = {},\n): 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)\n 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)\n 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)\n 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)\n 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)\n 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","// Native TypeScript port of NitroPatchHelper/PatchHelper.cs, GPL-3.0.\nimport { hashDigest, readFile } from '#nitro-runtime';\nimport { bytes } from './nitro/binary';\nimport { NDSFile } from './nitro/nds-file';\nimport { readZip } from './patch/zip';\nimport { decodeXdelta } from './patch/xdelta';\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}\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\nconst md5 = (data: Uint8Array): string => hashDigest('md5', data).toString('hex');\n\n/** Apply an existing NitroPatcher ZIP package entirely in memory. */\nexport const patchBuffer = (\n original: Uint8Array,\n patch: Uint8Array,\n options: PatchOptions = {},\n): PatchResult => {\n const input = bytes(original, 'original ROM');\n const patches = readZip(patch, options);\n const inputMd5 = md5(input);\n const preprocessing = patches.get(`preprocessing/${inputMd5}.xdelta`);\n const rom = new NDSFile(\n preprocessing ? decodeXdelta(input, preprocessing, options) : input,\n options,\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\n ? decodeXdelta(rom.getFile(path), delta, options)\n : patches.get(normalized);\n if (replacement) rom.replaceFile(path, replacement);\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 };\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({ patchBuffer, patchIt });\nexport { decodeXdelta } from './patch/xdelta';\nexport type { XdeltaOptions } from './patch/xdelta';\n"],"mappings":";;;;;;;;;;AASA,IAAM,OAAO,CAAC,YAA2B;AACvC,QAAM,IAAI,MAAM,QAAQ,OAAO,EAAE;AACnC;AACA,IAAM,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC;AAErD,IAAM,QACJ;AAEF,SAAS,WAAWA,QAA2B;AAC7C,MAAI;AACF,WAAO,KAAK,OAAOA,MAAK;AAAA,EAC1B,QAAQ;AACN,WAAO,KAAK,wBAAwB;AAAA,EACtC;AACF;AACA,SAAS,YAAY,OAA2B,UAAkB,OAAuB;AACvF,YAAU;AACV,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ;AAC1C,UAAM,IAAI,WAAW,GAAG,KAAK,sCAAsC;AACrE,SAAO;AACT;AACA,SAAS,WAAWA,QAAe,QAAgB,QAAgB,MAAMA,OAAM,QAAc;AAC3F,MAAI,SAAS,KAAK,SAAS,OAAO,SAAS,MAAM;AAC/C,SAAK,wCAAwC,MAAM,YAAY,MAAM,GAAG;AAC5E;AACA,SAAS,OAAOA,QAAoC;AAClD,QAAM,SAAS,oBAAI,IAAoB;AACvC,MAAI,SAAS;AACb,SAAO,SAASA,OAAM,QAAQ;AAC5B,eAAWA,QAAO,QAAQ,CAAC;AAC3B,UAAM,KAAKA,OAAM,aAAa,MAAM,GAClC,SAASA,OAAM,aAAa,SAAS,CAAC;AACxC,cAAU;AACV,eAAWA,QAAO,QAAQ,MAAM;AAChC,QAAI,OAAO,EAAG,MAAK,gCAAgC;AACnD,WAAO,IAAI,IAAIA,OAAM,SAAS,QAAQ,SAAS,MAAM,CAAC;AACtD,cAAU;AAAA,EACZ;AACA,SAAO;AACT;AACA,SAAS,SAAS,KAAa,OAAe,QAAqC;AACjF,MAAI,QAAQ,KAAO,QAAO,WAAW,GAAG;AACxC,QAAM,UAAU,OAAO,IAAI,KAAM;AACjC,MAAI,WAAW,QAAQ,UAAU,KAAK,QAAQ,CAAC,MAAM,KAAK,QAAQ,aAAa,CAAC,MAAM,MAAM,GAAG;AAC7F,WAAO,WAAW,QAAQ,SAAS,CAAC,CAAC;AACvC,MAAI,SAAS;AACb,aAAW,QAAQ,IAAK,WAAU,OAAO,MAAM,OAAO,aAAa,IAAI,IAAI,MAAM,OAAO,GAAG;AAC3F,SAAO;AACT;AASO,IAAM,UAAU,CAAC,OAAmB,UAAsB,CAAC,MAA2B;AAC3F,MAAI,EAAE,iBAAiB;AACrB,UAAM,IAAI,UAAU,0CAA0C;AAChE,QAAMA,SAAQ,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AAC1E,QAAM,UAAU;AAAA,IACd,QAAQ;AAAA,IACR,OAAO,OAAO;AAAA,IACd;AAAA,EACF;AACA,QAAM,aAAa,YAAY,QAAQ,YAAY,KAAQ,YAAY;AACvE,MAAI,OAAO;AACX,WAASC,UAASD,OAAM,SAAS,IAAIC,WAAU,KAAK,IAAI,GAAGD,OAAM,SAAS,KAAK,GAAGC,WAAU;AAC1F,QACED,OAAM,aAAaC,OAAM,MAAM,aAC/BA,UAAS,KAAKD,OAAM,aAAaC,UAAS,EAAE,MAAMD,OAAM,QACxD;AACA,aAAOC;AACP;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,EAAG,MAAK,iDAAiD;AACpE,QAAM,OAAOD,OAAM,aAAa,OAAO,CAAC,GACtC,cAAcA,OAAM,aAAa,OAAO,CAAC;AAC3C,QAAM,cAAcA,OAAM,aAAa,OAAO,CAAC,GAC7C,QAAQA,OAAM,aAAa,OAAO,EAAE;AACtC,QAAM,cAAcA,OAAM,aAAa,OAAO,EAAE,GAC9C,gBAAgBA,OAAM,aAAa,OAAO,EAAE;AAC9C,MACE,UAAU,SACV,gBAAgB,SAChB,gBAAgB,cAChB,kBAAkB,cACjB,QAAQ,MAAMA,OAAM,aAAa,OAAO,EAAE,MAAM;AAEjD,SAAK,gCAAgC;AACvC,MAAI,QAAQ,eAAe,gBAAgB;AACzC,SAAK,iDAAiD;AACxD,MAAI,QAAQ,WAAY,MAAK,6BAA6B,UAAU,EAAE;AACtE,aAAWA,QAAO,eAAe,aAAa,IAAI;AAClD,MAAI,gBAAgB,gBAAgB,KAAM,MAAK,wCAAwC;AACvF,MAAI,QAAQ,KAAK,MAAM,cAAc,EAAE,EAAG,MAAK,4CAA4C;AAC3F,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,SAAgD,CAAC;AACvD,MAAI,SAAS,eACX,YAAY;AACd,WAAS,QAAQ,GAAG,QAAQ,OAAO,SAAS;AAC1C,eAAWA,QAAO,QAAQ,IAAI,IAAI;AAClC,QAAIA,OAAM,aAAa,MAAM,MAAM,SAAY,MAAK,qCAAqC;AACzF,UAAM,QAAQA,OAAM,aAAa,SAAS,CAAC,GACzC,SAASA,OAAM,aAAa,SAAS,EAAE;AACzC,UAAM,WAAWA,OAAM,aAAa,SAAS,EAAE,GAC7C,iBAAiBA,OAAM,aAAa,SAAS,EAAE,GAC/C,OAAOA,OAAM,aAAa,SAAS,EAAE;AACvC,UAAM,aAAaA,OAAM,aAAa,SAAS,EAAE,GAC/C,cAAcA,OAAM,aAAa,SAAS,EAAE,GAC5C,gBAAgBA,OAAM,aAAa,SAAS,EAAE;AAChD,UAAM,YAAYA,OAAM,aAAa,SAAS,EAAE,GAC9C,cAAcA,OAAM,aAAa,SAAS,EAAE;AAC9C,QACE,SAAS,cACT,mBAAmB,cACnB,gBAAgB,cAChB,cAAc;AAEd,WAAK,4BAA4B;AACnC,QAAI,UAAW,MAAK,6CAA6C;AACjE,QAAI,QAAQ,KAAQ,MAAK,mCAAmC;AAC5D,QAAI,QAAQ,CAAC,KAAO,MAAK,mCAAmC;AAC5D,QAAI,WAAW,KAAK,WAAW,EAAG,MAAK,kCAAkC,MAAM,EAAE;AACjF,QAAI,OAAO,UAAU,UAAW,MAAK,mCAAmC,OAAO,EAAE;AACjF,iBAAa;AACb,eAAWA,QAAO,SAAS,IAAI,aAAa,cAAc,eAAe,IAAI;AAC7E,UAAM,UAAUA,OAAM,SAAS,SAAS,IAAI,SAAS,KAAK,UAAU;AACpE,UAAM,SAAS;AAAA,MACbA,OAAM,SAAS,SAAS,KAAK,YAAY,SAAS,KAAK,aAAa,WAAW;AAAA,IACjF;AACA,UAAM,OAAO,SAAS,SAAS,OAAO,MAAM,EAAE,WAAW,MAAM,GAAG,EAAE,YAAY;AAChF,cAAU,KAAK,aAAa,cAAc;AAE1C,eAAWA,QAAO,aAAa,IAAI,aAAa;AAChD,QAAIA,OAAM,aAAa,WAAW,MAAM,SAAY,MAAK,gCAAgC;AACzF,QACEA,OAAM,aAAa,cAAc,CAAC,MAAM,SACxCA,OAAM,aAAa,cAAc,CAAC,MAAM;AAExC,WAAK,gDAAgD;AACvD,UAAM,kBAAkBA,OAAM,aAAa,cAAc,EAAE,GACzD,mBAAmBA,OAAM,aAAa,cAAc,EAAE;AACxD,UAAM,aAAa,cAAc,KAAK,kBAAkB;AACxD,eAAWA,QAAO,cAAc,IAAI,kBAAkB,kBAAkB,aAAa;AACrF,QAAI,CAAC,QAAQ,OAAOA,OAAM,SAAS,cAAc,IAAI,cAAc,KAAK,eAAe,CAAC;AACtF,WAAK,+CAA+C;AACtD,WAAOA,OAAM,SAAS,cAAc,KAAK,iBAAiB,UAAU,CAAC;AACrE,QACE,EAAE,QAAQ,OACTA,OAAM,aAAa,cAAc,EAAE,MAAM,YACxCA,OAAM,aAAa,cAAc,EAAE,MAAM,kBACzCA,OAAM,aAAa,cAAc,EAAE,MAAM;AAE3C,WAAK,gCAAgC;AACvC,eAAWA,QAAO,YAAY,gBAAgB,aAAa;AAC3D,QAAI,UAAU,aAAa;AAC3B,QAAI,QAAQ,GAAG;AAGb,iBAAWA,QAAO,SAAS,IAAI,aAAa;AAC5C,YAAM,UAAU,CAAC,OACf,KAAK,MAAM,iBACXA,OAAM,aAAa,EAAE,MAAM,YAC3BA,OAAM,aAAa,KAAK,CAAC,MAAM,kBAC/BA,OAAM,aAAa,KAAK,CAAC,MAAM;AACjC,UAAI,QAAQ,OAAO,EAAG,YAAW;AAAA,eACxBA,OAAM,aAAa,OAAO,MAAM,aAAc,QAAQ,UAAU,CAAC,EAAG,YAAW;AAAA,UACnF,MAAK,mCAAmC;AAAA,IAC/C;AACA,WAAO,KAAK,EAAE,OAAO,aAAa,KAAK,QAAQ,CAAC;AAChD,UAAM,aAAaA,OAAM,SAAS,YAAY,aAAa,cAAc;AACzE,QAAI;AACJ,QAAI,WAAW,GAAG;AAChB,UAAI,mBAAmB,KAAM,MAAK,oDAAoD;AACtF,aAAO,OAAO,KAAK,UAAU;AAAA,IAC/B,OAAO;AACL,UAAI;AACF,eAAO,eAAe,YAAY,EAAE,iBAAiB,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAAA,MAC1E,SAAS,OAAO;AACd,eAAO;AAAA,UACL,wBAAwB,KAAK,UAAU,IAAI,CAAC,0BAA0B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QAC9H;AAAA,MACF;AAAA,IACF;AACA,QAAI,KAAK,WAAW,KAAM,MAAK,kCAAkC,KAAK,UAAU,IAAI,CAAC,EAAE;AACvF,QAAI,MAAM,IAAI,MAAM,SAAU,MAAK,sBAAsB,KAAK,UAAU,IAAI,CAAC,EAAE;AAC/E,QAAI,QAAQ,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO,IAAI,MAAM,IAAI;AAAA,EACxD;AACA,MAAI,WAAW,KAAM,MAAK,+CAA+C;AACzE,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACvC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ;AACjC,QAAI,OAAO,CAAC,EAAG,QAAQ,OAAO,IAAI,CAAC,EAAG,IAAK,MAAK,gCAAgC;AAClF,SAAO;AACT;;;ACzMA,IAAM,aAAa;AACnB,IAAME,QAAO,CAAC,YAA2B;AACvC,QAAM,IAAI,MAAM,WAAW,OAAO,EAAE;AACtC;AAEA,IAAM,SAAN,MAAa;AAAA,EAEX,YACWC,QACA,QAAQ,SACjB;AAFS,iBAAAA;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA,EAHX,MAAM;AAAA,EAKN,IAAI,YAAY;AACd,WAAO,KAAK,MAAM,SAAS,KAAK;AAAA,EAClC;AAAA,EACA,OAAO;AACL,QAAI,CAAC,KAAK,UAAW,CAAAD,MAAK,aAAa,KAAK,KAAK,EAAE;AACnD,WAAO,KAAK,MAAM,KAAK,KAAK;AAAA,EAC9B;AAAA,EACA,UAAkB;AAChB,QAAI,QAAQ;AACZ,aAAS,QAAQ,GAAG,QAAQ,GAAG,SAAS;AACtC,YAAM,OAAO,KAAK,KAAK;AACvB,cAAQ,QAAQ,OAAO,OAAO;AAC9B,UAAI,QAAQ,WAAY,CAAAA,MAAK,mCAAmC,KAAK,KAAK,EAAE;AAC5E,UAAI,EAAE,OAAO,KAAM,QAAO;AAAA,IAC5B;AACA,WAAOA,MAAK,sCAAsC,KAAK,KAAK,EAAE;AAAA,EAChE;AAAA,EACA,KAAK,MAAsB;AACzB,QAAI,OAAO,KAAK;AACd,MAAAA,MAAK,aAAa,KAAK,KAAK,eAAe,IAAI,WAAW,KAAK,SAAS,SAAS;AACnF,UAAM,SAAS,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,MAAM,IAAI;AAC5D,SAAK,OAAO;AACZ,WAAO;AAAA,EACT;AACF;AAIA,IAAM,QAAuB,CAAC;AAC9B,IAAM,KAAK,CAAC,MAAyB,MAAc,OAAO,OAAkB,EAAE,MAAM,MAAM,KAAK;AAC/F,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AACzB,SAAS,OAAO,GAAG,QAAQ,IAAI,OAAQ,OAAM,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACnE,SAAS,OAAO,GAAG,QAAQ,GAAG,QAAQ;AACpC,QAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;AAChC,WAAS,OAAO,GAAG,QAAQ,IAAI,OAAQ,OAAM,KAAK,CAAC,GAAG,QAAQ,MAAM,IAAI,CAAC,CAAC;AAC5E;AACA,SAAS,OAAO,GAAG,QAAQ,GAAG,QAAQ;AACpC,WAAS,MAAM,GAAG,OAAO,GAAG,OAAO;AACjC,aAAS,OAAO,GAAG,QAAQ,GAAG,OAAQ,OAAM,KAAK,CAAC,GAAG,OAAO,GAAG,GAAG,GAAG,QAAQ,MAAM,IAAI,CAAC,CAAC;AAAA,EAC3F;AACF;AACA,SAAS,OAAO,GAAG,QAAQ,GAAG,QAAQ;AACpC,WAAS,MAAM,GAAG,OAAO,GAAG,MAAO,OAAM,KAAK,CAAC,GAAG,OAAO,GAAG,GAAG,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;AACrF;AACA,SAAS,OAAO,GAAG,QAAQ,GAAG,OAAQ,OAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;AAEpF,SAAS,QAAQC,QAA2B;AAC1C,MAAI,IAAI,GACN,IAAI;AACN,WAAS,QAAQ,GAAG,QAAQA,OAAM,QAAQ,SAAS,MAAM;AACvD,UAAM,MAAM,KAAK,IAAI,QAAQ,MAAMA,OAAM,MAAM;AAC/C,aAAS,IAAI,OAAO,IAAI,KAAK,KAAK;AAChC,WAAKA,OAAM,CAAC;AACZ,WAAK;AAAA,IACP;AACA,SAAK;AACL,SAAK;AAAA,EACP;AACA,SAAQ,IAAI,QAAQ,MAAO;AAC7B;AAEA,SAAS,SAAS,OAAmB,MAAsB;AACzD,MAAI,EAAE,iBAAiB,YAAa,OAAM,IAAI,UAAU,GAAG,IAAI,iCAAiC;AAChG,SAAO,OAAO,KAAK,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AACrE;AACA,SAAS,MAAM,OAA2B,UAAkB,MAAsB;AAChF,YAAU;AACV,MAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,KAAK,QAAQ;AACvD,UAAM,IAAI,WAAW,GAAG,IAAI,qCAAqC,UAAU,EAAE;AAC/E,SAAO;AACT;AAaO,IAAM,eAAe,CAC1B,QACA,OACA,UAAyB,CAAC,MACf;AACX,QAAM,cAAc,SAAS,QAAQ,QAAQ;AAC7C,QAAM,SAAS,IAAI,OAAO,SAAS,OAAO,OAAO,CAAC;AAClD,QAAM,YAAY,MAAM,QAAQ,eAAe,OAAO,OAAO,MAAM,eAAe;AAClF,QAAM,YAAY,MAAM,QAAQ,eAAe,KAAK,OAAO,MAAM,eAAe;AAChF,MAAI,CAAC,OAAO,KAAK,CAAC,EAAE,OAAO,OAAO,KAAK,CAAC,KAAM,KAAM,GAAI,CAAC,CAAC,EAAG,CAAAD,MAAK,wBAAwB;AAC1F,MAAI,OAAO,KAAK,MAAM,EAAG,CAAAA,MAAK,kCAAkC;AAChE,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,SAAS,CAAC,EAAG,CAAAA,MAAK,oCAAoC;AAC1D,MAAI,SAAS;AACX,IAAAA,MAAK,gFAAgF;AACvF,MAAI,SAAS,EAAG,CAAAA,MAAK,kCAAkC;AACvD,MAAI,SAAS,EAAG,QAAO,KAAK,OAAO,QAAQ,CAAC;AAC5C,QAAM,UAAoB,CAAC;AAC3B,MAAI,QAAQ;AAEZ,SAAO,OAAO,WAAW;AACvB,UAAM,YAAY,OAAO,KAAK;AAC9B,QAAI,YAAY,CAAC,MAAM,YAAY,OAAO;AACxC,MAAAA,MAAK,oEAAoE;AAC3E,QAAI,aAAqB,OAAO,MAAM,CAAC;AACvC,QAAI,YAAY,GAAG;AACjB,YAAM,SAAS,OAAO,QAAQ;AAC9B,YAAM,SAAS,OAAO,QAAQ;AAC9B,YAAM,YAAY,YAAY,IAAI,YAAY,SAAS;AACvD,UAAI,SAAS,aAAa,SAAS,YAAY;AAC7C,QAAAA,MAAK,uDAAuD;AAC9D,UAAI,YAAY,EAAG,cAAa,YAAY,SAAS,QAAQ,SAAS,MAAM;AAAA,WACvE;AACH,qBAAa,OAAO,YAAY,MAAM;AACtC,YAAI,OAAO;AACX,mBAAW,YAAY,SAAS;AAC9B,gBAAM,QAAQ,KAAK,IAAI,QAAQ,IAAI,GACjC,MAAM,KAAK,IAAI,SAAS,QAAQ,OAAO,SAAS,MAAM;AACxD,cAAI,MAAM,MAAO,UAAS,KAAK,YAAY,QAAQ,QAAQ,QAAQ,MAAM,MAAM,IAAI;AACnF,kBAAQ,SAAS;AACjB,cAAI,QAAQ,SAAS,OAAQ;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AACA,UAAM,cAAc,OAAO,QAAQ;AACnC,UAAM,QAAQ,IAAI,OAAO,OAAO,KAAK,WAAW,GAAG,cAAc;AACjE,UAAM,eAAe,MAAM,QAAQ;AACnC,QAAI,eAAe;AACjB,MAAAA,MAAK,eAAe,YAAY,0BAA0B,SAAS,EAAE;AACvE,QAAI,eAAe,YAAY,MAAO,CAAAA,MAAK,qCAAqC,SAAS,EAAE;AAC3F,QAAI,WAAW,SAAS,eAAe;AACrC,MAAAA,MAAK,2CAA2C;AAClD,UAAM,aAAa,MAAM,KAAK;AAC9B,QAAI,aAAa,CAAC,EAAG,CAAAA,MAAK,mCAAmC;AAC7D,QAAI,WAAY,CAAAA,MAAK,wDAAwD;AAC7E,UAAM,aAAa,MAAM,QAAQ,GAC/B,oBAAoB,MAAM,QAAQ,GAClC,gBAAgB,MAAM,QAAQ;AAChC,UAAM,WAAW,YAAY,IAAI,MAAM,KAAK,CAAC,EAAE,aAAa,IAAI;AAChE,UAAM,OAAO,IAAI,OAAO,MAAM,KAAK,UAAU,GAAG,cAAc;AAC9D,UAAM,eAAe,IAAI,OAAO,MAAM,KAAK,iBAAiB,GAAG,qBAAqB;AACpF,UAAM,YAAY,IAAI,OAAO,MAAM,KAAK,aAAa,GAAG,iBAAiB;AACzE,QAAI,MAAM,UAAW,CAAAA,MAAK,sDAAsD;AAChF,UAAM,SAAS,OAAO,YAAY,YAAY;AAC9C,UAAM,OAAO,IAAI,YAAY,CAAC,GAC5B,OAAO,IAAI,YAAY,GAAG;AAC5B,QAAI,YAAY,GACd,WAAW;AACb,WAAO,aAAa,WAAW;AAC7B,YAAM,aAAa,MAAM,aAAa,KAAK,CAAC;AAC5C,iBAAW,EAAE,MAAM,MAAM,WAAW,KAAK,KAAK,YAAY;AACxD,cAAM,OAAO,aAAa,aAAa,QAAQ;AAC/C,YAAI,OAAO,eAAe,SAAU,CAAAA,MAAK,GAAG,IAAI,oCAAoC;AACpF,YAAI,SAAS,OAAO;AAClB,eAAK,KAAK,IAAI,EAAE,KAAK,QAAQ,QAAQ;AAAA,QACvC,WAAW,SAAS,OAAO;AACzB,iBAAO,KAAK,KAAK,KAAK,GAAG,UAAU,WAAW,IAAI;AAAA,QACpD,OAAO;AACL,gBAAM,OAAO,WAAW,SAAS;AACjC,cAAI;AACJ,cAAI,SAAS,EAAG,WAAU,UAAU,QAAQ;AAAA,mBACnC,SAAS,EAAG,WAAU,OAAO,UAAU,QAAQ;AAAA,mBAC/C,OAAO,EAAG,WAAU,KAAK,OAAO,CAAC,IAAK,UAAU,QAAQ;AAAA,cAC5D,WAAU,MAAM,OAAO,KAAK,MAAM,UAAU,KAAK,CAAC;AACvD,cAAI,UAAU,KAAK,WAAW;AAC5B,YAAAA,MAAK,gBAAgB,OAAO,qCAAqC,IAAI,EAAE;AACzE,eAAK,SAAS,IAAI;AAClB,uBAAa,YAAY,KAAK,KAAK;AACnC,eAAK,UAAU,KAAK,MAAM,IAAI;AAC9B,cAAI,SAAS;AACb,cAAI,UAAU,WAAW,QAAQ;AAC/B,qBAAS,KAAK,IAAI,MAAM,WAAW,SAAS,OAAO;AACnD,uBAAW,KAAK,QAAQ,UAAU,SAAS,UAAU,MAAM;AAAA,UAC7D;AAGA,gBAAM,OAAO,KAAK,IAAI,GAAG,UAAU,WAAW,MAAM;AACpD,iBAAO,SAAS,MAAM;AACpB,kBAAM,QAAQ,KAAK,IAAI,OAAO,QAAQ,WAAW,SAAS,IAAI;AAC9D,gBAAI,SAAS,EAAG,CAAAA,MAAK,0CAA0C;AAC/D,mBAAO,KAAK,QAAQ,WAAW,QAAQ,MAAM,OAAO,KAAK;AACzD,sBAAU;AAAA,UACZ;AAAA,QACF;AACA,oBAAY;AAAA,MACd;AAAA,IACF;AACA,QAAI,aAAa;AACf,MAAAA,MAAK,0CAA0C,QAAQ,cAAc,YAAY,EAAE;AACrF,QAAI,KAAK,aAAa,UAAU,UAAW,CAAAA,MAAK,sCAAsC;AACtF,QAAI,aAAa,QAAQ,QAAQ,MAAM,MAAM,SAAU,CAAAA,MAAK,2BAA2B;AACvF,YAAQ,KAAK,MAAM;AACnB,aAAS;AAAA,EACX;AACA,SAAO,OAAO,OAAO,SAAS,KAAK;AACrC;;;AC9MO,IAAM,mBAAmB,OAAO,OAAO;AAAA,EAC5C,SAAS;AAAA,EACT,cAAc;AAChB,CAAU;AAuBV,IAAM,MAAM,CAAC,SAA6B,WAAW,OAAO,IAAI,EAAE,SAAS,KAAK;AAGzE,IAAM,cAAc,CACzB,UACA,OACA,UAAwB,CAAC,MACT;AAChB,QAAM,QAAQ,MAAM,UAAU,cAAc;AAC5C,QAAM,UAAU,QAAQ,OAAO,OAAO;AACtC,QAAM,WAAW,IAAI,KAAK;AAC1B,QAAM,gBAAgB,QAAQ,IAAI,iBAAiB,QAAQ,SAAS;AACpE,QAAM,MAAM,IAAI;AAAA,IACd,gBAAgB,aAAa,OAAO,eAAe,OAAO,IAAI;AAAA,IAC9D;AAAA,EACF;AACA,MAAI,UAAU;AACd,QAAM,UAAU,QAAQ,IAAI,SAAS;AACrC,MAAI,SAAS;AACX,cAAU;AACV,eAAW,QAAQ,QAAQ,SAAS,MAAM,EAAE,MAAM,IAAI,GAAG;AACvD,YAAM,WAAW,KAAK,KAAK;AAC3B,UAAI,CAAC,YAAY,SAAS,WAAW,GAAG,EAAG;AAE3C,UAAI,SAAS,WAAW,GAAI,OAAM,IAAI,MAAM,gHAA2B;AACvE,UAAI,SAAS,YAAY,MAAM,UAAU;AACvC,kBAAU;AACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,aAAW,EAAE,KAAK,KAAK,IAAI,UAAU,GAAG;AACtC,UAAM,aAAa,KAAK,WAAW,MAAM,GAAG,EAAE,YAAY;AAC1D,UAAM,QAAQ,QAAQ,IAAI,UAAU,UAAU,EAAE;AAChD,UAAM,cAAc,QAChB,aAAa,IAAI,QAAQ,IAAI,GAAG,OAAO,OAAO,IAC9C,QAAQ,IAAI,UAAU;AAC1B,QAAI,YAAa,KAAI,YAAY,MAAM,WAAW;AAAA,EACpD;AACA,QAAM,SAAS,IAAI,SAAS;AAC5B,SAAO;AAAA,IACL;AAAA,IACA,aAAa,UAAU,iBAAiB,UAAU,iBAAiB;AAAA,IACnE;AAAA,IACA,WAAW,IAAI,MAAM;AAAA,EACvB;AACF;AAGO,IAAM,UAAU,OACrB,cACA,WACA,UAAwB,CAAC,MACA;AACzB,QAAM,CAAC,UAAU,KAAK,IAAI,MAAM,QAAQ,IAAI,CAAC,SAAS,YAAY,GAAG,SAAS,SAAS,CAAC,CAAC;AACzF,SAAO,YAAY,UAAU,OAAO,OAAO;AAC7C;AAGO,IAAM,cAAc,OAAO,OAAO,EAAE,aAAa,QAAQ,CAAC;","names":["bytes","offset","fail","bytes"]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
patchIt
|
|
4
|
+
} from "./chunk-L7ZM4LLP.js";
|
|
5
|
+
import "./chunk-GFF67HX4.js";
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
import { mkdtemp, readFile, realpath, rename, rm, stat, writeFile } from "fs/promises";
|
|
9
|
+
import { basename, dirname, join, resolve } from "path";
|
|
10
|
+
import { parseArgs } from "util";
|
|
11
|
+
var help = `nitro-patcher \u2014 \u539F\u751F Node.js NDS ROM \u8865\u4E01\u5DE5\u5177
|
|
12
|
+
|
|
13
|
+
\u7528\u6CD5\uFF1Anitro-patcher [--json] <\u539F\u59CBROM> <\u8865\u4E01ZIP> <\u8F93\u51FAROM>
|
|
14
|
+
|
|
15
|
+
--json \u8F93\u51FA JSON \u72B6\u6001\u548C MD5
|
|
16
|
+
-h, --help \u663E\u793A\u5E2E\u52A9
|
|
17
|
+
-v, --version \u663E\u793A\u7248\u672C`;
|
|
18
|
+
var sameFile = async (input, output) => {
|
|
19
|
+
if (resolve(input) === resolve(output)) return true;
|
|
20
|
+
const original = await stat(input);
|
|
21
|
+
try {
|
|
22
|
+
const destination = await stat(output);
|
|
23
|
+
return original.dev === destination.dev && original.ino === destination.ino;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var main = async () => {
|
|
30
|
+
const { values, positionals } = parseArgs({
|
|
31
|
+
allowPositionals: true,
|
|
32
|
+
options: {
|
|
33
|
+
help: { type: "boolean", short: "h" },
|
|
34
|
+
version: { type: "boolean", short: "v" },
|
|
35
|
+
json: { type: "boolean" }
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (values.help) {
|
|
39
|
+
process.stdout.write(help);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (values.version) {
|
|
43
|
+
const metadata = JSON.parse(
|
|
44
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8")
|
|
45
|
+
);
|
|
46
|
+
if (!metadata || typeof metadata !== "object" || !("version" in metadata) || typeof metadata.version !== "string")
|
|
47
|
+
throw new Error("Invalid package version metadata");
|
|
48
|
+
console.log(metadata.version);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (positionals.length !== 3) throw new Error(help);
|
|
52
|
+
const [original, patch, output] = positionals;
|
|
53
|
+
if (await sameFile(original, output) || await sameFile(patch, output))
|
|
54
|
+
throw new Error("\u8F93\u51FA\u8DEF\u5F84\u4E0D\u80FD\u8986\u76D6\u539F\u59CB ROM \u6216\u8865\u4E01\u5305\u3002");
|
|
55
|
+
const result = await patchIt(original, patch);
|
|
56
|
+
const destination = join(await realpath(dirname(resolve(output))), basename(output));
|
|
57
|
+
const temporary = await mkdtemp(join(dirname(destination), ".nitro-patcher-"));
|
|
58
|
+
try {
|
|
59
|
+
const file = join(temporary, "output.nds");
|
|
60
|
+
await writeFile(file, result.buffer);
|
|
61
|
+
await rename(file, destination);
|
|
62
|
+
} finally {
|
|
63
|
+
await rm(temporary, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
const { returnValue, inputMd5, outputMd5 } = result;
|
|
66
|
+
if (values.json) console.log(JSON.stringify({ returnValue, inputMd5, outputMd5 }));
|
|
67
|
+
else console.log(`${returnValue}
|
|
68
|
+
|
|
69
|
+
\u539F\u59CB ROM \u7684 MD5\uFF1A${inputMd5}
|
|
70
|
+
\u751F\u6210 ROM \u7684 MD5\uFF1A${outputMd5}`);
|
|
71
|
+
};
|
|
72
|
+
main().catch((error) => {
|
|
73
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { mkdtemp, readFile, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join, resolve } from 'node:path';\nimport { parseArgs } from 'node:util';\nimport { patchIt } from './nitro-patch-helper';\n\nconst help = `nitro-patcher — 原生 Node.js NDS ROM 补丁工具\n\n用法:nitro-patcher [--json] <原始ROM> <补丁ZIP> <输出ROM>\n\n --json 输出 JSON 状态和 MD5\n -h, --help 显示帮助\n -v, --version 显示版本`;\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 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 },\n });\n if (values.help) {\n process.stdout.write(help);\n return;\n }\n if (values.version) {\n const metadata: unknown = JSON.parse(\n await readFile(new URL('../package.json', import.meta.url), 'utf8'),\n );\n if (\n !metadata ||\n typeof metadata !== 'object' ||\n !('version' in metadata) ||\n typeof metadata.version !== 'string'\n )\n throw new Error('Invalid package version metadata');\n console.log(metadata.version);\n return;\n }\n if (positionals.length !== 3) throw new Error(help);\n const [original, patch, output] = positionals;\n if ((await sameFile(original, output)) || (await sameFile(patch, output)))\n throw new Error('输出路径不能覆盖原始 ROM 或补丁包。');\n const result = await patchIt(original, patch);\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 const { returnValue, inputMd5, outputMd5 } = result;\n if (values.json) console.log(JSON.stringify({ returnValue, inputMd5, outputMd5 }));\n else console.log(`${returnValue}\\n\\n原始 ROM 的 MD5:${inputMd5}\\n生成 ROM 的 MD5:${outputMd5}`);\n};\n\nmain().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;;AACA,SAAS,SAAS,UAAU,UAAU,QAAQ,IAAI,MAAM,iBAAiB;AACzE,SAAS,UAAU,SAAS,MAAM,eAAe;AACjD,SAAS,iBAAiB;AAG1B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQb,IAAM,WAAW,OAAO,OAAe,WAAqC;AAC1E,MAAI,QAAQ,KAAK,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC/C,QAAM,WAAW,MAAM,KAAK,KAAK;AACjC,MAAI;AACF,UAAM,cAAc,MAAM,KAAK,MAAM;AACrC,WAAO,SAAS,QAAQ,YAAY,OAAO,SAAS,QAAQ,YAAY;AAAA,EAC1E,SAAS,OAAO;AACd,QAAI,EAAE,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS,UAAW,OAAM;AACnF,WAAO;AAAA,EACT;AACF;AAEA,IAAM,OAAO,YAA2B;AACtC,QAAM,EAAE,QAAQ,YAAY,IAAI,UAAU;AAAA,IACxC,kBAAkB;AAAA,IAClB,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,WAAW,OAAO,IAAI;AAAA,MACpC,SAAS,EAAE,MAAM,WAAW,OAAO,IAAI;AAAA,MACvC,MAAM,EAAE,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF,CAAC;AACD,MAAI,OAAO,MAAM;AACf,YAAQ,OAAO,MAAM,IAAI;AACzB;AAAA,EACF;AACA,MAAI,OAAO,SAAS;AAClB,UAAM,WAAoB,KAAK;AAAA,MAC7B,MAAM,SAAS,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM;AAAA,IACpE;AACA,QACE,CAAC,YACD,OAAO,aAAa,YACpB,EAAE,aAAa,aACf,OAAO,SAAS,YAAY;AAE5B,YAAM,IAAI,MAAM,kCAAkC;AACpD,YAAQ,IAAI,SAAS,OAAO;AAC5B;AAAA,EACF;AACA,MAAI,YAAY,WAAW,EAAG,OAAM,IAAI,MAAM,IAAI;AAClD,QAAM,CAAC,UAAU,OAAO,MAAM,IAAI;AAClC,MAAK,MAAM,SAAS,UAAU,MAAM,KAAO,MAAM,SAAS,OAAO,MAAM;AACrE,UAAM,IAAI,MAAM,iGAAsB;AACxC,QAAM,SAAS,MAAM,QAAQ,UAAU,KAAK;AAC5C,QAAM,cAAc,KAAK,MAAM,SAAS,QAAQ,QAAQ,MAAM,CAAC,CAAC,GAAG,SAAS,MAAM,CAAC;AACnF,QAAM,YAAY,MAAM,QAAQ,KAAK,QAAQ,WAAW,GAAG,iBAAiB,CAAC;AAC7E,MAAI;AACF,UAAM,OAAO,KAAK,WAAW,YAAY;AACzC,UAAM,UAAU,MAAM,OAAO,MAAM;AACnC,UAAM,OAAO,MAAM,WAAW;AAAA,EAChC,UAAE;AACA,UAAM,GAAG,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACtD;AACA,QAAM,EAAE,aAAa,UAAU,UAAU,IAAI;AAC7C,MAAI,OAAO,KAAM,SAAQ,IAAI,KAAK,UAAU,EAAE,aAAa,UAAU,UAAU,CAAC,CAAC;AAAA,MAC5E,SAAQ,IAAI,GAAG,WAAW;AAAA;AAAA,mCAAoB,QAAQ;AAAA,mCAAkB,SAAS,EAAE;AAC1F;AAEA,KAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,UAAQ,WAAW;AACrB,CAAC;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { Arm9Header, Banner, BinaryWriter, FatEntry, FileAllocationTable, FileNameTable, Header, ModcryptHeader, NDSFile, NitroFile, NitroFolder, OverlayItem, OverlayRange, OverlayTable, RomFileInfo, RomOptions, SignedTwlHeader, TWL, TwlHeader, UnitCode, aes128CtrCrypt, compressArm9, compressBLZ, crc16, crc32, decompressArm9, decompressBLZ, encryptSecureArea, modcryptKey, updateHeaderSignatures } from './nitro-helper.js';
|
|
2
|
+
export { PatchHelper, PatchOptions, PatchResult, PatchReturnValue, XdeltaOptions, decodeXdelta, patchBuffer, patchIt } from './nitro-patch-helper.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "./chunk-ANN47R34.js";
|
|
2
|
+
import {
|
|
3
|
+
PatchHelper,
|
|
4
|
+
PatchReturnValue,
|
|
5
|
+
decodeXdelta,
|
|
6
|
+
patchBuffer,
|
|
7
|
+
patchIt
|
|
8
|
+
} from "./chunk-L7ZM4LLP.js";
|
|
9
|
+
import {
|
|
10
|
+
Banner,
|
|
11
|
+
BinaryWriter,
|
|
12
|
+
FileAllocationTable,
|
|
13
|
+
FileNameTable,
|
|
14
|
+
Header,
|
|
15
|
+
NDSFile,
|
|
16
|
+
OverlayTable,
|
|
17
|
+
TWL,
|
|
18
|
+
UnitCode,
|
|
19
|
+
aes128CtrCrypt,
|
|
20
|
+
compressArm9,
|
|
21
|
+
compressBLZ,
|
|
22
|
+
crc16,
|
|
23
|
+
crc32,
|
|
24
|
+
decompressArm9,
|
|
25
|
+
decompressBLZ,
|
|
26
|
+
encryptSecureArea,
|
|
27
|
+
modcryptKey,
|
|
28
|
+
updateHeaderSignatures
|
|
29
|
+
} from "./chunk-GFF67HX4.js";
|
|
30
|
+
export {
|
|
31
|
+
Banner,
|
|
32
|
+
BinaryWriter,
|
|
33
|
+
FileAllocationTable,
|
|
34
|
+
FileNameTable,
|
|
35
|
+
Header,
|
|
36
|
+
NDSFile,
|
|
37
|
+
OverlayTable,
|
|
38
|
+
PatchHelper,
|
|
39
|
+
PatchReturnValue,
|
|
40
|
+
TWL,
|
|
41
|
+
UnitCode,
|
|
42
|
+
aes128CtrCrypt,
|
|
43
|
+
compressArm9,
|
|
44
|
+
compressBLZ,
|
|
45
|
+
crc16,
|
|
46
|
+
crc32,
|
|
47
|
+
decodeXdelta,
|
|
48
|
+
decompressArm9,
|
|
49
|
+
decompressBLZ,
|
|
50
|
+
encryptSecureArea,
|
|
51
|
+
modcryptKey,
|
|
52
|
+
patchBuffer,
|
|
53
|
+
patchIt,
|
|
54
|
+
updateHeaderSignatures
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
declare const UnitCode: Readonly<{
|
|
2
|
+
NDS: 0;
|
|
3
|
+
NDS_DSi: 2;
|
|
4
|
+
DSi: 3;
|
|
5
|
+
}>;
|
|
6
|
+
declare class Header {
|
|
7
|
+
raw: Buffer;
|
|
8
|
+
trimmedRom: boolean;
|
|
9
|
+
doublePadding: boolean;
|
|
10
|
+
nitrocode: boolean;
|
|
11
|
+
decrypted: boolean;
|
|
12
|
+
headerCRC: boolean;
|
|
13
|
+
logoCRC: boolean;
|
|
14
|
+
secureCRC: boolean;
|
|
15
|
+
dlp_signature: Buffer;
|
|
16
|
+
ARM9romOffset: number;
|
|
17
|
+
ARM9entryAddress: number;
|
|
18
|
+
ARM9ramAddress: number;
|
|
19
|
+
ARM9size: number;
|
|
20
|
+
ARM7romOffset: number;
|
|
21
|
+
ARM7entryAddress: number;
|
|
22
|
+
ARM7ramAddress: number;
|
|
23
|
+
ARM7size: number;
|
|
24
|
+
FNToffset: number;
|
|
25
|
+
FNTsize: number;
|
|
26
|
+
FAToffset: number;
|
|
27
|
+
FATsize: number;
|
|
28
|
+
ARM9overlayOffset: number;
|
|
29
|
+
ARM9overlaySize: number;
|
|
30
|
+
ARM7overlayOffset: number;
|
|
31
|
+
ARM7overlaySize: number;
|
|
32
|
+
flagsRead: number;
|
|
33
|
+
flagsInit: number;
|
|
34
|
+
bannerOffset: number;
|
|
35
|
+
dsi9_rom_offset: number;
|
|
36
|
+
offset_0x1C4: number;
|
|
37
|
+
dsi9_ram_address: number;
|
|
38
|
+
dsi9_size: number;
|
|
39
|
+
dsi7_rom_offset: number;
|
|
40
|
+
offset_0x1D4: number;
|
|
41
|
+
dsi7_ram_address: number;
|
|
42
|
+
dsi7_size: number;
|
|
43
|
+
digest_ntr_start: number;
|
|
44
|
+
digest_ntr_size: number;
|
|
45
|
+
digest_twl_start: number;
|
|
46
|
+
digest_twl_size: number;
|
|
47
|
+
sector_hashtable_start: number;
|
|
48
|
+
sector_hashtable_size: number;
|
|
49
|
+
block_hashtable_start: number;
|
|
50
|
+
block_hashtable_size: number;
|
|
51
|
+
digest_sector_size: number;
|
|
52
|
+
digest_block_sectorcount: number;
|
|
53
|
+
banner_size: number;
|
|
54
|
+
offset_0x20C: number;
|
|
55
|
+
total_rom_size: number;
|
|
56
|
+
offset_0x214: number;
|
|
57
|
+
offset_0x218: number;
|
|
58
|
+
offset_0x21C: number;
|
|
59
|
+
modcrypt1_start: number;
|
|
60
|
+
modcrypt1_size: number;
|
|
61
|
+
modcrypt2_start: number;
|
|
62
|
+
modcrypt2_size: number;
|
|
63
|
+
tid_low: number;
|
|
64
|
+
tid_high: number;
|
|
65
|
+
public_sav_size: number;
|
|
66
|
+
private_sav_size: number;
|
|
67
|
+
unitCode: number;
|
|
68
|
+
encryptionSeed: number;
|
|
69
|
+
twlInternalFlags: number;
|
|
70
|
+
permitsFlags: number;
|
|
71
|
+
ROMversion: number;
|
|
72
|
+
internalFlags: number;
|
|
73
|
+
secureCRC16: number;
|
|
74
|
+
ROMtimeout: number;
|
|
75
|
+
ARM9autoload: number;
|
|
76
|
+
ARM7autoload: number;
|
|
77
|
+
ROMsize: number;
|
|
78
|
+
headerSize: number;
|
|
79
|
+
logoCRC16: number;
|
|
80
|
+
headerCRC16: number;
|
|
81
|
+
debug_romOffset: number;
|
|
82
|
+
debug_size: number;
|
|
83
|
+
debug_ramAddress: number;
|
|
84
|
+
reserved3: number;
|
|
85
|
+
mbk9_wramcnt_setting: number;
|
|
86
|
+
region_flags: number;
|
|
87
|
+
access_control: number;
|
|
88
|
+
scfg_ext_mask: number;
|
|
89
|
+
gameTitle: string;
|
|
90
|
+
gameCode: string;
|
|
91
|
+
makerCode: string;
|
|
92
|
+
reserved: Buffer;
|
|
93
|
+
reserved2: Buffer;
|
|
94
|
+
logo: Buffer;
|
|
95
|
+
appflags: Buffer;
|
|
96
|
+
hmac_arm9: Buffer;
|
|
97
|
+
hmac_arm7: Buffer;
|
|
98
|
+
hmac_digest_master: Buffer;
|
|
99
|
+
hmac_icon_title: Buffer;
|
|
100
|
+
hmac_arm9i: Buffer;
|
|
101
|
+
hmac_arm7i: Buffer;
|
|
102
|
+
hmac_arm9_no_secure: Buffer;
|
|
103
|
+
rsa_signature: Buffer;
|
|
104
|
+
secureDisable: bigint;
|
|
105
|
+
constructor(input: Uint8Array);
|
|
106
|
+
get size(): number;
|
|
107
|
+
set size(value: number);
|
|
108
|
+
toBuffer(): Buffer<ArrayBuffer>;
|
|
109
|
+
toString(): string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
declare class Banner {
|
|
113
|
+
version: number;
|
|
114
|
+
raw: Buffer;
|
|
115
|
+
bannerCRC: boolean;
|
|
116
|
+
tileData: Buffer;
|
|
117
|
+
palette: Buffer;
|
|
118
|
+
japaneseTitle: string;
|
|
119
|
+
englishTitle: string;
|
|
120
|
+
frenchTitle: string;
|
|
121
|
+
germanTitle: string;
|
|
122
|
+
italianTitle: string;
|
|
123
|
+
spanishTitle: string;
|
|
124
|
+
chineseTitle: string;
|
|
125
|
+
koreanTitle: string;
|
|
126
|
+
constructor(input: Uint8Array, offset?: number, hardSize?: number);
|
|
127
|
+
get titleCount(): 8 | 7 | 6;
|
|
128
|
+
getDefSize(hardSize?: number): number;
|
|
129
|
+
toBuffer(): Buffer<ArrayBuffer>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** A file in the original ROM, optionally replaced with new bytes. */
|
|
133
|
+
interface NitroFile {
|
|
134
|
+
name: string;
|
|
135
|
+
id: number;
|
|
136
|
+
offset: number;
|
|
137
|
+
size: number;
|
|
138
|
+
replacement?: Buffer;
|
|
139
|
+
}
|
|
140
|
+
interface NitroFolder {
|
|
141
|
+
name: string;
|
|
142
|
+
id: number;
|
|
143
|
+
files: NitroFile[];
|
|
144
|
+
folders: NitroFolder[];
|
|
145
|
+
}
|
|
146
|
+
interface FatEntry {
|
|
147
|
+
id: number;
|
|
148
|
+
offset: number;
|
|
149
|
+
size: number;
|
|
150
|
+
}
|
|
151
|
+
declare class FileAllocationTable {
|
|
152
|
+
readonly entries: FatEntry[];
|
|
153
|
+
readonly sortedIDs: number[];
|
|
154
|
+
constructor(input: Uint8Array, offset: number, size: number);
|
|
155
|
+
}
|
|
156
|
+
declare class FileNameTable {
|
|
157
|
+
readonly root: NitroFolder;
|
|
158
|
+
constructor(fat: FileAllocationTable, input: Uint8Array, offset: number, size: number);
|
|
159
|
+
static findFile(id: number, folder: NitroFolder): NitroFile | undefined;
|
|
160
|
+
}
|
|
161
|
+
interface OverlayItem {
|
|
162
|
+
overlayId: number;
|
|
163
|
+
ramAddress: number;
|
|
164
|
+
ramSize: number;
|
|
165
|
+
bssSize: number;
|
|
166
|
+
staticInitialiserStartAddress: number;
|
|
167
|
+
staticInitialiserEndAddress: number;
|
|
168
|
+
fileId: number;
|
|
169
|
+
reserved: number;
|
|
170
|
+
}
|
|
171
|
+
declare class OverlayTable {
|
|
172
|
+
readonly isArm9: boolean;
|
|
173
|
+
readonly entries: OverlayItem[];
|
|
174
|
+
constructor(input: Uint8Array, offset: number, size: number, isArm9: boolean);
|
|
175
|
+
readBasicOverlays(fat: FileAllocationTable): NitroFile[];
|
|
176
|
+
toBuffer(): Buffer;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Seekable, bounded in-memory writer; unwritten gaps have MemoryStream's zero fill. */
|
|
180
|
+
declare class BinaryWriter {
|
|
181
|
+
readonly maxSize: number;
|
|
182
|
+
buffer: Buffer;
|
|
183
|
+
position: number;
|
|
184
|
+
length: number;
|
|
185
|
+
constructor(initialSize?: number, maxSize?: number);
|
|
186
|
+
ensure(end: number): void;
|
|
187
|
+
write(data: Uint8Array): void;
|
|
188
|
+
fillTo(end: number, fill?: number): void;
|
|
189
|
+
pad(alignment: number, fill?: number): void;
|
|
190
|
+
toBuffer(): Buffer<ArrayBufferLike>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
interface ModcryptHeader {
|
|
194
|
+
gameCode: string;
|
|
195
|
+
gameTitle: string;
|
|
196
|
+
twlInternalFlags: number;
|
|
197
|
+
appflags?: Buffer;
|
|
198
|
+
hmac_arm9i?: Buffer;
|
|
199
|
+
}
|
|
200
|
+
declare const aes128CtrCrypt: (key: Buffer, counter: Buffer, data: Buffer) => Buffer;
|
|
201
|
+
declare const modcryptKey: (header: ModcryptHeader) => Buffer;
|
|
202
|
+
declare const encryptSecureArea: (gameCode: string, input: Buffer) => Buffer;
|
|
203
|
+
|
|
204
|
+
interface Arm9Header {
|
|
205
|
+
reserved2: Buffer;
|
|
206
|
+
ARM9ramAddress: number;
|
|
207
|
+
ARM9size: number;
|
|
208
|
+
}
|
|
209
|
+
declare const decompressBLZ: (input: Buffer, maxSize?: number) => Buffer;
|
|
210
|
+
declare const compressBLZ: (input: Buffer) => Buffer;
|
|
211
|
+
declare const decompressArm9: (data: Buffer, header: Arm9Header) => {
|
|
212
|
+
compressed: boolean;
|
|
213
|
+
data: Buffer;
|
|
214
|
+
};
|
|
215
|
+
declare const compressArm9: (data: Buffer, header: Arm9Header, postSize?: number) => Buffer;
|
|
216
|
+
|
|
217
|
+
interface TwlHeader extends ModcryptHeader {
|
|
218
|
+
digest_sector_size: number;
|
|
219
|
+
digest_block_sectorcount: number;
|
|
220
|
+
digest_ntr_start: number;
|
|
221
|
+
digest_ntr_size: number;
|
|
222
|
+
digest_twl_start: number;
|
|
223
|
+
digest_twl_size: number;
|
|
224
|
+
sector_hashtable_start: number;
|
|
225
|
+
sector_hashtable_size: number;
|
|
226
|
+
block_hashtable_start: number;
|
|
227
|
+
block_hashtable_size: number;
|
|
228
|
+
dsi9_rom_offset: number;
|
|
229
|
+
dsi9_size: number;
|
|
230
|
+
dsi7_rom_offset: number;
|
|
231
|
+
dsi7_size: number;
|
|
232
|
+
modcrypt1_start: number;
|
|
233
|
+
modcrypt1_size: number;
|
|
234
|
+
modcrypt2_start: number;
|
|
235
|
+
modcrypt2_size: number;
|
|
236
|
+
total_rom_size: number;
|
|
237
|
+
trimmedRom: boolean;
|
|
238
|
+
decrypted: boolean;
|
|
239
|
+
hmac_arm9: Buffer;
|
|
240
|
+
hmac_arm7: Buffer;
|
|
241
|
+
hmac_digest_master: Buffer;
|
|
242
|
+
}
|
|
243
|
+
interface SignedTwlHeader {
|
|
244
|
+
hmac_digest_master: Buffer;
|
|
245
|
+
rsa_signature: Buffer;
|
|
246
|
+
}
|
|
247
|
+
interface OverlayRange {
|
|
248
|
+
offset: number;
|
|
249
|
+
size: number;
|
|
250
|
+
}
|
|
251
|
+
declare class TWL {
|
|
252
|
+
readonly firstDSiHashOffset: number;
|
|
253
|
+
readonly dsiHashSize: number;
|
|
254
|
+
readonly Hashtable1Data: Buffer;
|
|
255
|
+
readonly Hashtable2Data: Buffer;
|
|
256
|
+
readonly Header2Data: Buffer[] | null;
|
|
257
|
+
readonly Overlays9Sha1Hmac: Buffer[];
|
|
258
|
+
readonly twlEncrypted: boolean;
|
|
259
|
+
DSi9Data: Buffer;
|
|
260
|
+
DSi7Data: Buffer;
|
|
261
|
+
constructor(header: TwlHeader, overlays: readonly OverlayRange[], original: Buffer);
|
|
262
|
+
private cryptSections;
|
|
263
|
+
importArm9iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
264
|
+
importArm7iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
265
|
+
private importData;
|
|
266
|
+
updateOverlays9Sha1Hmac(arm9Data: Buffer, header: Arm9Header, overlays: readonly Buffer[]): Buffer;
|
|
267
|
+
writeTo(writer: BinaryWriter, h: TwlHeader): Buffer;
|
|
268
|
+
}
|
|
269
|
+
/** Refresh the digest and keep valid RSA signatures; optional no$gba hash mask. */
|
|
270
|
+
declare const updateHeaderSignatures: (writer: BinaryWriter, header: SignedTwlHeader, originalHeader: Buffer, keepOriginal: boolean) => void;
|
|
271
|
+
|
|
272
|
+
interface RomOptions {
|
|
273
|
+
/** Maximum generated ROM size in bytes. Defaults to 1 GiB. */
|
|
274
|
+
maxOutputSize?: number;
|
|
275
|
+
}
|
|
276
|
+
interface RomFileInfo {
|
|
277
|
+
/** ROM-relative path, including data/, overlay/, or a system filename. */
|
|
278
|
+
path: string;
|
|
279
|
+
id: number;
|
|
280
|
+
offset: number;
|
|
281
|
+
size: number;
|
|
282
|
+
}
|
|
283
|
+
/** Parsed Nintendo DS ROM. Replacements do not change the input buffer. */
|
|
284
|
+
declare class NDSFile {
|
|
285
|
+
readonly original: Buffer;
|
|
286
|
+
readonly header: Header;
|
|
287
|
+
readonly banner: Banner;
|
|
288
|
+
readonly fatTable: FileAllocationTable;
|
|
289
|
+
readonly fntTable: FileNameTable;
|
|
290
|
+
readonly overlay9Table: OverlayTable;
|
|
291
|
+
readonly overlay7Table: OverlayTable;
|
|
292
|
+
readonly root: NitroFolder;
|
|
293
|
+
readonly twl: TWL | undefined;
|
|
294
|
+
readonly warnings: string[];
|
|
295
|
+
private readonly paths;
|
|
296
|
+
private readonly maxOutputSize;
|
|
297
|
+
constructor(input: Uint8Array, options?: RomOptions);
|
|
298
|
+
static fromFile(path: string, options?: RomOptions): Promise<NDSFile>;
|
|
299
|
+
get data(): NitroFolder;
|
|
300
|
+
get overlay(): NitroFolder;
|
|
301
|
+
listFiles(): RomFileInfo[];
|
|
302
|
+
hasFile(path: string): boolean;
|
|
303
|
+
getFile(path: string): Buffer;
|
|
304
|
+
/** Replace an existing file. Adding or deleting filesystem entries is not supported. */
|
|
305
|
+
replaceFile(path: string, content: Uint8Array): void;
|
|
306
|
+
private fileBytes;
|
|
307
|
+
/** Rebuild offsets, allocation table, CRCs and ROM padding. Safe to call repeatedly. */
|
|
308
|
+
toBuffer(): Buffer;
|
|
309
|
+
saveAs(path: string): Promise<void>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
declare const crc16: (data: Uint8Array, initial?: number) => number;
|
|
313
|
+
declare const crc32: (data: Uint8Array) => number;
|
|
314
|
+
|
|
315
|
+
export { type Arm9Header, Banner, BinaryWriter, type FatEntry, FileAllocationTable, FileNameTable, Header, type ModcryptHeader, NDSFile, type NitroFile, type NitroFolder, type OverlayItem, type OverlayRange, OverlayTable, type RomFileInfo, type RomOptions, type SignedTwlHeader, TWL, type TwlHeader, UnitCode, aes128CtrCrypt, compressArm9, compressBLZ, crc16, crc32, decompressArm9, decompressBLZ, encryptSecureArea, modcryptKey, updateHeaderSignatures };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import "./chunk-ANN47R34.js";
|
|
2
|
+
import {
|
|
3
|
+
Banner,
|
|
4
|
+
BinaryWriter,
|
|
5
|
+
FileAllocationTable,
|
|
6
|
+
FileNameTable,
|
|
7
|
+
Header,
|
|
8
|
+
NDSFile,
|
|
9
|
+
OverlayTable,
|
|
10
|
+
TWL,
|
|
11
|
+
UnitCode,
|
|
12
|
+
aes128CtrCrypt,
|
|
13
|
+
compressArm9,
|
|
14
|
+
compressBLZ,
|
|
15
|
+
crc16,
|
|
16
|
+
crc32,
|
|
17
|
+
decompressArm9,
|
|
18
|
+
decompressBLZ,
|
|
19
|
+
encryptSecureArea,
|
|
20
|
+
modcryptKey,
|
|
21
|
+
updateHeaderSignatures
|
|
22
|
+
} from "./chunk-GFF67HX4.js";
|
|
23
|
+
export {
|
|
24
|
+
Banner,
|
|
25
|
+
BinaryWriter,
|
|
26
|
+
FileAllocationTable,
|
|
27
|
+
FileNameTable,
|
|
28
|
+
Header,
|
|
29
|
+
NDSFile,
|
|
30
|
+
OverlayTable,
|
|
31
|
+
TWL,
|
|
32
|
+
UnitCode,
|
|
33
|
+
aes128CtrCrypt,
|
|
34
|
+
compressArm9,
|
|
35
|
+
compressBLZ,
|
|
36
|
+
crc16,
|
|
37
|
+
crc32,
|
|
38
|
+
decompressArm9,
|
|
39
|
+
decompressBLZ,
|
|
40
|
+
encryptSecureArea,
|
|
41
|
+
modcryptKey,
|
|
42
|
+
updateHeaderSignatures
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=nitro-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Buffer|Uint8Array} source Original bytes.
|
|
3
|
+
* @param {Buffer|Uint8Array} patch VCDIFF bytes.
|
|
4
|
+
* @param {{maxOutputSize?: number, maxWindowSize?: number}} [options]
|
|
5
|
+
* @returns {Buffer} Decoded target, only returned after all windows/checksums validate.
|
|
6
|
+
*/
|
|
7
|
+
interface XdeltaOptions {
|
|
8
|
+
maxOutputSize?: number;
|
|
9
|
+
maxWindowSize?: number;
|
|
10
|
+
}
|
|
11
|
+
declare const decodeXdelta: (source: Uint8Array, patch: Uint8Array, options?: XdeltaOptions) => Buffer;
|
|
12
|
+
|
|
13
|
+
declare const PatchReturnValue: Readonly<{
|
|
14
|
+
readonly SUCCESS: "SUCCESS";
|
|
15
|
+
readonly MD5_MISMATCH: "MD5_MISMATCH";
|
|
16
|
+
}>;
|
|
17
|
+
type PatchReturnValue = (typeof PatchReturnValue)[keyof typeof PatchReturnValue];
|
|
18
|
+
interface PatchResult {
|
|
19
|
+
/** Generated ROM bytes. Also returned when the MD5 does not match, as in the original helper. */
|
|
20
|
+
buffer: Buffer;
|
|
21
|
+
returnValue: PatchReturnValue;
|
|
22
|
+
/** MD5 of the original input, before preprocessing. */
|
|
23
|
+
inputMd5: string;
|
|
24
|
+
outputMd5: string;
|
|
25
|
+
}
|
|
26
|
+
interface PatchOptions {
|
|
27
|
+
/** Maximum generated ROM/Xdelta target size in bytes, default 1 GiB. */
|
|
28
|
+
maxOutputSize?: number;
|
|
29
|
+
/** Maximum target size of an individual VCDIFF window, default 16 MiB. */
|
|
30
|
+
maxWindowSize?: number;
|
|
31
|
+
/** Maximum total uncompressed ZIP bytes, default 1 GiB. */
|
|
32
|
+
maxUncompressedSize?: number;
|
|
33
|
+
/** Maximum ZIP entry count. */
|
|
34
|
+
maxEntries?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Apply an existing NitroPatcher ZIP package entirely in memory. */
|
|
37
|
+
declare const patchBuffer: (original: Uint8Array, patch: Uint8Array, options?: PatchOptions) => PatchResult;
|
|
38
|
+
/** Read input files and apply the patch. Does not write an output file. */
|
|
39
|
+
declare const patchIt: (originalPath: string, patchPath: string, options?: PatchOptions) => Promise<PatchResult>;
|
|
40
|
+
/** Namespaced counterpart of the original C# helper, sharing the same typed functions. */
|
|
41
|
+
declare const PatchHelper: Readonly<{
|
|
42
|
+
patchBuffer: (original: Uint8Array, patch: Uint8Array, options?: PatchOptions) => PatchResult;
|
|
43
|
+
patchIt: (originalPath: string, patchPath: string, options?: PatchOptions) => Promise<PatchResult>;
|
|
44
|
+
}>;
|
|
45
|
+
|
|
46
|
+
export { PatchHelper, type PatchOptions, type PatchResult, PatchReturnValue, type XdeltaOptions, decodeXdelta, patchBuffer, patchIt };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PatchHelper,
|
|
3
|
+
PatchReturnValue,
|
|
4
|
+
decodeXdelta,
|
|
5
|
+
patchBuffer,
|
|
6
|
+
patchIt
|
|
7
|
+
} from "./chunk-L7ZM4LLP.js";
|
|
8
|
+
import "./chunk-GFF67HX4.js";
|
|
9
|
+
export {
|
|
10
|
+
PatchHelper,
|
|
11
|
+
PatchReturnValue,
|
|
12
|
+
decodeXdelta,
|
|
13
|
+
patchBuffer,
|
|
14
|
+
patchIt
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=nitro-patch-helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|