Get rid of old code

This commit is contained in:
Alex Barney 2019-01-11 15:53:41 -06:00
parent 3e4dcd9466
commit 8861f25bc7
6 changed files with 41 additions and 358 deletions

View file

@ -1,53 +0,0 @@
using System.IO;
using System.Linq;
using DiscUtils.Fat;
namespace LibHac.Nand
{
public class NandPartition : IFileSystemOld
{
public FatFileSystem Fs { get; }
public NandPartition(FatFileSystem fileSystem)
{
Fs = fileSystem;
}
public bool FileExists(string path)
{
return Fs.FileExists(path);
}
public bool DirectoryExists(string path)
{
return Fs.DirectoryExists(path);
}
public Stream OpenFile(string path, FileMode mode)
{
return Fs.OpenFile(path, mode);
}
public Stream OpenFile(string path, FileMode mode, FileAccess access)
{
return Fs.OpenFile(path, mode, access);
}
public string[] GetFileSystemEntries(string path, string searchPattern)
{
return Fs.GetFileSystemEntries(path, searchPattern);
}
public string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
string[] files = Fs.GetFiles(path, searchPattern, searchOption);
string[] dirs = Fs.GetDirectories(path, searchPattern, searchOption);
return files.Concat(dirs).ToArray();
}
public string GetFullPath(string path)
{
return path;
}
}
}

View file

@ -1,73 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace LibHac
{
public class FileSystem : IFileSystemOld
{
public string Root { get; }
public FileSystem(string rootDir)
{
Root = Path.GetFullPath(rootDir);
}
public bool FileExists(string path)
{
return File.Exists(Path.Combine(Root, path));
}
public bool DirectoryExists(string path)
{
return Directory.Exists(Path.Combine(Root, path));
}
public Stream OpenFile(string path, FileMode mode)
{
return new FileStream(Path.Combine(Root, path), mode);
}
public Stream OpenFile(string path, FileMode mode, FileAccess access)
{
return new FileStream(Path.Combine(Root, path), mode, access);
}
public string[] GetFileSystemEntries(string path, string searchPattern)
{
return Directory.GetFileSystemEntries(Path.Combine(Root, path), searchPattern);
}
public string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
//return Directory.GetFileSystemEntries(Path.Combine(Root, path), searchPattern, searchOption);
var result = new List<string>();
try
{
result.AddRange(GetFileSystemEntries(Path.Combine(Root, path), searchPattern));
}
catch (UnauthorizedAccessException) { /* Skip this directory */ }
if (searchOption == SearchOption.TopDirectoryOnly)
return result.ToArray();
string[] searchDirectories = Directory.GetDirectories(Path.Combine(Root, path));
foreach (string search in searchDirectories)
{
try
{
result.AddRange(GetFileSystemEntries(search, searchPattern, searchOption));
}
catch (UnauthorizedAccessException) { /* Skip this result */ }
}
return result.ToArray();
}
public string GetFullPath(string path)
{
return Path.Combine(Root, path);
}
}
}

View file

@ -1,15 +0,0 @@
using System.IO;
namespace LibHac
{
public interface IFileSystemOld
{
bool FileExists(string path);
bool DirectoryExists(string path);
Stream OpenFile(string path, FileMode mode);
Stream OpenFile(string path, FileMode mode, FileAccess access);
string[] GetFileSystemEntries(string path, string searchPattern);
string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption);
string GetFullPath(string path);
}
}

View file

@ -1,93 +0,0 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using LibHac.IO;
namespace LibHac
{
public class Nax0 : IDisposable
{
private const int SectorSize = 0x4000;
public byte[] Hmac { get; private set; }
public byte[][] EncKeys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10);
public byte[][] Keys { get; } = Util.CreateJaggedArray<byte[][]>(2, 0x10);
public byte[] Key { get; } = new byte[0x20];
public long Length { get; private set; }
public IStorage BaseStorage { get; }
private bool LeaveOpen { get; }
public Nax0(Keyset keyset, IStorage storage, string sdPath, bool leaveOpen)
{
LeaveOpen = leaveOpen;
ReadHeader(storage.AsStream());
DeriveKeys(keyset, sdPath, storage);
BaseStorage = new CachedStorage(new Aes128XtsStorage(storage.Slice(SectorSize), Key, SectorSize, leaveOpen), 4, leaveOpen);
}
private void ReadHeader(Stream stream)
{
var reader = new BinaryReader(stream);
Hmac = reader.ReadBytes(0x20);
string magic = reader.ReadAscii(4);
reader.BaseStream.Position += 4;
if (magic != "NAX0") throw new InvalidDataException("Not an NAX0 file");
EncKeys[0] = reader.ReadBytes(0x10);
EncKeys[1] = reader.ReadBytes(0x10);
Length = reader.ReadInt64();
}
private void DeriveKeys(Keyset keyset, string sdPath, IStorage storage)
{
var validationHashKey = new byte[0x60];
storage.Read(validationHashKey, 0x20);
// Try both the NCA and save key sources and pick the one that works
for (int k = 0; k < 2; k++)
{
var naxSpecificKeys = Util.CreateJaggedArray<byte[][]>(2, 0x10);
var hashKey = new byte[0x10];
Array.Copy(keyset.SdCardKeys[k], hashKey, 0x10);
// Use the sd path to generate the kek for this NAX0
var hash = new HMACSHA256(hashKey);
byte[] sdPathBytes = Encoding.ASCII.GetBytes(sdPath);
byte[] checksum = hash.ComputeHash(sdPathBytes, 0, sdPathBytes.Length);
Array.Copy(checksum, 0, naxSpecificKeys[0], 0, 0x10);
Array.Copy(checksum, 0x10, naxSpecificKeys[1], 0, 0x10);
// Decrypt this NAX0's keys
Crypto.DecryptEcb(naxSpecificKeys[0], EncKeys[0], Keys[0], 0x10);
Crypto.DecryptEcb(naxSpecificKeys[1], EncKeys[1], Keys[1], 0x10);
Array.Copy(Keys[0], 0, Key, 0, 0x10);
Array.Copy(Keys[1], 0, Key, 0x10, 0x10);
// Copy the decrypted keys into the NAX0 header and use that for the HMAC key
// for validating that the keys are correct
Array.Copy(Keys[0], 0, validationHashKey, 8, 0x10);
Array.Copy(Keys[1], 0, validationHashKey, 0x18, 0x10);
var validationHash = new HMACSHA256(validationHashKey);
byte[] validationMac = validationHash.ComputeHash(keyset.SdCardKeys[k], 0x10, 0x10);
if (Util.ArraysEqual(Hmac, validationMac))
{
return;
}
}
throw new ArgumentException("NAX0 key derivation failed.");
}
public void Dispose()
{
if (!LeaveOpen)
{
BaseStorage?.Dispose();
}
}
}
}

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using LibHac.IO; using LibHac.IO;
using LibHac.IO.Save; using LibHac.IO.Save;
@ -12,44 +11,23 @@ namespace LibHac
public class SwitchFs : IDisposable public class SwitchFs : IDisposable
{ {
public Keyset Keyset { get; } public Keyset Keyset { get; }
public IFileSystemOld Fs { get; } public IAttributeFileSystem BaseFs { get; }
public string ContentsDir { get; } public AesXtsFileSystem Fs { get; }
public string SaveDir { get; }
public Dictionary<string, Nca> Ncas { get; } = new Dictionary<string, Nca>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, Nca> Ncas { get; } = new Dictionary<string, Nca>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, SaveDataFileSystem> Saves { get; } = new Dictionary<string, SaveDataFileSystem>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, SaveDataFileSystem> Saves { get; } = new Dictionary<string, SaveDataFileSystem>(StringComparer.OrdinalIgnoreCase);
public Dictionary<ulong, Title> Titles { get; } = new Dictionary<ulong, Title>(); public Dictionary<ulong, Title> Titles { get; } = new Dictionary<ulong, Title>();
public Dictionary<ulong, Application> Applications { get; } = new Dictionary<ulong, Application>(); public Dictionary<ulong, Application> Applications { get; } = new Dictionary<ulong, Application>();
public SwitchFs(Keyset keyset, IFileSystemOld fs) public SwitchFs(Keyset keyset, IAttributeFileSystem fs)
{ {
Fs = fs; BaseFs = fs;
Keyset = keyset; Keyset = keyset;
if (fs.DirectoryExists("Nintendo")) var concatFs = new ConcatenationFileSystem(BaseFs);
{ Fs = new AesXtsFileSystem(concatFs, keyset.SdCardKeys[1], 0x4000);
ContentsDir = fs.GetFullPath(Path.Combine("Nintendo", "Contents"));
SaveDir = fs.GetFullPath(Path.Combine("Nintendo", "save"));
}
else
{
if (fs.DirectoryExists("Contents"))
{
ContentsDir = fs.GetFullPath("Contents");
}
if (fs.DirectoryExists("save")) // OpenAllSaves();
{
SaveDir = fs.GetFullPath("save");
}
}
if (ContentsDir == null)
{
throw new DirectoryNotFoundException("Could not find \"Contents\" directory");
}
OpenAllSaves();
OpenAllNcas(); OpenAllNcas();
ReadTitles(); ReadTitles();
ReadControls(); ReadControls();
@ -58,88 +36,70 @@ namespace LibHac
private void OpenAllNcas() private void OpenAllNcas()
{ {
string[] files = Fs.GetFileSystemEntries(ContentsDir, "*.nca", SearchOption.AllDirectories); IEnumerable<DirectoryEntry> files = Fs.OpenDirectory("/", OpenDirectoryMode.All).EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories);
foreach (string file in files) foreach (DirectoryEntry fileEntry in files)
{ {
Nca nca = null; Nca nca = null;
try try
{ {
bool isNax0; var storage = new FileStorage(Fs.OpenFile(fileEntry.FullPath, OpenMode.Read));
IStorage storage = OpenSplitNcaStorage(Fs, file);
if (storage == null) continue;
using (var reader = new BinaryReader(storage.AsStream(), Encoding.Default, true))
{
reader.BaseStream.Position = 0x20;
isNax0 = reader.ReadUInt32() == 0x3058414E; // NAX0
reader.BaseStream.Position = 0;
}
if (isNax0)
{
string sdPath = "/" + Util.GetRelativePath(file, ContentsDir).Replace('\\', '/');
var nax0 = new Nax0(Keyset, storage, sdPath, false);
nca = new Nca(Keyset, nax0.BaseStorage, false);
}
else
{
nca = new Nca(Keyset, storage, false); nca = new Nca(Keyset, storage, false);
}
nca.NcaId = Path.GetFileNameWithoutExtension(file); nca.NcaId = Path.GetFileNameWithoutExtension(fileEntry.Name);
string extension = nca.Header.ContentType == ContentType.Meta ? ".cnmt.nca" : ".nca"; string extension = nca.Header.ContentType == ContentType.Meta ? ".cnmt.nca" : ".nca";
nca.Filename = nca.NcaId + extension; nca.Filename = nca.NcaId + extension;
} }
catch (MissingKeyException ex) catch (MissingKeyException ex)
{ {
if (ex.Name == null) if (ex.Name == null)
{ Console.WriteLine($"{ex.Message} File:\n{file}"); } { Console.WriteLine($"{ex.Message} File:\n{fileEntry}"); }
else else
{ {
string name = ex.Type == KeyType.Title ? $"Title key for rights ID {ex.Name}" : ex.Name; string name = ex.Type == KeyType.Title ? $"Title key for rights ID {ex.Name}" : ex.Name;
Console.WriteLine($"{ex.Message}\nKey: {name}\nFile: {file}"); Console.WriteLine($"{ex.Message}\nKey: {name}\nFile: {fileEntry}");
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"{ex.Message} File: {file}"); Console.WriteLine($"{ex.Message} File: {fileEntry}");
} }
if (nca?.NcaId != null) Ncas.Add(nca.NcaId, nca); if (nca?.NcaId != null) Ncas.Add(nca.NcaId, nca);
} }
} }
private void OpenAllSaves() //private void OpenAllSaves()
{ //{
if (SaveDir == null) return; // if (SaveDir == null) return;
string[] files = Fs.GetFileSystemEntries(SaveDir, "*"); // string[] files = Fs.GetFileSystemEntries(SaveDir, "*");
foreach (string file in files) // foreach (string file in files)
{ // {
SaveDataFileSystem save = null; // SaveDataFileSystem save = null;
string saveName = Path.GetFileNameWithoutExtension(file); // string saveName = Path.GetFileNameWithoutExtension(file);
try // try
{ // {
IStorage storage = Fs.OpenFile(file, FileMode.Open).AsStorage(); // IStorage storage = Fs.OpenFile(file, FileMode.Open).AsStorage();
string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/'); // string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/');
var nax0 = new Nax0(Keyset, storage, sdPath, false); // var nax0 = new Nax0(Keyset, storage, sdPath, false);
save = new SaveDataFileSystem(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true); // save = new SaveDataFileSystem(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
Console.WriteLine($"{ex.Message} File: {file}"); // Console.WriteLine($"{ex.Message} File: {file}");
} // }
if (save != null && saveName != null) // if (save != null && saveName != null)
{ // {
Saves[saveName] = save; // Saves[saveName] = save;
} // }
} // }
} //}
private void ReadTitles() private void ReadTitles()
{ {
@ -232,49 +192,6 @@ namespace LibHac
} }
} }
internal static IStorage OpenSplitNcaStorage(IFileSystemOld fs, string path)
{
var files = new List<string>();
var storages = new List<IStorage>();
if (fs.DirectoryExists(path))
{
while (true)
{
string partName = Path.Combine(path, $"{files.Count:D2}");
if (!fs.FileExists(partName)) break;
files.Add(partName);
}
}
else if (fs.FileExists(path))
{
if (Path.GetFileName(path) != "00")
{
return fs.OpenFile(path, FileMode.Open, FileAccess.Read).AsStorage();
}
files.Add(path);
}
else
{
throw new FileNotFoundException("Could not find the input file or directory");
}
if (files.Count == 1)
{
return fs.OpenFile(files[0], FileMode.Open, FileAccess.Read).AsStorage();
}
foreach (string file in files)
{
storages.Add(fs.OpenFile(file, FileMode.Open, FileAccess.Read).AsStorage());
}
if (storages.Count == 0) return null; //todo
return new ConcatenationStorage(storages, true);
}
private void DisposeNcas() private void DisposeNcas()
{ {
foreach (Nca nca in Ncas.Values) foreach (Nca nca in Ncas.Values)

View file

@ -13,7 +13,7 @@ namespace hactoolnet
{ {
public static void Process(Context ctx) public static void Process(Context ctx)
{ {
var switchFs = new SwitchFs(ctx.Keyset, new FileSystem(ctx.Options.InFile)); var switchFs = new SwitchFs(ctx.Keyset, new LocalFileSystem($"{ctx.Options.InFile}/Nintendo/Contents"));
if (ctx.Options.ListNcas) if (ctx.Options.ListNcas)
{ {