NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsImageBuildImporter.cs
1namespace NdsForge;
2
4internal static class NdsImageBuildImporter
5{
10 public static async ValueTask<NdsImageBuilder> ImportAsync(
11 NdsImage image,
12 CancellationToken cancellationToken)
13 {
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(
18 NdsProcessor.Arm9,
19 arm9,
20 image.Header.Arm9.LoadAddress,
21 image.Header.Arm9.EntryAddress);
22 if (image.Header.Arm9.Footer is not null)
23 {
24 arm9Definition.SetFooter(
25 await ReadRegionAsync(image, image.Header.Arm9.Footer.Value, cancellationToken).ConfigureAwait(false));
26 }
27
28 NdsProgramDefinition? arm9iDefinition = await ImportDsiProgramAsync(
29 image,
30 image.Header.Arm9i,
31 cancellationToken).ConfigureAwait(false);
32 NdsProgramDefinition? arm7iDefinition = await ImportDsiProgramAsync(
33 image,
34 image.Header.Arm7i,
35 cancellationToken).ConfigureAwait(false);
36 NdsDsiBuildMetadata? dsiMetadata = image.Header.Dsi is null
37 ? null
38 : NdsDsiBuildMetadata.FromHeader(image.Header.Dsi);
39 if (dsiMetadata is not null)
40 {
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>());
45 }
46
47 var builder = new NdsImageBuilder
48 {
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,
64 Arm7 = new(
65 NdsProcessor.Arm7,
66 arm7,
67 image.Header.Arm7.LoadAddress,
68 image.Header.Arm7.EntryAddress),
69 Arm9i = arm9iDefinition,
70 Arm7i = arm7iDefinition,
71 DsiMetadata = dsiMetadata,
72 Banner = image.Banner,
73 };
74 builder.SetNintendoLogo(image.Header.RawData.Span.Slice(0xC0, 156));
75
76 foreach (NdsDirectory directory in image.FileSystem.Directories)
77 {
78 if (directory.Parent is not null)
79 {
80 builder.FileSystem.CreateDirectory(directory.FullPath);
81 }
82 }
83
84 foreach (NdsFile file in image.FileSystem.Files)
85 {
86 builder.FileSystem.AddFile(
87 file.FullPath,
88 await file.ReadAllBytesAsync(cancellationToken).ConfigureAwait(false));
89 }
90
91 var importedPrivateFileIds = new HashSet<uint>();
92 foreach (NdsOverlay overlay in image.Arm9Overlays.Concat(image.Arm7Overlays))
93 {
94 builder.AddOverlay(await ImportOverlayAsync(
95 image,
96 overlay,
97 builder.FileSystem,
98 importedPrivateFileIds,
99 cancellationToken).ConfigureAwait(false));
100 }
101
102 return builder;
103 }
104
110 private static async ValueTask<NdsProgramDefinition?> ImportDsiProgramAsync(
111 NdsImage image,
112 NdsProgram? program,
113 CancellationToken cancellationToken)
114 {
115 if (program is null)
116 {
117 return null;
118 }
119
120 return new(
121 program.Processor,
122 await ReadRegionAsync(image, program.Data, cancellationToken).ConfigureAwait(false),
123 program.LoadAddress,
124 program.LoadAddress);
125 }
126
134 private static async ValueTask<NdsOverlayDefinition> ImportOverlayAsync(
135 NdsImage image,
136 NdsOverlay overlay,
137 NdsFileSystemBuilder fileSystem,
138 HashSet<uint> importedPrivateFileIds,
139 CancellationToken cancellationToken)
140 {
141 if (overlay.File is not null)
142 {
143 return NdsOverlayDefinition.LinkToFile(
144 overlay.Processor,
145 overlay.Id,
146 fileSystem.GetFile(overlay.File.FullPath),
147 overlay.LoadAddress,
148 overlay.RamSize,
149 overlay.BssSize,
150 overlay.StaticInitializerStart,
151 overlay.StaticInitializerEnd,
152 overlay.CompressedSize,
153 overlay.Flags);
154 }
155
156 if (overlay.Data is null)
157 {
158 throw new InvalidDataException($"Overlay {overlay.Id} references missing File ID {overlay.FileId}.");
159 }
160
161 if (!importedPrivateFileIds.Add(overlay.FileId))
162 {
163 throw new NotSupportedException("Multiple Overlays sharing one unnamed Allocation require an explicit shared-allocation recipe.");
164 }
165
166 return new(
167 overlay.Processor,
168 overlay.Id,
169 await ReadRegionAsync(image, overlay.Data.Value, cancellationToken).ConfigureAwait(false),
170 overlay.LoadAddress,
171 overlay.RamSize,
172 overlay.BssSize,
173 overlay.StaticInitializerStart,
174 overlay.StaticInitializerEnd,
175 overlay.CompressedSize,
176 overlay.Flags);
177 }
178
184 private static async ValueTask<byte[]> ReadRegionAsync(
185 NdsImage image,
186 NdsRegion region,
187 CancellationToken cancellationToken)
188 {
189 if (region.Length > Array.MaxLength)
190 {
191 throw new IOException("A build component is too large to materialize in a detached recipe.");
192 }
193
194 using Stream stream = image.OpenRead(region);
195 byte[] data = new byte[region.Length];
196 await stream.ReadExactlyAsync(data, cancellationToken).ConfigureAwait(false);
197 return data;
198 }
199}
@ 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.