nitro-patcher 0.2.0 → 0.2.1

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.
@@ -1,7 +1,7 @@
1
1
  // Port of NitroHelper/Header.cs. Copyright Xzonn and upstream contributors, GPL-3.0.
2
- import { bytes, range } from './binary';
3
- import { crc16 } from './crc';
4
- import { encryptSecureArea } from './crypto';
2
+ import { bytes, range } from "./binary";
3
+ import { crc16 } from "./crc";
4
+ import { encryptSecureArea } from "./crypto";
5
5
 
6
6
  export const UnitCode = Object.freeze({ NDS: 0, NDS_DSi: 2, DSi: 3 });
7
7
 
@@ -106,43 +106,30 @@ export class Header {
106
106
  declare secureDisable: bigint;
107
107
 
108
108
  constructor(input: Uint8Array) {
109
- const data = bytes(input, 'header');
110
- range(data, 0, 0x170, 'header');
109
+ const data = bytes(input, "header");
110
+ range(data, 0, 0x170, "header");
111
111
  const size = data.readUInt32LE(0x84);
112
112
  if (size < 0x170 || size > data.length) throw new RangeError(`Invalid header size: ${size}`);
113
113
  this.raw = Buffer.from(data.subarray(0, size));
114
- if (this.unitCode & 2 && size > 0x200) range(this.raw, 0, 0x1000, 'DSi header');
115
- this.trimmedRom = this.total_rom_size
116
- ? this.total_rom_size >= data.length
117
- : data.length !== this.size;
114
+ if (this.unitCode & 2 && size > 0x200) range(this.raw, 0, 0x1000, "DSi header");
115
+ this.trimmedRom = this.total_rom_size ? this.total_rom_size >= data.length : data.length !== this.size;
118
116
  this.doublePadding =
119
- (this.ARM9size % 0x400 < 0x200 &&
120
- this.ARM7romOffset % 0x400 === 0 &&
121
- this.ARM9overlayOffset % 0x400 === 0) ||
122
- (this.ARM7size % 0x400 < 0x200 &&
123
- this.FNToffset % 0x400 === 0 &&
124
- this.ARM7overlayOffset % 0x400 === 0) ||
117
+ (this.ARM9size % 0x400 < 0x200 && this.ARM7romOffset % 0x400 === 0 && this.ARM9overlayOffset % 0x400 === 0) ||
118
+ (this.ARM7size % 0x400 < 0x200 && this.FNToffset % 0x400 === 0 && this.ARM7overlayOffset % 0x400 === 0) ||
125
119
  (this.FNTsize % 0x400 < 0x200 && this.FAToffset % 0x400 === 0) ||
126
120
  (this.FATsize % 0x400 < 0x200 && this.bannerOffset % 0x400 === 0);
127
121
  const nitroOffset = this.ARM9romOffset + this.ARM9size;
128
122
  this.nitrocode =
129
- nitroOffset >= size &&
130
- nitroOffset + 4 <= data.length &&
131
- data.readUInt32LE(nitroOffset) === 0xdec00621;
123
+ nitroOffset >= size && nitroOffset + 4 <= data.length && data.readUInt32LE(nitroOffset) === 0xdec00621;
132
124
  this.decrypted = data.length >= 0x4008 && data.readBigUInt64LE(0x4000) === 0xe7ffdeffe7ffdeffn;
133
125
  this.headerCRC = crc16(this.raw.subarray(0, 0x15e)) === this.headerCRC16;
134
126
  this.logoCRC = crc16(this.logo) === this.logoCRC16;
135
127
  const secure = data.subarray(0x4000, 0x8000);
136
128
  this.secureCRC =
137
129
  secure.length > 0 &&
138
- crc16(this.decrypted ? encryptSecureArea(this.gameCode, secure) : secure) ===
139
- this.secureCRC16;
130
+ crc16(this.decrypted ? encryptSecureArea(this.gameCode, secure) : secure) === this.secureCRC16;
140
131
  this.dlp_signature = Buffer.alloc(0);
141
- if (
142
- this.ROMsize >= size &&
143
- this.ROMsize + 0x88 <= data.length &&
144
- data.readUInt32LE(this.ROMsize) === 0x00016361
145
- ) {
132
+ if (this.ROMsize >= size && this.ROMsize + 0x88 <= data.length && data.readUInt32LE(this.ROMsize) === 0x00016361) {
146
133
  this.dlp_signature = Buffer.from(data.subarray(this.ROMsize, this.ROMsize + 0x88));
147
134
  }
148
135
  }
@@ -153,7 +140,7 @@ export class Header {
153
140
  set size(value: number) {
154
141
  const power = Math.log2(value);
155
142
  if (!Number.isInteger(power) || power < 17 || power > 32)
156
- throw new RangeError('ROM capacity must be a power of two from 128 KiB to 4 GiB');
143
+ throw new RangeError("ROM capacity must be a power of two from 128 KiB to 4 GiB");
157
144
  this.raw[0x14] = power - 17;
158
145
  }
159
146
  toBuffer() {
@@ -163,7 +150,7 @@ export class Header {
163
150
  return data;
164
151
  }
165
152
  toString() {
166
- return `NDS header: ${this.gameTitle.replace(/\0+$/, '')} (${this.gameCode})`;
153
+ return `NDS header: ${this.gameTitle.replace(/\0+$/, "")} (${this.gameCode})`;
167
154
  }
168
155
  }
169
156
 
@@ -171,44 +158,44 @@ function field(
171
158
  name: string,
172
159
  offset: number,
173
160
  length: number,
174
- type: 'buffer' | 'string' | 'number' = 'buffer',
161
+ type: "buffer" | "string" | "number" = "buffer",
175
162
  dsi = false,
176
163
  ) {
177
164
  Object.defineProperty(Header.prototype, name, {
178
165
  enumerable: true,
179
166
  get(this: Header) {
180
167
  if (dsi && (!(this.unitCode & 2) || this.raw.length <= 0x200))
181
- return type === 'buffer' ? Buffer.alloc(length) : 0;
168
+ return type === "buffer" ? Buffer.alloc(length) : 0;
182
169
  const data = range(this.raw, offset, length, `header ${name}`);
183
- if (type === 'string') return data.toString('latin1');
184
- if (type === 'buffer') return data;
170
+ if (type === "string") return data.toString("latin1");
171
+ if (type === "buffer") return data;
185
172
  return length === 8 ? data.readBigUInt64LE() : data.readUIntLE(0, length);
186
173
  },
187
174
  set(this: Header, value: string | number | bigint | Uint8Array) {
188
175
  const target = range(this.raw, offset, length, `header ${name}`);
189
- if (type === 'string') {
190
- if (typeof value !== 'string' || Buffer.byteLength(value, 'latin1') > length)
176
+ if (type === "string") {
177
+ if (typeof value !== "string" || Buffer.byteLength(value, "latin1") > length)
191
178
  throw new TypeError(`Invalid ${name}`);
192
179
  target.fill(0);
193
- target.write(value, 'latin1');
194
- } else if (type === 'buffer') {
180
+ target.write(value, "latin1");
181
+ } else if (type === "buffer") {
195
182
  if (!(value instanceof Uint8Array)) throw new TypeError(`Invalid ${name}`);
196
183
  const source = bytes(value, name);
197
184
  if (source.length !== length) throw new RangeError(`${name} must contain ${length} bytes`);
198
185
  source.copy(target);
199
186
  } else if (length === 8) {
200
- if (typeof value !== 'bigint') throw new TypeError(`Invalid ${name}`);
187
+ if (typeof value !== "bigint") throw new TypeError(`Invalid ${name}`);
201
188
  target.writeBigUInt64LE(value);
202
189
  } else {
203
- if (typeof value !== 'number') throw new TypeError(`Invalid ${name}`);
190
+ if (typeof value !== "number") throw new TypeError(`Invalid ${name}`);
204
191
  target.writeUIntLE(value, 0, length);
205
192
  }
206
193
  },
207
194
  });
208
195
  }
209
- field('gameTitle', 0, 12, 'string');
210
- field('gameCode', 12, 4, 'string');
211
- field('makerCode', 16, 2, 'string');
196
+ field("gameTitle", 0, 12, "string");
197
+ field("gameCode", 12, 4, "string");
198
+ field("makerCode", 16, 2, "string");
212
199
  for (const [name, offset] of Object.entries({
213
200
  unitCode: 0x12,
214
201
  encryptionSeed: 0x13,
@@ -217,38 +204,38 @@ for (const [name, offset] of Object.entries({
217
204
  ROMversion: 0x1e,
218
205
  internalFlags: 0x1f,
219
206
  }))
220
- field(name, offset, 1, 'number');
221
- field('reserved', 0x15, 7);
207
+ field(name, offset, 1, "number");
208
+ field("reserved", 0x15, 7);
222
209
  const ntr32 =
223
- 'ARM9romOffset ARM9entryAddress ARM9ramAddress ARM9size ARM7romOffset ARM7entryAddress ARM7ramAddress ARM7size FNToffset FNTsize FAToffset FATsize ARM9overlayOffset ARM9overlaySize ARM7overlayOffset ARM7overlaySize flagsRead flagsInit bannerOffset'.split(
224
- ' ',
210
+ "ARM9romOffset ARM9entryAddress ARM9ramAddress ARM9size ARM7romOffset ARM7entryAddress ARM7ramAddress ARM7size FNToffset FNTsize FAToffset FATsize ARM9overlayOffset ARM9overlaySize ARM7overlayOffset ARM7overlaySize flagsRead flagsInit bannerOffset".split(
211
+ " ",
225
212
  );
226
- ntr32.forEach((name, i) => field(name, 0x20 + i * 4, 4, 'number'));
227
- field('secureCRC16', 0x6c, 2, 'number');
228
- field('ROMtimeout', 0x6e, 2, 'number');
229
- field('ARM9autoload', 0x70, 4, 'number');
230
- field('ARM7autoload', 0x74, 4, 'number');
231
- field('secureDisable', 0x78, 8, 'number');
232
- field('ROMsize', 0x80, 4, 'number');
233
- field('headerSize', 0x84, 4, 'number');
234
- field('reserved2', 0x88, 56);
235
- field('logo', 0xc0, 156);
236
- field('logoCRC16', 0x15c, 2, 'number');
237
- field('headerCRC16', 0x15e, 2, 'number');
238
- 'debug_romOffset debug_size debug_ramAddress reserved3'
239
- .split(' ')
240
- .forEach((name, i) => field(name, 0x160 + i * 4, 4, 'number'));
241
- field('appflags', 0x1bc, 4, 'buffer', true);
242
- 'mbk9_wramcnt_setting region_flags access_control scfg_ext_mask'
243
- .split(' ')
244
- .forEach((name, i) => field(name, 0x1ac + i * 4, 4, 'number', true));
213
+ ntr32.forEach((name, i) => field(name, 0x20 + i * 4, 4, "number"));
214
+ field("secureCRC16", 0x6c, 2, "number");
215
+ field("ROMtimeout", 0x6e, 2, "number");
216
+ field("ARM9autoload", 0x70, 4, "number");
217
+ field("ARM7autoload", 0x74, 4, "number");
218
+ field("secureDisable", 0x78, 8, "number");
219
+ field("ROMsize", 0x80, 4, "number");
220
+ field("headerSize", 0x84, 4, "number");
221
+ field("reserved2", 0x88, 56);
222
+ field("logo", 0xc0, 156);
223
+ field("logoCRC16", 0x15c, 2, "number");
224
+ field("headerCRC16", 0x15e, 2, "number");
225
+ "debug_romOffset debug_size debug_ramAddress reserved3"
226
+ .split(" ")
227
+ .forEach((name, i) => field(name, 0x160 + i * 4, 4, "number"));
228
+ field("appflags", 0x1bc, 4, "buffer", true);
229
+ "mbk9_wramcnt_setting region_flags access_control scfg_ext_mask"
230
+ .split(" ")
231
+ .forEach((name, i) => field(name, 0x1ac + i * 4, 4, "number", true));
245
232
  const twl32 =
246
- 'dsi9_rom_offset offset_0x1C4 dsi9_ram_address dsi9_size dsi7_rom_offset offset_0x1D4 dsi7_ram_address dsi7_size digest_ntr_start digest_ntr_size digest_twl_start digest_twl_size sector_hashtable_start sector_hashtable_size block_hashtable_start block_hashtable_size digest_sector_size digest_block_sectorcount banner_size offset_0x20C total_rom_size offset_0x214 offset_0x218 offset_0x21C modcrypt1_start modcrypt1_size modcrypt2_start modcrypt2_size tid_low tid_high public_sav_size private_sav_size'.split(
247
- ' ',
233
+ "dsi9_rom_offset offset_0x1C4 dsi9_ram_address dsi9_size dsi7_rom_offset offset_0x1D4 dsi7_ram_address dsi7_size digest_ntr_start digest_ntr_size digest_twl_start digest_twl_size sector_hashtable_start sector_hashtable_size block_hashtable_start block_hashtable_size digest_sector_size digest_block_sectorcount banner_size offset_0x20C total_rom_size offset_0x214 offset_0x218 offset_0x21C modcrypt1_start modcrypt1_size modcrypt2_start modcrypt2_size tid_low tid_high public_sav_size private_sav_size".split(
234
+ " ",
248
235
  );
249
- twl32.forEach((name, i) => field(name, 0x1c0 + i * 4, 4, 'number', true));
250
- 'hmac_arm9 hmac_arm7 hmac_digest_master hmac_icon_title hmac_arm9i hmac_arm7i'
251
- .split(' ')
252
- .forEach((name, i) => field(name, 0x300 + i * 20, 20, 'buffer', true));
253
- field('hmac_arm9_no_secure', 0x3a0, 20, 'buffer', true);
254
- field('rsa_signature', 0xf80, 0x80, 'buffer', true);
236
+ twl32.forEach((name, i) => field(name, 0x1c0 + i * 4, 4, "number", true));
237
+ "hmac_arm9 hmac_arm7 hmac_digest_master hmac_icon_title hmac_arm9i hmac_arm7i"
238
+ .split(" ")
239
+ .forEach((name, i) => field(name, 0x300 + i * 20, 20, "buffer", true));
240
+ field("hmac_arm9_no_secure", 0x3a0, 20, "buffer", true);
241
+ field("rsa_signature", 0xf80, 0x80, "buffer", true);
@@ -1,13 +1,14 @@
1
1
  // Native TypeScript port of NitroHelper/NDSFile.cs, GPL-3.0.
2
- import { readFile, writeFile } from '#nitro-runtime';
3
- import { BinaryWriter, bytes, range, DEFAULT_MAX_SIZE } from './binary';
4
- import { Header } from './header';
5
- import { Banner } from './banner';
6
- import { FileAllocationTable, FileNameTable, OverlayTable } from './filesystem';
7
- import type { NitroFile, NitroFolder } from './filesystem';
8
- import { crc16 } from './crc';
9
- import { encryptSecureArea } from './crypto';
10
- import { TWL } from './twl';
2
+ import { readFile, writeFile } from "#nitro-runtime";
3
+
4
+ import { Banner } from "./banner";
5
+ import { BinaryWriter, DEFAULT_MAX_SIZE, bytes, range } from "./binary";
6
+ import { crc16 } from "./crc";
7
+ import { encryptSecureArea } from "./crypto";
8
+ import { FileAllocationTable, FileNameTable, OverlayTable } from "./filesystem";
9
+ import type { NitroFile, NitroFolder } from "./filesystem";
10
+ import { Header } from "./header";
11
+ import { TWL } from "./twl";
11
12
 
12
13
  export interface RomOptions {
13
14
  /** Maximum generated ROM size in bytes. Defaults to 1 GiB. */
@@ -22,7 +23,7 @@ export interface RomFileInfo {
22
23
  size: number;
23
24
  }
24
25
 
25
- const key = (path: string): string => path.replaceAll('\\', '/').toLowerCase();
26
+ const key = (path: string): string => path.replaceAll("\\", "/").toLowerCase();
26
27
 
27
28
  /** Parsed Nintendo DS ROM. Replacements do not change the input buffer. */
28
29
  export class NDSFile {
@@ -40,31 +41,21 @@ export class NDSFile {
40
41
  private readonly maxOutputSize: number;
41
42
 
42
43
  constructor(input: Uint8Array, options: RomOptions = {}) {
43
- this.original = bytes(input, 'ROM');
44
+ this.original = bytes(input, "ROM");
44
45
  this.maxOutputSize = options.maxOutputSize ?? DEFAULT_MAX_SIZE;
45
46
  this.header = new Header(this.original);
46
47
  const h = this.header;
47
- range(this.original, h.ARM9romOffset, h.ARM9size + (h.nitrocode ? 12 : 0), 'ARM9');
48
- range(this.original, h.ARM7romOffset, h.ARM7size, 'ARM7');
48
+ range(this.original, h.ARM9romOffset, h.ARM9size + (h.nitrocode ? 12 : 0), "ARM9");
49
+ range(this.original, h.ARM7romOffset, h.ARM7size, "ARM7");
49
50
  this.banner = new Banner(this.original, h.bannerOffset, h.banner_size);
50
51
  this.fatTable = new FileAllocationTable(this.original, h.FAToffset, h.FATsize);
51
52
  this.fntTable = new FileNameTable(this.fatTable, this.original, h.FNToffset, h.FNTsize);
52
53
  this.root = this.fntTable.root;
53
- this.overlay9Table = new OverlayTable(
54
- this.original,
55
- h.ARM9overlayOffset,
56
- h.ARM9overlaySize,
57
- true,
58
- );
59
- this.overlay7Table = new OverlayTable(
60
- this.original,
61
- h.ARM7overlayOffset,
62
- h.ARM7overlaySize,
63
- false,
64
- );
54
+ this.overlay9Table = new OverlayTable(this.original, h.ARM9overlayOffset, h.ARM9overlaySize, true);
55
+ this.overlay7Table = new OverlayTable(this.original, h.ARM7overlayOffset, h.ARM7overlaySize, false);
65
56
  const arm9Overlays = this.overlay9Table.readBasicOverlays(this.fatTable);
66
57
  this.root.folders.push({
67
- name: 'overlay',
58
+ name: "overlay",
68
59
  id: 0xffff,
69
60
  files: [...arm9Overlays, ...this.overlay7Table.readBasicOverlays(this.fatTable)],
70
61
  folders: [],
@@ -73,23 +64,23 @@ export class NDSFile {
73
64
  range(this.original, offset, size, name);
74
65
  this.root.files.push({ name, id: 0xffff, offset, size });
75
66
  };
76
- system('header.bin', 0, h.headerSize);
77
- system('banner.bin', h.bannerOffset, this.banner.raw.length);
78
- system('fnt.bin', h.FNToffset, h.FNTsize);
79
- system('fat.bin', h.FAToffset, h.FATsize);
80
- system('arm9.bin', h.ARM9romOffset, h.ARM9size + (h.nitrocode ? 12 : 0));
81
- system('arm7.bin', h.ARM7romOffset, h.ARM7size);
82
- if (h.ARM9overlaySize) system('overarm9.bin', h.ARM9overlayOffset, h.ARM9overlaySize);
83
- if (h.ARM7overlaySize) system('overarm7.bin', h.ARM7overlayOffset, h.ARM7overlaySize);
67
+ system("header.bin", 0, h.headerSize);
68
+ system("banner.bin", h.bannerOffset, this.banner.raw.length);
69
+ system("fnt.bin", h.FNToffset, h.FNTsize);
70
+ system("fat.bin", h.FAToffset, h.FATsize);
71
+ system("arm9.bin", h.ARM9romOffset, h.ARM9size + (h.nitrocode ? 12 : 0));
72
+ system("arm7.bin", h.ARM7romOffset, h.ARM7size);
73
+ if (h.ARM9overlaySize) system("overarm9.bin", h.ARM9overlayOffset, h.ARM9overlaySize);
74
+ if (h.ARM7overlaySize) system("overarm7.bin", h.ARM7overlayOffset, h.ARM7overlaySize);
84
75
  const visit = (folder: NitroFolder, prefix: string): void => {
85
76
  for (const file of folder.files) {
86
77
  const path = prefix + file.name;
87
78
  if (this.paths.has(key(path))) throw new Error(`Duplicate ROM path: ${path}`);
88
79
  this.paths.set(key(path), { path, file });
89
80
  }
90
- for (const child of folder.folders) visit(child, prefix + child.name + '/');
81
+ for (const child of folder.folders) visit(child, `${prefix + child.name}/`);
91
82
  };
92
- visit(this.root, '');
83
+ visit(this.root, "");
93
84
  if (h.unitCode & 2 && h.twlInternalFlags & 1 && h.tid_high && h.tid_high !== 0xffffffff) {
94
85
  // Upstream silently discarded malformed TWL regions. Preserve valid DSi data,
95
86
  // and reject malformed data instead of emitting a ROM with missing sections.
@@ -135,20 +126,13 @@ export class NDSFile {
135
126
 
136
127
  /** Rebuild offsets, allocation table, CRCs and ROM padding. Safe to call repeatedly. */
137
128
  toBuffer(): Buffer {
138
- const fntReplacement = this.root.files.find((file) => file.name === 'fnt.bin')?.replacement;
129
+ const fntReplacement = this.root.files.find((file) => file.name === "fnt.bin")?.replacement;
139
130
  if (fntReplacement) {
140
- const replacementTree = new FileNameTable(
141
- this.fatTable,
142
- fntReplacement,
143
- 0,
144
- fntReplacement.length,
145
- );
131
+ const replacementTree = new FileNameTable(this.fatTable, fntReplacement, 0, fntReplacement.length);
146
132
  const collect = (folder: NitroFolder): number[] =>
147
- [...folder.files.map((file) => file.id), ...folder.folders.flatMap(collect)].sort(
148
- (a, b) => a - b,
149
- );
150
- if (collect(replacementTree.root).join(',') !== collect(this.data).join(','))
151
- throw new Error('Replacement FNT must preserve existing file IDs');
133
+ [...folder.files.map((file) => file.id), ...folder.folders.flatMap(collect)].sort((a, b) => a - b);
134
+ if (collect(replacementTree.root).join(",") !== collect(this.data).join(","))
135
+ throw new Error("Replacement FNT must preserve existing file IDs");
152
136
  }
153
137
  const h = new Header(this.header.toBuffer());
154
138
  h.nitrocode = this.header.nitrocode;
@@ -161,7 +145,7 @@ export class NDSFile {
161
145
  if (!file) throw new Error(`Missing ROM system file: ${name}`);
162
146
  return file;
163
147
  };
164
- const replacedHeader = system('header.bin').replacement;
148
+ const replacedHeader = system("header.bin").replacement;
165
149
  if (replacedHeader) {
166
150
  const replacement = new Header(replacedHeader);
167
151
  h.gameTitle = replacement.gameTitle;
@@ -177,13 +161,13 @@ export class NDSFile {
177
161
  const overlayIds = new Set<number>();
178
162
  const writeOverlays = (arm9: boolean): void => {
179
163
  const originalTable = arm9 ? this.overlay9Table : this.overlay7Table;
180
- const name = arm9 ? 'overarm9.bin' : 'overarm7.bin';
164
+ const name = arm9 ? "overarm9.bin" : "overarm7.bin";
181
165
  const entry = this.root.files.find((file) => file.name === name);
182
166
  if (!entry) return;
183
167
  const raw = entry.replacement ?? originalTable.toBuffer();
184
168
  const table = new OverlayTable(raw, 0, raw.length, arm9);
185
169
  const files = this.overlay.files
186
- .filter((file) => file.name.startsWith(arm9 ? 'overlay_' : 'overlay7_'))
170
+ .filter((file) => file.name.startsWith(arm9 ? "overlay_" : "overlay7_"))
187
171
  .sort((a, b) => a.id - b.id);
188
172
  for (const file of files) {
189
173
  const item = table.entries.find((item) => item.fileId === file.id);
@@ -193,8 +177,7 @@ export class NDSFile {
193
177
  item.reserved = ((item.reserved & 0xff000000) | (size & 0xffffff)) >>> 0;
194
178
  overlayIds.add(file.id);
195
179
  }
196
- if (table.entries.length !== files.length)
197
- throw new Error('Changing overlay file count is unsupported');
180
+ if (table.entries.length !== files.length) throw new Error("Changing overlay file count is unsupported");
198
181
  const encoded = table.toBuffer();
199
182
  if (arm9) {
200
183
  h.ARM9overlayOffset = writer.position;
@@ -209,30 +192,29 @@ export class NDSFile {
209
192
  };
210
193
  writer.position = h.headerSize;
211
194
  h.ARM9romOffset = writer.position;
212
- const arm9 = system('arm9.bin');
195
+ const arm9 = system("arm9.bin");
213
196
  const arm9Bytes = this.fileBytes(arm9);
214
197
  if (arm9.replacement) {
215
- h.nitrocode =
216
- arm9Bytes.length >= 12 && arm9Bytes.readUInt32LE(arm9Bytes.length - 12) === 0xdec00621;
198
+ h.nitrocode = arm9Bytes.length >= 12 && arm9Bytes.readUInt32LE(arm9Bytes.length - 12) === 0xdec00621;
217
199
  }
218
200
  h.ARM9size = arm9Bytes.length - (h.nitrocode ? 12 : 0);
219
201
  write(arm9);
220
202
  h.ARM9overlayOffset = 0;
221
203
  writeOverlays(true);
222
204
  h.ARM7romOffset = writer.position;
223
- h.ARM7size = this.fileBytes(system('arm7.bin')).length;
224
- write(system('arm7.bin'));
205
+ h.ARM7size = this.fileBytes(system("arm7.bin")).length;
206
+ write(system("arm7.bin"));
225
207
  h.ARM7overlayOffset = 0;
226
208
  writeOverlays(false);
227
209
  h.FNToffset = writer.position;
228
- h.FNTsize = this.fileBytes(system('fnt.bin')).length;
229
- write(system('fnt.bin'));
210
+ h.FNTsize = this.fileBytes(system("fnt.bin")).length;
211
+ write(system("fnt.bin"));
230
212
  h.FAToffset = writer.position;
231
213
  h.FATsize = this.fatTable.entries.length * 8;
232
214
  writer.write(Buffer.alloc(h.FATsize));
233
215
  writer.pad(0x200);
234
216
  h.bannerOffset = writer.position;
235
- const bannerFile = system('banner.bin');
217
+ const bannerFile = system("banner.bin");
236
218
  const banner = bannerFile.replacement
237
219
  ? new Banner(bannerFile.replacement, 0, bannerFile.replacement.length)
238
220
  : this.banner;
@@ -265,8 +247,7 @@ export class NDSFile {
265
247
  writer.position = logicalEnd;
266
248
  // An ARM9 replacement can add or remove the decrypted secure-area marker.
267
249
  // Both TWL hashing and the header CRC must use the bytes being serialized.
268
- h.decrypted =
269
- writer.length >= 0x4008 && writer.buffer.readBigUInt64LE(0x4000) === 0xe7ffdeffe7ffdeffn;
250
+ h.decrypted = writer.length >= 0x4008 && writer.buffer.readBigUInt64LE(0x4000) === 0xe7ffdeffe7ffdeffn;
270
251
  if (this.twl) this.twl.writeTo(writer, h);
271
252
  h.ROMsize = writer.position;
272
253
  h.size = 2 ** Math.ceil(Math.log2(Math.max(h.ROMsize + h.dlp_signature.length, 0x20000)));