Ensure more FS classes are updated for 13.1.0

- FsSrv.Impl.IResultConvertDirectory
- FsSrv.Impl.IResultConvertFile
- FsSrv.Impl.IResultConvertFileSystem
- FsSrv.Impl.SaveDataExtraDataAccessorCacheManager
- FsSrv.Impl.SaveDataExtraDataResultConvertAccessor
- FsSrv.Impl.SaveDataResultConvert
- FsSrv.Impl.SaveDataResultConvertDirectory
- FsSrv.Impl.SaveDataResultConvertFile
- FsSrv.Impl.SaveDataResultConvertFileSystem
- FsSrv.Impl.StorageInterfaceAdapter
- FsSystem.ISaveDataCommitTimeStampGetter
- FsSystem.ISaveDataExtraDataAccessor
- FsSystem.ISaveDataFileSystemCacheManager
- FsSystem.SaveDataFileSystemCacheManager
- FsSystem.SaveDataFileSystemCacheRegisterBase
This commit is contained in:
Alex Barney 2022-01-27 15:05:58 -07:00
parent 32ffda1d3f
commit e969554639
11 changed files with 132 additions and 71 deletions

View file

@ -6,6 +6,11 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
/// <summary>
/// Wraps an <see cref="IFile"/>, converting its returned <see cref="Result"/>s to different
/// <see cref="Result"/>s based on the <see cref="ConvertResult"/> function.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public abstract class IResultConvertFile : IFile public abstract class IResultConvertFile : IFile
{ {
protected UniqueRef<IFile> BaseFile; protected UniqueRef<IFile> BaseFile;
@ -56,6 +61,11 @@ public abstract class IResultConvertFile : IFile
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
/// <summary>
/// Wraps an <see cref="IDirectory"/>, converting its returned <see cref="Result"/>s to different
/// <see cref="Result"/>s based on the <see cref="ConvertResult"/> function.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public abstract class IResultConvertDirectory : IDirectory public abstract class IResultConvertDirectory : IDirectory
{ {
protected UniqueRef<IDirectory> BaseDirectory; protected UniqueRef<IDirectory> BaseDirectory;
@ -85,6 +95,11 @@ public abstract class IResultConvertDirectory : IDirectory
} }
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
/// <summary>
/// Wraps an <see cref="IFileSystem"/>, converting its returned <see cref="Result"/>s to different
/// <see cref="Result"/>s based on the <see cref="ConvertResult"/> function.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public abstract class IResultConvertFileSystem : IFileSystem public abstract class IResultConvertFileSystem : IFileSystem
{ {
protected SharedRef<IFileSystem> BaseFileSystem; protected SharedRef<IFileSystem> BaseFileSystem;
@ -145,6 +160,8 @@ public abstract class IResultConvertFileSystem : IFileSystem
return ConvertResult(BaseFileSystem.Get.GetEntryType(out entryType, path)); return ConvertResult(BaseFileSystem.Get.GetEntryType(out entryType, path));
} }
// Note: The original code uses templates to determine which type of IFile/IDirectory to return. To make things
// easier in C# these two functions have been made abstract functions.
protected abstract override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode); protected abstract override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode);
protected abstract override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path, protected abstract override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,

View file

@ -10,8 +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>
public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorCacheObserver
{ {
/// <summary>
/// Holds a single cached extra data accessor identified by its save data ID and save data space ID.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
[NonCopyable] [NonCopyable]
private struct Cache : IDisposable private struct Cache : IDisposable
{ {
@ -19,8 +24,7 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC
private readonly SaveDataSpaceId _spaceId; private readonly SaveDataSpaceId _spaceId;
private readonly ulong _saveDataId; private readonly ulong _saveDataId;
public Cache(in SharedRef<ISaveDataExtraDataAccessor> accessor, SaveDataSpaceId spaceId, public Cache(in SharedRef<ISaveDataExtraDataAccessor> accessor, SaveDataSpaceId spaceId, ulong saveDataId)
ulong saveDataId)
{ {
_accessor = new WeakRef<ISaveDataExtraDataAccessor>(in accessor); _accessor = new WeakRef<ISaveDataExtraDataAccessor>(in accessor);
_spaceId = spaceId; _spaceId = spaceId;
@ -49,7 +53,7 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorC
public SaveDataExtraDataAccessorCacheManager() public SaveDataExtraDataAccessorCacheManager()
{ {
_accessorList = new LinkedList<Cache>(); _accessorList = new LinkedList<Cache>();
_mutex.Initialize(); _mutex = new SdkRecursiveMutexType();
} }
public void Dispose() public void Dispose()

View file

@ -6,6 +6,10 @@ using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
/// <summary>
/// Contains functions for converting internal save data <see cref="Result"/>s to external <see cref="Result"/>s.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public static class SaveDataResultConvert public static class SaveDataResultConvert
{ {
private static Result ConvertCorruptedResult(Result result) private static Result ConvertCorruptedResult(Result result)
@ -13,19 +17,19 @@ public static class SaveDataResultConvert
if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result)) if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result))
{ {
if (ResultFs.IncorrectIntegrityVerificationMagicCode.Includes(result)) if (ResultFs.IncorrectIntegrityVerificationMagicCode.Includes(result))
return ResultFs.IncorrectSaveDataIntegrityVerificationMagicCode.Value; return ResultFs.IncorrectSaveDataIntegrityVerificationMagicCode.LogConverted(result);
if (ResultFs.InvalidZeroHash.Includes(result)) if (ResultFs.InvalidZeroHash.Includes(result))
return ResultFs.InvalidSaveDataZeroHash.Value; return ResultFs.InvalidSaveDataZeroHash.LogConverted(result);
if (ResultFs.NonRealDataVerificationFailed.Includes(result)) if (ResultFs.NonRealDataVerificationFailed.Includes(result))
return ResultFs.SaveDataNonRealDataVerificationFailed.Value; return ResultFs.SaveDataNonRealDataVerificationFailed.LogConverted(result);
if (ResultFs.ClearedRealDataVerificationFailed.Includes(result)) if (ResultFs.ClearedRealDataVerificationFailed.Includes(result))
return ResultFs.ClearedSaveDataRealDataVerificationFailed.Value; return ResultFs.ClearedSaveDataRealDataVerificationFailed.LogConverted(result);
if (ResultFs.UnclearedRealDataVerificationFailed.Includes(result)) if (ResultFs.UnclearedRealDataVerificationFailed.Includes(result))
return ResultFs.UnclearedSaveDataRealDataVerificationFailed.Value; return ResultFs.UnclearedSaveDataRealDataVerificationFailed.LogConverted(result);
Assert.SdkAssert(false); Assert.SdkAssert(false);
} }
@ -33,16 +37,16 @@ public static class SaveDataResultConvert
if (ResultFs.HostFileSystemCorrupted.Includes(result)) if (ResultFs.HostFileSystemCorrupted.Includes(result))
{ {
if (ResultFs.HostEntryCorrupted.Includes(result)) if (ResultFs.HostEntryCorrupted.Includes(result))
return ResultFs.SaveDataHostEntryCorrupted.Value; return ResultFs.SaveDataHostEntryCorrupted.LogConverted(result);
if (ResultFs.HostFileDataCorrupted.Includes(result)) if (ResultFs.HostFileDataCorrupted.Includes(result))
return ResultFs.SaveDataHostFileDataCorrupted.Value; return ResultFs.SaveDataHostFileDataCorrupted.LogConverted(result);
if (ResultFs.HostFileCorrupted.Includes(result)) if (ResultFs.HostFileCorrupted.Includes(result))
return ResultFs.SaveDataHostFileCorrupted.Value; return ResultFs.SaveDataHostFileCorrupted.LogConverted(result);
if (ResultFs.InvalidHostHandle.Includes(result)) if (ResultFs.InvalidHostHandle.Includes(result))
return ResultFs.InvalidSaveDataHostHandle.Value; return ResultFs.InvalidSaveDataHostHandle.LogConverted(result);
Assert.SdkAssert(false); Assert.SdkAssert(false);
} }
@ -50,25 +54,25 @@ public static class SaveDataResultConvert
if (ResultFs.DatabaseCorrupted.Includes(result)) if (ResultFs.DatabaseCorrupted.Includes(result))
{ {
if (ResultFs.InvalidAllocationTableBlock.Includes(result)) if (ResultFs.InvalidAllocationTableBlock.Includes(result))
return ResultFs.InvalidSaveDataAllocationTableBlock.Value; return ResultFs.InvalidSaveDataAllocationTableBlock.LogConverted(result);
if (ResultFs.InvalidKeyValueListElementIndex.Includes(result)) if (ResultFs.InvalidKeyValueListElementIndex.Includes(result))
return ResultFs.InvalidSaveDataKeyValueListElementIndex.Value; return ResultFs.InvalidSaveDataKeyValueListElementIndex.LogConverted(result);
if (ResultFs.InvalidAllocationTableChainEntry.Includes(result)) if (ResultFs.InvalidAllocationTableChainEntry.Includes(result))
return ResultFs.InvalidSaveDataAllocationTableChainEntry.Value; return ResultFs.InvalidSaveDataAllocationTableChainEntry.LogConverted(result);
if (ResultFs.InvalidAllocationTableOffset.Includes(result)) if (ResultFs.InvalidAllocationTableOffset.Includes(result))
return ResultFs.InvalidSaveDataAllocationTableOffset.Value; return ResultFs.InvalidSaveDataAllocationTableOffset.LogConverted(result);
if (ResultFs.InvalidAllocationTableBlockCount.Includes(result)) if (ResultFs.InvalidAllocationTableBlockCount.Includes(result))
return ResultFs.InvalidSaveDataAllocationTableBlockCount.Value; return ResultFs.InvalidSaveDataAllocationTableBlockCount.LogConverted(result);
if (ResultFs.InvalidKeyValueListEntryIndex.Includes(result)) if (ResultFs.InvalidKeyValueListEntryIndex.Includes(result))
return ResultFs.InvalidSaveDataKeyValueListEntryIndex.Value; return ResultFs.InvalidSaveDataKeyValueListEntryIndex.LogConverted(result);
if (ResultFs.InvalidBitmapIndex.Includes(result)) if (ResultFs.InvalidBitmapIndex.Includes(result))
return ResultFs.InvalidSaveDataBitmapIndex.Value; return ResultFs.InvalidSaveDataBitmapIndex.LogConverted(result);
Assert.SdkAssert(false); Assert.SdkAssert(false);
} }
@ -76,7 +80,7 @@ public static class SaveDataResultConvert
if (ResultFs.ZeroBitmapFileCorrupted.Includes(result)) if (ResultFs.ZeroBitmapFileCorrupted.Includes(result))
{ {
if (ResultFs.IncompleteBlockInZeroBitmapHashStorageFile.Includes(result)) if (ResultFs.IncompleteBlockInZeroBitmapHashStorageFile.Includes(result))
return ResultFs.IncompleteBlockInZeroBitmapHashStorageFileSaveData.Value; return ResultFs.IncompleteBlockInZeroBitmapHashStorageFileSaveData.LogConverted(result);
Assert.SdkAssert(false); Assert.SdkAssert(false);
} }
@ -84,13 +88,13 @@ public static class SaveDataResultConvert
return result; return result;
} }
public static Result ConvertSaveFsDriverPublicResult(Result result) public static Result ConvertSaveFsDriverPrivateResult(Result result)
{ {
if (result.IsSuccess()) if (result.IsSuccess())
return result; return result;
if (ResultFs.UnsupportedVersion.Includes(result)) if (ResultFs.UnsupportedVersion.Includes(result))
return ResultFs.UnsupportedSaveDataVersion.Value; return ResultFs.UnsupportedSaveDataVersion.LogConverted(result);
if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result) || if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result) ||
ResultFs.BuiltInStorageCorrupted.Includes(result) || ResultFs.BuiltInStorageCorrupted.Includes(result) ||
@ -105,21 +109,21 @@ public static class SaveDataResultConvert
return result; return result;
if (ResultFs.NotFound.Includes(result)) if (ResultFs.NotFound.Includes(result))
return ResultFs.PathNotFound.Value; return ResultFs.PathNotFound.LogConverted(result);
if (ResultFs.AllocationTableFull.Includes(result)) if (ResultFs.AllocationTableFull.Includes(result))
return ResultFs.UsableSpaceNotEnough.Value; return ResultFs.UsableSpaceNotEnough.LogConverted(result);
if (ResultFs.AlreadyExists.Includes(result)) if (ResultFs.AlreadyExists.Includes(result))
return ResultFs.PathAlreadyExists.Value; return ResultFs.PathAlreadyExists.LogConverted(result);
if (ResultFs.InvalidOffset.Includes(result)) if (ResultFs.InvalidOffset.Includes(result))
return ResultFs.OutOfRange.Value; return ResultFs.OutOfRange.LogConverted(result);
if (ResultFs.IncompatiblePath.Includes(result) || if (ResultFs.IncompatiblePath.Includes(result) ||
ResultFs.FileNotFound.Includes(result)) ResultFs.FileNotFound.Includes(result))
{ {
return ResultFs.PathNotFound.Value; return ResultFs.PathNotFound.LogConverted(result);
} }
return result; return result;
@ -130,6 +134,7 @@ public static class SaveDataResultConvert
/// Wraps an <see cref="IFile"/>, converting its returned <see cref="Result"/>s /// Wraps an <see cref="IFile"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s. /// to save-data-specific <see cref="Result"/>s.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataResultConvertFile : IResultConvertFile public class SaveDataResultConvertFile : IResultConvertFile
{ {
public SaveDataResultConvertFile(ref UniqueRef<IFile> baseFile) : base(ref baseFile) public SaveDataResultConvertFile(ref UniqueRef<IFile> baseFile) : base(ref baseFile)
@ -138,7 +143,7 @@ public class SaveDataResultConvertFile : IResultConvertFile
protected override Result ConvertResult(Result result) protected override Result ConvertResult(Result result)
{ {
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(result); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result);
} }
} }
@ -146,6 +151,7 @@ public class SaveDataResultConvertFile : IResultConvertFile
/// Wraps an <see cref="IDirectory"/>, converting its returned <see cref="Result"/>s /// Wraps an <see cref="IDirectory"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s. /// to save-data-specific <see cref="Result"/>s.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataResultConvertDirectory : IResultConvertDirectory public class SaveDataResultConvertDirectory : IResultConvertDirectory
{ {
public SaveDataResultConvertDirectory(ref UniqueRef<IDirectory> baseDirectory) : base(ref baseDirectory) public SaveDataResultConvertDirectory(ref UniqueRef<IDirectory> baseDirectory) : base(ref baseDirectory)
@ -154,7 +160,7 @@ public class SaveDataResultConvertDirectory : IResultConvertDirectory
protected override Result ConvertResult(Result result) protected override Result ConvertResult(Result result)
{ {
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(result); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result);
} }
} }
@ -162,6 +168,7 @@ public class SaveDataResultConvertDirectory : IResultConvertDirectory
/// Wraps an <see cref="IFileSystem"/>, converting its returned <see cref="Result"/>s /// Wraps an <see cref="IFileSystem"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s. /// to save-data-specific <see cref="Result"/>s.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataResultConvertFileSystem : IResultConvertFileSystem public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
{ {
public SaveDataResultConvertFileSystem(ref SharedRef<IFileSystem> baseFileSystem) public SaveDataResultConvertFileSystem(ref SharedRef<IFileSystem> baseFileSystem)
@ -192,7 +199,7 @@ public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
protected override Result ConvertResult(Result result) protected override Result ConvertResult(Result result)
{ {
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(result); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(result);
} }
} }
@ -200,6 +207,7 @@ public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
/// Wraps an <see cref="ISaveDataExtraDataAccessor"/>, converting its returned <see cref="Result"/>s /// Wraps an <see cref="ISaveDataExtraDataAccessor"/>, converting its returned <see cref="Result"/>s
/// to save-data-specific <see cref="Result"/>s. /// to save-data-specific <see cref="Result"/>s.
/// </summary> /// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor
{ {
private SharedRef<ISaveDataExtraDataAccessor> _accessor; private SharedRef<ISaveDataExtraDataAccessor> _accessor;
@ -217,19 +225,19 @@ public class SaveDataExtraDataResultConvertAccessor : ISaveDataExtraDataAccessor
public Result WriteExtraData(in SaveDataExtraData extraData) public Result WriteExtraData(in SaveDataExtraData extraData)
{ {
Result rc = _accessor.Get.WriteExtraData(in extraData); Result rc = _accessor.Get.WriteExtraData(in extraData);
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
} }
public Result CommitExtraData(bool updateTimeStamp) public Result CommitExtraData(bool updateTimeStamp)
{ {
Result rc = _accessor.Get.CommitExtraData(updateTimeStamp); Result rc = _accessor.Get.CommitExtraData(updateTimeStamp);
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
} }
public Result ReadExtraData(out SaveDataExtraData extraData) public Result ReadExtraData(out SaveDataExtraData extraData)
{ {
Result rc = _accessor.Get.ReadExtraData(out extraData); Result rc = _accessor.Get.ReadExtraData(out extraData);
return SaveDataResultConvert.ConvertSaveFsDriverPublicResult(rc); return SaveDataResultConvert.ConvertSaveFsDriverPrivateResult(rc);
} }
public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId,

View file

@ -8,6 +8,10 @@ using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.FsSrv.Impl; namespace LibHac.FsSrv.Impl;
/// <summary>
/// Wraps an <see cref="IStorage"/> to allow interfacing with it via the <see cref="IStorageSf"/> interface over IPC.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class StorageInterfaceAdapter : IStorageSf public class StorageInterfaceAdapter : IStorageSf
{ {
private SharedRef<IStorage> _baseStorage; private SharedRef<IStorage> _baseStorage;
@ -32,6 +36,9 @@ public class StorageInterfaceAdapter : IStorageSf
if (destination.Size < 0) if (destination.Size < 0)
return ResultFs.InvalidSize.Log(); return ResultFs.InvalidSize.Log();
if (destination.Size < size)
return ResultFs.InvalidSize.Log();
Result rc = Result.Success; Result rc = Result.Success;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
@ -54,6 +61,9 @@ public class StorageInterfaceAdapter : IStorageSf
if (source.Size < 0) if (source.Size < 0)
return ResultFs.InvalidSize.Log(); return ResultFs.InvalidSize.Log();
if (source.Size < size)
return ResultFs.InvalidSize.Log();
using var scopedPriorityChanger = new ScopedThreadPriorityChangerByAccessPriority( using var scopedPriorityChanger = new ScopedThreadPriorityChangerByAccessPriority(
ScopedThreadPriorityChangerByAccessPriority.AccessMode.Write); ScopedThreadPriorityChangerByAccessPriority.AccessMode.Write);

View file

@ -1,5 +1,9 @@
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// Gets Unix timestamps used to update a save data's commit time.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public interface ISaveDataCommitTimeStampGetter public interface ISaveDataCommitTimeStampGetter
{ {
Result Get(out long timeStamp); Result Get(out long timeStamp);

View file

@ -3,6 +3,10 @@ using LibHac.Fs;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// Provides read/write access to a save data file system's extra data.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public interface ISaveDataExtraDataAccessor : IDisposable public interface ISaveDataExtraDataAccessor : IDisposable
{ {
Result WriteExtraData(in SaveDataExtraData extraData); Result WriteExtraData(in SaveDataExtraData extraData);

View file

@ -1,8 +1,15 @@
using System; using System;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSrv.Impl;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// Used when adding an <see cref="ISaveDataExtraDataAccessor"/> to the
/// <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.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public interface ISaveDataExtraDataAccessorCacheObserver : IDisposable public interface ISaveDataExtraDataAccessorCacheObserver : IDisposable
{ {
void Unregister(SaveDataSpaceId spaceId, ulong saveDataId); void Unregister(SaveDataSpaceId spaceId, ulong saveDataId);

View file

@ -4,6 +4,10 @@ using LibHac.Fs;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// Provides a mechanism for caching save data file systems.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public interface ISaveDataFileSystemCacheManager : IDisposable public interface ISaveDataFileSystemCacheManager : IDisposable
{ {
bool GetCache(ref SharedRef<SaveDataFileSystemHolder> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId); bool GetCache(ref SharedRef<SaveDataFileSystemHolder> outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId);

View file

@ -5,8 +5,17 @@ using LibHac.Os;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <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 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
{ {
/// <summary>
/// Holds a single cached file system identified by its save data ID and save data space ID.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
[NonCopyable] [NonCopyable]
private struct Cache private struct Cache
{ {
@ -26,9 +35,9 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
return _fileSystem.HasValue && _spaceId == spaceId && _saveDataId == saveDataId; return _fileSystem.HasValue && _spaceId == spaceId && _saveDataId == saveDataId;
} }
public void Move(ref SharedRef<SaveDataFileSystemHolder> outFileSystem) public SharedRef<SaveDataFileSystemHolder> Move()
{ {
outFileSystem.SetByMove(ref _fileSystem); return SharedRef<SaveDataFileSystemHolder>.CreateMove(ref _fileSystem);
} }
public void Register(ref SharedRef<SaveDataFileSystemHolder> fileSystem) public void Register(ref SharedRef<SaveDataFileSystemHolder> fileSystem)
@ -52,7 +61,7 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
public SaveDataFileSystemCacheManager() public SaveDataFileSystemCacheManager()
{ {
_mutex.Initialize(); _mutex = new SdkRecursiveMutexType();
} }
public void Dispose() public void Dispose()
@ -77,11 +86,12 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
Assert.SdkAssert(_cachedFileSystems is null); Assert.SdkAssert(_cachedFileSystems is null);
_maxCachedFileSystemCount = maxCacheCount; _maxCachedFileSystemCount = maxCacheCount;
if (maxCacheCount <= 0) if (maxCacheCount > 0)
return Result.Success; {
// Note: The original checks for overflow here // Note: The original checks for overflow here
_cachedFileSystems = new Cache[maxCacheCount]; _cachedFileSystems = new Cache[maxCacheCount];
}
return Result.Success; return Result.Success;
} }
@ -96,7 +106,9 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
{ {
if (_cachedFileSystems[i].IsCached(spaceId, saveDataId)) if (_cachedFileSystems[i].IsCached(spaceId, saveDataId))
{ {
_cachedFileSystems[i].Move(ref outFileSystem); using SharedRef<SaveDataFileSystemHolder> cachedFs = _cachedFileSystems[i].Move();
outFileSystem.SetByMove(ref cachedFs.Ref());
return true; return true;
} }
} }

View file

@ -1,5 +1,5 @@
using System; using System;
using InlineIL; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
@ -14,6 +14,7 @@ namespace LibHac.FsSystem;
/// </summary> /// </summary>
/// <typeparam name="T">The type of the base file system. Must be one of <see cref="SaveDataFileSystem"/>, /// <typeparam name="T">The type of the base file system. Must be one of <see cref="SaveDataFileSystem"/>,
/// <see cref="ApplicationTemporaryFileSystem"/> or <see cref="DirectorySaveDataFileSystem"/>.</typeparam> /// <see cref="ApplicationTemporaryFileSystem"/> or <see cref="DirectorySaveDataFileSystem"/>.</typeparam>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
public class SaveDataFileSystemCacheRegisterBase<T> : IFileSystem where T : IFileSystem public class SaveDataFileSystemCacheRegisterBase<T> : IFileSystem where T : IFileSystem
{ {
private SharedRef<T> _baseFileSystem; private SharedRef<T> _baseFileSystem;
@ -36,11 +37,11 @@ public class SaveDataFileSystemCacheRegisterBase<T> : IFileSystem where T : IFil
{ {
if (typeof(T) == typeof(SaveDataFileSystemHolder)) if (typeof(T) == typeof(SaveDataFileSystemHolder))
{ {
_cacheManager.Register(ref GetBaseFileSystemNormal()); _cacheManager.Register(ref Unsafe.As<SharedRef<T>, SharedRef<SaveDataFileSystemHolder>>(ref _baseFileSystem));
} }
else if (typeof(T) == typeof(ApplicationTemporaryFileSystem)) else if (typeof(T) == typeof(ApplicationTemporaryFileSystem))
{ {
_cacheManager.Register(ref GetBaseFileSystemTemp()); _cacheManager.Register(ref Unsafe.As<SharedRef<T>, SharedRef<ApplicationTemporaryFileSystem>>(ref _baseFileSystem));
} }
else else
{ {
@ -48,23 +49,6 @@ public class SaveDataFileSystemCacheRegisterBase<T> : IFileSystem where T : IFil
} }
} }
// Hack around not being able to use Unsafe.As on ref structs
private ref SharedRef<SaveDataFileSystemHolder> GetBaseFileSystemNormal()
{
IL.Emit.Ldarg_0();
IL.Emit.Ldflda(new FieldRef(typeof(SaveDataFileSystemCacheRegisterBase<T>), nameof(_baseFileSystem)));
IL.Emit.Ret();
throw IL.Unreachable();
}
private ref SharedRef<ApplicationTemporaryFileSystem> GetBaseFileSystemTemp()
{
IL.Emit.Ldarg_0();
IL.Emit.Ldflda(new FieldRef(typeof(SaveDataFileSystemCacheRegisterBase<T>), nameof(_baseFileSystem)));
IL.Emit.Ret();
throw IL.Unreachable();
}
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
return _baseFileSystem.Get.OpenFile(ref outFile, path, mode); return _baseFileSystem.Get.OpenFile(ref outFile, path, mode);

View file

@ -6,6 +6,13 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
/// <summary>
/// Holds a file system for adding to the save data file system cache.
/// </summary>
/// <remarks> Nintendo uses concrete types in <see cref="ISaveDataFileSystemCacheManager"/> instead of an interface.
/// This class allows <see cref="DirectorySaveDataFileSystem"/> to be cached in a way that changes the original
/// design as little as possible.
/// </remarks>
public class SaveDataFileSystemHolder : ForwardingFileSystem public class SaveDataFileSystemHolder : ForwardingFileSystem
{ {
public SaveDataFileSystemHolder(ref SharedRef<IFileSystem> baseFileSystem) : base(ref baseFileSystem) public SaveDataFileSystemHolder(ref SharedRef<IFileSystem> baseFileSystem) : base(ref baseFileSystem)