diff --git a/NDecrypt.Core/NDecrypt.Core.csproj b/NDecrypt.Core/NDecrypt.Core.csproj
index 52804c3..675791d 100644
--- a/NDecrypt.Core/NDecrypt.Core.csproj
+++ b/NDecrypt.Core/NDecrypt.Core.csproj
@@ -1,4 +1,4 @@
-
+
@@ -48,7 +48,7 @@
-
+
diff --git a/NDecrypt.N3DS/CIATool.cs b/NDecrypt.N3DS/CIATool.cs
index 96f2633..9c780a2 100644
--- a/NDecrypt.N3DS/CIATool.cs
+++ b/NDecrypt.N3DS/CIATool.cs
@@ -5,6 +5,8 @@ using System.Numerics;
using NDecrypt.Core;
using SabreTools.Models.N3DS;
using static NDecrypt.Core.Helper;
+using CIADeserializer = SabreTools.Serialization.Deserializers.CIA;
+using N3DSDeserializer = SabreTools.Serialization.Deserializers.N3DS;
namespace NDecrypt.N3DS
{
@@ -71,9 +73,9 @@ namespace NDecrypt.N3DS
// Open the read and write on the same file for inplace processing
using var reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
-
+
// Deserialize the CIA information
- var cia = Serializer.ReadCIA(reader);
+ var cia = ReadCIA(reader);
if (cia == null)
{
Console.WriteLine("Error: Not a 3DS CIA!");
@@ -284,7 +286,7 @@ namespace NDecrypt.N3DS
uint mediaUnitSize = 0x200; // mediaUnitSize;
reader.BaseStream.Seek((tableEntry.Offset + ncchHeader.ExeFSOffsetInMediaUnits) * mediaUnitSize, SeekOrigin.Begin);
- var exefsHeader = Serializer.ReadExeFSHeader(reader);
+ var exefsHeader = ReadExeFSHeader(reader);
// If the header failed to read, log and return
if (exefsHeader?.FileHeaders == null)
@@ -659,5 +661,59 @@ namespace NDecrypt.N3DS
}
#endregion
+
+ #region Serialization
+
+ ///
+ /// Read from a stream and get a CIA header, if possible
+ ///
+ /// BinaryReader representing the input stream
+ /// CIA header object, null on error
+ private static CIA? ReadCIA(BinaryReader reader)
+ {
+ try
+ {
+ return CIADeserializer.DeserializeStream(reader.BaseStream);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Read from a stream and get an ExeFS header, if possible
+ ///
+ /// BinaryReader representing the input stream
+ /// ExeFS header object, null on error
+ private static ExeFSHeader? ReadExeFSHeader(BinaryReader reader)
+ {
+ var header = new ExeFSHeader();
+
+ try
+ {
+ header.FileHeaders = new ExeFSFileHeader[10];
+ for (int i = 0; i < 10; i++)
+ {
+ header.FileHeaders[i] = N3DSDeserializer.ParseExeFSFileHeader(reader.BaseStream)!;
+ }
+
+ header.Reserved = reader.ReadBytes(0x20);
+
+ header.FileHashes = new byte[10][];
+ for (int i = 0; i < 10; i++)
+ {
+ header.FileHashes[9 - i] = reader.ReadBytes(0x20);
+ }
+
+ return header;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/NDecrypt.N3DS/NDecrypt.N3DS.csproj b/NDecrypt.N3DS/NDecrypt.N3DS.csproj
index 22ca8cb..416fa9a 100644
--- a/NDecrypt.N3DS/NDecrypt.N3DS.csproj
+++ b/NDecrypt.N3DS/NDecrypt.N3DS.csproj
@@ -40,7 +40,8 @@
-
+
+
diff --git a/NDecrypt.N3DS/Serializer.cs b/NDecrypt.N3DS/Serializer.cs
deleted file mode 100644
index 2bb9ac6..0000000
--- a/NDecrypt.N3DS/Serializer.cs
+++ /dev/null
@@ -1,781 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using SabreTools.Models.N3DS;
-
-namespace NDecrypt.N3DS
-{
- internal static class Serializer
- {
- #region Constants
-
- private const string NCCHMagicNumber = "NCCH";
- private const string NCSDMagicNumber = "NCSD";
-
- #endregion
-
- #region Reading
-
- ///
- /// Read from a stream and get N3DS cart image, if possible
- ///
- /// BinaryReader representing the input stream
- /// True if development cart, false otherwise
- /// N3DS cart image object, null on error
- public static Cart? ReadCart(BinaryReader reader, bool development)
- {
- var cart = new Cart();
-
- try
- {
- cart.Header = ReadNCSDHeader(reader);
- if (cart.Header == null)
- return null;
-
- if (cart.Header.PartitionsFSType == FilesystemType.Normal
- || cart.Header.PartitionsFSType == FilesystemType.None)
- {
- cart.CardInfoHeader = ReadCardInfoHeader(reader);
- if (cart.CardInfoHeader == null)
- return null;
-
- if (development)
- {
- cart.DevelopmentCardInfoHeader = ReadDevelopmentCardInfoHeader(reader);
- if (cart.DevelopmentCardInfoHeader == null)
- return null;
- }
- }
-
- cart.Partitions = new NCCHHeader[8];
- for (int i = 0; i < 8; i++)
- {
- // Check the entry is valid
- var tableEntry = cart.Header.PartitionsTable![i];
- if (tableEntry == null || tableEntry.Offset == 0 || tableEntry.Length == 0)
- continue;
-
- // Seek to the beginning of the NCCH partition
- long offset = tableEntry.Offset * cart.Header.ImageSizeInMediaUnits;
- reader.BaseStream.Seek(offset, SeekOrigin.Begin);
-
- cart.Partitions[i] = ReadNCCHHeader(reader, readSignature: true);
- }
-
- return cart;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get a CIA header, if possible
- ///
- /// BinaryReader representing the input stream
- /// CIA header object, null on error
- public static CIA? ReadCIA(BinaryReader reader)
- {
- var cia = new CIA();
-
- try
- {
- var header = new CIAHeader();
-
- header.HeaderSize = reader.ReadUInt32();
- header.Type = reader.ReadUInt16();
- header.Version = reader.ReadUInt16();
- header.CertificateChainSize = reader.ReadUInt32();
- header.TicketSize = reader.ReadUInt32();
- header.TMDFileSize = reader.ReadUInt32();
- header.MetaSize = reader.ReadUInt32();
- header.ContentSize = reader.ReadUInt64();
- header.ContentIndex = reader.ReadBytes(0x2000);
-
- cia.Header = header;
-
- var certificateChain = new Certificate[3];
- if (reader.BaseStream.Position % 64 != 0)
- reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
-
- certificateChain[0] = ReadCertificate(reader)!; // CA
- certificateChain[1] = ReadCertificate(reader)!; // Ticket
- certificateChain[2] = ReadCertificate(reader)!; // TMD
- if (reader.BaseStream.Position % 64 != 0)
- reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
-
- cia.CertificateChain = certificateChain;
-
- cia.Ticket = ReadTicket(reader, header.TicketSize);
- if (reader.BaseStream.Position % 64 != 0)
- reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
-
- cia.TMDFileData = ReadTitleMetadata(reader, header.TMDFileSize);
- if (reader.BaseStream.Position % 64 != 0)
- reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
-
- long startingPosition = reader.BaseStream.Position;
- var headers = new List();
- while ((ulong)reader.BaseStream.Position < (ulong)startingPosition + header.ContentSize)
- {
- long initPosition = reader.BaseStream.Position;
- var ncchHeader = ReadNCCHHeader(reader, readSignature: true);
- if (ncchHeader == null)
- break;
-
- headers.Add(ncchHeader);
- reader.BaseStream.Seek(initPosition + ncchHeader.ContentSizeInMediaUnits * 0x200, SeekOrigin.Begin);
- }
-
- cia.Partitions = [.. headers];
- if (header.MetaSize > 0)
- cia.MetaData = ReadMetaData(reader);
-
- return cia;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an ExeFS header, if possible
- ///
- /// BinaryReader representing the input stream
- /// ExeFS header object, null on error
- public static ExeFSHeader? ReadExeFSHeader(BinaryReader reader)
- {
- var header = new ExeFSHeader();
-
- try
- {
- header.FileHeaders = new ExeFSFileHeader[10];
- for (int i = 0; i < 10; i++)
- {
- header.FileHeaders[i] = ReadExeFSFileHeader(reader)!;
- }
-
- header.Reserved = reader.ReadBytes(0x20);
-
- header.FileHashes = new byte[10][];
- for (int i = 0; i < 10; i++)
- {
- header.FileHashes[9 - i] = reader.ReadBytes(0x20);
- }
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an NCCH header, if possible
- ///
- /// BinaryReader representing the input stream
- /// True if the RSA signature is read, false otherwise
- /// NCCH header object, null on error
- public static NCCHHeader? ReadNCCHHeader(BinaryReader reader, bool readSignature)
- {
- var header = new NCCHHeader();
-
- try
- {
- if (readSignature)
- header.RSA2048Signature = reader.ReadBytes(0x100);
-
- if (new string(reader.ReadChars(4)) != NCCHMagicNumber)
- return null;
-
- header.ContentSizeInMediaUnits = reader.ReadUInt32();
- header.PartitionId = reader.ReadUInt64();
- header.MakerCode = reader.ReadUInt16();
- header.Version = reader.ReadUInt16();
- header.VerificationHash = reader.ReadUInt32();
- header.ProgramId = reader.ReadBytes(8);
- header.Reserved1 = reader.ReadBytes(0x10);
- header.LogoRegionHash = reader.ReadBytes(0x20);
- byte[] productCodeBytes = reader.ReadBytes(0x10);
- header.ProductCode = Encoding.ASCII.GetString(productCodeBytes);
- header.ExtendedHeaderHash = reader.ReadBytes(0x20);
- header.ExtendedHeaderSizeInBytes = reader.ReadUInt32();
- header.Reserved2 = reader.ReadUInt32();
- header.Flags = ReadNCCHHeaderFlags(reader);
- header.PlainRegionOffsetInMediaUnits = reader.ReadUInt32();
- header.PlainRegionSizeInMediaUnits = reader.ReadUInt32();
- header.LogoRegionOffsetInMediaUnits = reader.ReadUInt32();
- header.LogoRegionSizeInMediaUnits = reader.ReadUInt32();
- header.ExeFSOffsetInMediaUnits = reader.ReadUInt32();
- header.ExeFSSizeInMediaUnits = reader.ReadUInt32();
- header.ExeFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
- header.Reserved3 = reader.ReadUInt32();
- header.RomFSOffsetInMediaUnits = reader.ReadUInt32();
- header.RomFSSizeInMediaUnits = reader.ReadUInt32();
- header.RomFSHashRegionSizeInMediaUnits = reader.ReadUInt32();
- header.Reserved4 = reader.ReadUInt32();
- header.ExeFSSuperblockHash = reader.ReadBytes(0x20);
- header.RomFSSuperblockHash = reader.ReadBytes(0x20);
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an CardInfo header, if possible
- ///
- /// BinaryReader representing the input stream
- /// CardInfo header object, null on error
- private static CardInfoHeader? ReadCardInfoHeader(BinaryReader reader)
- {
- var header = new CardInfoHeader();
-
- try
- {
- header.WritableAddressMediaUnits = reader.ReadUInt32();
- header.CardInfoBitmask = reader.ReadUInt32();
- header.Reserved3 = reader.ReadBytes(0x108);
- header.TitleVersion = reader.ReadUInt16();
- header.CardRevision = reader.ReadUInt16();
- header.Reserved4 = reader.ReadBytes(0xCD6);
- header.InitialData = ReadInitialData(reader);
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get certificate, if possible
- ///
- /// BinaryReader representing the input stream
- /// Certificate object, null on error
- private static Certificate? ReadCertificate(BinaryReader reader)
- {
- var ct = new Certificate();
-
- try
- {
- ct.SignatureType = (SignatureType)reader.ReadUInt32();
- switch (ct.SignatureType)
- {
- case SignatureType.RSA_4096_SHA1:
- case SignatureType.RSA_4096_SHA256:
- ct.SignatureSize = 0x200;
- ct.PaddingSize = 0x3C;
- break;
- case SignatureType.RSA_2048_SHA1:
- case SignatureType.RSA_2048_SHA256:
- ct.SignatureSize = 0x100;
- ct.PaddingSize = 0x3C;
- break;
- case SignatureType.ECDSA_SHA1:
- case SignatureType.ECDSA_SHA256:
- ct.SignatureSize = 0x03C;
- ct.PaddingSize = 0x40;
- break;
- default:
- return null;
- }
-
- ct.Signature = reader.ReadBytes(ct.SignatureSize);
- reader.ReadBytes(ct.PaddingSize); // Padding
- byte[] issuerBytes = reader.ReadBytes(0x40);
- ct.Issuer = Encoding.ASCII.GetString(issuerBytes);
- ct.KeyType = (PublicKeyType)reader.ReadUInt32();
- byte[] nameBytes = reader.ReadBytes(0x40);
- ct.Name = Encoding.ASCII.GetString(nameBytes);
- ct.ExpirationTime = reader.ReadUInt32();
-
- switch (ct.KeyType)
- {
- case PublicKeyType.RSA_4096:
- ct.RSAModulus = reader.ReadBytes(0x200);
- ct.RSAPublicExponent = reader.ReadUInt32();
- ct.RSAPadding = reader.ReadBytes(0x34);
- break;
- case PublicKeyType.RSA_2048:
- ct.RSAModulus = reader.ReadBytes(0x100);
- ct.RSAPublicExponent = reader.ReadUInt32();
- ct.RSAPadding = reader.ReadBytes(0x34);
- break;
- case PublicKeyType.EllipticCurve:
- ct.ECCPublicKey = reader.ReadBytes(0x3C);
- ct.ECCPadding = reader.ReadBytes(0x3C);
- break;
- default:
- return null;
- }
-
- return ct;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get content chunk record, if possible
- ///
- /// BinaryReader representing the input stream
- /// Content chunk record object, null on error
- private static ContentChunkRecord? ReadContentChunkRecord(BinaryReader reader)
- {
- var ccr = new ContentChunkRecord();
-
- try
- {
- ccr.ContentId = reader.ReadUInt32();
- ccr.ContentIndex = (ContentIndex)reader.ReadUInt16();
- ccr.ContentType = (TMDContentType)reader.ReadUInt16();
- ccr.ContentSize = reader.ReadUInt64();
- ccr.SHA256Hash = reader.ReadBytes(0x20);
- return ccr;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get content info record, if possible
- ///
- /// BinaryReader representing the input stream
- /// Content info record object, null on error
- private static ContentInfoRecord? ReadContentInfoRecord(BinaryReader reader)
- {
- var cir = new ContentInfoRecord();
-
- try
- {
- cir.ContentIndexOffset = reader.ReadUInt16();
- cir.ContentCommandCount = reader.ReadUInt16();
- cir.UnhashedContentRecordsSHA256Hash = reader.ReadBytes(0x20);
- return cir;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an DevelopmentCardInfo header, if possible
- ///
- /// BinaryReader representing the input stream
- /// DevelopmentCardInfo object, null on error
- private static DevelopmentCardInfoHeader? ReadDevelopmentCardInfoHeader(BinaryReader reader)
- {
- var header = new DevelopmentCardInfoHeader();
-
- try
- {
- header.CardDeviceReserved1 = reader.ReadBytes(0x200);
- header.TitleKey = reader.ReadBytes(0x10);
- header.CardDeviceReserved2 = reader.ReadBytes(0xF0);
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an ExeFS file header, if possible
- ///
- /// BinaryReader representing the input stream
- /// ExeFS file header object, null on error
- private static ExeFSFileHeader? ReadExeFSFileHeader(BinaryReader reader)
- {
- var header = new ExeFSFileHeader();
-
- try
- {
- byte[] fileNameBytes = reader.ReadBytes(8);
- header.FileName = Encoding.ASCII.GetString(fileNameBytes);
- header.FileOffset = reader.ReadUInt32();
- header.FileSize = reader.ReadUInt32();
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get inital data, if possible
- ///
- /// BinaryReader representing the input stream
- /// Initial data object, null on error
- private static InitialData? ReadInitialData(BinaryReader reader)
- {
- var id = new InitialData();
-
- try
- {
- id.CardSeedKeyY = reader.ReadBytes(0x10);
- id.EncryptedCardSeed = reader.ReadBytes(0x10);
- id.CardSeedAESMAC = reader.ReadBytes(0x10);
- id.CardSeedNonce = reader.ReadBytes(0x0C);
- id.Reserved = reader.ReadBytes(0xC4);
- id.BackupHeader = ReadNCCHHeader(reader, readSignature: false);
-
- return id;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get the Metafile data, if possible
- ///
- /// BinaryReader representing the input stream
- /// Metafile data object, null on error
- private static MetaData? ReadMetaData(BinaryReader reader)
- {
- var metaData = new MetaData();
-
- try
- {
- metaData.TitleIDDependencyList = reader.ReadBytes(0x180);
- metaData.Reserved1 = reader.ReadBytes(0x180);
- metaData.CoreVersion = reader.ReadUInt32();
- metaData.Reserved2 = reader.ReadBytes(0xFC);
- metaData.IconData = reader.ReadBytes(0x36C0);
-
- return metaData;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an NCCH header flags, if possible
- ///
- /// BinaryReader representing the input stream
- /// NCCH header flags object, null on error
- private static NCCHHeaderFlags? ReadNCCHHeaderFlags(BinaryReader reader)
- {
- var flags = new NCCHHeaderFlags();
-
- try
- {
- flags.Reserved0 = reader.ReadByte();
- flags.Reserved1 = reader.ReadByte();
- flags.Reserved2 = reader.ReadByte();
- flags.CryptoMethod = (CryptoMethod)reader.ReadByte();
- flags.ContentPlatform = (ContentPlatform)reader.ReadByte();
- flags.MediaPlatformIndex = (ContentType)reader.ReadByte();
- flags.ContentUnitSize = reader.ReadByte();
- flags.BitMasks = (BitMasks)reader.ReadByte();
- return flags;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an NCSD header, if possible
- ///
- /// BinaryReader representing the input stream
- /// NCSD header object, null on error
- private static NCSDHeader? ReadNCSDHeader(BinaryReader reader)
- {
- var header = new NCSDHeader();
-
- try
- {
- header.RSA2048Signature = reader.ReadBytes(0x100);
-
- if (new string(reader.ReadChars(4)) != NCSDMagicNumber)
- return null;
-
- header.ImageSizeInMediaUnits = reader.ReadUInt32();
- header.MediaId = reader.ReadBytes(8);
- header.PartitionsFSType = (FilesystemType)reader.ReadUInt64();
- header.PartitionsCryptType = reader.ReadBytes(8);
-
- header.PartitionsTable = new PartitionTableEntry[8];
- for (int i = 0; i < 8; i++)
- {
- header.PartitionsTable[i] = ReadPartitionTableEntry(reader)!;
- }
-
- if (header.PartitionsFSType == FilesystemType.Normal
- || header.PartitionsFSType == FilesystemType.None)
- {
- header.ExheaderHash = reader.ReadBytes(0x20);
- header.AdditionalHeaderSize = reader.ReadUInt32();
- header.SectorZeroOffset = reader.ReadUInt32();
- header.PartitionFlags = reader.ReadBytes(8);
-
- header.PartitionIdTable = new ulong[8];
- for (int i = 0; i < 8; i++)
- {
- header.PartitionIdTable[i] = reader.ReadUInt64();
- }
-
- header.Reserved1 = reader.ReadBytes(0x20);
- header.Reserved2 = reader.ReadBytes(0xE);
- header.FirmUpdateByte1 = reader.ReadByte();
- header.FirmUpdateByte2 = reader.ReadByte();
- }
- else if (header.PartitionsFSType == FilesystemType.FIRM)
- {
- header.Unknown = reader.ReadBytes(0x5E);
- header.EncryptedMBR = reader.ReadBytes(0x42);
- }
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get partition table entry, if possible
- ///
- /// BinaryReader representing the input stream
- /// Partition table entry object, null on error
- private static PartitionTableEntry? ReadPartitionTableEntry(BinaryReader reader)
- {
- var entry = new PartitionTableEntry();
-
- try
- {
- entry.Offset = reader.ReadUInt32();
- entry.Length = reader.ReadUInt32();
- return entry;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get ticket, if possible
- ///
- /// BinaryReader representing the input stream
- /// Ticket size from the header
- /// Ticket object, null on error
- private static Ticket? ReadTicket(BinaryReader reader, uint ticketSize)
- {
- var tk = new Ticket();
-
- try
- {
- long startingPosition = reader.BaseStream.Position;
-
- tk.SignatureType = (SignatureType)reader.ReadUInt32();
- switch (tk.SignatureType)
- {
- case SignatureType.RSA_4096_SHA1:
- case SignatureType.RSA_4096_SHA256:
- tk.SignatureSize = 0x200;
- tk.PaddingSize = 0x3C;
- break;
- case SignatureType.RSA_2048_SHA1:
- case SignatureType.RSA_2048_SHA256:
- tk.SignatureSize = 0x100;
- tk.PaddingSize = 0x3C;
- break;
- case SignatureType.ECDSA_SHA1:
- case SignatureType.ECDSA_SHA256:
- tk.SignatureSize = 0x03C;
- tk.PaddingSize = 0x40;
- break;
- default:
- return null;
- }
-
- tk.Signature = reader.ReadBytes(tk.SignatureSize);
- reader.ReadBytes(tk.PaddingSize); // Padding
- byte[] issuerBytes = reader.ReadBytes(0x40);
- tk.Issuer = Encoding.ASCII.GetString(issuerBytes);
- tk.ECCPublicKey = reader.ReadBytes(0x3C);
- tk.Version = reader.ReadByte();
- tk.CaCrlVersion = reader.ReadByte();
- tk.SignerCrlVersion = reader.ReadByte();
- tk.TitleKey = reader.ReadBytes(0x10);
- tk.Reserved1 = reader.ReadByte();
- tk.TicketID = reader.ReadUInt64();
- tk.ConsoleID = reader.ReadUInt32();
- tk.TitleID = reader.ReadUInt64();
- tk.Reserved2 = reader.ReadBytes(2);
- tk.TicketTitleVersion = reader.ReadUInt16();
- tk.Reserved3 = reader.ReadBytes(8);
- tk.LicenseType = reader.ReadByte();
- tk.CommonKeyYIndex = reader.ReadByte();
- tk.Reserved4 = reader.ReadBytes(0x2A);
- tk.eShopAccountID = reader.ReadUInt32();
- tk.Reserved5 = reader.ReadByte();
- tk.Audit = reader.ReadByte();
- tk.Reserved6 = reader.ReadBytes(0x42);
-
- tk.Limits = new uint[0x10];
- for (int i = 0; i < 0x10; i++)
- {
- tk.Limits[i] = reader.ReadUInt32();
- }
-
- reader.ReadBytes(4); // Seek to size in Content Index
- tk.ContentIndexSize = BitConverter.ToUInt32(reader.ReadBytes(4).Reverse().ToArray(), 0);
- reader.BaseStream.Seek(-8, SeekOrigin.Current);
- tk.ContentIndex = reader.ReadBytes((int)tk.ContentIndexSize);
- if (reader.BaseStream.Position % 64 != 0)
- reader.BaseStream.Seek(64 - (reader.BaseStream.Position % 64), SeekOrigin.Current);
-
- if (ticketSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
- {
- tk.CertificateChain = new Certificate[2];
- tk.CertificateChain[0] = ReadCertificate(reader)!; // Ticket
- tk.CertificateChain[1] = ReadCertificate(reader)!; // CA
- }
-
- return tk;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get ticket metadata, if possible
- ///
- /// BinaryReader representing the input stream
- /// Metadata size from the header
- /// Title metadata object, null on error
- private static TitleMetadata? ReadTitleMetadata(BinaryReader reader, uint metadataSize)
- {
- var tm = new TitleMetadata();
-
- try
- {
- long startingPosition = reader.BaseStream.Position;
-
- tm.SignatureType = (SignatureType)reader.ReadUInt32();
- switch (tm.SignatureType)
- {
- case SignatureType.RSA_4096_SHA1:
- case SignatureType.RSA_4096_SHA256:
- tm.SignatureSize = 0x200;
- tm.PaddingSize = 0x3C;
- break;
- case SignatureType.RSA_2048_SHA1:
- case SignatureType.RSA_2048_SHA256:
- tm.SignatureSize = 0x100;
- tm.PaddingSize = 0x3C;
- break;
- case SignatureType.ECDSA_SHA1:
- case SignatureType.ECDSA_SHA256:
- tm.SignatureSize = 0x03C;
- tm.PaddingSize = 0x40;
- break;
- }
-
- tm.Signature = reader.ReadBytes(tm.SignatureSize);
- tm.Padding1 = reader.ReadBytes(tm.PaddingSize);
- byte[] issuerBytes = reader.ReadBytes(0x40);
- tm.Issuer = Encoding.ASCII.GetString(issuerBytes);
- tm.Version = reader.ReadByte();
- tm.CaCrlVersion = reader.ReadByte();
- tm.SignerCrlVersion = reader.ReadByte();
- tm.Reserved1 = reader.ReadByte();
- tm.SystemVersion = reader.ReadUInt64();
- tm.TitleID = reader.ReadUInt64();
- tm.TitleType = reader.ReadUInt32();
- tm.GroupID = reader.ReadUInt16();
- tm.SaveDataSize = reader.ReadUInt32();
- tm.SRLPrivateSaveDataSize = reader.ReadUInt32();
- tm.Reserved2 = reader.ReadBytes(4);
- tm.SRLFlag = reader.ReadByte();
- tm.Reserved3 = reader.ReadBytes(0x31);
- tm.AccessRights = reader.ReadUInt32();
- tm.TitleVersion = reader.ReadUInt16();
- tm.ContentCount = BitConverter.ToUInt16(reader.ReadBytes(2).Reverse().ToArray(), 0);
- tm.BootContent = reader.ReadUInt16();
- tm.Padding2 = reader.ReadBytes(2);
- tm.SHA256HashContentInfoRecords = reader.ReadBytes(0x20);
-
- tm.ContentInfoRecords = new ContentInfoRecord[64];
- for (int i = 0; i < 64; i++)
- {
- tm.ContentInfoRecords[i] = ReadContentInfoRecord(reader)!;
- }
-
- tm.ContentChunkRecords = new ContentChunkRecord[tm.ContentCount];
- for (int i = 0; i < tm.ContentCount; i++)
- {
- tm.ContentChunkRecords[i] = ReadContentChunkRecord(reader)!;
- }
-
- if (metadataSize > (reader.BaseStream.Position - startingPosition) + (2 * 0x200))
- {
- tm.CertificateChain = new Certificate[2];
- tm.CertificateChain[0] = ReadCertificate(reader)!; // TMD
- tm.CertificateChain[1] = ReadCertificate(reader)!; // CA
- }
-
- return tm;
- }
- catch
- {
- return null;
- }
- }
-
- #endregion
-
- #region Writing
-
- ///
- /// Write NCCH header flags to stream, if possible
- ///
- /// BinaryWriter representing the output stream
- public static void Write(NCCHHeaderFlags flags, BinaryWriter writer)
- {
- try
- {
- writer.Write(flags.Reserved0);
- writer.Write(flags.Reserved1);
- writer.Write(flags.Reserved2);
- writer.Write((byte)flags.CryptoMethod);
- writer.Write((byte)flags.ContentPlatform);
- writer.Write((byte)flags.MediaPlatformIndex);
- writer.Write(flags.ContentUnitSize);
- writer.Write((byte)flags.BitMasks);
-
- }
- catch { }
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/NDecrypt.N3DS/ThreeDSTool.cs b/NDecrypt.N3DS/ThreeDSTool.cs
index 8a78112..62b9e7e 100644
--- a/NDecrypt.N3DS/ThreeDSTool.cs
+++ b/NDecrypt.N3DS/ThreeDSTool.cs
@@ -5,6 +5,7 @@ using System.Numerics;
using NDecrypt.Core;
using SabreTools.Models.N3DS;
using static NDecrypt.Core.Helper;
+using N3DSDeserializer = SabreTools.Serialization.Deserializers.N3DS;
namespace NDecrypt.N3DS
{
@@ -72,7 +73,7 @@ namespace NDecrypt.N3DS
using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
// Deserialize the cart information
- var cart = Serializer.ReadCart(reader, decryptArgs.Development);
+ var cart = ReadCart(reader);
if (cart?.Header == null || cart?.CardInfoHeader?.InitialData?.BackupHeader == null)
{
Console.WriteLine("Error: Not a 3DS cart image!");
@@ -303,7 +304,7 @@ namespace NDecrypt.N3DS
uint exeFsOffset = (partitionOffsetMU + exeFsOffsetMU + 1) * cart.MediaUnitSize();
reader.BaseStream.Seek(exeFsHeaderOffset, SeekOrigin.Begin);
- var exefsHeader = Serializer.ReadExeFSHeader(reader);
+ var exefsHeader = ReadExeFSHeader(reader);
// If the header failed to read, log and return
if (exefsHeader == null)
@@ -744,5 +745,88 @@ namespace NDecrypt.N3DS
}
#endregion
+
+ #region Serialization
+
+ ///
+ /// Read from a stream and get N3DS cart image, if possible
+ ///
+ /// BinaryReader representing the input stream
+ /// N3DS cart image object, null on error
+ private static Cart? ReadCart(BinaryReader reader)
+ {
+ var cart = new Cart();
+
+ try
+ {
+ cart.Header = N3DSDeserializer.ParseNCSDHeader(reader.BaseStream);
+ if (cart.Header == null)
+ return null;
+
+ if (cart.Header.PartitionsFSType == FilesystemType.Normal
+ || cart.Header.PartitionsFSType == FilesystemType.None)
+ {
+ cart.CardInfoHeader = N3DSDeserializer.ParseCardInfoHeader(reader.BaseStream);
+ if (cart.CardInfoHeader == null)
+ return null;
+ }
+
+ cart.Partitions = new NCCHHeader[8];
+ for (int i = 0; i < 8; i++)
+ {
+ // Check the entry is valid
+ var tableEntry = cart.Header.PartitionsTable![i];
+ if (tableEntry == null || tableEntry.Offset == 0 || tableEntry.Length == 0)
+ continue;
+
+ // Seek to the beginning of the NCCH partition
+ long offset = tableEntry.Offset * cart.Header.ImageSizeInMediaUnits;
+ reader.BaseStream.Seek(offset, SeekOrigin.Begin);
+
+ cart.Partitions[i] = N3DSDeserializer.ParseNCCHHeader(reader.BaseStream, skipSignature: false);
+ }
+
+ return cart;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Read from a stream and get an ExeFS header, if possible
+ ///
+ /// BinaryReader representing the input stream
+ /// ExeFS header object, null on error
+ private static ExeFSHeader? ReadExeFSHeader(BinaryReader reader)
+ {
+ var header = new ExeFSHeader();
+
+ try
+ {
+ header.FileHeaders = new ExeFSFileHeader[10];
+ for (int i = 0; i < 10; i++)
+ {
+ header.FileHeaders[i] = N3DSDeserializer.ParseExeFSFileHeader(reader.BaseStream)!;
+ }
+
+ header.Reserved = reader.ReadBytes(0x20);
+
+ header.FileHashes = new byte[10][];
+ for (int i = 0; i < 10; i++)
+ {
+ header.FileHashes[9 - i] = reader.ReadBytes(0x20);
+ }
+
+ return header;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ #endregion
}
}
diff --git a/NDecrypt.Nitro/DSTool.cs b/NDecrypt.Nitro/DSTool.cs
index 68fca22..2307b16 100644
--- a/NDecrypt.Nitro/DSTool.cs
+++ b/NDecrypt.Nitro/DSTool.cs
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using NDecrypt.Core;
using SabreTools.Models.Nitro;
+using NitroDeserializer = SabreTools.Serialization.Deserializers.Nitro;
namespace NDecrypt.Nitro
{
@@ -43,7 +44,7 @@ namespace NDecrypt.Nitro
using var writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
// Deserialize the cart information
- Cart? cart = Serializer.ReadCart(reader);
+ Cart? cart = ReadCart(reader);
if (cart == null)
{
Console.WriteLine("Error: Not a DS or DSi Rom!");
@@ -360,5 +361,26 @@ namespace NDecrypt.Nitro
_cardHash[i + 18 + 1] = tmp2;
}
}
+
+ #region Serialization
+
+ ///
+ /// Read from a stream and get an NDS/NDSi Cart, if possible
+ ///
+ /// BinaryReader representing the input stream
+ /// NDS/NDSi Cart object, null on error
+ private static Cart? ReadCart(BinaryReader reader)
+ {
+ try
+ {
+ return NitroDeserializer.DeserializeStream(reader.BaseStream);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ #endregion
}
}
diff --git a/NDecrypt.Nitro/NDecrypt.Nitro.csproj b/NDecrypt.Nitro/NDecrypt.Nitro.csproj
index 13bda8d..3024b21 100644
--- a/NDecrypt.Nitro/NDecrypt.Nitro.csproj
+++ b/NDecrypt.Nitro/NDecrypt.Nitro.csproj
@@ -40,7 +40,8 @@
-
+
+
\ No newline at end of file
diff --git a/NDecrypt.Nitro/Serializer.cs b/NDecrypt.Nitro/Serializer.cs
deleted file mode 100644
index 7100277..0000000
--- a/NDecrypt.Nitro/Serializer.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System.IO;
-using System.Text;
-using SabreTools.Models.Nitro;
-
-namespace NDecrypt.Nitro
-{
- internal static class Serializer
- {
- ///
- /// Read from a stream and get an NDS/NDSi Cart, if possible
- ///
- /// BinaryReader representing the input stream
- /// NDS/NDSi Cart object, null on error
- public static Cart? ReadCart(BinaryReader reader)
- {
- var cart = new Cart();
-
- try
- {
- cart.CommonHeader = ReadCommonHeader(reader);
- if (cart.CommonHeader == null)
- return null;
-
- // If we have a DSi compatible title
- if (cart.CommonHeader.UnitCode == Unitcode.NDSPlusDSi
- || cart.CommonHeader.UnitCode == Unitcode.DSi)
- {
- cart.ExtendedDSiHeader = ReadExtendedDSiHeader(reader);
- if (cart.ExtendedDSiHeader == null)
- return null;
- }
-
- return cart;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get a common header, if possible
- ///
- /// BinaryReader representing the input stream
- /// Common header object, null on error
- private static CommonHeader? ReadCommonHeader(BinaryReader reader)
- {
- var header = new CommonHeader();
-
- try
- {
- byte[] gameTitleBytes = reader.ReadBytes(0x0C);
- header.GameTitle = Encoding.ASCII.GetString(gameTitleBytes);
- header.GameCode = reader.ReadUInt32();
- byte[] makerCodeBytes = reader.ReadBytes(2);
- header.MakerCode = Encoding.ASCII.GetString(makerCodeBytes);
- header.UnitCode = (Unitcode)reader.ReadByte();
- header.EncryptionSeedSelect = reader.ReadByte();
- header.DeviceCapacity = reader.ReadByte();
- header.Reserved1 = reader.ReadBytes(7);
- header.GameRevision = reader.ReadUInt16();
- header.RomVersion = reader.ReadByte();
- header.InternalFlags = reader.ReadByte();
- header.ARM9RomOffset = reader.ReadUInt32();
- header.ARM9EntryAddress = reader.ReadUInt32();
- header.ARM9LoadAddress = reader.ReadUInt32();
- header.ARM9Size = reader.ReadUInt32();
- header.ARM7RomOffset = reader.ReadUInt32();
- header.ARM7EntryAddress = reader.ReadUInt32();
- header.ARM7LoadAddress = reader.ReadUInt32();
- header.ARM7Size = reader.ReadUInt32();
- header.FileNameTableOffset = reader.ReadUInt32();
- header.FileNameTableLength = reader.ReadUInt32();
- header.FileAllocationTableOffset = reader.ReadUInt32();
- header.FileAllocationTableLength = reader.ReadUInt32();
- header.ARM9OverlayOffset = reader.ReadUInt32();
- header.ARM9OverlayLength = reader.ReadUInt32();
- header.ARM7OverlayOffset = reader.ReadUInt32();
- header.ARM7OverlayLength = reader.ReadUInt32();
- header.SecureDisable = reader.ReadBytes(8);
- header.NTRRegionRomSize = reader.ReadUInt32();
- header.HeaderSize = reader.ReadUInt32();
- header.Reserved2 = reader.ReadBytes(56);
- header.NintendoLogo = reader.ReadBytes(156);
- header.NintendoLogoCRC = reader.ReadUInt16();
- header.DebuggerReserved = reader.ReadBytes(0x20);
-
- return header;
- }
- catch
- {
- return null;
- }
- }
-
- ///
- /// Read from a stream and get an extended DSi header, if possible
- ///
- /// BinaryReader representing the input stream
- /// Extended DSi header object, null on error
- private static ExtendedDSiHeader? ReadExtendedDSiHeader(BinaryReader reader)
- {
- var header = new ExtendedDSiHeader();
-
- try
- {
- header.GlobalMBK15Settings = new uint[5];
- for (int i = 0; i < 5; i++)
- {
- header.GlobalMBK15Settings[i] = reader.ReadUInt32();
- }
- header.LocalMBK68SettingsARM9 = new uint[3];
- for (int i = 0; i < 3; i++)
- {
- header.LocalMBK68SettingsARM9[i] = reader.ReadUInt32();
- }
- header.LocalMBK68SettingsARM7 = new uint[3];
- for (int i = 0; i < 3; i++)
- {
- header.LocalMBK68SettingsARM7[i] = reader.ReadUInt32();
- }
- header.GlobalMBK9Setting = reader.ReadUInt32();
- header.RegionFlags = reader.ReadUInt32();
- header.AccessControl = reader.ReadUInt32();
- header.ARM7SCFGEXTMask = reader.ReadUInt32();
- header.ReservedFlags = reader.ReadUInt32();
- header.ARM9iRomOffset = reader.ReadUInt32();
- header.Reserved3 = reader.ReadUInt32();
- header.ARM9iLoadAddress = reader.ReadUInt32();
- header.ARM9iSize = reader.ReadUInt32();
- header.ARM7iRomOffset = reader.ReadUInt32();
- header.Reserved4 = reader.ReadUInt32();
- header.ARM7iLoadAddress = reader.ReadUInt32();
- header.ARM7iSize = reader.ReadUInt32();
- header.DigestNTRRegionOffset = reader.ReadUInt32();
- header.DigestNTRRegionLength = reader.ReadUInt32();
- header.DigestTWLRegionOffset = reader.ReadUInt32();
- header.DigestTWLRegionLength = reader.ReadUInt32();
- header.DigestSectorHashtableRegionOffset = reader.ReadUInt32();
- header.DigestSectorHashtableRegionLength = reader.ReadUInt32();
- header.DigestBlockHashtableRegionOffset = reader.ReadUInt32();
- header.DigestBlockHashtableRegionLength = reader.ReadUInt32();
- header.DigestSectorSize = reader.ReadUInt32();
- header.DigestBlockSectorCount = reader.ReadUInt32();
- header.IconBannerSize = reader.ReadUInt32();
- header.Unknown1 = reader.ReadUInt32();
- header.ModcryptArea1Offset = reader.ReadUInt32();
- header.ModcryptArea1Size = reader.ReadUInt32();
- header.ModcryptArea2Offset = reader.ReadUInt32();
- header.ModcryptArea2Size = reader.ReadUInt32();
- header.TitleID = reader.ReadBytes(8);
- header.DSiWarePublicSavSize = reader.ReadUInt32();
- header.DSiWarePrivateSavSize = reader.ReadUInt32();
- header.ReservedZero = reader.ReadBytes(176);
- header.Unknown2 = reader.ReadBytes(0x10);
- header.ARM9WithSecureAreaSHA1HMACHash = reader.ReadBytes(20);
- header.ARM7SHA1HMACHash = reader.ReadBytes(20);
- header.DigestMasterSHA1HMACHash = reader.ReadBytes(20);
- header.BannerSHA1HMACHash = reader.ReadBytes(20);
- header.ARM9iDecryptedSHA1HMACHash = reader.ReadBytes(20);
- header.ARM7iDecryptedSHA1HMACHash = reader.ReadBytes(20);
- header.Reserved5 = reader.ReadBytes(40);
- header.ARM9NoSecureAreaSHA1HMACHash = reader.ReadBytes(20);
- header.Reserved6 = reader.ReadBytes(2636);
- header.ReservedAndUnchecked = reader.ReadBytes(0x180);
- header.RSASignature = reader.ReadBytes(0x80);
-
- return header;
- }
- catch
- {
- return null;
- }
- }
- }
-}