nitro-patcher 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  patchBuffer,
4
4
  readPatchInfo
5
- } from "./chunk-IGURDH5L.js";
5
+ } from "./chunk-ZQM3LT5L.js";
6
6
  import {
7
7
  NDSFile
8
- } from "./chunk-GLK3K2HY.js";
8
+ } from "./chunk-LK3JXA24.js";
9
9
 
10
10
  // src/cli.ts
11
11
  import { render } from "markdansi";
@@ -22,6 +22,7 @@ var help = `nitro-patcher \u2014 NDS ROM \u8865\u4E01\u5DE5\u5177
22
22
 
23
23
  --json \u7ED3\u679C\u8F93\u51FA\u4E3A JSON \u683C\u5F0F
24
24
  --dry-run \u5B8C\u6574\u6267\u884C\u8865\u4E01\u6D41\u7A0B\uFF0C\u4F46\u4E0D\u5199\u5165\u8F93\u51FA ROM
25
+ --dsi-mode extract/inspect \u7684 DSi \u5904\u7406\u6A21\u5F0F\uFF1Alegacy \u6216 complete
25
26
  -i, --input \u8F93\u5165 ROM \u8DEF\u5F84
26
27
  -p, --patch \u8865\u4E01 ZIP \u8DEF\u5F84
27
28
  -o, --output \u8F93\u51FA ROM \u6216\u63D0\u53D6\u76EE\u5F55\uFF1B\u8BD5\u8FD0\u884C\u65F6\u53EF\u7701\u7565
@@ -67,7 +68,7 @@ var formatMetadata = (metadata) => {
67
68
  var extractRom = async (options) => {
68
69
  if (!options.input || !options.output || options.patch !== void 0 || options.dryRun) throw new Error(help);
69
70
  const destination = join(await realpath(dirname(resolve(options.output))), basename(resolve(options.output)));
70
- const rom = new NDSFile(await readFile(options.input));
71
+ const rom = new NDSFile(await readFile(options.input), { dsiMode: options.dsiMode ?? "legacy" });
71
72
  const files = rom.listFiles();
72
73
  await mkdir(destination, { recursive: true });
73
74
  for (const { path } of files) {
@@ -92,7 +93,7 @@ var inspectRom = async (options) => {
92
93
  if (!options.input || options.patch !== void 0 || options.output !== void 0 || options.dryRun)
93
94
  throw new Error(help);
94
95
  const input = await readFile(options.input);
95
- const rom = new NDSFile(input);
96
+ const rom = new NDSFile(input, { dsiMode: options.dsiMode ?? "legacy" });
96
97
  const info = {
97
98
  gameTitle: rom.header.gameTitle.replace(/\0+$/, ""),
98
99
  gameCode: rom.header.gameCode,
@@ -103,7 +104,8 @@ var inspectRom = async (options) => {
103
104
  logo: rom.header.logoCRC,
104
105
  secureArea: rom.header.secureCRC,
105
106
  banner: rom.banner.bannerCRC
106
- }
107
+ },
108
+ ...options.dsiMode === "complete" && { dsi: rom.verifyDsi() }
107
109
  };
108
110
  if (options.json) {
109
111
  console.log(JSON.stringify(info));
@@ -120,6 +122,9 @@ var inspectRom = async (options) => {
120
122
  - Logo\uFF1A${info.checksums.logo ? "\u901A\u8FC7" : "\u5931\u8D25"}
121
123
  - Secure Area\uFF1A${info.checksums.secureArea ? "\u901A\u8FC7" : "\u5931\u8D25"}
122
124
  - Banner\uFF1A${info.checksums.banner ? "\u901A\u8FC7" : "\u5931\u8D25"}`);
125
+ if (info.dsi)
126
+ for (const [name, result] of Object.entries(info.dsi))
127
+ console.log(`- DSi ${name}\uFF1A${result.status}${result.reason ? ` (${result.reason})` : ""}`);
123
128
  };
124
129
  var main = async () => {
125
130
  const { values, positionals } = parseArgs({
@@ -129,6 +134,7 @@ var main = async () => {
129
134
  version: { type: "boolean", short: "v" },
130
135
  json: { type: "boolean" },
131
136
  "dry-run": { type: "boolean" },
137
+ "dsi-mode": { type: "string" },
132
138
  input: { type: "string", short: "i" },
133
139
  patch: { type: "string", short: "p" },
134
140
  output: { type: "string", short: "o" }
@@ -145,12 +151,16 @@ var main = async () => {
145
151
  console.log(metadata.version);
146
152
  return;
147
153
  }
154
+ const dsiMode = values["dsi-mode"];
155
+ if (dsiMode !== void 0 && dsiMode !== "legacy" && dsiMode !== "complete")
156
+ throw new Error("Invalid --dsi-mode; expected legacy or complete");
148
157
  const options = {
149
158
  json: values.json ?? false,
150
159
  dryRun: values["dry-run"] ?? false,
151
160
  input: values.input,
152
161
  patch: values.patch,
153
- output: values.output
162
+ output: values.output,
163
+ dsiMode
154
164
  };
155
165
  const command = positionals.length === 1 ? positionals[0] : void 0;
156
166
  if (positionals.length !== 0 && positionals.length !== 1 && positionals.length !== 3) throw new Error(help);
@@ -165,6 +175,7 @@ var main = async () => {
165
175
  return;
166
176
  }
167
177
  const dryRun = options.dryRun;
178
+ if (options.dsiMode !== void 0) throw new Error("patch selects DSi processing from metadata.json formatVersion");
168
179
  const usesLegacyPaths = positionals.length === 3;
169
180
  if (usesLegacyPaths && (values.input !== void 0 || values.patch !== void 0 || values.output !== void 0))
170
181
  throw new Error(help);
@@ -199,7 +210,8 @@ var main = async () => {
199
210
  inputMd5,
200
211
  outputMd5,
201
212
  ...info,
202
- ...dryRun && { dryRun: true }
213
+ ...dryRun && { dryRun: true },
214
+ ...result.dsi && { dsi: result.dsi }
203
215
  })
204
216
  );
205
217
  return;
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":";;;;;;;;;;AACA,SAAS,cAAc;AACvB,SAAS,OAAO,SAAS,UAAU,UAAU,QAAQ,IAAI,MAAM,iBAAiB;AAChF,SAAS,UAAU,SAAS,MAAM,SAAS,WAAW;AACtD,SAAS,iBAAiB;AAK1B,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0Bb,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,mBAAmB,CAAC,UAA0B;AAClD,QAAM,aAAuB,CAAC;AAC9B,aAAW,aAAa,MAAM,WAAW,QAAQ,IAAI,EAAE,WAAW,MAAM,IAAI,GAAG;AAC7E,UAAM,OAAO,UAAU,YAAY,CAAC;AACpC,QACG,OAAO,MAAQ,SAAS,KAAQ,SAAS,MACzC,QAAQ,OAAQ,QAAQ,OACxB,QAAQ,QAAU,QAAQ,QAC1B,QAAQ,QAAU,QAAQ;AAE3B;AACF,eAAW,KAAK,SAAS;AAAA,EAC3B;AACA,SAAO,WAAW,KAAK,EAAE;AAC3B;AAEA,IAAM,iBAAiB,CAAC,aAAoC;AAC1D,QAAM,SAAS;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,EACX;AACA,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,CAAC,UAAU,QAAQ,YAAY,SAAS,GAAY;AACtE,UAAM,QAAQ,SAAS,KAAK;AAC5B,QAAI,UAAU,OAAW;AACzB,UAAM,SAAS,UAAU,aAAa,SAAS,SAAS,mCAAU;AAClE,UAAM,KAAK,KAAK,OAAO,KAAK,CAAC,SAAI,iBAAiB,KAAK,EAAE,WAAW,MAAM,GAAG,CAAC,GAAG,MAAM,EAAE;AAAA,EAC3F;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,IAAM,aAAa,OAAO,YAA2C;AACnE,MAAI,CAAC,QAAQ,SAAS,CAAC,QAAQ,UAAU,QAAQ,UAAU,UAAa,QAAQ,OAAQ,OAAM,IAAI,MAAM,IAAI;AAC5G,QAAM,cAAc,KAAK,MAAM,SAAS,QAAQ,QAAQ,QAAQ,MAAM,CAAC,CAAC,GAAG,SAAS,QAAQ,QAAQ,MAAM,CAAC,CAAC;AAC5G,QAAM,MAAM,IAAI,QAAQ,MAAM,SAAS,QAAQ,KAAK,GAAG,EAAE,SAAS,QAAQ,WAAW,SAAS,CAAC;AAC/F,QAAM,QAAQ,IAAI,UAAU;AAC5B,QAAM,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,aAAW,EAAE,KAAK,KAAK,OAAO;AAC5B,UAAM,SAAS,QAAQ,aAAa,IAAI;AACxC,QAAI,CAAC,OAAO,WAAW,GAAG,WAAW,GAAG,GAAG,EAAE,EAAG,OAAM,IAAI,MAAM,yEAAkB,IAAI,EAAE;AACxF,UAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,UAAM,UAAU,QAAQ,IAAI,QAAQ,IAAI,CAAC;AAAA,EAC3C;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ;AAAA,MACN,KAAK,UAAU;AAAA,QACb,QAAQ;AAAA,QACR,WAAW,MAAM;AAAA,QACjB,OAAO,MAAM,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,sBAAO,MAAM,MAAM,kCAAS,WAAW,EAAE;AAAA,EACvD;AACF;AAEA,IAAM,aAAa,OAAO,YAA2C;AACnE,MAAI,CAAC,QAAQ,SAAS,QAAQ,UAAU,UAAa,QAAQ,WAAW,UAAa,QAAQ;AAC3F,UAAM,IAAI,MAAM,IAAI;AACtB,QAAM,QAAQ,MAAM,SAAS,QAAQ,KAAK;AAC1C,QAAM,MAAM,IAAI,QAAQ,OAAO,EAAE,SAAS,QAAQ,WAAW,SAAS,CAAC;AACvE,QAAM,OAAO;AAAA,IACX,WAAW,IAAI,OAAO,UAAU,QAAQ,QAAQ,EAAE;AAAA,IAClD,UAAU,IAAI,OAAO;AAAA,IACrB,MAAM,MAAM;AAAA,IACZ,WAAW,IAAI,UAAU,EAAE;AAAA,IAC3B,WAAW;AAAA,MACT,QAAQ,IAAI,OAAO;AAAA,MACnB,MAAM,IAAI,OAAO;AAAA,MACjB,YAAY,IAAI,OAAO;AAAA,MACvB,QAAQ,IAAI,OAAO;AAAA,IACrB;AAAA,IACA,GAAI,QAAQ,YAAY,cAAc,EAAE,KAAK,IAAI,UAAU,EAAE;AAAA,EAC/D;AACA,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,IAAI,CAAC;AAChC;AAAA,EACF;AACA,UAAQ,IAAI;AAAA;AAAA,oBAET,iBAAiB,KAAK,SAAS,EAAE,WAAW,MAAM,GAAG,CAAC;AAAA,gCACpD,iBAAiB,KAAK,QAAQ,EAAE,WAAW,MAAM,GAAG,CAAC;AAAA,oBACvD,KAAK,IAAI;AAAA,gCACP,KAAK,SAAS;AAAA;AAAA,gBAEV,KAAK,UAAU,SAAS,iBAAO,cAAI;AAAA,cACrC,KAAK,UAAU,OAAO,iBAAO,cAAI;AAAA,qBAC1B,KAAK,UAAU,aAAa,iBAAO,cAAI;AAAA,gBAC5C,KAAK,UAAU,SAAS,iBAAO,cAAI,EAAE;AAC9C,MAAI,KAAK;AACP,eAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,GAAG;AAClD,cAAQ,IAAI,SAAS,IAAI,SAAI,OAAO,MAAM,GAAG,OAAO,SAAS,KAAK,OAAO,MAAM,MAAM,EAAE,EAAE;AAC/F;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,MACxB,WAAW,EAAE,MAAM,UAAU;AAAA,MAC7B,YAAY,EAAE,MAAM,SAAS;AAAA,MAC7B,OAAO,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,MACpC,OAAO,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,MACpC,QAAQ,EAAE,MAAM,UAAU,OAAO,IAAI;AAAA,IACvC;AAAA,EACF,CAAC;AACD,MAAI,OAAO,MAAM;AACf,YAAQ,OAAO,MAAM,IAAI;AACzB;AAAA,EACF;AACA,MAAI,OAAO,SAAS;AAClB,UAAM,WAAoB,KAAK,MAAM,MAAM,SAAS,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC;AACxG,QAAI,CAAC,YAAY,OAAO,aAAa,YAAY,EAAE,aAAa,aAAa,OAAO,SAAS,YAAY;AACvG,YAAM,IAAI,MAAM,kCAAkC;AACpD,YAAQ,IAAI,SAAS,OAAO;AAC5B;AAAA,EACF;AACA,QAAM,UAAU,OAAO,UAAU;AACjC,MAAI,YAAY,UAAa,YAAY,YAAY,YAAY;AAC/D,UAAM,IAAI,MAAM,iDAAiD;AACnE,QAAM,UAA0B;AAAA,IAC9B,MAAM,OAAO,QAAQ;AAAA,IACrB,QAAQ,OAAO,SAAS,KAAK;AAAA,IAC7B,OAAO,OAAO;AAAA,IACd,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf;AAAA,EACF;AACA,QAAM,UAAU,YAAY,WAAW,IAAI,YAAY,CAAC,IAAI;AAC5D,MAAI,YAAY,WAAW,KAAK,YAAY,WAAW,KAAK,YAAY,WAAW,EAAG,OAAM,IAAI,MAAM,IAAI;AAC1G,MAAI,YAAY,UAAa,YAAY,WAAW,YAAY,aAAa,YAAY;AACvF,UAAM,IAAI,MAAM,IAAI;AACtB,MAAI,YAAY,WAAW;AACzB,UAAM,WAAW,OAAO;AACxB;AAAA,EACF;AACA,MAAI,YAAY,WAAW;AACzB,UAAM,WAAW,OAAO;AACxB;AAAA,EACF;AAEA,QAAM,SAAS,QAAQ;AACvB,MAAI,QAAQ,YAAY,OAAW,OAAM,IAAI,MAAM,+DAA+D;AAClH,QAAM,kBAAkB,YAAY,WAAW;AAC/C,MAAI,oBAAoB,OAAO,UAAU,UAAa,OAAO,UAAU,UAAa,OAAO,WAAW;AACpG,UAAM,IAAI,MAAM,IAAI;AACtB,MAAI,CAAC,oBAAoB,CAAC,OAAO,SAAS,CAAC,OAAO,SAAU,CAAC,UAAU,CAAC,OAAO,QAAU,OAAM,IAAI,MAAM,IAAI;AAC7G,QAAM,CAAC,UAAU,OAAO,MAAM,IAAI,kBAAkB,cAAc,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,MAAM;AAC5G,MAAI,CAAC,YAAY,CAAC,MAAO,OAAM,IAAI,MAAM,IAAI;AAC7C,MAAI,CAAC,QAAQ;AACX,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,IAAI;AACjC,QAAK,MAAM,SAAS,UAAU,MAAM,KAAO,MAAM,SAAS,OAAO,MAAM;AACrE,YAAM,IAAI,MAAM,iGAAsB;AAAA,EAC1C;AACA,QAAM,CAAC,eAAe,UAAU,IAAI,MAAM,QAAQ,IAAI,CAAC,SAAS,QAAQ,GAAG,SAAS,KAAK,CAAC,CAAC;AAC3F,QAAM,OAAO,cAAc,UAAU;AACrC,QAAM,SAAS,YAAY,eAAe,UAAU;AACpD,MAAI,CAAC,QAAQ;AACX,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,IAAI;AACjC,UAAM,cAAc,KAAK,MAAM,SAAS,QAAQ,QAAQ,MAAM,CAAC,CAAC,GAAG,SAAS,MAAM,CAAC;AACnF,UAAM,YAAY,MAAM,QAAQ,KAAK,QAAQ,WAAW,GAAG,iBAAiB,CAAC;AAC7E,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,YAAY;AACzC,YAAM,UAAU,MAAM,OAAO,MAAM;AACnC,YAAM,OAAO,MAAM,WAAW;AAAA,IAChC,UAAE;AACA,YAAM,GAAG,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IACtD;AAAA,EACF;AACA,QAAM,EAAE,aAAa,UAAU,UAAU,IAAI;AAC7C,MAAI,QAAQ,MAAM;AAChB,YAAQ;AAAA,MACN,KAAK,UAAU;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAG;AAAA,QACH,GAAI,UAAU,EAAE,QAAQ,KAAK;AAAA,QAC7B,GAAI,OAAO,OAAO,EAAE,KAAK,OAAO,IAAI;AAAA,MACtC,CAAC;AAAA,IACH;AACA;AAAA,EACF;AAEA,QAAM,WAAqB,CAAC;AAC5B,MAAI,KAAK,UAAU;AACjB,UAAM,WAAW,eAAe,KAAK,QAAQ;AAC7C,QAAI,SAAU,UAAS,KAAK;AAAA;AAAA,EAAY,QAAQ,EAAE;AAAA,EACpD;AACA,MAAI,KAAK,QAAQ;AACf,UAAM,UAAU,iBAAiB,KAAK,OAAO,OAAO;AACpD,UAAM,WACJ,KAAK,OAAO,WAAW,aACnB,OAAO,SAAS;AAAA,MACd,OAAO,QAAQ,OAAO,WAAW;AAAA,MACjC,OAAO,QAAQ,QAAQ,OAAO,KAAK;AAAA,MACnC,YAAY;AAAA,IACd,CAAC,IACD;AACN,aAAS,KAAK;AAAA;AAAA,EAAY,SAAS,KAAK,CAAC,EAAE;AAAA,EAC7C;AACA,MAAI,OAAQ,UAAS,KAAK,8EAAkB;AAC5C,WAAS,KAAK,GAAG,WAAW;AAAA;AAAA,mCAAoB,QAAQ;AAAA,mCAAkB,SAAS,EAAE;AACrF,UAAQ,IAAI,SAAS,KAAK,MAAM,CAAC;AACnC;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 CHANGED
@@ -1,2 +1,3 @@
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, PatchInfo, PatchMetadata, PatchOptions, PatchReadme, PatchResult, PatchReturnValue, XdeltaOptions, decodeXdelta, extractZipEntry, patchBuffer, patchIt, readPatchInfo, readPatchInfoFile, readPatchMetadata, readPatchMetadataFile, readPatchReadme, readPatchReadmeFile } from './nitro-patch-helper.js';
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';
3
+ export { CreatePatchOptions, CreatePatchResult, PatchHelper, PatchInfo, PatchMetadata, PatchOptions, PatchReadme, PatchResult, PatchReturnValue, XdeltaOptions, createPatch, decodeXdelta, extractZipEntry, patchBuffer, patchIt, readPatchInfo, readPatchInfoFile, readPatchMetadata, readPatchMetadataFile, readPatchReadme, readPatchReadmeFile } from './nitro-patch-helper.js';
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import "./chunk-ANN47R34.js";
2
2
  import {
3
3
  PatchHelper,
4
4
  PatchReturnValue,
5
+ createPatch,
5
6
  decodeXdelta,
6
7
  extractZipEntry,
7
8
  patchBuffer,
@@ -12,7 +13,7 @@ import {
12
13
  readPatchMetadataFile,
13
14
  readPatchReadme,
14
15
  readPatchReadmeFile
15
- } from "./chunk-IGURDH5L.js";
16
+ } from "./chunk-ZQM3LT5L.js";
16
17
  import {
17
18
  Banner,
18
19
  BinaryWriter,
@@ -30,10 +31,11 @@ import {
30
31
  crc32,
31
32
  decompressArm9,
32
33
  decompressBLZ,
34
+ decryptSecureArea,
33
35
  encryptSecureArea,
34
36
  modcryptKey,
35
37
  updateHeaderSignatures
36
- } from "./chunk-GLK3K2HY.js";
38
+ } from "./chunk-LK3JXA24.js";
37
39
  export {
38
40
  Banner,
39
41
  BinaryWriter,
@@ -51,9 +53,11 @@ export {
51
53
  compressBLZ,
52
54
  crc16,
53
55
  crc32,
56
+ createPatch,
54
57
  decodeXdelta,
55
58
  decompressArm9,
56
59
  decompressBLZ,
60
+ decryptSecureArea,
57
61
  encryptSecureArea,
58
62
  extractZipEntry,
59
63
  modcryptKey,
@@ -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(): 8 | 7 | 6;
19
+ get titleCount(): 7 | 8 | 6;
17
20
  getDefSize(hardSize?: number): number;
18
21
  toBuffer(): Buffer<ArrayBuffer>;
19
22
  }
20
23
 
21
- /** A file in the original ROM, optionally replaced with new bytes. */
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
- DSi9Data: Buffer;
260
- DSi7Data: Buffer;
261
- constructor(header: TwlHeader, overlays: readonly OverlayRange[], original: Buffer);
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 and keep valid RSA signatures; optional no$gba hash mask. */
270
- declare const updateHeaderSignatures: (writer: BinaryWriter, header: SignedTwlHeader, originalHeader: Buffer, keepOriginal: boolean) => void;
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. Adding or deleting filesystem entries is not supported. */
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 { 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 };
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 };
@@ -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-GLK3K2HY.js";
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,17 @@
1
+ import { a as DsiVerification } from './twl-complete-B2is30H0.js';
2
+
3
+ interface CreatePatchOptions extends PatchOptions {
4
+ metadata?: Omit<PatchMetadata, "formatVersion">;
5
+ readme?: string;
6
+ }
7
+ interface CreatePatchResult {
8
+ buffer: Uint8Array<ArrayBuffer>;
9
+ changedFiles: number;
10
+ warnings: string[];
11
+ }
12
+ /** Create an XZP; differences and application checks are returned as warnings. */
13
+ declare const createPatch: (original: Uint8Array, modified: Uint8Array, options?: CreatePatchOptions) => Promise<CreatePatchResult>;
14
+
1
15
  /**
2
16
  * @param {Buffer|Uint8Array} source Original bytes.
3
17
  * @param {Buffer|Uint8Array} patch VCDIFF bytes.
@@ -22,6 +36,7 @@ interface PatchResult {
22
36
  /** MD5 of the original input, before preprocessing. */
23
37
  inputMd5: string;
24
38
  outputMd5: string;
39
+ dsi?: DsiVerification;
25
40
  }
26
41
  interface PatchOptions {
27
42
  /** Maximum generated ROM/Xdelta target size in bytes, default 1 GiB. */
@@ -34,6 +49,7 @@ interface PatchOptions {
34
49
  maxEntries?: number;
35
50
  }
36
51
  interface PatchMetadata {
52
+ formatVersion?: number;
37
53
  id?: string;
38
54
  author?: string;
39
55
  name?: string;
@@ -80,4 +96,4 @@ declare const PatchHelper: Readonly<{
80
96
  readPatchReadmeFile: (patchPath: string, options?: PatchOptions) => Promise<PatchReadme | null>;
81
97
  }>;
82
98
 
83
- export { PatchHelper, type PatchInfo, type PatchMetadata, type PatchOptions, type PatchReadme, type PatchResult, PatchReturnValue, type XdeltaOptions, decodeXdelta, extractZipEntry, patchBuffer, patchIt, readPatchInfo, readPatchInfoFile, readPatchMetadata, readPatchMetadataFile, readPatchReadme, readPatchReadmeFile };
99
+ export { type CreatePatchOptions, type CreatePatchResult, PatchHelper, type PatchInfo, type PatchMetadata, type PatchOptions, type PatchReadme, type PatchResult, PatchReturnValue, type XdeltaOptions, createPatch, decodeXdelta, extractZipEntry, patchBuffer, patchIt, readPatchInfo, readPatchInfoFile, readPatchMetadata, readPatchMetadataFile, readPatchReadme, readPatchReadmeFile };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  PatchHelper,
3
3
  PatchReturnValue,
4
+ createPatch,
4
5
  decodeXdelta,
5
6
  extractZipEntry,
6
7
  patchBuffer,
@@ -11,11 +12,12 @@ import {
11
12
  readPatchMetadataFile,
12
13
  readPatchReadme,
13
14
  readPatchReadmeFile
14
- } from "./chunk-IGURDH5L.js";
15
- import "./chunk-GLK3K2HY.js";
15
+ } from "./chunk-ZQM3LT5L.js";
16
+ import "./chunk-LK3JXA24.js";
16
17
  export {
17
18
  PatchHelper,
18
19
  PatchReturnValue,
20
+ createPatch,
19
21
  decodeXdelta,
20
22
  extractZipEntry,
21
23
  patchBuffer,