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 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 public ref struct PathParser
{ {
private ReadOnlySpan<byte> _path; private ReadOnlySpan<byte> _path;
@ -22,9 +27,16 @@ namespace LibHac.IO
_path = path; _path = path;
_offset = 0; _offset = 0;
_length = 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) public bool TryGetNext(out ReadOnlySpan<byte> name)
{ {
bool success = MoveNext(); bool success = MoveNext();
@ -32,6 +44,12 @@ namespace LibHac.IO
return success; 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() public bool MoveNext()
{ {
if (_finished) return false; if (_finished) return false;
@ -50,11 +68,19 @@ namespace LibHac.IO
return true; return true;
} }
/// <summary>
/// Gets the current path segment's name.
/// </summary>
/// <returns>The current path segment.</returns>
public ReadOnlySpan<byte> GetCurrent() public ReadOnlySpan<byte> GetCurrent()
{ {
return _path.Slice(_offset, _length); 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; public bool IsFinished() => _finished;
} }
} }

View file

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

View file

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

View file

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