diff --git a/BurnOutSharp.Builders/N3DS.cs b/BurnOutSharp.Builders/N3DS.cs index 1066490a..5f320c18 100644 --- a/BurnOutSharp.Builders/N3DS.cs +++ b/BurnOutSharp.Builders/N3DS.cs @@ -106,6 +106,9 @@ namespace BurnOutSharp.Builders #endregion + // Cache the media unit size for further use + long mediaUnitSize = (uint)(0x200 * Math.Pow(2, header.PartitionFlags[(int)NCSDFlags.MediaUnitSize])); + #region Extended Headers // Create the extended header table @@ -119,7 +122,6 @@ namespace BurnOutSharp.Builders 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; @@ -133,7 +135,32 @@ namespace BurnOutSharp.Builders #endregion - // TODO: Parse ExeFS file entries (decrypted only) + #region ExeFS Headers + + // Create the ExeFS header table + cart.ExeFSHeaders = new ExeFSHeader[8]; + + // Iterate and build the ExeFS 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 ExeFS header offset + long offset = (cart.Header.PartitionsTable[i].Offset + cart.Partitions[i].ExeFSOffsetInMediaUnits) * mediaUnitSize; + if (offset < 0 || offset >= data.Length) + continue; + + // Seek to the ExeFS header + data.Seek(offset, SeekOrigin.Begin); + + // Parse the ExeFS header + cart.ExeFSHeaders[i] = ParseExeFSHeader(data); + } + + #endregion + // TODO: Parse ExeFS filename table (decrypted only) return cart; @@ -579,6 +606,49 @@ namespace BurnOutSharp.Builders return arm9AccessControl; } + /// + /// Parse a Stream into an ExeFS header + /// + /// Stream to parse + /// Filled ExeFS header on success, null on error + private static ExeFSHeader ParseExeFSHeader(Stream data) + { + // TODO: Use marshalling here instead of building + ExeFSHeader exeFSHeader = new ExeFSHeader(); + + exeFSHeader.FileHeaders = new ExeFSFileHeader[10]; + for (int i = 0; i < 10; i++) + { + exeFSHeader.FileHeaders[i] = ParseExeFSFileHeader(data); + } + exeFSHeader.Reserved = data.ReadBytes(0x20); + exeFSHeader.FileHashes = new byte[10][]; + for (int i = 0; i < 10; i++) + { + exeFSHeader.FileHashes[i] = data.ReadBytes(0x20); + } + + return exeFSHeader; + } + + /// + /// Parse a Stream into an ExeFS file header + /// + /// Stream to parse + /// Filled ExeFS file header on success, null on error + private static ExeFSFileHeader ParseExeFSFileHeader(Stream data) + { + // TODO: Use marshalling here instead of building + ExeFSFileHeader exeFSFileHeader = new ExeFSFileHeader(); + + byte[] fileName = data.ReadBytes(8); + exeFSFileHeader.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0'); + exeFSFileHeader.FileOffset = data.ReadUInt32(); + exeFSFileHeader.FileSize = data.ReadUInt32(); + + return exeFSFileHeader; + } + #endregion } } diff --git a/BurnOutSharp.Models/N3DS/Cart.cs b/BurnOutSharp.Models/N3DS/Cart.cs index d1b2ffde..0d8afe13 100644 --- a/BurnOutSharp.Models/N3DS/Cart.cs +++ b/BurnOutSharp.Models/N3DS/Cart.cs @@ -29,5 +29,10 @@ namespace BurnOutSharp.Models.N3DS /// NCCH extended headers /// public NCCHExtendedHeader[] ExtendedHeaders { get; set; } + + /// + /// ExeFS headers associated with each partition + /// + public ExeFSHeader[] ExeFSHeaders { get; set; } } } \ No newline at end of file diff --git a/BurnOutSharp.Models/N3DS/ExeFSHeader.cs b/BurnOutSharp.Models/N3DS/ExeFSHeader.cs index 9c6dda89..061ecad8 100644 --- a/BurnOutSharp.Models/N3DS/ExeFSHeader.cs +++ b/BurnOutSharp.Models/N3DS/ExeFSHeader.cs @@ -22,5 +22,11 @@ /// Reserved /// public byte[] Reserved; + + /// + /// File hashes (10 hashes maximum, 32 bytes each, one for each header) + /// + /// SHA-256 hashes + public byte[][] FileHashes; } }