Implement adding entries to HierarchicalSaveFileTable

This commit is contained in:
Alex Barney 2019-03-29 16:25:22 -05:00
parent 5c1d8e0786
commit 079ffa6d3b
2 changed files with 219 additions and 12 deletions

View file

@ -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)]

View file

@ -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];