4internal static class NdsImageLayoutPlanner
11 public static NdsImageBuildLayout Plan(
12 NdsImageBuilder builder,
13 NdsImageBuildContent content,
14 NdsImageBuildOptions options)
17 long cursor = options.HeaderSize;
18 NdsRegion arm9 = PlaceProgram(
20 content.Arm9Data.Length,
21 content.Arm9DeclaredLength,
22 options.SectionAlignment);
23 NdsRegion? arm9Footer = content.Arm9TrailingData.IsEmpty
25 : Place(ref cursor, content.Arm9TrailingData.Length, alignment: 1);
26 NdsRegion arm9Overlays = content.Arm9OverlayTable.Length == 0
28 : Place(ref cursor, content.Arm9OverlayTable.Length, ndstoolProfile ? 1 : options.SectionAlignment);
29 NdsRegion arm7 = PlaceProgram(
31 content.Arm7Data.Length,
32 content.Arm7DeclaredLength,
33 options.SectionAlignment);
34 NdsRegion arm7Overlays = content.Arm7OverlayTable.Length == 0
36 : Place(ref cursor, content.Arm7OverlayTable.Length, ndstoolProfile ? 1 : options.SectionAlignment);
37 NdsRegion fnt = Place(ref cursor, content.FileSystem.FileNameTable.Length, options.SectionAlignment);
38 NdsRegion fat = Place(ref cursor, checked(content.Allocations.Length * 8), options.SectionAlignment);
39 NdsRegion? banner = builder.Banner is
null
41 : Place(ref cursor, builder.Banner.RawData.Length, options.SectionAlignment);
42 NdsRegion[] files = PlaceAllocations(ref cursor, content, options, ndstoolProfile);
66 private static NdsRegion[] PlaceAllocations(
68 NdsImageBuildContent content,
69 NdsImageBuildOptions options,
72 var files =
new NdsRegion[content.Allocations.Length];
73 int alignment = ndstoolProfile ? options.SectionAlignment : options.FileAlignment;
74 for (
int fileId = 0; fileId < files.Length; fileId++)
76 files[fileId] = Place(ref cursor, content.Allocations[fileId].Length, alignment);
98 private static NdsImageBuildLayout PlanImageEnd(
99 NdsImageBuilder builder,
100 NdsImageBuildContent content,
101 NdsImageBuildOptions options,
103 NdsRegion? arm9Footer,
104 NdsRegion arm9Overlays,
106 NdsRegion arm7Overlays,
111 long commonContentEnd,
114 long cursor = commonContentEnd;
115 NdsRegion? arm9i =
null;
116 NdsRegion? arm7i =
null;
117 NdsRegion ntrDigest =
default;
118 NdsRegion twlDigest =
default;
119 NdsRegion sectorHashTable =
default;
120 NdsRegion blockHashTable =
default;
125 physicalSize = Align(commonContentEnd, options.FileAlignment);
126 usedSize = ndstoolProfile ? physicalSize : commonContentEnd;
130 usedSize = Align(commonContentEnd, options.FileAlignment);
132 arm9i = Place(ref cursor, content.Arm9iData.Length, alignment: 0x400);
133 arm7i = Place(ref cursor, content.Arm7iData.Length, options.SectionAlignment);
134 if (builder.DsiMetadata!.Digests is not
null)
136 NdsDsiDigestOptions digestOptions = builder.DsiMetadata.Digests;
137 ntrDigest =
new(arm9.Offset, checked(usedSize - arm9.Offset));
138 twlDigest =
new(arm9i.Value.Offset, checked(arm7i.Value.End - arm9i.Value.Offset));
139 long sectorCount = checked(
140 DivideRoundUp(ntrDigest.Length, digestOptions.SectorSize) +
141 DivideRoundUp(twlDigest.Length, digestOptions.SectorSize));
142 sectorHashTable = Place(ref cursor, checked(sectorCount * 20), digestOptions.SectorSize);
143 long blockCount = DivideRoundUp(sectorCount, digestOptions.BlockSectorCount);
144 blockHashTable = Place(ref cursor, checked(blockCount * 20), options.SectionAlignment);
147 physicalSize = Align(cursor, options.SectionAlignment);
150 if (physicalSize > uint.MaxValue)
152 throw new InvalidDataException(
"The generated image exceeds the DS header's 32-bit offset and size fields.");
180 private static NdsRegion Place(ref
long cursor,
long length,
int alignment)
182 cursor = Align(cursor, alignment);
183 var region =
new NdsRegion(cursor, length);
194 private static NdsRegion PlaceProgram(
200 if (declaredLength < physicalLength)
202 throw new InvalidDataException(
"A Program's declared length cannot exclude physical executable bytes.");
205 cursor = Align(cursor, alignment);
206 var region =
new NdsRegion(cursor, declaredLength);
207 cursor = checked(cursor + physicalLength);
215 private static long Align(
long value,
int alignment) => checked((value + alignment - 1) & -alignment);
221 private static long DivideRoundUp(
long value,
int divisor) => checked((value + divisor - 1) / divisor);
NdsImageKind
Identifies the hardware family targeted by an image.
NdsImageBuildProfile
Selects a documented Layout personality without contaminating the logical Build Recipe.