Fix bugs in the savedata file table

PathParser: Make sure IsFinished is properly set if the path is the root directory.

SaveFsList: When getting the index of a key, the wrong offset would be returned if the key did not exist.

Save/Rom FileTables: The rom file table code assumed the root directory was always at offset 0. This code was copied to the save file table, but the root directory in the save file table is never at index 0. Remove this assumption.
This commit is contained in:
Alex Barney 2019-03-23 16:56:08 -05:00
parent 4557665805
commit 5c1d8e0786
4 changed files with 39 additions and 12 deletions

View file

@ -3,6 +3,11 @@ using System.Diagnostics;
namespace LibHac.IO
{
/// <summary>
/// Enumerates a file or directory path one segment at a time.
/// </summary>
/// <remarks>When the parser is initialized <see cref="GetCurrent"/>
/// will return the root directory name, i.e. an empty string.</remarks>
public ref struct PathParser
{
private ReadOnlySpan<byte> _path;
@ -22,9 +27,16 @@ namespace LibHac.IO
_path = path;
_offset = 0;
_length = 0;
_finished = false;
_finished = path.Length == 1;
}
/// <summary>
/// Moves the iterator to the next segment in the path and gets the name of that segment.
/// </summary>
/// <param name="name">When this method returns, contains the path segment's name.</param>
/// <returns><see langword="true"/> if the <see cref="PathParser"/> was able to
/// move to the next path segment.
/// <see langword="false"/> if there are no remaining path segments.</returns>
public bool TryGetNext(out ReadOnlySpan<byte> name)
{
bool success = MoveNext();
@ -32,6 +44,12 @@ namespace LibHac.IO
return success;
}
/// <summary>
/// Moves the iterator to the next segment in the path.
/// </summary>
/// <returns><see langword="true"/> if the <see cref="PathParser"/> was able to
/// move to the next path segment.
/// <see langword="false"/> if there are no remaining path segments.</returns>
public bool MoveNext()
{
if (_finished) return false;
@ -50,11 +68,19 @@ namespace LibHac.IO
return true;
}
/// <summary>
/// Gets the current path segment's name.
/// </summary>
/// <returns>The current path segment.</returns>
public ReadOnlySpan<byte> GetCurrent()
{
return _path.Slice(_offset, _length);
}
/// <summary>
/// Checks if the current path segment is the final one.
/// </summary>
/// <returns><see langword="true"/> if the current path segment is the final one.</returns>
public bool IsFinished() => _finished;
}
}

View file

@ -206,7 +206,7 @@ namespace LibHac.IO.RomFs
path = PathTools.Normalize(path);
ReadOnlySpan<byte> pathBytes = Util.GetUtf8Bytes(path);
if(path == "/") throw new ArgumentException("Path cannot be empty");
if (path == "/") throw new ArgumentException("Path cannot be empty");
CreateFileRecursiveInternal(pathBytes, ref fileInfo);
}
@ -358,12 +358,13 @@ namespace LibHac.IO.RomFs
private void FindPathRecursive(ReadOnlySpan<byte> path, out RomEntryKey key)
{
var parser = new PathParser(path);
key = default;
key = new RomEntryKey(parser.GetCurrent(), 0);
do
while (!parser.IsFinished())
{
key.Parent = DirectoryTable.GetOffsetFromKey(ref key);
} while (parser.TryGetNext(out key.Name) && !parser.IsFinished());
parser.TryGetNext(out key.Name);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]

View file

@ -100,12 +100,13 @@ namespace LibHac.IO.Save
private void FindPathRecursive(ReadOnlySpan<byte> path, out SaveEntryKey key)
{
var parser = new PathParser(path);
key = default;
key = new SaveEntryKey(parser.GetCurrent(), 0);
do
while (!parser.IsFinished())
{
key.Parent = DirectoryTable.GetOffsetFromKey(ref key);
} while (parser.TryGetNext(out key.Name) && !parser.IsFinished());
parser.TryGetNext(out key.Name);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]

View file

@ -27,7 +27,6 @@ namespace LibHac.IO.Save
ref SaveFsEntry entry = ref GetEntryFromBytes(entryBytes);
int capacity = GetListCapacity();
int entryId = -1;
ReadEntry(UsedListHeadIndex, entryBytes);
@ -35,16 +34,16 @@ namespace LibHac.IO.Save
{
if (entry.Next > capacity) throw new IndexOutOfRangeException("Save entry index out of range");
entryId = entry.Next;
int entryId = entry.Next;
ReadEntry(entry.Next, out entry);
if (entry.Parent == key.Parent && Util.StringSpansEqual(name, key.Name))
{
break;
return entryId;
}
}
return entryId;
return -1;
}
public bool TryGetValue(ref SaveEntryKey key, out T value)