Add SaveDataExtraDataAccessorCacheManager

This commit is contained in:
Alex Barney 2021-05-03 16:05:32 -07:00
parent 44229f5986
commit d06731f464
2 changed files with 313 additions and 0 deletions

View file

@ -0,0 +1,134 @@
using System.Collections.Generic;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Os;
namespace LibHac.FsSrv.Impl
{
/// <summary>
/// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems.
/// </summary>
public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver
{
private struct Cache
{
private ReferenceCountedDisposable<ISaveDataExtraDataAccessor>.WeakReference _accessor;
private readonly SaveDataSpaceId _spaceId;
private readonly ulong _saveDataId;
public Cache(ReferenceCountedDisposable<ISaveDataExtraDataAccessor> accessor, SaveDataSpaceId spaceId,
ulong saveDataId)
{
_accessor = new ReferenceCountedDisposable<ISaveDataExtraDataAccessor>.WeakReference(accessor);
_spaceId = spaceId;
_saveDataId = saveDataId;
}
public bool Contains(SaveDataSpaceId spaceId, ulong saveDataId)
{
return _spaceId == spaceId && _saveDataId == saveDataId;
}
public ReferenceCountedDisposable<ISaveDataExtraDataAccessor> Lock()
{
return _accessor.TryAddReference();
}
}
private readonly LinkedList<Cache> _accessorList;
private SdkRecursiveMutex _mutex;
public SaveDataExtraDataAccessorCacheManager()
{
_accessorList = new LinkedList<Cache>();
_mutex = new SdkRecursiveMutex();
}
public void Dispose()
{
_accessorList.Clear();
}
public Result Register(ReferenceCountedDisposable<ISaveDataExtraDataAccessor> accessor,
SaveDataSpaceId spaceId, ulong saveDataId)
{
var cache = new Cache(accessor, spaceId, saveDataId);
using ScopedLock<SdkRecursiveMutex> scopedLock = ScopedLock.Lock(ref _mutex);
_accessorList.AddLast(cache);
return Result.Success;
}
public void Unregister(SaveDataSpaceId spaceId, ulong saveDataId)
{
using ScopedLock<SdkRecursiveMutex> scopedLock = ScopedLock.Lock(ref _mutex);
UnregisterImpl(spaceId, saveDataId);
}
private void UnregisterImpl(SaveDataSpaceId spaceId, ulong saveDataId)
{
LinkedListNode<Cache> 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<ISaveDataExtraDataAccessor> accessor,
SaveDataSpaceId spaceId, ulong saveDataId)
{
UnsafeHelpers.SkipParamInit(out accessor);
using ScopedLock<SdkRecursiveMutex> scopedLock = ScopedLock.Lock(ref _mutex);
LinkedListNode<Cache> currentNode = _accessorList.First;
while (true)
{
if (currentNode is null)
return ResultFs.TargetNotFound.Log();
if (currentNode.ValueRef.Contains(spaceId, saveDataId))
break;
currentNode = currentNode.Next;
}
ReferenceCountedDisposable<ISaveDataExtraDataAccessor> 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<SdkRecursiveMutex> GetScopedLock()
{
return new ScopedLock<SdkRecursiveMutex>(ref _mutex);
}
}
}

View file

@ -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;
}
}
/// <summary>
/// Wraps an <see cref="ISaveDataExtraDataAccessor"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s.
/// </summary>
public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor
{
private ReferenceCountedDisposable<ISaveDataExtraDataAccessor> _accessor;
public SaveDataExtraDataResultConvertAccessor(
ref ReferenceCountedDisposable<ISaveDataExtraDataAccessor> accessor)
{
_accessor = Shared.Move(ref accessor);
}
public static ReferenceCountedDisposable<ISaveDataExtraDataAccessor> CreateShared(
ref ReferenceCountedDisposable<ISaveDataExtraDataAccessor> accessor)
{
var resultConvertAccessor = new SaveDataExtraDataResultConvertAccessor(ref accessor);
return new ReferenceCountedDisposable<ISaveDataExtraDataAccessor>(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);
}
}
}