4internal static class NdsImageBuildImporter
10 public static async ValueTask<NdsImageBuilder> ImportAsync(
12 CancellationToken cancellationToken)
14 ArgumentNullException.ThrowIfNull(image);
15 byte[] arm9 = await ReadRegionAsync(image, image.Header.Arm9.Data, cancellationToken).ConfigureAwait(
false);
16 byte[] arm7 = await ReadRegionAsync(image, image.Header.Arm7.Data, cancellationToken).ConfigureAwait(
false);
17 var arm9Definition =
new NdsProgramDefinition(
20 image.Header.Arm9.LoadAddress,
21 image.Header.Arm9.EntryAddress);
22 if (image.Header.Arm9.Footer is not
null)
24 arm9Definition.SetFooter(
25 await ReadRegionAsync(image, image.Header.Arm9.Footer.Value, cancellationToken).ConfigureAwait(
false));
28 NdsProgramDefinition? arm9iDefinition = await ImportDsiProgramAsync(
31 cancellationToken).ConfigureAwait(
false);
32 NdsProgramDefinition? arm7iDefinition = await ImportDsiProgramAsync(
35 cancellationToken).ConfigureAwait(
false);
36 NdsDsiBuildMetadata? dsiMetadata = image.Header.Dsi is
null
38 : NdsDsiBuildMetadata.FromHeader(image.Header.Dsi);
39 if (dsiMetadata is not
null)
41 dsiMetadata.DsiFlags = image.Header.DsiFlags;
42 dsiMetadata.AnchorModcryptAreas(
43 new[] { image.Header.Arm9, image.Header.Arm7, image.Header.Arm9i, image.Header.Arm7i }
44 .OfType<NdsProgram>());
47 var builder =
new NdsImageBuilder
49 Kind = image.Header.Kind,
50 Title = image.Header.Title,
51 GameCode = image.Header.GameCode,
52 MakerCode = image.Header.MakerCode,
53 Version = image.Header.Version,
54 EncryptionSeedSelect = image.Header.EncryptionSeedSelect,
55 RegionCode = image.Header.RegionCode,
56 AutoStart = image.Header.AutoStart,
57 NormalCardControl = image.Header.NormalCardControl,
58 SecureCardControl = image.Header.SecureCardControl,
59 SecureTransferTimeout = image.Header.SecureTransferTimeout,
60 Arm9AutoLoad = image.Header.Arm9AutoLoad,
61 Arm7AutoLoad = image.Header.Arm7AutoLoad,
62 SecureDisable = image.Header.SecureDisable,
63 Arm9 = arm9Definition,
67 image.Header.Arm7.LoadAddress,
68 image.Header.Arm7.EntryAddress),
69 Arm9i = arm9iDefinition,
70 Arm7i = arm7iDefinition,
71 DsiMetadata = dsiMetadata,
74 builder.SetNintendoLogo(image.Header.RawData.Span.Slice(0xC0, 156));
76 foreach (NdsDirectory directory
in image.FileSystem.Directories)
78 if (directory.Parent is not
null)
80 builder.FileSystem.CreateDirectory(directory.FullPath);
84 foreach (NdsFile file
in image.FileSystem.Files)
86 builder.FileSystem.AddFile(
88 await file.ReadAllBytesAsync(cancellationToken).ConfigureAwait(
false));
91 var importedPrivateFileIds =
new HashSet<uint>();
92 foreach (NdsOverlay overlay
in image.Arm9Overlays.Concat(image.Arm7Overlays))
94 builder.AddOverlay(await ImportOverlayAsync(
98 importedPrivateFileIds,
99 cancellationToken).ConfigureAwait(
false));
110 private static async ValueTask<NdsProgramDefinition?> ImportDsiProgramAsync(
113 CancellationToken cancellationToken)
122 await ReadRegionAsync(image, program.Data, cancellationToken).ConfigureAwait(
false),
124 program.LoadAddress);
134 private static async ValueTask<NdsOverlayDefinition> ImportOverlayAsync(
137 NdsFileSystemBuilder fileSystem,
138 HashSet<uint> importedPrivateFileIds,
139 CancellationToken cancellationToken)
141 if (overlay.File is not
null)
143 return NdsOverlayDefinition.LinkToFile(
146 fileSystem.GetFile(overlay.File.FullPath),
150 overlay.StaticInitializerStart,
151 overlay.StaticInitializerEnd,
152 overlay.CompressedSize,
156 if (overlay.Data is
null)
158 throw new InvalidDataException($
"Overlay {overlay.Id} references missing File ID {overlay.FileId}.");
161 if (!importedPrivateFileIds.Add(overlay.FileId))
163 throw new NotSupportedException(
"Multiple Overlays sharing one unnamed Allocation require an explicit shared-allocation recipe.");
169 await ReadRegionAsync(image, overlay.Data.Value, cancellationToken).ConfigureAwait(
false),
173 overlay.StaticInitializerStart,
174 overlay.StaticInitializerEnd,
175 overlay.CompressedSize,
184 private static async ValueTask<byte[]> ReadRegionAsync(
187 CancellationToken cancellationToken)
189 if (region.Length > Array.MaxLength)
191 throw new IOException(
"A build component is too large to materialize in a detached recipe.");
194 using Stream stream = image.OpenRead(region);
195 byte[] data =
new byte[region.Length];
196 await stream.ReadExactlyAsync(data, cancellationToken).ConfigureAwait(
false);
@ Banner
Exports the complete version-sized banner structure, including reserved and animated DSi bytes.
NdsProcessor
Identifies a processor and execution mode.
@ Arm7
The ARM7 program used in Nintendo DS mode.
@ Arm9i
The ARM9 program used in DSi mode.
@ Arm7i
The ARM7 program used in DSi mode.
@ Arm9
The ARM9 program used in Nintendo DS mode.