NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsImage.cs
1namespace NdsForge;
2
4public sealed class NdsImage : IDisposable, IAsyncDisposable
5{
7 private readonly IImageDataSource _source;
9 private bool _disposed;
10
18 internal NdsImage(
19 IImageDataSource source,
20 NdsHeader header,
21 NdsFileSystem fileSystem,
22 IReadOnlyList<NdsOverlay> arm9Overlays,
23 IReadOnlyList<NdsOverlay> arm7Overlays,
24 NdsBanner? banner)
25 {
26 _source = source;
27 Header = header;
28 FileSystem = fileSystem;
29 Arm9Overlays = arm9Overlays;
30 Arm7Overlays = arm7Overlays;
31 Banner = banner;
32 }
33
35 public NdsHeader Header { get; }
36
38 public NdsFileSystem FileSystem { get; }
39
41 public IReadOnlyList<NdsOverlay> Arm9Overlays { get; }
42
44 public IReadOnlyList<NdsOverlay> Arm7Overlays { get; }
45
47 public NdsBanner? Banner { get; }
48
50 public long Length => _source.Length;
51
57 public static async ValueTask<NdsImage> OpenAsync(
58 string path,
59 NdsReadOptions? options = null,
60 CancellationToken cancellationToken = default)
61 => await NdsImageLoader.OpenPathAsync(path, options, cancellationToken).ConfigureAwait(false);
62
68 public static NdsImage Open(
69 Stream stream,
70 bool leaveOpen = false,
71 NdsReadOptions? options = null)
72 => NdsImageLoader.OpenStream(stream, leaveOpen, options);
73
80 public static async ValueTask<NdsImage> OpenAsync(
81 Stream stream,
82 bool leaveOpen = false,
83 NdsReadOptions? options = null,
84 CancellationToken cancellationToken = default)
85 => await NdsImageLoader.OpenStreamAsync(stream, leaveOpen, options, cancellationToken).ConfigureAwait(false);
86
92 public static NdsImage Load(ReadOnlyMemory<byte> data, NdsReadOptions? options = null)
93 => NdsImageLoader.LoadMemory(data, options);
94
98 public Stream OpenRead(NdsRegion region)
99 {
100 ObjectDisposedException.ThrowIf(_disposed, this);
101 ValidateRegion(region, Length);
102 return new ImageSliceStream(_source, region);
103 }
104
110 public async ValueTask CopyToAsync(
111 NdsRegion region,
112 Stream destination,
113 CancellationToken cancellationToken = default)
114 {
115 ObjectDisposedException.ThrowIf(_disposed, this);
116 ArgumentNullException.ThrowIfNull(destination);
117 if (!destination.CanWrite)
118 {
119 throw new ArgumentException("The image-region destination must be writable.", nameof(destination));
120 }
121
122 using Stream source = OpenRead(region);
123 await source.CopyToAsync(destination, cancellationToken).ConfigureAwait(false);
124 }
125
136 public async ValueTask TransformModcryptAreaAsync(
137 NdsModcryptArea area,
138 Stream destination,
139 NdsModcryptContext context,
140 CancellationToken cancellationToken = default)
141 {
142 ObjectDisposedException.ThrowIf(_disposed, this);
143 ArgumentNullException.ThrowIfNull(destination);
144 ArgumentNullException.ThrowIfNull(context);
145 NdsDsiHeader dsi = Header.Dsi ??
146 throw new InvalidOperationException("A DS-only image does not declare modcrypt areas.");
147 NdsRegion region = dsi.GetModcryptArea(area);
148
149 using Stream source = OpenRead(region);
151 source,
152 destination,
153 region.Length,
154 context,
155 area,
156 cancellationToken: cancellationToken).ConfigureAwait(false);
157 }
158
164 public ValueTask<NdsExtractionResult> ExtractAsync(
165 string destination,
166 NdsExtractionOptions? options = null,
167 CancellationToken cancellationToken = default)
168 {
169 ObjectDisposedException.ThrowIf(_disposed, this);
170 ArgumentException.ThrowIfNullOrWhiteSpace(destination);
171 return new NdsImageExtractor(this, destination, options ?? NdsExtractionOptions.Default)
172 .ExtractAsync(cancellationToken);
173 }
174
178 {
179 ObjectDisposedException.ThrowIf(_disposed, this);
180 return new(this);
181 }
182
187 {
188 ObjectDisposedException.ThrowIf(_disposed, this);
189 options ??= NdsValidationOptions.Default;
190 options.Validate();
191 return NdsImageValidator.Validate(this, options);
192 }
193
197 public ValueTask<NdsImageManifest> CreateManifestAsync(CancellationToken cancellationToken = default)
198 {
199 ObjectDisposedException.ThrowIf(_disposed, this);
200 return NdsImageManifest.CaptureAsync(this, cancellationToken);
201 }
202
207 public ValueTask<NdsImageDiff> CompareAsync(
208 NdsImage other,
209 CancellationToken cancellationToken = default)
210 {
211 ObjectDisposedException.ThrowIf(_disposed, this);
212 ArgumentNullException.ThrowIfNull(other);
213 return NdsImageComparer.CompareAsync(this, other, cancellationToken);
214 }
215
217 public void Dispose()
218 {
219 if (_disposed)
220 {
221 return;
222 }
223
224 _source.Dispose();
225 _disposed = true;
226 }
227
230 public async ValueTask DisposeAsync()
231 {
232 if (_disposed)
233 {
234 return;
235 }
236
237 await _source.DisposeAsync().ConfigureAwait(false);
238 _disposed = true;
239 }
240
244 private static void ValidateRegion(NdsRegion region, long imageLength)
245 {
246 if (region.Offset < 0 || region.Length < 0 || region.Offset > imageLength - region.Length)
247 {
248 throw new ArgumentOutOfRangeException(nameof(region), "The region is outside the image.");
249 }
250 }
251
252}
Represents a versioned menu icon, localized titles, and optional DSi animation.
Definition NdsBanner.cs:9
Projects DSi security, digest, memory, title, and save metadata while preserving the complete extensi...
NdsRegion GetModcryptArea(NdsModcryptArea area)
Selects one declared modcrypt interval while rejecting undefined enum values at the model boundary.
Controls component and NitroFS extraction.
static NdsExtractionOptions Default
Exports every supported component, rejects existing targets, and applies no NitroFS predicate.
Provides tree, path, and file-ID access to an image's NitroFS.
Projects the common cartridge header into typed fields while retaining every byte needed for lossless...
Definition NdsHeader.cs:5
Compares hash-bearing manifests so tooling can distinguish content edits from identity and layout cha...
static async ValueTask< NdsImageDiff > CompareAsync(NdsImage left, NdsImage right, CancellationToken cancellationToken=default)
Captures and compares two live images without taking ownership of either source.
Collects explicit image changes and saves them without mutating the source.
Provides a stable, content-addressed description of one parsed image for CI artifacts,...
static ValueTask< NdsImageManifest > CaptureAsync(NdsImage image, CancellationToken cancellationToken=default)
Captures hashes and structured metadata from a live image without taking ownership of it.
long Length
Reports physical source bytes, which may exceed the header's used-ROM size because cartridges are cap...
Definition NdsImage.cs:50
ValueTask< NdsExtractionResult > ExtractAsync(string destination, NdsExtractionOptions? options=null, CancellationToken cancellationToken=default)
Safely exports selected image components to a directory.
Definition NdsImage.cs:164
NdsValidationResult Validate(NdsValidationOptions? options=null)
Validates checksums, component relationships, bounds, and optional DSi authentication fields.
Definition NdsImage.cs:186
NdsHeader Header
Preserves both typed DS/DSi fields and the raw bytes required for checksums and lossless edits.
Definition NdsImage.cs:35
async ValueTask TransformModcryptAreaAsync(NdsModcryptArea area, Stream destination, NdsModcryptContext context, CancellationToken cancellationToken=default)
Reads one declared DSi modcrypt area and writes its symmetric AES-CTR transformation without loading ...
Definition NdsImage.cs:136
IReadOnlyList< NdsOverlay > Arm9Overlays
Gets ARM9 overlays in table order.
Definition NdsImage.cs:41
ValueTask< NdsImageManifest > CreateManifestAsync(CancellationToken cancellationToken=default)
Captures a detached, SHA-256-addressed automation manifest without transferring image ownership.
Definition NdsImage.cs:197
static NdsImage Open(Stream stream, bool leaveOpen=false, NdsReadOptions? options=null)
Opens an image from a caller-supplied readable, seekable stream.
Definition NdsImage.cs:68
Stream OpenRead(NdsRegion region)
Opens a read-only stream over a validated image region.
Definition NdsImage.cs:98
NdsFileSystem FileSystem
Connects navigable FNT paths with every FAT allocation, including unnamed overlay payloads.
Definition NdsImage.cs:38
NdsImageEditor Edit()
Begins an explicit, non-mutating edit session for this source image.
Definition NdsImage.cs:177
async ValueTask CopyToAsync(NdsRegion region, Stream destination, CancellationToken cancellationToken=default)
Copies an arbitrary validated image interval without materializing it as one managed array.
Definition NdsImage.cs:110
void Dispose()
Synchronously releases the image source and prevents further payload access.
Definition NdsImage.cs:217
static async ValueTask< NdsImage > OpenAsync(Stream stream, bool leaveOpen=false, NdsReadOptions? options=null, CancellationToken cancellationToken=default)
Asynchronously opens an image from a caller-supplied readable, seekable stream.
Definition NdsImage.cs:80
ValueTask< NdsImageDiff > CompareAsync(NdsImage other, CancellationToken cancellationToken=default)
Compares this image with another live image at semantic, numeric-identity, and physical-layout levels...
Definition NdsImage.cs:207
async ValueTask DisposeAsync()
Asynchronously releases the image source and prevents further payload access.
Definition NdsImage.cs:230
static async ValueTask< NdsImage > OpenAsync(string path, NdsReadOptions? options=null, CancellationToken cancellationToken=default)
Opens an image from a filesystem path without loading the entire file into memory.
Definition NdsImage.cs:57
IReadOnlyList< NdsOverlay > Arm7Overlays
Gets ARM7 overlays in table order.
Definition NdsImage.cs:44
static NdsImage Load(ReadOnlyMemory< byte > data, NdsReadOptions? options=null)
Loads an image from caller-owned memory.
Definition NdsImage.cs:92
NdsBanner? Banner
Gets the parsed menu banner, or null when absent.
Definition NdsImage.cs:47
Captures the AES-128 normal key and both HMAC-derived initial counters needed to transform DSi modcry...
Applies the DSi AES-CTR transform with little-endian counter advancement. Encryption and decryption a...
static async ValueTask TransformAsync(Stream source, Stream destination, long length, NdsModcryptContext context, NdsModcryptArea area, long byteOffset=0, CancellationToken cancellationToken=default)
Transforms exactly length bytes without closing either stream. If a late source truncation occurs,...
Controls resource limits applied while parsing an image.
Identifies a bounded range of bytes in an image.
Definition NdsRegion.cs:11
Supplies optional trust material and policy for validation checks that cannot be inferred from image ...
static NdsValidationOptions Default
Returns a fresh keyless policy suitable for structural validation without shared mutable state.
Contains all diagnostics produced by a validation pass.
NdsModcryptArea
Identifies which DSi modcrypt interval and HMAC-derived initial counter a transformation uses.