Add the new save data cache classes from 14.0.0

This commit is contained in:
Alex Barney 2022-04-21 19:29:03 -07:00
parent 99ad308b84
commit cac796ac19
10 changed files with 322 additions and 62 deletions

View file

@ -10,13 +10,13 @@ namespace LibHac.FsSrv.Impl;
/// <summary> /// <summary>
/// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems. /// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks> /// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorObserver
{ {
/// <summary> /// <summary>
/// Holds a single cached extra data accessor identified by its save data ID and save data space ID. /// Holds a single cached extra data accessor identified by its save data ID and save data space ID.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks> /// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
[NonCopyable] [NonCopyable]
private struct Cache : IDisposable private struct Cache : IDisposable
{ {
@ -77,13 +77,14 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC
public Result Register(in SharedRef<ISaveDataExtraDataAccessor> accessor, SaveDataSpaceId spaceId, public Result Register(in SharedRef<ISaveDataExtraDataAccessor> accessor, SaveDataSpaceId spaceId,
ulong saveDataId) ulong saveDataId)
{ {
accessor.Get.RegisterCacheObserver(this, spaceId, saveDataId);
var node = new LinkedListNode<Cache>(new Cache(in accessor, spaceId, saveDataId)); var node = new LinkedListNode<Cache>(new Cache(in accessor, spaceId, saveDataId));
using (ScopedLock.Lock(ref _mutex)) using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
{
UnregisterImpl(spaceId, saveDataId); UnregisterImpl(spaceId, saveDataId);
_accessorList.AddLast(node); _accessorList.AddLast(node);
}
return Result.Success; return Result.Success;
} }
@ -134,7 +135,7 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC
if (!accessor.HasValue) if (!accessor.HasValue)
return ResultFs.TargetNotFound.Log(); return ResultFs.TargetNotFound.Log();
outAccessor.Reset(new SaveDataExtraDataResultConvertAccessor(ref accessor.Ref())); outAccessor.SetByMove(ref accessor.Ref());
return Result.Success; return Result.Success;
} }

View file

@ -0,0 +1,165 @@
using System;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Os;
namespace LibHac.FsSrv.Impl;
/// <summary>
/// Manages a list of cached save data file systems. Each file system is registered and retrieved
/// based on its save data ID and save data space ID.
/// </summary>
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public class SaveDataFileSystemCacheManager : IDisposable
{
[NonCopyable]
private struct Cache
{
private SharedRef<ISaveDataFileSystem> _fileSystem;
private ulong _saveDataId;
private SaveDataSpaceId _spaceId;
public void Dispose()
{
_fileSystem.Destroy();
}
public bool IsCached(SaveDataSpaceId spaceId, ulong saveDataId)
{
return _fileSystem.HasValue && _spaceId == spaceId && _saveDataId == saveDataId;
}
public SharedRef<ISaveDataFileSystem> Move()
{
return SharedRef<ISaveDataFileSystem>.CreateMove(ref _fileSystem);
}
public void Register(ref SharedRef<ISaveDataFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
_fileSystem.SetByMove(ref fileSystem);
_spaceId = spaceId;
_saveDataId = saveDataId;
}
public void Unregister()
{
_fileSystem.Reset();
}
}
private SdkRecursiveMutexType _mutex;
private Cache[] _cachedFileSystems;
private int _maxCachedFileSystemCount;
private int _nextCacheIndex;
public SaveDataFileSystemCacheManager()
{
_mutex = new SdkRecursiveMutexType();
}
public void Dispose()
{
Cache[] caches = Shared.Move(ref _cachedFileSystems);
if (caches is not null)
{
for (int i = 0; i < caches.Length; i++)
{
caches[i].Dispose();
}
}
}
public Result Initialize(int maxCacheCount)
{
Assert.SdkRequiresGreaterEqual(maxCacheCount, 0);
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
Assert.SdkAssert(_cachedFileSystems is null);
_maxCachedFileSystemCount = maxCacheCount;
if (maxCacheCount > 0)
{
// Note: The original checks for overflow here
_cachedFileSystems = new Cache[maxCacheCount];
}
return Result.Success;
}
public UniqueLockRef<SdkRecursiveMutexType> GetScopedLock()
{
return new UniqueLockRef<SdkRecursiveMutexType>(ref _mutex);
}
public bool GetCache(ref SharedRef<ISaveDataFileSystem> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
Assert.SdkRequiresGreaterEqual(_maxCachedFileSystemCount, 0);
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
for (int i = 0; i < _maxCachedFileSystemCount; i++)
{
if (_cachedFileSystems[i].IsCached(spaceId, saveDataId))
{
using SharedRef<ISaveDataFileSystem> cachedFs = _cachedFileSystems[i].Move();
outFileSystem.SetByMove(ref cachedFs.Ref());
return true;
}
}
return false;
}
public void Register(ref SharedRef<ISaveDataFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
Assert.SdkRequiresNotNull(in fileSystem);
if (_maxCachedFileSystemCount <= 0)
return;
Assert.SdkRequiresGreaterEqual(_nextCacheIndex, 0);
Assert.SdkRequiresGreater(_maxCachedFileSystemCount, _nextCacheIndex);
if (!fileSystem.Get.IsSaveDataFileSystemCacheEnabled())
{
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
fileSystem.Reset();
}
else if (spaceId == SaveDataSpaceId.SdSystem)
{
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
fileSystem.Reset();
}
else
{
Result rc = fileSystem.Get.RollbackOnlyModified();
if (rc.IsSuccess())
{
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
_cachedFileSystems[_nextCacheIndex].Register(ref fileSystem, spaceId, saveDataId);
_nextCacheIndex = (_nextCacheIndex + 1) % _maxCachedFileSystemCount;
}
}
}
public void Unregister(SaveDataSpaceId spaceId, ulong saveDataId)
{
Assert.SdkRequiresGreaterEqual(_maxCachedFileSystemCount, 0);
using ScopedLock<SdkRecursiveMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
for (int i = 0; i < _maxCachedFileSystemCount; i++)
{
if (_cachedFileSystems[i].IsCached(spaceId, saveDataId))
{
_cachedFileSystems[i].Unregister();
}
}
}
}

View file

@ -0,0 +1,117 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl;
/// <summary>
/// Wraps an <see cref="ISaveDataFileSystem"/>.
/// Upon disposal the base file system is returned to the provided <see cref="ISaveDataFileSystemCacheManager"/>.
/// </summary>
/// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public class SaveDataFileSystemCacheRegister : IFileSystem
{
private SharedRef<ISaveDataFileSystem> _baseFileSystem;
private SaveDataFileSystemCacheManager _cacheManager;
private SaveDataSpaceId _spaceId;
private ulong _saveDataId;
public SaveDataFileSystemCacheRegister(ref SharedRef<ISaveDataFileSystem> baseFileSystem,
SaveDataFileSystemCacheManager cacheManager, SaveDataSpaceId spaceId, ulong saveDataId)
{
_baseFileSystem = SharedRef<ISaveDataFileSystem>.CreateMove(ref baseFileSystem);
_cacheManager = cacheManager;
_spaceId = spaceId;
_saveDataId = saveDataId;
}
public override void Dispose()
{
_cacheManager.Register(ref _baseFileSystem, _spaceId, _saveDataId);
_baseFileSystem.Destroy();
base.Dispose();
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{
return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode);
}
protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{
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)
{
return _baseFileSystem.Get.CreateFile(in path, size, option);
}
protected override Result DoDeleteFile(in Path path)
{
return _baseFileSystem.Get.DeleteFile(in path);
}
protected override Result DoCreateDirectory(in Path path)
{
return _baseFileSystem.Get.CreateDirectory(in path);
}
protected override Result DoDeleteDirectory(in Path path)
{
return _baseFileSystem.Get.DeleteDirectory(in path);
}
protected override Result DoDeleteDirectoryRecursively(in Path path)
{
return _baseFileSystem.Get.DeleteDirectoryRecursively(in path);
}
protected override Result DoCleanDirectoryRecursively(in Path path)
{
return _baseFileSystem.Get.CleanDirectoryRecursively(in path);
}
protected override Result DoRenameFile(in Path currentPath, in Path newPath)
{
return _baseFileSystem.Get.RenameFile(in currentPath, in newPath);
}
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
{
return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath);
}
protected override Result DoCommit()
{
return _baseFileSystem.Get.Commit();
}
protected override Result DoCommitProvisionally(long counter)
{
return _baseFileSystem.Get.CommitProvisionally(counter);
}
protected override Result DoRollback()
{
return _baseFileSystem.Get.Rollback();
}
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);
}
}

View file

@ -2,7 +2,6 @@
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
@ -201,48 +200,4 @@ public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
{ {
return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result);
} }
}
/// <summary>
/// Wraps an <see cref="ISaveDataExtraDataAccessor"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor
{
private SharedRef<ISaveDataExtraDataAccessor> _accessor;
public SaveDataExtraDataResultConvertAccessor(ref SharedRef<ISaveDataExtraDataAccessor> accessor)
{
_accessor = SharedRef<ISaveDataExtraDataAccessor>.CreateMove(ref accessor);
}
public void Dispose()
{
_accessor.Destroy();
}
public Result WriteExtraData(in SaveDataExtraData extraData)
{
Result rc = _accessor.Get.WriteExtraData(in extraData);
return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
}
public Result CommitExtraData(bool updateTimeStamp)
{
Result rc = _accessor.Get.CommitExtraData(updateTimeStamp);
return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
}
public Result ReadExtraData(out SaveDataExtraData extraData)
{
Result rc = _accessor.Get.ReadExtraData(out extraData);
return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
}
public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId,
ulong saveDataId)
{
_accessor.Get.RegisterCacheObserver(observer, spaceId, saveDataId);
}
} }

View file

@ -20,7 +20,7 @@ public class SaveDataFileSystemServiceImpl
private Configuration _config; private Configuration _config;
private EncryptionSeed _encryptionSeed; private EncryptionSeed _encryptionSeed;
private SaveDataFileSystemCacheManager _saveDataFsCacheManager; private FsSystem.SaveDataFileSystemCacheManager _saveDataFsCacheManager;
private SaveDataExtraDataAccessorCacheManager _extraDataCacheManager; private SaveDataExtraDataAccessorCacheManager _extraDataCacheManager;
// Save data porter manager // Save data porter manager
private bool _isSdCardAccessible; private bool _isSdCardAccessible;
@ -47,7 +47,7 @@ public class SaveDataFileSystemServiceImpl
public SaveDataFileSystemServiceImpl(in Configuration configuration) public SaveDataFileSystemServiceImpl(in Configuration configuration)
{ {
_config = configuration; _config = configuration;
_saveDataFsCacheManager = new SaveDataFileSystemCacheManager(); _saveDataFsCacheManager = new FsSystem.SaveDataFileSystemCacheManager();
_extraDataCacheManager = new SaveDataExtraDataAccessorCacheManager(); _extraDataCacheManager = new SaveDataExtraDataAccessorCacheManager();
_timeStampGetter = new TimeStampGetter(this); _timeStampGetter = new TimeStampGetter(this);

View file

@ -83,7 +83,7 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ISaveDataExtraDataAcc
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId) public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View file

@ -50,7 +50,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
private RandomDataGenerator _randomGenerator; private RandomDataGenerator _randomGenerator;
// Additions to support caching // Additions to support caching
private ISaveDataExtraDataAccessorCacheObserver _cacheObserver; private ISaveDataExtraDataAccessorObserver _cacheObserver;
private SaveDataSpaceId _spaceId; private SaveDataSpaceId _spaceId;
private ulong _saveDataId; private ulong _saveDataId;
@ -1056,7 +1056,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
return Result.Success; return Result.Success;
} }
public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId,
ulong saveDataId) ulong saveDataId)
{ {
_cacheObserver = observer; _cacheObserver = observer;

View file

@ -12,5 +12,5 @@ public interface ISaveDataExtraDataAccessor : IDisposable
Result WriteExtraData(in SaveDataExtraData extraData); Result WriteExtraData(in SaveDataExtraData extraData);
Result CommitExtraData(bool updateTimeStamp); Result CommitExtraData(bool updateTimeStamp);
Result ReadExtraData(out SaveDataExtraData extraData); Result ReadExtraData(out SaveDataExtraData extraData);
void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId); void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
} }

View file

@ -9,8 +9,8 @@ namespace LibHac.FsSystem;
/// <see cref="SaveDataExtraDataAccessorCacheManager"/>. When an extra data accessor is disposed, the accessor will /// <see cref="SaveDataExtraDataAccessorCacheManager"/>. When an extra data accessor is disposed, the accessor will
/// use this interface to notify the cache manager that it should be removed from the extra data cache. /// use this interface to notify the cache manager that it should be removed from the extra data cache.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks> /// <remarks>Based on FS 14.1.0 (nnSdk 14.3.0)</remarks>
public interface ISaveDataExtraDataAccessorCacheObserver : IDisposable public interface ISaveDataExtraDataAccessorObserver : IDisposable
{ {
void Unregister(SaveDataSpaceId spaceId, ulong saveDataId); void Unregister(SaveDataSpaceId spaceId, ulong saveDataId);
} }

View file

@ -0,0 +1,22 @@
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSystem;
// ReSharper disable once InconsistentNaming
public abstract class ISaveDataFileSystem : IFileSystem, ICacheableSaveDataFileSystem, ISaveDataExtraDataAccessor
{
public abstract bool IsSaveDataFileSystemCacheEnabled();
public abstract Result RollbackOnlyModified();
public abstract Result WriteExtraData(in SaveDataExtraData extraData);
public abstract Result CommitExtraData(bool updateTimeStamp);
public abstract Result ReadExtraData(out SaveDataExtraData extraData);
public abstract void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
}
public interface ICacheableSaveDataFileSystem
{
bool IsSaveDataFileSystemCacheEnabled();
Result RollbackOnlyModified();
}