mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Create SaveFs class
This commit is contained in:
parent
6935983b71
commit
75cb28364f
147
LibHac/Save/SaveFs.cs
Normal file
147
LibHac/Save/SaveFs.cs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using LibHac.Streams;
|
||||||
|
|
||||||
|
namespace LibHac.Save
|
||||||
|
{
|
||||||
|
public class SaveFs
|
||||||
|
{
|
||||||
|
private AllocationTable AllocationTable { get; }
|
||||||
|
private SharedStreamSource StreamSource { 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 SaveFs(Stream storage, Stream allocationTable, SaveHeader header)
|
||||||
|
{
|
||||||
|
StreamSource = new SharedStreamSource(storage);
|
||||||
|
AllocationTable = new AllocationTable(allocationTable);
|
||||||
|
Header = header;
|
||||||
|
|
||||||
|
ReadFileInfo();
|
||||||
|
var dictionary = new Dictionary<string, FileEntry>();
|
||||||
|
foreach (FileEntry entry in Files)
|
||||||
|
{
|
||||||
|
dictionary[entry.FullPath] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDictionary = dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile(string filename)
|
||||||
|
{
|
||||||
|
if (!FileDictionary.TryGetValue(filename, out FileEntry file))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream OpenFile(FileEntry file)
|
||||||
|
{
|
||||||
|
if (file.BlockIndex < 0)
|
||||||
|
{
|
||||||
|
return Stream.Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenFatBlock(file.BlockIndex, file.FileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string filename) => FileDictionary.ContainsKey(filename);
|
||||||
|
|
||||||
|
public Stream OpenRawSaveFs() => StreamSource.CreateStream();
|
||||||
|
|
||||||
|
private void ReadFileInfo()
|
||||||
|
{
|
||||||
|
// todo: Query the FAT for the file size when none is given
|
||||||
|
AllocationTableStream dirTableStream = OpenFatBlock(Header.DirectoryTableBlock, 1000000);
|
||||||
|
AllocationTableStream fileTableStream = OpenFatBlock(Header.FileTableBlock, 1000000);
|
||||||
|
|
||||||
|
DirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
|
||||||
|
FileEntry[] fileEntries = ReadFileEntries(fileTableStream);
|
||||||
|
|
||||||
|
foreach (DirectoryEntry dir in dirEntries)
|
||||||
|
{
|
||||||
|
if (dir.NextSiblingIndex != 0) dir.NextSibling = dirEntries[dir.NextSiblingIndex];
|
||||||
|
if (dir.FirstChildIndex != 0) dir.FirstChild = dirEntries[dir.FirstChildIndex];
|
||||||
|
if (dir.FirstFileIndex != 0) dir.FirstFile = fileEntries[dir.FirstFileIndex];
|
||||||
|
if (dir.NextInChainIndex != 0) dir.NextInChain = dirEntries[dir.NextInChainIndex];
|
||||||
|
if (dir.ParentDirIndex != 0 && dir.ParentDirIndex < dirEntries.Length)
|
||||||
|
dir.ParentDir = dirEntries[dir.ParentDirIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (FileEntry file in fileEntries)
|
||||||
|
{
|
||||||
|
if (file.NextSiblingIndex != 0) file.NextSibling = fileEntries[file.NextSiblingIndex];
|
||||||
|
if (file.NextInChainIndex != 0) file.NextInChain = fileEntries[file.NextInChainIndex];
|
||||||
|
if (file.ParentDirIndex != 0 && file.ParentDirIndex < dirEntries.Length)
|
||||||
|
file.ParentDir = dirEntries[file.ParentDirIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
RootDirectory = dirEntries[2];
|
||||||
|
|
||||||
|
FileEntry fileChain = fileEntries[1].NextInChain;
|
||||||
|
var files = new List<FileEntry>();
|
||||||
|
while (fileChain != null)
|
||||||
|
{
|
||||||
|
files.Add(fileChain);
|
||||||
|
fileChain = fileChain.NextInChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryEntry dirChain = dirEntries[1].NextInChain;
|
||||||
|
var dirs = new List<DirectoryEntry>();
|
||||||
|
while (dirChain != null)
|
||||||
|
{
|
||||||
|
dirs.Add(dirChain);
|
||||||
|
dirChain = dirChain.NextInChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
Files = files.ToArray();
|
||||||
|
Directories = dirs.ToArray();
|
||||||
|
|
||||||
|
FsEntry.ResolveFilenames(Files);
|
||||||
|
FsEntry.ResolveFilenames(Directories);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileEntry[] ReadFileEntries(Stream stream)
|
||||||
|
{
|
||||||
|
var reader = new BinaryReader(stream);
|
||||||
|
int count = reader.ReadInt32();
|
||||||
|
|
||||||
|
reader.BaseStream.Position -= 4;
|
||||||
|
|
||||||
|
var entries = new FileEntry[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
entries[i] = new FileEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DirectoryEntry[] ReadDirEntries(Stream stream)
|
||||||
|
{
|
||||||
|
var reader = new BinaryReader(stream);
|
||||||
|
int count = reader.ReadInt32();
|
||||||
|
|
||||||
|
reader.BaseStream.Position -= 4;
|
||||||
|
|
||||||
|
var entries = new DirectoryEntry[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
entries[i] = new DirectoryEntry(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AllocationTableStream OpenFatBlock(int blockIndex, long size)
|
||||||
|
{
|
||||||
|
return new AllocationTableStream(StreamSource.CreateStream(), AllocationTable, (int)Header.BlockSize, blockIndex, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LibHac.Streams;
|
using LibHac.Streams;
|
||||||
@ -18,7 +17,7 @@ namespace LibHac.Save
|
|||||||
public SharedStreamSource JournalStreamSource { get; }
|
public SharedStreamSource JournalStreamSource { get; }
|
||||||
private HierarchicalIntegrityVerificationStream IvfcStream { get; }
|
private HierarchicalIntegrityVerificationStream IvfcStream { get; }
|
||||||
public SharedStreamSource IvfcStreamSource { get; }
|
public SharedStreamSource IvfcStreamSource { get; }
|
||||||
private AllocationTable AllocationTable { get; }
|
public SaveFs SaveFs { get; }
|
||||||
|
|
||||||
public Stream DuplexL1A { get; }
|
public Stream DuplexL1A { get; }
|
||||||
public Stream DuplexL1B { get; }
|
public Stream DuplexL1B { get; }
|
||||||
@ -36,10 +35,9 @@ namespace LibHac.Save
|
|||||||
public Stream JournalLayer3Hash { get; }
|
public Stream JournalLayer3Hash { get; }
|
||||||
public Stream JournalFat { get; }
|
public Stream JournalFat { get; }
|
||||||
|
|
||||||
public DirectoryEntry RootDirectory { get; private set; }
|
public DirectoryEntry RootDirectory => SaveFs.RootDirectory;
|
||||||
public FileEntry[] Files { get; private set; }
|
public FileEntry[] Files => SaveFs.Files;
|
||||||
public DirectoryEntry[] Directories { get; private set; }
|
public DirectoryEntry[] Directories => SaveFs.Directories;
|
||||||
private Dictionary<string, FileEntry> FileDict { get; }
|
|
||||||
|
|
||||||
public Savefile(Keyset keyset, Stream file, IntegrityCheckLevel integrityCheckLevel)
|
public Savefile(Keyset keyset, Stream file, IntegrityCheckLevel integrityCheckLevel)
|
||||||
{
|
{
|
||||||
@ -97,7 +95,6 @@ namespace LibHac.Save
|
|||||||
JournalLayer2Hash = MetaRemapSource.CreateStream(layout.IvfcL2Offset, layout.IvfcL2Size);
|
JournalLayer2Hash = MetaRemapSource.CreateStream(layout.IvfcL2Offset, layout.IvfcL2Size);
|
||||||
JournalLayer3Hash = MetaRemapSource.CreateStream(layout.IvfcL3Offset, layout.IvfcL3Size);
|
JournalLayer3Hash = MetaRemapSource.CreateStream(layout.IvfcL3Offset, layout.IvfcL3Size);
|
||||||
JournalFat = MetaRemapSource.CreateStream(layout.FatOffset, layout.FatSize);
|
JournalFat = MetaRemapSource.CreateStream(layout.FatOffset, layout.FatSize);
|
||||||
AllocationTable = new AllocationTable(JournalFat);
|
|
||||||
|
|
||||||
MappingEntry[] journalMap = JournalStream.ReadMappingEntries(JournalTable, Header.Journal.MainDataBlockCount);
|
MappingEntry[] journalMap = JournalStream.ReadMappingEntries(JournalTable, Header.Journal.MainDataBlockCount);
|
||||||
|
|
||||||
@ -107,16 +104,10 @@ namespace LibHac.Save
|
|||||||
JournalStreamSource = new SharedStreamSource(JournalStream);
|
JournalStreamSource = new SharedStreamSource(JournalStream);
|
||||||
|
|
||||||
IvfcStream = InitIvfcStream(integrityCheckLevel);
|
IvfcStream = InitIvfcStream(integrityCheckLevel);
|
||||||
|
|
||||||
|
SaveFs = new SaveFs(IvfcStream, MetaRemapSource.CreateStream(layout.FatOffset, layout.FatSize), Header.Save);
|
||||||
|
|
||||||
IvfcStreamSource = new SharedStreamSource(IvfcStream);
|
IvfcStreamSource = new SharedStreamSource(IvfcStream);
|
||||||
|
|
||||||
ReadFileInfo();
|
|
||||||
var dictionary = new Dictionary<string, FileEntry>();
|
|
||||||
foreach (FileEntry entry in Files)
|
|
||||||
{
|
|
||||||
dictionary[entry.FullPath] = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileDict = dictionary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,114 +147,16 @@ namespace LibHac.Save
|
|||||||
|
|
||||||
public Stream OpenFile(string filename)
|
public Stream OpenFile(string filename)
|
||||||
{
|
{
|
||||||
if (!FileDict.TryGetValue(filename, out FileEntry file))
|
return SaveFs.OpenFile(filename);
|
||||||
{
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return OpenFile(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream OpenFile(FileEntry file)
|
public Stream OpenFile(FileEntry file)
|
||||||
{
|
{
|
||||||
if (file.BlockIndex < 0)
|
return SaveFs.OpenFile(file);
|
||||||
{
|
|
||||||
return Stream.Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OpenFatBlock(file.BlockIndex, file.FileSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AllocationTableStream OpenFatBlock(int blockIndex, long size)
|
|
||||||
{
|
|
||||||
return new AllocationTableStream(IvfcStreamSource.CreateStream(), AllocationTable, (int)Header.Save.BlockSize, blockIndex, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FileExists(string filename) => FileDict.ContainsKey(filename);
|
public bool FileExists(string filename) => SaveFs.FileExists(filename);
|
||||||
|
|
||||||
private void ReadFileInfo()
|
|
||||||
{
|
|
||||||
// todo: Query the FAT for the file size when none is given
|
|
||||||
AllocationTableStream dirTableStream = OpenFatBlock(Header.Save.DirectoryTableBlock, 1000000);
|
|
||||||
AllocationTableStream fileTableStream = OpenFatBlock(Header.Save.FileTableBlock, 1000000);
|
|
||||||
|
|
||||||
DirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
|
|
||||||
FileEntry[] fileEntries = ReadFileEntries(fileTableStream);
|
|
||||||
|
|
||||||
foreach (DirectoryEntry dir in dirEntries)
|
|
||||||
{
|
|
||||||
if (dir.NextSiblingIndex != 0) dir.NextSibling = dirEntries[dir.NextSiblingIndex];
|
|
||||||
if (dir.FirstChildIndex != 0) dir.FirstChild = dirEntries[dir.FirstChildIndex];
|
|
||||||
if (dir.FirstFileIndex != 0) dir.FirstFile = fileEntries[dir.FirstFileIndex];
|
|
||||||
if (dir.NextInChainIndex != 0) dir.NextInChain = dirEntries[dir.NextInChainIndex];
|
|
||||||
if (dir.ParentDirIndex != 0 && dir.ParentDirIndex < dirEntries.Length)
|
|
||||||
dir.ParentDir = dirEntries[dir.ParentDirIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (FileEntry file in fileEntries)
|
|
||||||
{
|
|
||||||
if (file.NextSiblingIndex != 0) file.NextSibling = fileEntries[file.NextSiblingIndex];
|
|
||||||
if (file.NextInChainIndex != 0) file.NextInChain = fileEntries[file.NextInChainIndex];
|
|
||||||
if (file.ParentDirIndex != 0 && file.ParentDirIndex < dirEntries.Length)
|
|
||||||
file.ParentDir = dirEntries[file.ParentDirIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
RootDirectory = dirEntries[2];
|
|
||||||
|
|
||||||
FileEntry fileChain = fileEntries[1].NextInChain;
|
|
||||||
var files = new List<FileEntry>();
|
|
||||||
while (fileChain != null)
|
|
||||||
{
|
|
||||||
files.Add(fileChain);
|
|
||||||
fileChain = fileChain.NextInChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryEntry dirChain = dirEntries[1].NextInChain;
|
|
||||||
var dirs = new List<DirectoryEntry>();
|
|
||||||
while (dirChain != null)
|
|
||||||
{
|
|
||||||
dirs.Add(dirChain);
|
|
||||||
dirChain = dirChain.NextInChain;
|
|
||||||
}
|
|
||||||
|
|
||||||
Files = files.ToArray();
|
|
||||||
Directories = dirs.ToArray();
|
|
||||||
|
|
||||||
FsEntry.ResolveFilenames(Files);
|
|
||||||
FsEntry.ResolveFilenames(Directories);
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileEntry[] ReadFileEntries(Stream stream)
|
|
||||||
{
|
|
||||||
var reader = new BinaryReader(stream);
|
|
||||||
int count = reader.ReadInt32();
|
|
||||||
|
|
||||||
reader.BaseStream.Position -= 4;
|
|
||||||
|
|
||||||
var entries = new FileEntry[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
entries[i] = new FileEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DirectoryEntry[] ReadDirEntries(Stream stream)
|
|
||||||
{
|
|
||||||
var reader = new BinaryReader(stream);
|
|
||||||
int count = reader.ReadInt32();
|
|
||||||
|
|
||||||
reader.BaseStream.Position -= 4;
|
|
||||||
|
|
||||||
var entries = new DirectoryEntry[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
entries[i] = new DirectoryEntry(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SignHeader(Keyset keyset)
|
public bool SignHeader(Keyset keyset)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user