From ef5a01edf76ad704c3871b8da3c855f1f109503c Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 22 Jul 2021 22:36:05 -0700 Subject: [PATCH] Add CIA header (nw) --- NDecrypt/N3DS/CIATool.cs | 80 ++++++++++++++++++++ NDecrypt/N3DS/Headers/CIAHeader.cs | 114 +++++++++++++++++++++++++++++ NDecrypt/N3DS/Headers/MetaFile.cs | 57 +++++++++++++++ NDecrypt/Program.cs | 7 ++ 4 files changed, 258 insertions(+) create mode 100644 NDecrypt/N3DS/CIATool.cs create mode 100644 NDecrypt/N3DS/Headers/CIAHeader.cs create mode 100644 NDecrypt/N3DS/Headers/MetaFile.cs diff --git a/NDecrypt/N3DS/CIATool.cs b/NDecrypt/N3DS/CIATool.cs new file mode 100644 index 0000000..61e6f2d --- /dev/null +++ b/NDecrypt/N3DS/CIATool.cs @@ -0,0 +1,80 @@ +using System; +using System.IO; +using NDecrypt.N3DS.Headers; + +namespace NDecrypt.N3DS +{ + // https://www.3dbrew.org/wiki/CIA + internal class CIATool : ITool + { + /// + /// Name of the input CIA file + /// + private readonly string filename; + + /// + /// Flag to detrmine if development keys should be used + /// + private readonly bool development; + + /// + /// Flag to determine if encrypting or decrypting + /// + private readonly bool encrypt; + + /// + /// Flag to determine if forcing operations + /// + private readonly bool force; + + public CIATool(string filename, bool development, bool encrypt, bool force) + { + this.filename = filename; + this.development = development; + this.encrypt = encrypt; + this.force = force; + } + + #region Common Methods + + /// + /// Process an input file given the input values + /// + public bool ProcessFile() + { + // Ensure the constants are all set + Constants.Init(); + if (Constants.IsReady != true) + { + Console.WriteLine("Could not read keys from keys.bin. Please make sure the file exists and try again."); + return false; + } + + try + { + // Open the read and write on the same file for inplace processing + using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + using (BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))) + { + CIAHeader header = CIAHeader.Read(reader); + if (header == null) + { + Console.WriteLine("Error: Not a 3DS Rom!"); + return false; + } + + // TODO: Implement CIA encrypt/decrypt + return false; + } + } + catch + { + Console.WriteLine($"An error has occurred. {filename} may be corrupted if it was partially processed."); + Console.WriteLine("Please check that the file was a valid 3DS or New 3DS file and try again."); + return false; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/NDecrypt/N3DS/Headers/CIAHeader.cs b/NDecrypt/N3DS/Headers/CIAHeader.cs new file mode 100644 index 0000000..748585a --- /dev/null +++ b/NDecrypt/N3DS/Headers/CIAHeader.cs @@ -0,0 +1,114 @@ +using System.IO; + +namespace NDecrypt.N3DS.Headers +{ + internal class CIAHeader + { + /// + /// Archive header size, usually 0x2020 bytes + /// + public uint HeaderSize { get; private set; } + + /// + /// Type + /// + public ushort Type { get; private set; } + + /// + /// Version + /// + public ushort Version { get; private set; } + + /// + /// Certificate chain size + /// + public int CertificateChainSize { get; private set; } + + /// + /// Ticket size + /// + public int TicketSize { get; private set; } + + /// + /// TMD file size + /// + public int TMDFileSize { get; private set; } + + /// + /// Meta size (0 if no Meta data is present) + /// + public int MetaSize { get; private set; } + + /// + /// Content size + /// + public long ContentSize { get; private set; } + + /// + /// Content Index + /// + public byte[] ContentIndex { get; private set; } + + /// + /// Certificate chain + /// + public byte[] CertificateChain { get; set; } + + /// + /// Ticket + /// + public byte[] Ticket { get; set; } + + /// + /// TMD file data + /// + public byte[] TMDFileData { get; set; } + + /// + /// Content file data + /// + public byte[] ContentFileData { get; set; } + + /// + /// Meta file data (Not a necessary component) + /// + public MetaFile MetaFileData { get; set; } + + /// + /// Read from a stream and get a CIA header, if possible + /// + /// BinaryReader representing the input stream + /// CIA header object, null on error + public static CIAHeader Read(BinaryReader reader) + { + CIAHeader header = new CIAHeader(); + + try + { + header.HeaderSize = reader.ReadUInt32(); + header.Type = reader.ReadUInt16(); + header.Version = reader.ReadUInt16(); + header.CertificateChainSize = reader.ReadInt32(); + header.TicketSize = reader.ReadInt32(); + header.TMDFileSize = reader.ReadInt32(); + header.MetaSize = reader.ReadInt32(); + header.ContentSize = reader.ReadInt64(); + header.ContentIndex = reader.ReadBytes(0x2000); + + header.CertificateChain = reader.ReadBytes(header.CertificateChainSize); + header.Ticket = reader.ReadBytes(header.TicketSize); + header.TMDFileData = reader.ReadBytes(header.TMDFileSize); + header.ContentFileData = reader.ReadBytes((int)header.ContentSize); + + if (header.MetaSize > 0) + header.MetaFileData = MetaFile.Read(reader); + + return header; + } + catch + { + return null; + } + } + } +} \ No newline at end of file diff --git a/NDecrypt/N3DS/Headers/MetaFile.cs b/NDecrypt/N3DS/Headers/MetaFile.cs new file mode 100644 index 0000000..8625550 --- /dev/null +++ b/NDecrypt/N3DS/Headers/MetaFile.cs @@ -0,0 +1,57 @@ +using System.IO; + +namespace NDecrypt.N3DS.Headers +{ + internal class MetaFile + { + /// + /// Title ID dependency list - Taken from the application's ExHeader + /// + public byte[] TitleIDDependencyList { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved1 { get; private set; } + + /// + /// Core Version + /// + public uint CoreVersion { get; private set; } + + /// + /// Reserved + /// + public byte[] Reserved2 { get; private set; } + + /// + /// Icon Data(.ICN) - Taken from the application's ExeFS + /// + public byte[] IconData { get; private set; } + + /// + /// Read from a stream and get the Metafile data, if possible + /// + /// BinaryReader representing the input stream + /// Metafile data object, null on error + public static MetaFile Read(BinaryReader reader) + { + MetaFile metaFile = new MetaFile(); + + try + { + metaFile.TitleIDDependencyList = reader.ReadBytes(0x180); + metaFile.Reserved1 = reader.ReadBytes(0x180); + metaFile.CoreVersion = reader.ReadUInt32(); + metaFile.Reserved2 = reader.ReadBytes(0xFC); + metaFile.IconData = reader.ReadBytes(0x36C0); + + return metaFile; + } + catch + { + return null; + } + } + } +} \ No newline at end of file diff --git a/NDecrypt/Program.cs b/NDecrypt/Program.cs index eabb6b2..ec847c2 100644 --- a/NDecrypt/Program.cs +++ b/NDecrypt/Program.cs @@ -18,6 +18,7 @@ namespace NDecrypt iQueDS, N3DS, iQue3DS, + N3DSCIA, } public static void Main(string[] args) @@ -139,6 +140,9 @@ More than one path can be specified at a time."); case FileType.N3DS: Console.WriteLine("File recognized as Nintendo 3DS"); return new ThreeDSTool(filename, development, encrypt, force); + // case FileType.N3DSCIA: + // Console.WriteLine("File recognized as Nintendo 3DS"); + // return new CIATool(filename, development, encrypt, force); case FileType.NULL: default: Console.WriteLine($"Unrecognized file format for {filename}. Expected *.nds, *.srl, *.dsi, *.3ds"); @@ -165,6 +169,9 @@ More than one path can be specified at a time."); else if (filename.EndsWith(".3ds", StringComparison.OrdinalIgnoreCase)) return FileType.N3DS; + + else if (filename.EndsWith(".cia", StringComparison.OrdinalIgnoreCase)) + return FileType.N3DSCIA; return FileType.NULL; }