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)