From fb4619f4ab7091305f6b289429f040032c0a9b12 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 27 Jan 2019 20:44:38 -0600 Subject: [PATCH] Use the dictionary in the romfs instead of creating our own --- ...ierarchicalIntegrityVerificationStorage.cs | 4 +- src/LibHac/IO/HierarchicalRomFileTable.cs | 197 ++++++++++++++++++ src/LibHac/IO/PathParser.cs | 60 ++++++ src/LibHac/IO/PathTools.cs | 38 ++++ src/LibHac/IO/RomFsDictionary.cs | 128 ++++++++++++ src/LibHac/IO/RomFsDirectory.cs | 48 ++--- src/LibHac/IO/RomFsFileSystem.cs | 106 +++------- src/LibHac/IO/RomfsEntry.cs | 98 --------- src/LibHac/IO/StorageExtensions.cs | 9 + src/hactoolnet/ProcessNca.cs | 4 +- src/hactoolnet/ProcessRomfs.cs | 4 +- 11 files changed, 481 insertions(+), 215 deletions(-) create mode 100644 src/LibHac/IO/HierarchicalRomFileTable.cs create mode 100644 src/LibHac/IO/PathParser.cs create mode 100644 src/LibHac/IO/RomFsDictionary.cs delete mode 100644 src/LibHac/IO/RomfsEntry.cs diff --git a/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs b/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs index c91c2932..f23b0859 100644 --- a/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs +++ b/src/LibHac/IO/HierarchicalIntegrityVerificationStorage.cs @@ -33,7 +33,9 @@ namespace LibHac.IO { var levelData = new IntegrityVerificationStorage(levelInfo[i], Levels[i - 1], integrityCheckLevel, leaveOpen); - Levels[i] = new CachedStorage(levelData, 4, leaveOpen); + int cacheCount = Math.Min((int)Util.DivideByRoundUp(levelData.Length, levelInfo[i].BlockSize), 4); + + Levels[i] = new CachedStorage(levelData, cacheCount, leaveOpen); LevelValidities[i - 1] = levelData.BlockValidities; IntegrityStorages[i - 1] = levelData; } diff --git a/src/LibHac/IO/HierarchicalRomFileTable.cs b/src/LibHac/IO/HierarchicalRomFileTable.cs new file mode 100644 index 00000000..2614790c --- /dev/null +++ b/src/LibHac/IO/HierarchicalRomFileTable.cs @@ -0,0 +1,197 @@ +using System; +using System.Runtime.InteropServices; + +namespace LibHac.IO +{ + public class HierarchicalRomFileTable + { + private IStorage DirHashTableStorage { get; } + private IStorage DirEntryTableStorage { get; } + private IStorage FileHashTableStorage { get; } + private IStorage FileEntryTableStorage { get; } + + private RomFsDictionary FileTable { get; } + private RomFsDictionary DirectoryTable { get; } + + public HierarchicalRomFileTable(IStorage dirHashTable, IStorage dirEntryTable, IStorage fileHashTable, + IStorage fileEntryTable) + { + DirHashTableStorage = dirHashTable; + DirEntryTableStorage = dirEntryTable; + FileHashTableStorage = fileHashTable; + FileEntryTableStorage = fileEntryTable; + + FileTable = new RomFsDictionary(FileHashTableStorage, FileEntryTableStorage); + DirectoryTable = new RomFsDictionary(DirHashTableStorage, DirEntryTableStorage); + } + + public bool OpenFile(string path, out RomFileInfo fileInfo) + { + FindFileRecursive(path.AsSpan(), out RomEntryKey key); + + if (FileTable.TryGetValue(ref key, out FileRomEntry entry, out int _)) + { + fileInfo = entry.Info; + return true; + } + + fileInfo = default; + return false; + } + + public bool OpenFile(int offset, out RomFileInfo fileInfo) + { + if (FileTable.TryGetValue(offset, out FileRomEntry entry)) + { + fileInfo = entry.Info; + return true; + } + + fileInfo = default; + return false; + } + + public bool OpenDirectory(string path, out FindPosition position) + { + FindDirectoryRecursive(path.AsSpan(), out RomEntryKey key); + + if (DirectoryTable.TryGetValue(ref key, out DirectoryRomEntry entry, out int _)) + { + position = entry.Pos; + return true; + } + + position = default; + return false; + } + + public bool OpenDirectory(int offset, out FindPosition position) + { + if (DirectoryTable.TryGetValue(offset, out DirectoryRomEntry entry)) + { + position = entry.Pos; + return true; + } + + position = default; + return false; + } + + public bool FindNextFile(ref FindPosition position, out RomFileInfo info, out string name) + { + if (FileTable.TryGetValue(position.NextFile, out FileRomEntry entry, out name)) + { + position.NextFile = entry.NextSibling; + info = entry.Info; + return true; + } + + info = default; + return false; + } + + public bool FindNextDirectory(ref FindPosition position, out string name) + { + if (DirectoryTable.TryGetValue(position.NextDirectory, out DirectoryRomEntry entry, out name)) + { + position.NextDirectory = entry.NextSibling; + return true; + } + + return false; + } + + private void FindFileRecursive(ReadOnlySpan path, out RomEntryKey key) + { + var parser = new PathParser(path); + FindParentDirectoryRecursive(ref parser, out DirectoryRomEntry _, out int parentOffset); + + key = new RomEntryKey(parser.GetCurrent(), parentOffset); + } + + private void FindDirectoryRecursive(ReadOnlySpan path, out RomEntryKey key) + { + var parser = new PathParser(path); + FindParentDirectoryRecursive(ref parser, out DirectoryRomEntry _, out int parentOffset); + + ReadOnlySpan name = parser.GetCurrent(); + if (name.Length == 0) parentOffset = 0; + + key = new RomEntryKey(name, parentOffset); + } + + private void FindParentDirectoryRecursive(ref PathParser parser, out DirectoryRomEntry parentEntry, out int parentOffset) + { + parentEntry = default; + parentOffset = default; + RomEntryKey key = default; + + while (parser.TryGetNext(out key.Name) && !parser.IsFinished()) + { + DirectoryTable.TryGetValue(ref key, out parentEntry, out parentOffset); + key.Parent = parentOffset; + } + } + } + + internal ref struct RomEntryKey + { + public ReadOnlySpan Name; + public int Parent; + + public RomEntryKey(ReadOnlySpan name, int parent) + { + Name = name; + Parent = parent; + } + + public uint GetRomHashCode() + { + uint hash = 123456789 ^ (uint)Parent; + + foreach (char c in Name) + { + hash = c ^ ((hash << 27) | (hash >> 5)); + } + + return hash; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct RomFsEntry where T : unmanaged + { + public int Parent; + public T Value; + public int Next; + public int KeyLength; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct FileRomEntry + { + public int NextSibling; + public RomFileInfo Info; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RomFileInfo + { + public long Offset; + public long Length; + } + + [StructLayout(LayoutKind.Sequential)] + public struct DirectoryRomEntry + { + public int NextSibling; + public FindPosition Pos; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FindPosition + { + public int NextDirectory; + public int NextFile; + } +} diff --git a/src/LibHac/IO/PathParser.cs b/src/LibHac/IO/PathParser.cs new file mode 100644 index 00000000..1a997c71 --- /dev/null +++ b/src/LibHac/IO/PathParser.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; + +namespace LibHac.IO +{ + public ref struct PathParser + { + private ReadOnlySpan _path; + private int _offset; + private int _length; + private bool _finished; + + public PathParser(ReadOnlySpan path) + { + Debug.Assert(PathTools.IsNormalized(path)); + + if (path.Length < 1 || path[0] != '/') + { + throw new ArgumentException("Path must begin with a '/'"); + } + + _path = path; + _offset = 0; + _length = 0; + _finished = false; + } + + public bool TryGetNext(out ReadOnlySpan name) + { + bool success = MoveNext(); + name = GetCurrent(); + return success; + } + + public bool MoveNext() + { + if (_finished) return false; + + _offset = _offset + _length + 1; + int end = _offset; + + while (end < _path.Length && _path[end] != '/') + { + end++; + } + + _finished = end + 1 >= _path.Length; + _length = end - _offset; + + return true; + } + + public ReadOnlySpan GetCurrent() + { + return _path.Slice(_offset, _length); + } + + public bool IsFinished() => _finished; + } +} diff --git a/src/LibHac/IO/PathTools.cs b/src/LibHac/IO/PathTools.cs index 599af969..e47b2830 100644 --- a/src/LibHac/IO/PathTools.cs +++ b/src/LibHac/IO/PathTools.cs @@ -91,6 +91,35 @@ namespace LibHac.IO return path.Substring(0, i); } + public static bool IsNormalized(ReadOnlySpan path) + { + var state = NormalizeState.Initial; + + foreach (char c in path) + { + switch (state) + { + case NormalizeState.Initial when c == '/': state = NormalizeState.Delimiter; break; + case NormalizeState.Initial: return false; + + case NormalizeState.Normal when c == '/': state = NormalizeState.Delimiter; break; + + case NormalizeState.Delimiter when c == '/': return false; + case NormalizeState.Delimiter when c == '.': state = NormalizeState.Dot; break; + case NormalizeState.Delimiter: state = NormalizeState.Normal; break; + + case NormalizeState.Dot when c == '/': return false; + case NormalizeState.Dot when c == '.': state = NormalizeState.DoubleDot; break; + case NormalizeState.Dot: state = NormalizeState.Normal; break; + + case NormalizeState.DoubleDot when c == '/': return false; + case NormalizeState.DoubleDot: state = NormalizeState.Normal; break; + } + } + + return state == NormalizeState.Normal || state == NormalizeState.Delimiter; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool IsDirectorySeparator(char c) { @@ -111,5 +140,14 @@ namespace LibHac.IO (index + 3 == path.Length || IsDirectorySeparator(path[index + 3])) && path[index + 1] == '.' && path[index + 2] == '.'; } + + private enum NormalizeState + { + Initial, + Normal, + Delimiter, + Dot, + DoubleDot + } } } diff --git a/src/LibHac/IO/RomFsDictionary.cs b/src/LibHac/IO/RomFsDictionary.cs new file mode 100644 index 00000000..bfd0903f --- /dev/null +++ b/src/LibHac/IO/RomFsDictionary.cs @@ -0,0 +1,128 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace LibHac.IO +{ + internal class RomFsDictionary where T : unmanaged + { + private int HashBucketCount { get; } + private IStorage BucketStorage { get; } + private IStorage EntryStorage { get; } + + // Hack around not being able to get the size of generic structures + private readonly int _sizeOfEntry = 12 + Marshal.SizeOf(); + + public RomFsDictionary(IStorage bucketStorage, IStorage entryStorage) + { + BucketStorage = bucketStorage; + EntryStorage = entryStorage; + HashBucketCount = (int)(bucketStorage.Length / 4); + } + + public bool TryGetValue(ref RomEntryKey key, out T value, out int offset) + { + int i = FindEntry(ref key); + offset = i; + + if (i >= 0) + { + GetEntryInternal(i, out RomFsEntry entry); + value = entry.Value; + return true; + } + + value = default; + return false; + } + + public bool TryGetValue(int offset, out T value, out string entryName) + { + if (offset < 0 || offset + _sizeOfEntry >= EntryStorage.Length) + { + value = default; + entryName = default; + return false; + } + + GetEntryInternal(offset, out RomFsEntry entry, out entryName); + value = entry.Value; + return true; + } + + public bool TryGetValue(int offset, out T value) + { + if (offset < 0 || offset + _sizeOfEntry >= EntryStorage.Length) + { + value = default; + return false; + } + + GetEntryInternal(offset, out RomFsEntry entry); + value = entry.Value; + return true; + } + + private int FindEntry(ref RomEntryKey key) + { + uint hashCode = key.GetRomHashCode(); + int i = GetBucket((int)(hashCode % HashBucketCount)); + + while (i != -1) + { + GetEntryInternal(i, out RomFsEntry entry); + + if (IsEqual(ref key, ref entry, i)) + { + break; + } + + i = entry.Next; + } + + return i; + } + + private bool IsEqual(ref RomEntryKey key, ref RomFsEntry entry, int entryOffset) + { + if (key.Parent != entry.Parent) return false; + if (key.Name.Length != entry.KeyLength) return false; + + GetEntryInternal(entryOffset, out RomFsEntry _, out string name); + + return key.Name.Equals(name.AsSpan(), StringComparison.Ordinal); + } + + private void GetEntryInternal(int offset, out RomFsEntry outEntry) + { + Span b = stackalloc byte[_sizeOfEntry]; + EntryStorage.Read(b, offset); + outEntry = MemoryMarshal.Read>(b); + } + + private void GetEntryInternal(int offset, out RomFsEntry outEntry, out string entryName) + { + GetEntryInternal(offset, out outEntry); + + if (outEntry.KeyLength > 0x300) + { + throw new InvalidDataException("Rom entry name is too long."); + } + + var buf = new byte[outEntry.KeyLength]; + EntryStorage.Read(buf, offset + _sizeOfEntry); + entryName = Encoding.ASCII.GetString(buf); + } + + private int GetBucket(int index) + { + Debug.Assert(index < HashBucketCount); + + Span buf = stackalloc byte[4]; + BucketStorage.Read(buf, index * 4); + return MemoryMarshal.Read(buf); + } + } +} diff --git a/src/LibHac/IO/RomFsDirectory.cs b/src/LibHac/IO/RomFsDirectory.cs index 4fdc5d2a..4a9f3ef8 100644 --- a/src/LibHac/IO/RomFsDirectory.cs +++ b/src/LibHac/IO/RomFsDirectory.cs @@ -1,52 +1,43 @@ using System.Collections.Generic; -using System.IO; namespace LibHac.IO { public class RomFsDirectory : IDirectory { - public IFileSystem ParentFileSystem { get; } + IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; + public RomFsFileSystem ParentFileSystem { get; } public string FullPath { get; } - private RomfsDir Directory { get; } public OpenDirectoryMode Mode { get; } - public RomFsDirectory(RomFsFileSystem fs, string path, OpenDirectoryMode mode) + private FindPosition InitialPosition { get; } + + public RomFsDirectory(RomFsFileSystem fs, string path, FindPosition position, OpenDirectoryMode mode) { - path = PathTools.Normalize(path); - - if (!fs.DirectoryDict.TryGetValue(path, out RomfsDir dir)) - { - throw new DirectoryNotFoundException(path); - } - ParentFileSystem = fs; - Directory = dir; + InitialPosition = position; FullPath = path; Mode = mode; } public IEnumerable Read() { + FindPosition position = InitialPosition; + HierarchicalRomFileTable tab = ParentFileSystem.FileTable; + if (Mode.HasFlag(OpenDirectoryMode.Directories)) { - RomfsDir dirEntry = Directory.FirstChild; - - while (dirEntry != null) + while (tab.FindNextDirectory(ref position, out string name)) { - yield return new DirectoryEntry(dirEntry.Name, FullPath + '/' + dirEntry.Name, DirectoryEntryType.Directory, 0); - dirEntry = dirEntry.NextSibling; + yield return new DirectoryEntry(name, FullPath + '/' + name, DirectoryEntryType.Directory, 0); } } if (Mode.HasFlag(OpenDirectoryMode.Files)) { - RomfsFile fileEntry = Directory.FirstFile; - - while (fileEntry != null) + while (tab.FindNextFile(ref position, out RomFileInfo info, out string name)) { - yield return new DirectoryEntry(fileEntry.Name, FullPath + '/' + fileEntry.Name, DirectoryEntryType.File, fileEntry.DataLength); - fileEntry = fileEntry.NextSibling; + yield return new DirectoryEntry(name, FullPath + '/' + name, DirectoryEntryType.File, info.Length); } } } @@ -55,25 +46,22 @@ namespace LibHac.IO { int count = 0; + FindPosition position = InitialPosition; + HierarchicalRomFileTable tab = ParentFileSystem.FileTable; + if (Mode.HasFlag(OpenDirectoryMode.Directories)) { - RomfsDir dirEntry = Directory.FirstChild; - - while (dirEntry != null) + while (tab.FindNextDirectory(ref position, out string _)) { count++; - dirEntry = dirEntry.NextSibling; } } if (Mode.HasFlag(OpenDirectoryMode.Files)) { - RomfsFile fileEntry = Directory.FirstFile; - - while (fileEntry != null) + while (tab.FindNextFile(ref position, out RomFileInfo _, out string _)) { count++; - fileEntry = fileEntry.NextSibling; } } diff --git a/src/LibHac/IO/RomFsFileSystem.cs b/src/LibHac/IO/RomFsFileSystem.cs index ec5c5f30..6ed8166b 100644 --- a/src/LibHac/IO/RomFsFileSystem.cs +++ b/src/LibHac/IO/RomFsFileSystem.cs @@ -1,97 +1,35 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; namespace LibHac.IO { public class RomFsFileSystem : IFileSystem { public RomfsHeader Header { get; } - public List Directories { get; } = new List(); - public List Files { get; } = new List(); - public RomfsDir RootDir { get; } - public Dictionary FileDict { get; } - public Dictionary DirectoryDict { get; } + public HierarchicalRomFileTable FileTable { get; } private IStorage BaseStorage { get; } // todo Don't parse entire table when opening public RomFsFileSystem(IStorage storage) { BaseStorage = storage; + Header = new RomfsHeader(storage.AsFile(OpenMode.Read)); - byte[] dirMetaTable; - byte[] fileMetaTable; + IStorage dirHashTable = storage.Slice(Header.DirHashTableOffset, Header.DirHashTableSize); + IStorage dirEntryTable = storage.Slice(Header.DirMetaTableOffset, Header.DirMetaTableSize); + IStorage fileHashTable = storage.Slice(Header.FileHashTableOffset, Header.FileHashTableSize); + IStorage fileEntryTable = storage.Slice(Header.FileMetaTableOffset, Header.FileMetaTableSize); - using (var reader = new BinaryReader(BaseStorage.AsStream(), Encoding.Default, true)) - { - Header = new RomfsHeader(reader); - reader.BaseStream.Position = Header.DirMetaTableOffset; - dirMetaTable = reader.ReadBytes((int)Header.DirMetaTableSize); - reader.BaseStream.Position = Header.FileMetaTableOffset; - fileMetaTable = reader.ReadBytes((int)Header.FileMetaTableSize); - } - - using (var reader = new BinaryReader(new MemoryStream(dirMetaTable))) - { - int position = 0; - while (position + 20 < Header.DirMetaTableSize) - { - var dir = new RomfsDir(reader) { Offset = position }; - Directories.Add(dir); - if (dir.ParentDirOffset == position) RootDir = dir; - position = (int)reader.BaseStream.Position; - } - } - - using (var reader = new BinaryReader(new MemoryStream(fileMetaTable))) - { - int position = 0; - while (position + 20 < Header.FileMetaTableSize) - { - var file = new RomfsFile(reader) { Offset = position }; - Files.Add(file); - position = (int)reader.BaseStream.Position; - } - } - - SetReferences(); - RomfsEntry.ResolveFilenames(Files); - RomfsEntry.ResolveFilenames(Directories); - FileDict = Files.ToDictionary(x => x.FullPath, x => x); - DirectoryDict = Directories.ToDictionary(x => x.FullPath, x => x); - } - - private void SetReferences() - { - Dictionary dirDict = Directories.ToDictionary(x => x.Offset, x => x); - Dictionary fileDict = Files.ToDictionary(x => x.Offset, x => x); - - foreach (RomfsDir dir in Directories) - { - if (dir.ParentDirOffset >= 0 && dir.ParentDirOffset != dir.Offset) dir.ParentDir = dirDict[dir.ParentDirOffset]; - if (dir.NextSiblingOffset >= 0) dir.NextSibling = dirDict[dir.NextSiblingOffset]; - if (dir.FirstChildOffset >= 0) dir.FirstChild = dirDict[dir.FirstChildOffset]; - if (dir.FirstFileOffset >= 0) dir.FirstFile = fileDict[dir.FirstFileOffset]; - if (dir.NextDirHashOffset >= 0) dir.NextDirHash = dirDict[dir.NextDirHashOffset]; - } - - foreach (RomfsFile file in Files) - { - if (file.ParentDirOffset >= 0) file.ParentDir = dirDict[file.ParentDirOffset]; - if (file.NextSiblingOffset >= 0) file.NextSibling = fileDict[file.NextSiblingOffset]; - if (file.NextFileHashOffset >= 0) file.NextFileHash = fileDict[file.NextFileHashOffset]; - } + FileTable = new HierarchicalRomFileTable(dirHashTable, dirEntryTable, fileHashTable, fileEntryTable); } public DirectoryEntryType GetEntryType(string path) { path = PathTools.Normalize(path); - if (FileDict.ContainsKey(path)) return DirectoryEntryType.File; - if (DirectoryDict.ContainsKey(path)) return DirectoryEntryType.Directory; + if (FileExists(path)) return DirectoryEntryType.File; + if (DirectoryExists(path)) return DirectoryEntryType.Directory; throw new FileNotFoundException(path); } @@ -103,14 +41,21 @@ namespace LibHac.IO public IDirectory OpenDirectory(string path, OpenDirectoryMode mode) { - return new RomFsDirectory(this, path, mode); + path = PathTools.Normalize(path); + + if (!FileTable.OpenDirectory(path, out FindPosition position)) + { + throw new DirectoryNotFoundException(); + } + + return new RomFsDirectory(this, path, position, mode); } public IFile OpenFile(string path, OpenMode mode) { path = PathTools.Normalize(path); - if (!FileDict.TryGetValue(path, out RomfsFile file)) + if (!FileTable.OpenFile(path, out RomFileInfo info)) { throw new FileNotFoundException(); } @@ -120,26 +65,21 @@ namespace LibHac.IO throw new ArgumentOutOfRangeException(nameof(mode), "RomFs files must be opened read-only."); } - return OpenFile(file); - } - - public IFile OpenFile(RomfsFile file) - { - return new RomFsFile(BaseStorage, Header.DataOffset + file.DataOffset, file.DataLength); + return new RomFsFile(BaseStorage, Header.DataOffset + info.Offset, info.Length); } public bool DirectoryExists(string path) { path = PathTools.Normalize(path); - return DirectoryDict.ContainsKey(path); + return FileTable.OpenDirectory(path, out FindPosition _); } public bool FileExists(string path) { path = PathTools.Normalize(path); - return FileDict.ContainsKey(path); + return FileTable.OpenFile(path, out RomFileInfo _); } public IStorage GetBaseStorage() @@ -168,8 +108,10 @@ namespace LibHac.IO public long FileMetaTableSize { get; } public long DataOffset { get; } - public RomfsHeader(BinaryReader reader) + public RomfsHeader(IFile file) { + var reader = new FileReader(file); + HeaderSize = reader.ReadInt64(); DirHashTableOffset = reader.ReadInt64(); DirHashTableSize = reader.ReadInt64(); diff --git a/src/LibHac/IO/RomfsEntry.cs b/src/LibHac/IO/RomfsEntry.cs deleted file mode 100644 index 30e1f881..00000000 --- a/src/LibHac/IO/RomfsEntry.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; - -namespace LibHac.IO -{ - public abstract class RomfsEntry - { - public int Offset { get; set; } - public int ParentDirOffset { get; protected set; } - public int NameLength { get; protected set; } - public string Name { get; protected set; } - - public RomfsDir ParentDir { get; internal set; } - public string FullPath { get; private set; } - - internal static void ResolveFilenames(IEnumerable entries) - { - var list = new List(); - var sb = new StringBuilder(); - const string delimiter = "/"; - foreach (RomfsEntry file in entries) - { - list.Add(file.Name); - RomfsDir dir = file.ParentDir; - while (dir != null) - { - list.Add(delimiter); - list.Add(dir.Name); - dir = dir.ParentDir; - } - - //todo - if (list.Count == 1) list.Add("/"); - - for (int i = list.Count - 1; i >= 0; i--) - { - sb.Append(list[i]); - } - - file.FullPath = sb.ToString(); - list.Clear(); - sb.Clear(); - } - } - } - - [DebuggerDisplay("{" + nameof(Name) + "}")] - public class RomfsDir : RomfsEntry - { - public int NextSiblingOffset { get; } - public int FirstChildOffset { get; } - public int FirstFileOffset { get; } - public int NextDirHashOffset { get; } - - public RomfsDir NextSibling { get; internal set; } - public RomfsDir FirstChild { get; internal set; } - public RomfsFile FirstFile { get; internal set; } - public RomfsDir NextDirHash { get; internal set; } - - public RomfsDir(BinaryReader reader) - { - ParentDirOffset = reader.ReadInt32(); - NextSiblingOffset = reader.ReadInt32(); - FirstChildOffset = reader.ReadInt32(); - FirstFileOffset = reader.ReadInt32(); - NextDirHashOffset = reader.ReadInt32(); - NameLength = reader.ReadInt32(); - Name = reader.ReadUtf8(NameLength); - reader.BaseStream.Position = Util.GetNextMultiple(reader.BaseStream.Position, 4); - } - } - - [DebuggerDisplay("{" + nameof(Name) + "}")] - public class RomfsFile : RomfsEntry - { - public int NextSiblingOffset { get; } - public long DataOffset { get; } - public long DataLength { get; } - public int NextFileHashOffset { get; } - - public RomfsFile NextSibling { get; internal set; } - public RomfsFile NextFileHash { get; internal set; } - - public RomfsFile(BinaryReader reader) - { - ParentDirOffset = reader.ReadInt32(); - NextSiblingOffset = reader.ReadInt32(); - DataOffset = reader.ReadInt64(); - DataLength = reader.ReadInt64(); - NextFileHashOffset = reader.ReadInt32(); - NameLength = reader.ReadInt32(); - Name = reader.ReadUtf8(NameLength); - reader.BaseStream.Position = Util.GetNextMultiple(reader.BaseStream.Position, 4); - } - } -} diff --git a/src/LibHac/IO/StorageExtensions.cs b/src/LibHac/IO/StorageExtensions.cs index 4e68377e..26c91b60 100644 --- a/src/LibHac/IO/StorageExtensions.cs +++ b/src/LibHac/IO/StorageExtensions.cs @@ -102,6 +102,15 @@ namespace LibHac.IO } } + public static byte[] ToArray(this IStorage storage) + { + if (storage == null) return new byte[0]; + + var arr = new byte[storage.Length]; + storage.CopyTo(new MemoryStorage(arr)); + return arr; + } + public static void CopyToStream(this IStorage input, Stream output, long length, IProgressReport progress = null) { const int bufferSize = 0x8000; diff --git a/src/hactoolnet/ProcessNca.cs b/src/hactoolnet/ProcessNca.cs index ce3ee3cd..51f40abc 100644 --- a/src/hactoolnet/ProcessNca.cs +++ b/src/hactoolnet/ProcessNca.cs @@ -46,9 +46,9 @@ namespace hactoolnet { var romfs = new RomFsFileSystem(nca.OpenSection(1, false, ctx.Options.IntegrityLevel, true)); - foreach (RomfsFile romfsFile in romfs.Files) + foreach (DirectoryEntry entry in romfs.EnumerateEntries()) { - ctx.Logger.LogMessage(romfsFile.FullPath); + ctx.Logger.LogMessage(entry.FullPath); } } diff --git a/src/hactoolnet/ProcessRomfs.cs b/src/hactoolnet/ProcessRomfs.cs index 4a76f45e..762e23d2 100644 --- a/src/hactoolnet/ProcessRomfs.cs +++ b/src/hactoolnet/ProcessRomfs.cs @@ -18,9 +18,9 @@ namespace hactoolnet { if (ctx.Options.ListRomFs) { - foreach (RomfsFile romfsFile in romfs.Files) + foreach (DirectoryEntry entry in romfs.EnumerateEntries()) { - ctx.Logger.LogMessage(romfsFile.FullPath); + ctx.Logger.LogMessage(entry.FullPath); } }