mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Implement adding entries to HierarchicalSaveFileTable
This commit is contained in:
parent
5c1d8e0786
commit
079ffa6d3b
2 changed files with 219 additions and 12 deletions
|
@ -16,7 +16,11 @@ namespace LibHac.IO.Save
|
||||||
|
|
||||||
public bool TryOpenFile(string path, out SaveFileInfo fileInfo)
|
public bool TryOpenFile(string path, out SaveFileInfo fileInfo)
|
||||||
{
|
{
|
||||||
FindPathRecursive(Util.GetUtf8Bytes(path), out SaveEntryKey key);
|
if (!FindPathRecursive(Util.GetUtf8Bytes(path), out SaveEntryKey key))
|
||||||
|
{
|
||||||
|
fileInfo = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (FileTable.TryGetValue(ref key, out FileSaveEntry value))
|
if (FileTable.TryGetValue(ref key, out FileSaveEntry value))
|
||||||
{
|
{
|
||||||
|
@ -83,9 +87,77 @@ namespace LibHac.IO.Save
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddFile(string path, ref SaveFileInfo fileInfo)
|
||||||
|
{
|
||||||
|
path = PathTools.Normalize(path);
|
||||||
|
ReadOnlySpan<byte> pathBytes = Util.GetUtf8Bytes(path);
|
||||||
|
|
||||||
|
if (path == "/") throw new ArgumentException("Path cannot be empty");
|
||||||
|
|
||||||
|
CreateFileRecursiveInternal(pathBytes, ref fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateFileRecursiveInternal(ReadOnlySpan<byte> path, ref SaveFileInfo fileInfo)
|
||||||
|
{
|
||||||
|
var parser = new PathParser(path);
|
||||||
|
var key = new SaveEntryKey(parser.GetCurrent(), 0);
|
||||||
|
|
||||||
|
int prevIndex = 0;
|
||||||
|
|
||||||
|
while (!parser.IsFinished())
|
||||||
|
{
|
||||||
|
int index = DirectoryTable.GetIndexFromKey(ref key).Index;
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
var newEntry = new DirectorySaveEntry();
|
||||||
|
index = DirectoryTable.Add(ref key, ref newEntry);
|
||||||
|
|
||||||
|
if (prevIndex > 0)
|
||||||
|
{
|
||||||
|
DirectoryTable.GetValue(prevIndex, out DirectorySaveEntry parentEntry);
|
||||||
|
|
||||||
|
newEntry.NextSibling = parentEntry.Pos.NextDirectory;
|
||||||
|
parentEntry.Pos.NextDirectory = index;
|
||||||
|
|
||||||
|
DirectoryTable.SetValue(prevIndex, ref parentEntry);
|
||||||
|
DirectoryTable.SetValue(index, ref newEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevIndex = index;
|
||||||
|
key.Parent = index;
|
||||||
|
parser.TryGetNext(out key.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int index = FileTable.GetIndexFromKey(ref key).Index;
|
||||||
|
var fileEntry = new FileSaveEntry();
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index = FileTable.Add(ref key, ref fileEntry);
|
||||||
|
|
||||||
|
DirectoryTable.GetValue(prevIndex, out DirectorySaveEntry parentEntry);
|
||||||
|
|
||||||
|
fileEntry.NextSibling = (int)parentEntry.Pos.NextFile;
|
||||||
|
parentEntry.Pos.NextFile = index;
|
||||||
|
|
||||||
|
DirectoryTable.SetValue(prevIndex, ref parentEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileEntry.Info = fileInfo;
|
||||||
|
FileTable.SetValue(index, ref fileEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryOpenDirectory(string path, out SaveFindPosition position)
|
public bool TryOpenDirectory(string path, out SaveFindPosition position)
|
||||||
{
|
{
|
||||||
FindPathRecursive(Util.GetUtf8Bytes(path), out SaveEntryKey key);
|
if (!FindPathRecursive(Util.GetUtf8Bytes(path), out SaveEntryKey key))
|
||||||
|
{
|
||||||
|
position = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (DirectoryTable.TryGetValue(ref key, out DirectorySaveEntry value))
|
if (DirectoryTable.TryGetValue(ref key, out DirectorySaveEntry value))
|
||||||
{
|
{
|
||||||
|
@ -97,16 +169,21 @@ namespace LibHac.IO.Save
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindPathRecursive(ReadOnlySpan<byte> path, out SaveEntryKey key)
|
private bool FindPathRecursive(ReadOnlySpan<byte> path, out SaveEntryKey key)
|
||||||
{
|
{
|
||||||
var parser = new PathParser(path);
|
var parser = new PathParser(path);
|
||||||
key = new SaveEntryKey(parser.GetCurrent(), 0);
|
key = new SaveEntryKey(parser.GetCurrent(), 0);
|
||||||
|
|
||||||
while (!parser.IsFinished())
|
while (!parser.IsFinished())
|
||||||
{
|
{
|
||||||
key.Parent = DirectoryTable.GetOffsetFromKey(ref key);
|
key.Parent = DirectoryTable.GetIndexFromKey(ref key).Index;
|
||||||
|
|
||||||
|
if (key.Parent < 0) return false;
|
||||||
|
|
||||||
parser.TryGetNext(out key.Name);
|
parser.TryGetNext(out key.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace LibHac.IO.Save
|
||||||
Storage = tableStorage;
|
Storage = tableStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetOffsetFromKey(ref SaveEntryKey key)
|
public (int Index, int PreviousIndex) GetIndexFromKey(ref SaveEntryKey key)
|
||||||
{
|
{
|
||||||
Span<byte> entryBytes = stackalloc byte[_sizeOfEntry];
|
Span<byte> entryBytes = stackalloc byte[_sizeOfEntry];
|
||||||
Span<byte> name = entryBytes.Slice(4, MaxNameLength);
|
Span<byte> name = entryBytes.Slice(4, MaxNameLength);
|
||||||
|
@ -29,26 +29,92 @@ namespace LibHac.IO.Save
|
||||||
int capacity = GetListCapacity();
|
int capacity = GetListCapacity();
|
||||||
|
|
||||||
ReadEntry(UsedListHeadIndex, entryBytes);
|
ReadEntry(UsedListHeadIndex, entryBytes);
|
||||||
|
int prevIndex = UsedListHeadIndex;
|
||||||
|
int index = entry.Next;
|
||||||
|
|
||||||
while (entry.Next > 0)
|
while (index > 0)
|
||||||
{
|
{
|
||||||
if (entry.Next > capacity) throw new IndexOutOfRangeException("Save entry index out of range");
|
if (index > capacity) throw new IndexOutOfRangeException("Save entry index out of range");
|
||||||
|
|
||||||
int entryId = entry.Next;
|
ReadEntry(index, out entry);
|
||||||
ReadEntry(entry.Next, out entry);
|
|
||||||
|
|
||||||
if (entry.Parent == key.Parent && Util.StringSpansEqual(name, key.Name))
|
if (entry.Parent == key.Parent && Util.StringSpansEqual(name, key.Name))
|
||||||
{
|
{
|
||||||
return entryId;
|
return (index, prevIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevIndex = index;
|
||||||
|
index = entry.Next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return (-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Add(ref SaveEntryKey key, ref T value)
|
||||||
|
{
|
||||||
|
int index = GetIndexFromKey(ref key).Index;
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
SetValue(index, ref value);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = AllocateEntry();
|
||||||
|
|
||||||
|
ReadEntry(index, out SaveFsEntry entry);
|
||||||
|
entry.Value = value;
|
||||||
|
WriteEntry(index, ref entry, ref key);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AllocateEntry()
|
||||||
|
{
|
||||||
|
ReadEntry(FreeListHeadIndex, out SaveFsEntry freeListHead);
|
||||||
|
ReadEntry(UsedListHeadIndex, out SaveFsEntry usedListHead);
|
||||||
|
|
||||||
|
if (freeListHead.Next != 0)
|
||||||
|
{
|
||||||
|
ReadEntry(freeListHead.Next, out SaveFsEntry firstFreeEntry);
|
||||||
|
|
||||||
|
int allocatedIndex = freeListHead.Next;
|
||||||
|
|
||||||
|
freeListHead.Next = firstFreeEntry.Next;
|
||||||
|
firstFreeEntry.Next = usedListHead.Next;
|
||||||
|
usedListHead.Next = allocatedIndex;
|
||||||
|
|
||||||
|
WriteEntry(FreeListHeadIndex, ref freeListHead);
|
||||||
|
WriteEntry(UsedListHeadIndex, ref usedListHead);
|
||||||
|
WriteEntry(allocatedIndex, ref firstFreeEntry);
|
||||||
|
|
||||||
|
return allocatedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = GetListLength();
|
||||||
|
int capacity = GetListCapacity();
|
||||||
|
|
||||||
|
if (capacity == 0 || length >= capacity)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetListLength(length + 1);
|
||||||
|
|
||||||
|
ReadEntry(length, out SaveFsEntry newEntry);
|
||||||
|
|
||||||
|
newEntry.Next = usedListHead.Next;
|
||||||
|
usedListHead.Next = length;
|
||||||
|
|
||||||
|
WriteEntry(UsedListHeadIndex, ref usedListHead);
|
||||||
|
WriteEntry(length, ref newEntry);
|
||||||
|
|
||||||
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(ref SaveEntryKey key, out T value)
|
public bool TryGetValue(ref SaveEntryKey key, out T value)
|
||||||
{
|
{
|
||||||
int index = GetOffsetFromKey(ref key);
|
int index = GetIndexFromKey(ref key).Index;
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
|
@ -123,6 +189,18 @@ namespace LibHac.IO.Save
|
||||||
value = entry.Value;
|
value = entry.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetValue(int index, ref T value)
|
||||||
|
{
|
||||||
|
Span<byte> entryBytes = stackalloc byte[_sizeOfEntry];
|
||||||
|
ref SaveFsEntry entry = ref GetEntryFromBytes(entryBytes);
|
||||||
|
|
||||||
|
ReadEntry(index, out entry);
|
||||||
|
|
||||||
|
entry.Value = value;
|
||||||
|
|
||||||
|
WriteEntry(index, ref entry);
|
||||||
|
}
|
||||||
|
|
||||||
private int GetListCapacity()
|
private int GetListCapacity()
|
||||||
{
|
{
|
||||||
Span<byte> buf = stackalloc byte[sizeof(int)];
|
Span<byte> buf = stackalloc byte[sizeof(int)];
|
||||||
|
@ -139,6 +217,22 @@ namespace LibHac.IO.Save
|
||||||
return MemoryMarshal.Read<int>(buf);
|
return MemoryMarshal.Read<int>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetListCapacity(int capacity)
|
||||||
|
{
|
||||||
|
Span<byte> buf = stackalloc byte[sizeof(int)];
|
||||||
|
MemoryMarshal.Write(buf, ref capacity);
|
||||||
|
|
||||||
|
Storage.Write(buf, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetListLength(int length)
|
||||||
|
{
|
||||||
|
Span<byte> buf = stackalloc byte[sizeof(int)];
|
||||||
|
MemoryMarshal.Write(buf, ref length);
|
||||||
|
|
||||||
|
Storage.Write(buf, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private void ReadEntry(int index, out SaveFsEntry entry)
|
private void ReadEntry(int index, out SaveFsEntry entry)
|
||||||
{
|
{
|
||||||
Span<byte> bytes = stackalloc byte[_sizeOfEntry];
|
Span<byte> bytes = stackalloc byte[_sizeOfEntry];
|
||||||
|
@ -147,6 +241,34 @@ namespace LibHac.IO.Save
|
||||||
entry = GetEntryFromBytes(bytes);
|
entry = GetEntryFromBytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WriteEntry(int index, ref SaveFsEntry entry, ref SaveEntryKey key)
|
||||||
|
{
|
||||||
|
Span<byte> bytes = stackalloc byte[_sizeOfEntry];
|
||||||
|
Span<byte> nameSpan = bytes.Slice(4, MaxNameLength);
|
||||||
|
|
||||||
|
// Copy needed for .NET Framework compat
|
||||||
|
ref SaveFsEntry newEntry = ref GetEntryFromBytes(bytes);
|
||||||
|
newEntry = entry;
|
||||||
|
|
||||||
|
newEntry.Parent = key.Parent;
|
||||||
|
key.Name.CopyTo(nameSpan);
|
||||||
|
|
||||||
|
nameSpan.Slice(key.Name.Length).Fill(0);
|
||||||
|
|
||||||
|
WriteEntry(index, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteEntry(int index, ref SaveFsEntry entry)
|
||||||
|
{
|
||||||
|
Span<byte> bytes = stackalloc byte[_sizeOfEntry];
|
||||||
|
|
||||||
|
// Copy needed for .NET Framework compat
|
||||||
|
ref SaveFsEntry newEntry = ref GetEntryFromBytes(bytes);
|
||||||
|
newEntry = entry;
|
||||||
|
|
||||||
|
WriteEntry(index, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
private void ReadEntry(int index, Span<byte> entry)
|
private void ReadEntry(int index, Span<byte> entry)
|
||||||
{
|
{
|
||||||
Debug.Assert(entry.Length == _sizeOfEntry);
|
Debug.Assert(entry.Length == _sizeOfEntry);
|
||||||
|
@ -155,6 +277,14 @@ namespace LibHac.IO.Save
|
||||||
Storage.Read(entry, offset);
|
Storage.Read(entry, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void WriteEntry(int index, Span<byte> entry)
|
||||||
|
{
|
||||||
|
Debug.Assert(entry.Length == _sizeOfEntry);
|
||||||
|
|
||||||
|
int offset = index * _sizeOfEntry;
|
||||||
|
Storage.Write(entry, offset);
|
||||||
|
}
|
||||||
|
|
||||||
private ref SaveFsEntry GetEntryFromBytes(Span<byte> entry)
|
private ref SaveFsEntry GetEntryFromBytes(Span<byte> entry)
|
||||||
{
|
{
|
||||||
return ref MemoryMarshal.Cast<byte, SaveFsEntry>(entry)[0];
|
return ref MemoryMarshal.Cast<byte, SaveFsEntry>(entry)[0];
|
||||||
|
|
Loading…
Reference in a new issue