diff --git a/src/LibHac/Fs/MemoryStorage.cs b/src/LibHac/Fs/MemoryStorage.cs
index 9a9dde6d..f9cdf523 100644
--- a/src/LibHac/Fs/MemoryStorage.cs
+++ b/src/LibHac/Fs/MemoryStorage.cs
@@ -1,14 +1,20 @@
using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace LibHac.Fs;
+///
+/// Allows interacting with a array via the interface.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class MemoryStorage : IStorage
{
- private byte[] StorageBuffer { get; }
+ private byte[] _storageBuffer;
public MemoryStorage(byte[] buffer)
{
- StorageBuffer = buffer;
+ _storageBuffer = buffer;
}
public override Result Read(long offset, Span destination)
@@ -16,10 +22,10 @@ public class MemoryStorage : IStorage
if (destination.Length == 0)
return Result.Success;
- if (!CheckAccessRange(offset, destination.Length, StorageBuffer.Length))
+ if (!CheckAccessRange(offset, destination.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log();
- StorageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
+ _storageBuffer.AsSpan((int)offset, destination.Length).CopyTo(destination);
return Result.Success;
}
@@ -29,10 +35,10 @@ public class MemoryStorage : IStorage
if (source.Length == 0)
return Result.Success;
- if (!CheckAccessRange(offset, source.Length, StorageBuffer.Length))
+ if (!CheckAccessRange(offset, source.Length, _storageBuffer.Length))
return ResultFs.OutOfRange.Log();
- source.CopyTo(StorageBuffer.AsSpan((int)offset));
+ source.CopyTo(_storageBuffer.AsSpan((int)offset));
return Result.Success;
}
@@ -49,7 +55,7 @@ public class MemoryStorage : IStorage
public override Result GetSize(out long size)
{
- size = StorageBuffer.Length;
+ size = _storageBuffer.Length;
return Result.Success;
}
@@ -57,6 +63,18 @@ public class MemoryStorage : IStorage
public override Result OperateRange(Span outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan inBuffer)
{
- throw new NotImplementedException();
+ switch (operationId)
+ {
+ case OperationId.InvalidateCache:
+ return Result.Success;
+ case OperationId.QueryRange:
+ if (outBuffer.Length != Unsafe.SizeOf())
+ return ResultFs.InvalidSize.Log();
+
+ Unsafe.As(ref MemoryMarshal.GetReference(outBuffer)).Clear();
+ return Result.Success;
+ default:
+ return ResultFs.UnsupportedOperateRangeForMemoryStorage.Log();
+ }
}
}
\ No newline at end of file
diff --git a/src/LibHac/Fs/ReadOnlyFileSystem.cs b/src/LibHac/Fs/ReadOnlyFileSystem.cs
new file mode 100644
index 00000000..e8a15436
--- /dev/null
+++ b/src/LibHac/Fs/ReadOnlyFileSystem.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Diag;
+using LibHac.Fs.Fsa;
+
+namespace LibHac.Fs;
+
+///
+/// Wraps an and only allows read operations on it.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+internal class ReadOnlyFile : IFile
+{
+ private UniqueRef _baseFile;
+
+ public ReadOnlyFile(ref UniqueRef baseFile)
+ {
+ _baseFile = new UniqueRef(ref baseFile);
+
+ Assert.SdkRequires(_baseFile.HasValue);
+ }
+
+ public override void Dispose()
+ {
+ _baseFile.Destroy();
+
+ base.Dispose();
+ }
+
+ protected override Result DoRead(out long bytesRead, long offset, Span 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 source, in WriteOption option)
+ {
+ return ResultFs.UnsupportedWriteForReadOnlyFile.Log();
+ }
+
+ protected override Result DoSetSize(long size)
+ {
+ return ResultFs.UnsupportedWriteForReadOnlyFile.Log();
+ }
+
+ protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size,
+ ReadOnlySpan inBuffer)
+ {
+ switch (operationId)
+ {
+ case OperationId.InvalidateCache:
+ case OperationId.QueryRange:
+ return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
+ default:
+ return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log();
+ }
+ }
+}
+
+///
+/// Wraps an and only allows read operations on it.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+public class ReadOnlyFileSystem : IFileSystem
+{
+ private SharedRef _baseFileSystem;
+
+ public ReadOnlyFileSystem(ref SharedRef baseFileSystem)
+ {
+ _baseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
+
+ Assert.SdkRequires(_baseFileSystem.HasValue);
+ }
+
+ public override void Dispose()
+ {
+ _baseFileSystem.Destroy();
+
+ base.Dispose();
+ }
+
+ protected override Result DoOpenFile(ref UniqueRef 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();
+ 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 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();
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/ForwardingFileSystem.cs b/src/LibHac/FsSystem/ForwardingFileSystem.cs
index ea0b18ca..d82c8f82 100644
--- a/src/LibHac/FsSystem/ForwardingFileSystem.cs
+++ b/src/LibHac/FsSystem/ForwardingFileSystem.cs
@@ -5,11 +5,80 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSystem;
+///
+/// An wrapper that forwards all calls to the base .
+/// Meant for use as a base class when the derived class only needs to override some of the methods.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+public class ForwardingFile : IFile
+{
+ protected UniqueRef BaseFile;
+
+ protected ForwardingFile(ref UniqueRef baseFile)
+ {
+ BaseFile = new UniqueRef(ref baseFile);
+ }
+
+ public override void Dispose()
+ {
+ BaseFile.Destroy();
+
+ base.Dispose();
+ }
+
+ protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option) =>
+ BaseFile.Get.Read(out bytesRead, offset, destination, in option);
+
+ protected override Result DoWrite(long offset, ReadOnlySpan 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 outBuffer, OperationId operationId, long offset, long size,
+ ReadOnlySpan inBuffer) => BaseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
+}
+
+///
+/// An wrapper that forwards all calls to the base .
+/// Primarily meant for use as a base class when the derived class only needs to override some of the methods.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+public class ForwardingDirectory : IDirectory
+{
+ protected UniqueRef BaseDirectory;
+
+ protected ForwardingDirectory(ref UniqueRef baseDirectory)
+ {
+ BaseDirectory = new UniqueRef(ref baseDirectory);
+ }
+
+ public override void Dispose()
+ {
+ BaseDirectory.Destroy();
+
+ base.Dispose();
+ }
+
+ protected override Result DoRead(out long entriesRead, Span entryBuffer) =>
+ BaseDirectory.Get.Read(out entriesRead, entryBuffer);
+
+ protected override Result DoGetEntryCount(out long entryCount) => BaseDirectory.Get.GetEntryCount(out entryCount);
+}
+
+///
+/// An wrapper that forwards all calls to the base .
+/// Primarily meant for use as a base class when the derived class only needs to override some of the methods.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class ForwardingFileSystem : IFileSystem
{
protected SharedRef BaseFileSystem;
- public ForwardingFileSystem(ref SharedRef baseFileSystem)
+ protected ForwardingFileSystem(ref SharedRef baseFileSystem)
{
BaseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
}
@@ -17,6 +86,7 @@ public class ForwardingFileSystem : IFileSystem
public override void Dispose()
{
BaseFileSystem.Destroy();
+
base.Dispose();
}
@@ -71,4 +141,4 @@ public class ForwardingFileSystem : IFileSystem
protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId,
in Path path) => BaseFileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, in path);
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/ReadOnlyFile.cs b/src/LibHac/FsSystem/ReadOnlyFile.cs
deleted file mode 100644
index ab7a8030..00000000
--- a/src/LibHac/FsSystem/ReadOnlyFile.cs
+++ /dev/null
@@ -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 _baseFile;
-
- public ReadOnlyFile(ref UniqueRef baseFile)
- {
- _baseFile = new UniqueRef(ref baseFile);
- }
-
- protected override Result DoRead(out long bytesRead, long offset, Span 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 source, in WriteOption option)
- {
- return ResultFs.WriteUnpermitted.Log();
- }
-
- protected override Result DoSetSize(long size)
- {
- return ResultFs.WriteUnpermitted.Log();
- }
-
- protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer)
- {
- switch (operationId)
- {
- case OperationId.InvalidateCache:
- case OperationId.QueryRange:
- return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
- default:
- return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log();
- }
- }
-}
diff --git a/src/LibHac/FsSystem/ReadOnlyFileSystem.cs b/src/LibHac/FsSystem/ReadOnlyFileSystem.cs
deleted file mode 100644
index d868d559..00000000
--- a/src/LibHac/FsSystem/ReadOnlyFileSystem.cs
+++ /dev/null
@@ -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 _baseFileSystem;
-
- public ReadOnlyFileSystem(ref SharedRef baseFileSystem)
- {
- _baseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
-
- Assert.SdkRequires(_baseFileSystem.HasValue);
- }
-
- public override void Dispose()
- {
- _baseFileSystem.Destroy();
- base.Dispose();
- }
-
- protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
- OpenDirectoryMode mode)
- {
- return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode);
- }
-
- protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode)
- {
- using var baseFile = new UniqueRef();
- 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();
-}
diff --git a/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs b/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs
index 36fbe6b9..3450513e 100644
--- a/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs
+++ b/src/LibHac/FsSystem/StorageLayoutTypeSetter.cs
@@ -18,6 +18,10 @@ internal enum StorageType
All = Bis | SdCard | GameCard | Usb
}
+///
+/// Contains functions for validating the storage layout type flag.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
internal static class StorageLayoutType
{
public static bool IsStorageFlagValid(StorageType storageFlag)
@@ -40,6 +44,11 @@ internal struct ScopedStorageLayoutTypeSetter : IDisposable
}
}
+///
+/// Wraps an , 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.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
internal class StorageLayoutTypeSetStorage : IStorage
{
private SharedRef _baseStorage;
@@ -55,8 +64,10 @@ internal class StorageLayoutTypeSetStorage : IStorage
public override void Dispose()
{
- using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag);
- _baseStorage.Destroy();
+ using (new ScopedStorageLayoutTypeSetter(_storageFlag))
+ {
+ _baseStorage.Destroy();
+ }
base.Dispose();
}
@@ -99,6 +110,11 @@ internal class StorageLayoutTypeSetStorage : IStorage
}
}
+///
+/// Wraps an , 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.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
internal class StorageLayoutTypeSetFile : IFile
{
private IFile _baseFile;
@@ -126,11 +142,12 @@ internal class StorageLayoutTypeSetFile : IFile
public override void Dispose()
{
- using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag);
-
- _baseFile = null;
- _baseFileUnique.Destroy();
- _baseFileShared.Destroy();
+ using (new ScopedStorageLayoutTypeSetter(_storageFlag))
+ {
+ _baseFile = null;
+ _baseFileUnique.Destroy();
+ _baseFileShared.Destroy();
+ }
base.Dispose();
}
@@ -173,6 +190,11 @@ internal class StorageLayoutTypeSetFile : IFile
}
}
+///
+/// Wraps an , 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.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
internal class StorageLayoutTypeSetDirectory : IDirectory
{
private UniqueRef _baseDirectory;
@@ -186,8 +208,10 @@ internal class StorageLayoutTypeSetDirectory : IDirectory
public override void Dispose()
{
- using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag);
- _baseDirectory.Destroy();
+ using (new ScopedStorageLayoutTypeSetter(_storageFlag))
+ {
+ _baseDirectory.Destroy();
+ }
base.Dispose();
}
@@ -205,6 +229,10 @@ internal class StorageLayoutTypeSetDirectory : IDirectory
}
}
+///
+/// Wraps an , 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.
+///
internal class StorageLayoutTypeSetFileSystem : IFileSystem
{
private SharedRef _baseFileSystem;
@@ -220,8 +248,11 @@ internal class StorageLayoutTypeSetFileSystem : IFileSystem
public override void Dispose()
{
- using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag);
- _baseFileSystem.Destroy();
+ using (new ScopedStorageLayoutTypeSetter(_storageFlag))
+ {
+ _baseFileSystem.Destroy();
+ }
+
base.Dispose();
}