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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
|
@ -83,9 +87,77 @@ namespace LibHac.IO.Save
|
|||
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)
|
||||
{
|
||||
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))
|
||||
{
|
||||
|
@ -97,16 +169,21 @@ namespace LibHac.IO.Save
|
|||
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);
|
||||
key = new SaveEntryKey(parser.GetCurrent(), 0);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace LibHac.IO.Save
|
|||
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> name = entryBytes.Slice(4, MaxNameLength);
|
||||
|
@ -29,26 +29,92 @@ namespace LibHac.IO.Save
|
|||
int capacity = GetListCapacity();
|
||||
|
||||
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(entry.Next, out entry);
|
||||
ReadEntry(index, out entry);
|
||||
|
||||
if (entry.Parent == key.Parent && Util.StringSpansEqual(name, key.Name))
|
||||
{
|
||||
return entryId;
|
||||
}
|
||||
return (index, prevIndex);
|
||||
}
|
||||
|
||||
return -1;
|
||||
prevIndex = index;
|
||||
index = entry.Next;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int index = GetOffsetFromKey(ref key);
|
||||
int index = GetIndexFromKey(ref key).Index;
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
|
@ -123,6 +189,18 @@ namespace LibHac.IO.Save
|
|||
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()
|
||||
{
|
||||
Span<byte> buf = stackalloc byte[sizeof(int)];
|
||||
|
@ -139,6 +217,22 @@ namespace LibHac.IO.Save
|
|||
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)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[_sizeOfEntry];
|
||||
|
@ -147,6 +241,34 @@ namespace LibHac.IO.Save
|
|||
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)
|
||||
{
|
||||
Debug.Assert(entry.Length == _sizeOfEntry);
|
||||
|
@ -155,6 +277,14 @@ namespace LibHac.IO.Save
|
|||
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)
|
||||
{
|
||||
return ref MemoryMarshal.Cast<byte, SaveFsEntry>(entry)[0];
|
||||
|
|
Loading…
Reference in a new issue