mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Use SdFs class to read titles
This commit is contained in:
parent
dbf3eba32a
commit
e4d9b46e60
5 changed files with 144 additions and 93 deletions
|
@ -11,108 +11,59 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
var sdfs = LoadSdFs(args);
|
||||||
|
|
||||||
Console.WriteLine("Listing NCA files");
|
Console.WriteLine("Listing NCA files");
|
||||||
ListSdContents(args);
|
ListNcas(sdfs);
|
||||||
|
|
||||||
Console.WriteLine("Listing titles");
|
Console.WriteLine("Listing titles");
|
||||||
var watch = Stopwatch.StartNew();
|
ListTitles(sdfs);
|
||||||
|
|
||||||
DumpMeta(args);
|
//DecryptNax0(sdfs, "C0628FB07A89E9050BDA258F74868E8D");
|
||||||
watch.Stop();
|
|
||||||
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecryptNax0(string[] args)
|
static void DecryptNax0(SdFs sdFs, string name)
|
||||||
{
|
{
|
||||||
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
var nca = sdFs.Ncas[name];
|
||||||
keyset.SetSdSeed(args[1].ToBytes());
|
using (var output = new FileStream($"{nca.NcaId}.nca", FileMode.Create))
|
||||||
|
|
||||||
using (var nax0 = Nax0.CreateFromPath(keyset, args[2], args[3]))
|
|
||||||
{
|
|
||||||
var nca = new Nca(keyset, nax0.Stream, true);
|
|
||||||
|
|
||||||
using (var output = new FileStream(args[4], FileMode.Create))
|
|
||||||
using (var progress = new ProgressBar())
|
using (var progress = new ProgressBar())
|
||||||
{
|
{
|
||||||
progress.LogMessage($"Title ID: {nca.Header.TitleId:X16}");
|
progress.LogMessage($"Title ID: {nca.Header.TitleId:X16}");
|
||||||
progress.LogMessage($"Writing {args[4]}");
|
progress.LogMessage($"Writing {nca.NcaId}.nca");
|
||||||
nax0.Stream.CopyStream(output, nax0.Stream.Length, progress);
|
nca.Stream.Position = 0;
|
||||||
}
|
nca.Stream.CopyStream(output, nca.Stream.Length, progress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ListSdContents(string[] args)
|
static SdFs LoadSdFs(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Using key file {args[0]}");
|
|
||||||
Console.WriteLine($"SD seed {BitConverter.ToString(args[1].ToBytes())}");
|
|
||||||
Console.WriteLine($"SD path {args[2]}");
|
|
||||||
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
||||||
|
keyset.SetSdSeed(args[1].ToBytes());
|
||||||
if (keyset.master_keys[0].IsEmpty())
|
var sdfs = new SdFs(keyset, args[2]);
|
||||||
{
|
return sdfs;
|
||||||
Console.WriteLine("Need master key 0");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keyset.SetSdSeed(args[1].ToBytes());
|
static void ListNcas(SdFs sdfs)
|
||||||
using (var sdfs = new SdFs(keyset, args[2]))
|
|
||||||
{
|
{
|
||||||
sdfs.OpenAllNcas();
|
foreach (Nca nca in sdfs.Ncas.Values.OrderBy(x => x.Header.TitleId))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{nca.Header.TitleId:X16} {nca.Header.ContentType.ToString().PadRight(10, ' ')} {nca.NcaId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (Nca nca in sdfs.Ncas)
|
static void ListTitles(SdFs sdfs)
|
||||||
|
{
|
||||||
|
foreach (var title in sdfs.Titles.Values.OrderBy(x => x.Id))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{title.Id:X16} v{title.Version.Version} ({title.Version}) {title.Metadata.Type}");
|
||||||
|
|
||||||
|
foreach (var content in title.Metadata.ContentEntries)
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"{nca.Header.TitleId:X16} {nca.Header.ContentType.ToString().PadRight(10, ' ')} {nca.Name}");
|
$" {BitConverter.ToString(content.NcaId).Replace("-", "")}.nca {content.Type} {Util.GetBytesReadable(content.Size)}");
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DumpMeta(string[] args)
|
Console.WriteLine("");
|
||||||
{
|
|
||||||
var keyset = ExternalKeys.ReadKeyFile(args[0]);
|
|
||||||
keyset.SetSdSeed(args[1].ToBytes());
|
|
||||||
List<Nca> ncas;
|
|
||||||
using (var sdfs = new SdFs(keyset, args[2]))
|
|
||||||
{
|
|
||||||
sdfs.OpenAllNcas();
|
|
||||||
ncas = sdfs.Ncas;
|
|
||||||
|
|
||||||
var metadata = new List<Cnmt>();
|
|
||||||
|
|
||||||
using (var progress = new ProgressBar())
|
|
||||||
{
|
|
||||||
foreach (var nca in ncas.Where(x => x.Header.ContentType == ContentType.Meta))
|
|
||||||
{
|
|
||||||
foreach (var section in nca.Sections.Where(x => x.Header.FsType == SectionFsType.Pfs0))
|
|
||||||
{
|
|
||||||
var sect = nca.OpenSection(section.SectionNum);
|
|
||||||
var pfs0 = new Pfs0(sect);
|
|
||||||
|
|
||||||
foreach (var entry in pfs0.Entries)
|
|
||||||
{
|
|
||||||
var path = Path.Combine("meta", entry.Name);
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
||||||
var file = pfs0.GetFile(entry.Index);
|
|
||||||
|
|
||||||
metadata.Add(new Cnmt(new MemoryStream(file)));
|
|
||||||
File.WriteAllBytes(path, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var meta in metadata.OrderBy(x => x.TitleId))
|
|
||||||
{
|
|
||||||
progress.LogMessage($"{meta.TitleId:X16} v{meta.TitleVersion} {meta.Type}");
|
|
||||||
|
|
||||||
foreach (var content in meta.ContentEntries)
|
|
||||||
{
|
|
||||||
// Add an actual hexdump function
|
|
||||||
progress.LogMessage(
|
|
||||||
$" {BitConverter.ToString(content.NcaId).Replace("-", "")}.nca {content.Type}");
|
|
||||||
}
|
|
||||||
|
|
||||||
progress.LogMessage("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace libhac
|
||||||
public class Nca : IDisposable
|
public class Nca : IDisposable
|
||||||
{
|
{
|
||||||
public NcaHeader Header { get; private set; }
|
public NcaHeader Header { get; private set; }
|
||||||
public string Name { get; set; }
|
public string NcaId { get; set; }
|
||||||
public bool HasRightsId { get; private set; }
|
public bool HasRightsId { get; private set; }
|
||||||
public int CryptoType { get; private set; }
|
public int CryptoType { get; private set; }
|
||||||
public byte[][] DecryptedKeys { get; } = Util.CreateJaggedArray<byte[][]>(4, 0x10);
|
public byte[][] DecryptedKeys { get; } = Util.CreateJaggedArray<byte[][]>(4, 0x10);
|
||||||
|
|
|
@ -216,6 +216,29 @@ namespace libhac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TitleVersion
|
||||||
|
{
|
||||||
|
public uint Version { get; }
|
||||||
|
public byte Major { get; }
|
||||||
|
public byte Minor { get; }
|
||||||
|
public byte Patch { get; }
|
||||||
|
public byte Revision { get; }
|
||||||
|
|
||||||
|
public TitleVersion(uint version)
|
||||||
|
{
|
||||||
|
Version = version;
|
||||||
|
Revision = (byte)version;
|
||||||
|
Patch = (byte)(version >> 8);
|
||||||
|
Minor = (byte)(version >> 16);
|
||||||
|
Major = (byte)(version >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{Major}.{Minor}.{Patch}.{Revision}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum ContentType
|
public enum ContentType
|
||||||
{
|
{
|
||||||
Program,
|
Program,
|
||||||
|
|
|
@ -11,7 +11,10 @@ namespace libhac
|
||||||
public string RootDir { get; }
|
public string RootDir { get; }
|
||||||
public string ContentsDir { get; }
|
public string ContentsDir { get; }
|
||||||
public string[] Files { get; }
|
public string[] Files { get; }
|
||||||
public List<Nca> Ncas { get; } = new List<Nca>();
|
|
||||||
|
public Dictionary<string, Nca> Ncas { get; } = new Dictionary<string, Nca>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
public Dictionary<ulong, Title> Titles { get; } = new Dictionary<ulong, Title>();
|
||||||
|
|
||||||
private List<Nax0> Nax0s { get; } = new List<Nax0>();
|
private List<Nax0> Nax0s { get; } = new List<Nax0>();
|
||||||
|
|
||||||
public SdFs(Keyset keyset, string sdPath)
|
public SdFs(Keyset keyset, string sdPath)
|
||||||
|
@ -24,6 +27,8 @@ namespace libhac
|
||||||
}
|
}
|
||||||
|
|
||||||
Files = Directory.GetFiles(ContentsDir, "00", SearchOption.AllDirectories).Select(Path.GetDirectoryName).ToArray();
|
Files = Directory.GetFiles(ContentsDir, "00", SearchOption.AllDirectories).Select(Path.GetDirectoryName).ToArray();
|
||||||
|
OpenAllNcas();
|
||||||
|
ReadTitles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OpenAllNcas()
|
public void OpenAllNcas()
|
||||||
|
@ -37,30 +42,47 @@ namespace libhac
|
||||||
var nax0 = Nax0.CreateFromPath(Keyset, file, sdPath);
|
var nax0 = Nax0.CreateFromPath(Keyset, file, sdPath);
|
||||||
Nax0s.Add(nax0);
|
Nax0s.Add(nax0);
|
||||||
nca = new Nca(Keyset, nax0.Stream, false);
|
nca = new Nca(Keyset, nax0.Stream, false);
|
||||||
nca.Name = Path.GetFileName(file);
|
nca.NcaId = Path.GetFileNameWithoutExtension(file);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{ex.Message} {file}");
|
Console.WriteLine($"{ex.Message} {file}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nca != null) Ncas.Add(nca);
|
if (nca != null) Ncas.Add(nca.NcaId, nca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadTitles()
|
||||||
|
{
|
||||||
|
foreach (var nca in Ncas.Values.Where(x => x.Header.ContentType == ContentType.Meta))
|
||||||
|
{
|
||||||
|
var title = new Title();
|
||||||
|
|
||||||
|
// Meta contents always have 1 Partition FS section with 1 file in it
|
||||||
|
Stream sect = nca.OpenSection(0);
|
||||||
|
var pfs0 = new Pfs0(sect);
|
||||||
|
var file = pfs0.GetFile(0);
|
||||||
|
|
||||||
|
var metadata = new Cnmt(new MemoryStream(file));
|
||||||
|
title.Id = metadata.TitleId;
|
||||||
|
title.Version = new TitleVersion(metadata.TitleVersion);
|
||||||
|
title.Metadata = metadata;
|
||||||
|
Titles.Add(title.Id, title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisposeNcas()
|
private void DisposeNcas()
|
||||||
{
|
{
|
||||||
foreach (var nca in Ncas)
|
foreach (Nca nca in Ncas.Values)
|
||||||
{
|
{
|
||||||
nca.Dispose();
|
nca.Dispose();
|
||||||
}
|
}
|
||||||
Ncas.Clear();
|
Ncas.Clear();
|
||||||
|
|
||||||
foreach (var nax0 in Nax0s)
|
// Disposing the Nca disposes the Nax0 as well
|
||||||
{
|
|
||||||
nax0.Dispose();
|
|
||||||
}
|
|
||||||
Nax0s.Clear();
|
Nax0s.Clear();
|
||||||
|
Titles.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -68,4 +90,12 @@ namespace libhac
|
||||||
DisposeNcas();
|
DisposeNcas();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Title
|
||||||
|
{
|
||||||
|
public ulong Id { get; internal set; }
|
||||||
|
public TitleVersion Version { get; internal set; }
|
||||||
|
public List<Nca> Ncas { get; } = new List<Nca>();
|
||||||
|
public Cnmt Metadata { get; internal set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,5 +181,52 @@ namespace libhac
|
||||||
{
|
{
|
||||||
return MediaSize * media;
|
return MediaSize * media;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetBytesReadable(long bytes)
|
||||||
|
{
|
||||||
|
// Get absolute value
|
||||||
|
long absBytes = (bytes < 0 ? -bytes : bytes);
|
||||||
|
// Determine the suffix and readable value
|
||||||
|
string suffix;
|
||||||
|
double readable;
|
||||||
|
if (absBytes >= 0x1000000000000000) // Exabyte
|
||||||
|
{
|
||||||
|
suffix = "EB";
|
||||||
|
readable = (bytes >> 50);
|
||||||
|
}
|
||||||
|
else if (absBytes >= 0x4000000000000) // Petabyte
|
||||||
|
{
|
||||||
|
suffix = "PB";
|
||||||
|
readable = (bytes >> 40);
|
||||||
|
}
|
||||||
|
else if (absBytes >= 0x10000000000) // Terabyte
|
||||||
|
{
|
||||||
|
suffix = "TB";
|
||||||
|
readable = (bytes >> 30);
|
||||||
|
}
|
||||||
|
else if (absBytes >= 0x40000000) // Gigabyte
|
||||||
|
{
|
||||||
|
suffix = "GB";
|
||||||
|
readable = (bytes >> 20);
|
||||||
|
}
|
||||||
|
else if (absBytes >= 0x100000) // Megabyte
|
||||||
|
{
|
||||||
|
suffix = "MB";
|
||||||
|
readable = (bytes >> 10);
|
||||||
|
}
|
||||||
|
else if (absBytes >= 0x400) // Kilobyte
|
||||||
|
{
|
||||||
|
suffix = "KB";
|
||||||
|
readable = bytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return bytes.ToString("0 B"); // Byte
|
||||||
|
}
|
||||||
|
// Divide by 1024 to get fractional value
|
||||||
|
readable = (readable / 1024);
|
||||||
|
// Return formatted number with suffix
|
||||||
|
return readable.ToString("0.### ") + suffix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue