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