NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsElfAssembler.cs
1namespace NdsForge;
2
4internal static class NdsElfAssembler
5{
12 public static NdsElfImportResult Assemble(
13 ReadOnlyMemory<byte> data,
14 NdsElfFile elf,
15 NdsProcessor processor,
16 NdsElfImportOptions options)
17 {
18 bool twl = processor is NdsProcessor.Arm9i or NdsProcessor.Arm7i;
19 NdsElfSegment[] selectedSegments = elf.Segments
20 .Where(segment => segment.IsLoadable && !segment.IsOverlay && segment.IsTwl == twl)
21 .ToArray();
22 NdsElfSegment[] mainSegments = selectedSegments
23 .Where(static segment => segment.FileSize != 0)
24 .OrderBy(static segment => segment.PhysicalAddress)
25 .ToArray();
26 if (mainSegments.Length == 0)
27 {
28 throw new InvalidDataException($"The ELF contains no file-backed segments for {processor}.");
29 }
30
31 uint start = mainSegments[0].PhysicalAddress;
32 ulong end = mainSegments.Max(static segment => (ulong)segment.PhysicalAddress + segment.FileSize);
33 ulong outputLength = end - start;
34 if (outputLength > (uint)options.MaxProgramBytes)
35 {
36 throw new InvalidDataException("The ELF's contiguous Program extent exceeds the configured output limit.");
37 }
38
39 ValidateNoFileOverlap(mainSegments);
40 var contents = new byte[(int)outputLength];
41 foreach (NdsElfSegment segment in mainSegments)
42 {
43 int sourceOffset = checked((int)segment.FileOffset);
44 int destinationOffset = checked((int)(segment.PhysicalAddress - start));
45 data.Span.Slice(sourceOffset, checked((int)segment.FileSize)).CopyTo(contents.AsSpan(destinationOffset));
46 }
47
48 uint entryPoint = TranslateEntryPoint(elf.EntryPoint, mainSegments);
49 var program = new NdsProgramDefinition(processor, contents, start, entryPoint);
50 NdsElfSegment[] overlaySegments = elf.Segments
51 .Where(static segment => segment.IsLoadable && segment.IsOverlay)
52 .ToArray();
53 NdsOverlayDefinition[] overlays = options.ImportOverlays &&
54 overlaySegments.Length != 0 &&
55 processor is NdsProcessor.Arm9 or NdsProcessor.Arm7
56 ? ImportOverlays(data, overlaySegments, processor, options)
57 : [];
58 uint? arm7WramAddress = processor == NdsProcessor.Arm7i
59 ? FindArm7WramAddress(selectedSegments)
60 : null;
61 return new(program, overlays, overlaySegments.Length != 0, arm7WramAddress);
62 }
63
66 private static void ValidateNoFileOverlap(NdsElfSegment[] segments)
67 {
68 ulong previousEnd = 0;
69 for (int index = 0; index < segments.Length; index++)
70 {
71 NdsElfSegment segment = segments[index];
72 if (index != 0 && segment.PhysicalAddress < previousEnd)
73 {
74 throw new InvalidDataException("ELF file-backed Program segments overlap in physical memory.");
75 }
76
77 previousEnd = (ulong)segment.PhysicalAddress + segment.FileSize;
78 }
79 }
80
85 private static uint TranslateEntryPoint(uint entryPoint, IEnumerable<NdsElfSegment> segments)
86 {
87 foreach (NdsElfSegment segment in segments)
88 {
89 ulong virtualEnd = (ulong)segment.VirtualAddress + segment.FileSize;
90 if (entryPoint >= segment.VirtualAddress && entryPoint < virtualEnd)
91 {
92 return checked(segment.PhysicalAddress + (entryPoint - segment.VirtualAddress));
93 }
94 }
95
96 return entryPoint;
97 }
98
102 private static uint? FindArm7WramAddress(IEnumerable<NdsElfSegment> segments) => segments
103 .Select(static segment => segment.VirtualAddress)
104 .Cast<uint?>()
105 .FirstOrDefault(static address => address is >= 0x0300_0000 and < 0x037F_8000);
106
113 private static NdsOverlayDefinition[] ImportOverlays(
114 ReadOnlyMemory<byte> data,
115 NdsElfSegment[] segments,
116 NdsProcessor processor,
117 NdsElfImportOptions options)
118 {
119 NdsElfSegment table = segments[0];
120 if (table.FileSize == 0 || table.FileSize % 12 != 0)
121 {
122 throw new InvalidDataException("The ELF Overlay metadata segment must contain complete 12-byte records.");
123 }
124
125 int count = checked((int)(table.FileSize / 12));
126 if (count > options.MaxOverlays || segments.Length != count + 1)
127 {
128 throw new InvalidDataException("The ELF Overlay record and payload segment counts are inconsistent or exceed policy.");
129 }
130
131 var result = new NdsOverlayDefinition[count];
132 ReadOnlySpan<byte> tableData = data.Span.Slice(checked((int)table.FileOffset), checked((int)table.FileSize));
133 for (int index = 0; index < count; index++)
134 {
135 NdsElfSegment payload = segments[index + 1];
136 ReadOnlySpan<byte> record = tableData.Slice(index * 12, 12);
137 uint control = NdsBinary.ReadUInt32(record, 8);
138 byte[] contents = data.Span
139 .Slice(checked((int)payload.FileOffset), checked((int)payload.FileSize))
140 .ToArray();
141 result[index] = new(
142 processor,
143 checked((uint)index),
144 contents,
145 payload.VirtualAddress,
146 payload.FileSize,
147 payload.MemorySize - payload.FileSize,
148 NdsBinary.ReadUInt32(record, 0),
149 NdsBinary.ReadUInt32(record, 4),
150 control & 0x00FF_FFFF,
151 (byte)(control >> 24));
152 }
153
154 return result;
155 }
156}
NdsProcessor
Identifies a processor and execution mode.