diff --git a/BurnOutSharp.Builders/N3DS.cs b/BurnOutSharp.Builders/N3DS.cs index cc95b8cb..62abd79b 100644 --- a/BurnOutSharp.Builders/N3DS.cs +++ b/BurnOutSharp.Builders/N3DS.cs @@ -161,6 +161,32 @@ namespace BurnOutSharp.Builders #endregion + #region RomFS Headers + + // Create the RomFS header table + cart.RomFSHeaders = new RomFSHeader[8]; + + // Iterate and build the RomFS 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 RomFS header offset + long offset = (cart.Header.PartitionsTable[i].Offset + cart.Partitions[i].RomFSOffsetInMediaUnits) * mediaUnitSize; + if (offset < 0 || offset >= data.Length) + continue; + + // Seek to the RomFS header + data.Seek(offset, SeekOrigin.Begin); + + // Parse the RomFS header + cart.RomFSHeaders[i] = ParseRomFSHeader(data); + } + + #endregion + return cart; } @@ -647,6 +673,44 @@ namespace BurnOutSharp.Builders return exeFSFileHeader; } + /// + /// Parse a Stream into an RomFS header + /// + /// Stream to parse + /// Filled RomFS header on success, null on error + private static RomFSHeader ParseRomFSHeader(Stream data) + { + // TODO: Use marshalling here instead of building + RomFSHeader romFSHeader = new RomFSHeader(); + + byte[] magicString = data.ReadBytes(4); + romFSHeader.MagicString = Encoding.ASCII.GetString(magicString).TrimEnd('\0'); + if (romFSHeader.MagicString != RomFSMagicNumber) + return null; + + romFSHeader.MagicNumber = data.ReadUInt32(); + if (romFSHeader.MagicNumber != RomFSSecondMagicNumber) + return null; + + romFSHeader.MasterHashSize = data.ReadUInt32(); + romFSHeader.Level1LogicalOffset = data.ReadUInt64(); + romFSHeader.Level1HashdataSize = data.ReadUInt64(); + romFSHeader.Level1BlockSizeLog2 = data.ReadUInt32(); + romFSHeader.Reserved1 = data.ReadBytes(4); + romFSHeader.Level2LogicalOffset = data.ReadUInt64(); + romFSHeader.Level2HashdataSize = data.ReadUInt64(); + romFSHeader.Level2BlockSizeLog2 = data.ReadUInt32(); + romFSHeader.Reserved2 = data.ReadBytes(4); + romFSHeader.Level3LogicalOffset = data.ReadUInt64(); + romFSHeader.Level3HashdataSize = data.ReadUInt64(); + romFSHeader.Level3BlockSizeLog2 = data.ReadUInt32(); + romFSHeader.Reserved3 = data.ReadBytes(4); + romFSHeader.Reserved4 = data.ReadBytes(4); + romFSHeader.OptionalInfoSize = data.ReadUInt32(); + + return romFSHeader; + } + #endregion } } diff --git a/BurnOutSharp.Models/N3DS/Cart.cs b/BurnOutSharp.Models/N3DS/Cart.cs index 0d8afe13..c48751aa 100644 --- a/BurnOutSharp.Models/N3DS/Cart.cs +++ b/BurnOutSharp.Models/N3DS/Cart.cs @@ -34,5 +34,10 @@ namespace BurnOutSharp.Models.N3DS /// ExeFS headers associated with each partition /// public ExeFSHeader[] ExeFSHeaders { get; set; } + + /// + /// RomFS headers associated with each partition + /// + public RomFSHeader[] RomFSHeaders { get; set; } } } \ No newline at end of file diff --git a/BurnOutSharp.Models/N3DS/RomFSHeader.cs b/BurnOutSharp.Models/N3DS/RomFSHeader.cs index a951cd23..80ea79ad 100644 --- a/BurnOutSharp.Models/N3DS/RomFSHeader.cs +++ b/BurnOutSharp.Models/N3DS/RomFSHeader.cs @@ -5,8 +5,19 @@ /// used as external file storage. /// /// + /// TODO: Implement the other parts of the RomFS tree structure public sealed class RomFSHeader { + /// + /// Magic "IVFC" + /// + public string MagicString; + + /// + /// Magic number 0x10000 + /// + public uint MagicNumber; + /// /// Master hash size /// diff --git a/BurnOutSharp.Wrappers/N3DS.cs b/BurnOutSharp.Wrappers/N3DS.cs index c6778498..dc7acd8e 100644 --- a/BurnOutSharp.Wrappers/N3DS.cs +++ b/BurnOutSharp.Wrappers/N3DS.cs @@ -204,6 +204,13 @@ namespace BurnOutSharp.Wrappers #endregion + #region RomFS Headers + + /// + public Models.N3DS.RomFSHeader[] RomFSHeaders => _cart.RomFSHeaders; + + #endregion + #endregion #region Instance Variables @@ -284,6 +291,7 @@ namespace BurnOutSharp.Wrappers PrintPartitions(); PrintExtendedHeaders(); PrintExeFSHeaders(); + PrintRomFSHeaders(); } /// @@ -641,7 +649,7 @@ namespace BurnOutSharp.Wrappers } /// - /// Print ExeFS header header information + /// Print ExeFS header information /// private void PrintExeFSHeaders() { @@ -688,6 +696,52 @@ namespace BurnOutSharp.Wrappers Console.WriteLine(); } + /// + /// Print RomFS header information + /// + private void PrintRomFSHeaders() + { + Console.WriteLine(" RomFS Header Information:"); + Console.WriteLine(" -------------------------"); + if (RomFSHeaders == null || RomFSHeaders.Length == 0) + { + Console.WriteLine(" No RomFS headers"); + } + else + { + for (int i = 0; i < RomFSHeaders.Length; i++) + { + var romFSHeader = RomFSHeaders[i]; + Console.WriteLine($" RomFS Header {i}"); + if (romFSHeader == null) + { + Console.WriteLine($" Unrecognized RomFS data, no data can be parsed"); + } + else + { + Console.WriteLine(value: $" Magic string: {romFSHeader.MagicString}"); + Console.WriteLine(value: $" Magic number: {romFSHeader.MagicNumber}"); + Console.WriteLine(value: $" Master hash size: {romFSHeader.MasterHashSize}"); + Console.WriteLine(value: $" Level 1 logical offset: {romFSHeader.Level1LogicalOffset}"); + Console.WriteLine(value: $" Level 1 hashdata size: {romFSHeader.Level1HashdataSize}"); + Console.WriteLine(value: $" Level 1 block size: {romFSHeader.Level1BlockSizeLog2}"); + Console.WriteLine(value: $" Reserved 1: {BitConverter.ToString(romFSHeader.Reserved1).Replace('-', ' ')}"); + Console.WriteLine(value: $" Level 2 logical offset: {romFSHeader.Level2LogicalOffset}"); + Console.WriteLine(value: $" Level 2 hashdata size: {romFSHeader.Level2HashdataSize}"); + Console.WriteLine(value: $" Level 2 block size: {romFSHeader.Level2BlockSizeLog2}"); + Console.WriteLine(value: $" Reserved 2: {BitConverter.ToString(romFSHeader.Reserved2).Replace('-', ' ')}"); + Console.WriteLine(value: $" Level 3 logical offset: {romFSHeader.Level3LogicalOffset}"); + Console.WriteLine(value: $" Level 3 hashdata size: {romFSHeader.Level3HashdataSize}"); + Console.WriteLine(value: $" Level 3 block size: {romFSHeader.Level3BlockSizeLog2}"); + Console.WriteLine(value: $" Reserved 3: {BitConverter.ToString(romFSHeader.Reserved3).Replace('-', ' ')}"); + Console.WriteLine(value: $" Reserved 4: {BitConverter.ToString(romFSHeader.Reserved4).Replace('-', ' ')}"); + Console.WriteLine(value: $" Optional info size: {romFSHeader.OptionalInfoSize}"); + } + } + } + Console.WriteLine(); + } + #endregion } } \ No newline at end of file