Add alignment, partition parsing, fix BE

This commit is contained in:
Matt Nadareski
2023-01-09 11:33:54 -08:00
parent 6b7ed456ac
commit 53dd1e9aa5
4 changed files with 156 additions and 22 deletions

View File

@@ -244,6 +244,12 @@ namespace BurnOutSharp.Builders
#endregion
// Align to 64-byte boundary, if needed
while (data.Position < data.Length - 1 && data.Position % 64 != 0)
{
_ = data.ReadByteValue();
}
#region Certificate Chain
// Create the certificate chain
@@ -261,6 +267,12 @@ namespace BurnOutSharp.Builders
#endregion
// Align to 64-byte boundary, if needed
while (data.Position < data.Length - 1 && data.Position % 64 != 0)
{
_ = data.ReadByteValue();
}
#region Ticket
// Try to parse the ticket
@@ -273,6 +285,12 @@ namespace BurnOutSharp.Builders
#endregion
// Align to 64-byte boundary, if needed
while (data.Position < data.Length - 1 && data.Position % 64 != 0)
{
_ = data.ReadByteValue();
}
#region Title Metadata
// Try to parse the title metadata
@@ -285,14 +303,31 @@ namespace BurnOutSharp.Builders
#endregion
// Align to 64-byte boundary, if needed
while (data.Position < data.Length - 1 && data.Position % 64 != 0)
{
_ = data.ReadByteValue();
}
#region Content File Data
// If we have content
if (header.ContentSize > 0)
cia.ContentFileData = data.ReadBytes((int)header.ContentSize);
// Create the partition table
cia.Partitions = new NCCHHeader[8];
// Iterate and build the partitions
for (int i = 0; i < 8; i++)
{
cia.Partitions[i] = ParseNCCHHeader(data);
}
#endregion
// Align to 64-byte boundary, if needed
while (data.Position < data.Length - 1 && data.Position % 64 != 0)
{
_ = data.ReadByteValue();
}
#region Meta Data
// If we have a meta data
@@ -933,8 +968,9 @@ namespace BurnOutSharp.Builders
/// Parse a Stream into a ticket
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="fromCdn">Indicates if the ticket is from CDN</param>
/// <returns>Filled ticket on success, null on error</returns>
private static Ticket ParseTicket(Stream data)
private static Ticket ParseTicket(Stream data, bool fromCdn = false)
{
// TODO: Use marshalling here instead of building
Ticket ticket = new Ticket();
@@ -1011,14 +1047,19 @@ namespace BurnOutSharp.Builders
data.Seek(-8, SeekOrigin.Current);
ticket.ContentIndex = data.ReadBytes((int)ticket.ContentIndexSize);
ticket.CertificateChain = new Certificate[2];
for (int i = 0; i < 2; i++)
{
var certificate = ParseCertificate(data);
if (certificate == null)
return null;
ticket.CertificateChain[i] = certificate;
// Certificates only exist in standalone CETK files
if (fromCdn)
{
ticket.CertificateChain = new Certificate[2];
for (int i = 0; i < 2; i++)
{
var certificate = ParseCertificate(data);
if (certificate == null)
return null;
ticket.CertificateChain[i] = certificate;
}
}
return ticket;
@@ -1028,8 +1069,9 @@ namespace BurnOutSharp.Builders
/// Parse a Stream into a title metadata
/// </summary>
/// <param name="data">Stream to parse</param>
/// <param name="fromCdn">Indicates if the ticket is from CDN</param>
/// <returns>Filled title metadata on success, null on error</returns>
private static TitleMetadata ParseTitleMetadata(Stream data)
private static TitleMetadata ParseTitleMetadata(Stream data, bool fromCdn = false)
{
// TODO: Use marshalling here instead of building
TitleMetadata titleMetadata = new TitleMetadata();
@@ -1084,7 +1126,12 @@ namespace BurnOutSharp.Builders
titleMetadata.Reserved3 = data.ReadBytes(0x31);
titleMetadata.AccessRights = data.ReadUInt32();
titleMetadata.TitleVersion = data.ReadUInt16();
titleMetadata.ContentCount = data.ReadUInt16();
// Read the content count (big-endian)
byte[] contentCount = data.ReadBytes(2);
Array.Reverse(contentCount);
titleMetadata.ContentCount = BitConverter.ToUInt16(contentCount, 0);
titleMetadata.BootContent = data.ReadUInt16();
titleMetadata.Padding2 = data.ReadBytes(2);
titleMetadata.SHA256HashContentInfoRecords = data.ReadBytes(0x20);
@@ -1098,14 +1145,19 @@ namespace BurnOutSharp.Builders
{
titleMetadata.ContentChunkRecords[i] = ParseContentChunkRecord(data);
}
titleMetadata.CertificateChain = new Certificate[2];
for (int i = 0; i < 2; i++)
{
var certificate = ParseCertificate(data);
if (certificate == null)
return null;
titleMetadata.CertificateChain[i] = certificate;
// Certificates only exist in standalone TMD files
if (fromCdn)
{
titleMetadata.CertificateChain = new Certificate[2];
for (int i = 0; i < 2; i++)
{
var certificate = ParseCertificate(data);
if (certificate == null)
return null;
titleMetadata.CertificateChain[i] = certificate;
}
}
return titleMetadata;

View File

@@ -37,6 +37,11 @@ namespace BurnOutSharp.Models.N3DS
/// </summary>
public TitleMetadata TMDFileData { get; set; }
/// <summary>
/// Content file data
/// </summary>
public NCCHHeader[] Partitions { get; set; }
/// <summary>
/// Content file data
/// </summary>

View File

@@ -114,7 +114,7 @@ namespace BurnOutSharp.Models.N3DS
public ushort TitleVersion;
/// <summary>
/// Content Count
/// Content Count (big-endian)
/// </summary>
public ushort ContentCount;

View File

@@ -224,6 +224,13 @@ namespace BurnOutSharp.Wrappers
#endregion
#region Partitions
/// <inheritdoc cref="Models.N3DS.CIA.Partitions"/>
public Models.N3DS.NCCHHeader[] Partitions => _cia.Partitions;
#endregion
#region Meta Data
/// <inheritdoc cref="Models.N3DS.MetaData.TitleIDDependencyList"/>
@@ -321,7 +328,7 @@ namespace BurnOutSharp.Wrappers
PrintCertificateChain();
PrintTicket();
PrintTitleMetadata();
// TODO: Print the content file data
PrintPartitions();
PrintMetaData();
}
@@ -602,6 +609,76 @@ namespace BurnOutSharp.Wrappers
Console.WriteLine();
}
/// <summary>
/// Print NCCH partition header information
/// </summary>
private void PrintPartitions()
{
Console.WriteLine(" NCCH Partition Header Information:");
Console.WriteLine(" -------------------------");
if (Partitions == null || Partitions.Length == 0)
{
Console.WriteLine(" No NCCH partition headers");
}
else
{
for (int i = 0; i < Partitions.Length; i++)
{
var partitionHeader = Partitions[i];
Console.WriteLine($" NCCH Partition Header {i}");
if (partitionHeader.MagicID == string.Empty)
{
Console.WriteLine($" Empty partition, no data can be parsed");
}
else if (partitionHeader.MagicID != Models.N3DS.Constants.NCCHMagicNumber)
{
Console.WriteLine($" Unrecognized partition data, no data can be parsed");
}
else
{
Console.WriteLine($" RSA-2048 SHA-256 signature: {BitConverter.ToString(partitionHeader.RSA2048Signature).Replace('-', ' ')}");
Console.WriteLine($" Magic ID: {partitionHeader.MagicID}");
Console.WriteLine($" Content size in media units: {partitionHeader.ContentSizeInMediaUnits}");
Console.WriteLine($" Partition ID: {partitionHeader.PartitionId}");
Console.WriteLine($" Maker code: {partitionHeader.MakerCode}");
Console.WriteLine($" Version: {partitionHeader.Version}");
Console.WriteLine($" Verification hash: {partitionHeader.VerificationHash}");
Console.WriteLine($" Program ID: {BitConverter.ToString(partitionHeader.ProgramId).Replace('-', ' ')}");
Console.WriteLine($" Reserved 1: {BitConverter.ToString(partitionHeader.Reserved1).Replace('-', ' ')}");
Console.WriteLine($" Logo region SHA-256 hash: {BitConverter.ToString(partitionHeader.LogoRegionHash).Replace('-', ' ')}");
Console.WriteLine($" Product code: {partitionHeader.ProductCode}");
Console.WriteLine($" Extended header SHA-256 hash: {BitConverter.ToString(partitionHeader.ExtendedHeaderHash).Replace('-', ' ')}");
Console.WriteLine($" Extended header size in bytes: {partitionHeader.ExtendedHeaderSizeInBytes}");
Console.WriteLine($" Reserved 2: {BitConverter.ToString(partitionHeader.Reserved2).Replace('-', ' ')}");
Console.WriteLine(" Flags:");
Console.WriteLine($" Reserved 0: {partitionHeader.Flags.Reserved0}");
Console.WriteLine($" Reserved 1: {partitionHeader.Flags.Reserved1}");
Console.WriteLine($" Reserved 2: {partitionHeader.Flags.Reserved2}");
Console.WriteLine($" Crypto method: {partitionHeader.Flags.CryptoMethod}");
Console.WriteLine($" Content platform: {partitionHeader.Flags.ContentPlatform}");
Console.WriteLine($" Content type: {partitionHeader.Flags.MediaPlatformIndex}");
Console.WriteLine($" Content unit size: {partitionHeader.Flags.ContentUnitSize}");
Console.WriteLine($" Bitmasks: {partitionHeader.Flags.BitMasks}");
Console.WriteLine($" Plain region offset, in media units: {partitionHeader.PlainRegionOffsetInMediaUnits}");
Console.WriteLine($" Plain region size, in media units: {partitionHeader.PlainRegionSizeInMediaUnits}");
Console.WriteLine($" Logo region offset, in media units: {partitionHeader.LogoRegionOffsetInMediaUnits}");
Console.WriteLine($" Logo region size, in media units: {partitionHeader.LogoRegionSizeInMediaUnits}");
Console.WriteLine($" ExeFS offset, in media units: {partitionHeader.ExeFSOffsetInMediaUnits}");
Console.WriteLine($" ExeFS size, in media units: {partitionHeader.ExeFSSizeInMediaUnits}");
Console.WriteLine($" ExeFS hash region size, in media units: {partitionHeader.ExeFSHashRegionSizeInMediaUnits}");
Console.WriteLine($" Reserved 3: {BitConverter.ToString(partitionHeader.Reserved3).Replace('-', ' ')}");
Console.WriteLine($" RomFS offset, in media units: {partitionHeader.RomFSOffsetInMediaUnits}");
Console.WriteLine($" RomFS size, in media units: {partitionHeader.RomFSSizeInMediaUnits}");
Console.WriteLine($" RomFS hash region size, in media units: {partitionHeader.RomFSHashRegionSizeInMediaUnits}");
Console.WriteLine($" Reserved 4: {BitConverter.ToString(partitionHeader.Reserved4).Replace('-', ' ')}");
Console.WriteLine($" ExeFS superblock SHA-256 hash: {BitConverter.ToString(partitionHeader.ExeFSSuperblockHash).Replace('-', ' ')}");
Console.WriteLine($" RomFS superblock SHA-256 hash: {BitConverter.ToString(partitionHeader.RomFSSuperblockHash).Replace('-', ' ')}");
}
}
}
Console.WriteLine();
}
/// <summary>
/// Print metadata information
/// </summary>