mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Savefile: Build file tree structure
This commit is contained in:
parent
7bc718c975
commit
0dc36560fb
@ -1,10 +1,11 @@
|
||||
namespace LibHac.Savefile
|
||||
using System;
|
||||
|
||||
namespace LibHac.Savefile
|
||||
{
|
||||
public class AllocationTableIterator
|
||||
{
|
||||
private AllocationTable Fat { get; }
|
||||
|
||||
public bool IsValid { get; }
|
||||
public int VirtualBlock { get; private set; }
|
||||
public int PhysicalBlock { get; private set; }
|
||||
public int CurrentSegmentSize { get; private set; }
|
||||
@ -12,7 +13,10 @@
|
||||
public AllocationTableIterator(AllocationTable table, int initialBlock)
|
||||
{
|
||||
Fat = table;
|
||||
IsValid = BeginIteration(initialBlock);
|
||||
if (!BeginIteration(initialBlock))
|
||||
{
|
||||
throw new ArgumentException($"Attempted to start FAT iteration from an invalid block. ({initialBlock}");
|
||||
}
|
||||
}
|
||||
|
||||
public bool BeginIteration(int initialBlock)
|
||||
|
@ -17,7 +17,6 @@ namespace LibHac.Savefile
|
||||
BlockSize = blockSize;
|
||||
Length = length;
|
||||
Iterator = new AllocationTableIterator(table, initialBlock);
|
||||
if (!Iterator.IsValid) Length = 0;
|
||||
Data.Position = Iterator.PhysicalBlock * BlockSize;
|
||||
}
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibHac.Savefile
|
||||
{
|
||||
public class FileEntry
|
||||
{
|
||||
public int ParentDirIndex { get; }
|
||||
public string Name { get; }
|
||||
public int Field44 { get; }
|
||||
public int BlockIndex { get; }
|
||||
public long Size { get; }
|
||||
public long Field54 { get; }
|
||||
public int NextIndex { get; }
|
||||
|
||||
public long Offset { get; internal set; }
|
||||
public string FullPath { get; private set; }
|
||||
public FileEntry ParentDir { get; internal set; }
|
||||
public FileEntry Next { get; internal set; }
|
||||
|
||||
public FileEntry(BinaryReader reader)
|
||||
{
|
||||
var start = reader.BaseStream.Position;
|
||||
ParentDirIndex = reader.ReadInt32();
|
||||
Name = reader.ReadUtf8Z(0x40);
|
||||
reader.BaseStream.Position = start + 0x44;
|
||||
|
||||
Field44 = reader.ReadInt32();
|
||||
BlockIndex = reader.ReadInt32();
|
||||
Size = reader.ReadInt64();
|
||||
Field54 = reader.ReadInt64();
|
||||
NextIndex = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public static void ResolveFilenames(FileEntry[] entries)
|
||||
{
|
||||
var list = new List<string>();
|
||||
var sb = new StringBuilder();
|
||||
var delimiter = "/";
|
||||
foreach (var file in entries)
|
||||
{
|
||||
list.Add(file.Name);
|
||||
var dir = file.ParentDir;
|
||||
while (dir != null)
|
||||
{
|
||||
list.Add(delimiter);
|
||||
list.Add(dir.Name);
|
||||
dir = dir.ParentDir;
|
||||
}
|
||||
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
sb.Append(list[i]);
|
||||
}
|
||||
|
||||
file.FullPath = sb.ToString();
|
||||
list.Clear();
|
||||
sb.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
98
LibHac/Savefile/FsEntry.cs
Normal file
98
LibHac/Savefile/FsEntry.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibHac.Savefile
|
||||
{
|
||||
[DebuggerDisplay("{" + nameof(FullPath) + "}")]
|
||||
public abstract class FsEntry
|
||||
{
|
||||
public int ParentDirIndex { get; protected set; }
|
||||
public string Name { get; protected set; }
|
||||
|
||||
public string FullPath { get; private set; }
|
||||
public DirectoryEntry ParentDir { get; internal set; }
|
||||
|
||||
internal static void ResolveFilenames(IEnumerable<FsEntry> entries)
|
||||
{
|
||||
var list = new List<string>();
|
||||
var sb = new StringBuilder();
|
||||
var delimiter = "/";
|
||||
foreach (var file in entries)
|
||||
{
|
||||
list.Add(file.Name);
|
||||
var dir = file.ParentDir;
|
||||
while (dir != null)
|
||||
{
|
||||
list.Add(delimiter);
|
||||
list.Add(dir.Name);
|
||||
dir = dir.ParentDir;
|
||||
}
|
||||
|
||||
for (int i = list.Count - 1; i >= 0; i--)
|
||||
{
|
||||
sb.Append(list[i]);
|
||||
}
|
||||
|
||||
file.FullPath = sb.ToString();
|
||||
list.Clear();
|
||||
sb.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FileEntry : FsEntry
|
||||
{
|
||||
public int NextSiblingIndex { get; }
|
||||
public int BlockIndex { get; }
|
||||
public long FileSize { get; }
|
||||
public long Field54 { get; }
|
||||
public int NextInChainIndex { get; }
|
||||
|
||||
public FileEntry NextSibling { get; internal set; }
|
||||
public FileEntry NextInChain { get; internal set; }
|
||||
|
||||
public FileEntry(BinaryReader reader)
|
||||
{
|
||||
var start = reader.BaseStream.Position;
|
||||
ParentDirIndex = reader.ReadInt32();
|
||||
Name = reader.ReadUtf8Z(0x40);
|
||||
reader.BaseStream.Position = start + 0x44;
|
||||
|
||||
NextSiblingIndex = reader.ReadInt32();
|
||||
BlockIndex = reader.ReadInt32();
|
||||
FileSize = reader.ReadInt64();
|
||||
Field54 = reader.ReadInt64();
|
||||
NextInChainIndex = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public class DirectoryEntry : FsEntry
|
||||
{
|
||||
public int NextSiblingIndex { get; }
|
||||
public int FirstChildIndex { get; }
|
||||
public long FirstFileIndex { get; }
|
||||
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 DirectoryEntry(BinaryReader reader)
|
||||
{
|
||||
var start = reader.BaseStream.Position;
|
||||
ParentDirIndex = reader.ReadInt32();
|
||||
Name = reader.ReadUtf8Z(0x40);
|
||||
reader.BaseStream.Position = start + 0x44;
|
||||
|
||||
NextSiblingIndex = reader.ReadInt32();
|
||||
FirstChildIndex = reader.ReadInt32();
|
||||
FirstFileIndex = reader.ReadInt64();
|
||||
Field54 = reader.ReadInt64();
|
||||
NextInChainIndex = reader.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using LibHac.Streams;
|
||||
@ -33,7 +32,9 @@ namespace LibHac.Savefile
|
||||
public Stream JournalLayer3Hash { get; }
|
||||
public Stream JournalFat { get; }
|
||||
|
||||
public DirectoryEntry RootDirectory { get; private set; }
|
||||
public FileEntry[] Files { get; private set; }
|
||||
public DirectoryEntry[] Directories { get; private set; }
|
||||
private Dictionary<string, FileEntry> FileDict { get; }
|
||||
|
||||
public Savefile(Stream file, IProgressReport logger = null)
|
||||
@ -126,7 +127,7 @@ namespace LibHac.Savefile
|
||||
return JournalStreamSource.CreateStream(0, 0);
|
||||
}
|
||||
|
||||
return OpenFatBlock(file.BlockIndex, file.Size);
|
||||
return OpenFatBlock(file.BlockIndex, file.FileSize);
|
||||
}
|
||||
|
||||
private AllocationTableStream OpenFatBlock(int blockIndex, long size)
|
||||
@ -138,34 +139,54 @@ namespace LibHac.Savefile
|
||||
|
||||
private void ReadFileInfo()
|
||||
{
|
||||
var blockSize = Header.Save.BlockSize;
|
||||
|
||||
// todo: Query the FAT for the file size when none is given
|
||||
var dirTableStream = OpenFatBlock(Header.Save.DirectoryTableBlock, 1000000);
|
||||
var fileTableStream = OpenFatBlock(Header.Save.FileTableBlock, 1000000);
|
||||
|
||||
FileEntry[] dirEntries = ReadFileEntries(dirTableStream);
|
||||
DirectoryEntry[] dirEntries = ReadDirEntries(dirTableStream);
|
||||
FileEntry[] fileEntries = ReadFileEntries(fileTableStream);
|
||||
|
||||
foreach (var dir in dirEntries)
|
||||
foreach (DirectoryEntry dir in dirEntries)
|
||||
{
|
||||
if (dir.NextIndex != 0) dir.Next = dirEntries[dir.NextIndex];
|
||||
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 (var file in fileEntries)
|
||||
foreach (FileEntry file in fileEntries)
|
||||
{
|
||||
if (file.NextIndex != 0) file.Next = fileEntries[file.NextIndex];
|
||||
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];
|
||||
file.Offset = file.BlockIndex < 0 ? 0 : file.BlockIndex * blockSize;
|
||||
}
|
||||
|
||||
Files = new FileEntry[fileEntries.Length - 2];
|
||||
Array.Copy(fileEntries, 2, Files, 0, Files.Length);
|
||||
RootDirectory = dirEntries[2];
|
||||
|
||||
FileEntry.ResolveFilenames(Files);
|
||||
var fileChain = fileEntries[1].NextInChain;
|
||||
var files = new List<FileEntry>();
|
||||
while (fileChain != null)
|
||||
{
|
||||
files.Add(fileChain);
|
||||
fileChain = fileChain.NextInChain;
|
||||
}
|
||||
|
||||
var 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)
|
||||
@ -183,6 +204,22 @@ namespace LibHac.Savefile
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
private DirectoryEntry[] ReadDirEntries(Stream stream)
|
||||
{
|
||||
var reader = new BinaryReader(stream);
|
||||
var 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 static class SavefileExtensions
|
||||
|
Loading…
x
Reference in New Issue
Block a user