diff --git a/BurnOutSharp.Builders/N3DS.cs b/BurnOutSharp.Builders/N3DS.cs
index 62abd79b..b0a560e5 100644
--- a/BurnOutSharp.Builders/N3DS.cs
+++ b/BurnOutSharp.Builders/N3DS.cs
@@ -32,6 +32,27 @@ namespace BurnOutSharp.Builders
return ParseCart(dataStream);
}
+ ///
+ /// Parse a byte array into a CIA archive
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// Filled CIA archive on success, null on error
+ public static CIA ParseCIA(byte[] data, int offset)
+ {
+ // If the data is invalid
+ if (data == null)
+ return null;
+
+ // If the offset is out of bounds
+ if (offset < 0 || offset >= data.Length)
+ return null;
+
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
+ return ParseCIA(dataStream);
+ }
+
#endregion
#region Stream Data
@@ -190,6 +211,99 @@ namespace BurnOutSharp.Builders
return cart;
}
+ ///
+ /// Parse a Stream into a CIA archive
+ ///
+ /// Stream to parse
+ /// Filled CIA archive on success, null on error
+ public static CIA ParseCIA(Stream data)
+ {
+ // If the data is invalid
+ if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
+ return null;
+
+ // If the offset is out of bounds
+ if (data.Position < 0 || data.Position >= data.Length)
+ return null;
+
+ // Cache the current offset
+ int initialOffset = (int)data.Position;
+
+ // Create a new CIA archive to fill
+ var cia = new CIA();
+
+ #region CIA Header
+
+ // Try to parse the header
+ var header = ParseCIAHeader(data);
+ if (header == null)
+ return null;
+
+ // Set the CIA archive header
+ cia.Header = header;
+
+ #endregion
+
+ #region Certificate Chain
+
+ // Create the certificate chain
+ cia.CertificateChain = new Certificate[3];
+
+ // Try to parse the certificates
+ for (int i = 0; i < 3; i++)
+ {
+ var certificate = ParseCertificate(data);
+ if (certificate == null)
+ return null;
+
+ cia.CertificateChain[i] = certificate;
+ }
+
+ #endregion
+
+ #region Ticket
+
+ // Try to parse the ticket
+ var ticket = ParseTicket(data);
+ if (ticket == null)
+ return null;
+
+ // Set the ticket
+ cia.Ticket = ticket;
+
+ #endregion
+
+ #region Title Metadata
+
+ // Try to parse the title metadata
+ var titleMetadata = ParseTitleMetadata(data);
+ if (titleMetadata == null)
+ return null;
+
+ // Set the title metadata
+ cia.TMDFileData = titleMetadata;
+
+ #endregion
+
+ #region Meta Data
+
+ // If we have a meta data
+ if (header.MetaSize > 0)
+ {
+ // Try to parse the meta
+ var meta = ParseMetaData(data);
+ if (meta == null)
+ return null;
+
+ // Set the meta
+ cia.MetaData = meta;
+ }
+
+ #endregion
+
+ return cia;
+ }
+
///
/// Parse a Stream into an NCSD header
///
@@ -711,6 +825,339 @@ namespace BurnOutSharp.Builders
return romFSHeader;
}
+ ///
+ /// Parse a Stream into a CIA header
+ ///
+ /// Stream to parse
+ /// Filled CIA header on success, null on error
+ private static CIAHeader ParseCIAHeader(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ CIAHeader ciaHeader = new CIAHeader();
+
+ ciaHeader.HeaderSize = data.ReadUInt32();
+ ciaHeader.Type = data.ReadUInt16();
+ ciaHeader.Version = data.ReadUInt16();
+ ciaHeader.CertificateChainSize = data.ReadUInt32();
+ ciaHeader.TicketSize = data.ReadUInt32();
+ ciaHeader.TMDFileSize = data.ReadUInt32();
+ ciaHeader.MetaSize = data.ReadUInt32();
+ ciaHeader.ContentSize = data.ReadUInt64();
+ ciaHeader.ContentIndex = data.ReadBytes(0x2000);
+
+ return ciaHeader;
+ }
+
+ ///
+ /// Parse a Stream into a certificate
+ ///
+ /// Stream to parse
+ /// Filled certificate on success, null on error
+ private static Certificate ParseCertificate(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ Certificate certificate = new Certificate();
+
+ certificate.SignatureType = (SignatureType)data.ReadUInt32();
+ switch (certificate.SignatureType)
+ {
+ case SignatureType.RSA_4096_SHA1:
+ certificate.SignatureSize = 0x200;
+ certificate.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA1:
+ certificate.SignatureSize = 0x100;
+ certificate.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA1:
+ certificate.SignatureSize = 0x3C;
+ certificate.PaddingSize = 0x40;
+ break;
+ case SignatureType.RSA_4096_SHA256:
+ certificate.SignatureSize = 0x200;
+ certificate.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA256:
+ certificate.SignatureSize = 0x100;
+ certificate.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA256:
+ certificate.SignatureSize = 0x3C;
+ certificate.PaddingSize = 0x40;
+ break;
+ default:
+ return null;
+ }
+
+ certificate.Signature = data.ReadBytes(certificate.SignatureSize);
+ certificate.Padding = data.ReadBytes(certificate.PaddingSize);
+ byte[] issuer = data.ReadBytes(0x40);
+ certificate.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
+ certificate.KeyType = (PublicKeyType)data.ReadUInt32();
+ byte[] name = data.ReadBytes(0x40);
+ certificate.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
+ certificate.ExpirationTime = data.ReadUInt32();
+
+ switch (certificate.KeyType)
+ {
+ case PublicKeyType.RSA_4096:
+ certificate.RSAModulus = data.ReadBytes(0x200);
+ certificate.RSAPublicExponent = data.ReadUInt32();
+ certificate.RSAPadding = data.ReadBytes(0x34);
+ break;
+ case PublicKeyType.RSA_2048:
+ certificate.RSAModulus = data.ReadBytes(0x100);
+ certificate.RSAPublicExponent = data.ReadUInt32();
+ certificate.RSAPadding = data.ReadBytes(0x34);
+ break;
+ case PublicKeyType.EllipticCurve:
+ certificate.ECCPublicKey = data.ReadBytes(0x3C);
+ certificate.ECCPadding = data.ReadBytes(0x3C);
+ break;
+ default:
+ return null;
+ }
+
+ return certificate;
+ }
+
+ ///
+ /// Parse a Stream into a ticket
+ ///
+ /// Stream to parse
+ /// Filled ticket on success, null on error
+ private static Ticket ParseTicket(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ Ticket ticket = new Ticket();
+
+ ticket.SignatureType = (SignatureType)data.ReadUInt32();
+ switch (ticket.SignatureType)
+ {
+ case SignatureType.RSA_4096_SHA1:
+ ticket.SignatureSize = 0x200;
+ ticket.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA1:
+ ticket.SignatureSize = 0x100;
+ ticket.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA1:
+ ticket.SignatureSize = 0x3C;
+ ticket.PaddingSize = 0x40;
+ break;
+ case SignatureType.RSA_4096_SHA256:
+ ticket.SignatureSize = 0x200;
+ ticket.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA256:
+ ticket.SignatureSize = 0x100;
+ ticket.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA256:
+ ticket.SignatureSize = 0x3C;
+ ticket.PaddingSize = 0x40;
+ break;
+ default:
+ return null;
+ }
+
+ ticket.Signature = data.ReadBytes(ticket.SignatureSize);
+ ticket.Padding = data.ReadBytes(ticket.PaddingSize);
+ byte[] issuer = data.ReadBytes(0x40);
+ ticket.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
+ ticket.ECCPublicKey = data.ReadBytes(0x3C);
+ ticket.Version = data.ReadByteValue();
+ ticket.CaCrlVersion = data.ReadByteValue();
+ ticket.SignerCrlVersion = data.ReadByteValue();
+ ticket.TitleKey = data.ReadBytes(0x10);
+ ticket.Reserved1 = data.ReadByteValue();
+ ticket.TicketID = data.ReadUInt64();
+ ticket.ConsoleID = data.ReadUInt32();
+ ticket.TitleID = data.ReadUInt64();
+ ticket.Reserved2 = data.ReadBytes(2);
+ ticket.TicketTitleVersion = data.ReadUInt16();
+ ticket.Reserved3 = data.ReadBytes(8);
+ ticket.LicenseType = data.ReadByteValue();
+ ticket.CommonKeyYIndex = data.ReadByteValue();
+ ticket.Reserved4 = data.ReadBytes(0x2A);
+ ticket.eShopAccountID = data.ReadUInt32();
+ ticket.Reserved5 = data.ReadByteValue();
+ ticket.Audit = data.ReadByteValue();
+ ticket.Reserved6 = data.ReadBytes(0x42);
+ ticket.Limits = new uint[0x10];
+ for (int i = 0; i < ticket.Limits.Length; i++)
+ {
+ ticket.Limits[i] = data.ReadUInt32();
+ }
+
+ // Seek to the content index size
+ data.Seek(4, SeekOrigin.Current);
+
+ // Read the size (big-endian)
+ byte[] contentIndexSize = data.ReadBytes(4);
+ Array.Reverse(contentIndexSize);
+ ticket.ContentIndexSize = BitConverter.ToUInt32(contentIndexSize, 0);
+
+ // Seek back to the start of the content index
+ 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;
+ }
+
+ return ticket;
+ }
+
+ ///
+ /// Parse a Stream into a title metadata
+ ///
+ /// Stream to parse
+ /// Filled title metadata on success, null on error
+ private static TitleMetadata ParseTitleMetadata(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ TitleMetadata titleMetadata = new TitleMetadata();
+
+ titleMetadata.SignatureType = (SignatureType)data.ReadUInt32();
+ switch (titleMetadata.SignatureType)
+ {
+ case SignatureType.RSA_4096_SHA1:
+ titleMetadata.SignatureSize = 0x200;
+ titleMetadata.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA1:
+ titleMetadata.SignatureSize = 0x100;
+ titleMetadata.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA1:
+ titleMetadata.SignatureSize = 0x3C;
+ titleMetadata.PaddingSize = 0x40;
+ break;
+ case SignatureType.RSA_4096_SHA256:
+ titleMetadata.SignatureSize = 0x200;
+ titleMetadata.PaddingSize = 0x3C;
+ break;
+ case SignatureType.RSA_2048_SHA256:
+ titleMetadata.SignatureSize = 0x100;
+ titleMetadata.PaddingSize = 0x3C;
+ break;
+ case SignatureType.ECDSA_SHA256:
+ titleMetadata.SignatureSize = 0x3C;
+ titleMetadata.PaddingSize = 0x40;
+ break;
+ default:
+ return null;
+ }
+
+ titleMetadata.Signature = data.ReadBytes(titleMetadata.SignatureSize);
+ titleMetadata.Padding1 = data.ReadBytes(titleMetadata.PaddingSize);
+ byte[] issuer = data.ReadBytes(0x40);
+ titleMetadata.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0');
+ titleMetadata.Version = data.ReadByteValue();
+ titleMetadata.CaCrlVersion = data.ReadByteValue();
+ titleMetadata.SignerCrlVersion = data.ReadByteValue();
+ titleMetadata.Reserved1 = data.ReadByteValue();
+ titleMetadata.SystemVersion = data.ReadUInt64();
+ titleMetadata.TitleID = data.ReadUInt64();
+ titleMetadata.TitleType = data.ReadUInt32();
+ titleMetadata.GroupID = data.ReadUInt16();
+ titleMetadata.SaveDataSize = data.ReadUInt32();
+ titleMetadata.SRLPrivateSaveDataSize = data.ReadUInt32();
+ titleMetadata.Reserved2 = data.ReadBytes(4);
+ titleMetadata.SRLFlag = data.ReadByteValue();
+ titleMetadata.Reserved3 = data.ReadBytes(0x31);
+ titleMetadata.AccessRights = data.ReadUInt32();
+ titleMetadata.TitleVersion = data.ReadUInt16();
+ titleMetadata.ContentCount = data.ReadUInt16();
+ titleMetadata.BootContent = data.ReadUInt16();
+ titleMetadata.Padding2 = data.ReadBytes(2);
+ titleMetadata.SHA256HashContentInfoRecords = data.ReadBytes(0x20);
+ titleMetadata.ContentInfoRecords = new ContentInfoRecord[64];
+ for (int i = 0; i < 64; i++)
+ {
+ titleMetadata.ContentInfoRecords[i] = ParseContentInfoRecord(data);
+ }
+ titleMetadata.ContentChunkRecords = new ContentChunkRecord[titleMetadata.ContentCount];
+ for (int i = 0; i < titleMetadata.ContentCount; i++)
+ {
+ 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;
+ }
+
+ return titleMetadata;
+ }
+
+ ///
+ /// Parse a Stream into a content info record
+ ///
+ /// Stream to parse
+ /// Filled content info record on success, null on error
+ private static ContentInfoRecord ParseContentInfoRecord(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ ContentInfoRecord contentInfoRecord = new ContentInfoRecord();
+
+ contentInfoRecord.ContentIndexOffset = data.ReadUInt16();
+ contentInfoRecord.ContentCommandCount = data.ReadUInt16();
+ contentInfoRecord.UnhashedContentRecordsSHA256Hash = data.ReadBytes(0x20);
+
+ return contentInfoRecord;
+ }
+
+ ///
+ /// Parse a Stream into a content chunk record
+ ///
+ /// Stream to parse
+ /// Filled content chunk record on success, null on error
+ private static ContentChunkRecord ParseContentChunkRecord(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ ContentChunkRecord contentChunkRecord = new ContentChunkRecord();
+
+ contentChunkRecord.ContentId = data.ReadUInt32();
+ contentChunkRecord.ContentIndex = (ContentIndex)data.ReadUInt16();
+ contentChunkRecord.ContentType = (TMDContentType)data.ReadUInt16();
+ contentChunkRecord.ContentSize = data.ReadUInt64();
+ contentChunkRecord.SHA256Hash = data.ReadBytes(0x20);
+
+ return contentChunkRecord;
+ }
+
+ ///
+ /// Parse a Stream into a meta data
+ ///
+ /// Stream to parse
+ /// Filled meta data on success, null on error
+ private static MetaData ParseMetaData(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ MetaData metaData = new MetaData();
+
+ metaData.TitleIDDependencyList = data.ReadBytes(0x180);
+ metaData.Reserved1 = data.ReadBytes(0x180);
+ metaData.CoreVersion = data.ReadUInt32();
+ metaData.Reserved2 = data.ReadBytes(0xFC);
+ metaData.IconData = data.ReadBytes(0x36C0);
+
+ return metaData;
+ }
+
#endregion
}
}
diff --git a/BurnOutSharp.Models/N3DS/CIA.cs b/BurnOutSharp.Models/N3DS/CIA.cs
new file mode 100644
index 00000000..cdef9b55
--- /dev/null
+++ b/BurnOutSharp.Models/N3DS/CIA.cs
@@ -0,0 +1,50 @@
+namespace BurnOutSharp.Models.N3DS
+{
+ ///
+ /// CIA stands for CTR Importable Archive. This format allows the installation of
+ /// titles to the 3DS. CIA files and titles on Nintendo's CDN contain identical data.
+ /// As a consequence, valid CIA files can be generated from CDN content. This also
+ /// means CIA files can contain anything that titles on Nintendo's CDN can contain.
+ ///
+ /// Under normal circumstances CIA files are used where downloading a title is
+ /// impractical or not possible. Such as distributing a Download Play child, or
+ /// installing forced Gamecard updates. Those CIA(s) are stored by the titles in
+ /// question, in an auxiliary CFA file.
+ ///
+ ///
+ public class CIA
+ {
+ ///
+ /// CIA header
+ ///
+ public CIAHeader Header { get; set; }
+
+ ///
+ /// Certificate chain
+ ///
+ ///
+ /// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
+ ///
+ public Certificate[] CertificateChain { get; set; }
+
+ ///
+ /// Ticket
+ ///
+ public Ticket Ticket { get; set; }
+
+ ///
+ /// TMD file data
+ ///
+ public TitleMetadata TMDFileData { get; set; }
+
+ ///
+ /// Content file data
+ ///
+ public NCCHHeader[] Partitions { get; set; }
+
+ ///
+ /// Meta file data (Not a necessary component)
+ ///
+ public MetaData MetaData { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/N3DS/CIAHeader.cs b/BurnOutSharp.Models/N3DS/CIAHeader.cs
index aec997ad..a0806666 100644
--- a/BurnOutSharp.Models/N3DS/CIAHeader.cs
+++ b/BurnOutSharp.Models/N3DS/CIAHeader.cs
@@ -1,23 +1,12 @@
namespace BurnOutSharp.Models.N3DS
{
- ///
- /// CIA stands for CTR Importable Archive. This format allows the installation of
- /// titles to the 3DS. CIA files and titles on Nintendo's CDN contain identical data.
- /// As a consequence, valid CIA files can be generated from CDN content. This also
- /// means CIA files can contain anything that titles on Nintendo's CDN can contain.
- ///
- /// Under normal circumstances CIA files are used where downloading a title is
- /// impractical or not possible. Such as distributing a Download Play child, or
- /// installing forced Gamecard updates. Those CIA(s) are stored by the titles in
- /// question, in an auxiliary CFA file.
- ///
- ///
+ ///
public sealed class CIAHeader
{
///
/// Archive header size, usually 0x2020 bytes
///
- public int HeaderSize;
+ public uint HeaderSize;
///
/// Type
@@ -32,63 +21,31 @@ namespace BurnOutSharp.Models.N3DS
///
/// Certificate chain size
///
- public int CertificateChainSize;
+ public uint CertificateChainSize;
///
/// Ticket size
///
- public int TicketSize;
+ public uint TicketSize;
///
/// TMD file size
///
- public int TMDFileSize;
+ public uint TMDFileSize;
///
/// Meta size (0 if no Meta data is present)
///
- public int MetaSize;
+ public uint MetaSize;
///
/// Content size
///
- public long ContentSize;
+ public ulong ContentSize;
///
/// Content Index
///
public byte[] ContentIndex;
-
- #region Content Index
-
- ///
- /// Certificate chain
- ///
- ///
- /// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
- ///
- public Certificate[] CertificateChain;
-
- ///
- /// Ticket
- ///
- public Ticket Ticket;
-
- ///
- /// TMD file data
- ///
- public TitleMetadata TMDFileData;
-
- ///
- /// Content file data
- ///
- public NCCHHeader[] Partitions;
-
- ///
- /// Meta file data (Not a necessary component)
- ///
- public MetaFile MetaFileData;
-
- #endregion
}
}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/N3DS/Certificate.cs b/BurnOutSharp.Models/N3DS/Certificate.cs
index 83a31c48..0d6f772d 100644
--- a/BurnOutSharp.Models/N3DS/Certificate.cs
+++ b/BurnOutSharp.Models/N3DS/Certificate.cs
@@ -29,6 +29,11 @@ namespace BurnOutSharp.Models.N3DS
///
public byte[] Signature;
+ ///
+ /// Padding to align next data to 0x40 bytes
+ ///
+ public byte[] Padding;
+
///
/// Issuer
///
@@ -49,18 +54,23 @@ namespace BurnOutSharp.Models.N3DS
///
public uint ExpirationTime;
- // This contains the Public Key(i.e. Modulus & Public Exponent)
- #region RSA
+ // This contains the Public Key (i.e. Modulus & Public Exponent)
+ #region RSA-4096 and RSA-2048
///
/// Modulus
///
- public byte[] Modulus;
+ public byte[] RSAModulus;
///
/// Public Exponent
///
- public uint PublicExponent;
+ public uint RSAPublicExponent;
+
+ ///
+ /// Padding
+ ///
+ public byte[] RSAPadding;
#endregion
@@ -70,7 +80,12 @@ namespace BurnOutSharp.Models.N3DS
///
/// Public Key
///
- public byte[] PublicKey;
+ public byte[] ECCPublicKey;
+
+ ///
+ /// Padding
+ ///
+ public byte[] ECCPadding;
#endregion
}
diff --git a/BurnOutSharp.Models/N3DS/Enums.cs b/BurnOutSharp.Models/N3DS/Enums.cs
index 1d4f4580..1d66dea2 100644
--- a/BurnOutSharp.Models/N3DS/Enums.cs
+++ b/BurnOutSharp.Models/N3DS/Enums.cs
@@ -189,7 +189,7 @@ namespace BurnOutSharp.Models.N3DS
{
RSA_4096 = 0x00000000,
RSA_2048 = 0x01000000,
- ECDSA = 0x02000000,
+ EllipticCurve = 0x02000000,
}
public enum ResourceLimitCategory
diff --git a/BurnOutSharp.Models/N3DS/MetaFile.cs b/BurnOutSharp.Models/N3DS/MetaData.cs
similarity index 88%
rename from BurnOutSharp.Models/N3DS/MetaFile.cs
rename to BurnOutSharp.Models/N3DS/MetaData.cs
index 6962899a..618a4155 100644
--- a/BurnOutSharp.Models/N3DS/MetaFile.cs
+++ b/BurnOutSharp.Models/N3DS/MetaData.cs
@@ -1,11 +1,12 @@
namespace BurnOutSharp.Models.N3DS
{
///
- public sealed class MetaFile
+ public sealed class MetaData
{
///
/// Title ID dependency list - Taken from the application's ExHeader
///
+ /// TODO: Determine numeric format of each entry
public byte[] TitleIDDependencyList;
///
diff --git a/BurnOutSharp.Models/N3DS/Ticket.cs b/BurnOutSharp.Models/N3DS/Ticket.cs
index 048990f6..a37c4cc4 100644
--- a/BurnOutSharp.Models/N3DS/Ticket.cs
+++ b/BurnOutSharp.Models/N3DS/Ticket.cs
@@ -27,6 +27,11 @@ namespace BurnOutSharp.Models.N3DS
///
public byte[] Signature;
+ ///
+ /// Padding
+ ///
+ public byte[] Padding;
+
///
/// Issuer
///
@@ -91,7 +96,7 @@ namespace BurnOutSharp.Models.N3DS
///
/// Reserved
///
- public ushort Reserved2;
+ public byte[] Reserved2;
///
/// Ticket title version
@@ -105,7 +110,7 @@ namespace BurnOutSharp.Models.N3DS
///
/// Reserved
///
- public ulong Reserved3;
+ public byte[] Reserved3;
///
/// License Type
@@ -113,8 +118,7 @@ namespace BurnOutSharp.Models.N3DS
public byte LicenseType;
///
- /// Index to the common keyY used for this ticket, usually 0x1 for retail system titles;
- /// see below.
+ /// Index to the common keyY used for this ticket, usually 0x1 for retail system titles
///
public byte CommonKeyYIndex;
@@ -149,14 +153,14 @@ namespace BurnOutSharp.Models.N3DS
///
/// In demos, the first u32 in the "Limits" section is 0x4, then the second u32 is the max-playcount.
///
- public int[] Limits;
+ public uint[] Limits;
///
/// The Content Index of a ticket has its own size defined within itself,
/// with seemingly a minimal of 20 bytes, the second u32 in big endian defines
/// the full value of X.
///
- public int ContentIndexSize;
+ public uint ContentIndexSize;
///
/// Content Index
diff --git a/BurnOutSharp.Models/N3DS/TitleMetadata.cs b/BurnOutSharp.Models/N3DS/TitleMetadata.cs
index b99c4ca1..cb56cc0c 100644
--- a/BurnOutSharp.Models/N3DS/TitleMetadata.cs
+++ b/BurnOutSharp.Models/N3DS/TitleMetadata.cs
@@ -28,10 +28,15 @@ namespace BurnOutSharp.Models.N3DS
///
public byte[] Signature;
+ ///
+ /// Padding
+ ///
+ public byte[] Padding1;
+
///
/// Signature Issuer
///
- public byte[] SignatureIssuer;
+ public string Issuer;
///
/// Version
@@ -86,7 +91,7 @@ namespace BurnOutSharp.Models.N3DS
///
/// Reserved
///
- public uint Reserved2;
+ public byte[] Reserved2;
///
/// SRL Flag
@@ -121,7 +126,7 @@ namespace BurnOutSharp.Models.N3DS
///
/// Padding
///
- public ushort Padding;
+ public byte[] Padding2;
///
/// SHA-256 Hash of the Content Info Records