From b99b2a53cf3c677f551aaec777b6a4fb09fe3557 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 7 Jan 2023 22:12:58 -0800 Subject: [PATCH] Add extended header parsing for N3DS --- BurnOutSharp.Builders/N3DS.cs | 232 +++++++++++++++++- .../N3DS/ARM11KernelCapabilities.cs | 3 +- .../N3DS/ARM11LocalSystemCapabilities.cs | 8 +- BurnOutSharp.Models/N3DS/ARM9AccessControl.cs | 2 +- BurnOutSharp.Models/N3DS/Cart.cs | 5 + BurnOutSharp.Models/N3DS/CodeSetInfo.cs | 2 +- BurnOutSharp.Models/N3DS/Enums.cs | 3 +- ...xtendedHeader.cs => NCCHExtendedHeader.cs} | 2 +- BurnOutSharp.Models/N3DS/StorageInfo.cs | 6 +- BurnOutSharp.Models/N3DS/SystemControlInfo.cs | 8 +- BurnOutSharp.Models/N3DS/SystemInfo.cs | 2 +- 11 files changed, 256 insertions(+), 17 deletions(-) rename BurnOutSharp.Models/N3DS/{CXIExtendedHeader.cs => NCCHExtendedHeader.cs} (96%) diff --git a/BurnOutSharp.Builders/N3DS.cs b/BurnOutSharp.Builders/N3DS.cs index c134c1c2..ce0188db 100644 --- a/BurnOutSharp.Builders/N3DS.cs +++ b/BurnOutSharp.Builders/N3DS.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Text; using BurnOutSharp.Models.N3DS; @@ -105,7 +106,33 @@ namespace BurnOutSharp.Builders #endregion - // TODO: Parse partition extended headers (decrypted only) + #region Extended Headers + + // Create the extended header table + cart.NCCHExtendedHeaders = new NCCHExtendedHeader[8]; + + // Iterate and build the extended headers + for (int i = 0; i < 8; i++) + { + // If we have an encrypted or invalid partition + if (cart.Partitions[i].MagicID != NCCHMagicNumber) + continue; + + // Get the extended header offset + long mediaUnitSize = (uint)(0x200 * Math.Pow(2, header.PartitionFlags[(int)NCSDFlags.MediaUnitSize])); + long offset = (cart.Header.PartitionsTable[i].Offset * mediaUnitSize) + 0x200; + if (offset < 0 || offset >= data.Length) + continue; + + // Seek to the extended header + data.Seek(offset, SeekOrigin.Begin); + + // Parse the extended header + cart.NCCHExtendedHeaders[i] = ParseNCCHExtendedHeader(data); + } + + #endregion + // TODO: Parse ExeFS file entries (decrypted only) // TODO: Parse ExeFS filename table (decrypted only) @@ -349,6 +376,209 @@ namespace BurnOutSharp.Builders return testData; } + /// + /// Parse a Stream into an NCCH extended header + /// + /// Stream to parse + /// Filled NCCH extended header on success, null on error + private static NCCHExtendedHeader ParseNCCHExtendedHeader(Stream data) + { + // TODO: Use marshalling here instead of building + NCCHExtendedHeader extendedHeader = new NCCHExtendedHeader(); + + extendedHeader.SCI = ParseSystemControlInfo(data); + if (extendedHeader.SCI == null) + return null; + + extendedHeader.ACI = ParseAccessControlInfo(data); + if (extendedHeader.ACI == null) + return null; + + extendedHeader.AccessDescSignature = data.ReadBytes(0x100); + extendedHeader.NCCHHDRPublicKey = data.ReadBytes(0x100); + + extendedHeader.ACIForLimitations = ParseAccessControlInfo(data); + if (extendedHeader.ACI == null) + return null; + + return extendedHeader; + } + + /// + /// Parse a Stream into a system control info + /// + /// Stream to parse + /// Filled system control info on success, null on error + private static SystemControlInfo ParseSystemControlInfo(Stream data) + { + // TODO: Use marshalling here instead of building + SystemControlInfo systemControlInfo = new SystemControlInfo(); + + byte[] applicationTitle = data.ReadBytes(8); + systemControlInfo.ApplicationTitle = Encoding.ASCII.GetString(applicationTitle).TrimEnd('\0'); + systemControlInfo.Reserved1 = data.ReadBytes(5); + systemControlInfo.Flag = data.ReadByteValue(); + systemControlInfo.RemasterVersion = data.ReadUInt16(); + systemControlInfo.TextCodeSetInfo = ParseCodeSetInfo(data); + systemControlInfo.StackSize = data.ReadUInt32(); + systemControlInfo.ReadOnlyCodeSetInfo = ParseCodeSetInfo(data); + systemControlInfo.Reserved2 = data.ReadBytes(4); + systemControlInfo.DataCodeSetInfo = ParseCodeSetInfo(data); + systemControlInfo.BSSSize = data.ReadUInt32(); + systemControlInfo.DependencyModuleList = new ulong[48]; + for (int i = 0; i < 48; i++) + { + systemControlInfo.DependencyModuleList[i] = data.ReadUInt64(); + } + systemControlInfo.SystemInfo = ParseSystemInfo(data); + + return systemControlInfo; + } + + /// + /// Parse a Stream into a code set info + /// + /// Stream to parse + /// Filled code set info on success, null on error + private static CodeSetInfo ParseCodeSetInfo(Stream data) + { + // TODO: Use marshalling here instead of building + CodeSetInfo codeSetInfo = new CodeSetInfo(); + + codeSetInfo.Address = data.ReadUInt32(); + codeSetInfo.PhysicalRegionSizeInPages = data.ReadUInt32(); + codeSetInfo.SizeInBytes = data.ReadUInt32(); + + return codeSetInfo; + } + + /// + /// Parse a Stream into a system info + /// + /// Stream to parse + /// Filled system info on success, null on error + private static SystemInfo ParseSystemInfo(Stream data) + { + // TODO: Use marshalling here instead of building + SystemInfo systemInfo = new SystemInfo(); + + systemInfo.SaveDataSize = data.ReadUInt64(); + systemInfo.JumpID = data.ReadUInt64(); + systemInfo.Reserved = data.ReadBytes(0x30); + + return systemInfo; + } + + /// + /// Parse a Stream into an access control info + /// + /// Stream to parse + /// Filled access control info on success, null on error + private static AccessControlInfo ParseAccessControlInfo(Stream data) + { + // TODO: Use marshalling here instead of building + AccessControlInfo accessControlInfo = new AccessControlInfo(); + + accessControlInfo.ARM11LocalSystemCapabilities = ParseARM11LocalSystemCapabilities(data); + accessControlInfo.ARM11KernelCapabilities = ParseARM11KernelCapabilities(data); + accessControlInfo.ARM9AccessControl = ParseARM9AccessControl(data); + + return accessControlInfo; + } + + /// + /// Parse a Stream into an ARM11 local system capabilities + /// + /// Stream to parse + /// Filled ARM11 local system capabilities on success, null on error + private static ARM11LocalSystemCapabilities ParseARM11LocalSystemCapabilities(Stream data) + { + // TODO: Use marshalling here instead of building + ARM11LocalSystemCapabilities arm11LocalSystemCapabilities = new ARM11LocalSystemCapabilities(); + + arm11LocalSystemCapabilities.ProgramID = data.ReadUInt64(); + arm11LocalSystemCapabilities.CoreVersion = data.ReadUInt32(); + arm11LocalSystemCapabilities.Flag1 = (ARM11LSCFlag1)data.ReadByteValue(); + arm11LocalSystemCapabilities.Flag2 = (ARM11LSCFlag2)data.ReadByteValue(); + arm11LocalSystemCapabilities.Flag0 = (ARM11LSCFlag0)data.ReadByteValue(); + arm11LocalSystemCapabilities.Priority = data.ReadByteValue(); + arm11LocalSystemCapabilities.ResourceLimitDescriptors = new ushort[16]; + for (int i = 0; i < 16; i++) + { + arm11LocalSystemCapabilities.ResourceLimitDescriptors[i] = data.ReadUInt16(); + } + arm11LocalSystemCapabilities.StorageInfo = ParseStorageInfo(data); + arm11LocalSystemCapabilities.ServiceAccessControl = new ulong[32]; + for (int i = 0; i < 32; i++) + { + arm11LocalSystemCapabilities.ServiceAccessControl[i] = data.ReadUInt64(); + } + arm11LocalSystemCapabilities.ExtendedServiceAccessControl = new ulong[2]; + for (int i = 0; i < 2; i++) + { + arm11LocalSystemCapabilities.ExtendedServiceAccessControl[i] = data.ReadUInt64(); + } + arm11LocalSystemCapabilities.Reserved = data.ReadBytes(0x0F); + arm11LocalSystemCapabilities.ResourceLimitCategory = (ResourceLimitCategory)data.ReadByteValue(); + + return arm11LocalSystemCapabilities; + } + + /// + /// Parse a Stream into a storage info + /// + /// Stream to parse + /// Filled storage info on success, null on error + private static StorageInfo ParseStorageInfo(Stream data) + { + // TODO: Use marshalling here instead of building + StorageInfo storageInfo = new StorageInfo(); + + storageInfo.ExtdataID = data.ReadUInt64(); + storageInfo.SystemSavedataIDs = data.ReadBytes(8); + storageInfo.StorageAccessibleUniqueIDs = data.ReadBytes(8); + storageInfo.FileSystemAccessInfo = data.ReadBytes(7); + storageInfo.OtherAttributes = (StorageInfoOtherAttributes)data.ReadByteValue(); + + return storageInfo; + } + + /// + /// Parse a Stream into an ARM11 kernel capabilities + /// + /// Stream to parse + /// Filled ARM11 kernel capabilities on success, null on error + private static ARM11KernelCapabilities ParseARM11KernelCapabilities(Stream data) + { + // TODO: Use marshalling here instead of building + ARM11KernelCapabilities arm11KernelCapabilities = new ARM11KernelCapabilities(); + + arm11KernelCapabilities.Descriptors = new uint[28]; + for (int i = 0; i < 28; i++) + { + arm11KernelCapabilities.Descriptors[i] = data.ReadUInt32(); + } + arm11KernelCapabilities.Reserved = data.ReadBytes(0x10); + + return arm11KernelCapabilities; + } + + /// + /// Parse a Stream into an ARM11 access control + /// + /// Stream to parse + /// Filled ARM11 access control on success, null on error + private static ARM9AccessControl ParseARM9AccessControl(Stream data) + { + // TODO: Use marshalling here instead of building + ARM9AccessControl arm9AccessControl = new ARM9AccessControl(); + + arm9AccessControl.Descriptors = data.ReadBytes(15); + arm9AccessControl.DescriptorVersion = data.ReadByteValue(); + + return arm9AccessControl; + } + #endregion } } diff --git a/BurnOutSharp.Models/N3DS/ARM11KernelCapabilities.cs b/BurnOutSharp.Models/N3DS/ARM11KernelCapabilities.cs index 94d03774..78bb3814 100644 --- a/BurnOutSharp.Models/N3DS/ARM11KernelCapabilities.cs +++ b/BurnOutSharp.Models/N3DS/ARM11KernelCapabilities.cs @@ -33,7 +33,8 @@ /// 12 Special memory /// 13 Process has access to CPU core 2 (New3DS only) /// - public byte[][] Descriptors; + /// TODO: Make enum for flag values + public uint[] Descriptors; /// /// Reserved diff --git a/BurnOutSharp.Models/N3DS/ARM11LocalSystemCapabilities.cs b/BurnOutSharp.Models/N3DS/ARM11LocalSystemCapabilities.cs index 0d21a859..7cda9cfd 100644 --- a/BurnOutSharp.Models/N3DS/ARM11LocalSystemCapabilities.cs +++ b/BurnOutSharp.Models/N3DS/ARM11LocalSystemCapabilities.cs @@ -6,7 +6,7 @@ /// /// Program ID /// - public byte[] ProgramID; + public ulong ProgramID; /// /// Core version (The Title ID low of the required FIRM) @@ -36,7 +36,7 @@ /// /// Resource limit descriptors. The first byte here controls the maximum allowed CpuTime. /// - public byte[][] ResourceLimitDescriptors; + public ushort[] ResourceLimitDescriptors; /// /// Storage info @@ -46,12 +46,12 @@ /// /// Service access control /// - public byte[][] ServiceAccessControl; + public ulong[] ServiceAccessControl; /// /// Extended service access control, support for this was implemented with 9.3.0-X. /// - public byte[][] ExtendedServiceAccessControl; + public ulong[] ExtendedServiceAccessControl; /// /// Reserved diff --git a/BurnOutSharp.Models/N3DS/ARM9AccessControl.cs b/BurnOutSharp.Models/N3DS/ARM9AccessControl.cs index 9bb45594..8cefc0ea 100644 --- a/BurnOutSharp.Models/N3DS/ARM9AccessControl.cs +++ b/BurnOutSharp.Models/N3DS/ARM9AccessControl.cs @@ -6,7 +6,7 @@ /// /// Descriptors /// - public ARM9AccessControlDescriptors[] Descriptors; + public byte[] Descriptors; /// /// ARM9 Descriptor Version. Originally this value had to be ≥ 2. diff --git a/BurnOutSharp.Models/N3DS/Cart.cs b/BurnOutSharp.Models/N3DS/Cart.cs index 98496919..57c9752f 100644 --- a/BurnOutSharp.Models/N3DS/Cart.cs +++ b/BurnOutSharp.Models/N3DS/Cart.cs @@ -24,5 +24,10 @@ namespace BurnOutSharp.Models.N3DS /// NCCH partitions /// public NCCHHeader[] Partitions { get; set; } + + /// + /// NCCH extended headers + /// + public NCCHExtendedHeader[] NCCHExtendedHeaders { get; set; } } } \ No newline at end of file diff --git a/BurnOutSharp.Models/N3DS/CodeSetInfo.cs b/BurnOutSharp.Models/N3DS/CodeSetInfo.cs index a76db265..4bf8d93d 100644 --- a/BurnOutSharp.Models/N3DS/CodeSetInfo.cs +++ b/BurnOutSharp.Models/N3DS/CodeSetInfo.cs @@ -6,7 +6,7 @@ /// /// Address /// - public byte[] Address; + public uint Address; /// /// Physical region size (in page-multiples) diff --git a/BurnOutSharp.Models/N3DS/Enums.cs b/BurnOutSharp.Models/N3DS/Enums.cs index 94ddb9f4..1d4f4580 100644 --- a/BurnOutSharp.Models/N3DS/Enums.cs +++ b/BurnOutSharp.Models/N3DS/Enums.cs @@ -2,6 +2,7 @@ namespace BurnOutSharp.Models.N3DS { + // TODO: Fix this, I don't think it's correct [Flags] public enum ARM9AccessControlDescriptors : byte { @@ -14,7 +15,7 @@ namespace BurnOutSharp.Models.N3DS CreateSeed = 0x20, UseCardSPI = 0x40, SDApplication = 0x80, - MoundSdmcWriteAccess = 0xF0, + MountSdmcWriteAccess = 0xF0, } [Flags] diff --git a/BurnOutSharp.Models/N3DS/CXIExtendedHeader.cs b/BurnOutSharp.Models/N3DS/NCCHExtendedHeader.cs similarity index 96% rename from BurnOutSharp.Models/N3DS/CXIExtendedHeader.cs rename to BurnOutSharp.Models/N3DS/NCCHExtendedHeader.cs index 93a18121..a3581bf8 100644 --- a/BurnOutSharp.Models/N3DS/CXIExtendedHeader.cs +++ b/BurnOutSharp.Models/N3DS/NCCHExtendedHeader.cs @@ -6,7 +6,7 @@ /// - A signed copy of NCCH HDR public key, and exheader ACI. This version of the ACI is used as limitation to the actual ACI. /// /// - public sealed class CXIExtendedHeader + public sealed class NCCHExtendedHeader { /// /// SCI diff --git a/BurnOutSharp.Models/N3DS/StorageInfo.cs b/BurnOutSharp.Models/N3DS/StorageInfo.cs index d5a1158c..682b128d 100644 --- a/BurnOutSharp.Models/N3DS/StorageInfo.cs +++ b/BurnOutSharp.Models/N3DS/StorageInfo.cs @@ -9,7 +9,7 @@ /// /// Extdata ID /// - public byte[] ExtdataID; + public ulong ExtdataID; /// /// System savedata IDs @@ -24,7 +24,9 @@ /// /// Filesystem access info /// - public byte[] FilesystemAccessInfo; + /// TODO: Create enum for the flag values + /// TODO: Combine with "other attributes" + public byte[] FileSystemAccessInfo; /// /// Other attributes diff --git a/BurnOutSharp.Models/N3DS/SystemControlInfo.cs b/BurnOutSharp.Models/N3DS/SystemControlInfo.cs index 339960cc..e2d630d9 100644 --- a/BurnOutSharp.Models/N3DS/SystemControlInfo.cs +++ b/BurnOutSharp.Models/N3DS/SystemControlInfo.cs @@ -6,7 +6,7 @@ /// /// Application title (default is "CtrApp") /// - public char[] ApplicationTitle; + public string ApplicationTitle; /// /// Reserved @@ -21,12 +21,12 @@ /// /// Remaster version /// - public byte[] RemasterVersion; + public ushort RemasterVersion; /// /// Text code set info /// - public CodeSetInfo TextCodesetInfo; + public CodeSetInfo TextCodeSetInfo; /// /// Stack size @@ -56,7 +56,7 @@ /// /// Dependency module (program ID) list /// - public byte[][] DependencyModuleList; + public ulong[] DependencyModuleList; /// /// SystemInfo diff --git a/BurnOutSharp.Models/N3DS/SystemInfo.cs b/BurnOutSharp.Models/N3DS/SystemInfo.cs index 8a7abae9..2001a707 100644 --- a/BurnOutSharp.Models/N3DS/SystemInfo.cs +++ b/BurnOutSharp.Models/N3DS/SystemInfo.cs @@ -11,7 +11,7 @@ /// /// Jump ID /// - public byte[] JumpID; + public ulong JumpID; /// /// Reserved