6internal static class NdsImageLoader
9 private const int BaseHeaderLength = 0x200;
12 private const int DsiHeaderLength = 0x1000;
19 public static async ValueTask<NdsImage> OpenPathAsync(
21 NdsReadOptions? options,
22 CancellationToken cancellationToken)
24 ArgumentException.ThrowIfNullOrWhiteSpace(path);
25 var source =
new FileImageDataSource(path);
28 return await LoadAsync(source, options, cancellationToken).ConfigureAwait(
false);
32 await source.DisposeAsync().ConfigureAwait(
false);
42 public static NdsImage OpenStream(Stream stream,
bool leaveOpen, NdsReadOptions? options)
44 var source =
new StreamImageDataSource(stream, leaveOpen);
47 return Load(source, options);
62 public static async ValueTask<NdsImage> OpenStreamAsync(
65 NdsReadOptions? options,
66 CancellationToken cancellationToken)
68 var source =
new StreamImageDataSource(stream, leaveOpen);
71 return await LoadAsync(source, options, cancellationToken).ConfigureAwait(
false);
75 await source.DisposeAsync().ConfigureAwait(
false);
84 public static NdsImage LoadMemory(ReadOnlyMemory<byte> data, NdsReadOptions? options)
86 var source =
new MemoryImageDataSource(data);
89 return Load(source, options);
102 private static NdsImage Load(IImageDataSource source, NdsReadOptions? options)
104 options ??= NdsReadOptions.Default;
106 NdsHeader header = ReadHeader(source);
107 DetectArm9Footer(source, header);
108 NdsFileSystem fileSystem = NitroFileSystemParser.Parse(source, header, options);
109 IReadOnlyList<NdsOverlay> arm9Overlays = NdsOverlayParser.Parse(
110 source, header.Arm9OverlayTable,
NdsProcessor.Arm9, fileSystem, options);
111 IReadOnlyList<NdsOverlay> arm7Overlays = NdsOverlayParser.Parse(
112 source, header.Arm7OverlayTable,
NdsProcessor.Arm7, fileSystem, options);
113 NdsBanner? banner = NdsBannerParser.Parse(source, header.BannerOffset, options);
114 return new NdsImage(source, header, fileSystem, arm9Overlays, arm7Overlays, banner);
122 private static async ValueTask<NdsImage> LoadAsync(
123 IImageDataSource source,
124 NdsReadOptions? options,
125 CancellationToken cancellationToken)
127 options ??= NdsReadOptions.Default;
129 NdsHeader header = await ReadHeaderAsync(source, cancellationToken).ConfigureAwait(
false);
130 await DetectArm9FooterAsync(source, header, cancellationToken).ConfigureAwait(
false);
131 NdsFileSystem fileSystem = await NitroFileSystemParser.ParseAsync(
132 source, header, options, cancellationToken).ConfigureAwait(
false);
133 IReadOnlyList<NdsOverlay> arm9Overlays = await NdsOverlayParser.ParseAsync(
134 source, header.Arm9OverlayTable,
NdsProcessor.Arm9, fileSystem, options, cancellationToken)
135 .ConfigureAwait(
false);
136 IReadOnlyList<NdsOverlay> arm7Overlays = await NdsOverlayParser.ParseAsync(
137 source, header.Arm7OverlayTable,
NdsProcessor.Arm7, fileSystem, options, cancellationToken)
138 .ConfigureAwait(
false);
139 NdsBanner? banner = await NdsBannerParser.ParseAsync(
140 source, header.BannerOffset, options, cancellationToken).ConfigureAwait(
false);
141 return new NdsImage(source, header, fileSystem, arm9Overlays, arm7Overlays, banner);
147 private static NdsHeader ReadHeader(IImageDataSource source)
149 byte[] baseHeader =
new byte[BaseHeaderLength];
150 source.ReadExactly(0, baseHeader);
151 int length = (baseHeader[0x12] & 2) == 0 ? BaseHeaderLength : DsiHeaderLength;
152 if (length == BaseHeaderLength)
154 return new NdsHeader(baseHeader);
157 byte[] fullHeader =
new byte[length];
158 baseHeader.CopyTo(fullHeader, 0);
159 source.ReadExactly(BaseHeaderLength, fullHeader.AsSpan(BaseHeaderLength));
160 return new NdsHeader(fullHeader);
167 private static async ValueTask<NdsHeader> ReadHeaderAsync(
168 IImageDataSource source,
169 CancellationToken cancellationToken)
171 byte[] baseHeader =
new byte[BaseHeaderLength];
172 await source.ReadExactlyAsync(0, baseHeader, cancellationToken).ConfigureAwait(
false);
173 int length = (baseHeader[0x12] & 2) == 0 ? BaseHeaderLength : DsiHeaderLength;
174 if (length == BaseHeaderLength)
176 return new NdsHeader(baseHeader);
179 byte[] fullHeader =
new byte[length];
180 baseHeader.CopyTo(fullHeader, 0);
181 await source.ReadExactlyAsync(
183 fullHeader.AsMemory(BaseHeaderLength),
184 cancellationToken).ConfigureAwait(
false);
185 return new NdsHeader(fullHeader);
192 private static void DetectArm9Footer<TSource>(TSource source, NdsHeader header)
193 where TSource : IImageDataSource
195 if (header.Arm9.Data.End > source.Length - 12)
200 Span<byte> marker = stackalloc
byte[4];
201 source.ReadExactly(header.Arm9.Data.End, marker);
202 if (NdsBinary.ReadUInt32(marker, 0) == 0xDEC00621)
204 header.Arm9.Footer =
new(header.Arm9.Data.End, 12);
213 private static async ValueTask DetectArm9FooterAsync<TSource>(
216 CancellationToken cancellationToken)
217 where TSource : IImageDataSource
219 if (header.Arm9.Data.End > source.Length - 12)
224 byte[] marker =
new byte[4];
225 await source.ReadExactlyAsync(header.Arm9.Data.End, marker, cancellationToken).ConfigureAwait(
false);
226 if (NdsBinary.ReadUInt32(marker, 0) == 0xDEC00621)
228 header.Arm9.Footer =
new(header.Arm9.Data.End, 12);
NdsProcessor
Identifies a processor and execution mode.