1using System.Buffers.Binary;
11internal static class NdsImageHeaderWriter
24 public static byte[] Write(
25 NdsImageBuilder builder,
26 NdsImageBuildLayout layout,
27 NdsImageBuildContent content,
28 NdsImageBuildOptions options,
29 NdsDsiDigestBuildResult? digestResult)
31 byte[] header =
new byte[options.HeaderSize];
32 WriteAscii(header.AsSpan(0x00, 12), builder.Title);
33 WriteAscii(header.AsSpan(0x0C, 4), builder.GameCode);
34 WriteAscii(header.AsSpan(0x10, 2), builder.MakerCode);
35 header[0x12] = (byte)builder.Kind;
36 header[0x13] = builder.EncryptionSeedSelect;
37 header[0x14] = CalculateDeviceCapacity(layout.PhysicalSize);
38 header[0x1D] = builder.RegionCode;
39 header[0x1C] = builder.DsiMetadata?.DsiFlags ?? 0;
40 header[0x1E] = builder.Version;
41 header[0x1F] = builder.AutoStart;
42 WriteProgram(header, 0x20, layout.Arm9, builder.Arm9!);
43 WriteProgram(header, 0x30, layout.Arm7, builder.Arm7!);
44 WriteRegion(header, 0x40, layout.FileNameTable);
45 WriteRegion(header, 0x48, layout.FileAllocationTable);
46 WriteRegion(header, 0x50, layout.Arm9OverlayTable);
47 WriteRegion(header, 0x58, layout.Arm7OverlayTable);
50 NdsBinary.WriteUInt32(header, 0x60, ndstoolProfile ? 0x00406000U : builder.NormalCardControl);
51 NdsBinary.WriteUInt32(header, 0x64, ndstoolProfile ? 0x20000000U : builder.SecureCardControl);
52 BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(0x68), checked((uint)(layout.Banner?.Offset ?? 0)));
53 NdsBinary.WriteUInt16(header, 0x6E, ndstoolProfile ? (ushort)0x051E : builder.SecureTransferTimeout);
54 NdsBinary.WriteUInt32(header, 0x70, builder.Arm9AutoLoad);
55 NdsBinary.WriteUInt32(header, 0x74, builder.Arm7AutoLoad);
56 NdsBinary.WriteUInt32(header, 0x78, (uint)builder.SecureDisable);
57 NdsBinary.WriteUInt32(header, 0x7C, (uint)(builder.SecureDisable >> 32));
58 BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(0x80), checked((uint)layout.UsedSize));
59 BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(0x84), checked((uint)options.HeaderSize));
60 if (!builder.NintendoLogo.IsEmpty)
62 builder.NintendoLogo.Span.CopyTo(header.AsSpan(0xC0, 156));
67 NdsDsiHeaderWriter.Write(header, builder, layout, content, digestResult);
70 BinaryPrimitives.WriteUInt16LittleEndian(
72 NdsChecksums.ComputeCrc16(header.AsSpan(0xC0, 156)));
73 BinaryPrimitives.WriteUInt16LittleEndian(
75 NdsChecksums.ComputeCrc16(header.AsSpan(0, 0x15E)));
78 NdsDsiHeaderWriter.FinalizeSignature(header, builder.DsiMetadata!.Integrity);
93 private static void WriteProgram(Span<byte> header,
int offset, NdsRegion region, NdsProgramDefinition program)
95 NdsBinary.WriteUInt32(header, offset, checked((uint)region.Offset));
96 NdsBinary.WriteUInt32(header, offset + 4, program.EntryAddress);
97 NdsBinary.WriteUInt32(header, offset + 8, program.LoadAddress);
98 NdsBinary.WriteUInt32(header, offset + 12, checked((uint)region.Length));
109 private static void WriteRegion(Span<byte> header,
int offset, NdsRegion region)
111 NdsBinary.WriteUInt32(header, offset, checked((uint)region.Offset));
112 NdsBinary.WriteUInt32(header, offset + 4, checked((uint)region.Length));
121 private static byte CalculateDeviceCapacity(
long physicalSize)
124 long capacity = 128 * 1024;
125 while (capacity < physicalSize && exponent < 31)
140 private static void WriteAscii(Span<byte> destination,
string value) => Encoding.ASCII.GetBytes(value, destination);
NdsImageKind
Identifies the hardware family targeted by an image.
NdsImageBuildProfile
Selects a documented Layout personality without contaminating the logical Build Recipe.