Make IFile and IDirectory implement IFileSystemDirectory

Fix SwitchFs implementation
Fix NandPartition implementation
This commit is contained in:
shadowninja108 2018-12-08 16:58:30 -07:00
parent 0ba490b2dc
commit d61088b72f
5 changed files with 152 additions and 70 deletions

View file

@ -1,4 +1,5 @@
using System.IO; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using DiscUtils.Fat; using DiscUtils.Fat;
@ -8,46 +9,88 @@ namespace LibHac.Nand
{ {
public FatFileSystem Fs { get; } public FatFileSystem Fs { get; }
public override string PathSeperator => "/";
public override IDirectory RootDirectory => new IDirectory(this, "");
public NandPartition(FatFileSystem fileSystem) public NandPartition(FatFileSystem fileSystem)
{ {
Fs = 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[] files = Fs.GetFiles(directory.Path, searchPattern, searchOption);
string[] dirs = Fs.GetDirectories(path, searchPattern, searchOption); string[] dirs = Fs.GetDirectories(directory.Path, searchPattern, searchOption);
return files.Concat(dirs).ToArray(); 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) public string GetFullPath(string path)
{ {
return 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<IFile> files = new List<IFile>();
foreach (string file in Fs.GetFiles(directory.Path))
files.Add(GetFile(file));
return files.ToArray();
}
public override IDirectory[] GetDirectories(IDirectory directory)
{
List<IDirectory> directories = new List<IDirectory>();
foreach (string dir in Fs.GetDirectories(directory.Path))
directories.Add(GetDirectory(dir));
return directories.ToArray();
}
public override IFileSytemEntry[] GetEntries(IDirectory directory)
{
List<IFileSytemEntry> list = new List<IFileSytemEntry>();
list.AddRange(GetDirectories(directory));
list.AddRange(GetFiles(directory));
return list.ToArray();
}
} }
} }

View file

@ -17,11 +17,11 @@ namespace LibHac
} }
public abstract Stream OpenFile(IFile file, FileMode mode, FileAccess access); 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); 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) public IDirectory GetDirectory(string path)
{ {
@ -35,20 +35,28 @@ namespace LibHac
{ {
if (path.StartsWith(PathSeperator)) if (path.StartsWith(PathSeperator))
path = path.Substring(PathSeperator.Length); path = path.Substring(PathSeperator.Length);
return GetFile(path); return GetFileImpl(path);
} }
protected abstract IFile GetFileImpl(string path); protected abstract IFile GetFileImpl(string path);
public abstract IFile[] GetFiles(IDirectory directory); public abstract IFile[] GetFiles(IDirectory directory);
public abstract IDirectory[] GetDirectories(IDirectory directory); public abstract IDirectory[] GetDirectories(IDirectory directory);
public abstract IFileSytemEntry[] GetEntries(IDirectory directory);
} }
public class IDirectory public interface IFileSytemEntry
{ {
public IFileSystem FileSystem; IFileSystem FileSystem { get; }
public string Path; string Path { get; }
bool Exists { get; }
}
public class IDirectory : IFileSytemEntry
{
public IFileSystem FileSystem { get; }
public string Path { get; }
public IDirectory Parent public IDirectory Parent
{ {
@ -79,17 +87,28 @@ namespace LibHac
return FileSystem.GetDirectory(Path + FileSystem.PathSeperator + path); 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 IFileSystem FileSystem { get; }
public string Path; public string Path { get; }
public string Name => System.IO.Path.GetFileNameWithoutExtension(Path); public string Name => System.IO.Path.GetFileNameWithoutExtension(Path);
public string Extension => System.IO.Path.GetExtension(Path); public string Extension => System.IO.Path.GetExtension(Path);
public string FileName => System.IO.Path.GetFileName(Path); public string FileName => System.IO.Path.GetFileName(Path);
public bool Exists => FileSystem.FileExists(this);
public IDirectory Parent { public IDirectory Parent {
get get
{ {
@ -98,7 +117,6 @@ namespace LibHac
} }
} }
public bool Exists => FileSystem.FileExists(this);
public IFile(IFileSystem filesystem, string path) public IFile(IFileSystem filesystem, string path)
{ {

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
namespace LibHac namespace LibHac
{ {
@ -30,28 +31,18 @@ namespace LibHac
return new FileStream(GetFullPath(file), mode, access); 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<IFileSytemEntry>();
var result = new List<IFile>();
try DirectoryInfo root = new DirectoryInfo(GetFullPath(path));
foreach(FileSystemInfo info in root.EnumerateFileSystemInfos(searchPattern, searchOption))
{ {
result.AddRange(GetFileSystemEntries(path, searchPattern)); string relativePath = Util.GetRelativePath(info.FullName, Root);
} if (info.Attributes.HasFlag(FileAttributes.Directory))
catch (UnauthorizedAccessException) { /* Skip this directory */ } result.Add(new IDirectory(this, relativePath));
else
if (searchOption == SearchOption.TopDirectoryOnly) result.Add(new LocalFile(this, relativePath));
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 */ }
} }
return result.ToArray(); return result.ToArray();
@ -76,6 +67,14 @@ namespace LibHac
return directories; return directories;
} }
public override IFileSytemEntry[] GetEntries(IDirectory directory)
{
List<IFileSytemEntry> list = new List<IFileSytemEntry>();
list.AddRange(GetDirectories(directory));
list.AddRange(GetFiles(directory));
return list.ToArray();
}
protected override IDirectory GetPath(string path) protected override IDirectory GetPath(string path)
{ {
return new IDirectory(this, path); return new IDirectory(this, path);

View file

@ -58,20 +58,16 @@ namespace LibHac
private void OpenAllNcas() private void OpenAllNcas()
{ {
IFile[] files = ContentsDir.Files;
IDirectory[] directories = ContentsDir.Directories;
Dictionary<string, IStorage> storages = new Dictionary<string, IStorage>(); Dictionary<IFileSytemEntry, IStorage> storages = new Dictionary<IFileSytemEntry, IStorage>();
foreach (IFile file in files) foreach (IFileSytemEntry nca in ContentsDir.GetFileSystemEntries("*.nca", SearchOption.AllDirectories))
storages[file.Path] = file.Open(FileMode.Open, FileAccess.Read).AsStorage(); storages[nca] = OpenSplitNcaStream(nca);
foreach(IDirectory directory in directories)
storages[directory.Path] = OpenSplitNcaStream(Fs, directory);
foreach (KeyValuePair<string, IStorage> kv in storages) foreach (KeyValuePair<IFileSytemEntry, IStorage> kv in storages)
{ {
Nca nca = null; Nca nca = null;
string path = kv.Key; IFileSytemEntry file = kv.Key;
IStorage storage = kv.Value; IStorage storage = kv.Value;
try try
{ {
@ -87,7 +83,7 @@ namespace LibHac
if (isNax0) if (isNax0)
{ {
string sdPath = "/" + path.Replace('\\', '/'); string sdPath = Util.GetRelativePath(file.Path, ContentsDir.Path).Replace("\\", "/");
var nax0 = new Nax0(Keyset, storage, sdPath, false); var nax0 = new Nax0(Keyset, storage, sdPath, false);
nca = new Nca(Keyset, nax0.BaseStorage, false); nca = new Nca(Keyset, nax0.BaseStorage, false);
} }
@ -96,23 +92,23 @@ namespace LibHac
nca = new Nca(Keyset, storage, false); 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"; 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{path}"); } { Console.WriteLine($"{ex.Message} File:\n{file.Path}"); }
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: {path}"); Console.WriteLine($"{ex.Message}\nKey: {name}\nFile: {file.Path}");
} }
} }
catch (Exception ex) 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); if (nca?.NcaId != null) Ncas.Add(nca.NcaId, nca);
@ -123,7 +119,7 @@ namespace LibHac
{ {
if (SaveDir == null) return; if (SaveDir == null) return;
IFile[] files = Fs.GetFileSystemEntries(SaveDir, "*"); IFileSytemEntry[] files = Fs.GetFileSystemEntries(SaveDir, "*");
foreach (IFile file in files) foreach (IFile file in files)
{ {
@ -134,7 +130,7 @@ namespace LibHac
{ {
IStorage storage = Fs.OpenFile(file, FileMode.Open).AsStorage(); 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); var nax0 = new Nax0(Keyset, storage, sdPath, false);
save = new Savefile(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true); save = new Savefile(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
} }
@ -157,7 +153,15 @@ namespace LibHac
var title = new Title(); var title = new Title();
// Meta contents always have 1 Partition FS section with 1 file in it // 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); var pfs0 = new Pfs(sect);
IStorage file = pfs0.OpenFile(pfs0.Files[0]); 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<IFile>(); var files = new List<IFile>();
var storages = new List<IStorage>(); var storages = new List<IStorage>();
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}"); IFile file = (IFile)nca;
if (!partFile.Exists) break; 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 else
@ -261,7 +278,6 @@ namespace LibHac
if (files.Count == 1) if (files.Count == 1)
return files[0].Open(FileMode.Open, FileAccess.Read).AsStorage(); return files[0].Open(FileMode.Open, FileAccess.Read).AsStorage();
foreach (IFile file in files) foreach (IFile file in files)
storages.Add(file.Open( FileMode.Open, FileAccess.Read).AsStorage()); storages.Add(file.Open( FileMode.Open, FileAccess.Read).AsStorage());

View file

@ -10,7 +10,7 @@ namespace LibHac.Tests
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
LocalFileSystem fs = new LocalFileSystem("C:\\\\"); LocalFileSystem fs = new LocalFileSystem("G:\\\\");
foreach(IFile file in fs.RootDirectory.Files) foreach(IFile file in fs.RootDirectory.Files)
{ {
Console.WriteLine(file.Path); Console.WriteLine(file.Path);
@ -21,6 +21,12 @@ namespace LibHac.Tests
Console.WriteLine(directory.Path); 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(); Console.ReadKey();
} }
} }