diff --git a/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs index 1d3ce965..abaa6f6c 100644 --- a/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs +++ b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs @@ -10,13 +10,13 @@ namespace LibHac.FsSrv.Impl; /// /// Holds the s for opened save data file systems. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) -public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver +/// Based on FS 14.1.0 (nnSdk 14.3.0) +public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorObserver { /// /// Holds a single cached extra data accessor identified by its save data ID and save data space ID. /// - /// Based on FS 13.1.0 (nnSdk 13.4.0) + /// Based on FS 14.1.0 (nnSdk 14.3.0) [NonCopyable] private struct Cache : IDisposable { @@ -77,13 +77,14 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC public Result Register(in SharedRef accessor, SaveDataSpaceId spaceId, ulong saveDataId) { + accessor.Get.RegisterCacheObserver(this, spaceId, saveDataId); + var node = new LinkedListNode(new Cache(in accessor, spaceId, saveDataId)); - using (ScopedLock.Lock(ref _mutex)) - { - UnregisterImpl(spaceId, saveDataId); - _accessorList.AddLast(node); - } + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + UnregisterImpl(spaceId, saveDataId); + _accessorList.AddLast(node); return Result.Success; } @@ -134,7 +135,7 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC if (!accessor.HasValue) return ResultFs.TargetNotFound.Log(); - outAccessor.Reset(new SaveDataExtraDataResultConvertAccessor(ref accessor.Ref())); + outAccessor.SetByMove(ref accessor.Ref()); return Result.Success; } diff --git a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs new file mode 100644 index 00000000..f2bf5858 --- /dev/null +++ b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs @@ -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; + +/// +/// 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. +/// +/// Based on FS 14.1.0 (nnSdk 14.3.0) +public class SaveDataFileSystemCacheManager : IDisposable +{ + [NonCopyable] + private struct Cache + { + private SharedRef _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 Move() + { + return SharedRef.CreateMove(ref _fileSystem); + } + + public void Register(ref SharedRef 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 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 GetScopedLock() + { + return new UniqueLockRef(ref _mutex); + } + + public bool GetCache(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId) + { + Assert.SdkRequiresGreaterEqual(_maxCachedFileSystemCount, 0); + + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + + for (int i = 0; i < _maxCachedFileSystemCount; i++) + { + if (_cachedFileSystems[i].IsCached(spaceId, saveDataId)) + { + using SharedRef cachedFs = _cachedFileSystems[i].Move(); + outFileSystem.SetByMove(ref cachedFs.Ref()); + + return true; + } + } + + return false; + } + + public void Register(ref SharedRef 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 scopedLock = ScopedLock.Lock(ref _mutex); + fileSystem.Reset(); + } + else if (spaceId == SaveDataSpaceId.SdSystem) + { + using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); + fileSystem.Reset(); + } + else + { + Result rc = fileSystem.Get.RollbackOnlyModified(); + if (rc.IsSuccess()) + { + using ScopedLock 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 scopedLock = ScopedLock.Lock(ref _mutex); + + for (int i = 0; i < _maxCachedFileSystemCount; i++) + { + if (_cachedFileSystems[i].IsCached(spaceId, saveDataId)) + { + _cachedFileSystems[i].Unregister(); + } + } + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs new file mode 100644 index 00000000..c2166d00 --- /dev/null +++ b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs @@ -0,0 +1,117 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; + +namespace LibHac.FsSrv.Impl; + +/// +/// Wraps an . +/// Upon disposal the base file system is returned to the provided . +/// +/// Based on FS 14.1.0 (nnSdk 14.3.0) +public class SaveDataFileSystemCacheRegister : IFileSystem +{ + private SharedRef _baseFileSystem; + private SaveDataFileSystemCacheManager _cacheManager; + private SaveDataSpaceId _spaceId; + private ulong _saveDataId; + + public SaveDataFileSystemCacheRegister(ref SharedRef baseFileSystem, + SaveDataFileSystemCacheManager cacheManager, SaveDataSpaceId spaceId, ulong saveDataId) + { + _baseFileSystem = SharedRef.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 outFile, in Path path, OpenMode mode) + { + return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode); + } + + protected override Result DoOpenDirectory(ref UniqueRef 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); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs index d49eab27..e53dffc8 100644 --- a/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs +++ b/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs @@ -2,7 +2,6 @@ using LibHac.Diag; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.FsSystem; namespace LibHac.FsSrv.Impl; @@ -201,48 +200,4 @@ public class SaveDataResultConvertFileSystem : IResultConvertFileSystem { return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result); } -} - -/// -/// Wraps an , converting its returned s -/// to save-data-specific s. -/// -/// Based on FS 13.1.0 (nnSdk 13.4.0) -public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor -{ - private SharedRef _accessor; - - public SaveDataExtraDataResultConvertAccessor(ref SharedRef accessor) - { - _accessor = SharedRef.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); - } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index 3fd82d87..4b30b23d 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -20,7 +20,7 @@ public class SaveDataFileSystemServiceImpl private Configuration _config; private EncryptionSeed _encryptionSeed; - private SaveDataFileSystemCacheManager _saveDataFsCacheManager; + private FsSystem.SaveDataFileSystemCacheManager _saveDataFsCacheManager; private SaveDataExtraDataAccessorCacheManager _extraDataCacheManager; // Save data porter manager private bool _isSdCardAccessible; @@ -47,7 +47,7 @@ public class SaveDataFileSystemServiceImpl public SaveDataFileSystemServiceImpl(in Configuration configuration) { _config = configuration; - _saveDataFsCacheManager = new SaveDataFileSystemCacheManager(); + _saveDataFsCacheManager = new FsSystem.SaveDataFileSystemCacheManager(); _extraDataCacheManager = new SaveDataExtraDataAccessorCacheManager(); _timeStampGetter = new TimeStampGetter(this); diff --git a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs index c96ea630..7c5e722a 100644 --- a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs +++ b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs @@ -83,7 +83,7 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ISaveDataExtraDataAcc throw new NotImplementedException(); } - public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId) + public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId) { throw new NotImplementedException(); } diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index 7e610c01..c56ed65c 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -50,7 +50,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess private RandomDataGenerator _randomGenerator; // Additions to support caching - private ISaveDataExtraDataAccessorCacheObserver _cacheObserver; + private ISaveDataExtraDataAccessorObserver _cacheObserver; private SaveDataSpaceId _spaceId; private ulong _saveDataId; @@ -1056,7 +1056,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess return Result.Success; } - public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, + public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId) { _cacheObserver = observer; diff --git a/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs b/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs index 3fdc1341..4ee8ccc3 100644 --- a/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs +++ b/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs @@ -12,5 +12,5 @@ public interface ISaveDataExtraDataAccessor : IDisposable Result WriteExtraData(in SaveDataExtraData extraData); Result CommitExtraData(bool updateTimeStamp); Result ReadExtraData(out SaveDataExtraData extraData); - void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId); + void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId); } \ No newline at end of file diff --git a/src/LibHac/FsSystem/ISaveDataExtraDataAccessorCacheObserver.cs b/src/LibHac/FsSystem/ISaveDataExtraDataAccessorCacheObserver.cs index cb3c74d7..782d4b96 100644 --- a/src/LibHac/FsSystem/ISaveDataExtraDataAccessorCacheObserver.cs +++ b/src/LibHac/FsSystem/ISaveDataExtraDataAccessorCacheObserver.cs @@ -9,8 +9,8 @@ namespace LibHac.FsSystem; /// . 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. /// -/// Based on FS 13.1.0 (nnSdk 13.4.0) -public interface ISaveDataExtraDataAccessorCacheObserver : IDisposable +/// Based on FS 14.1.0 (nnSdk 14.3.0) +public interface ISaveDataExtraDataAccessorObserver : IDisposable { void Unregister(SaveDataSpaceId spaceId, ulong saveDataId); } \ No newline at end of file diff --git a/src/LibHac/FsSystem/ISaveDataFileSystem.cs b/src/LibHac/FsSystem/ISaveDataFileSystem.cs new file mode 100644 index 00000000..61fdb234 --- /dev/null +++ b/src/LibHac/FsSystem/ISaveDataFileSystem.cs @@ -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(); +} \ No newline at end of file