From 734d86d336bb7b755aaf252bbe681fc634082e0d Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 14 Sep 2019 18:35:25 -0500 Subject: [PATCH] Change IDirectory to match the interface in FS --- src/LibHac/Common/BlitStruct.cs | 52 +++++++++ src/LibHac/Common/SpanHelpers.cs | 35 ++++++ src/LibHac/Common/StringUtils.cs | 85 +++++++++++++++ src/LibHac/Fs/AesXtsDirectory.cs | 87 ++++++++------- src/LibHac/Fs/AesXtsFileHeader.cs | 2 +- src/LibHac/Fs/AesXtsFileSystem.cs | 6 +- src/LibHac/Fs/ConcatenationDirectory.cs | 86 ++++++++++----- src/LibHac/Fs/ConcatenationFileSystem.cs | 5 +- src/LibHac/Fs/DirectoryEntry.cs | 21 +++- src/LibHac/Fs/DirectorySaveDataFileSystem.cs | 8 +- src/LibHac/Fs/DirectoryUtils.cs | 85 +++++++++++++++ src/LibHac/Fs/FileSystemExtensions.cs | 100 +++++++++--------- src/LibHac/Fs/FsPath.cs | 16 +++ src/LibHac/Fs/IDirectory.cs | 40 +++---- src/LibHac/Fs/IFileSystem.cs | 5 +- src/LibHac/Fs/LayeredFileSystem.cs | 2 +- src/LibHac/Fs/LayeredFileSystemDirectory.cs | 51 +++++---- src/LibHac/Fs/LocalDirectory.cs | 45 +++++--- src/LibHac/Fs/PartitionDirectory.cs | 53 +++++++--- src/LibHac/Fs/PartitionFileSystemBuilder.cs | 4 +- src/LibHac/Fs/PathTools.cs | 2 + src/LibHac/Fs/ReadOnlyDirectory.cs | 22 ---- src/LibHac/Fs/ReadOnlyFileSystem.cs | 8 +- src/LibHac/Fs/RomFs/RomFsBuilder.cs | 2 +- src/LibHac/Fs/RomFs/RomFsDirectory.cs | 84 +++++++++------ src/LibHac/Fs/RomFs/RomFsFileSystem.cs | 2 +- src/LibHac/Fs/Save/SaveDataDirectory.cs | 84 +++++++++------ src/LibHac/Fs/Save/SaveDataFileSystemCore.cs | 9 +- src/LibHac/Fs/SubdirectoryFileSystem.cs | 5 +- .../Fs/SubdirectoryFileSystemDirectory.cs | 34 ------ .../FsClient/Accessors/DirectoryAccessor.cs | 20 +++- .../FsClient/Accessors/FileSystemAccessor.cs | 2 +- .../FsClient/FileSystemClient.Directory.cs | 2 +- src/LibHac/FsClient/FileSystemManager.cs | 28 ++++- src/LibHac/FsClient/FileSystemManagerUtils.cs | 14 +-- src/LibHac/LibHac.csproj | 1 + src/LibHac/SwitchFs.cs | 10 +- src/hactoolnet/FsUtils.cs | 4 +- src/hactoolnet/ProcessNca.cs | 2 +- src/hactoolnet/ProcessRomfs.cs | 2 +- src/hactoolnet/ProcessSave.cs | 4 +- src/hactoolnet/ProcessSwitchFs.cs | 4 +- 42 files changed, 738 insertions(+), 395 deletions(-) create mode 100644 src/LibHac/Common/BlitStruct.cs create mode 100644 src/LibHac/Common/SpanHelpers.cs create mode 100644 src/LibHac/Common/StringUtils.cs create mode 100644 src/LibHac/Fs/DirectoryUtils.cs create mode 100644 src/LibHac/Fs/FsPath.cs delete mode 100644 src/LibHac/Fs/ReadOnlyDirectory.cs delete mode 100644 src/LibHac/Fs/SubdirectoryFileSystemDirectory.cs diff --git a/src/LibHac/Common/BlitStruct.cs b/src/LibHac/Common/BlitStruct.cs new file mode 100644 index 00000000..70e4dad8 --- /dev/null +++ b/src/LibHac/Common/BlitStruct.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common +{ + public ref struct BlitStruct where T : unmanaged + { + private readonly Span _buffer; + + public int Length => _buffer.Length; + + public ref T Value => ref _buffer[0]; + public ref T this[int index] => ref _buffer[index]; + + public BlitStruct(Span data) + { + _buffer = data; + + Debug.Assert(_buffer.Length != 0); + } + + public BlitStruct(Span data) + { + _buffer = MemoryMarshal.Cast(data); + + Debug.Assert(_buffer.Length != 0); + } + + public BlitStruct(ref T data) + { + _buffer = SpanHelpers.AsSpan(ref data); + } + + public Span GetByteSpan() + { + return MemoryMarshal.Cast(_buffer); + } + + public Span GetByteSpan(int elementIndex) + { + Span element = _buffer.Slice(elementIndex, 1); + return MemoryMarshal.Cast(element); + } + + public static int QueryByteLength(int elementCount) + { + return Unsafe.SizeOf() * elementCount; + } + } +} diff --git a/src/LibHac/Common/SpanHelpers.cs b/src/LibHac/Common/SpanHelpers.cs new file mode 100644 index 00000000..ce8aabd4 --- /dev/null +++ b/src/LibHac/Common/SpanHelpers.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LibHac.Common +{ + public static class SpanHelpers + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if NETCOREAPP + public static Span CreateSpan(ref T reference, int length) + { + return MemoryMarshal.CreateSpan(ref reference, length); + } +#else + public static unsafe Span CreateSpan(ref T reference, int length) + { + return new Span(Unsafe.AsPointer(ref reference), length); + } +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsSpan(ref T reference) where T : unmanaged + { + return CreateSpan(ref reference, 1); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span AsByteSpan(ref T reference) where T : unmanaged + { + Span span = AsSpan(ref reference); + return MemoryMarshal.Cast(span); + } + } +} diff --git a/src/LibHac/Common/StringUtils.cs b/src/LibHac/Common/StringUtils.cs new file mode 100644 index 00000000..364d8396 --- /dev/null +++ b/src/LibHac/Common/StringUtils.cs @@ -0,0 +1,85 @@ +using System; +using System.Text; + +namespace LibHac.Common +{ + public static class StringUtils + { + public static int Copy(Span dest, ReadOnlySpan source) + { + int maxLen = Math.Min(dest.Length, source.Length); + + int i; + for (i = 0; i < maxLen && source[i] != 0; i++) + dest[i] = source[i]; + + if (i < dest.Length) + { + dest[i] = 0; + } + + return i; + } + + public static int GetLength(ReadOnlySpan s) + { + int i = 0; + + while (i < s.Length && s[i] != 0) + { + i++; + } + + return i; + } + + /// + /// Concatenates 2 byte strings. + /// + /// + /// + /// The length of the resulting string. + /// This function appends the source string to the end of the null-terminated destination string. + /// If the destination buffer is not large enough to contain the resulting string, + /// bytes from the source string will be appended to the destination string util the buffer is full. + /// If the length of the final string is the same length of the destination buffer, + /// no null terminating byte will be written to the end of the string. + public static int Concat(Span dest, ReadOnlySpan source) + { + return Concat(dest, GetLength(dest), source); + } + + public static int Concat(Span dest, int destLength, ReadOnlySpan source) + { + int iDest = destLength; + + for (int i = 0; iDest < dest.Length && i < source.Length && source[i] != 0; i++, iDest++) + { + dest[iDest] = source[i]; + } + + if (iDest < dest.Length) + { + dest[iDest] = 0; + } + + return iDest; + } + + public static string FromUtf8Z(this Span value) => FromUtf8Z((ReadOnlySpan)value); + + public static string FromUtf8Z(this ReadOnlySpan value) + { + int i; + for (i = 0; i < value.Length && value[i] != 0; i++) { } + + value = value.Slice(0, i); + +#if STRING_SPAN + return Encoding.UTF8.GetString(value); +#else + return Encoding.UTF8.GetString(value.ToArray()); +#endif + } + } +} diff --git a/src/LibHac/Fs/AesXtsDirectory.cs b/src/LibHac/Fs/AesXtsDirectory.cs index 67106723..a8bd71cc 100644 --- a/src/LibHac/Fs/AesXtsDirectory.cs +++ b/src/LibHac/Fs/AesXtsDirectory.cs @@ -1,85 +1,90 @@ using System; -using System.Collections.Generic; +using LibHac.Common; namespace LibHac.Fs { public class AesXtsDirectory : IDirectory { - IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; - public AesXtsFileSystem ParentFileSystem { get; } - - public string FullPath { get; } - public OpenDirectoryMode Mode { get; } + private string Path { get; } + private OpenDirectoryMode Mode { get; } private IFileSystem BaseFileSystem { get; } private IDirectory BaseDirectory { get; } - public AesXtsDirectory(AesXtsFileSystem parentFs, IDirectory baseDir, OpenDirectoryMode mode) + public AesXtsDirectory(IFileSystem baseFs, IDirectory baseDir, string path, OpenDirectoryMode mode) { - ParentFileSystem = parentFs; + BaseFileSystem = baseFs; BaseDirectory = baseDir; Mode = mode; - BaseFileSystem = BaseDirectory.ParentFileSystem; - FullPath = BaseDirectory.FullPath; + Path = path; } - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - foreach (DirectoryEntry entry in BaseDirectory.Read()) - { - if (entry.Type == DirectoryEntryType.Directory) - { - yield return entry; - } - else - { - // todo: FS returns invalid file entries with a size of 0 - long size = GetAesXtsFileSize(entry.FullPath); - if (size == -1) continue; + Result rc = BaseDirectory.Read(out entriesRead, entryBuffer); + if (rc.IsFailure()) return rc; - yield return new DirectoryEntry(entry.Name, entry.FullPath, entry.Type, size); + for (int i = 0; i < entriesRead; i++) + { + ref DirectoryEntry entry = ref entryBuffer[i]; + + if (entry.Type == DirectoryEntryType.File) + { + if (Mode.HasFlag(OpenDirectoryMode.NoFileSize)) + { + entry.Size = 0; + } + else + { + string entryName = Util.GetUtf8StringNullTerminated(entry.Name); + entry.Size = GetAesXtsFileSize(PathTools.Combine(Path, entryName)); + } } } + + return Result.Success; } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { - return BaseDirectory.GetEntryCount(); + return BaseDirectory.GetEntryCount(out entryCount); } /// - /// Reads the size of a NAX0 file from its header. Returns -1 on error. + /// Reads the size of a NAX0 file from its header. Returns 0 on error. /// /// /// private long GetAesXtsFileSize(string path) { + const long magicOffset = 0x20; + const long fileSizeOffset = 0x48; + + // Todo: Remove try/catch when more code uses Result try { - BaseFileSystem.OpenFile(out IFile file, path, OpenMode.Read).ThrowIfFailure(); + Result rc = BaseFileSystem.OpenFile(out IFile file, path, OpenMode.Read); + if (rc.IsFailure()) return 0; using (file) { - file.GetSize(out long fileSize).ThrowIfFailure(); + uint magic = 0; + long fileSize = 0; + long bytesRead; - if (fileSize < 0x50) - { - return -1; - } + file.Read(out bytesRead, magicOffset, SpanHelpers.AsByteSpan(ref magic), ReadOption.None); + if (bytesRead != sizeof(uint) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0; - // todo: Use result codes - var buffer = new byte[8]; + file.Read(out bytesRead, fileSizeOffset, SpanHelpers.AsByteSpan(ref fileSize), ReadOption.None); + if (bytesRead != sizeof(long) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0; - file.Read(out long _, 0x20, buffer); - if (BitConverter.ToUInt32(buffer, 0) != 0x3058414E) return -1; - - file.Read(out long _, 0x48, buffer); - return BitConverter.ToInt64(buffer, 0); + return fileSize; } + } - catch (ArgumentOutOfRangeException) + catch (Exception) { - return -1; + return 0; } } } diff --git a/src/LibHac/Fs/AesXtsFileHeader.cs b/src/LibHac/Fs/AesXtsFileHeader.cs index 6c416179..292b39e0 100644 --- a/src/LibHac/Fs/AesXtsFileHeader.cs +++ b/src/LibHac/Fs/AesXtsFileHeader.cs @@ -7,7 +7,7 @@ namespace LibHac.Fs { public class AesXtsFileHeader { - private const uint AesXtsFileMagic = 0x3058414E; + internal const uint AesXtsFileMagic = 0x3058414E; public byte[] Signature { get; set; } = new byte[0x20]; public uint Magic { get; } public byte[] EncryptedKey1 { get; } = new byte[0x10]; diff --git a/src/LibHac/Fs/AesXtsFileSystem.cs b/src/LibHac/Fs/AesXtsFileSystem.cs index 3078bd05..529e4c5b 100644 --- a/src/LibHac/Fs/AesXtsFileSystem.cs +++ b/src/LibHac/Fs/AesXtsFileSystem.cs @@ -92,7 +92,7 @@ namespace LibHac.Fs Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode); if (rc.IsFailure()) return rc; - directory = new AesXtsDirectory(this, baseDir, mode); + directory = new AesXtsDirectory(BaseFileSystem, baseDir, path, mode); return Result.Success; } @@ -147,9 +147,7 @@ namespace LibHac.Fs private void RenameDirectoryImpl(string srcDir, string dstDir, bool doRollback) { - OpenDirectory(out IDirectory dir, dstDir, OpenDirectoryMode.All); - - foreach (DirectoryEntry entry in dir.Read()) + foreach (DirectoryEntryEx entry in this.EnumerateEntries(srcDir, "*")) { string subSrcPath = $"{srcDir}/{entry.Name}"; string subDstPath = $"{dstDir}/{entry.Name}"; diff --git a/src/LibHac/Fs/ConcatenationDirectory.cs b/src/LibHac/Fs/ConcatenationDirectory.cs index 90718113..88972e75 100644 --- a/src/LibHac/Fs/ConcatenationDirectory.cs +++ b/src/LibHac/Fs/ConcatenationDirectory.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using LibHac.Common; #if CROSS_PLATFORM using System.Runtime.InteropServices; @@ -8,58 +9,92 @@ namespace LibHac.Fs { public class ConcatenationDirectory : IDirectory { - IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; - public string FullPath { get; } - public OpenDirectoryMode Mode { get; } + private string Path { get; } + private OpenDirectoryMode Mode { get; } private ConcatenationFileSystem ParentFileSystem { get; } + private IFileSystem BaseFileSystem { get; } private IDirectory ParentDirectory { get; } - public ConcatenationDirectory(ConcatenationFileSystem fs, IDirectory parentDirectory, OpenDirectoryMode mode) + public ConcatenationDirectory(ConcatenationFileSystem fs, IFileSystem baseFs, IDirectory parentDirectory, OpenDirectoryMode mode, string path) { ParentFileSystem = fs; + BaseFileSystem = baseFs; ParentDirectory = parentDirectory; Mode = mode; - FullPath = parentDirectory.FullPath; + Path = path; } - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - foreach (DirectoryEntry entry in ParentDirectory.Read()) + entriesRead = 0; + var entry = new DirectoryEntry(); + Span entrySpan = SpanHelpers.AsSpan(ref entry); + + int i; + for (i = 0; i < entryBuffer.Length; i++) { - bool isSplit = IsConcatenationFile(entry); + Result rc = ParentDirectory.Read(out long baseEntriesRead, entrySpan); + if (rc.IsFailure()) return rc; - if (!CanReturnEntry(entry, isSplit)) continue; + if (baseEntriesRead == 0) break; - if (isSplit) + // Check if the current open mode says we should return the entry + bool isConcatFile = IsConcatenationFile(entry); + if (!CanReturnEntry(entry, isConcatFile)) continue; + + if (isConcatFile) { entry.Type = DirectoryEntryType.File; - entry.Size = ParentFileSystem.GetConcatenationFileSize(entry.FullPath); - entry.Attributes = NxFileAttributes.None; + + if (!Mode.HasFlag(OpenDirectoryMode.NoFileSize)) + { + string entryName = Util.GetUtf8StringNullTerminated(entry.Name); + string entryFullPath = PathTools.Combine(Path, entryName); + + entry.Size = ParentFileSystem.GetConcatenationFileSize(entryFullPath); + } } - yield return entry; + entry.Attributes = NxFileAttributes.None; + + entryBuffer[i] = entry; } + + entriesRead = i; + return Result.Success; } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { - int count = 0; + entryCount = 0; + long count = 0; - foreach (DirectoryEntry entry in ParentDirectory.Read()) + Result rc = BaseFileSystem.OpenDirectory(out IDirectory _, Path, + OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize); + if (rc.IsFailure()) return rc; + + var entry = new DirectoryEntry(); + Span entrySpan = SpanHelpers.AsSpan(ref entry); + + while (true) { - bool isSplit = IsConcatenationFile(entry); + rc = ParentDirectory.Read(out long baseEntriesRead, entrySpan); + if (rc.IsFailure()) return rc; - if (CanReturnEntry(entry, isSplit)) count++; + if (baseEntriesRead == 0) break; + + if (CanReturnEntry(entry, IsConcatenationFile(entry))) count++; } - return count; + entryCount = count; + return Result.Success; } - private bool CanReturnEntry(DirectoryEntry entry, bool isSplit) + private bool CanReturnEntry(DirectoryEntry entry, bool isConcatFile) { - return Mode.HasFlag(OpenDirectoryMode.File) && (entry.Type == DirectoryEntryType.File || isSplit) || - Mode.HasFlag(OpenDirectoryMode.Directory) && entry.Type == DirectoryEntryType.Directory && !isSplit; + return Mode.HasFlag(OpenDirectoryMode.File) && (entry.Type == DirectoryEntryType.File || isConcatFile) || + Mode.HasFlag(OpenDirectoryMode.Directory) && entry.Type == DirectoryEntryType.Directory && !isConcatFile; } private bool IsConcatenationFile(DirectoryEntry entry) @@ -71,7 +106,10 @@ namespace LibHac.Fs } else { - return ParentFileSystem.IsConcatenationFile(entry.FullPath); + string name = Util.GetUtf8StringNullTerminated(entry.Name); + string fullPath = PathTools.Combine(Path, name); + + return ParentFileSystem.IsConcatenationFile(fullPath); } #else return ConcatenationFileSystem.HasConcatenationFileAttribute(entry.Attributes); diff --git a/src/LibHac/Fs/ConcatenationFileSystem.cs b/src/LibHac/Fs/ConcatenationFileSystem.cs index 9c2a4471..b25f0f3e 100644 --- a/src/LibHac/Fs/ConcatenationFileSystem.cs +++ b/src/LibHac/Fs/ConcatenationFileSystem.cs @@ -80,7 +80,8 @@ namespace LibHac.Fs Result rc = BaseFileSystem.OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.Directory); if (rc.IsFailure()) return false; - if (dir.GetEntryCount() > 0) return false; + rc = dir.GetEntryCount(out long subDirCount); + if (rc.IsFailure() || subDirCount > 0) return false; // Should be enough checks to avoid most false positives. Maybe return true; @@ -222,7 +223,7 @@ namespace LibHac.Fs Result rc = BaseFileSystem.OpenDirectory(out IDirectory parentDir, path, OpenDirectoryMode.All); if (rc.IsFailure()) return rc; - directory = new ConcatenationDirectory(this, parentDir, mode); + directory = new ConcatenationDirectory(this, BaseFileSystem, parentDir, mode, path); return Result.Success; } diff --git a/src/LibHac/Fs/DirectoryEntry.cs b/src/LibHac/Fs/DirectoryEntry.cs index fbefb8e7..62293fa7 100644 --- a/src/LibHac/Fs/DirectoryEntry.cs +++ b/src/LibHac/Fs/DirectoryEntry.cs @@ -1,8 +1,10 @@ using System; +using System.Runtime.InteropServices; +using LibHac.Common; namespace LibHac.Fs { - public class DirectoryEntry + public class DirectoryEntryEx { public string Name { get; set; } public string FullPath { get; set; } @@ -10,7 +12,7 @@ namespace LibHac.Fs public DirectoryEntryType Type { get; set; } public long Size { get; set; } - public DirectoryEntry(string name, string fullPath, DirectoryEntryType type, long size) + public DirectoryEntryEx(string name, string fullPath, DirectoryEntryType type, long size) { Name = name; FullPath = PathTools.Normalize(fullPath); @@ -19,7 +21,18 @@ namespace LibHac.Fs } } - public enum DirectoryEntryType + [StructLayout(LayoutKind.Explicit)] + public struct DirectoryEntry + { + [FieldOffset(0)] private byte _name; + [FieldOffset(0x301)] public NxFileAttributes Attributes; + [FieldOffset(0x304)] public DirectoryEntryType Type; + [FieldOffset(0x308)] public long Size; + + public Span Name => SpanHelpers.CreateSpan(ref _name, PathTools.MaxPathLength + 1); + } + + public enum DirectoryEntryType : byte { Directory, File, @@ -27,7 +40,7 @@ namespace LibHac.Fs } [Flags] - public enum NxFileAttributes + public enum NxFileAttributes : byte { None = 0, Directory = 1 << 0, diff --git a/src/LibHac/Fs/DirectorySaveDataFileSystem.cs b/src/LibHac/Fs/DirectorySaveDataFileSystem.cs index 9e4aca7e..70d74b54 100644 --- a/src/LibHac/Fs/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/Fs/DirectorySaveDataFileSystem.cs @@ -212,13 +212,7 @@ namespace LibHac.Fs rc = BaseFs.CreateDirectory(dest); if (rc.IsFailure()) return rc; - rc = BaseFs.OpenDirectory(out IDirectory sourceDir, src, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; - - rc = BaseFs.OpenDirectory(out IDirectory destDir, dest, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; - - return sourceDir.CopyDirectory(destDir); + return this.CopyDirectory(this, src, dest); } internal void NotifyCloseWritableFile() diff --git a/src/LibHac/Fs/DirectoryUtils.cs b/src/LibHac/Fs/DirectoryUtils.cs new file mode 100644 index 00000000..2ff0839f --- /dev/null +++ b/src/LibHac/Fs/DirectoryUtils.cs @@ -0,0 +1,85 @@ +using System; +using LibHac.Common; + +namespace LibHac.Fs +{ + public static class DirectoryUtils + { + public delegate Result Blah(ReadOnlySpan path, ref DirectoryEntry entry); + + public static Result IterateDirectoryRecursivelyInternal(IFileSystem fs, Span workPath, + ref DirectoryEntry entry, Blah onEnterDir, Blah onExitDir, Blah onFile) + { + string currentPath = Util.GetUtf8StringNullTerminated(workPath); + + Result rc = fs.OpenDirectory(out IDirectory _, currentPath, OpenDirectoryMode.All); + if (rc.IsFailure()) return rc; + + onFile(workPath, ref entry); + + return Result.Success; + } + + public static Result IterateDirectoryRecursively(IFileSystem fs, ReadOnlySpan path, Blah onEnterDir, Blah onExitDir, Blah onFile) + { + return Result.Success; + } + + public static Result CopyDirectoryRecursively(IFileSystem sourceFs, IFileSystem destFs, string sourcePath, + string destPath) + { + return Result.Success; + } + + public static Result CopyFile(IFileSystem destFs, IFileSystem sourceFs, ReadOnlySpan destParentPath, + ReadOnlySpan sourcePath, ref DirectoryEntry dirEntry, Span copyBuffer) + { + IFile srcFile = null; + IFile dstFile = null; + + try + { + Result rc = sourceFs.OpenFile(out srcFile, sourcePath.FromUtf8Z(), OpenMode.Read); + if (rc.IsFailure()) return rc; + + FsPath dstPath = default; + int dstPathLen = StringUtils.Concat(dstPath.Str, destParentPath); + dstPathLen = StringUtils.Concat(dstPath.Str, dstPathLen, dirEntry.Name); + + if (dstPathLen > FsPath.MaxLength) + { + throw new ArgumentException(); + } + + string dstPathStr = dstPath.Str.FromUtf8Z(); + + rc = destFs.CreateFile(dstPathStr, dirEntry.Size, CreateFileOptions.None); + if (rc.IsFailure()) return rc; + + rc = destFs.OpenFile(out dstFile, dstPathStr, OpenMode.Write); + if (rc.IsFailure()) return rc; + + long fileSize = dirEntry.Size; + long offset = 0; + + while (offset < fileSize) + { + rc = srcFile.Read(out long bytesRead, offset, copyBuffer, ReadOption.None); + if (rc.IsFailure()) return rc; + + rc = dstFile.Write(offset, copyBuffer.Slice(0, (int)bytesRead), WriteOption.None); + if (rc.IsFailure()) return rc; + + offset += bytesRead; + } + + return Result.Success; + } + finally + { + srcFile?.Dispose(); + dstFile?.Dispose(); + } + } + } +} diff --git a/src/LibHac/Fs/FileSystemExtensions.cs b/src/LibHac/Fs/FileSystemExtensions.cs index 50997c48..c55b1498 100644 --- a/src/LibHac/Fs/FileSystemExtensions.cs +++ b/src/LibHac/Fs/FileSystemExtensions.cs @@ -2,33 +2,27 @@ using System.Buffers; using System.Collections.Generic; using System.IO; +using LibHac.Common; namespace LibHac.Fs { public static class FileSystemExtensions { - public static Result CopyDirectory(this IDirectory source, IDirectory dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None) + public static Result CopyDirectory(this IFileSystem sourceFs, IFileSystem destFs, string sourcePath, string destPath, + IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None) { - IFileSystem sourceFs = source.ParentFileSystem; - IFileSystem destFs = dest.ParentFileSystem; Result rc; - foreach (DirectoryEntry entry in source.Read()) + foreach (DirectoryEntryEx entry in sourceFs.EnumerateEntries()) { - string subSrcPath = PathTools.Normalize(PathTools.Combine(source.FullPath, entry.Name)); - string subDstPath = PathTools.Normalize(PathTools.Combine(dest.FullPath, entry.Name)); + string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); + string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); if (entry.Type == DirectoryEntryType.Directory) { destFs.EnsureDirectoryExists(subDstPath); - rc = sourceFs.OpenDirectory(out IDirectory subSrcDir, subSrcPath, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; - - rc = destFs.OpenDirectory(out IDirectory subDstDir, subDstPath, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; - - rc = subSrcDir.CopyDirectory(subDstDir, logger, options); + rc = sourceFs.CopyDirectory(destFs, subSrcPath, subDstPath, logger, options); if (rc.IsFailure()) return rc; } @@ -56,52 +50,45 @@ namespace LibHac.Fs return Result.Success; } - public static void CopyFileSystem(this IFileSystem source, IFileSystem dest, IProgressReport logger = null, CreateFileOptions options = CreateFileOptions.None) - { - source.OpenDirectory(out IDirectory sourceRoot, "/", OpenDirectoryMode.All).ThrowIfFailure(); - dest.OpenDirectory(out IDirectory destRoot, "/", OpenDirectoryMode.All).ThrowIfFailure(); - - sourceRoot.CopyDirectory(destRoot, logger, options).ThrowIfFailure(); - } - public static void Extract(this IFileSystem source, string destinationPath, IProgressReport logger = null) { var destFs = new LocalFileSystem(destinationPath); - source.CopyFileSystem(destFs, logger); + source.CopyDirectory(destFs, "/", "/", logger); } - public static IEnumerable EnumerateEntries(this IFileSystem fileSystem) + public static IEnumerable EnumerateEntries(this IFileSystem fileSystem) { - return fileSystem.EnumerateEntries("*"); + return fileSystem.EnumerateEntries("/", "*"); } - public static IEnumerable EnumerateEntries(this IFileSystem fileSystem, string searchPattern) + public static IEnumerable EnumerateEntries(this IFileSystem fileSystem, string path, string searchPattern) { - return fileSystem.EnumerateEntries(searchPattern, SearchOptions.RecurseSubdirectories); + return fileSystem.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories); } - public static IEnumerable EnumerateEntries(this IFileSystem fileSystem, string searchPattern, SearchOptions searchOptions) + public static IEnumerable EnumerateEntries(this IFileSystem fileSystem, string searchPattern, SearchOptions searchOptions) { - fileSystem.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure(); - - return rootDir.EnumerateEntries(searchPattern, searchOptions); + return EnumerateEntries(fileSystem, "/", searchPattern, searchOptions); } - public static IEnumerable EnumerateEntries(this IDirectory directory) - { - return directory.EnumerateEntries("*", SearchOptions.Default); - } - - public static IEnumerable EnumerateEntries(this IDirectory directory, string searchPattern, SearchOptions searchOptions) + public static IEnumerable EnumerateEntries(this IFileSystem fileSystem, string path, string searchPattern, SearchOptions searchOptions) { bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive); bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories); - IFileSystem fs = directory.ParentFileSystem; + IFileSystem fs = fileSystem; + DirectoryEntry dirEntry = default; - foreach (DirectoryEntry entry in directory.Read()) + fileSystem.OpenDirectory(out IDirectory directory, path, OpenDirectoryMode.All).ThrowIfFailure(); + + while(true) { + directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)).ThrowIfFailure(); + if (entriesRead == 0) break; + + DirectoryEntryEx entry = GetDirectoryEntryEx(ref dirEntry, path); + if (PathTools.MatchesPattern(searchPattern, entry.Name, ignoreCase)) { yield return entry; @@ -109,15 +96,28 @@ namespace LibHac.Fs if (entry.Type != DirectoryEntryType.Directory || !recurse) continue; - fs.OpenDirectory(out IDirectory subDir, PathTools.Combine(directory.FullPath, entry.Name), OpenDirectoryMode.All).ThrowIfFailure(); + IEnumerable subEntries = + fs.EnumerateEntries(PathTools.Combine(path, entry.Name), searchPattern, + searchOptions); - foreach (DirectoryEntry subEntry in subDir.EnumerateEntries(searchPattern, searchOptions)) + foreach (DirectoryEntryEx subEntry in subEntries) { yield return subEntry; } } } + private static DirectoryEntryEx GetDirectoryEntryEx(ref DirectoryEntry entry, string parentPath) + { + string name = entry.Name.FromUtf8Z(); + string path = PathTools.Combine(parentPath, name); + + var entryEx = new DirectoryEntryEx(name, path, entry.Type, entry.Size); + entryEx.Attributes = entry.Attributes; + + return entryEx; + } + public static void CopyTo(this IFile file, IFile dest, IProgressReport logger = null) { const int bufferSize = 0x8000; @@ -157,16 +157,14 @@ namespace LibHac.Fs public static int GetEntryCount(this IFileSystem fs, OpenDirectoryMode mode) { - fs.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure(); - - return rootDir.GetEntryCountRecursive(mode); + return GetEntryCountRecursive(fs, "/", mode); } - public static int GetEntryCountRecursive(this IDirectory directory, OpenDirectoryMode mode) + public static int GetEntryCountRecursive(this IFileSystem fs, string path, OpenDirectoryMode mode) { int count = 0; - foreach (DirectoryEntry entry in directory.EnumerateEntries()) + foreach (DirectoryEntryEx entry in fs.EnumerateEntries(path, "*")) { if (entry.Type == DirectoryEntryType.Directory && (mode & OpenDirectoryMode.Directory) != 0 || entry.Type == DirectoryEntryType.File && (mode & OpenDirectoryMode.File) != 0) @@ -194,19 +192,17 @@ namespace LibHac.Fs fs.QueryEntry(Span.Empty, Span.Empty, QueryId.MakeConcatFile, path); } - public static void CleanDirectoryRecursivelyGeneric(IDirectory directory) + public static void CleanDirectoryRecursivelyGeneric(IFileSystem fileSystem, string path) { - IFileSystem fs = directory.ParentFileSystem; + IFileSystem fs = fileSystem; - foreach (DirectoryEntry entry in directory.Read()) + foreach (DirectoryEntryEx entry in fileSystem.EnumerateEntries(path, "*")) { - string subPath = PathTools.Combine(directory.FullPath, entry.Name); + string subPath = PathTools.Combine(path, entry.Name); if (entry.Type == DirectoryEntryType.Directory) { - fs.OpenDirectory(out IDirectory subDir, subPath, OpenDirectoryMode.All).ThrowIfFailure(); - - CleanDirectoryRecursivelyGeneric(subDir); + CleanDirectoryRecursivelyGeneric(fileSystem, subPath); fs.DeleteDirectory(subPath); } else if (entry.Type == DirectoryEntryType.File) diff --git a/src/LibHac/Fs/FsPath.cs b/src/LibHac/Fs/FsPath.cs new file mode 100644 index 00000000..7db2b8f0 --- /dev/null +++ b/src/LibHac/Fs/FsPath.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.InteropServices; +using LibHac.Common; + +namespace LibHac.Fs +{ + [StructLayout(LayoutKind.Explicit, Size = MaxLength + 1)] + public struct FsPath + { + internal const int MaxLength = 0x300; + + [FieldOffset(0)] private byte _str; + + public Span Str => SpanHelpers.CreateSpan(ref _str, MaxLength + 1); + } +} diff --git a/src/LibHac/Fs/IDirectory.cs b/src/LibHac/Fs/IDirectory.cs index fb214d1a..401d26d7 100644 --- a/src/LibHac/Fs/IDirectory.cs +++ b/src/LibHac/Fs/IDirectory.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; namespace LibHac.Fs { @@ -8,32 +8,24 @@ namespace LibHac.Fs public interface IDirectory { /// - /// The that contains the current . + /// Retrieves the next entries that this directory contains. Does not search subdirectories. /// - IFileSystem ParentFileSystem { get; } + /// The number of s that + /// were read into . + /// The buffer the entries will be read into. + /// The of the requested operation. + /// With each call of , the object will + /// continue to iterate through all the entries it contains. + /// Each call will attempt to read as many entries as the buffer can contain. + /// Once all the entries have been read, all subsequent calls to will + /// read 0 entries into the buffer. + Result Read(out long entriesRead, Span entryBuffer); /// - /// The full path of the current in its . + /// Retrieves the number of file system entries that this directory contains. Does not search subdirectories. /// - string FullPath { get; } - - /// - /// Specifies which types of entries will be enumerated when is called. - /// - OpenDirectoryMode Mode { get; } - - /// - /// Returns an enumerable collection the file system entries of the types specified by - /// that this directory contains. Does not search subdirectories. - /// - /// An enumerable collection of file system entries in this directory. - IEnumerable Read(); - - /// - /// Returns the number of file system entries of the types specified by - /// that this directory contains. Does not search subdirectories. - /// - /// The number of child entries the directory contains. - int GetEntryCount(); + /// The number of child entries the directory contains. + /// The of the requested operation. + Result GetEntryCount(out long entryCount); } } \ No newline at end of file diff --git a/src/LibHac/Fs/IFileSystem.cs b/src/LibHac/Fs/IFileSystem.cs index 0703baf9..3d99bf3c 100644 --- a/src/LibHac/Fs/IFileSystem.cs +++ b/src/LibHac/Fs/IFileSystem.cs @@ -221,8 +221,9 @@ namespace LibHac.Fs [Flags] public enum OpenDirectoryMode { - Directory = 1, - File = 2, + Directory = 1 << 0, + File = 1 << 1, + NoFileSize = 1 << 31, All = Directory | File } diff --git a/src/LibHac/Fs/LayeredFileSystem.cs b/src/LibHac/Fs/LayeredFileSystem.cs index 500dde46..329f7094 100644 --- a/src/LibHac/Fs/LayeredFileSystem.cs +++ b/src/LibHac/Fs/LayeredFileSystem.cs @@ -38,7 +38,7 @@ namespace LibHac.Fs } } - directory = new LayeredFileSystemDirectory(this, dirs, path, mode); + directory = new LayeredFileSystemDirectory(dirs); return Result.Success; } diff --git a/src/LibHac/Fs/LayeredFileSystemDirectory.cs b/src/LibHac/Fs/LayeredFileSystemDirectory.cs index ee980bc4..9d1e31dc 100644 --- a/src/LibHac/Fs/LayeredFileSystemDirectory.cs +++ b/src/LibHac/Fs/LayeredFileSystemDirectory.cs @@ -1,44 +1,51 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; namespace LibHac.Fs { public class LayeredFileSystemDirectory : IDirectory { - public IFileSystem ParentFileSystem { get; } - - public string FullPath { get; } - public OpenDirectoryMode Mode { get; } - private List Sources { get; } - public LayeredFileSystemDirectory(IFileSystem fs, List sources, string path, OpenDirectoryMode mode) + public LayeredFileSystemDirectory(List sources) { - ParentFileSystem = fs; Sources = sources; - FullPath = path; - Mode = mode; } - public IEnumerable Read() + // Todo: Don't return duplicate entries + public Result Read(out long entriesRead, Span entryBuffer) { - var returnedFiles = new HashSet(); + entriesRead = 0; + int entryIndex = 0; - foreach (IDirectory source in Sources) + for (int i = 0; i < Sources.Count && entryIndex < entryBuffer.Length; i++) { - foreach (DirectoryEntry entry in source.Read()) - { - if (returnedFiles.Contains(entry.FullPath)) continue; + Result rs = Sources[i].Read(out long subEntriesRead, entryBuffer.Slice(entryIndex)); + if (rs.IsFailure()) return rs; - returnedFiles.Add(entry.FullPath); - yield return entry; - } + entryIndex += (int)subEntriesRead; } + + entriesRead = entryIndex; + return Result.Success; } - public int GetEntryCount() + // Todo: Don't count duplicate entries + public Result GetEntryCount(out long entryCount) { - return Read().Count(); + entryCount = 0; + long totalEntryCount = 0; + + foreach (IDirectory dir in Sources) + { + Result rc = dir.GetEntryCount(out long subEntryCount); + if (rc.IsFailure()) return rc; + + totalEntryCount += subEntryCount; + } + + entryCount = totalEntryCount; + return Result.Success; } } } diff --git a/src/LibHac/Fs/LocalDirectory.cs b/src/LibHac/Fs/LocalDirectory.cs index 3086be3b..bbce488d 100644 --- a/src/LibHac/Fs/LocalDirectory.cs +++ b/src/LibHac/Fs/LocalDirectory.cs @@ -2,22 +2,19 @@ using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using LibHac.Common; namespace LibHac.Fs { public class LocalDirectory : IDirectory { - public IFileSystem ParentFileSystem { get; } - public string FullPath { get; } - private string LocalPath { get; } - public OpenDirectoryMode Mode { get; } + private OpenDirectoryMode Mode { get; } private DirectoryInfo DirInfo { get; } + private IEnumerator EntryEnumerator { get; } public LocalDirectory(LocalFileSystem fs, string path, OpenDirectoryMode mode) { - ParentFileSystem = fs; - FullPath = path; LocalPath = fs.ResolveLocalPath(path); Mode = mode; @@ -36,27 +33,42 @@ namespace LibHac.Fs { ThrowHelper.ThrowResult(ResultFs.PathNotFound); } + + EntryEnumerator = DirInfo.EnumerateFileSystemInfos().GetEnumerator(); } - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - foreach (FileSystemInfo entry in DirInfo.EnumerateFileSystemInfos()) + int i = 0; + + while (i < entryBuffer.Length && EntryEnumerator.MoveNext()) { - bool isDir = (entry.Attributes & FileAttributes.Directory) != 0; + FileSystemInfo localEntry = EntryEnumerator.Current; + if (localEntry == null) break; + + bool isDir = localEntry.Attributes.HasFlag(FileAttributes.Directory); if (!CanReturnEntry(isDir, Mode)) continue; + ReadOnlySpan name = Util.GetUtf8Bytes(localEntry.Name); DirectoryEntryType type = isDir ? DirectoryEntryType.Directory : DirectoryEntryType.File; - long length = isDir ? 0 : ((FileInfo)entry).Length; + long length = isDir ? 0 : ((FileInfo)localEntry).Length; - yield return new DirectoryEntry(entry.Name, PathTools.Combine(FullPath, entry.Name), type, length) - { - Attributes = entry.Attributes.ToNxAttributes() - }; + StringUtils.Copy(entryBuffer[i].Name, name); + entryBuffer[i].Name[PathTools.MaxPathLength] = 0; + + entryBuffer[i].Attributes = localEntry.Attributes.ToNxAttributes(); + entryBuffer[i].Type = type; + entryBuffer[i].Size = length; + + i++; } + + entriesRead = i; + return Result.Success; } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { int count = 0; @@ -67,7 +79,8 @@ namespace LibHac.Fs if (CanReturnEntry(isDir, Mode)) count++; } - return count; + entryCount = count; + return Result.Success; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/LibHac/Fs/PartitionDirectory.cs b/src/LibHac/Fs/PartitionDirectory.cs index dc6e4db1..4f8feaba 100644 --- a/src/LibHac/Fs/PartitionDirectory.cs +++ b/src/LibHac/Fs/PartitionDirectory.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; +using System; using System.IO; +using System.Text; +using LibHac.Common; namespace LibHac.Fs { public class PartitionDirectory : IDirectory { - IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; - public PartitionFileSystem ParentFileSystem { get; } - public string FullPath { get; } - - public OpenDirectoryMode Mode { get; } + private PartitionFileSystem ParentFileSystem { get; } + private OpenDirectoryMode Mode { get; } + private int CurrentIndex { get; set; } public PartitionDirectory(PartitionFileSystem fs, string path, OpenDirectoryMode mode) { @@ -18,23 +18,43 @@ namespace LibHac.Fs if (path != "/") throw new DirectoryNotFoundException(); ParentFileSystem = fs; - FullPath = path; Mode = mode; + + CurrentIndex = 0; } - - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - if (Mode.HasFlag(OpenDirectoryMode.File)) + if (!Mode.HasFlag(OpenDirectoryMode.File)) { - foreach (PartitionFileEntry entry in ParentFileSystem.Files) - { - yield return new DirectoryEntry(entry.Name, '/' + entry.Name, DirectoryEntryType.File, entry.Size); - } + entriesRead = 0; + return Result.Success; } + + int entriesRemaining = ParentFileSystem.Files.Length - CurrentIndex; + int toRead = Math.Min(entriesRemaining, entryBuffer.Length); + + for (int i = 0; i < toRead; i++) + { + PartitionFileEntry fileEntry = ParentFileSystem.Files[CurrentIndex]; + ref DirectoryEntry entry = ref entryBuffer[i]; + + Span nameUtf8 = Encoding.UTF8.GetBytes(fileEntry.Name); + + entry.Type = DirectoryEntryType.File; + entry.Size = fileEntry.Size; + + StringUtils.Copy(entry.Name, nameUtf8); + entry.Name[PathTools.MaxPathLength] = 0; + + CurrentIndex++; + } + + entriesRead = toRead; + return Result.Success; } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { int count = 0; @@ -43,7 +63,8 @@ namespace LibHac.Fs count += ParentFileSystem.Files.Length; } - return count; + entryCount = count; + return Result.Success; } } } \ No newline at end of file diff --git a/src/LibHac/Fs/PartitionFileSystemBuilder.cs b/src/LibHac/Fs/PartitionFileSystemBuilder.cs index 9a41c18d..853ae77a 100644 --- a/src/LibHac/Fs/PartitionFileSystemBuilder.cs +++ b/src/LibHac/Fs/PartitionFileSystemBuilder.cs @@ -22,9 +22,7 @@ namespace LibHac.Fs /// public PartitionFileSystemBuilder(IFileSystem input) { - input.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.File).ThrowIfFailure(); - - foreach (DirectoryEntry entry in rootDir.Read().OrderBy(x => x.FullPath, StringComparer.Ordinal)) + foreach (DirectoryEntryEx entry in input.EnumerateEntries().OrderBy(x => x.FullPath, StringComparer.Ordinal)) { input.OpenFile(out IFile file, entry.FullPath, OpenMode.Read).ThrowIfFailure(); diff --git a/src/LibHac/Fs/PathTools.cs b/src/LibHac/Fs/PathTools.cs index 538bd218..d5ad17f8 100644 --- a/src/LibHac/Fs/PathTools.cs +++ b/src/LibHac/Fs/PathTools.cs @@ -13,6 +13,8 @@ namespace LibHac.Fs public static readonly char DirectorySeparator = '/'; public static readonly char MountSeparator = ':'; internal const int MountNameLength = 0xF; + + // Todo: Remove internal const int MaxPathLength = 0x300; public static string Normalize(string inPath) diff --git a/src/LibHac/Fs/ReadOnlyDirectory.cs b/src/LibHac/Fs/ReadOnlyDirectory.cs deleted file mode 100644 index 5d8a3c27..00000000 --- a/src/LibHac/Fs/ReadOnlyDirectory.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace LibHac.Fs -{ - public class ReadOnlyDirectory : IDirectory - { - private IDirectory BaseDir { get; } - public IFileSystem ParentFileSystem { get; } - - public string FullPath => BaseDir.FullPath; - public OpenDirectoryMode Mode => BaseDir.Mode; - - public ReadOnlyDirectory(IFileSystem parentFileSystem, IDirectory baseDirectory) - { - ParentFileSystem = parentFileSystem; - BaseDir = baseDirectory; - } - - public IEnumerable Read() => BaseDir.Read(); - public int GetEntryCount() => BaseDir.GetEntryCount(); - } -} diff --git a/src/LibHac/Fs/ReadOnlyFileSystem.cs b/src/LibHac/Fs/ReadOnlyFileSystem.cs index 417b2526..a4fc43d7 100644 --- a/src/LibHac/Fs/ReadOnlyFileSystem.cs +++ b/src/LibHac/Fs/ReadOnlyFileSystem.cs @@ -13,13 +13,7 @@ namespace LibHac.Fs public Result OpenDirectory(out IDirectory directory, string path, OpenDirectoryMode mode) { - directory = default; - - Result rc = BaseFs.OpenDirectory(out IDirectory baseDir, path, mode); - if (rc.IsFailure()) return rc; - - directory = new ReadOnlyDirectory(this, baseDir); - return Result.Success; + return BaseFs.OpenDirectory(out directory, path, mode); } public Result OpenFile(out IFile file, string path, OpenMode mode) diff --git a/src/LibHac/Fs/RomFs/RomFsBuilder.cs b/src/LibHac/Fs/RomFs/RomFsBuilder.cs index 055e2c4f..c4ab96d8 100644 --- a/src/LibHac/Fs/RomFs/RomFsBuilder.cs +++ b/src/LibHac/Fs/RomFs/RomFsBuilder.cs @@ -32,7 +32,7 @@ namespace LibHac.Fs.RomFs /// public RomFsBuilder(IFileSystem input) { - foreach (DirectoryEntry entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) + foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) .OrderBy(x => x.FullPath, StringComparer.Ordinal)) { input.OpenFile(out IFile file, entry.FullPath, OpenMode.Read).ThrowIfFailure(); diff --git a/src/LibHac/Fs/RomFs/RomFsDirectory.cs b/src/LibHac/Fs/RomFs/RomFsDirectory.cs index 3b8cb119..7b39248c 100644 --- a/src/LibHac/Fs/RomFs/RomFsDirectory.cs +++ b/src/LibHac/Fs/RomFs/RomFsDirectory.cs @@ -1,71 +1,87 @@ -using System.Collections.Generic; +using System; +using System.Text; +using LibHac.Common; namespace LibHac.Fs.RomFs { public class RomFsDirectory : IDirectory { - IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; - public RomFsFileSystem ParentFileSystem { get; } - public string FullPath { get; } + private RomFsFileSystem ParentFileSystem { get; } - public OpenDirectoryMode Mode { get; } + private OpenDirectoryMode Mode { get; } private FindPosition InitialPosition { get; } + private FindPosition _currentPosition; - public RomFsDirectory(RomFsFileSystem fs, string path, FindPosition position, OpenDirectoryMode mode) + public RomFsDirectory(RomFsFileSystem fs, FindPosition position, OpenDirectoryMode mode) { ParentFileSystem = fs; InitialPosition = position; - FullPath = path; + _currentPosition = position; Mode = mode; } - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - FindPosition position = InitialPosition; - HierarchicalRomFileTable tab = ParentFileSystem.FileTable; - - if (Mode.HasFlag(OpenDirectoryMode.Directory)) - { - while (tab.FindNextDirectory(ref position, out string name)) - { - yield return new DirectoryEntry(name, PathTools.Combine(FullPath, name), DirectoryEntryType.Directory, 0); - } - } - - if (Mode.HasFlag(OpenDirectoryMode.File)) - { - while (tab.FindNextFile(ref position, out RomFileInfo info, out string name)) - { - yield return new DirectoryEntry(name, PathTools.Combine(FullPath, name), DirectoryEntryType.File, info.Length); - } - } + return ReadImpl(out entriesRead, ref _currentPosition, entryBuffer); } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { - int count = 0; - FindPosition position = InitialPosition; + + return ReadImpl(out entryCount, ref position, Span.Empty); + } + + private Result ReadImpl(out long entriesRead, ref FindPosition position, Span entryBuffer) + { HierarchicalRomFileTable tab = ParentFileSystem.FileTable; + int i = 0; + if (Mode.HasFlag(OpenDirectoryMode.Directory)) { - while (tab.FindNextDirectory(ref position, out string _)) + while ((entryBuffer.IsEmpty || i < entryBuffer.Length) && tab.FindNextDirectory(ref position, out string name)) { - count++; + if (!entryBuffer.IsEmpty) + { + ref DirectoryEntry entry = ref entryBuffer[i]; + Span nameUtf8 = Encoding.UTF8.GetBytes(name); + + StringUtils.Copy(entry.Name, nameUtf8); + entry.Name[PathTools.MaxPathLength] = 0; + + entry.Type = DirectoryEntryType.Directory; + entry.Size = 0; + } + + i++; } } if (Mode.HasFlag(OpenDirectoryMode.File)) { - while (tab.FindNextFile(ref position, out RomFileInfo _, out string _)) + while ((entryBuffer.IsEmpty || i < entryBuffer.Length) && tab.FindNextFile(ref position, out RomFileInfo info, out string name)) { - count++; + if (!entryBuffer.IsEmpty) + { + ref DirectoryEntry entry = ref entryBuffer[i]; + Span nameUtf8 = Encoding.UTF8.GetBytes(name); + + StringUtils.Copy(entry.Name, nameUtf8); + entry.Name[PathTools.MaxPathLength] = 0; + + entry.Type = DirectoryEntryType.File; + entry.Size = info.Length; + } + + i++; } } - return count; + entriesRead = i; + + return Result.Success; } } } diff --git a/src/LibHac/Fs/RomFs/RomFsFileSystem.cs b/src/LibHac/Fs/RomFs/RomFsFileSystem.cs index 0b9970a4..4651a177 100644 --- a/src/LibHac/Fs/RomFs/RomFsFileSystem.cs +++ b/src/LibHac/Fs/RomFs/RomFsFileSystem.cs @@ -57,7 +57,7 @@ namespace LibHac.Fs.RomFs return ResultFs.PathNotFound.Log(); } - directory = new RomFsDirectory(this, path, position, mode); + directory = new RomFsDirectory(this, position, mode); return Result.Success; } diff --git a/src/LibHac/Fs/Save/SaveDataDirectory.cs b/src/LibHac/Fs/Save/SaveDataDirectory.cs index b2fe7fd3..c4a73343 100644 --- a/src/LibHac/Fs/Save/SaveDataDirectory.cs +++ b/src/LibHac/Fs/Save/SaveDataDirectory.cs @@ -1,71 +1,87 @@ -using System.Collections.Generic; +using System; +using System.Text; +using LibHac.Common; namespace LibHac.Fs.Save { public class SaveDataDirectory : IDirectory { - IFileSystem IDirectory.ParentFileSystem => ParentFileSystem; - public SaveDataFileSystemCore ParentFileSystem { get; } - public string FullPath { get; } + private SaveDataFileSystemCore ParentFileSystem { get; } - public OpenDirectoryMode Mode { get; } + private OpenDirectoryMode Mode { get; } private SaveFindPosition InitialPosition { get; } + private SaveFindPosition _currentPosition; - public SaveDataDirectory(SaveDataFileSystemCore fs, string path, SaveFindPosition position, OpenDirectoryMode mode) + public SaveDataDirectory(SaveDataFileSystemCore fs, SaveFindPosition position, OpenDirectoryMode mode) { ParentFileSystem = fs; InitialPosition = position; - FullPath = path; + _currentPosition = position; Mode = mode; } - public IEnumerable Read() + public Result Read(out long entriesRead, Span entryBuffer) { - SaveFindPosition position = InitialPosition; - HierarchicalSaveFileTable tab = ParentFileSystem.FileTable; - - if (Mode.HasFlag(OpenDirectoryMode.Directory)) - { - while (tab.FindNextDirectory(ref position, out string name)) - { - yield return new DirectoryEntry(name, PathTools.Combine(FullPath, name), DirectoryEntryType.Directory, 0); - } - } - - if (Mode.HasFlag(OpenDirectoryMode.File)) - { - while (tab.FindNextFile(ref position, out SaveFileInfo info, out string name)) - { - yield return new DirectoryEntry(name, PathTools.Combine(FullPath, name), DirectoryEntryType.File, info.Length); - } - } + return ReadImpl(out entriesRead, ref _currentPosition, entryBuffer); } - public int GetEntryCount() + public Result GetEntryCount(out long entryCount) { - int count = 0; - SaveFindPosition position = InitialPosition; + + return ReadImpl(out entryCount, ref position, Span.Empty); + } + + private Result ReadImpl(out long entriesRead, ref SaveFindPosition position, Span entryBuffer) + { HierarchicalSaveFileTable tab = ParentFileSystem.FileTable; + int i = 0; + if (Mode.HasFlag(OpenDirectoryMode.Directory)) { - while (tab.FindNextDirectory(ref position, out string _)) + while ((entryBuffer.IsEmpty || i < entryBuffer.Length) && tab.FindNextDirectory(ref position, out string name)) { - count++; + if (!entryBuffer.IsEmpty) + { + ref DirectoryEntry entry = ref entryBuffer[i]; + Span nameUtf8 = Encoding.UTF8.GetBytes(name); + + StringUtils.Copy(entry.Name, nameUtf8); + entry.Name[64] = 0; + + entry.Type = DirectoryEntryType.Directory; + entry.Size = 0; + } + + i++; } } if (Mode.HasFlag(OpenDirectoryMode.File)) { - while (tab.FindNextFile(ref position, out SaveFileInfo _, out string _)) + while ((entryBuffer.IsEmpty || i < entryBuffer.Length) && tab.FindNextFile(ref position, out SaveFileInfo info, out string name)) { - count++; + if (!entryBuffer.IsEmpty) + { + ref DirectoryEntry entry = ref entryBuffer[i]; + Span nameUtf8 = Encoding.UTF8.GetBytes(name); + + StringUtils.Copy(entry.Name, nameUtf8); + entry.Name[64] = 0; + + entry.Type = DirectoryEntryType.File; + entry.Size = info.Length; + } + + i++; } } - return count; + entriesRead = i; + + return Result.Success; } } } diff --git a/src/LibHac/Fs/Save/SaveDataFileSystemCore.cs b/src/LibHac/Fs/Save/SaveDataFileSystemCore.cs index 721fe75b..c408a325 100644 --- a/src/LibHac/Fs/Save/SaveDataFileSystemCore.cs +++ b/src/LibHac/Fs/Save/SaveDataFileSystemCore.cs @@ -88,10 +88,7 @@ namespace LibHac.Fs.Save { path = PathTools.Normalize(path); - Result rc = OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.All); - if (rc.IsFailure()) return rc; - - FileSystemExtensions.CleanDirectoryRecursivelyGeneric(dir); + FileSystemExtensions.CleanDirectoryRecursivelyGeneric(this, path); return Result.Success; } @@ -125,7 +122,7 @@ namespace LibHac.Fs.Save return ResultFs.PathNotFound.Log(); } - directory = new SaveDataDirectory(this, path, position, mode); + directory = new SaveDataDirectory(this, position, mode); return Result.Success; } @@ -223,7 +220,7 @@ namespace LibHac.Fs.Save { AllocationTable.FsTrim(); - foreach (DirectoryEntry file in this.EnumerateEntries("*", SearchOptions.RecurseSubdirectories)) + foreach (DirectoryEntryEx file in this.EnumerateEntries("*", SearchOptions.RecurseSubdirectories)) { if (FileTable.TryOpenFile(file.FullPath, out SaveFileInfo fileInfo) && fileInfo.StartBlock >= 0) { diff --git a/src/LibHac/Fs/SubdirectoryFileSystem.cs b/src/LibHac/Fs/SubdirectoryFileSystem.cs index 57f80d1a..b5fabd41 100644 --- a/src/LibHac/Fs/SubdirectoryFileSystem.cs +++ b/src/LibHac/Fs/SubdirectoryFileSystem.cs @@ -64,10 +64,7 @@ namespace LibHac.Fs { path = PathTools.Normalize(path); - ParentFileSystem.OpenDirectory(out IDirectory baseDir, ResolveFullPath(path), mode); - - directory = new SubdirectoryFileSystemDirectory(this, baseDir, path, mode); - return Result.Success; + return ParentFileSystem.OpenDirectory(out directory, ResolveFullPath(path), mode); } public Result OpenFile(out IFile file, string path, OpenMode mode) diff --git a/src/LibHac/Fs/SubdirectoryFileSystemDirectory.cs b/src/LibHac/Fs/SubdirectoryFileSystemDirectory.cs deleted file mode 100644 index cc2762d6..00000000 --- a/src/LibHac/Fs/SubdirectoryFileSystemDirectory.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; - -namespace LibHac.Fs -{ - public class SubdirectoryFileSystemDirectory : IDirectory - { - public SubdirectoryFileSystemDirectory(SubdirectoryFileSystem fs, IDirectory baseDir, string path, OpenDirectoryMode mode) - { - ParentFileSystem = fs; - BaseDirectory = baseDir; - FullPath = path; - Mode = mode; - } - - public IFileSystem ParentFileSystem { get; } - public string FullPath { get; } - public OpenDirectoryMode Mode { get; } - - private IDirectory BaseDirectory { get; } - - public IEnumerable Read() - { - foreach (DirectoryEntry entry in BaseDirectory.Read()) - { - yield return new DirectoryEntry(entry.Name, PathTools.Combine(FullPath, entry.Name), entry.Type, entry.Size); - } - } - - public int GetEntryCount() - { - return BaseDirectory.GetEntryCount(); - } - } -} diff --git a/src/LibHac/FsClient/Accessors/DirectoryAccessor.cs b/src/LibHac/FsClient/Accessors/DirectoryAccessor.cs index 94df1121..86eed8ed 100644 --- a/src/LibHac/FsClient/Accessors/DirectoryAccessor.cs +++ b/src/LibHac/FsClient/Accessors/DirectoryAccessor.cs @@ -10,24 +10,34 @@ namespace LibHac.FsClient.Accessors public FileSystemAccessor Parent { get; } - public DirectoryAccessor(IDirectory baseDirectory, FileSystemAccessor parent) + private IFileSystem ParentFs { get; } + private string Path { get; } + + public DirectoryAccessor(IDirectory baseDirectory, FileSystemAccessor parent, IFileSystem parentFs, string path) { Directory = baseDirectory; Parent = parent; + ParentFs = parentFs; + Path = path; } - public IEnumerable Read() + public IEnumerable Read() { CheckIfDisposed(); - return Directory.Read(); + return ParentFs.EnumerateEntries(Path, "*", SearchOptions.Default); } - public int GetEntryCount() + public Result Read(out long entriesRead, Span entryBuffer) + { + return Directory.Read(out entriesRead, entryBuffer); + } + + public Result GetEntryCount(out long entryCount) { CheckIfDisposed(); - return Directory.GetEntryCount(); + return Directory.GetEntryCount(out entryCount); } public void Dispose() diff --git a/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs b/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs index 317e7dd0..8916322b 100644 --- a/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs +++ b/src/LibHac/FsClient/Accessors/FileSystemAccessor.cs @@ -63,7 +63,7 @@ namespace LibHac.FsClient.Accessors Result rc = FileSystem.OpenDirectory(out IDirectory rawDirectory, path, mode); if (rc.IsFailure()) return rc; - var accessor = new DirectoryAccessor(rawDirectory, this); + var accessor = new DirectoryAccessor(rawDirectory, this, FileSystem, path); lock (_locker) { diff --git a/src/LibHac/FsClient/FileSystemClient.Directory.cs b/src/LibHac/FsClient/FileSystemClient.Directory.cs index e72d67c2..4395e55d 100644 --- a/src/LibHac/FsClient/FileSystemClient.Directory.cs +++ b/src/LibHac/FsClient/FileSystemClient.Directory.cs @@ -12,7 +12,7 @@ namespace LibHac.FsClient } // todo: change to not use IEnumerable - public IEnumerable ReadDirectory(DirectoryHandle handle) + public IEnumerable ReadDirectory(DirectoryHandle handle) { throw new NotImplementedException(); } diff --git a/src/LibHac/FsClient/FileSystemManager.cs b/src/LibHac/FsClient/FileSystemManager.cs index 08d87565..87d442ce 100644 --- a/src/LibHac/FsClient/FileSystemManager.cs +++ b/src/LibHac/FsClient/FileSystemManager.cs @@ -509,17 +509,15 @@ namespace LibHac.FsClient // ========================== public Result GetDirectoryEntryCount(out long count, DirectoryHandle handle) { - count = handle.Directory.GetEntryCount(); - - return Result.Success; + return handle.Directory.GetEntryCount(out count); } - public IEnumerable ReadDirectory(DirectoryHandle handle) + public IEnumerable ReadDirectory(DirectoryHandle handle) { if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) { TimeSpan startTime = Time.GetCurrent(); - IEnumerable entries = handle.Directory.Read(); + IEnumerable entries = handle.Directory.Read(); TimeSpan endTime = Time.GetCurrent(); OutputAccessLog(Result.Success, startTime, endTime, handle, string.Empty); @@ -529,6 +527,26 @@ namespace LibHac.FsClient return handle.Directory.Read(); } + public Result ReadDirectory2(out long entriesRead, Span entryBuffer, DirectoryHandle handle) + { + Result rc; + + if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) + { + TimeSpan startTime = Time.GetCurrent(); + rc = handle.Directory.Read(out entriesRead, entryBuffer); + TimeSpan endTime = Time.GetCurrent(); + + OutputAccessLog(rc, startTime, endTime, handle, string.Empty); + } + else + { + rc = handle.Directory.Read(out entriesRead, entryBuffer); + } + + return rc; + } + public void CloseDirectory(DirectoryHandle handle) { if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle)) diff --git a/src/LibHac/FsClient/FileSystemManagerUtils.cs b/src/LibHac/FsClient/FileSystemManagerUtils.cs index 31d6fe55..af9bf0a6 100644 --- a/src/LibHac/FsClient/FileSystemManagerUtils.cs +++ b/src/LibHac/FsClient/FileSystemManagerUtils.cs @@ -16,7 +16,7 @@ namespace LibHac.FsClient using (sourceHandle) { - foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle)) + foreach (DirectoryEntryEx entry in fs.ReadDirectory(sourceHandle)) { string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); @@ -95,17 +95,17 @@ namespace LibHac.FsClient return Result.Success; } - public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path) + public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path) { return fs.EnumerateEntries(path, "*"); } - public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path, string searchPattern) + public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path, string searchPattern) { return fs.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories); } - public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path, string searchPattern, SearchOptions searchOptions) + public static IEnumerable EnumerateEntries(this FileSystemManager fs, string path, string searchPattern, SearchOptions searchOptions) { bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive); bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories); @@ -114,7 +114,7 @@ namespace LibHac.FsClient using (sourceHandle) { - foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle)) + foreach (DirectoryEntryEx entry in fs.ReadDirectory(sourceHandle)) { if (PathTools.MatchesPattern(searchPattern, entry.Name, ignoreCase)) { @@ -125,9 +125,9 @@ namespace LibHac.FsClient string subPath = PathTools.Normalize(PathTools.Combine(path, entry.Name)); - IEnumerable subEntries = fs.EnumerateEntries(subPath, searchPattern, searchOptions); + IEnumerable subEntries = fs.EnumerateEntries(subPath, searchPattern, searchOptions); - foreach (DirectoryEntry subEntry in subEntries) + foreach (DirectoryEntryEx subEntry in subEntries) { subEntry.FullPath = PathTools.Combine(path, subEntry.Name); yield return subEntry; diff --git a/src/LibHac/LibHac.csproj b/src/LibHac/LibHac.csproj index 7091ca5c..c1033804 100644 --- a/src/LibHac/LibHac.csproj +++ b/src/LibHac/LibHac.csproj @@ -23,6 +23,7 @@ true $(NoWarn);1591;NU5105 true + true diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index 2f91685c..f87b879a 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -67,13 +67,11 @@ namespace LibHac private void OpenAllNcas() { - ContentFs.OpenDirectory(out IDirectory rootDir, "/", OpenDirectoryMode.All).ThrowIfFailure(); - // Todo: give warning if directories named "*.nca" are found or manually fix the archive bit - IEnumerable files = rootDir.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories) + IEnumerable files = ContentFs.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories) .Where(x => x.Type == DirectoryEntryType.File); - foreach (DirectoryEntry fileEntry in files) + foreach (DirectoryEntryEx fileEntry in files) { SwitchFsNca nca = null; try @@ -109,7 +107,7 @@ namespace LibHac { if (SaveFs == null) return; - foreach (DirectoryEntry fileEntry in SaveFs.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) + foreach (DirectoryEntryEx fileEntry in SaveFs.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) { SaveDataFileSystem save = null; string saveName = Path.GetFileNameWithoutExtension(fileEntry.Name); @@ -141,7 +139,7 @@ namespace LibHac var title = new Title(); IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - string cnmtPath = fs.EnumerateEntries("*.cnmt").Single().FullPath; + string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; fs.OpenFile(out IFile file, cnmtPath, OpenMode.Read).ThrowIfFailure(); diff --git a/src/hactoolnet/FsUtils.cs b/src/hactoolnet/FsUtils.cs index 0e3d2c1b..1614a723 100644 --- a/src/hactoolnet/FsUtils.cs +++ b/src/hactoolnet/FsUtils.cs @@ -30,7 +30,7 @@ namespace hactoolnet using (sourceHandle) { - foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle)) + foreach (DirectoryEntryEx entry in fs.ReadDirectory(sourceHandle)) { string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); @@ -57,7 +57,7 @@ namespace hactoolnet { long size = 0; - foreach (DirectoryEntry entry in fs.EnumerateEntries(path, searchPattern)) + foreach (DirectoryEntryEx entry in fs.EnumerateEntries(path, searchPattern)) { size += entry.Size; } diff --git a/src/hactoolnet/ProcessNca.cs b/src/hactoolnet/ProcessNca.cs index 5b301917..73b94ce9 100644 --- a/src/hactoolnet/ProcessNca.cs +++ b/src/hactoolnet/ProcessNca.cs @@ -73,7 +73,7 @@ namespace hactoolnet { IFileSystem romfs = OpenFileSystemByType(NcaSectionType.Data); - foreach (DirectoryEntry entry in romfs.EnumerateEntries()) + foreach (DirectoryEntryEx entry in romfs.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } diff --git a/src/hactoolnet/ProcessRomfs.cs b/src/hactoolnet/ProcessRomfs.cs index ac16b8df..950b757a 100644 --- a/src/hactoolnet/ProcessRomfs.cs +++ b/src/hactoolnet/ProcessRomfs.cs @@ -20,7 +20,7 @@ namespace hactoolnet if (ctx.Options.ListRomFs) { - foreach (DirectoryEntry entry in romfs.EnumerateEntries()) + foreach (DirectoryEntryEx entry in romfs.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs index e95f8340..ae1b7d6b 100644 --- a/src/hactoolnet/ProcessSave.cs +++ b/src/hactoolnet/ProcessSave.cs @@ -131,7 +131,7 @@ namespace hactoolnet if (ctx.Options.ListFiles) { - foreach (DirectoryEntry entry in save.EnumerateEntries()) + foreach (DirectoryEntryEx entry in save.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } @@ -228,7 +228,7 @@ namespace hactoolnet { var sb = new StringBuilder(); - foreach (DirectoryEntry entry in save.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) + foreach (DirectoryEntryEx entry in save.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) { save.FileTable.TryOpenFile(entry.FullPath, out SaveFileInfo fileInfo); if (fileInfo.StartBlock < 0) continue; diff --git a/src/hactoolnet/ProcessSwitchFs.cs b/src/hactoolnet/ProcessSwitchFs.cs index 35ead159..9dfc8ba9 100644 --- a/src/hactoolnet/ProcessSwitchFs.cs +++ b/src/hactoolnet/ProcessSwitchFs.cs @@ -316,7 +316,7 @@ namespace hactoolnet IFileSystem fs = switchFs.ContentFs; - DirectoryEntry[] ncaDirs = fs.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories) + DirectoryEntryEx[] ncaDirs = fs.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories) .Where(x => x.Type == DirectoryEntryType.Directory) .Where(x => fs.FileExists($"{x.FullPath}/00")) .ToArray(); @@ -326,7 +326,7 @@ namespace hactoolnet ctx.Logger.LogMessage("Warning: NCA folders without the archive flag were found. Fixing..."); } - foreach (DirectoryEntry file in ncaDirs) + foreach (DirectoryEntryEx file in ncaDirs) { fs.SetConcatenationFileAttribute(file.FullPath); ctx.Logger.LogMessage($"{file.FullPath}");