diff --git a/src/LibHac/IO/SubdirectoryFileSystemDirectory.cs b/src/LibHac/IO/SubdirectoryFileSystemDirectory.cs index e5d5fdbe..55422183 100644 --- a/src/LibHac/IO/SubdirectoryFileSystemDirectory.cs +++ b/src/LibHac/IO/SubdirectoryFileSystemDirectory.cs @@ -20,7 +20,10 @@ namespace LibHac.IO public IEnumerable Read() { - return BaseDirectory.Read(); + foreach (DirectoryEntry entry in BaseDirectory.Read()) + { + yield return new DirectoryEntry(entry.Name, FullPath + '/' + entry.Name, entry.Type, entry.Size); + } } public int GetEntryCount() diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index 75b2e70f..d73d11ac 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -11,39 +11,63 @@ namespace LibHac public class SwitchFs : IDisposable { public Keyset Keyset { get; } - public IAttributeFileSystem BaseFs { get; } - public AesXtsFileSystem Fs { get; } + public IFileSystem ContentFs { get; } + public IFileSystem SaveFs { get; } public Dictionary Ncas { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary Saves { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary Titles { get; } = new Dictionary(); public Dictionary Applications { get; } = new Dictionary(); - public SwitchFs(Keyset keyset, IAttributeFileSystem fs) + public SwitchFs(Keyset keyset, IFileSystem contentFileSystem, IFileSystem saveFileSystem) { - BaseFs = fs; Keyset = keyset; + ContentFs = contentFileSystem; + SaveFs = saveFileSystem; - var concatFs = new ConcatenationFileSystem(BaseFs); - Fs = new AesXtsFileSystem(concatFs, keyset.SdCardKeys[1], 0x4000); - - // OpenAllSaves(); + OpenAllSaves(); OpenAllNcas(); ReadTitles(); ReadControls(); CreateApplications(); } + public static SwitchFs OpenSdCard(Keyset keyset, IAttributeFileSystem fileSystem) + { + var concatFs = new ConcatenationFileSystem(fileSystem); + var saveDirFs = new SubdirectoryFileSystem(concatFs, "/Nintendo/save"); + var contentDirFs = new SubdirectoryFileSystem(concatFs, "/Nintendo/Contents"); + + var encSaveFs = new AesXtsFileSystem(saveDirFs, keyset.SdCardKeys[0], 0x4000); + var encContentFs = new AesXtsFileSystem(contentDirFs, keyset.SdCardKeys[1], 0x4000); + + return new SwitchFs(keyset, encContentFs, encSaveFs); + } + + public static SwitchFs OpenNandPartition(Keyset keyset, IAttributeFileSystem fileSystem) + { + var concatFs = new ConcatenationFileSystem(fileSystem); + var saveDirFs = new SubdirectoryFileSystem(concatFs, "/save"); + var contentDirFs = new SubdirectoryFileSystem(concatFs, "/Contents"); + + return new SwitchFs(keyset, contentDirFs, saveDirFs); + } + + public static SwitchFs OpenNcaDirectory(Keyset keyset, IFileSystem fileSystem) + { + return new SwitchFs(keyset, fileSystem, null); + } + private void OpenAllNcas() { - IEnumerable files = Fs.OpenDirectory("/", OpenDirectoryMode.All).EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories); + IEnumerable files = ContentFs.OpenDirectory("/", OpenDirectoryMode.All).EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories); foreach (DirectoryEntry fileEntry in files) { Nca nca = null; try { - var storage = new FileStorage(Fs.OpenFile(fileEntry.FullPath, OpenMode.Read)); + var storage = new FileStorage(ContentFs.OpenFile(fileEntry.FullPath, OpenMode.Read)); nca = new Nca(Keyset, storage, false); @@ -63,43 +87,38 @@ namespace LibHac } catch (Exception ex) { - Console.WriteLine($"{ex.Message} File: {fileEntry}"); + Console.WriteLine($"{ex.Message} File: {fileEntry.FullPath}"); } if (nca?.NcaId != null) Ncas.Add(nca.NcaId, nca); } } - //private void OpenAllSaves() - //{ - // if (SaveDir == null) return; + private void OpenAllSaves() + { + if (SaveFs == null) return; - // string[] files = Fs.GetFileSystemEntries(SaveDir, "*"); + foreach (DirectoryEntry fileEntry in SaveFs.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) + { + SaveDataFileSystem save = null; + string saveName = Path.GetFileNameWithoutExtension(fileEntry.Name); - // foreach (string file in files) - // { - // SaveDataFileSystem save = null; - // string saveName = Path.GetFileNameWithoutExtension(file); + try + { + IFile file = SaveFs.OpenFile(fileEntry.FullPath, OpenMode.Read); + save = new SaveDataFileSystem(Keyset, new FileStorage(file), IntegrityCheckLevel.None, true); + } + catch (Exception ex) + { + Console.WriteLine($"{ex.Message} File: {fileEntry.FullPath}"); + } - // try - // { - // IStorage storage = Fs.OpenFile(file, FileMode.Open).AsStorage(); - - // string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/'); - // var nax0 = new Nax0(Keyset, storage, sdPath, false); - // save = new SaveDataFileSystem(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true); - // } - // catch (Exception ex) - // { - // Console.WriteLine($"{ex.Message} File: {file}"); - // } - - // if (save != null && saveName != null) - // { - // Saves[saveName] = save; - // } - // } - //} + if (save != null && saveName != null) + { + Saves[saveName] = save; + } + } + } private void ReadTitles() { diff --git a/src/NandReader/Program.cs b/src/NandReader/Program.cs index 581ce48a..4725e7b5 100644 --- a/src/NandReader/Program.cs +++ b/src/NandReader/Program.cs @@ -50,8 +50,7 @@ namespace NandReader Keyset keyset = OpenKeyset(); var nand = new Nand(stream, keyset); FatFileSystemProvider user = nand.OpenSystemPartition(); - // todo - //var sdfs = new SwitchFs(keyset, user); + SwitchFs sdfs = SwitchFs.OpenNandPartition(keyset, user); } } diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs index 912c406e..3fbf1596 100644 --- a/src/hactoolnet/ProcessSwitchFs.cs +++ b/src/hactoolnet/ProcessSwitchFs.cs @@ -13,7 +13,24 @@ namespace hactoolnet { public static void Process(Context ctx) { - var switchFs = new SwitchFs(ctx.Keyset, new LocalFileSystem($"{ctx.Options.InFile}/Nintendo/Contents")); + SwitchFs switchFs; + var baseFs = new LocalFileSystem(ctx.Options.InFile); + + if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Nintendo", "Contents", "registered"))) + { + ctx.Logger.LogMessage("Treating path as SD card storage"); + switchFs = SwitchFs.OpenSdCard(ctx.Keyset, baseFs); + } + else if (Directory.Exists(Path.Combine(ctx.Options.InFile, "Contents", "registered"))) + { + ctx.Logger.LogMessage("Treating path as NAND storage"); + switchFs = SwitchFs.OpenNandPartition(ctx.Keyset, baseFs); + } + else + { + ctx.Logger.LogMessage("Treating path as a directory of loose NCAs"); + switchFs = SwitchFs.OpenNcaDirectory(ctx.Keyset, baseFs); + } if (ctx.Options.ListNcas) {