From f583d01248183a284aa8827d95500dcbfbd0574e Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 26 Jun 2018 19:42:01 -0500 Subject: [PATCH] Make sure meta nca is included in title --- hactoolnet/Program.cs | 25 ++++++++++++++++++++++--- libhac/Nca.cs | 1 + libhac/SdFs.cs | 18 ++++++++++++++++-- libhac/Util.cs | 26 ++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/hactoolnet/Program.cs b/hactoolnet/Program.cs index d971d552..afa41a22 100644 --- a/hactoolnet/Program.cs +++ b/hactoolnet/Program.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using libhac; @@ -20,10 +18,12 @@ namespace hactoolnet ListTitles(sdfs); //DecryptNax0(sdfs, "C0628FB07A89E9050BDA258F74868E8D"); + DecryptTitle(sdfs, 0x0100000000010800); } static void DecryptNax0(SdFs sdFs, string name) { + if (!sdFs.Ncas.ContainsKey(name)) return; var nca = sdFs.Ncas[name]; using (var output = new FileStream($"{nca.NcaId}.nca", FileMode.Create)) using (var progress = new ProgressBar()) @@ -35,6 +35,25 @@ namespace hactoolnet } } + static void DecryptTitle(SdFs sdFs, ulong titleId) + { + var title = sdFs.Titles[titleId]; + var dirName = $"{titleId:X16}v{title.Version.Version}"; + + Directory.CreateDirectory(dirName); + + foreach (var nca in title.Ncas) + { + using (var output = new FileStream(Path.Combine(dirName, nca.Filename), FileMode.Create)) + using (var progress = new ProgressBar()) + { + progress.LogMessage($"Writing {nca.Filename}"); + nca.Stream.Position = 0; + nca.Stream.CopyStream(output, nca.Stream.Length, progress); + } + } + } + static SdFs LoadSdFs(string[] args) { var keyset = ExternalKeys.ReadKeyFile(args[0]); @@ -60,7 +79,7 @@ namespace hactoolnet foreach (var content in title.Metadata.ContentEntries) { Console.WriteLine( - $" {BitConverter.ToString(content.NcaId).Replace("-", "")}.nca {content.Type} {Util.GetBytesReadable(content.Size)}"); + $" {content.NcaId.ToHexString()}.nca {content.Type} {Util.GetBytesReadable(content.Size)}"); } Console.WriteLine(""); diff --git a/libhac/Nca.cs b/libhac/Nca.cs index e504e44c..345803e0 100644 --- a/libhac/Nca.cs +++ b/libhac/Nca.cs @@ -9,6 +9,7 @@ namespace libhac { public NcaHeader Header { get; private set; } public string NcaId { get; set; } + public string Filename { get; set; } public bool HasRightsId { get; private set; } public int CryptoType { get; private set; } public byte[][] DecryptedKeys { get; } = Util.CreateJaggedArray(4, 0x10); diff --git a/libhac/SdFs.cs b/libhac/SdFs.cs index 56c66422..966a189b 100644 --- a/libhac/SdFs.cs +++ b/libhac/SdFs.cs @@ -31,7 +31,7 @@ namespace libhac ReadTitles(); } - public void OpenAllNcas() + private void OpenAllNcas() { foreach (var file in Files) { @@ -43,6 +43,8 @@ namespace libhac Nax0s.Add(nax0); nca = new Nca(Keyset, nax0.Stream, false); nca.NcaId = Path.GetFileNameWithoutExtension(file); + var extention = nca.Header.ContentType == ContentType.Meta ? ".cnmt.nca" : ".nca"; + nca.Filename = nca.NcaId + extention; } catch (Exception ex) { @@ -53,7 +55,7 @@ namespace libhac } } - public void ReadTitles() + private void ReadTitles() { foreach (var nca in Ncas.Values.Where(x => x.Header.ContentType == ContentType.Meta)) { @@ -68,6 +70,18 @@ namespace libhac title.Id = metadata.TitleId; title.Version = new TitleVersion(metadata.TitleVersion); title.Metadata = metadata; + title.Ncas.Add(nca); + + foreach (var content in metadata.ContentEntries) + { + var ncaId = content.NcaId.ToHexString(); + + if (Ncas.TryGetValue(ncaId, out Nca contentNca)) + { + title.Ncas.Add(contentNca); + } + } + Titles.Add(title.Id, title); } } diff --git a/libhac/Util.cs b/libhac/Util.cs index 2cd53635..d0ef138a 100644 --- a/libhac/Util.cs +++ b/libhac/Util.cs @@ -177,6 +177,32 @@ namespace libhac return result; } + private static readonly uint[] Lookup32 = CreateLookup32(); + + private static uint[] CreateLookup32() + { + var result = new uint[256]; + for (int i = 0; i < 256; i++) + { + string s = i.ToString("X2"); + result[i] = s[0] + ((uint)s[1] << 16); + } + return result; + } + + public static string ToHexString(this byte[] bytes) + { + var lookup32 = Lookup32; + var result = new char[bytes.Length * 2]; + for (int i = 0; i < bytes.Length; i++) + { + var val = lookup32[bytes[i]]; + result[2 * i] = (char)val; + result[2 * i + 1] = (char)(val >> 16); + } + return new string(result); + } + internal static long MediaToReal(long media) { return MediaSize * media;