From 5c1d8e0786c472ab437a81edd0107000ad7f0f40 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 23 Mar 2019 16:56:08 -0500 Subject: [PATCH] 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. --- src/LibHac/IO/PathParser.cs | 28 ++++++++++++++++++- .../IO/RomFs/HierarchicalRomFileTable.cs | 9 +++--- .../IO/Save/HierarchicalSaveFileTable.cs | 7 +++-- src/LibHac/IO/Save/SaveFsList.cs | 7 ++--- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/LibHac/IO/PathParser.cs b/src/LibHac/IO/PathParser.cs index 2fdb4e3f..a081fa27 100644 --- a/src/LibHac/IO/PathParser.cs +++ b/src/LibHac/IO/PathParser.cs @@ -3,6 +3,11 @@ using System.Diagnostics; namespace LibHac.IO { + /// + /// Enumerates a file or directory path one segment at a time. + /// + /// When the parser is initialized + /// will return the root directory name, i.e. an empty string. public ref struct PathParser { private ReadOnlySpan _path; @@ -22,9 +27,16 @@ namespace LibHac.IO _path = path; _offset = 0; _length = 0; - _finished = false; + _finished = path.Length == 1; } + /// + /// Moves the iterator to the next segment in the path and gets the name of that segment. + /// + /// When this method returns, contains the path segment's name. + /// if the was able to + /// move to the next path segment. + /// if there are no remaining path segments. public bool TryGetNext(out ReadOnlySpan name) { bool success = MoveNext(); @@ -32,6 +44,12 @@ namespace LibHac.IO return success; } + /// + /// Moves the iterator to the next segment in the path. + /// + /// if the was able to + /// move to the next path segment. + /// if there are no remaining path segments. public bool MoveNext() { if (_finished) return false; @@ -50,11 +68,19 @@ namespace LibHac.IO return true; } + /// + /// Gets the current path segment's name. + /// + /// The current path segment. public ReadOnlySpan GetCurrent() { return _path.Slice(_offset, _length); } + /// + /// Checks if the current path segment is the final one. + /// + /// if the current path segment is the final one. public bool IsFinished() => _finished; } } diff --git a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs index 64f2541b..3ca47d0c 100644 --- a/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs +++ b/src/LibHac/IO/RomFs/HierarchicalRomFileTable.cs @@ -206,7 +206,7 @@ namespace LibHac.IO.RomFs path = PathTools.Normalize(path); ReadOnlySpan 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 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)] diff --git a/src/LibHac/IO/Save/HierarchicalSaveFileTable.cs b/src/LibHac/IO/Save/HierarchicalSaveFileTable.cs index fceaa472..3d3bb82a 100644 --- a/src/LibHac/IO/Save/HierarchicalSaveFileTable.cs +++ b/src/LibHac/IO/Save/HierarchicalSaveFileTable.cs @@ -100,12 +100,13 @@ namespace LibHac.IO.Save private void FindPathRecursive(ReadOnlySpan 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)] diff --git a/src/LibHac/IO/Save/SaveFsList.cs b/src/LibHac/IO/Save/SaveFsList.cs index 0e091e7a..ad7af15a 100644 --- a/src/LibHac/IO/Save/SaveFsList.cs +++ b/src/LibHac/IO/Save/SaveFsList.cs @@ -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)