mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Change IDirectory to match the interface in FS
This commit is contained in:
parent
104312bf06
commit
734d86d336
42 changed files with 738 additions and 395 deletions
52
src/LibHac/Common/BlitStruct.cs
Normal file
52
src/LibHac/Common/BlitStruct.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common
|
||||
{
|
||||
public ref struct BlitStruct<T> where T : unmanaged
|
||||
{
|
||||
private readonly Span<T> _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<T> data)
|
||||
{
|
||||
_buffer = data;
|
||||
|
||||
Debug.Assert(_buffer.Length != 0);
|
||||
}
|
||||
|
||||
public BlitStruct(Span<byte> data)
|
||||
{
|
||||
_buffer = MemoryMarshal.Cast<byte, T>(data);
|
||||
|
||||
Debug.Assert(_buffer.Length != 0);
|
||||
}
|
||||
|
||||
public BlitStruct(ref T data)
|
||||
{
|
||||
_buffer = SpanHelpers.AsSpan(ref data);
|
||||
}
|
||||
|
||||
public Span<byte> GetByteSpan()
|
||||
{
|
||||
return MemoryMarshal.Cast<T, byte>(_buffer);
|
||||
}
|
||||
|
||||
public Span<byte> GetByteSpan(int elementIndex)
|
||||
{
|
||||
Span<T> element = _buffer.Slice(elementIndex, 1);
|
||||
return MemoryMarshal.Cast<T, byte>(element);
|
||||
}
|
||||
|
||||
public static int QueryByteLength(int elementCount)
|
||||
{
|
||||
return Unsafe.SizeOf<T>() * elementCount;
|
||||
}
|
||||
}
|
||||
}
|
35
src/LibHac/Common/SpanHelpers.cs
Normal file
35
src/LibHac/Common/SpanHelpers.cs
Normal file
|
@ -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<T> CreateSpan<T>(ref T reference, int length)
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref reference, length);
|
||||
}
|
||||
#else
|
||||
public static unsafe Span<T> CreateSpan<T>(ref T reference, int length)
|
||||
{
|
||||
return new Span<T>(Unsafe.AsPointer(ref reference), length);
|
||||
}
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateSpan(ref reference, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
Span<T> span = AsSpan(ref reference);
|
||||
return MemoryMarshal.Cast<T, byte>(span);
|
||||
}
|
||||
}
|
||||
}
|
85
src/LibHac/Common/StringUtils.cs
Normal file
85
src/LibHac/Common/StringUtils.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace LibHac.Common
|
||||
{
|
||||
public static class StringUtils
|
||||
{
|
||||
public static int Copy(Span<byte> dest, ReadOnlySpan<byte> 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<byte> s)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < s.Length && s[i] != 0)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates 2 byte strings.
|
||||
/// </summary>
|
||||
/// <param name="dest"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <returns>The length of the resulting string.</returns>
|
||||
/// <remarks>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.</remarks>
|
||||
public static int Concat(Span<byte> dest, ReadOnlySpan<byte> source)
|
||||
{
|
||||
return Concat(dest, GetLength(dest), source);
|
||||
}
|
||||
|
||||
public static int Concat(Span<byte> dest, int destLength, ReadOnlySpan<byte> 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<byte> value) => FromUtf8Z((ReadOnlySpan<byte>)value);
|
||||
|
||||
public static string FromUtf8Z(this ReadOnlySpan<byte> 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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}";
|
||||
|
|
|
@ -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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
{
|
||||
foreach (DirectoryEntry entry in ParentDirectory.Read())
|
||||
entriesRead = 0;
|
||||
var entry = new DirectoryEntry();
|
||||
Span<DirectoryEntry> 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<DirectoryEntry> 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<byte> 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,
|
||||
|
|
|
@ -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()
|
||||
|
|
85
src/LibHac/Fs/DirectoryUtils.cs
Normal file
85
src/LibHac/Fs/DirectoryUtils.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
public static class DirectoryUtils
|
||||
{
|
||||
public delegate Result Blah(ReadOnlySpan<byte> path, ref DirectoryEntry entry);
|
||||
|
||||
public static Result IterateDirectoryRecursivelyInternal(IFileSystem fs, Span<byte> 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<byte> 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<byte> destParentPath,
|
||||
ReadOnlySpan<byte> sourcePath, ref DirectoryEntry dirEntry, Span<byte> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<DirectoryEntry> EnumerateEntries(this IFileSystem fileSystem)
|
||||
public static IEnumerable<DirectoryEntryEx> EnumerateEntries(this IFileSystem fileSystem)
|
||||
{
|
||||
return fileSystem.EnumerateEntries("*");
|
||||
return fileSystem.EnumerateEntries("/", "*");
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this IFileSystem fileSystem, string searchPattern)
|
||||
public static IEnumerable<DirectoryEntryEx> EnumerateEntries(this IFileSystem fileSystem, string path, string searchPattern)
|
||||
{
|
||||
return fileSystem.EnumerateEntries(searchPattern, SearchOptions.RecurseSubdirectories);
|
||||
return fileSystem.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories);
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this IFileSystem fileSystem, string searchPattern, SearchOptions searchOptions)
|
||||
public static IEnumerable<DirectoryEntryEx> 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<DirectoryEntry> EnumerateEntries(this IDirectory directory)
|
||||
{
|
||||
return directory.EnumerateEntries("*", SearchOptions.Default);
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this IDirectory directory, string searchPattern, SearchOptions searchOptions)
|
||||
public static IEnumerable<DirectoryEntryEx> 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<DirectoryEntryEx> 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<byte>.Empty, Span<byte>.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)
|
||||
|
|
16
src/LibHac/Fs/FsPath.cs
Normal file
16
src/LibHac/Fs/FsPath.cs
Normal file
|
@ -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<byte> Str => SpanHelpers.CreateSpan(ref _str, MaxLength + 1);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
|
@ -8,32 +8,24 @@ namespace LibHac.Fs
|
|||
public interface IDirectory
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IFileSystem"/> that contains the current <see cref="IDirectory"/>.
|
||||
/// Retrieves the next entries that this directory contains. Does not search subdirectories.
|
||||
/// </summary>
|
||||
IFileSystem ParentFileSystem { get; }
|
||||
/// <param name="entriesRead">The number of <see cref="DirectoryEntry"/>s that
|
||||
/// were read into <paramref name="entryBuffer"/>.</param>
|
||||
/// <param name="entryBuffer">The buffer the entries will be read into.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>With each call of <see cref="Read"/>, the <see cref="IDirectory"/> 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 <see cref="Read"/> will
|
||||
/// read 0 entries into the buffer.</remarks>
|
||||
Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// The full path of the current <see cref="IDirectory"/> in its <see cref="ParentFileSystem"/>.
|
||||
/// Retrieves the number of file system entries that this directory contains. Does not search subdirectories.
|
||||
/// </summary>
|
||||
string FullPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies which types of entries will be enumerated when <see cref="Read"/> is called.
|
||||
/// </summary>
|
||||
OpenDirectoryMode Mode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerable collection the file system entries of the types specified by
|
||||
/// <see cref="Mode"/> that this directory contains. Does not search subdirectories.
|
||||
/// </summary>
|
||||
/// <returns>An enumerable collection of file system entries in this directory.</returns>
|
||||
IEnumerable<DirectoryEntry> Read();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of file system entries of the types specified by
|
||||
/// <see cref="Mode"/> that this directory contains. Does not search subdirectories.
|
||||
/// </summary>
|
||||
/// <returns>The number of child entries the directory contains.</returns>
|
||||
int GetEntryCount();
|
||||
/// <param name="entryCount">The number of child entries the directory contains.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result GetEntryCount(out long entryCount);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace LibHac.Fs
|
|||
}
|
||||
}
|
||||
|
||||
directory = new LayeredFileSystemDirectory(this, dirs, path, mode);
|
||||
directory = new LayeredFileSystemDirectory(dirs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IDirectory> Sources { get; }
|
||||
|
||||
public LayeredFileSystemDirectory(IFileSystem fs, List<IDirectory> sources, string path, OpenDirectoryMode mode)
|
||||
public LayeredFileSystemDirectory(List<IDirectory> sources)
|
||||
{
|
||||
ParentFileSystem = fs;
|
||||
Sources = sources;
|
||||
FullPath = path;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public IEnumerable<DirectoryEntry> Read()
|
||||
// Todo: Don't return duplicate entries
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
{
|
||||
var returnedFiles = new HashSet<string>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<FileSystemInfo> 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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> 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<byte> 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)]
|
||||
|
|
|
@ -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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> 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<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,9 +22,7 @@ namespace LibHac.Fs
|
|||
/// </summary>
|
||||
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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<DirectoryEntry> Read() => BaseDir.Read();
|
||||
public int GetEntryCount() => BaseDir.GetEntryCount();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace LibHac.Fs.RomFs
|
|||
/// </summary>
|
||||
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();
|
||||
|
|
|
@ -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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
{
|
||||
FindPosition position = InitialPosition;
|
||||
HierarchicalRomFileTable<RomFileInfo> 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<DirectoryEntry>.Empty);
|
||||
}
|
||||
|
||||
private Result ReadImpl(out long entriesRead, ref FindPosition position, Span<DirectoryEntry> entryBuffer)
|
||||
{
|
||||
HierarchicalRomFileTable<RomFileInfo> 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<byte> 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<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<DirectoryEntry> Read()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> 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<DirectoryEntry>.Empty);
|
||||
}
|
||||
|
||||
private Result ReadImpl(out long entriesRead, ref SaveFindPosition position, Span<DirectoryEntry> 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<byte> 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<byte> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<DirectoryEntry> 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<DirectoryEntry> Read()
|
||||
public IEnumerable<DirectoryEntryEx> Read()
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
return Directory.Read();
|
||||
return ParentFs.EnumerateEntries(Path, "*", SearchOptions.Default);
|
||||
}
|
||||
|
||||
public int GetEntryCount()
|
||||
public Result Read(out long entriesRead, Span<DirectoryEntry> 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()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace LibHac.FsClient
|
|||
}
|
||||
|
||||
// todo: change to not use IEnumerable
|
||||
public IEnumerable<DirectoryEntry> ReadDirectory(DirectoryHandle handle)
|
||||
public IEnumerable<DirectoryEntryEx> ReadDirectory(DirectoryHandle handle)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -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<DirectoryEntry> ReadDirectory(DirectoryHandle handle)
|
||||
public IEnumerable<DirectoryEntryEx> ReadDirectory(DirectoryHandle handle)
|
||||
{
|
||||
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
IEnumerable<DirectoryEntry> entries = handle.Directory.Read();
|
||||
IEnumerable<DirectoryEntryEx> 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<DirectoryEntry> 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))
|
||||
|
|
|
@ -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<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path)
|
||||
public static IEnumerable<DirectoryEntryEx> EnumerateEntries(this FileSystemManager fs, string path)
|
||||
{
|
||||
return fs.EnumerateEntries(path, "*");
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path, string searchPattern)
|
||||
public static IEnumerable<DirectoryEntryEx> EnumerateEntries(this FileSystemManager fs, string path, string searchPattern)
|
||||
{
|
||||
return fs.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories);
|
||||
}
|
||||
|
||||
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path, string searchPattern, SearchOptions searchOptions)
|
||||
public static IEnumerable<DirectoryEntryEx> 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<DirectoryEntry> subEntries = fs.EnumerateEntries(subPath, searchPattern, searchOptions);
|
||||
IEnumerable<DirectoryEntryEx> 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;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<IncludeSource>true</IncludeSource>
|
||||
<NoWarn>$(NoWarn);1591;NU5105</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net46' ">
|
||||
|
|
|
@ -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<DirectoryEntry> files = rootDir.EnumerateEntries("*.nca", SearchOptions.RecurseSubdirectories)
|
||||
IEnumerable<DirectoryEntryEx> 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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}");
|
||||
|
|
Loading…
Reference in a new issue