diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs
index 527ef384..010cf410 100644
--- a/src/LibHac/FsSrv/FileSystemServer.cs
+++ b/src/LibHac/FsSrv/FileSystemServer.cs
@@ -1,5 +1,6 @@
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Storage;
+using LibHac.FsSystem;
namespace LibHac.FsSrv;
@@ -34,6 +35,7 @@ internal struct FileSystemServerGlobals
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
public MultiCommitManagerGlobals MultiCommitManager;
public LocationResolverSetGlobals LocationResolverSet;
+ public PooledBufferGlobals PooledBuffer;
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
{
@@ -43,6 +45,7 @@ internal struct FileSystemServerGlobals
SaveDataSharedFileStorage.Initialize(fsServer);
MultiCommitManager.Initialize();
LocationResolverSet.Initialize();
+ PooledBuffer.Initialize();
}
}
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index f36d95c6..03c6a330 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -150,8 +150,8 @@ public static class FileSystemServerInitializer
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
var statusReportServiceConfig = new StatusReportServiceImpl.Configuration();
- statusReportServiceConfig.NcaFsServiceImpl = ncaFsService;
- statusReportServiceConfig.SaveFsServiceImpl = saveFsService;
+ statusReportServiceConfig.NcaFileSystemServiceImpl = ncaFsService;
+ statusReportServiceConfig.SaveDataFileSystemServiceImpl = saveFsService;
statusReportServiceConfig.BufferManagerMemoryReport = null;
statusReportServiceConfig.ExpHeapMemoryReport = null;
statusReportServiceConfig.BufferPoolMemoryReport = null;
diff --git a/src/LibHac/FsSrv/SaveDataInfoFilterReader.cs b/src/LibHac/FsSrv/SaveDataInfoFilterReader.cs
index c2cfa072..1c69705a 100644
--- a/src/LibHac/FsSrv/SaveDataInfoFilterReader.cs
+++ b/src/LibHac/FsSrv/SaveDataInfoFilterReader.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
@@ -8,6 +9,108 @@ using LibHac.Util;
namespace LibHac.FsSrv;
+///
+/// Contains filter parameters for and can check
+/// to see if a matches those parameters.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
+internal struct SaveDataInfoFilter
+{
+ private Optional _spaceId;
+ private Optional _programId;
+ private Optional _saveDataType;
+ private Optional _userId;
+ private Optional _saveDataId;
+ private Optional _index;
+ private int _rank;
+
+ public SaveDataInfoFilter(in SaveDataInfoFilter filter)
+ {
+ this = filter;
+ }
+
+ public SaveDataInfoFilter(SaveDataSpaceId spaceId, in SaveDataFilter filter)
+ {
+ // Start out with no optional values
+ this = default;
+
+ _spaceId = new Optional(spaceId);
+ _rank = (int)filter.Rank;
+
+ if (filter.FilterByProgramId)
+ {
+ _programId = new Optional(filter.Attribute.ProgramId);
+ }
+
+ if (filter.FilterBySaveDataType)
+ {
+ _saveDataType = new Optional(filter.Attribute.Type);
+ }
+
+ if (filter.FilterByUserId)
+ {
+ _userId = new Optional(in filter.Attribute.UserId);
+ }
+
+ if (filter.FilterBySaveDataId)
+ {
+ _saveDataId = new Optional(filter.Attribute.StaticSaveDataId);
+ }
+
+ if (filter.FilterByIndex)
+ {
+ _index = new Optional(filter.Attribute.Index);
+ }
+ }
+
+ public SaveDataInfoFilter(Optional spaceId, Optional programId,
+ Optional saveDataType, Optional userId, Optional saveDataId,
+ Optional index, int rank)
+ {
+ _spaceId = spaceId;
+ _programId = programId;
+ _saveDataType = saveDataType;
+ _userId = userId;
+ _saveDataId = saveDataId;
+ _index = index;
+ _rank = rank;
+ }
+
+ public bool Includes(in SaveDataInfo saveInfo)
+ {
+ if (_spaceId.HasValue && saveInfo.SpaceId != _spaceId.Value)
+ return false;
+
+ if (_programId.HasValue && saveInfo.ProgramId != _programId.Value)
+ return false;
+
+ if (_saveDataType.HasValue && saveInfo.Type != _saveDataType.Value)
+ return false;
+
+ if (_userId.HasValue && saveInfo.UserId != _userId.Value)
+ return false;
+
+ if (_saveDataId.HasValue && saveInfo.SaveDataId != _saveDataId.Value)
+ return false;
+
+ if (_index.HasValue && saveInfo.Index != _index.Value)
+ return false;
+
+ var filterRank = (SaveDataRank)(_rank & 1);
+
+ // When filtering by secondary rank, match on both primary and secondary ranks
+ if (filterRank == SaveDataRank.Primary && saveInfo.Rank == SaveDataRank.Secondary)
+ return false;
+
+ return true;
+ }
+}
+
+///
+/// Wraps a and only allows
+/// that match a provided to be returned.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
{
private SharedRef _reader;
@@ -19,146 +122,35 @@ internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
_infoFilter = infoFilter;
}
+ public void Dispose()
+ {
+ _reader.Destroy();
+ }
+
+ [SkipLocalsInit]
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
{
UnsafeHelpers.SkipParamInit(out readCount);
Span outInfo = MemoryMarshal.Cast(saveDataInfoBuffer.Buffer);
-
- SaveDataInfo tempInfo = default;
- Span tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
-
- SaveDataInfoReaderImpl reader = _reader.Get;
int count = 0;
while (count < outInfo.Length)
{
- Result rc = reader.Read(out long baseReadCount, new OutBuffer(tempInfoBytes));
+ Unsafe.SkipInit(out SaveDataInfo info);
+ Result rc = _reader.Get.Read(out long baseReadCount, OutBuffer.FromStruct(ref info));
if (rc.IsFailure()) return rc;
if (baseReadCount == 0) break;
- if (_infoFilter.Includes(in tempInfo))
+ if (_infoFilter.Includes(in info))
{
- outInfo[count] = tempInfo;
-
+ outInfo[count] = info;
count++;
}
}
readCount = count;
-
return Result.Success;
}
-
- public void Dispose()
- {
- _reader.Destroy();
- }
-}
-
-internal struct SaveDataInfoFilter
-{
- public Optional SpaceId;
- public Optional ProgramId;
- public Optional SaveDataType;
- public Optional UserId;
- public Optional SaveDataId;
- public Optional Index;
- public int Rank;
-
- public SaveDataInfoFilter(in SaveDataInfoFilter filter)
- {
- this = filter;
- }
-
- public SaveDataInfoFilter(SaveDataSpaceId spaceId, in SaveDataFilter filter)
- {
- // Start out with no optional values
- this = new SaveDataInfoFilter();
-
- SpaceId = new Optional(spaceId);
- Rank = (int)filter.Rank;
-
- if (filter.FilterByProgramId)
- {
- ProgramId = new Optional(in filter.Attribute.ProgramId);
- }
-
- if (filter.FilterBySaveDataType)
- {
- SaveDataType = new Optional(in filter.Attribute.Type);
- }
-
- if (filter.FilterByUserId)
- {
- UserId = new Optional(in filter.Attribute.UserId);
- }
-
- if (filter.FilterBySaveDataId)
- {
- SaveDataId = new Optional(in filter.Attribute.StaticSaveDataId);
- }
-
- if (filter.FilterByIndex)
- {
- Index = new Optional(in filter.Attribute.Index);
- }
- }
-
- public SaveDataInfoFilter(Optional spaceId, Optional programId,
- Optional saveDataType, Optional userId, Optional saveDataId,
- Optional index, int rank)
- {
- SpaceId = spaceId;
- ProgramId = programId;
- SaveDataType = saveDataType;
- UserId = userId;
- SaveDataId = saveDataId;
- Index = index;
- Rank = rank;
- }
-
- public bool Includes(in SaveDataInfo saveInfo)
- {
- if (SpaceId.HasValue && saveInfo.SpaceId != SpaceId.Value)
- {
- return false;
- }
-
- if (ProgramId.HasValue && saveInfo.ProgramId != ProgramId.Value)
- {
- return false;
- }
-
- if (SaveDataType.HasValue && saveInfo.Type != SaveDataType.Value)
- {
- return false;
- }
-
- if (UserId.HasValue && saveInfo.UserId != UserId.Value)
- {
- return false;
- }
-
- if (SaveDataId.HasValue && saveInfo.SaveDataId != SaveDataId.Value)
- {
- return false;
- }
-
- if (Index.HasValue && saveInfo.Index != Index.Value)
- {
- return false;
- }
-
- var filterRank = (SaveDataRank)(Rank & 1);
-
- // When filtering by secondary rank, match on both primary and secondary ranks
- if (filterRank == SaveDataRank.Primary && saveInfo.Rank == SaveDataRank.Secondary)
- {
- return false;
- }
-
- return true;
- }
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/StatusReportService.cs b/src/LibHac/FsSrv/StatusReportService.cs
index 6ff8be12..4e6ccb0e 100644
--- a/src/LibHac/FsSrv/StatusReportService.cs
+++ b/src/LibHac/FsSrv/StatusReportService.cs
@@ -1,9 +1,16 @@
using LibHac.Diag;
using LibHac.Fs;
+using LibHac.FsSystem;
using LibHac.Os;
namespace LibHac.FsSrv;
+///
+/// Handles status-report-related calls for .
+///
+/// This struct handles forwarding calls to the object.
+/// No permissions are needed to call any of this struct's functions.
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public readonly struct StatusReportService
{
private readonly StatusReportServiceImpl _serviceImpl;
@@ -30,6 +37,10 @@ public readonly struct StatusReportService
}
}
+///
+/// Manages getting and resetting various status reports and statistics about parts of the FS service.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class StatusReportServiceImpl
{
private Configuration _config;
@@ -38,13 +49,13 @@ public class StatusReportServiceImpl
public StatusReportServiceImpl(in Configuration configuration)
{
_config = configuration;
- _mutex.Initialize();
+ _mutex = new SdkMutexType();
}
public struct Configuration
{
- public NcaFileSystemServiceImpl NcaFsServiceImpl;
- public SaveDataFileSystemServiceImpl SaveFsServiceImpl;
+ public NcaFileSystemServiceImpl NcaFileSystemServiceImpl;
+ public SaveDataFileSystemServiceImpl SaveDataFileSystemServiceImpl;
// Missing: FatFileSystemCreator (Not an IFatFileSystemCreator)
public MemoryReport BufferManagerMemoryReport;
public MemoryReport ExpHeapMemoryReport;
@@ -60,15 +71,19 @@ public class StatusReportServiceImpl
public Result GetAndClearFileSystemProxyErrorInfo(out FileSystemProxyErrorInfo errorInfo)
{
- errorInfo = new FileSystemProxyErrorInfo();
+ errorInfo = default;
- _config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount,
+ Assert.SdkRequiresNotNull(_config.NcaFileSystemServiceImpl);
+
+ _config.NcaFileSystemServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount,
out errorInfo.UnrecoverableDataCorruptionByRemountCount,
out errorInfo.RecoveredByInvalidateCacheCount);
// Missing: GetFatInfo
- Result rc = _config.SaveFsServiceImpl.GetSaveDataIndexCount(out int count);
+ Assert.SdkRequiresNotNull(_config.SaveDataFileSystemServiceImpl);
+
+ Result rc = _config.SaveDataFileSystemServiceImpl.GetSaveDataIndexCount(out int count);
if (rc.IsFailure()) return rc;
errorInfo.SaveDataIndexCount = count;
@@ -77,40 +92,40 @@ public class StatusReportServiceImpl
public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo)
{
- using ScopedLock lk = ScopedLock.Lock(ref _mutex);
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
- reportInfo = new MemoryReportInfo();
+ reportInfo = default;
- // Missing: Get and clear pooled buffer stats
- reportInfo.PooledBufferFreeSizePeak = 0;
- reportInfo.PooledBufferRetriedCount = 0;
- reportInfo.PooledBufferReduceAllocationCount = 0;
+ reportInfo.PooledBufferFreeSizePeak = _config.FsServer.GetPooledBufferFreeSizePeak();
+ reportInfo.PooledBufferRetriedCount = _config.FsServer.GetPooledBufferRetriedCount();
+ reportInfo.PooledBufferReduceAllocationCount = _config.FsServer.GetPooledBufferReduceAllocationCount();
+ reportInfo.PooledBufferFailedIdealAllocationCountOnAsyncAccess =
+ _config.FsServer.GetPooledBufferFailedIdealAllocationCountOnAsyncAccess();
- MemoryReport report = _config.BufferManagerMemoryReport;
- if (report != null)
+ _config.FsServer.ClearPooledBufferPeak();
+
+ if (_config.BufferManagerMemoryReport is not null)
{
- reportInfo.BufferManagerFreeSizePeak = report.GetFreeSizePeak();
- reportInfo.BufferManagerTotalAllocatableSizePeak = report.GetTotalAllocatableSizePeak();
- reportInfo.BufferManagerRetriedCount = report.GetRetriedCount();
- report.Clear();
+ reportInfo.BufferManagerFreeSizePeak = _config.BufferManagerMemoryReport.GetFreeSizePeak();
+ reportInfo.BufferManagerTotalAllocatableSizePeak = _config.BufferManagerMemoryReport.GetTotalAllocatableSizePeak();
+ reportInfo.BufferManagerRetriedCount = _config.BufferManagerMemoryReport.GetRetriedCount();
+ _config.BufferManagerMemoryReport.Clear();
}
- report = _config.ExpHeapMemoryReport;
- if (report != null)
+ if (_config.ExpHeapMemoryReport is not null)
{
- reportInfo.ExpHeapFreeSizePeak = report.GetFreeSizePeak();
- report.Clear();
+ reportInfo.ExpHeapFreeSizePeak = _config.ExpHeapMemoryReport.GetFreeSizePeak();
+ _config.ExpHeapMemoryReport.Clear();
}
- report = _config.BufferPoolMemoryReport;
- if (report != null)
+ if (_config.BufferPoolMemoryReport is not null)
{
- reportInfo.BufferPoolFreeSizePeak = report.GetFreeSizePeak();
- reportInfo.BufferPoolAllocateSizeMax = report.GetAllocateSizeMax();
- report.Clear();
+ reportInfo.BufferPoolFreeSizePeak = _config.BufferPoolMemoryReport.GetFreeSizePeak();
+ reportInfo.BufferPoolAllocateSizeMax = _config.BufferPoolMemoryReport.GetAllocateSizeMax();
+ _config.BufferPoolMemoryReport.Clear();
}
- if (_config.GetPatrolAllocateCounts != null)
+ if (_config.GetPatrolAllocateCounts is not null)
{
_config.GetPatrolAllocateCounts(out reportInfo.PatrolReadAllocateBufferSuccessCount,
out reportInfo.PatrolReadAllocateBufferFailureCount);
diff --git a/src/LibHac/FsSrv/TimeService.cs b/src/LibHac/FsSrv/TimeService.cs
index bf8f0977..f982f254 100644
--- a/src/LibHac/FsSrv/TimeService.cs
+++ b/src/LibHac/FsSrv/TimeService.cs
@@ -6,6 +6,12 @@ using LibHac.Os;
namespace LibHac.FsSrv;
+///
+/// Handles time-related calls for .
+///
+/// This struct handles checking a process' permissions before forwarding
+/// a request to the object.
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public readonly struct TimeService
{
private readonly TimeServiceImpl _serviceImpl;
@@ -19,7 +25,8 @@ public readonly struct TimeService
public Result SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
{
- Result rc = GetProgramInfo(out ProgramInfo programInfo);
+ using var programRegistry = new ProgramRegistryImpl(_serviceImpl.FsServer);
+ Result rc = programRegistry.GetProgramInfo(out ProgramInfo programInfo, _processId);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.SetCurrentPosixTime))
@@ -28,32 +35,33 @@ public readonly struct TimeService
_serviceImpl.SetCurrentPosixTimeWithTimeDifference(currentTime, timeDifference);
return Result.Success;
}
-
- private Result GetProgramInfo(out ProgramInfo programInfo)
- {
- return _serviceImpl.GetProgramInfo(out programInfo, _processId);
- }
}
+///
+/// Manages the current time used by the FS service.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class TimeServiceImpl
{
private long _basePosixTime;
- private int _timeDifference;
+ private int _timeDifferenceSeconds;
private SdkMutexType _mutex;
- private FileSystemServer _fsServer;
+ // LibHac addition
+ internal FileSystemServer FsServer { get; }
public TimeServiceImpl(FileSystemServer fsServer)
{
- _fsServer = fsServer;
_basePosixTime = 0;
- _timeDifference = 0;
- _mutex.Initialize();
+ _timeDifferenceSeconds = 0;
+ _mutex = new SdkMutexType();
+
+ FsServer = fsServer;
}
private long GetSystemSeconds()
{
- OsState os = _fsServer.Hos.Os;
+ OsState os = FsServer.Hos.Os;
Tick tick = os.GetSystemTick();
TimeSpan timeSpan = os.ConvertToTimeSpan(tick);
@@ -69,7 +77,7 @@ public class TimeServiceImpl
{
UnsafeHelpers.SkipParamInit(out currentTime, out timeDifference);
- using ScopedLock lk = ScopedLock.Lock(ref _mutex);
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
if (_basePosixTime == 0)
return ResultFs.NotInitialized.Log();
@@ -81,7 +89,7 @@ public class TimeServiceImpl
if (!Unsafe.IsNullRef(ref timeDifference))
{
- timeDifference = _timeDifference;
+ timeDifference = _timeDifferenceSeconds;
}
return Result.Success;
@@ -89,15 +97,9 @@ public class TimeServiceImpl
public void SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
{
- using ScopedLock lk = ScopedLock.Lock(ref _mutex);
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
_basePosixTime = currentTime - GetSystemSeconds();
- _timeDifference = timeDifference;
+ _timeDifferenceSeconds = timeDifference;
}
-
- internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
- {
- var registry = new ProgramRegistryImpl(_fsServer);
- return registry.GetProgramInfo(out programInfo, processId);
- }
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/AsynchronousAccess.cs b/src/LibHac/FsSystem/AsynchronousAccess.cs
index b85e513c..a6dddaf2 100644
--- a/src/LibHac/FsSystem/AsynchronousAccess.cs
+++ b/src/LibHac/FsSystem/AsynchronousAccess.cs
@@ -1,10 +1,19 @@
using System;
using LibHac.Common;
using LibHac.Diag;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
using LibHac.Util;
namespace LibHac.FsSystem;
+///
+/// Splits read and write requests on an or into smaller chunks
+/// so the request can be processed by multiple threads simultaneously.
+/// This interface exists because of where it will split requests into
+/// chunks that start and end on the boundaries of the compressed blocks.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public interface IAsynchronousAccessSplitter : IDisposable
{
private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new();
@@ -57,6 +66,11 @@ public interface IAsynchronousAccessSplitter : IDisposable
Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize);
}
+///
+/// The default that is used when an
+/// or doesn't need any special logic to split a request into multiple chunks.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
{
public void Dispose() { }
diff --git a/src/LibHac/FsSystem/PooledBuffer.cs b/src/LibHac/FsSystem/PooledBuffer.cs
index de482717..1c0437f1 100644
--- a/src/LibHac/FsSystem/PooledBuffer.cs
+++ b/src/LibHac/FsSystem/PooledBuffer.cs
@@ -1,9 +1,64 @@
using System;
using System.Buffers;
using LibHac.Diag;
+using LibHac.FsSrv;
+using LibHac.Os;
namespace LibHac.FsSystem;
+public static class PooledBufferGlobalMethods
+{
+ public static long GetPooledBufferRetriedCount(this FileSystemServer fsSrv)
+ {
+ return fsSrv.Globals.PooledBuffer.CountRetried;
+ }
+
+ public static long GetPooledBufferReduceAllocationCount(this FileSystemServer fsSrv)
+ {
+ return fsSrv.Globals.PooledBuffer.CountReduceAllocation;
+ }
+
+ public static long GetPooledBufferFailedIdealAllocationCountOnAsyncAccess(this FileSystemServer fsSrv)
+ {
+ return fsSrv.Globals.PooledBuffer.CountFailedIdealAllocationOnAsyncAccess;
+ }
+
+ public static long GetPooledBufferFreeSizePeak(this FileSystemServer fsSrv)
+ {
+ ref PooledBufferGlobals g = ref fsSrv.Globals.PooledBuffer;
+
+ using ScopedLock scopedLock = ScopedLock.Lock(ref g.HeapMutex);
+ return g.SizeHeapFreePeak;
+ }
+
+ public static void ClearPooledBufferPeak(this FileSystemServer fsSrv)
+ {
+ ref PooledBufferGlobals g = ref fsSrv.Globals.PooledBuffer;
+
+ using ScopedLock scopedLock = ScopedLock.Lock(ref g.HeapMutex);
+
+ // Missing: Get SizeHeapFreePeak from the heap object
+ g.CountRetried = 0;
+ g.CountReduceAllocation = 0;
+ g.CountFailedIdealAllocationOnAsyncAccess = 0;
+ }
+}
+
+internal struct PooledBufferGlobals
+{
+ public SdkMutexType HeapMutex;
+ public long SizeHeapFreePeak;
+ public Memory HeapBuffer;
+ public long CountRetried;
+ public long CountReduceAllocation;
+ public long CountFailedIdealAllocationOnAsyncAccess;
+
+ public void Initialize()
+ {
+ HeapMutex = new SdkMutexType();
+ }
+}
+
// Implement the PooledBuffer interface using .NET ArrayPools
public struct PooledBuffer : IDisposable
{
diff --git a/src/LibHac/FsSystem/StorageFile.cs b/src/LibHac/FsSystem/StorageFile.cs
index f622791d..3d95fb21 100644
--- a/src/LibHac/FsSystem/StorageFile.cs
+++ b/src/LibHac/FsSystem/StorageFile.cs
@@ -1,59 +1,68 @@
using System;
using LibHac.Common;
+using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSystem;
+///
+/// Allows interacting with an via an interface.
+///
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
public class StorageFile : IFile
{
- private IStorage BaseStorage { get; }
- private OpenMode Mode { get; }
+ private IStorage _baseStorage;
+ private OpenMode _mode;
public StorageFile(IStorage baseStorage, OpenMode mode)
{
- BaseStorage = baseStorage;
- Mode = mode;
+ _baseStorage = baseStorage;
+ _mode = mode;
}
- protected override Result DoRead(out long bytesRead, long offset, Span destination,
- in ReadOption option)
+ protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option)
{
UnsafeHelpers.SkipParamInit(out bytesRead);
- Result rc = DryRead(out long toRead, offset, destination.Length, in option, Mode);
+ Assert.SdkRequiresNotNull(_baseStorage);
+
+ Result rc = DryRead(out long readSize, offset, destination.Length, in option, _mode);
if (rc.IsFailure()) return rc;
- if (toRead == 0)
+ if (readSize == 0)
{
bytesRead = 0;
return Result.Success;
}
- rc = BaseStorage.Read(offset, destination.Slice(0, (int)toRead));
+ rc = _baseStorage.Read(offset, destination.Slice(0, (int)readSize));
if (rc.IsFailure()) return rc;
- bytesRead = toRead;
+ bytesRead = readSize;
return Result.Success;
}
protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
{
- Result rc = DryWrite(out bool isResizeNeeded, offset, source.Length, in option, Mode);
+ Assert.SdkRequiresNotNull(_baseStorage);
+
+ Result rc = DryWrite(out bool isAppendNeeded, offset, source.Length, in option, _mode);
if (rc.IsFailure()) return rc;
- if (isResizeNeeded)
+ if (isAppendNeeded)
{
rc = DoSetSize(offset + source.Length);
if (rc.IsFailure()) return rc;
}
- rc = BaseStorage.Write(offset, source);
+ rc = _baseStorage.Write(offset, source);
if (rc.IsFailure()) return rc;
if (option.HasFlushFlag())
{
- return Flush();
+ rc = Flush();
+ if (rc.IsFailure()) return rc.Miss();
}
return Result.Success;
@@ -61,28 +70,68 @@ public class StorageFile : IFile
protected override Result DoFlush()
{
- if (!Mode.HasFlag(OpenMode.Write))
+ Assert.SdkRequiresNotNull(_baseStorage);
+
+ if (!_mode.HasFlag(OpenMode.Write))
return Result.Success;
- return BaseStorage.Flush();
+ return _baseStorage.Flush();
}
protected override Result DoGetSize(out long size)
{
- return BaseStorage.GetSize(out size);
+ Assert.SdkRequiresNotNull(_baseStorage);
+
+ return _baseStorage.GetSize(out size);
}
protected override Result DoSetSize(long size)
{
- if (!Mode.HasFlag(OpenMode.Write))
- return ResultFs.WriteUnpermitted.Log();
+ Assert.SdkRequiresNotNull(_baseStorage);
- return BaseStorage.SetSize(size);
+ Result rc = DrySetSize(size, _mode);
+ if (rc.IsFailure()) return rc.Miss();
+
+ return _baseStorage.SetSize(size);
}
protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan inBuffer)
{
- return ResultFs.NotImplemented.Log();
+ Assert.SdkRequiresNotNull(_baseStorage);
+
+ switch (operationId)
+ {
+ case OperationId.InvalidateCache:
+ {
+ if (!_mode.HasFlag(OpenMode.Read))
+ return ResultFs.ReadUnpermitted.Log();
+
+ Result rc = _baseStorage.OperateRange(OperationId.InvalidateCache, offset, size);
+ if (rc.IsFailure()) return rc.Miss();
+
+ break;
+ }
+ case OperationId.QueryRange:
+ {
+ if (offset < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ Result rc = GetSize(out long fileSize);
+ if (rc.IsFailure()) return rc.Miss();
+
+ long operableSize = Math.Max(0, fileSize - offset);
+ long operateSize = Math.Min(operableSize, size);
+
+ rc = _baseStorage.OperateRange(outBuffer, operationId, offset, operateSize, inBuffer);
+ if (rc.IsFailure()) return rc.Miss();
+
+ break;
+ }
+ default:
+ return ResultFs.UnsupportedOperateRangeForStorageFile.Log();
+ }
+
+ return Result.Success;
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/Util/Optional.cs b/src/LibHac/Util/Optional.cs
index ea11fb83..e1549330 100644
--- a/src/LibHac/Util/Optional.cs
+++ b/src/LibHac/Util/Optional.cs
@@ -35,6 +35,12 @@ public struct Optional
_hasValue = true;
}
+ public Optional(T value)
+ {
+ _value = value;
+ _hasValue = true;
+ }
+
public static implicit operator Optional(in T value) => new Optional(in value);
public void Set(T value)