Ensure more FS classes are updated for 13.1.0

- FsSystem.ForwardingDirectory
- FsSystem.ForwardingFile
- FsSystem.ForwardingFileSystem
- FsSystem.StorageLayoutTypeSetDirectory
- FsSystem.StorageLayoutTypeSetFile
- FsSystem.StorageLayoutTypeSetFileSystem
- FsSystem.StorageLayoutTypeSetStorage
- Fs.MemoryStorage
- Fs.ReadOnlyFile
- Fs.ReadOnlyFileSystem
This commit is contained in:
Alex Barney 2022-01-25 17:02:44 -07:00
parent a7ffae3a14
commit 2c2fed445f
6 changed files with 299 additions and 162 deletions

View file

@ -1,14 +1,20 @@
using System; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Fs; namespace LibHac.Fs;
/// <summary>
/// Allows interacting with a <see cref="byte"/> array via the <see cref="IStorage"/> interface.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class MemoryStorage : IStorage public class MemoryStorage : IStorage
{ {
private byte[] StorageBuffer { get; } private byte[] _storageBuffer;
public MemoryStorage(byte[] buffer) public MemoryStorage(byte[] buffer)
{ {
StorageBuffer = buffer; _storageBuffer = buffer;
} }
public override Result Read(long offset, Span<byte> destination) public override Result Read(long offset, Span<byte> destination)
@ -16,10 +22,10 @@ public class MemoryStorage : IStorage
if (destination.Length == 0) if (destination.Length == 0)
return Result.Success; return Result.Success;
if (!CheckAccessRange(offset, destination.Length, StorageBuffer.Length)) if (!CheckAccessRange(offset, destination.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log(); return ResultFs.OutOfRange.Log();
StorageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination); _storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
return Result.Success; return Result.Success;
} }
@ -29,10 +35,10 @@ public class MemoryStorage : IStorage
if (source.Length == 0) if (source.Length == 0)
return Result.Success; return Result.Success;
if (!CheckAccessRange(offset, source.Length, StorageBuffer.Length)) if (!CheckAccessRange(offset, source.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log(); return ResultFs.OutOfRange.Log();
source.CopyTo(StorageBuffer.AsSpan((int)offset)); source.CopyTo(_storageBuffer.AsSpan((int)offset));
return Result.Success; return Result.Success;
} }
@ -49,7 +55,7 @@ public class MemoryStorage : IStorage
public override Result GetSize(out long size) public override Result GetSize(out long size)
{ {
size = StorageBuffer.Length; size = _storageBuffer.Length;
return Result.Success; return Result.Success;
} }
@ -57,6 +63,18 @@ public class MemoryStorage : IStorage
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer) ReadOnlySpan<byte> inBuffer)
{ {
throw new NotImplementedException(); switch (operationId)
{
case OperationId.InvalidateCache:
return Result.Success;
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
Unsafe.As<byte, QueryRangeInfo>(ref MemoryMarshal.GetReference(outBuffer)).Clear();
return Result.Success;
default:
return ResultFs.UnsupportedOperateRangeForMemoryStorage.Log();
}
} }
} }

View file

@ -0,0 +1,159 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Fsa;
namespace LibHac.Fs;
/// <summary>
/// Wraps an <see cref="IFile"/> and only allows read operations on it.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class ReadOnlyFile : IFile
{
private UniqueRef<IFile> _baseFile;
public ReadOnlyFile(ref UniqueRef<IFile> baseFile)
{
_baseFile = new UniqueRef<IFile>(ref baseFile);
Assert.SdkRequires(_baseFile.HasValue);
}
public override void Dispose()
{
_baseFile.Destroy();
base.Dispose();
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option)
{
return _baseFile.Get.Read(out bytesRead, offset, destination, option);
}
protected override Result DoGetSize(out long size)
{
return _baseFile.Get.GetSize(out size);
}
protected override Result DoFlush()
{
return Result.Success;
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
return ResultFs.UnsupportedWriteForReadOnlyFile.Log();
}
protected override Result DoSetSize(long size)
{
return ResultFs.UnsupportedWriteForReadOnlyFile.Log();
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
case OperationId.QueryRange:
return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
default:
return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log();
}
}
}
/// <summary>
/// Wraps an <see cref="IFileSystem"/> and only allows read operations on it.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class ReadOnlyFileSystem : IFileSystem
{
private SharedRef<IFileSystem> _baseFileSystem;
public ReadOnlyFileSystem(ref SharedRef<IFileSystem> baseFileSystem)
{
_baseFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
Assert.SdkRequires(_baseFileSystem.HasValue);
}
public override void Dispose()
{
_baseFileSystem.Destroy();
base.Dispose();
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{
// The Read flag must be the only flag set
if ((mode & OpenMode.All) != OpenMode.Read)
return ResultFs.InvalidModeForFileOpen.Log();
using var baseFile = new UniqueRef<IFile>();
Result rc = _baseFileSystem.Get.OpenFile(ref baseFile.Ref(), in path, mode);
if (rc.IsFailure()) return rc;
outFile.Reset(new ReadOnlyFile(ref baseFile.Ref()));
return Result.Success;
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{
// An IDirectory is already read-only so we don't need a wrapper ReadOnlyDictionary class
return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode);
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{
return _baseFileSystem.Get.GetEntryType(out entryType, in path);
}
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteFile(in Path path) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoCreateDirectory(in Path path) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteDirectory(in Path path) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteDirectoryRecursively(in Path path) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoCleanDirectoryRecursively(in Path path) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoRenameFile(in Path currentPath, in Path newPath) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) =>
ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoCommit() =>
Result.Success;
protected override Result DoCommitProvisionally(long counter) =>
ResultFs.UnsupportedCommitProvisionallyForReadOnlyFileSystem.Log();
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{
return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path);
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{
Unsafe.SkipInit(out totalSpace);
return ResultFs.UnsupportedGetTotalSpaceSizeForReadOnlyFileSystem.Log();
}
}

View file

@ -5,11 +5,80 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// An <see cref="IFile"/> wrapper that forwards all calls to the base <see cref="IFile"/>.
/// Meant for use as a base class when the derived class only needs to override some of the methods.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class ForwardingFile : IFile
{
protected UniqueRef<IFile> BaseFile;
protected ForwardingFile(ref UniqueRef<IFile> baseFile)
{
BaseFile = new UniqueRef<IFile>(ref baseFile);
}
public override void Dispose()
{
BaseFile.Destroy();
base.Dispose();
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option) =>
BaseFile.Get.Read(out bytesRead, offset, destination, in option);
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option) =>
BaseFile.Get.Write(offset, source, in option);
protected override Result DoFlush() => BaseFile.Get.Flush();
protected override Result DoSetSize(long size) => BaseFile.Get.SetSize(size);
protected override Result DoGetSize(out long size) => BaseFile.Get.GetSize(out size);
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer) => BaseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
}
/// <summary>
/// An <see cref="IDirectory"/> wrapper that forwards all calls to the base <see cref="IDirectory"/>.
/// Primarily meant for use as a base class when the derived class only needs to override some of the methods.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class ForwardingDirectory : IDirectory
{
protected UniqueRef<IDirectory> BaseDirectory;
protected ForwardingDirectory(ref UniqueRef<IDirectory> baseDirectory)
{
BaseDirectory = new UniqueRef<IDirectory>(ref baseDirectory);
}
public override void Dispose()
{
BaseDirectory.Destroy();
base.Dispose();
}
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer) =>
BaseDirectory.Get.Read(out entriesRead, entryBuffer);
protected override Result DoGetEntryCount(out long entryCount) => BaseDirectory.Get.GetEntryCount(out entryCount);
}
/// <summary>
/// An <see cref="IFileSystem"/> wrapper that forwards all calls to the base <see cref="IFileSystem"/>.
/// Primarily meant for use as a base class when the derived class only needs to override some of the methods.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class ForwardingFileSystem : IFileSystem public class ForwardingFileSystem : IFileSystem
{ {
protected SharedRef<IFileSystem> BaseFileSystem; protected SharedRef<IFileSystem> BaseFileSystem;
public ForwardingFileSystem(ref SharedRef<IFileSystem> baseFileSystem) protected ForwardingFileSystem(ref SharedRef<IFileSystem> baseFileSystem)
{ {
BaseFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem); BaseFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
} }
@ -17,6 +86,7 @@ public class ForwardingFileSystem : IFileSystem
public override void Dispose() public override void Dispose()
{ {
BaseFileSystem.Destroy(); BaseFileSystem.Destroy();
base.Dispose(); base.Dispose();
} }

View file

@ -1,54 +0,0 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSystem;
public class ReadOnlyFile : IFile
{
private UniqueRef<IFile> _baseFile;
public ReadOnlyFile(ref UniqueRef<IFile> baseFile)
{
_baseFile = new UniqueRef<IFile>(ref baseFile);
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option)
{
return _baseFile.Get.Read(out bytesRead, offset, destination, option);
}
protected override Result DoGetSize(out long size)
{
return _baseFile.Get.GetSize(out size);
}
protected override Result DoFlush()
{
return Result.Success;
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
return ResultFs.WriteUnpermitted.Log();
}
protected override Result DoSetSize(long size)
{
return ResultFs.WriteUnpermitted.Log();
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
case OperationId.QueryRange:
return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
default:
return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log();
}
}
}

View file

@ -1,87 +0,0 @@
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSystem;
public class ReadOnlyFileSystem : IFileSystem
{
private SharedRef<IFileSystem> _baseFileSystem;
public ReadOnlyFileSystem(ref SharedRef<IFileSystem> baseFileSystem)
{
_baseFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
Assert.SdkRequires(_baseFileSystem.HasValue);
}
public override void Dispose()
{
_baseFileSystem.Destroy();
base.Dispose();
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{
return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode);
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{
using var baseFile = new UniqueRef<IFile>();
Result rc = _baseFileSystem.Get.OpenFile(ref baseFile.Ref(), in path, mode);
if (rc.IsFailure()) return rc;
outFile.Reset(new ReadOnlyFile(ref baseFile.Ref()));
return Result.Success;
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{
return _baseFileSystem.Get.GetEntryType(out entryType, in path);
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{
return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path);
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{
return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path);
// FS does:
// return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{
return _baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, in path);
// FS does:
// return ResultFs.NotImplemented.Log();
}
protected override Result DoCommit()
{
return Result.Success;
}
protected override Result DoCreateDirectory(in Path path) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteDirectory(in Path path) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteDirectoryRecursively(in Path path) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoCleanDirectoryRecursively(in Path path) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoDeleteFile(in Path path) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
protected override Result DoRenameFile(in Path currentPath, in Path newPath) => ResultFs.UnsupportedWriteForReadOnlyFileSystem.Log();
}

View file

@ -18,6 +18,10 @@ internal enum StorageType
All = Bis | SdCard | GameCard | Usb All = Bis | SdCard | GameCard | Usb
} }
/// <summary>
/// Contains functions for validating the storage layout type flag.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal static class StorageLayoutType internal static class StorageLayoutType
{ {
public static bool IsStorageFlagValid(StorageType storageFlag) public static bool IsStorageFlagValid(StorageType storageFlag)
@ -40,6 +44,11 @@ internal struct ScopedStorageLayoutTypeSetter : IDisposable
} }
} }
/// <summary>
/// Wraps an <see cref="IStorage"/>, automatically setting the thread's storage type when accessing the storage.
/// This is used to determine which storage speed emulation parameters to use for the current thread.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class StorageLayoutTypeSetStorage : IStorage internal class StorageLayoutTypeSetStorage : IStorage
{ {
private SharedRef<IStorage> _baseStorage; private SharedRef<IStorage> _baseStorage;
@ -55,8 +64,10 @@ internal class StorageLayoutTypeSetStorage : IStorage
public override void Dispose() public override void Dispose()
{ {
using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); using (new ScopedStorageLayoutTypeSetter(_storageFlag))
{
_baseStorage.Destroy(); _baseStorage.Destroy();
}
base.Dispose(); base.Dispose();
} }
@ -99,6 +110,11 @@ internal class StorageLayoutTypeSetStorage : IStorage
} }
} }
/// <summary>
/// Wraps an <see cref="IFile"/>, automatically setting the thread's storage type when accessing the file.
/// This is used to determine which storage speed emulation parameters to use for the current thread.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class StorageLayoutTypeSetFile : IFile internal class StorageLayoutTypeSetFile : IFile
{ {
private IFile _baseFile; private IFile _baseFile;
@ -126,11 +142,12 @@ internal class StorageLayoutTypeSetFile : IFile
public override void Dispose() public override void Dispose()
{ {
using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); using (new ScopedStorageLayoutTypeSetter(_storageFlag))
{
_baseFile = null; _baseFile = null;
_baseFileUnique.Destroy(); _baseFileUnique.Destroy();
_baseFileShared.Destroy(); _baseFileShared.Destroy();
}
base.Dispose(); base.Dispose();
} }
@ -173,6 +190,11 @@ internal class StorageLayoutTypeSetFile : IFile
} }
} }
/// <summary>
/// Wraps an <see cref="IDirectory"/>, automatically setting the thread's storage type when accessing the directory.
/// This is used to determine which storage speed emulation parameters to use for the current thread.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class StorageLayoutTypeSetDirectory : IDirectory internal class StorageLayoutTypeSetDirectory : IDirectory
{ {
private UniqueRef<IDirectory> _baseDirectory; private UniqueRef<IDirectory> _baseDirectory;
@ -186,8 +208,10 @@ internal class StorageLayoutTypeSetDirectory : IDirectory
public override void Dispose() public override void Dispose()
{ {
using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); using (new ScopedStorageLayoutTypeSetter(_storageFlag))
{
_baseDirectory.Destroy(); _baseDirectory.Destroy();
}
base.Dispose(); base.Dispose();
} }
@ -205,6 +229,10 @@ internal class StorageLayoutTypeSetDirectory : IDirectory
} }
} }
/// <summary>
/// Wraps an <see cref="IFileSystem"/>, automatically setting the thread's storage type when accessing the file system.
/// This is used to determine which storage speed emulation parameters to use for the current thread.
/// </summary>
internal class StorageLayoutTypeSetFileSystem : IFileSystem internal class StorageLayoutTypeSetFileSystem : IFileSystem
{ {
private SharedRef<IFileSystem> _baseFileSystem; private SharedRef<IFileSystem> _baseFileSystem;
@ -220,8 +248,11 @@ internal class StorageLayoutTypeSetFileSystem : IFileSystem
public override void Dispose() public override void Dispose()
{ {
using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag); using (new ScopedStorageLayoutTypeSetter(_storageFlag))
{
_baseFileSystem.Destroy(); _baseFileSystem.Destroy();
}
base.Dispose(); base.Dispose();
} }