diff --git a/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs new file mode 100644 index 00000000..dabe0f0b --- /dev/null +++ b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs @@ -0,0 +1,134 @@ +using System.Collections.Generic; +using LibHac.Common; +using LibHac.Fs; +using LibHac.FsSystem; +using LibHac.Os; + +namespace LibHac.FsSrv.Impl +{ + /// + /// Holds the s for opened save data file systems. + /// + public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver + { + private struct Cache + { + private ReferenceCountedDisposable.WeakReference _accessor; + private readonly SaveDataSpaceId _spaceId; + private readonly ulong _saveDataId; + + public Cache(ReferenceCountedDisposable accessor, SaveDataSpaceId spaceId, + ulong saveDataId) + { + _accessor = new ReferenceCountedDisposable.WeakReference(accessor); + _spaceId = spaceId; + _saveDataId = saveDataId; + } + + public bool Contains(SaveDataSpaceId spaceId, ulong saveDataId) + { + return _spaceId == spaceId && _saveDataId == saveDataId; + } + + public ReferenceCountedDisposable Lock() + { + return _accessor.TryAddReference(); + } + } + + private readonly LinkedList _accessorList; + private SdkRecursiveMutex _mutex; + + public SaveDataExtraDataAccessorCacheManager() + { + _accessorList = new LinkedList(); + _mutex = new SdkRecursiveMutex(); + } + + public void Dispose() + { + _accessorList.Clear(); + } + + public Result Register(ReferenceCountedDisposable accessor, + SaveDataSpaceId spaceId, ulong saveDataId) + { + var cache = new Cache(accessor, spaceId, saveDataId); + + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + _accessorList.AddLast(cache); + return Result.Success; + } + + public void Unregister(SaveDataSpaceId spaceId, ulong saveDataId) + { + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + UnregisterImpl(spaceId, saveDataId); + } + + private void UnregisterImpl(SaveDataSpaceId spaceId, ulong saveDataId) + { + LinkedListNode currentNode = _accessorList.First; + + while (currentNode is not null) + { + if (currentNode.ValueRef.Contains(spaceId, saveDataId)) + { + _accessorList.Remove(currentNode); + return; + } + + currentNode = currentNode.Next; + } + } + + public Result GetCache(out ReferenceCountedDisposable accessor, + SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out accessor); + + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + LinkedListNode currentNode = _accessorList.First; + + while (true) + { + if (currentNode is null) + return ResultFs.TargetNotFound.Log(); + + if (currentNode.ValueRef.Contains(spaceId, saveDataId)) + break; + + currentNode = currentNode.Next; + } + + ReferenceCountedDisposable tempAccessor = null; + try + { + tempAccessor = currentNode.ValueRef.Lock(); + + // Return early if the accessor was already disposed + if (tempAccessor is null) + { + // Note: Nintendo doesn't remove the accessor from the list in this case + _accessorList.Remove(currentNode); + return ResultFs.TargetNotFound.Log(); + } + + accessor = SaveDataExtraDataResultConvertAccessor.CreateShared(ref tempAccessor); + return Result.Success; + } + finally + { + tempAccessor?.Dispose(); + } + } + + public ScopedLock GetScopedLock() + { + return new ScopedLock(ref _mutex); + } + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs new file mode 100644 index 00000000..5649416f --- /dev/null +++ b/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs @@ -0,0 +1,179 @@ +using LibHac.Common; +using LibHac.Diag; +using LibHac.Fs; +using LibHac.FsSystem; + +namespace LibHac.FsSrv.Impl +{ + public static class SaveDataResultConvert + { + private static Result ConvertCorruptedResult(Result result) + { + if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result)) + { + if (ResultFs.IncorrectIntegrityVerificationMagic.Includes(result)) + return ResultFs.IncorrectSaveDataIntegrityVerificationMagic.Value; + + if (ResultFs.InvalidZeroHash.Includes(result)) + return ResultFs.InvalidSaveDataZeroHash.Value; + + if (ResultFs.NonRealDataVerificationFailed.Includes(result)) + return ResultFs.SaveDataNonRealDataVerificationFailed.Value; + + if (ResultFs.ClearedRealDataVerificationFailed.Includes(result)) + return ResultFs.ClearedSaveDataRealDataVerificationFailed.Value; + + if (ResultFs.UnclearedRealDataVerificationFailed.Includes(result)) + return ResultFs.UnclearedSaveDataRealDataVerificationFailed.Value; + + Assert.SdkAssert(false); + } + + if (ResultFs.HostFileSystemCorrupted.Includes(result)) + { + if (ResultFs.HostEntryCorrupted.Includes(result)) + return ResultFs.SaveDataHostEntryCorrupted.Value; + + if (ResultFs.HostFileDataCorrupted.Includes(result)) + return ResultFs.SaveDataHostFileDataCorrupted.Value; + + if (ResultFs.HostFileCorrupted.Includes(result)) + return ResultFs.SaveDataHostFileCorrupted.Value; + + if (ResultFs.InvalidHostHandle.Includes(result)) + return ResultFs.InvalidSaveDataHostHandle.Value; + + Assert.SdkAssert(false); + } + + if (ResultFs.DatabaseCorrupted.Includes(result)) + { + if (ResultFs.InvalidAllocationTableBlock.Includes(result)) + return ResultFs.InvalidSaveDataAllocationTableBlock.Value; + + if (ResultFs.InvalidKeyValueListElementIndex.Includes(result)) + return ResultFs.InvalidSaveDataKeyValueListElementIndex.Value; + + if (ResultFs.AllocationTableIteratedRangeEntry.Includes(result)) + return ResultFs.SaveDataAllocationTableIteratedRangeEntry.Value; + + if (ResultFs.InvalidAllocationTableOffset.Includes(result)) + return ResultFs.InvalidSaveDataAllocationTableOffset.Value; + + if (ResultFs.InvalidAllocationTableBlockCount.Includes(result)) + return ResultFs.InvalidSaveDataAllocationTableBlockCount.Value; + + if (ResultFs.InvalidKeyValueListEntryIndex.Includes(result)) + return ResultFs.InvalidSaveDataKeyValueListEntryIndex.Value; + + if (ResultFs.InvalidBitmapIndex.Includes(result)) + return ResultFs.InvalidSaveDataBitmapIndex.Value; + + Assert.SdkAssert(false); + } + + if (ResultFs.ZeroBitmapFileCorrupted.Includes(result)) + { + if (ResultFs.IncompleteBlockInZeroBitmapHashStorageFile.Includes(result)) + return ResultFs.IncompleteBlockInZeroBitmapHashStorageFileSaveData.Value; + + Assert.SdkAssert(false); + } + + return result; + } + + public static Result ConvertSaveFsDriverPublicResult(Result result) + { + if (result.IsSuccess()) + return result; + + if (ResultFs.UnsupportedVersion.Includes(result)) + return ResultFs.UnsupportedSaveDataVersion.Value; + + if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result) || + ResultFs.BuiltInStorageCorrupted.Includes(result) || + ResultFs.HostFileSystemCorrupted.Includes(result) || + ResultFs.DatabaseCorrupted.Includes(result) || + ResultFs.ZeroBitmapFileCorrupted.Includes(result)) + { + return ConvertCorruptedResult(result); + } + + if (ResultFs.FatFileSystemCorrupted.Includes(result)) + return result; + + if (ResultFs.NotFound.Includes(result)) + return ResultFs.PathNotFound.Value; + + if (ResultFs.AllocationTableFull.Includes(result)) + return ResultFs.UsableSpaceNotEnough.Value; + + if (ResultFs.AlreadyExists.Includes(result)) + return ResultFs.PathAlreadyExists.Value; + + if (ResultFs.InvalidOffset.Includes(result)) + return ResultFs.OutOfRange.Value; + + if (ResultFs.IncompatiblePath.Includes(result) || + ResultFs.FileNotFound.Includes(result)) + { + return ResultFs.PathNotFound.Value; + } + + return result; + } + } + + /// + /// Wraps an , converting its returned s + /// to save-data-specific s. + /// + public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor + { + private ReferenceCountedDisposable _accessor; + + public SaveDataExtraDataResultConvertAccessor( + ref ReferenceCountedDisposable accessor) + { + _accessor = Shared.Move(ref accessor); + } + + public static ReferenceCountedDisposable CreateShared( + ref ReferenceCountedDisposable accessor) + { + var resultConvertAccessor = new SaveDataExtraDataResultConvertAccessor(ref accessor); + return new ReferenceCountedDisposable(resultConvertAccessor); + } + + public void Dispose() + { + _accessor?.Dispose(); + _accessor = null; + } + + public Result WriteExtraData(in SaveDataExtraData extraData) + { + Result rc = _accessor.Target.WriteExtraData(in extraData); + return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); + } + + public Result CommitExtraData(bool updateTimeStamp) + { + Result rc = _accessor.Target.CommitExtraData(updateTimeStamp); + return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); + } + + public Result ReadExtraData(out SaveDataExtraData extraData) + { + Result rc = _accessor.Target.ReadExtraData(out extraData); + return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); + } + + public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, + ulong saveDataId) + { + _accessor.Target.RegisterCacheObserver(observer, spaceId, saveDataId); + } + } +} \ No newline at end of file