mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-09-22 14:54:56 +00:00
Add CIA builder
This commit is contained in:
@@ -32,6 +32,27 @@ namespace BurnOutSharp.Builders
|
||||
return ParseCart(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a CIA archive
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled CIA archive on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CIA archive
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CIA archive on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an NCSD header
|
||||
/// </summary>
|
||||
@@ -711,6 +825,339 @@ namespace BurnOutSharp.Builders
|
||||
return romFSHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CIA header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CIA header on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a certificate
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled certificate on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ticket
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ticket on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a title metadata
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled title metadata on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a content info record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled content info record on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a content chunk record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled content chunk record on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a meta data
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled meta data on success, null on error</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
50
BurnOutSharp.Models/N3DS/CIA.cs
Normal file
50
BurnOutSharp.Models/N3DS/CIA.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace BurnOutSharp.Models.N3DS
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <see href="https://www.3dbrew.org/wiki/CIA"/>
|
||||
public class CIA
|
||||
{
|
||||
/// <summary>
|
||||
/// CIA header
|
||||
/// </summary>
|
||||
public CIAHeader Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
|
||||
/// </remarks>
|
||||
public Certificate[] CertificateChain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ticket
|
||||
/// </summary>
|
||||
public Ticket Ticket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TMD file data
|
||||
/// </summary>
|
||||
public TitleMetadata TMDFileData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content file data
|
||||
/// </summary>
|
||||
public NCCHHeader[] Partitions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Meta file data (Not a necessary component)
|
||||
/// </summary>
|
||||
public MetaData MetaData { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
namespace BurnOutSharp.Models.N3DS
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <see href="https://www.3dbrew.org/wiki/CIA"/>
|
||||
/// <see href="https://www.3dbrew.org/wiki/CIA#CIA_Header"/>
|
||||
public sealed class CIAHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// Archive header size, usually 0x2020 bytes
|
||||
/// </summary>
|
||||
public int HeaderSize;
|
||||
public uint HeaderSize;
|
||||
|
||||
/// <summary>
|
||||
/// Type
|
||||
@@ -32,63 +21,31 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Certificate chain size
|
||||
/// </summary>
|
||||
public int CertificateChainSize;
|
||||
public uint CertificateChainSize;
|
||||
|
||||
/// <summary>
|
||||
/// Ticket size
|
||||
/// </summary>
|
||||
public int TicketSize;
|
||||
public uint TicketSize;
|
||||
|
||||
/// <summary>
|
||||
/// TMD file size
|
||||
/// </summary>
|
||||
public int TMDFileSize;
|
||||
public uint TMDFileSize;
|
||||
|
||||
/// <summary>
|
||||
/// Meta size (0 if no Meta data is present)
|
||||
/// </summary>
|
||||
public int MetaSize;
|
||||
public uint MetaSize;
|
||||
|
||||
/// <summary>
|
||||
/// Content size
|
||||
/// </summary>
|
||||
public long ContentSize;
|
||||
public ulong ContentSize;
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
/// </summary>
|
||||
public byte[] ContentIndex;
|
||||
|
||||
#region Content Index
|
||||
|
||||
/// <summary>
|
||||
/// Certificate chain
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://www.3dbrew.org/wiki/CIA#Certificate_Chain
|
||||
/// </remarks>
|
||||
public Certificate[] CertificateChain;
|
||||
|
||||
/// <summary>
|
||||
/// Ticket
|
||||
/// </summary>
|
||||
public Ticket Ticket;
|
||||
|
||||
/// <summary>
|
||||
/// TMD file data
|
||||
/// </summary>
|
||||
public TitleMetadata TMDFileData;
|
||||
|
||||
/// <summary>
|
||||
/// Content file data
|
||||
/// </summary>
|
||||
public NCCHHeader[] Partitions;
|
||||
|
||||
/// <summary>
|
||||
/// Meta file data (Not a necessary component)
|
||||
/// </summary>
|
||||
public MetaFile MetaFileData;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,11 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// </summary>
|
||||
public byte[] Signature;
|
||||
|
||||
/// <summary>
|
||||
/// Padding to align next data to 0x40 bytes
|
||||
/// </summary>
|
||||
public byte[] Padding;
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
@@ -49,18 +54,23 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Modulus
|
||||
/// </summary>
|
||||
public byte[] Modulus;
|
||||
public byte[] RSAModulus;
|
||||
|
||||
/// <summary>
|
||||
/// Public Exponent
|
||||
/// </summary>
|
||||
public uint PublicExponent;
|
||||
public uint RSAPublicExponent;
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public byte[] RSAPadding;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -70,7 +80,12 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Public Key
|
||||
/// </summary>
|
||||
public byte[] PublicKey;
|
||||
public byte[] ECCPublicKey;
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public byte[] ECCPadding;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
{
|
||||
RSA_4096 = 0x00000000,
|
||||
RSA_2048 = 0x01000000,
|
||||
ECDSA = 0x02000000,
|
||||
EllipticCurve = 0x02000000,
|
||||
}
|
||||
|
||||
public enum ResourceLimitCategory
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
namespace BurnOutSharp.Models.N3DS
|
||||
{
|
||||
/// <see href="https://www.3dbrew.org/wiki/CIA#Meta"/>
|
||||
public sealed class MetaFile
|
||||
public sealed class MetaData
|
||||
{
|
||||
/// <summary>
|
||||
/// Title ID dependency list - Taken from the application's ExHeader
|
||||
/// </summary>
|
||||
/// TODO: Determine numeric format of each entry
|
||||
public byte[] TitleIDDependencyList;
|
||||
|
||||
/// <summary>
|
||||
@@ -27,6 +27,11 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// </summary>
|
||||
public byte[] Signature;
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public byte[] Padding;
|
||||
|
||||
/// <summary>
|
||||
/// Issuer
|
||||
/// </summary>
|
||||
@@ -91,7 +96,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public ushort Reserved2;
|
||||
public byte[] Reserved2;
|
||||
|
||||
/// <summary>
|
||||
/// Ticket title version
|
||||
@@ -105,7 +110,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public ulong Reserved3;
|
||||
public byte[] Reserved3;
|
||||
|
||||
/// <summary>
|
||||
/// License Type
|
||||
@@ -113,8 +118,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
public byte LicenseType;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public byte CommonKeyYIndex;
|
||||
|
||||
@@ -149,14 +153,14 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <remarks>
|
||||
/// In demos, the first u32 in the "Limits" section is 0x4, then the second u32 is the max-playcount.
|
||||
/// </remarks>
|
||||
public int[] Limits;
|
||||
public uint[] Limits;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public int ContentIndexSize;
|
||||
public uint ContentIndexSize;
|
||||
|
||||
/// <summary>
|
||||
/// Content Index
|
||||
|
||||
@@ -28,10 +28,15 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// </summary>
|
||||
public byte[] Signature;
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public byte[] Padding1;
|
||||
|
||||
/// <summary>
|
||||
/// Signature Issuer
|
||||
/// </summary>
|
||||
public byte[] SignatureIssuer;
|
||||
public string Issuer;
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
@@ -86,7 +91,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public uint Reserved2;
|
||||
public byte[] Reserved2;
|
||||
|
||||
/// <summary>
|
||||
/// SRL Flag
|
||||
@@ -121,7 +126,7 @@ namespace BurnOutSharp.Models.N3DS
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
public ushort Padding;
|
||||
public byte[] Padding2;
|
||||
|
||||
/// <summary>
|
||||
/// SHA-256 Hash of the Content Info Records
|
||||
|
||||
Reference in New Issue
Block a user