mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add SaveDataFileSystem
This commit is contained in:
parent
9f181c7b45
commit
f1f52e7151
18 changed files with 465 additions and 118 deletions
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace LibHac.IO
|
namespace LibHac.IO
|
||||||
{
|
{
|
||||||
|
@ -96,5 +97,29 @@ namespace LibHac.IO
|
||||||
logger?.SetTotal(0);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace LibHac.IO
|
||||||
{
|
{
|
||||||
IFileSystem ParentFileSystem { get; }
|
IFileSystem ParentFileSystem { get; }
|
||||||
string FullPath { get; }
|
string FullPath { get; }
|
||||||
|
OpenDirectoryMode Mode { get; }
|
||||||
|
|
||||||
IEnumerable<DirectoryEntry> Read();
|
IEnumerable<DirectoryEntry> Read();
|
||||||
int GetEntryCount();
|
int GetEntryCount();
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace LibHac.IO
|
||||||
public string FullPath { get; }
|
public string FullPath { get; }
|
||||||
|
|
||||||
private string LocalPath { get; }
|
private string LocalPath { get; }
|
||||||
private OpenDirectoryMode Mode { get; }
|
public OpenDirectoryMode Mode { get; }
|
||||||
private DirectoryInfo DirInfo { get; }
|
private DirectoryInfo DirInfo { get; }
|
||||||
|
|
||||||
public LocalDirectory(LocalFileSystem fs, string path, OpenDirectoryMode mode)
|
public LocalDirectory(LocalFileSystem fs, string path, OpenDirectoryMode mode)
|
||||||
|
|
75
src/LibHac/IO/NxFileStream.cs
Normal file
75
src/LibHac/IO/NxFileStream.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ namespace LibHac.IO
|
||||||
public PartitionFileSystem ParentFileSystem { get; }
|
public PartitionFileSystem ParentFileSystem { get; }
|
||||||
public string FullPath { get; }
|
public string FullPath { get; }
|
||||||
|
|
||||||
private OpenDirectoryMode Mode { get; }
|
public OpenDirectoryMode Mode { get; }
|
||||||
|
|
||||||
public PartitionDirectory(PartitionFileSystem fs, string path, OpenDirectoryMode mode)
|
public PartitionDirectory(PartitionFileSystem fs, string path, OpenDirectoryMode mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace LibHac.IO
|
||||||
public string FullPath { get; }
|
public string FullPath { get; }
|
||||||
|
|
||||||
private RomfsDir Directory { get; }
|
private RomfsDir Directory { get; }
|
||||||
private OpenDirectoryMode Mode { get; }
|
public OpenDirectoryMode Mode { get; }
|
||||||
|
|
||||||
public RomFsDirectory(RomFsFileSystem fs, string path, OpenDirectoryMode mode)
|
public RomFsDirectory(RomFsFileSystem fs, string path, OpenDirectoryMode mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace LibHac.IO.Save
|
||||||
|
|
||||||
public byte[] Data { get; }
|
public byte[] Data { get; }
|
||||||
|
|
||||||
public Header(Keyset keyset, IStorage storage)
|
public Header(IStorage storage, Keyset keyset)
|
||||||
{
|
{
|
||||||
MainStorage = storage;
|
MainStorage = storage;
|
||||||
MainHeader = MainStorage.Slice(0x100, 0x200);
|
MainHeader = MainStorage.Slice(0x100, 0x200);
|
||||||
|
|
75
src/LibHac/IO/Save/SaveDataDirectory.cs
Normal file
75
src/LibHac/IO/Save/SaveDataDirectory.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/LibHac/IO/Save/SaveDataFile.cs
Normal file
51
src/LibHac/IO/Save/SaveDataFile.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace LibHac.IO.Save
|
namespace LibHac.IO.Save
|
||||||
{
|
{
|
||||||
public class SaveData : IDisposable
|
public class SaveDataFileSystem : IFileSystem
|
||||||
{
|
{
|
||||||
public Header Header { get; }
|
public Header Header { get; }
|
||||||
public IStorage BaseStorage { get; }
|
public IStorage BaseStorage { get; }
|
||||||
|
@ -19,16 +18,15 @@ namespace LibHac.IO.Save
|
||||||
public HierarchicalDuplexStorage DuplexStorage { get; }
|
public HierarchicalDuplexStorage DuplexStorage { get; }
|
||||||
public JournalStorage JournalStorage { get; }
|
public JournalStorage JournalStorage { get; }
|
||||||
|
|
||||||
public DirectoryEntry RootDirectory => SaveDataFileSystemCore.RootDirectory;
|
private Keyset Keyset { get; }
|
||||||
public FileEntry[] Files => SaveDataFileSystemCore.Files;
|
|
||||||
public DirectoryEntry[] Directories => SaveDataFileSystemCore.Directories;
|
|
||||||
|
|
||||||
public SaveData(Keyset keyset, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
public SaveDataFileSystem(Keyset keyset, IStorage storage, IntegrityCheckLevel integrityCheckLevel, bool leaveOpen)
|
||||||
{
|
{
|
||||||
BaseStorage = storage;
|
BaseStorage = storage;
|
||||||
LeaveOpen = leaveOpen;
|
LeaveOpen = leaveOpen;
|
||||||
|
Keyset = keyset;
|
||||||
|
|
||||||
Header = new Header(keyset, BaseStorage);
|
Header = new Header(BaseStorage, keyset);
|
||||||
FsLayout layout = Header.Layout;
|
FsLayout layout = Header.Layout;
|
||||||
|
|
||||||
IStorage dataRemapBase = BaseStorage.Slice(layout.FileMapDataOffset, layout.FileMapDataSize);
|
IStorage dataRemapBase = BaseStorage.Slice(layout.FileMapDataOffset, layout.FileMapDataSize);
|
||||||
|
@ -119,21 +117,56 @@ namespace LibHac.IO.Save
|
||||||
IntegrityStorageType.Save, integrityCheckLevel, LeaveOpen);
|
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 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();
|
Stream headerStream = BaseStorage.AsStream();
|
||||||
|
|
||||||
var hashData = new byte[0x3d00];
|
var hashData = new byte[0x3d00];
|
||||||
|
@ -145,7 +178,7 @@ namespace LibHac.IO.Save
|
||||||
headerStream.Position = 0x108;
|
headerStream.Position = 0x108;
|
||||||
headerStream.Write(hash, 0, hash.Length);
|
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 cmacData = new byte[0x200];
|
||||||
var cmac = new byte[0x10];
|
var cmac = new byte[0x10];
|
||||||
|
@ -169,39 +202,5 @@ namespace LibHac.IO.Save
|
||||||
|
|
||||||
return validity;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ using System.IO;
|
||||||
|
|
||||||
namespace LibHac.IO.Save
|
namespace LibHac.IO.Save
|
||||||
{
|
{
|
||||||
public class SaveDataFileSystemCore
|
public class SaveDataFileSystemCore : IFileSystem
|
||||||
{
|
{
|
||||||
private IStorage BaseStorage { get; }
|
private IStorage BaseStorage { get; }
|
||||||
private IStorage HeaderStorage { get; }
|
private IStorage HeaderStorage { get; }
|
||||||
|
@ -11,10 +11,11 @@ namespace LibHac.IO.Save
|
||||||
public AllocationTable AllocationTable { get; }
|
public AllocationTable AllocationTable { get; }
|
||||||
private SaveHeader Header { get; }
|
private SaveHeader Header { get; }
|
||||||
|
|
||||||
public DirectoryEntry RootDirectory { get; private set; }
|
public SaveDirectoryEntry RootDirectory { get; private set; }
|
||||||
public FileEntry[] Files { get; private set; }
|
private SaveFileEntry[] Files { get; set; }
|
||||||
public DirectoryEntry[] Directories { get; private set; }
|
private SaveDirectoryEntry[] Directories { get; set; }
|
||||||
public Dictionary<string, FileEntry> FileDictionary { get; }
|
private Dictionary<string, SaveFileEntry> FileDictionary { get; }
|
||||||
|
private Dictionary<string, SaveDirectoryEntry> DirDictionary { get; }
|
||||||
|
|
||||||
public SaveDataFileSystemCore(IStorage storage, IStorage allocationTable, IStorage header)
|
public SaveDataFileSystemCore(IStorage storage, IStorage allocationTable, IStorage header)
|
||||||
{
|
{
|
||||||
|
@ -25,18 +26,23 @@ namespace LibHac.IO.Save
|
||||||
Header = new SaveHeader(HeaderStorage);
|
Header = new SaveHeader(HeaderStorage);
|
||||||
|
|
||||||
ReadFileInfo();
|
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)
|
public IStorage OpenFile(string filename)
|
||||||
{
|
{
|
||||||
if (!FileDictionary.TryGetValue(filename, out FileEntry file))
|
if (!FileDictionary.TryGetValue(filename, out SaveFileEntry file))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +50,7 @@ namespace LibHac.IO.Save
|
||||||
return OpenFile(file);
|
return OpenFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IStorage OpenFile(FileEntry file)
|
public IStorage OpenFile(SaveFileEntry file)
|
||||||
{
|
{
|
||||||
if (file.BlockIndex < 0)
|
if (file.BlockIndex < 0)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +61,76 @@ namespace LibHac.IO.Save
|
||||||
return OpenFatBlock(file.BlockIndex, file.FileSize);
|
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 bool FileExists(string filename) => FileDictionary.ContainsKey(filename);
|
||||||
|
public void Commit()
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
public IStorage GetBaseStorage() => BaseStorage.WithAccess(FileAccess.Read);
|
||||||
public IStorage GetHeaderStorage() => HeaderStorage.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 dirTableStream = OpenFatBlock(AllocationTable.Header.DirectoryTableBlock, 1000000);
|
||||||
AllocationTableStorage fileTableStream = OpenFatBlock(AllocationTable.Header.FileTableBlock, 1000000);
|
AllocationTableStorage fileTableStream = OpenFatBlock(AllocationTable.Header.FileTableBlock, 1000000);
|
||||||
|
|
||||||
DirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
|
SaveDirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
|
||||||
FileEntry[] fileEntries = ReadFileEntries(fileTableStream);
|
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.NextSiblingIndex != 0) dir.NextSibling = dirEntries[dir.NextSiblingIndex];
|
||||||
if (dir.FirstChildIndex != 0) dir.FirstChild = dirEntries[dir.FirstChildIndex];
|
if (dir.FirstChildIndex != 0) dir.FirstChild = dirEntries[dir.FirstChildIndex];
|
||||||
|
@ -79,7 +154,7 @@ namespace LibHac.IO.Save
|
||||||
dir.ParentDir = dirEntries[dir.ParentDirIndex];
|
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.NextSiblingIndex != 0) file.NextSibling = fileEntries[file.NextSiblingIndex];
|
||||||
if (file.NextInChainIndex != 0) file.NextInChain = fileEntries[file.NextInChainIndex];
|
if (file.NextInChainIndex != 0) file.NextInChain = fileEntries[file.NextInChainIndex];
|
||||||
|
@ -89,16 +164,16 @@ namespace LibHac.IO.Save
|
||||||
|
|
||||||
RootDirectory = dirEntries[2];
|
RootDirectory = dirEntries[2];
|
||||||
|
|
||||||
FileEntry fileChain = fileEntries[1].NextInChain;
|
SaveFileEntry fileChain = fileEntries[1].NextInChain;
|
||||||
var files = new List<FileEntry>();
|
var files = new List<SaveFileEntry>();
|
||||||
while (fileChain != null)
|
while (fileChain != null)
|
||||||
{
|
{
|
||||||
files.Add(fileChain);
|
files.Add(fileChain);
|
||||||
fileChain = fileChain.NextInChain;
|
fileChain = fileChain.NextInChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectoryEntry dirChain = dirEntries[1].NextInChain;
|
SaveDirectoryEntry dirChain = dirEntries[1].NextInChain;
|
||||||
var dirs = new List<DirectoryEntry>();
|
var dirs = new List<SaveDirectoryEntry>();
|
||||||
while (dirChain != null)
|
while (dirChain != null)
|
||||||
{
|
{
|
||||||
dirs.Add(dirChain);
|
dirs.Add(dirChain);
|
||||||
|
@ -108,37 +183,37 @@ namespace LibHac.IO.Save
|
||||||
Files = files.ToArray();
|
Files = files.ToArray();
|
||||||
Directories = dirs.ToArray();
|
Directories = dirs.ToArray();
|
||||||
|
|
||||||
FsEntry.ResolveFilenames(Files);
|
SaveFsEntry.ResolveFilenames(Files);
|
||||||
FsEntry.ResolveFilenames(Directories);
|
SaveFsEntry.ResolveFilenames(Directories);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileEntry[] ReadFileEntries(IStorage storage)
|
private SaveFileEntry[] ReadFileEntries(IStorage storage)
|
||||||
{
|
{
|
||||||
var reader = new BinaryReader(storage.AsStream());
|
var reader = new BinaryReader(storage.AsStream());
|
||||||
int count = reader.ReadInt32();
|
int count = reader.ReadInt32();
|
||||||
|
|
||||||
reader.BaseStream.Position -= 4;
|
reader.BaseStream.Position -= 4;
|
||||||
|
|
||||||
var entries = new FileEntry[count];
|
var entries = new SaveFileEntry[count];
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
entries[i] = new FileEntry(reader);
|
entries[i] = new SaveFileEntry(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private DirectoryEntry[] ReadDirEntries(IStorage storage)
|
private SaveDirectoryEntry[] ReadDirEntries(IStorage storage)
|
||||||
{
|
{
|
||||||
var reader = new BinaryReader(storage.AsStream());
|
var reader = new BinaryReader(storage.AsStream());
|
||||||
int count = reader.ReadInt32();
|
int count = reader.ReadInt32();
|
||||||
|
|
||||||
reader.BaseStream.Position -= 4;
|
reader.BaseStream.Position -= 4;
|
||||||
|
|
||||||
var entries = new DirectoryEntry[count];
|
var entries = new SaveDirectoryEntry[count];
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
entries[i] = new DirectoryEntry(reader);
|
entries[i] = new SaveDirectoryEntry(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
|
|
|
@ -6,23 +6,23 @@ using System.Text;
|
||||||
namespace LibHac.IO.Save
|
namespace LibHac.IO.Save
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{" + nameof(FullPath) + "}")]
|
[DebuggerDisplay("{" + nameof(FullPath) + "}")]
|
||||||
public abstract class FsEntry
|
public abstract class SaveFsEntry
|
||||||
{
|
{
|
||||||
public int ParentDirIndex { get; protected set; }
|
public int ParentDirIndex { get; protected set; }
|
||||||
public string Name { get; protected set; }
|
public string Name { get; protected set; }
|
||||||
|
|
||||||
public string FullPath { get; private 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 list = new List<string>();
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
string delimiter = "/";
|
string delimiter = "/";
|
||||||
foreach (FsEntry file in entries)
|
foreach (SaveFsEntry file in entries)
|
||||||
{
|
{
|
||||||
list.Add(file.Name);
|
list.Add(file.Name);
|
||||||
DirectoryEntry dir = file.ParentDir;
|
SaveDirectoryEntry dir = file.ParentDir;
|
||||||
while (dir != null)
|
while (dir != null)
|
||||||
{
|
{
|
||||||
list.Add(delimiter);
|
list.Add(delimiter);
|
||||||
|
@ -35,14 +35,14 @@ namespace LibHac.IO.Save
|
||||||
sb.Append(list[i]);
|
sb.Append(list[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
file.FullPath = sb.ToString();
|
file.FullPath = sb.Length == 0 ? delimiter : sb.ToString();
|
||||||
list.Clear();
|
list.Clear();
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileEntry : FsEntry
|
public class SaveFileEntry : SaveFsEntry
|
||||||
{
|
{
|
||||||
public int NextSiblingIndex { get; }
|
public int NextSiblingIndex { get; }
|
||||||
public int BlockIndex { get; }
|
public int BlockIndex { get; }
|
||||||
|
@ -50,10 +50,10 @@ namespace LibHac.IO.Save
|
||||||
public long Field54 { get; }
|
public long Field54 { get; }
|
||||||
public int NextInChainIndex { get; }
|
public int NextInChainIndex { get; }
|
||||||
|
|
||||||
public FileEntry NextSibling { get; internal set; }
|
public SaveFileEntry NextSibling { get; internal set; }
|
||||||
public FileEntry NextInChain { get; internal set; }
|
public SaveFileEntry NextInChain { get; internal set; }
|
||||||
|
|
||||||
public FileEntry(BinaryReader reader)
|
public SaveFileEntry(BinaryReader reader)
|
||||||
{
|
{
|
||||||
long start = reader.BaseStream.Position;
|
long start = reader.BaseStream.Position;
|
||||||
ParentDirIndex = reader.ReadInt32();
|
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 NextSiblingIndex { get; }
|
||||||
public int FirstChildIndex { get; }
|
public int FirstChildIndex { get; }
|
||||||
|
@ -76,12 +76,12 @@ namespace LibHac.IO.Save
|
||||||
public long Field54 { get; }
|
public long Field54 { get; }
|
||||||
public int NextInChainIndex { get; }
|
public int NextInChainIndex { get; }
|
||||||
|
|
||||||
public DirectoryEntry NextSibling { get; internal set; }
|
public SaveDirectoryEntry NextSibling { get; internal set; }
|
||||||
public DirectoryEntry FirstChild { get; internal set; }
|
public SaveDirectoryEntry FirstChild { get; internal set; }
|
||||||
public FileEntry FirstFile { get; internal set; }
|
public SaveFileEntry FirstFile { get; internal set; }
|
||||||
public DirectoryEntry NextInChain { get; internal set; }
|
public SaveDirectoryEntry NextInChain { get; internal set; }
|
||||||
|
|
||||||
public DirectoryEntry(BinaryReader reader)
|
public SaveDirectoryEntry(BinaryReader reader)
|
||||||
{
|
{
|
||||||
long start = reader.BaseStream.Position;
|
long start = reader.BaseStream.Position;
|
||||||
ParentDirIndex = reader.ReadInt32();
|
ParentDirIndex = reader.ReadInt32();
|
44
src/LibHac/IO/StorageFile.cs
Normal file
44
src/LibHac/IO/StorageFile.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ namespace LibHac
|
||||||
public string SaveDir { 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, 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, 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>();
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ namespace LibHac
|
||||||
|
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
SaveData save = null;
|
SaveDataFileSystem save = null;
|
||||||
string saveName = Path.GetFileNameWithoutExtension(file);
|
string saveName = Path.GetFileNameWithoutExtension(file);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -127,7 +127,7 @@ namespace LibHac
|
||||||
|
|
||||||
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 SaveData(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
|
save = new SaveDataFileSystem(Keyset, nax0.BaseStorage, IntegrityCheckLevel.None, true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -103,9 +103,9 @@ namespace NandReader
|
||||||
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
|
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
|
||||||
{
|
{
|
||||||
var tickets = new List<Ticket>();
|
var tickets = new List<Ticket>();
|
||||||
var save = new SaveData(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
|
var save = new SaveDataFileSystem(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
|
||||||
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin").AsStream());
|
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
|
||||||
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin").AsStream());
|
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
|
||||||
|
|
||||||
ulong titleId = ticketList.ReadUInt64();
|
ulong titleId = ticketList.ReadUInt64();
|
||||||
while (titleId != ulong.MaxValue)
|
while (titleId != ulong.MaxValue)
|
||||||
|
|
|
@ -87,9 +87,9 @@ namespace NandReaderGui.ViewModel
|
||||||
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
|
private static List<Ticket> ReadTickets(Keyset keyset, Stream savefile)
|
||||||
{
|
{
|
||||||
var tickets = new List<Ticket>();
|
var tickets = new List<Ticket>();
|
||||||
var save = new SaveData(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
|
var save = new SaveDataFileSystem(keyset, savefile.AsStorage(), IntegrityCheckLevel.None, true);
|
||||||
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin").AsStream());
|
var ticketList = new BinaryReader(save.OpenFile("/ticket_list.bin", OpenMode.Read).AsStream());
|
||||||
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin").AsStream());
|
var ticketFile = new BinaryReader(save.OpenFile("/ticket.bin", OpenMode.Read).AsStream());
|
||||||
|
|
||||||
ulong titleId = ticketList.ReadUInt64();
|
ulong titleId = ticketList.ReadUInt64();
|
||||||
while (titleId != ulong.MaxValue)
|
while (titleId != ulong.MaxValue)
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace hactoolnet
|
||||||
{
|
{
|
||||||
using (var file = new FileStream(ctx.Options.InFile, FileMode.Open, FileAccess.ReadWrite))
|
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)
|
if (ctx.Options.Validate)
|
||||||
{
|
{
|
||||||
|
@ -23,7 +23,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.OutDir != null)
|
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)
|
if (ctx.Options.DebugOutDir != null)
|
||||||
|
@ -99,13 +99,13 @@ namespace hactoolnet
|
||||||
string destFilename = ctx.Options.ReplaceFileDest;
|
string destFilename = ctx.Options.ReplaceFileDest;
|
||||||
if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ namespace hactoolnet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save.CommitHeader(ctx.Keyset))
|
if (save.Commit(ctx.Keyset))
|
||||||
{
|
{
|
||||||
ctx.Logger.LogMessage("Successfully signed save file");
|
ctx.Logger.LogMessage("Successfully signed save file");
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.SignSave)
|
if (ctx.Options.SignSave)
|
||||||
{
|
{
|
||||||
if (save.CommitHeader(ctx.Keyset))
|
if (save.Commit(ctx.Keyset))
|
||||||
{
|
{
|
||||||
ctx.Logger.LogMessage("Successfully signed save file");
|
ctx.Logger.LogMessage("Successfully signed save file");
|
||||||
}
|
}
|
||||||
|
@ -143,9 +143,11 @@ namespace hactoolnet
|
||||||
|
|
||||||
if (ctx.Options.ListFiles)
|
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;
|
int colLen = 25;
|
||||||
var sb = new StringBuilder();
|
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, "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, "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, $"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);
|
PrintIvfcHash(sb, colLen, 4, save.Header.Ivfc, IntegrityStorageType.Save);
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ namespace hactoolnet
|
||||||
|
|
||||||
private static void ExportSdSaves(Context ctx, SwitchFs switchFs)
|
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);
|
string outDir = Path.Combine(ctx.Options.SaveOutDir, save.Key);
|
||||||
save.Value.Extract(outDir, ctx.Logger);
|
save.Value.Extract(outDir, ctx.Logger);
|
||||||
|
|
Loading…
Reference in a new issue