Add SaveDataFileSystem

This commit is contained in:
Alex Barney 2019-01-04 21:00:56 -07:00
parent 9f181c7b45
commit f1f52e7151
18 changed files with 465 additions and 118 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
namespace LibHac.IO
{
@ -96,5 +97,29 @@ namespace LibHac.IO
logger?.SetTotal(0);
}
}
public static Stream AsStream(this IFile storage) => new NxFileStream(storage, true);
public static Stream AsStream(this IFile storage, bool keepOpen) => new NxFileStream(storage, keepOpen);
public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode)
{
return fs.OpenDirectory("/", OpenDirectoryMode.All).GetEntryCountRecursive(mode);
}
public static int GetEntryCountRecursive(this IDirectory directory, OpenDirectoryMode mode)
{
int count = 0;
foreach (DirectoryEntry entry in directory.EnumerateEntries())
{
if (entry.Type == DirectoryEntryType.Directory && (mode & OpenDirectoryMode.Directories) != 0 ||
entry.Type == DirectoryEntryType.File && (mode & OpenDirectoryMode.Files) != 0)
{
count++;
}
}
return count;
}
}
}

View file

@ -6,6 +6,7 @@ namespace LibHac.IO
{
IFileSystem ParentFileSystem { get; }
string FullPath { get; }
OpenDirectoryMode Mode { get; }
IEnumerable<DirectoryEntry> Read();
int GetEntryCount();

View file

@ -10,7 +10,7 @@ namespace LibHac.IO
public string FullPath { get; }
private string LocalPath { get; }
private OpenDirectoryMode Mode { get; }
public OpenDirectoryMode Mode { get; }
private DirectoryInfo DirInfo { get; }
public LocalDirectory(LocalFileSystem fs, string path, OpenDirectoryMode mode)

View file

@ -0,0 +1,75 @@
using System;
using System.IO;
namespace LibHac.IO
{
public class NxFileStream : Stream
{
private IFile BaseFile { get; }
private bool LeaveOpen { get; }
public NxFileStream(IFile baseFile, bool leaveOpen)
{
BaseFile = baseFile;
LeaveOpen = leaveOpen;
Length = baseFile.GetSize();
}
public override int Read(byte[] buffer, int offset, int count)
{
int toRead = (int)Math.Min(count, Length - Position);
BaseFile.Read(buffer.AsSpan(offset, count), Position);
Position += toRead;
return toRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
BaseFile.Write(buffer.AsSpan(offset, count), Position);
Position += count;
}
public override void Flush()
{
BaseFile.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
Position = offset;
break;
case SeekOrigin.Current:
Position += offset;
break;
case SeekOrigin.End:
Position = Length - offset;
break;
}
return Position;
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
// todo access
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => true;
public override long Length { get; }
public override long Position { get; set; }
protected override void Dispose(bool disposing)
{
if (!LeaveOpen) BaseFile?.Dispose();
base.Dispose(disposing);
}
}
}

View file

@ -9,7 +9,7 @@ namespace LibHac.IO
public PartitionFileSystem ParentFileSystem { get; }
public string FullPath { get; }
private OpenDirectoryMode Mode { get; }
public OpenDirectoryMode Mode { get; }
public PartitionDirectory(PartitionFileSystem fs, string path, OpenDirectoryMode mode)
{

View file

@ -9,7 +9,7 @@ namespace LibHac.IO
public string FullPath { get; }
private RomfsDir Directory { get; }
private OpenDirectoryMode Mode { get; }
public OpenDirectoryMode Mode { get; }
public RomFsDirectory(RomFsFileSystem fs, string path, OpenDirectoryMode mode)
{

View file

@ -35,7 +35,7 @@ namespace LibHac.IO.Save
public byte[] Data { get; }
public Header(Keyset keyset, IStorage storage)
public Header(IStorage storage, Keyset keyset)
{
MainStorage = storage;
MainHeader = MainStorage.Slice(0x100, 0x200);

View file

@ -0,0 +1,75 @@
using System.Collections.Generic;
namespace LibHac.IO.Save
{
class SaveDataDirectory : IDirectory
{
public IFileSystem ParentFileSystem { get; }
public string FullPath { get; }
public OpenDirectoryMode Mode { get; }
private SaveDirectoryEntry Directory { get; }
public SaveDataDirectory(SaveDataFileSystemCore fs, string path, SaveDirectoryEntry dir, OpenDirectoryMode mode)
{
ParentFileSystem = fs;
Directory = dir;
FullPath = path;
Mode = mode;
}
public IEnumerable<DirectoryEntry> Read()
{
if (Mode.HasFlag(OpenDirectoryMode.Directories))
{
SaveDirectoryEntry dirEntry = Directory.FirstChild;
while (dirEntry != null)
{
yield return new DirectoryEntry(dirEntry.Name, FullPath + '/' + dirEntry.Name, DirectoryEntryType.Directory, 0);
dirEntry = dirEntry.NextSibling;
}
}
if (Mode.HasFlag(OpenDirectoryMode.Files))
{
SaveFileEntry fileEntry = Directory.FirstFile;
while (fileEntry != null)
{
yield return new DirectoryEntry(fileEntry.Name, FullPath + '/' + fileEntry.Name, DirectoryEntryType.File, fileEntry.FileSize);
fileEntry = fileEntry.NextSibling;
}
}
}
public int GetEntryCount()
{
int count = 0;
if (Mode.HasFlag(OpenDirectoryMode.Directories))
{
SaveDirectoryEntry dirEntry = Directory.FirstChild;
while (dirEntry != null)
{
count++;
dirEntry = dirEntry.NextSibling;
}
}
if (Mode.HasFlag(OpenDirectoryMode.Files))
{
SaveFileEntry fileEntry = Directory.FirstFile;
while (fileEntry != null)
{
count++;
fileEntry = fileEntry.NextSibling;
}
}
return count;
}
}
}

View file

@ -0,0 +1,51 @@
using System;
namespace LibHac.IO.Save
{
public class SaveDataFile : FileBase
{
private AllocationTableStorage BaseStorage { get; }
private long Offset { get; }
private long Size { get; }
public SaveDataFile(AllocationTableStorage baseStorage, long offset, long size, OpenMode mode)
{
Mode = mode;
BaseStorage = baseStorage;
Offset = offset;
Size = size;
}
public override int Read(Span<byte> destination, long offset)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
long storageOffset = Offset + offset;
BaseStorage.Read(destination.Slice(0, toRead), storageOffset);
return toRead;
}
public override void Write(ReadOnlySpan<byte> source, long offset)
{
ValidateWriteParams(source, offset);
BaseStorage.Write(source, offset);
}
public override void Flush()
{
BaseStorage.Flush();
}
public override long GetSize()
{
return Size;
}
public override void SetSize(long size)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
namespace LibHac.IO.Save
{
public class SaveData : IDisposable
public class SaveDataFileSystem : IFileSystem
{
public Header Header { get; }
public IStorage BaseStorage { get; }
@ -19,16 +18,15 @@ namespace LibHac.IO.Save
public HierarchicalDuplexStorage DuplexStorage { get; }
public JournalStorage JournalStorage { get; }
public DirectoryEntry RootDirectory => SaveDataFileSystemCore.RootDirectory;
public FileEntry[] Files => SaveDataFileSystemCore.Files;
public DirectoryEntry[] Directories => SaveDataFileSystemCore.Directories;
private Keyset Keyset { get; }
public SaveData(Keyset keyset, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
public SaveDataFileSystem(Keyset keyset, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
{
BaseStorage = storage;
LeaveOpen = leaveOpen;
Keyset = keyset;
Header = new Header(keyset, BaseStorage);
Header = new Header(BaseStorage, keyset);
FsLayout layout = Header.Layout;
IStorage dataRemapBase = BaseStorage.Slice(layout.FileMapDataOffset, layout.FileMapDataSize);
@ -119,21 +117,56 @@ namespace LibHac.IO.Save
IntegrityStorageType.Save, integrityCheckLevel, LeaveOpen);
}
public IStorage OpenFile(string filename)
public void CreateDirectory(string path)
{
return SaveDataFileSystemCore.OpenFile(filename);
throw new System.NotImplementedException();
}
public IStorage OpenFile(FileEntry file)
public void CreateFile(string path, long size)
{
return SaveDataFileSystemCore.OpenFile(file);
throw new System.NotImplementedException();
}
public void DeleteDirectory(string path)
{
throw new System.NotImplementedException();
}
public void DeleteFile(string path)
{
throw new System.NotImplementedException();
}
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
{
return SaveDataFileSystemCore.OpenDirectory(path, mode);
}
public IFile OpenFile(string path, OpenMode mode)
{
return SaveDataFileSystemCore.OpenFile(path, mode);
}
public void RenameDirectory(string srcPath, string dstPath)
{
throw new System.NotImplementedException();
}
public void RenameFile(string srcPath, string dstPath)
{
throw new System.NotImplementedException();
}
public bool DirectoryExists(string path) => SaveDataFileSystemCore.DirectoryExists(path);
public bool FileExists(string filename) => SaveDataFileSystemCore.FileExists(filename);
public bool CommitHeader(Keyset keyset)
public void Commit()
{
Commit(Keyset);
}
public bool Commit(Keyset keyset)
{
// todo
Stream headerStream = BaseStorage.AsStream();
var hashData = new byte[0x3d00];
@ -145,7 +178,7 @@ namespace LibHac.IO.Save
headerStream.Position = 0x108;
headerStream.Write(hash, 0, hash.Length);
if (keyset.SaveMacKey.IsEmpty()) return false;
if (keyset == null || keyset.SaveMacKey.IsEmpty()) return false;
var cmacData = new byte[0x200];
var cmac = new byte[0x10];
@ -169,39 +202,5 @@ namespace LibHac.IO.Save
return validity;
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !LeaveOpen)
{
BaseStorage?.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
public static class SavefileExtensions
{
public static void Extract(this SaveData save, string outDir, IProgressReport logger = null)
{
foreach (FileEntry file in save.Files)
{
IStorage storage = save.OpenFile(file);
string outName = outDir + file.FullPath;
string dir = Path.GetDirectoryName(outName);
if (!string.IsNullOrWhiteSpace(dir)) Directory.CreateDirectory(dir);
using (var outFile = new FileStream(outName, FileMode.Create, FileAccess.ReadWrite))
{
logger?.LogMessage(file.FullPath);
storage.CopyToStream(outFile, storage.Length, logger);
}
}
}
}
}

View file

@ -3,7 +3,7 @@ using System.IO;
namespace LibHac.IO.Save
{
public class SaveDataFileSystemCore
public class SaveDataFileSystemCore : IFileSystem
{
private IStorage BaseStorage { get; }
private IStorage HeaderStorage { get; }
@ -11,10 +11,11 @@ namespace LibHac.IO.Save
public AllocationTable AllocationTable { get; }
private SaveHeader Header { get; }
public DirectoryEntry RootDirectory { get; private set; }
public FileEntry[] Files { get; private set; }
public DirectoryEntry[] Directories { get; private set; }
public Dictionary<string, FileEntry> FileDictionary { get; }
public SaveDirectoryEntry RootDirectory { get; private set; }
private SaveFileEntry[] Files { get; set; }
private SaveDirectoryEntry[] Directories { get; set; }
private Dictionary<string, SaveFileEntry> FileDictionary { get; }
private Dictionary<string, SaveDirectoryEntry> DirDictionary { get; }
public SaveDataFileSystemCore(IStorage storage, IStorage allocationTable, IStorage header)
{
@ -25,18 +26,23 @@ namespace LibHac.IO.Save
Header = new SaveHeader(HeaderStorage);
ReadFileInfo();
var dictionary = new Dictionary<string, FileEntry>();
foreach (FileEntry entry in Files)
FileDictionary = new Dictionary<string, SaveFileEntry>();
foreach (SaveFileEntry entry in Files)
{
dictionary[entry.FullPath] = entry;
FileDictionary[entry.FullPath] = entry;
}
FileDictionary = dictionary;
DirDictionary = new Dictionary<string, SaveDirectoryEntry>();
foreach (SaveDirectoryEntry entry in Directories)
{
DirDictionary[entry.FullPath] = entry;
}
}
public IStorage OpenFile(string filename)
{
if (!FileDictionary.TryGetValue(filename, out FileEntry file))
if (!FileDictionary.TryGetValue(filename, out SaveFileEntry file))
{
throw new FileNotFoundException();
}
@ -44,7 +50,7 @@ namespace LibHac.IO.Save
return OpenFile(file);
}
public IStorage OpenFile(FileEntry file)
public IStorage OpenFile(SaveFileEntry file)
{
if (file.BlockIndex < 0)
{
@ -55,7 +61,76 @@ namespace LibHac.IO.Save
return OpenFatBlock(file.BlockIndex, file.FileSize);
}
public void CreateDirectory(string path)
{
throw new System.NotImplementedException();
}
public void CreateFile(string path, long size)
{
throw new System.NotImplementedException();
}
public void DeleteDirectory(string path)
{
throw new System.NotImplementedException();
}
public void DeleteFile(string path)
{
throw new System.NotImplementedException();
}
public IDirectory OpenDirectory(string path, OpenDirectoryMode mode)
{
path = PathTools.Normalize(path);
if (!DirDictionary.TryGetValue(path, out SaveDirectoryEntry dir))
{
throw new DirectoryNotFoundException(path);
}
return new SaveDataDirectory(this, path, dir, mode);
}
public IFile OpenFile(string path, OpenMode mode)
{
if (!FileDictionary.TryGetValue(path, out SaveFileEntry file))
{
throw new FileNotFoundException();
}
if (file.BlockIndex < 0)
{
// todo
return new StorageFile(new MemoryStorage(new byte[0]), OpenMode.ReadWrite);
}
AllocationTableStorage storage = OpenFatBlock(file.BlockIndex, file.FileSize);
return new SaveDataFile(storage, 0, file.FileSize, mode);
}
public void RenameDirectory(string srcPath, string dstPath)
{
throw new System.NotImplementedException();
}
public void RenameFile(string srcPath, string dstPath)
{
throw new System.NotImplementedException();
}
public bool DirectoryExists(string path)
{
throw new System.NotImplementedException();
}
public bool FileExists(string filename) => FileDictionary.ContainsKey(filename);
public void Commit()
{
throw new System.NotImplementedException();
}
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
public IStorage GetHeaderStorage() => HeaderStorage.WithAccess(FileAccess.Read);
@ -66,10 +141,10 @@ namespace LibHac.IO.Save
AllocationTableStorage dirTableStream = OpenFatBlock(AllocationTable.Header.DirectoryTableBlock, 1000000);
AllocationTableStorage fileTableStream = OpenFatBlock(AllocationTable.Header.FileTableBlock, 1000000);
DirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
FileEntry[] fileEntries = ReadFileEntries(fileTableStream);
SaveDirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
SaveFileEntry[] fileEntries = ReadFileEntries(fileTableStream);
foreach (DirectoryEntry dir in dirEntries)
foreach (SaveDirectoryEntry dir in dirEntries)
{
if (dir.NextSiblingIndex != 0) dir.NextSibling = dirEntries[dir.NextSiblingIndex];
if (dir.FirstChildIndex != 0) dir.FirstChild = dirEntries[dir.FirstChildIndex];
@ -79,7 +154,7 @@ namespace LibHac.IO.Save
dir.ParentDir = dirEntries[dir.ParentDirIndex];
}
foreach (FileEntry file in fileEntries)
foreach (SaveFileEntry file in fileEntries)
{
if (file.NextSiblingIndex != 0) file.NextSibling = fileEntries[file.NextSiblingIndex];
if (file.NextInChainIndex != 0) file.NextInChain = fileEntries[file.NextInChainIndex];
@ -89,16 +164,16 @@ namespace LibHac.IO.Save
RootDirectory = dirEntries[2];
FileEntry fileChain = fileEntries[1].NextInChain;
var files = new List<FileEntry>();
SaveFileEntry fileChain = fileEntries[1].NextInChain;
var files = new List<SaveFileEntry>();
while (fileChain != null)
{
files.Add(fileChain);
fileChain = fileChain.NextInChain;
}
DirectoryEntry dirChain = dirEntries[1].NextInChain;
var dirs = new List<DirectoryEntry>();
SaveDirectoryEntry dirChain = dirEntries[1].NextInChain;
var dirs = new List<SaveDirectoryEntry>();
while (dirChain != null)
{
dirs.Add(dirChain);
@ -108,37 +183,37 @@ namespace LibHac.IO.Save
Files = files.ToArray();
Directories = dirs.ToArray();
FsEntry.ResolveFilenames(Files);
FsEntry.ResolveFilenames(Directories);
SaveFsEntry.ResolveFilenames(Files);
SaveFsEntry.ResolveFilenames(Directories);
}
private FileEntry[] ReadFileEntries(IStorage storage)
private SaveFileEntry[] ReadFileEntries(IStorage storage)
{
var reader = new BinaryReader(storage.AsStream());
int count = reader.ReadInt32();
reader.BaseStream.Position -= 4;
var entries = new FileEntry[count];
var entries = new SaveFileEntry[count];
for (int i = 0; i < count; i++)
{
entries[i] = new FileEntry(reader);
entries[i] = new SaveFileEntry(reader);
}
return entries;
}
private DirectoryEntry[] ReadDirEntries(IStorage storage)
private SaveDirectoryEntry[] ReadDirEntries(IStorage storage)
{
var reader = new BinaryReader(storage.AsStream());
int count = reader.ReadInt32();
reader.BaseStream.Position -= 4;
var entries = new DirectoryEntry[count];
var entries = new SaveDirectoryEntry[count];
for (int i = 0; i < count; i++)
{
entries[i] = new DirectoryEntry(reader);
entries[i] = new SaveDirectoryEntry(reader);
}
return entries;

View file

@ -6,23 +6,23 @@ using System.Text;
namespace LibHac.IO.Save
{
[DebuggerDisplay("{" + nameof(FullPath) + "}")]
public abstract class FsEntry
public abstract class SaveFsEntry
{
public int ParentDirIndex { get; protected set; }
public string Name { get; protected set; }
public string FullPath { get; private set; }
public DirectoryEntry ParentDir { get; internal set; }
public SaveDirectoryEntry ParentDir { get; internal set; }
internal static void ResolveFilenames(IEnumerable<FsEntry> entries)
internal static void ResolveFilenames(IEnumerable<SaveFsEntry> entries)
{
var list = new List<string>();
var sb = new StringBuilder();
string delimiter = "/";
foreach (FsEntry file in entries)
foreach (SaveFsEntry file in entries)
{
list.Add(file.Name);
DirectoryEntry dir = file.ParentDir;
SaveDirectoryEntry dir = file.ParentDir;
while (dir != null)
{
list.Add(delimiter);
@ -35,14 +35,14 @@ namespace LibHac.IO.Save
sb.Append(list[i]);
}
file.FullPath = sb.ToString();
file.FullPath = sb.Length == 0 ? delimiter : sb.ToString();
list.Clear();
sb.Clear();
}
}
}
public class FileEntry : FsEntry
public class SaveFileEntry : SaveFsEntry
{
public int NextSiblingIndex { get; }
public int BlockIndex { get; }
@ -50,10 +50,10 @@ namespace LibHac.IO.Save
public long Field54 { get; }
public int NextInChainIndex { get; }
public FileEntry NextSibling { get; internal set; }
public FileEntry NextInChain { get; internal set; }
public SaveFileEntry NextSibling { get; internal set; }
public SaveFileEntry NextInChain { get; internal set; }
public FileEntry(BinaryReader reader)
public SaveFileEntry(BinaryReader reader)
{
long start = reader.BaseStream.Position;
ParentDirIndex = reader.ReadInt32();
@ -68,7 +68,7 @@ namespace LibHac.IO.Save
}
}
public class DirectoryEntry : FsEntry
public class SaveDirectoryEntry : SaveFsEntry
{
public int NextSiblingIndex { get; }
public int FirstChildIndex { get; }
@ -76,12 +76,12 @@ namespace LibHac.IO.Save
public long Field54 { get; }
public int NextInChainIndex { get; }
public DirectoryEntry NextSibling { get; internal set; }
public DirectoryEntry FirstChild { get; internal set; }
public FileEntry FirstFile { get; internal set; }
public DirectoryEntry NextInChain { get; internal set; }
public SaveDirectoryEntry NextSibling { get; internal set; }
public SaveDirectoryEntry FirstChild { get; internal set; }
public SaveFileEntry FirstFile { get; internal set; }
public SaveDirectoryEntry NextInChain { get; internal set; }
public DirectoryEntry(BinaryReader reader)
public SaveDirectoryEntry(BinaryReader reader)
{
long start = reader.BaseStream.Position;
ParentDirIndex = reader.ReadInt32();

View file

@ -0,0 +1,44 @@
using System;
namespace LibHac.IO
{
public class StorageFile : FileBase
{
private IStorage BaseStorage { get; }
public StorageFile(IStorage baseStorage, OpenMode mode)
{
BaseStorage = baseStorage;
Mode = mode;
}
public override int Read(Span<byte> destination, long offset)
{
int toRead = ValidateReadParamsAndGetSize(destination, offset);
BaseStorage.Read(destination.Slice(0, toRead), offset);
return toRead;
}
public override void Write(ReadOnlySpan<byte> source, long offset)
{
BaseStorage.Write(source, offset);
}
public override void Flush()
{
BaseStorage.Flush();
}
public override long GetSize()
{
return BaseStorage.Length;
}
public override void SetSize(long size)
{
throw new NotImplementedException();
}
}
}

View file

@ -17,7 +17,7 @@ namespace LibHac
public string SaveDir { get; }
public Dictionary<string, Nca> Ncas { get; } = new Dictionary<string, Nca>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, SaveData> Saves { get; } = new Dictionary<string, SaveData>(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, Application> Applications { get; } = new Dictionary<ulong, Application>();
@ -118,7 +118,7 @@ namespace LibHac
foreach (string file in files)
{
SaveData save = null;
SaveDataFileSystem save = null;
string saveName = Path.GetFileNameWithoutExtension(file);
try
@ -127,7 +127,7 @@ namespace LibHac
string sdPath = "/" + Util.GetRelativePath(file, SaveDir).Replace('\\', '/');
var nax0 = new Nax0(Keyset, storage, sdPath, false);
save = new SaveData(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
save = new SaveDataFileSystem(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
}
catch (Exception ex)
{

View file

@ -103,9 +103,9 @@ namespace NandReader
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
{
var tickets = new List<Ticket>();
var save = new SaveData(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin").AsStream());
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin").AsStream());
var save = new SaveDataFileSystem(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
ulong titleId = ticketList.ReadUInt64();
while (titleId != ulong.MaxValue)

View file

@ -87,9 +87,9 @@ namespace NandReaderGui.ViewModel
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
{
var tickets = new List<Ticket>();
var save = new SaveData(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin").AsStream());
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin").AsStream());
var save = new SaveDataFileSystem(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
ulong titleId = ticketList.ReadUInt64();
while (titleId != ulong.MaxValue)

View file

@ -14,7 +14,7 @@ namespace hactoolnet
{
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
{
var save = new SaveData(ctx.Keyset, file.AsStorage(), ctx.Options.IntegrityLevel, true);
var save = new SaveDataFileSystem(ctx.Keyset, file.AsStorage(), ctx.Options.IntegrityLevel, true);
if (ctx.Options.Validate)
{
@ -23,7 +23,7 @@ namespace hactoolnet
if (ctx.Options.OutDir != null)
{
save.Extract(ctx.Options.OutDir, ctx.Logger);
save.SaveDataFileSystemCore.Extract(ctx.Options.OutDir, ctx.Logger);
}
if (ctx.Options.DebugOutDir != null)
@ -99,13 +99,13 @@ namespace hactoolnet
string destFilename = ctx.Options.ReplaceFileDest;
if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename;
using (IStorage inFile = new FileStream(ctx.Options.ReplaceFileSource, FileMode.Open, FileAccess.Read).AsStorage(false))
using (IFile inFile = new StorageFile(new FileStream(ctx.Options.ReplaceFileSource, FileMode.Open, FileAccess.Read).AsStorage(false), OpenMode.ReadWrite))
{
using (IStorage outFile = save.OpenFile(destFilename))
using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite))
{
if (inFile.Length != outFile.Length)
if (inFile.GetSize() != outFile.GetSize())
{
ctx.Logger.LogMessage($"Replacement file must be the same size as the original file. ({outFile.Length} bytes)");
ctx.Logger.LogMessage($"Replacement file must be the same size as the original file. ({outFile.GetSize()} bytes)");
return;
}
@ -115,7 +115,7 @@ namespace hactoolnet
}
}
if (save.CommitHeader(ctx.Keyset))
if (save.Commit(ctx.Keyset))
{
ctx.Logger.LogMessage("Successfully signed save file");
}
@ -129,7 +129,7 @@ namespace hactoolnet
if (ctx.Options.SignSave)
{
if (save.CommitHeader(ctx.Keyset))
if (save.Commit(ctx.Keyset))
{
ctx.Logger.LogMessage("Successfully signed save file");
}
@ -143,9 +143,11 @@ namespace hactoolnet
if (ctx.Options.ListFiles)
{
foreach (FileEntry fileEntry in save.Files)
IDirectory dir = save.SaveDataFileSystemCore.OpenDirectory("/", OpenDirectoryMode.All);
foreach (DirectoryEntry entry in dir.EnumerateEntries())
{
ctx.Logger.LogMessage(fileEntry.FullPath);
ctx.Logger.LogMessage(entry.FullPath);
}
}
@ -153,7 +155,7 @@ namespace hactoolnet
}
}
private static string Print(this SaveData save)
private static string Print(this SaveDataFileSystem save)
{
int colLen = 25;
var sb = new StringBuilder();
@ -170,7 +172,7 @@ namespace hactoolnet
PrintItem(sb, colLen, "Save Data Size:", $"0x{save.Header.ExtraData.DataSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.DataSize)})");
PrintItem(sb, colLen, "Journal Size:", $"0x{save.Header.ExtraData.JournalSize:x16} ({Util.GetBytesReadable(save.Header.ExtraData.JournalSize)})");
PrintItem(sb, colLen, $"Header Hash{save.Header.HeaderHashValidity.GetValidityString()}:", save.Header.Layout.Hash);
PrintItem(sb, colLen, "Number of Files:", save.Files.Length);
PrintItem(sb, colLen, "Number of Files:", save.GetEntryCount(OpenDirectoryMode.Files));
PrintIvfcHash(sb, colLen, 4, save.Header.Ivfc, IntegrityStorageType.Save);

View file

@ -286,7 +286,7 @@ namespace hactoolnet
private static void ExportSdSaves(Context ctx, SwitchFs switchFs)
{
foreach (KeyValuePair<string, SaveData> save in switchFs.Saves)
foreach (KeyValuePair<string, SaveDataFileSystem> save in switchFs.Saves)
{
string outDir = Path.Combine(ctx.Options.SaveOutDir, save.Key);
save.Value.Extract(outDir, ctx.Logger);