nitro-patcher 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +25 -2
- package/dist/browser.js +10507 -78
- package/dist/browser.js.LEGAL.txt +460 -0
- package/dist/browser.js.map +4 -4
- package/dist/{chunk-IGURDH5L.js → chunk-B7NTPBDX.js} +70 -8
- package/dist/chunk-B7NTPBDX.js.map +1 -0
- package/dist/chunk-LK3JXA24.js +2067 -0
- package/dist/chunk-LK3JXA24.js.map +1 -0
- package/dist/cli.js +19 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/nitro-helper.d.ts +43 -134
- package/dist/nitro-helper.js +3 -1
- package/dist/nitro-patch-helper.d.ts +4 -0
- package/dist/nitro-patch-helper.js +2 -2
- package/dist/twl-complete-B2is30H0.d.ts +201 -0
- package/package.json +8 -2
- package/src/browser/buffer.ts +1 -0
- package/src/browser.ts +23 -2
- package/src/cli.ts +15 -2
- package/src/nitro/blz.ts +13 -4
- package/src/nitro/crypto.ts +29 -11
- package/src/nitro/filesystem.ts +88 -5
- package/src/nitro/header.ts +4 -0
- package/src/nitro/nds-file.ts +378 -30
- package/src/nitro/twl-complete.ts +665 -0
- package/src/nitro/twl.ts +53 -19
- package/src/nitro-helper.ts +2 -1
- package/src/nitro-patch-helper.ts +64 -3
- package/src/patch/zip.ts +14 -4
- package/src/runtime/browser.ts +2 -2
- package/src/runtime/node.ts +2 -15
- package/src/runtime/rsa.ts +14 -0
- package/dist/chunk-GLK3K2HY.js +0 -1120
- package/dist/chunk-GLK3K2HY.js.map +0 -1
- package/dist/chunk-IGURDH5L.js.map +0 -1
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 -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}\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));\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);\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 };\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};\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 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 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 };\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 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 }),\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, 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":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { Banner, BinaryWriter, FatEntry, FileAllocationTable, FileNameTable, ModcryptHeader, NDSFile, NitroFile, NitroFolder, OverlayItem, OverlayRange, OverlayTable, RomFileInfo, RomOptions, SignedTwlHeader, TWL, TwlHeader, aes128CtrCrypt, crc16, crc32, decryptSecureArea, encryptSecureArea, modcryptKey, updateHeaderSignatures } from './nitro-helper.js';
|
|
2
|
+
export { A as Arm9Header, D as DsiCheck, a as DsiVerification, H as Header, U as UnitCode, c as compressArm9, b as compressBLZ, d as decompressArm9, e as decompressBLZ } from './twl-complete-B2is30H0.js';
|
|
2
3
|
export { PatchHelper, PatchInfo, PatchMetadata, PatchOptions, PatchReadme, PatchResult, PatchReturnValue, XdeltaOptions, decodeXdelta, extractZipEntry, patchBuffer, patchIt, readPatchInfo, readPatchInfoFile, readPatchMetadata, readPatchMetadataFile, readPatchReadme, readPatchReadmeFile } from './nitro-patch-helper.js';
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readPatchMetadataFile,
|
|
13
13
|
readPatchReadme,
|
|
14
14
|
readPatchReadmeFile
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-B7NTPBDX.js";
|
|
16
16
|
import {
|
|
17
17
|
Banner,
|
|
18
18
|
BinaryWriter,
|
|
@@ -30,10 +30,11 @@ import {
|
|
|
30
30
|
crc32,
|
|
31
31
|
decompressArm9,
|
|
32
32
|
decompressBLZ,
|
|
33
|
+
decryptSecureArea,
|
|
33
34
|
encryptSecureArea,
|
|
34
35
|
modcryptKey,
|
|
35
36
|
updateHeaderSignatures
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-LK3JXA24.js";
|
|
37
38
|
export {
|
|
38
39
|
Banner,
|
|
39
40
|
BinaryWriter,
|
|
@@ -54,6 +55,7 @@ export {
|
|
|
54
55
|
decodeXdelta,
|
|
55
56
|
decompressArm9,
|
|
56
57
|
decompressBLZ,
|
|
58
|
+
decryptSecureArea,
|
|
57
59
|
encryptSecureArea,
|
|
58
60
|
extractZipEntry,
|
|
59
61
|
modcryptKey,
|
package/dist/nitro-helper.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { C as CompleteTWL, A as Arm9Header, H as Header, a as DsiVerification } from './twl-complete-B2is30H0.js';
|
|
2
|
+
export { D as DsiCheck, U as UnitCode, c as compressArm9, b as compressBLZ, d as decompressArm9, e as decompressBLZ } from './twl-complete-B2is30H0.js';
|
|
3
|
+
|
|
1
4
|
declare class Banner {
|
|
2
5
|
version: number;
|
|
3
6
|
raw: Buffer;
|
|
@@ -13,14 +16,15 @@ declare class Banner {
|
|
|
13
16
|
chineseTitle: string;
|
|
14
17
|
koreanTitle: string;
|
|
15
18
|
constructor(input: Uint8Array, offset?: number, hardSize?: number);
|
|
16
|
-
get titleCount():
|
|
19
|
+
get titleCount(): 7 | 8 | 6;
|
|
17
20
|
getDefSize(hardSize?: number): number;
|
|
18
21
|
toBuffer(): Buffer<ArrayBuffer>;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
/**
|
|
24
|
+
/** Original IDs/offsets describe the input ROM; newly added files use -1 until serialized and reopened. */
|
|
22
25
|
interface NitroFile {
|
|
23
26
|
name: string;
|
|
27
|
+
nameBytes?: Buffer;
|
|
24
28
|
id: number;
|
|
25
29
|
offset: number;
|
|
26
30
|
size: number;
|
|
@@ -28,10 +32,17 @@ interface NitroFile {
|
|
|
28
32
|
}
|
|
29
33
|
interface NitroFolder {
|
|
30
34
|
name: string;
|
|
35
|
+
nameBytes?: Buffer;
|
|
31
36
|
id: number;
|
|
32
37
|
files: NitroFile[];
|
|
33
38
|
folders: NitroFolder[];
|
|
34
39
|
}
|
|
40
|
+
interface FileSystemLayout {
|
|
41
|
+
fnt: Buffer;
|
|
42
|
+
files: NitroFile[];
|
|
43
|
+
ids: Map<NitroFile, number>;
|
|
44
|
+
sourceIds: Map<number, number>;
|
|
45
|
+
}
|
|
35
46
|
interface FatEntry {
|
|
36
47
|
id: number;
|
|
37
48
|
offset: number;
|
|
@@ -45,6 +56,7 @@ declare class FileAllocationTable {
|
|
|
45
56
|
declare class FileNameTable {
|
|
46
57
|
readonly root: NitroFolder;
|
|
47
58
|
constructor(fat: FileAllocationTable, input: Uint8Array, offset: number, size: number);
|
|
59
|
+
serialize(fat: FileAllocationTable, overlays: NitroFile[]): FileSystemLayout;
|
|
48
60
|
static findFile(id: number, folder: NitroFolder): NitroFile | undefined;
|
|
49
61
|
}
|
|
50
62
|
interface OverlayItem {
|
|
@@ -65,117 +77,6 @@ declare class OverlayTable {
|
|
|
65
77
|
toBuffer(): Buffer;
|
|
66
78
|
}
|
|
67
79
|
|
|
68
|
-
declare const UnitCode: Readonly<{
|
|
69
|
-
NDS: 0;
|
|
70
|
-
NDS_DSi: 2;
|
|
71
|
-
DSi: 3;
|
|
72
|
-
}>;
|
|
73
|
-
declare class Header {
|
|
74
|
-
raw: Buffer;
|
|
75
|
-
trimmedRom: boolean;
|
|
76
|
-
doublePadding: boolean;
|
|
77
|
-
nitrocode: boolean;
|
|
78
|
-
decrypted: boolean;
|
|
79
|
-
headerCRC: boolean;
|
|
80
|
-
logoCRC: boolean;
|
|
81
|
-
secureCRC: boolean;
|
|
82
|
-
dlp_signature: Buffer;
|
|
83
|
-
ARM9romOffset: number;
|
|
84
|
-
ARM9entryAddress: number;
|
|
85
|
-
ARM9ramAddress: number;
|
|
86
|
-
ARM9size: number;
|
|
87
|
-
ARM7romOffset: number;
|
|
88
|
-
ARM7entryAddress: number;
|
|
89
|
-
ARM7ramAddress: number;
|
|
90
|
-
ARM7size: number;
|
|
91
|
-
FNToffset: number;
|
|
92
|
-
FNTsize: number;
|
|
93
|
-
FAToffset: number;
|
|
94
|
-
FATsize: number;
|
|
95
|
-
ARM9overlayOffset: number;
|
|
96
|
-
ARM9overlaySize: number;
|
|
97
|
-
ARM7overlayOffset: number;
|
|
98
|
-
ARM7overlaySize: number;
|
|
99
|
-
flagsRead: number;
|
|
100
|
-
flagsInit: number;
|
|
101
|
-
bannerOffset: number;
|
|
102
|
-
dsi9_rom_offset: number;
|
|
103
|
-
offset_0x1C4: number;
|
|
104
|
-
dsi9_ram_address: number;
|
|
105
|
-
dsi9_size: number;
|
|
106
|
-
dsi7_rom_offset: number;
|
|
107
|
-
offset_0x1D4: number;
|
|
108
|
-
dsi7_ram_address: number;
|
|
109
|
-
dsi7_size: number;
|
|
110
|
-
digest_ntr_start: number;
|
|
111
|
-
digest_ntr_size: number;
|
|
112
|
-
digest_twl_start: number;
|
|
113
|
-
digest_twl_size: number;
|
|
114
|
-
sector_hashtable_start: number;
|
|
115
|
-
sector_hashtable_size: number;
|
|
116
|
-
block_hashtable_start: number;
|
|
117
|
-
block_hashtable_size: number;
|
|
118
|
-
digest_sector_size: number;
|
|
119
|
-
digest_block_sectorcount: number;
|
|
120
|
-
banner_size: number;
|
|
121
|
-
offset_0x20C: number;
|
|
122
|
-
total_rom_size: number;
|
|
123
|
-
offset_0x214: number;
|
|
124
|
-
offset_0x218: number;
|
|
125
|
-
offset_0x21C: number;
|
|
126
|
-
modcrypt1_start: number;
|
|
127
|
-
modcrypt1_size: number;
|
|
128
|
-
modcrypt2_start: number;
|
|
129
|
-
modcrypt2_size: number;
|
|
130
|
-
tid_low: number;
|
|
131
|
-
tid_high: number;
|
|
132
|
-
public_sav_size: number;
|
|
133
|
-
private_sav_size: number;
|
|
134
|
-
unitCode: number;
|
|
135
|
-
encryptionSeed: number;
|
|
136
|
-
twlInternalFlags: number;
|
|
137
|
-
permitsFlags: number;
|
|
138
|
-
ROMversion: number;
|
|
139
|
-
internalFlags: number;
|
|
140
|
-
secureCRC16: number;
|
|
141
|
-
ROMtimeout: number;
|
|
142
|
-
ARM9autoload: number;
|
|
143
|
-
ARM7autoload: number;
|
|
144
|
-
ROMsize: number;
|
|
145
|
-
headerSize: number;
|
|
146
|
-
logoCRC16: number;
|
|
147
|
-
headerCRC16: number;
|
|
148
|
-
debug_romOffset: number;
|
|
149
|
-
debug_size: number;
|
|
150
|
-
debug_ramAddress: number;
|
|
151
|
-
reserved3: number;
|
|
152
|
-
mbk9_wramcnt_setting: number;
|
|
153
|
-
region_flags: number;
|
|
154
|
-
access_control: number;
|
|
155
|
-
scfg_ext_mask: number;
|
|
156
|
-
gameTitle: string;
|
|
157
|
-
gameCode: string;
|
|
158
|
-
makerCode: string;
|
|
159
|
-
reserved: Buffer;
|
|
160
|
-
reserved2: Buffer;
|
|
161
|
-
logo: Buffer;
|
|
162
|
-
appflags: Buffer;
|
|
163
|
-
hmac_arm9: Buffer;
|
|
164
|
-
hmac_arm7: Buffer;
|
|
165
|
-
hmac_digest_master: Buffer;
|
|
166
|
-
hmac_icon_title: Buffer;
|
|
167
|
-
hmac_arm9i: Buffer;
|
|
168
|
-
hmac_arm7i: Buffer;
|
|
169
|
-
hmac_arm9_no_secure: Buffer;
|
|
170
|
-
rsa_signature: Buffer;
|
|
171
|
-
secureDisable: bigint;
|
|
172
|
-
constructor(input: Uint8Array);
|
|
173
|
-
get size(): number;
|
|
174
|
-
set size(value: number);
|
|
175
|
-
toBuffer(): Buffer<ArrayBuffer>;
|
|
176
|
-
toString(): string;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
80
|
/** Seekable, bounded in-memory writer; unwritten gaps have MemoryStream's zero fill. */
|
|
180
81
|
declare class BinaryWriter {
|
|
181
82
|
readonly maxSize: number;
|
|
@@ -190,19 +91,6 @@ declare class BinaryWriter {
|
|
|
190
91
|
toBuffer(): Buffer<ArrayBufferLike>;
|
|
191
92
|
}
|
|
192
93
|
|
|
193
|
-
interface Arm9Header {
|
|
194
|
-
reserved2: Buffer;
|
|
195
|
-
ARM9ramAddress: number;
|
|
196
|
-
ARM9size: number;
|
|
197
|
-
}
|
|
198
|
-
declare const decompressBLZ: (input: Buffer, maxSize?: number) => Buffer;
|
|
199
|
-
declare const compressBLZ: (input: Buffer) => Buffer;
|
|
200
|
-
declare const decompressArm9: (data: Buffer, header: Arm9Header) => {
|
|
201
|
-
compressed: boolean;
|
|
202
|
-
data: Buffer;
|
|
203
|
-
};
|
|
204
|
-
declare const compressArm9: (data: Buffer, header: Arm9Header, postSize?: number) => Buffer;
|
|
205
|
-
|
|
206
94
|
interface ModcryptHeader {
|
|
207
95
|
gameCode: string;
|
|
208
96
|
gameTitle: string;
|
|
@@ -212,7 +100,8 @@ interface ModcryptHeader {
|
|
|
212
100
|
}
|
|
213
101
|
declare const aes128CtrCrypt: (key: Buffer, counter: Buffer, data: Buffer) => Buffer;
|
|
214
102
|
declare const modcryptKey: (header: ModcryptHeader) => Buffer;
|
|
215
|
-
declare const encryptSecureArea: (gameCode: string, input: Buffer) => Buffer;
|
|
103
|
+
declare const encryptSecureArea: (gameCode: string, input: Buffer, refreshCrc?: boolean) => Buffer;
|
|
104
|
+
declare const decryptSecureArea: (gameCode: string, input: Buffer) => Buffer;
|
|
216
105
|
|
|
217
106
|
interface TwlHeader extends ModcryptHeader {
|
|
218
107
|
digest_sector_size: number;
|
|
@@ -256,9 +145,17 @@ declare class TWL {
|
|
|
256
145
|
readonly Header2Data: Buffer[] | null;
|
|
257
146
|
readonly Overlays9Sha1Hmac: Buffer[];
|
|
258
147
|
readonly twlEncrypted: boolean;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
148
|
+
readonly complete: CompleteTWL | undefined;
|
|
149
|
+
private dsi9Data;
|
|
150
|
+
private dsi7Data;
|
|
151
|
+
get DSi9Data(): Buffer;
|
|
152
|
+
set DSi9Data(data: Buffer);
|
|
153
|
+
get DSi7Data(): Buffer;
|
|
154
|
+
set DSi7Data(data: Buffer);
|
|
155
|
+
constructor(header: TwlHeader, overlays: readonly OverlayRange[], original: Buffer, options?: {
|
|
156
|
+
dsiMode?: "legacy" | "complete";
|
|
157
|
+
maxOutputSize?: number;
|
|
158
|
+
});
|
|
262
159
|
private cryptSections;
|
|
263
160
|
importArm9iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
264
161
|
importArm7iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
@@ -266,16 +163,19 @@ declare class TWL {
|
|
|
266
163
|
updateOverlays9Sha1Hmac(arm9Data: Buffer, header: Arm9Header, overlays: readonly Buffer[]): Buffer;
|
|
267
164
|
writeTo(writer: BinaryWriter, h: TwlHeader): Buffer;
|
|
268
165
|
}
|
|
269
|
-
/** Refresh the digest
|
|
270
|
-
declare const updateHeaderSignatures: (writer: BinaryWriter, header: SignedTwlHeader, originalHeader: Buffer,
|
|
166
|
+
/** Refresh the master digest while preserving the cartridge signature. */
|
|
167
|
+
declare const updateHeaderSignatures: (writer: BinaryWriter, header: SignedTwlHeader, originalHeader: Buffer, _keepOriginal?: boolean) => void;
|
|
271
168
|
|
|
272
169
|
interface RomOptions {
|
|
273
170
|
/** Maximum generated ROM size in bytes. Defaults to 1 GiB. */
|
|
274
171
|
maxOutputSize?: number;
|
|
172
|
+
/** Complete DSi cartridge handling is opt-in for direct API calls. */
|
|
173
|
+
dsiMode?: "legacy" | "complete";
|
|
275
174
|
}
|
|
276
175
|
interface RomFileInfo {
|
|
277
176
|
/** ROM-relative path, including data/, overlay/, or a system filename. */
|
|
278
177
|
path: string;
|
|
178
|
+
/** Input ROM identity and position; both are -1 for an added file until the output is reopened. */
|
|
279
179
|
id: number;
|
|
280
180
|
offset: number;
|
|
281
181
|
size: number;
|
|
@@ -294,6 +194,8 @@ declare class NDSFile {
|
|
|
294
194
|
readonly warnings: string[];
|
|
295
195
|
private readonly paths;
|
|
296
196
|
private readonly maxOutputSize;
|
|
197
|
+
private readonly addedOverlays;
|
|
198
|
+
private structureChanged;
|
|
297
199
|
constructor(input: Uint8Array, options?: RomOptions);
|
|
298
200
|
static fromFile(path: string, options?: RomOptions): Promise<NDSFile>;
|
|
299
201
|
get data(): NitroFolder;
|
|
@@ -301,15 +203,22 @@ declare class NDSFile {
|
|
|
301
203
|
listFiles(): RomFileInfo[];
|
|
302
204
|
hasFile(path: string): boolean;
|
|
303
205
|
getFile(path: string): Buffer;
|
|
304
|
-
/** Replace an existing file.
|
|
206
|
+
/** Replace an existing file. */
|
|
305
207
|
replaceFile(path: string, content: Uint8Array): void;
|
|
208
|
+
/** Add a NitroFS file and any missing parent directories. */
|
|
209
|
+
addFile(path: string, content: Uint8Array): void;
|
|
210
|
+
/** Add an overlay; file IDs are assigned when the ROM is serialized. */
|
|
211
|
+
addOverlay(arm9: boolean, metadata: Omit<OverlayItem, "fileId">, content: Uint8Array): void;
|
|
212
|
+
private readOverlayTable;
|
|
306
213
|
private fileBytes;
|
|
214
|
+
verifyDsi(): DsiVerification;
|
|
307
215
|
/** Rebuild offsets, allocation table, CRCs and ROM padding. Safe to call repeatedly. */
|
|
308
216
|
toBuffer(): Buffer;
|
|
309
217
|
saveAs(path: string): Promise<void>;
|
|
218
|
+
private serializeDsi;
|
|
310
219
|
}
|
|
311
220
|
|
|
312
221
|
declare const crc16: (data: Uint8Array, initial?: number) => number;
|
|
313
222
|
declare const crc32: (data: Uint8Array) => number;
|
|
314
223
|
|
|
315
|
-
export {
|
|
224
|
+
export { Arm9Header, Banner, BinaryWriter, DsiVerification, 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, aes128CtrCrypt, crc16, crc32, decryptSecureArea, encryptSecureArea, modcryptKey, updateHeaderSignatures };
|
package/dist/nitro-helper.js
CHANGED
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
crc32,
|
|
17
17
|
decompressArm9,
|
|
18
18
|
decompressBLZ,
|
|
19
|
+
decryptSecureArea,
|
|
19
20
|
encryptSecureArea,
|
|
20
21
|
modcryptKey,
|
|
21
22
|
updateHeaderSignatures
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-LK3JXA24.js";
|
|
23
24
|
export {
|
|
24
25
|
Banner,
|
|
25
26
|
BinaryWriter,
|
|
@@ -37,6 +38,7 @@ export {
|
|
|
37
38
|
crc32,
|
|
38
39
|
decompressArm9,
|
|
39
40
|
decompressBLZ,
|
|
41
|
+
decryptSecureArea,
|
|
40
42
|
encryptSecureArea,
|
|
41
43
|
modcryptKey,
|
|
42
44
|
updateHeaderSignatures
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { a as DsiVerification } from './twl-complete-B2is30H0.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @param {Buffer|Uint8Array} source Original bytes.
|
|
3
5
|
* @param {Buffer|Uint8Array} patch VCDIFF bytes.
|
|
@@ -22,6 +24,7 @@ interface PatchResult {
|
|
|
22
24
|
/** MD5 of the original input, before preprocessing. */
|
|
23
25
|
inputMd5: string;
|
|
24
26
|
outputMd5: string;
|
|
27
|
+
dsi?: DsiVerification;
|
|
25
28
|
}
|
|
26
29
|
interface PatchOptions {
|
|
27
30
|
/** Maximum generated ROM/Xdelta target size in bytes, default 1 GiB. */
|
|
@@ -34,6 +37,7 @@ interface PatchOptions {
|
|
|
34
37
|
maxEntries?: number;
|
|
35
38
|
}
|
|
36
39
|
interface PatchMetadata {
|
|
40
|
+
formatVersion?: number;
|
|
37
41
|
id?: string;
|
|
38
42
|
author?: string;
|
|
39
43
|
name?: string;
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
readPatchMetadataFile,
|
|
12
12
|
readPatchReadme,
|
|
13
13
|
readPatchReadmeFile
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-B7NTPBDX.js";
|
|
15
|
+
import "./chunk-LK3JXA24.js";
|
|
16
16
|
export {
|
|
17
17
|
PatchHelper,
|
|
18
18
|
PatchReturnValue,
|
|
@@ -0,0 +1,201 @@
|
|
|
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
|
+
ntr_region_end: number;
|
|
79
|
+
twl_region_start: number;
|
|
80
|
+
headerSize: number;
|
|
81
|
+
logoCRC16: number;
|
|
82
|
+
headerCRC16: number;
|
|
83
|
+
debug_romOffset: number;
|
|
84
|
+
debug_size: number;
|
|
85
|
+
debug_ramAddress: number;
|
|
86
|
+
reserved3: number;
|
|
87
|
+
mbk9_wramcnt_setting: number;
|
|
88
|
+
region_flags: number;
|
|
89
|
+
access_control: number;
|
|
90
|
+
scfg_ext_mask: number;
|
|
91
|
+
gameTitle: string;
|
|
92
|
+
gameCode: string;
|
|
93
|
+
makerCode: string;
|
|
94
|
+
reserved: Buffer;
|
|
95
|
+
reserved2: Buffer;
|
|
96
|
+
logo: Buffer;
|
|
97
|
+
appflags: Buffer;
|
|
98
|
+
hmac_arm9: Buffer;
|
|
99
|
+
hmac_arm7: Buffer;
|
|
100
|
+
hmac_digest_master: Buffer;
|
|
101
|
+
hmac_icon_title: Buffer;
|
|
102
|
+
hmac_arm9i: Buffer;
|
|
103
|
+
hmac_arm7i: Buffer;
|
|
104
|
+
hmac_arm9_no_secure: Buffer;
|
|
105
|
+
rsa_signature: Buffer;
|
|
106
|
+
secureDisable: bigint;
|
|
107
|
+
constructor(input: Uint8Array);
|
|
108
|
+
get size(): number;
|
|
109
|
+
set size(value: number);
|
|
110
|
+
toBuffer(): Buffer<ArrayBuffer>;
|
|
111
|
+
toString(): string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface Arm9Header {
|
|
115
|
+
nitrocode?: boolean;
|
|
116
|
+
reserved2: Buffer;
|
|
117
|
+
ARM9ramAddress: number;
|
|
118
|
+
ARM9size: number;
|
|
119
|
+
}
|
|
120
|
+
declare const decompressBLZ: (input: Buffer, maxSize?: number) => Buffer;
|
|
121
|
+
declare const compressBLZ: (input: Buffer) => Buffer;
|
|
122
|
+
declare const decompressArm9: (data: Buffer, header: Arm9Header, maxSize?: number) => {
|
|
123
|
+
compressed: boolean;
|
|
124
|
+
data: Buffer;
|
|
125
|
+
};
|
|
126
|
+
declare const compressArm9: (data: Buffer, header: Arm9Header, postSize?: number) => Buffer;
|
|
127
|
+
|
|
128
|
+
interface DsiCheck {
|
|
129
|
+
status: "valid" | "invalid" | "not-applicable";
|
|
130
|
+
reason?: string;
|
|
131
|
+
}
|
|
132
|
+
interface DsiVerification {
|
|
133
|
+
arm9: DsiCheck;
|
|
134
|
+
arm7: DsiCheck;
|
|
135
|
+
arm9i: DsiCheck;
|
|
136
|
+
arm7i: DsiCheck;
|
|
137
|
+
arm9NoSecure: DsiCheck;
|
|
138
|
+
banner: DsiCheck;
|
|
139
|
+
sectors: DsiCheck;
|
|
140
|
+
blocks: DsiCheck;
|
|
141
|
+
master: DsiCheck;
|
|
142
|
+
signature: DsiCheck;
|
|
143
|
+
}
|
|
144
|
+
type ProgramName = "arm9" | "arm7" | "arm9i" | "arm7i";
|
|
145
|
+
interface Program {
|
|
146
|
+
name: ProgramName;
|
|
147
|
+
start: number;
|
|
148
|
+
length: number;
|
|
149
|
+
data: Buffer;
|
|
150
|
+
original: Buffer;
|
|
151
|
+
replaced: boolean;
|
|
152
|
+
}
|
|
153
|
+
interface CryptRegion {
|
|
154
|
+
id: 1 | 2;
|
|
155
|
+
program: ProgramName;
|
|
156
|
+
offset: number;
|
|
157
|
+
length: number;
|
|
158
|
+
kind: "whole" | "secure" | "fixed";
|
|
159
|
+
}
|
|
160
|
+
interface DsiSection {
|
|
161
|
+
label: string;
|
|
162
|
+
start: number;
|
|
163
|
+
originalSize: number;
|
|
164
|
+
data: Buffer;
|
|
165
|
+
place: (start: number) => void;
|
|
166
|
+
}
|
|
167
|
+
/** Complete cartridge processing; legacy TWL serialization stays independent. */
|
|
168
|
+
declare class CompleteTWL {
|
|
169
|
+
readonly header: Header;
|
|
170
|
+
readonly programs: Map<ProgramName, Program>;
|
|
171
|
+
readonly regions: CryptRegion[];
|
|
172
|
+
readonly hasDigests: boolean;
|
|
173
|
+
readonly Hashtable1Data: Buffer;
|
|
174
|
+
readonly Hashtable2Data: Buffer;
|
|
175
|
+
readonly Header2Data: Buffer[];
|
|
176
|
+
readonly Overlays9Sha1Hmac: Buffer[];
|
|
177
|
+
readonly twlEncrypted: boolean;
|
|
178
|
+
readonly firstDSiHashOffset: number;
|
|
179
|
+
readonly dsiHashSize: number;
|
|
180
|
+
private readonly original;
|
|
181
|
+
private readonly limit;
|
|
182
|
+
constructor(header: Header, overlays: readonly Buffer[], original: Buffer, limit: number);
|
|
183
|
+
private validateDigests;
|
|
184
|
+
private decryptPrograms;
|
|
185
|
+
get DSi9Data(): Buffer;
|
|
186
|
+
set DSi9Data(data: Buffer);
|
|
187
|
+
get DSi7Data(): Buffer;
|
|
188
|
+
set DSi7Data(data: Buffer);
|
|
189
|
+
getProgram(name: ProgramName): Buffer;
|
|
190
|
+
setProgram(name: ProgramName, data: Buffer): void;
|
|
191
|
+
importArm9iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
192
|
+
importArm7iData(data: Buffer | string, offset?: number, size?: number): void;
|
|
193
|
+
unchanged(): boolean;
|
|
194
|
+
updateOverlays9Sha1Hmac(arm9: Buffer, h: Arm9Header, overlays: readonly Buffer[]): Buffer;
|
|
195
|
+
private view;
|
|
196
|
+
verify(data?: Buffer<ArrayBufferLike>, h?: Header): DsiVerification;
|
|
197
|
+
serialize(h: Header, sections: DsiSection[], forceLayout: boolean): Buffer;
|
|
198
|
+
private checkMovableTwl;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export { type Arm9Header as A, CompleteTWL as C, type DsiCheck as D, Header as H, UnitCode as U, type DsiVerification as a, compressBLZ as b, compressArm9 as c, decompressArm9 as d, decompressBLZ as e };
|