NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsElfProgramImporter.cs
1using System.Buffers;
2
3namespace NdsForge;
4
9public static class NdsElfProgramImporter
10{
17 ReadOnlyMemory<byte> data,
18 NdsProcessor processor,
19 NdsElfImportOptions? options = null)
20 {
21 NdsElfImportOptions effectiveOptions = Snapshot(options);
22 ValidateProcessor(processor);
23 if (data.Length > effectiveOptions.MaxInputBytes)
24 {
25 throw new InvalidDataException("The ELF input exceeds the configured allocation limit.");
26 }
27
28 NdsElfFile elf = NdsElfParser.Parse(data, effectiveOptions);
29 return NdsElfAssembler.Assemble(data, elf, processor, effectiveOptions);
30 }
31
38 public static async ValueTask<NdsElfImportResult> ImportAsync(
39 string path,
40 NdsProcessor processor,
41 NdsElfImportOptions? options = null,
42 CancellationToken cancellationToken = default)
43 {
44 ArgumentException.ThrowIfNullOrWhiteSpace(path);
45 NdsElfImportOptions effectiveOptions = Snapshot(options);
46 ValidateProcessor(processor);
47 var stream = new FileStream(
48 path,
49 FileMode.Open,
50 FileAccess.Read,
51 FileShare.Read,
52 64 * 1024,
53 FileOptions.Asynchronous | FileOptions.SequentialScan);
54 await using (stream.ConfigureAwait(false))
55 {
56 return await ImportAsync(stream, processor, leaveOpen: true, effectiveOptions, cancellationToken).ConfigureAwait(false);
57 }
58 }
59
70 public static async ValueTask<NdsElfImportResult> ImportAsync(
71 Stream stream,
72 NdsProcessor processor,
73 bool leaveOpen = false,
74 NdsElfImportOptions? options = null,
75 CancellationToken cancellationToken = default)
76 {
77 ArgumentNullException.ThrowIfNull(stream);
78 NdsElfImportOptions effectiveOptions = Snapshot(options);
79 ValidateProcessor(processor);
80 try
81 {
82 if (!stream.CanRead)
83 {
84 throw new ArgumentException("The ELF source stream must be readable.", nameof(stream));
85 }
86
87 byte[] data = await ReadBoundedAsync(stream, effectiveOptions.MaxInputBytes, cancellationToken).ConfigureAwait(false);
88 return Import(data, processor, effectiveOptions);
89 }
90 finally
91 {
92 if (!leaveOpen)
93 {
94 await stream.DisposeAsync().ConfigureAwait(false);
95 }
96 }
97 }
98
104 private static async ValueTask<byte[]> ReadBoundedAsync(
105 Stream source,
106 long limit,
107 CancellationToken cancellationToken)
108 {
109 if (source.CanSeek && source.Length - source.Position > limit)
110 {
111 throw new InvalidDataException("The ELF input exceeds the configured allocation limit.");
112 }
113
114 byte[] buffer = ArrayPool<byte>.Shared.Rent(64 * 1024);
115 try
116 {
117 using var output = new MemoryStream();
118 while (true)
119 {
120 int count = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
121 if (count == 0)
122 {
123 return output.ToArray();
124 }
125
126 if (output.Length > limit - count)
127 {
128 throw new InvalidDataException("The ELF input exceeds the configured allocation limit.");
129 }
130
131 await output.WriteAsync(buffer.AsMemory(0, count), cancellationToken).ConfigureAwait(false);
132 }
133 }
134 finally
135 {
136 ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
137 }
138 }
139
143 private static NdsElfImportOptions Snapshot(NdsElfImportOptions? options)
144 {
145 options ??= NdsElfImportOptions.Default;
146 var snapshot = new NdsElfImportOptions
147 {
148 MaxInputBytes = options.MaxInputBytes,
149 MaxProgramBytes = options.MaxProgramBytes,
150 MaxProgramHeaders = options.MaxProgramHeaders,
151 MaxOverlays = options.MaxOverlays,
152 ImportOverlays = options.ImportOverlays,
153 };
154 snapshot.Validate();
155 return snapshot;
156 }
157
160 private static void ValidateProcessor(NdsProcessor processor)
161 {
162 if (!Enum.IsDefined(processor))
163 {
164 throw new ArgumentOutOfRangeException(nameof(processor), processor, "Unknown NDS processor identity.");
165 }
166 }
167}
Bounds ELF ingestion and controls whether ndstool-style Overlay program headers become definitions.
long MaxInputBytes
Limits buffered ELF input so non-seekable or hostile streams cannot force unbounded allocation.
Returns a builder-ready executable plus any Overlay definitions decoded from the same ELF image.
Imports executable ARM ELF32 files into builder-ready Program and Overlay definitions....
static async ValueTask< NdsElfImportResult > ImportAsync(Stream stream, NdsProcessor processor, bool leaveOpen=false, NdsElfImportOptions? options=null, CancellationToken cancellationToken=default)
Consumes ELF bytes from a stream's current position, including non-seekable sources,...
static NdsElfImportResult Import(ReadOnlyMemory< byte > data, NdsProcessor processor, NdsElfImportOptions? options=null)
Imports an ELF already available in memory without retaining or mutating the caller's buffer.
static async ValueTask< NdsElfImportResult > ImportAsync(string path, NdsProcessor processor, NdsElfImportOptions? options=null, CancellationToken cancellationToken=default)
Reads and imports a filesystem ELF after checking its length before allocation.
NdsProcessor
Identifies a processor and execution mode.