diff --git a/src/LibHac.Nand/NandPartition.cs b/src/LibHac.Nand/NandPartition.cs index 16406371..4c8cc545 100644 --- a/src/LibHac.Nand/NandPartition.cs +++ b/src/LibHac.Nand/NandPartition.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; using System.Linq; using DiscUtils.Fat; @@ -8,46 +9,88 @@ namespace LibHac.Nand { public FatFileSystem Fs { get; } + public override string PathSeperator => "/"; + public override IDirectory RootDirectory => new IDirectory(this, ""); + public NandPartition(FatFileSystem fileSystem) { Fs = fileSystem; } - public bool FileExists(string path) + public override bool FileExists(IFile file) { - return Fs.FileExists(path); + return Fs.FileExists(file.Path); } - public bool DirectoryExists(string path) + public override bool DirectoryExists(IDirectory directory) { - return Fs.DirectoryExists(path); + return Fs.DirectoryExists(directory.Path); } - public Stream OpenFile(string path, FileMode mode) + public new virtual Stream OpenFile(IFile file, FileMode mode) { - return Fs.OpenFile(path, mode); + return Fs.OpenFile(file.Path, mode); } - public Stream OpenFile(string path, FileMode mode, FileAccess access) + public override Stream OpenFile(IFile file, FileMode mode, FileAccess access) { - return Fs.OpenFile(path, mode, access); + return Fs.OpenFile(file.Path, mode, access); } - public string[] GetFileSystemEntries(string path, string searchPattern) + public new virtual IFileSytemEntry[] GetFileSystemEntries(IDirectory directory, string searchPattern) { - return Fs.GetFileSystemEntries(path, searchPattern); + return GetFileSystemEntries(directory, searchPattern, SearchOption.TopDirectoryOnly); } - public string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption) + public override IFileSytemEntry[] GetFileSystemEntries(IDirectory directory, string searchPattern, SearchOption searchOption) { - string[] files = Fs.GetFiles(path, searchPattern, searchOption); - string[] dirs = Fs.GetDirectories(path, searchPattern, searchOption); - return files.Concat(dirs).ToArray(); + string[] files = Fs.GetFiles(directory.Path, searchPattern, searchOption); + string[] dirs = Fs.GetDirectories(directory.Path, searchPattern, searchOption); + IFileSytemEntry[] entries = new IFileSytemEntry[files.Length + dirs.Length]; + for (int i = 0; i < files.Length; i++) + entries[i] = new IFile(this, files[i]); + for (int i = 0; i < dirs.Length; i++) + entries[i] = new IDirectory(this, files[i]); + return entries; } public string GetFullPath(string path) { return path; } + + protected override IDirectory GetPath(string path) + { + return new IDirectory(this, path); + } + + protected override IFile GetFileImpl(string path) + { + return new IFile(this, path); + } + + public override IFile[] GetFiles(IDirectory directory) + { + List files = new List(); + foreach (string file in Fs.GetFiles(directory.Path)) + files.Add(GetFile(file)); + return files.ToArray(); + } + + public override IDirectory[] GetDirectories(IDirectory directory) + { + List directories = new List(); + foreach (string dir in Fs.GetDirectories(directory.Path)) + directories.Add(GetDirectory(dir)); + return directories.ToArray(); + } + + public override IFileSytemEntry[] GetEntries(IDirectory directory) + { + List list = new List(); + list.AddRange(GetDirectories(directory)); + list.AddRange(GetFiles(directory)); + return list.ToArray(); + } } } diff --git a/src/LibHac/FileSystem/IFileSystem.cs b/src/LibHac/FileSystem/IFileSystem.cs index 0cdcf910..a10325db 100644 --- a/src/LibHac/FileSystem/IFileSystem.cs +++ b/src/LibHac/FileSystem/IFileSystem.cs @@ -17,11 +17,11 @@ namespace LibHac } public abstract Stream OpenFile(IFile file, FileMode mode, FileAccess access); - public IFile[] GetFileSystemEntries(IDirectory directory, string searchPattern) + public IFileSytemEntry[] GetFileSystemEntries(IDirectory directory, string searchPattern) { return GetFileSystemEntries(directory, searchPattern, SearchOption.TopDirectoryOnly); } - public abstract IFile[] GetFileSystemEntries(IDirectory path, string searchPattern, SearchOption searchOption); + public abstract IFileSytemEntry[] GetFileSystemEntries(IDirectory path, string searchPattern, SearchOption searchOption); public IDirectory GetDirectory(string path) { @@ -35,20 +35,28 @@ namespace LibHac { if (path.StartsWith(PathSeperator)) path = path.Substring(PathSeperator.Length); - return GetFile(path); + return GetFileImpl(path); } protected abstract IFile GetFileImpl(string path); public abstract IFile[] GetFiles(IDirectory directory); public abstract IDirectory[] GetDirectories(IDirectory directory); + public abstract IFileSytemEntry[] GetEntries(IDirectory directory); } - public class IDirectory + public interface IFileSytemEntry { - public IFileSystem FileSystem; - public string Path; + IFileSystem FileSystem { get; } + string Path { get; } + bool Exists { get; } + } + + public class IDirectory : IFileSytemEntry + { + public IFileSystem FileSystem { get; } + public string Path { get; } public IDirectory Parent { @@ -79,17 +87,28 @@ namespace LibHac return FileSystem.GetDirectory(Path + FileSystem.PathSeperator + path); } + public IFileSytemEntry[] GetFileSystemEntries(string searchOption) + { + return FileSystem.GetFileSystemEntries(this, searchOption); + } + + public IFileSytemEntry[] GetFileSystemEntries(string searchPattern, SearchOption searchOption) + { + return FileSystem.GetFileSystemEntries(this, searchPattern, searchOption); + } } - public class IFile + public class IFile : IFileSytemEntry { - public IFileSystem FileSystem; - public string Path; + public IFileSystem FileSystem { get; } + public string Path { get; } public string Name => System.IO.Path.GetFileNameWithoutExtension(Path); public string Extension => System.IO.Path.GetExtension(Path); public string FileName => System.IO.Path.GetFileName(Path); + public bool Exists => FileSystem.FileExists(this); + public IDirectory Parent { get { @@ -98,7 +117,6 @@ namespace LibHac } } - public bool Exists => FileSystem.FileExists(this); public IFile(IFileSystem filesystem, string path) { diff --git a/src/LibHac/FileSystem/LocalFileSystem.cs b/src/LibHac/FileSystem/LocalFileSystem.cs index a28be154..ce325424 100644 --- a/src/LibHac/FileSystem/LocalFileSystem.cs +++ b/src/LibHac/FileSystem/LocalFileSystem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace LibHac { @@ -30,28 +31,18 @@ namespace LibHac return new FileStream(GetFullPath(file), mode, access); } - public override IFile[] GetFileSystemEntries(IDirectory path, string searchPattern, SearchOption searchOption) + public override IFileSytemEntry[] GetFileSystemEntries(IDirectory path, string searchPattern, SearchOption searchOption) { - //return Directory.GetFileSystemEntries(Path.Combine(Root, path), searchPattern, searchOption); - var result = new List(); + var result = new List(); - try + DirectoryInfo root = new DirectoryInfo(GetFullPath(path)); + foreach(FileSystemInfo info in root.EnumerateFileSystemInfos(searchPattern, searchOption)) { - result.AddRange(GetFileSystemEntries(path, searchPattern)); - } - catch (UnauthorizedAccessException) { /* Skip this directory */ } - - if (searchOption == SearchOption.TopDirectoryOnly) - return result.ToArray(); - - string[] searchDirectories = Directory.GetDirectories(GetFullPath(path)); - foreach (string search in searchDirectories) - { - try - { - result.AddRange(GetFileSystemEntries(FullPathToDirectory(search), searchPattern, searchOption)); - } - catch (UnauthorizedAccessException) { /* Skip this result */ } + string relativePath = Util.GetRelativePath(info.FullName, Root); + if (info.Attributes.HasFlag(FileAttributes.Directory)) + result.Add(new IDirectory(this, relativePath)); + else + result.Add(new LocalFile(this, relativePath)); } return result.ToArray(); @@ -76,6 +67,14 @@ namespace LibHac return directories; } + public override IFileSytemEntry[] GetEntries(IDirectory directory) + { + List list = new List(); + list.AddRange(GetDirectories(directory)); + list.AddRange(GetFiles(directory)); + return list.ToArray(); + } + protected override IDirectory GetPath(string path) { return new IDirectory(this, path); diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index 771a4517..926e950a 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -58,20 +58,16 @@ namespace LibHac private void OpenAllNcas() { - IFile[] files = ContentsDir.Files; - IDirectory[] directories = ContentsDir.Directories; - Dictionary storages = new Dictionary(); + Dictionary storages = new Dictionary(); - foreach (IFile file in files) - storages[file.Path] = file.Open(FileMode.Open, FileAccess.Read).AsStorage(); - foreach(IDirectory directory in directories) - storages[directory.Path] = OpenSplitNcaStream(Fs, directory); + foreach (IFileSytemEntry nca in ContentsDir.GetFileSystemEntries("*.nca", SearchOption.AllDirectories)) + storages[nca] = OpenSplitNcaStream(nca); - foreach (KeyValuePair kv in storages) + foreach (KeyValuePair kv in storages) { Nca nca = null; - string path = kv.Key; + IFileSytemEntry file = kv.Key; IStorage storage = kv.Value; try { @@ -87,7 +83,7 @@ namespace LibHac if (isNax0) { - string sdPath = "/" + path.Replace('\\', '/'); + string sdPath = Util.GetRelativePath(file.Path, ContentsDir.Path).Replace("\\", "/"); var nax0 = new Nax0(Keyset, storage, sdPath, false); nca = new Nca(Keyset, nax0.BaseStorage, false); } @@ -96,23 +92,23 @@ namespace LibHac nca = new Nca(Keyset, storage, false); } - nca.NcaId = Path.GetFileNameWithoutExtension(path); + nca.NcaId = Path.GetFileNameWithoutExtension(file.Path); string extension = nca.Header.ContentType == ContentType.Meta ? ".cnmt.nca" : ".nca"; nca.Filename = nca.NcaId + extension; } catch (MissingKeyException ex) { if (ex.Name == null) - { Console.WriteLine($"{ex.Message} File:\n{path}"); } + { Console.WriteLine($"{ex.Message} File:\n{file.Path}"); } else { string name = ex.Type == KeyType.Title ? $"Title key for rights ID {ex.Name}" : ex.Name; - Console.WriteLine($"{ex.Message}\nKey: {name}\nFile: {path}"); + Console.WriteLine($"{ex.Message}\nKey: {name}\nFile: {file.Path}"); } } catch (Exception ex) { - Console.WriteLine($"{ex.Message} File: {path}"); + Console.WriteLine($"{ex.Message} File: {file.Path}"); } if (nca?.NcaId != null) Ncas.Add(nca.NcaId, nca); @@ -123,7 +119,7 @@ namespace LibHac { if (SaveDir == null) return; - IFile[] files = Fs.GetFileSystemEntries(SaveDir, "*"); + IFileSytemEntry[] files = Fs.GetFileSystemEntries(SaveDir, "*"); foreach (IFile file in files) { @@ -134,7 +130,7 @@ namespace LibHac { IStorage storage = Fs.OpenFile(file, FileMode.Open).AsStorage(); - string sdPath = file.Path.Replace('\\', '/'); + string sdPath = Util.GetRelativePath(file.Path, SaveDir.Path).Replace("\\", "/"); var nax0 = new Nax0(Keyset, storage, sdPath, false); save = new Savefile(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true); } @@ -157,7 +153,15 @@ namespace LibHac var title = new Title(); // Meta contents always have 1 Partition FS section with 1 file in it - IStorage sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true); + IStorage sect; + try + { + sect = nca.OpenSection(0, false, IntegrityCheckLevel.ErrorOnInvalid, true); + } catch(Exception e) + { + continue; + } + var pfs0 = new Pfs(sect); IStorage file = pfs0.OpenFile(pfs0.Files[0]); @@ -241,19 +245,32 @@ namespace LibHac } } - internal static IStorage OpenSplitNcaStream(IFileSystem fs, IDirectory directory) + internal static IStorage OpenSplitNcaStream(IFileSytemEntry nca) { var files = new List(); - var storages = new List(); + var storages = new List(); - if (directory.Exists) + if (nca.Exists) { - while (true) + if (typeof(IFile).IsAssignableFrom(nca.GetType())) // if entry is a IFile { - IFile partFile = directory.GetFile($"{files.Count:D2}"); - if (!partFile.Exists) break; + IFile file = (IFile)nca; + if (file.Name != "00") + { + return file.Open(FileMode.Open, FileAccess.Read).AsStorage(); + } + files.Add(file); + } + else if (typeof(IDirectory).IsAssignableFrom(nca.GetType())) + { + IDirectory directory = (IDirectory)nca; + while (true) + { + IFile partFile = directory.GetFile($"{files.Count:D2}"); + if (!partFile.Exists) break; - files.Add(partFile); + files.Add(partFile); + } } } else @@ -261,7 +278,6 @@ namespace LibHac if (files.Count == 1) return files[0].Open(FileMode.Open, FileAccess.Read).AsStorage(); - foreach (IFile file in files) storages.Add(file.Open( FileMode.Open, FileAccess.Read).AsStorage()); diff --git a/tests/LibHac.Tests/FileSystemTests.cs b/tests/LibHac.Tests/FileSystemTests.cs index ce83e168..bf5f685e 100644 --- a/tests/LibHac.Tests/FileSystemTests.cs +++ b/tests/LibHac.Tests/FileSystemTests.cs @@ -10,7 +10,7 @@ namespace LibHac.Tests { public static void Main(string[] args) { - LocalFileSystem fs = new LocalFileSystem("C:\\\\"); + LocalFileSystem fs = new LocalFileSystem("G:\\\\"); foreach(IFile file in fs.RootDirectory.Files) { Console.WriteLine(file.Path); @@ -21,6 +21,12 @@ namespace LibHac.Tests Console.WriteLine(directory.Path); } + string baseDir = "C:\\Users\\Somebody Whoisbored\\.switch\\"; + Keyset keyset = ExternalKeys.ReadKeyFile(baseDir + "prod.keys", baseDir + "title.keys", baseDir + "console.keys"); + + SwitchFs sw = new SwitchFs(keyset, fs); + ; + Console.ReadKey(); } }