mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Update DirectorySaveDataFileSystem to implement ISaveDataFileSystem
This commit is contained in:
parent
bc7fea5714
commit
7dfcebfc28
14 changed files with 188 additions and 196 deletions
|
@ -2,6 +2,7 @@
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.FsSrv.FsCreator;
|
using LibHac.FsSrv.FsCreator;
|
||||||
using LibHac.FsSrv.Sf;
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
namespace LibHac.FsSrv;
|
||||||
|
@ -14,7 +15,7 @@ public class DefaultFsServerObjects
|
||||||
public EmulatedSdCard SdCard { get; set; }
|
public EmulatedSdCard SdCard { get; set; }
|
||||||
|
|
||||||
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
|
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
|
||||||
FileSystemServer fsServer)
|
FileSystemServer fsServer, RandomDataGenerator randomGenerator)
|
||||||
{
|
{
|
||||||
var creators = new FileSystemCreatorInterfaces();
|
var creators = new FileSystemCreatorInterfaces();
|
||||||
var gameCard = new EmulatedGameCard(keySet);
|
var gameCard = new EmulatedGameCard(keySet);
|
||||||
|
@ -31,7 +32,7 @@ public class DefaultFsServerObjects
|
||||||
creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet);
|
creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet);
|
||||||
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();
|
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();
|
||||||
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
|
||||||
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, keySet, null, null);
|
creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, keySet, null, randomGenerator);
|
||||||
creators.GameCardStorageCreator = gcStorageCreator;
|
creators.GameCardStorageCreator = gcStorageCreator;
|
||||||
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
||||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
|
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
|
||||||
|
|
|
@ -63,13 +63,6 @@ public static class FileSystemServerInitializer
|
||||||
private static FileSystemProxyConfiguration InitializeFileSystemProxy(FileSystemServer server,
|
private static FileSystemProxyConfiguration InitializeFileSystemProxy(FileSystemServer server,
|
||||||
FileSystemServerConfig config)
|
FileSystemServerConfig config)
|
||||||
{
|
{
|
||||||
var random = new Random();
|
|
||||||
RandomDataGenerator randomGenerator = buffer =>
|
|
||||||
{
|
|
||||||
random.NextBytes(buffer);
|
|
||||||
return Result.Success;
|
|
||||||
};
|
|
||||||
|
|
||||||
var bufferManager = new FileSystemBufferManager();
|
var bufferManager = new FileSystemBufferManager();
|
||||||
Memory<byte> heapBuffer = GC.AllocateArray<byte>(BufferManagerHeapSize, true);
|
Memory<byte> heapBuffer = GC.AllocateArray<byte>(BufferManagerHeapSize, true);
|
||||||
bufferManager.Initialize(BufferManagerCacheSize, heapBuffer, BufferManagerBlockSize);
|
bufferManager.Initialize(BufferManagerCacheSize, heapBuffer, BufferManagerBlockSize);
|
||||||
|
@ -141,7 +134,7 @@ public static class FileSystemServerInitializer
|
||||||
saveFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
|
saveFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
|
||||||
saveFsServiceConfig.ProgramRegistryService = programRegistryService;
|
saveFsServiceConfig.ProgramRegistryService = programRegistryService;
|
||||||
saveFsServiceConfig.BufferManager = bufferManager;
|
saveFsServiceConfig.BufferManager = bufferManager;
|
||||||
saveFsServiceConfig.GenerateRandomData = randomGenerator;
|
saveFsServiceConfig.GenerateRandomData = config.RandomGenerator;
|
||||||
saveFsServiceConfig.IsPseudoSaveData = () => true;
|
saveFsServiceConfig.IsPseudoSaveData = () => true;
|
||||||
saveFsServiceConfig.MaxSaveFsCacheCount = 1;
|
saveFsServiceConfig.MaxSaveFsCacheCount = 1;
|
||||||
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
|
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
|
||||||
|
@ -278,4 +271,9 @@ public class FileSystemServerConfig
|
||||||
/// If null, an empty set will be created.
|
/// If null, an empty set will be created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExternalKeySet ExternalKeySet { get; set; }
|
public ExternalKeySet ExternalKeySet { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for generating random data for save data.
|
||||||
|
/// </summary>
|
||||||
|
public RandomDataGenerator RandomGenerator { get; set; }
|
||||||
}
|
}
|
|
@ -78,8 +78,8 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
|
||||||
using var saveDirFs = new SharedRef<DirectorySaveDataFileSystem>(
|
using var saveDirFs = new SharedRef<DirectorySaveDataFileSystem>(
|
||||||
new DirectorySaveDataFileSystem(ref tempFs.Ref(), _fsServer.Hos.Fs));
|
new DirectorySaveDataFileSystem(ref tempFs.Ref(), _fsServer.Hos.Fs));
|
||||||
|
|
||||||
rc = saveDirFs.Get.Initialize(timeStampGetter, _randomGenerator, isJournalingSupported,
|
rc = saveDirFs.Get.Initialize(isJournalingSupported, isMultiCommitSupported, !openReadOnly,
|
||||||
isMultiCommitSupported, !openReadOnly);
|
timeStampGetter, _randomGenerator);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
outFileSystem.SetByCopy(in saveDirFs);
|
outFileSystem.SetByCopy(in saveDirFs);
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorO
|
||||||
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);
|
accessor.Get.RegisterExtraDataAccessorObserver(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));
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class SaveDataFileSystemServiceImpl
|
||||||
// Cache the extra data accessor if needed
|
// Cache the extra data accessor if needed
|
||||||
if (cacheExtraData && extraDataAccessor.HasValue)
|
if (cacheExtraData && extraDataAccessor.HasValue)
|
||||||
{
|
{
|
||||||
extraDataAccessor.Get.RegisterCacheObserver(_extraDataCacheManager, spaceId, saveDataId);
|
extraDataAccessor.Get.RegisterExtraDataAccessorObserver(_extraDataCacheManager, spaceId, saveDataId);
|
||||||
|
|
||||||
rc = _extraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
|
rc = _extraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
|
||||||
if (rc.IsFailure()) return rc.Miss();
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
@ -359,7 +359,7 @@ public class SaveDataFileSystemServiceImpl
|
||||||
extraData.TimeStamp = 0;
|
extraData.TimeStamp = 0;
|
||||||
|
|
||||||
extraData.CommitId = 0;
|
extraData.CommitId = 0;
|
||||||
_config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId)).IgnoreResult();
|
_config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId));
|
||||||
|
|
||||||
extraData.Flags = creationInfo.Flags;
|
extraData.Flags = creationInfo.Flags;
|
||||||
extraData.DataSize = creationInfo.Size;
|
extraData.DataSize = creationInfo.Size;
|
||||||
|
|
|
@ -83,7 +83,8 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ISaveDataExtraDataAcc
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId)
|
public void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId,
|
||||||
|
ulong saveDataId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
namespace LibHac.FsSystem;
|
||||||
|
|
||||||
public delegate Result RandomDataGenerator(Span<byte> buffer);
|
public delegate void RandomDataGenerator(Span<byte> buffer);
|
|
@ -14,7 +14,7 @@ internal struct DirectorySaveDataFileSystemGlobals
|
||||||
|
|
||||||
public void Initialize(FileSystemClient fsClient)
|
public void Initialize(FileSystemClient fsClient)
|
||||||
{
|
{
|
||||||
SynchronizeDirectoryMutex.Initialize();
|
SynchronizeDirectoryMutex = new SdkMutexType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ internal struct DirectorySaveDataFileSystemGlobals
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Transactional commits should be atomic as long as the <see cref="IFileSystem.RenameDirectory"/> function of the
|
/// Transactional commits should be atomic as long as the <see cref="IFileSystem.RenameDirectory"/> function of the
|
||||||
/// underlying <see cref="IFileSystem"/> is atomic.
|
/// underlying <see cref="IFileSystem"/> is atomic.
|
||||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para>
|
/// <para>Based on FS 14.1.0 (nnSdk 14.3.0)</para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccessor
|
public class DirectorySaveDataFileSystem : ISaveDataFileSystem
|
||||||
{
|
{
|
||||||
private const int IdealWorkBufferSize = 0x100000; // 1 MiB
|
private const int IdealWorkBufferSize = 0x100000; // 1 MiB
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
private static ReadOnlySpan<byte> SynchronizingDirectoryName => new[] { (byte)'/', (byte)'_' };
|
private static ReadOnlySpan<byte> SynchronizingDirectoryName => new[] { (byte)'/', (byte)'_' };
|
||||||
private static ReadOnlySpan<byte> LockFileName => new[] { (byte)'/', (byte)'.', (byte)'l', (byte)'o', (byte)'c', (byte)'k' };
|
private static ReadOnlySpan<byte> LockFileName => new[] { (byte)'/', (byte)'.', (byte)'l', (byte)'o', (byte)'c', (byte)'k' };
|
||||||
|
|
||||||
private FileSystemClient _fsClient;
|
|
||||||
private IFileSystem _baseFs;
|
private IFileSystem _baseFs;
|
||||||
private SdkMutexType _mutex;
|
private SdkMutexType _mutex;
|
||||||
private UniqueRef<IFileSystem> _uniqueBaseFs;
|
private UniqueRef<IFileSystem> _uniqueBaseFs;
|
||||||
|
@ -45,16 +44,17 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
private bool _isMultiCommitSupported;
|
private bool _isMultiCommitSupported;
|
||||||
private bool _isJournalingEnabled;
|
private bool _isJournalingEnabled;
|
||||||
|
|
||||||
// Additions to support extra data
|
private ISaveDataExtraDataAccessorObserver _cacheObserver;
|
||||||
|
private ulong _saveDataId;
|
||||||
|
private SaveDataSpaceId _spaceId;
|
||||||
|
|
||||||
private ISaveDataCommitTimeStampGetter _timeStampGetter;
|
private ISaveDataCommitTimeStampGetter _timeStampGetter;
|
||||||
private RandomDataGenerator _randomGenerator;
|
private RandomDataGenerator _randomGenerator;
|
||||||
|
|
||||||
// Additions to support caching
|
// LibHac additions
|
||||||
private ISaveDataExtraDataAccessorObserver _cacheObserver;
|
private FileSystemClient _fsClient;
|
||||||
private SaveDataSpaceId _spaceId;
|
|
||||||
private ulong _saveDataId;
|
|
||||||
|
|
||||||
// Additions to ensure only one directory save data fs is opened at a time
|
// Addition to ensure only one directory save data fs is opened at a time
|
||||||
private UniqueRef<IFile> _lockFile;
|
private UniqueRef<IFile> _lockFile;
|
||||||
|
|
||||||
private class DirectorySaveDataFile : IFile
|
private class DirectorySaveDataFile : IFile
|
||||||
|
@ -116,27 +116,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
|
||||||
public DirectorySaveDataFileSystem(IFileSystem baseFileSystem)
|
|
||||||
{
|
|
||||||
_baseFs = baseFileSystem;
|
|
||||||
_mutex.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
|
||||||
public DirectorySaveDataFileSystem(ref UniqueRef<IFileSystem> baseFileSystem)
|
|
||||||
{
|
|
||||||
_baseFs = baseFileSystem.Get;
|
|
||||||
_mutex.Initialize();
|
|
||||||
_uniqueBaseFs = new UniqueRef<IFileSystem>(ref baseFileSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
|
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
|
||||||
/// If a <see cref="FileSystemClient"/> is provided a global mutex will be used when synchronizing directories.
|
/// If a <see cref="FileSystemClient"/> is provided a global mutex will be used when synchronizing directories.
|
||||||
|
@ -145,10 +124,10 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
||||||
/// <param name="fsClient">The <see cref="FileSystemClient"/> to use. May be null.</param>
|
/// <param name="fsClient">The <see cref="FileSystemClient"/> to use. May be null.</param>
|
||||||
public DirectorySaveDataFileSystem(IFileSystem baseFileSystem, FileSystemClient fsClient)
|
public DirectorySaveDataFileSystem(IFileSystem baseFileSystem, FileSystemClient fsClient = null)
|
||||||
{
|
{
|
||||||
_baseFs = baseFileSystem;
|
_baseFs = baseFileSystem;
|
||||||
_mutex.Initialize();
|
_mutex = new SdkMutexType();
|
||||||
_fsClient = fsClient;
|
_fsClient = fsClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,10 +139,10 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
|
||||||
/// <param name="fsClient">The <see cref="FileSystemClient"/> to use. May be null.</param>
|
/// <param name="fsClient">The <see cref="FileSystemClient"/> to use. May be null.</param>
|
||||||
public DirectorySaveDataFileSystem(ref UniqueRef<IFileSystem> baseFileSystem, FileSystemClient fsClient)
|
public DirectorySaveDataFileSystem(ref UniqueRef<IFileSystem> baseFileSystem, FileSystemClient fsClient = null)
|
||||||
{
|
{
|
||||||
_baseFs = baseFileSystem.Get;
|
_baseFs = baseFileSystem.Get;
|
||||||
_mutex.Initialize();
|
_mutex = new SdkMutexType();
|
||||||
_uniqueBaseFs = new UniqueRef<IFileSystem>(ref baseFileSystem);
|
_uniqueBaseFs = new UniqueRef<IFileSystem>(ref baseFileSystem);
|
||||||
_fsClient = fsClient;
|
_fsClient = fsClient;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +164,14 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
public Path ModifiedPath;
|
public Path ModifiedPath;
|
||||||
public Path SynchronizingPath;
|
public Path SynchronizingPath;
|
||||||
|
|
||||||
|
public RetryClosure(DirectorySaveDataFileSystem fs)
|
||||||
|
{
|
||||||
|
This = fs;
|
||||||
|
CommittedPath = new Path();
|
||||||
|
ModifiedPath = new Path();
|
||||||
|
SynchronizingPath = new Path();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
CommittedPath.Dispose();
|
CommittedPath.Dispose();
|
||||||
|
@ -195,7 +182,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
|
|
||||||
private delegate Result RetryDelegate(in RetryClosure closure);
|
private delegate Result RetryDelegate(in RetryClosure closure);
|
||||||
|
|
||||||
private Result RetryFinitelyForTargetLocked(RetryDelegate function, in RetryClosure closure)
|
private Result RetryFinitelyForTargetLocked(in RetryClosure closure, RetryDelegate function)
|
||||||
{
|
{
|
||||||
const int maxRetryCount = 10;
|
const int maxRetryCount = 10;
|
||||||
const int retryWaitTimeMs = 100;
|
const int retryWaitTimeMs = 100;
|
||||||
|
@ -228,22 +215,17 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
|
public Result Initialize(bool isJournalingSupported, bool isMultiCommitSupported,
|
||||||
{
|
bool isJournalingEnabled, ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator)
|
||||||
return Initialize(null, null, isJournalingSupported, isMultiCommitSupported, isJournalingEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Initialize(ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator,
|
|
||||||
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
|
|
||||||
{
|
{
|
||||||
_isJournalingSupported = isJournalingSupported;
|
_isJournalingSupported = isJournalingSupported;
|
||||||
_isMultiCommitSupported = isMultiCommitSupported;
|
_isMultiCommitSupported = isMultiCommitSupported;
|
||||||
_isJournalingEnabled = isJournalingEnabled;
|
_isJournalingEnabled = isJournalingEnabled;
|
||||||
_timeStampGetter = timeStampGetter ?? _timeStampGetter;
|
_timeStampGetter = timeStampGetter;
|
||||||
_randomGenerator = randomGenerator ?? _randomGenerator;
|
_randomGenerator = randomGenerator;
|
||||||
|
|
||||||
// Open the lock file
|
// Open the lock file
|
||||||
Result rc = GetFileSystemLock();
|
Result rc = AcquireLockFile();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
using var pathModifiedDirectory = new Path();
|
using var pathModifiedDirectory = new Path();
|
||||||
|
@ -273,8 +255,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
{
|
{
|
||||||
rc = _baseFs.CreateDirectory(in pathCommittedDirectory);
|
rc = _baseFs.CreateDirectory(in pathCommittedDirectory);
|
||||||
|
|
||||||
// Nintendo returns on all failures, but we'll keep going if committed already exists
|
// Changed: Nintendo returns on all failures, but we'll keep going if committed already
|
||||||
// to avoid confusing people manually creating savedata in emulators
|
// exists to avoid confusing people manually creating savedata in emulators
|
||||||
if (rc.IsFailure() && !ResultFs.PathAlreadyExists.Includes(rc))
|
if (rc.IsFailure() && !ResultFs.PathAlreadyExists.Includes(rc))
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -316,7 +298,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result GetFileSystemLock()
|
private Result AcquireLockFile()
|
||||||
{
|
{
|
||||||
// Having an open lock file means we already have the lock for the file system.
|
// Having an open lock file means we already have the lock for the file system.
|
||||||
if (_lockFile.HasValue)
|
if (_lockFile.HasValue)
|
||||||
|
@ -326,7 +308,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
Result rc = PathFunctions.SetUpFixedPath(ref pathLockFile.Ref(), LockFileName);
|
Result rc = PathFunctions.SetUpFixedPath(ref pathLockFile.Ref(), LockFileName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _baseFs.OpenFile(ref _lockFile, in pathLockFile, OpenMode.ReadWrite);
|
using var lockFile = new UniqueRef<IFile>();
|
||||||
|
rc = _baseFs.OpenFile(ref lockFile.Ref(), in pathLockFile, OpenMode.ReadWrite);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
|
@ -335,15 +318,16 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
rc = _baseFs.CreateFile(in pathLockFile, 0);
|
rc = _baseFs.CreateFile(in pathLockFile, 0);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _baseFs.OpenFile(ref _lockFile, in pathLockFile, OpenMode.ReadWrite);
|
rc = _baseFs.OpenFile(ref lockFile.Ref(), in pathLockFile, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return rc;
|
return rc.Miss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_lockFile.Set(ref lockFile.Ref());
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,8 +536,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
// Delete destination dir and recreate it.
|
// Delete destination dir and recreate it.
|
||||||
Result rc = _baseFs.DeleteDirectoryRecursively(destPath);
|
Result rc = _baseFs.DeleteDirectoryRecursively(destPath);
|
||||||
|
|
||||||
// Nintendo returns all errors unconditionally because SynchronizeDirectory is always called in situations
|
// Changed: Nintendo returns all errors unconditionally because SynchronizeDirectory is always called
|
||||||
// where a PathNotFound error would mean the save directory was in an invalid state.
|
// in situations where a PathNotFound error would mean the save directory was in an invalid state.
|
||||||
// We'll ignore PathNotFound errors to be more user-friendly to users who might accidentally
|
// We'll ignore PathNotFound errors to be more user-friendly to users who might accidentally
|
||||||
// put the save directory in an invalid state.
|
// put the save directory in an invalid state.
|
||||||
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc)) return rc;
|
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc)) return rc;
|
||||||
|
@ -563,7 +547,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
|
|
||||||
var directoryEntry = new DirectoryEntry();
|
var directoryEntry = new DirectoryEntry();
|
||||||
|
|
||||||
// Lock only if initialized with a client
|
// Changed: Lock only if initialized with a client
|
||||||
if (_fsClient is not null)
|
if (_fsClient is not null)
|
||||||
{
|
{
|
||||||
using ScopedLock<SdkMutexType> scopedLock =
|
using ScopedLock<SdkMutexType> scopedLock =
|
||||||
|
@ -585,63 +569,63 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoCommit()
|
private Result DoCommit(bool updateTimeStamp)
|
||||||
{
|
{
|
||||||
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _mutex);
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
if (!_isJournalingEnabled || !_isJournalingSupported)
|
if (!_isJournalingEnabled || !_isJournalingSupported)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
var closure = new RetryClosure();
|
using var closure = new RetryClosure(this);
|
||||||
closure.This = this;
|
|
||||||
|
|
||||||
Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath, ModifiedDirectoryName);
|
Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath.Ref(), ModifiedDirectoryName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = PathFunctions.SetUpFixedPath(ref closure.CommittedPath, CommittedDirectoryName);
|
rc = PathFunctions.SetUpFixedPath(ref closure.CommittedPath.Ref(), CommittedDirectoryName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath, SynchronizingDirectoryName);
|
rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath.Ref(), SynchronizingDirectoryName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (_openWritableFileCount > 0)
|
|
||||||
{
|
|
||||||
// All files must be closed before commiting save data.
|
// All files must be closed before commiting save data.
|
||||||
|
if (_openWritableFileCount > 0)
|
||||||
return ResultFs.WriteModeFileNotClosed.Log();
|
return ResultFs.WriteModeFileNotClosed.Log();
|
||||||
}
|
|
||||||
|
|
||||||
static Result RenameCommittedDir(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This._baseFs.RenameDirectory(in closure.CommittedPath,
|
|
||||||
in closure.SynchronizingPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result SynchronizeWorkingDir(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This.SynchronizeDirectory(in closure.SynchronizingPath,
|
|
||||||
in closure.ModifiedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result RenameSynchronizingDir(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This._baseFs.RenameDirectory(in closure.SynchronizingPath,
|
|
||||||
in closure.CommittedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get rid of the previous commit by renaming the folder.
|
// Get rid of the previous commit by renaming the folder.
|
||||||
rc = RetryFinitelyForTargetLocked(RenameCommittedDir, in closure);
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This._baseFs.RenameDirectory(in c.CommittedPath, in c.SynchronizingPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// If something goes wrong beyond this point, the commit will be
|
// If something goes wrong beyond this point, the commit of the main data
|
||||||
// completed the next time the savedata is opened.
|
// will be completed the next time the savedata is opened.
|
||||||
|
|
||||||
rc = RetryFinitelyForTargetLocked(SynchronizeWorkingDir, in closure);
|
if (updateTimeStamp && _timeStampGetter is not null)
|
||||||
|
{
|
||||||
|
Assert.SdkNotNull(_randomGenerator);
|
||||||
|
|
||||||
|
rc = UpdateExtraDataTimeStamp();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = CommitExtraDataImpl();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This.SynchronizeDirectory(in c.SynchronizingPath, in c.ModifiedPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = RetryFinitelyForTargetLocked(RenameSynchronizingDir, in closure);
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This._baseFs.RenameDirectory(in c.SynchronizingPath, in c.CommittedPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
closure.Dispose();
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCommit()
|
||||||
|
{
|
||||||
|
Result rc = DoCommit(updateTimeStamp: true);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,11 +639,15 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
|
|
||||||
protected override Result DoRollback()
|
protected override Result DoRollback()
|
||||||
{
|
{
|
||||||
// No old data is kept for non-journaling save data, so there's nothing to rollback to
|
// No old data is kept for non-journaling save data, so there's nothing to rollback to in that case
|
||||||
if (!_isJournalingSupported)
|
if (_isJournalingSupported)
|
||||||
return Result.Success;
|
{
|
||||||
|
Result rc = Initialize(_isJournalingSupported, _isMultiCommitSupported, _isJournalingEnabled,
|
||||||
|
_timeStampGetter, _randomGenerator);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
}
|
||||||
|
|
||||||
return Initialize(_isJournalingSupported, _isMultiCommitSupported, _isJournalingEnabled);
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
|
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
|
||||||
|
@ -694,6 +682,46 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsSaveDataFileSystemCacheEnabled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result RollbackOnlyModified()
|
||||||
|
{
|
||||||
|
return ResultFs.UnsupportedRollbackOnlyModifiedForDirectorySaveDataFileSystem.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result WriteExtraData(in SaveDataExtraData extraData)
|
||||||
|
{
|
||||||
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
return WriteExtraDataImpl(in extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result CommitExtraData(bool updateTimeStamp)
|
||||||
|
{
|
||||||
|
Result rc = DoCommit(updateTimeStamp);
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result ReadExtraData(out SaveDataExtraData extraData)
|
||||||
|
{
|
||||||
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||||
|
|
||||||
|
return ReadExtraDataImpl(out extraData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer,
|
||||||
|
SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
{
|
||||||
|
_cacheObserver = observer;
|
||||||
|
_spaceId = spaceId;
|
||||||
|
_saveDataId = saveDataId;
|
||||||
|
}
|
||||||
|
|
||||||
private void DecrementWriteOpenFileCount()
|
private void DecrementWriteOpenFileCount()
|
||||||
{
|
{
|
||||||
// Todo?: Calling OpenFile when outFile already contains a DirectorySaveDataFile
|
// Todo?: Calling OpenFile when outFile already contains a DirectorySaveDataFile
|
||||||
|
@ -703,7 +731,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
_openWritableFileCount--;
|
_openWritableFileCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The original class doesn't support extra data.
|
// The original class doesn't support transactional extra data,
|
||||||
|
// always writing the extra data directly to the /extradata file.
|
||||||
// Everything below this point is a LibHac extension.
|
// Everything below this point is a LibHac extension.
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> CommittedExtraDataName => // "/ExtraData0"
|
private static ReadOnlySpan<byte> CommittedExtraDataName => // "/ExtraData0"
|
||||||
|
@ -860,6 +889,15 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Result GetExtraDataPath(ref Path path)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> extraDataName = _isJournalingSupported && !_isJournalingEnabled
|
||||||
|
? CommittedExtraDataName
|
||||||
|
: ModifiedExtraDataName;
|
||||||
|
|
||||||
|
return PathFunctions.SetUpFixedPath(ref path, extraDataName);
|
||||||
|
}
|
||||||
|
|
||||||
private Result EnsureExtraDataSize(in Path path)
|
private Result EnsureExtraDataSize(in Path path)
|
||||||
{
|
{
|
||||||
using var file = new UniqueRef<IFile>();
|
using var file = new UniqueRef<IFile>();
|
||||||
|
@ -902,42 +940,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result GetExtraDataPath(ref Path path)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> extraDataName = _isJournalingSupported && !_isJournalingEnabled
|
|
||||||
? CommittedExtraDataName
|
|
||||||
: ModifiedExtraDataName;
|
|
||||||
|
|
||||||
return PathFunctions.SetUpFixedPath(ref path, extraDataName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result WriteExtraData(in SaveDataExtraData extraData)
|
|
||||||
{
|
|
||||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
|
||||||
|
|
||||||
return WriteExtraDataImpl(in extraData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CommitExtraData(bool updateTimeStamp)
|
|
||||||
{
|
|
||||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
|
||||||
|
|
||||||
if (updateTimeStamp && _timeStampGetter is not null && _randomGenerator is not null)
|
|
||||||
{
|
|
||||||
Result rc = UpdateExtraDataTimeStamp();
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CommitExtraDataImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result ReadExtraData(out SaveDataExtraData extraData)
|
|
||||||
{
|
|
||||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
|
||||||
|
|
||||||
return ReadExtraDataImpl(out extraData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result UpdateExtraDataTimeStamp()
|
private Result UpdateExtraDataTimeStamp()
|
||||||
{
|
{
|
||||||
Assert.SdkRequires(_mutex.IsLockedByCurrentThread());
|
Assert.SdkRequires(_mutex.IsLockedByCurrentThread());
|
||||||
|
@ -987,50 +989,33 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
if (!_isJournalingSupported || !_isJournalingEnabled)
|
if (!_isJournalingSupported || !_isJournalingEnabled)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
var closure = new RetryClosure();
|
using var closure = new RetryClosure(this);
|
||||||
closure.This = this;
|
|
||||||
|
|
||||||
Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath, ModifiedExtraDataName);
|
Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath.Ref(), ModifiedExtraDataName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = PathFunctions.SetUpFixedPath(ref closure.CommittedPath, CommittedExtraDataName);
|
rc = PathFunctions.SetUpFixedPath(ref closure.CommittedPath.Ref(), CommittedExtraDataName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath, SynchronizingExtraDataName);
|
rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath.Ref(), SynchronizingExtraDataName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
static Result RenameCommittedFile(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This._baseFs.RenameFile(in closure.CommittedPath,
|
|
||||||
in closure.SynchronizingPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result SynchronizeWorkingFile(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This.SynchronizeExtraData(in closure.SynchronizingPath,
|
|
||||||
in closure.ModifiedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result RenameSynchronizingFile(in RetryClosure closure)
|
|
||||||
{
|
|
||||||
return closure.This._baseFs.RenameFile(in closure.SynchronizingPath,
|
|
||||||
in closure.CommittedPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get rid of the previous commit by renaming the file.
|
// Get rid of the previous commit by renaming the file.
|
||||||
rc = RetryFinitelyForTargetLocked(RenameCommittedFile, in closure);
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This._baseFs.RenameFile(in c.CommittedPath, in c.SynchronizingPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// If something goes wrong beyond this point, the commit will be
|
// If something goes wrong beyond this point, the commit will be
|
||||||
// completed the next time the savedata is opened.
|
// completed the next time the savedata is opened.
|
||||||
|
|
||||||
rc = RetryFinitelyForTargetLocked(SynchronizeWorkingFile, in closure);
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This.SynchronizeExtraData(in c.SynchronizingPath, in c.ModifiedPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = RetryFinitelyForTargetLocked(RenameSynchronizingFile, in closure);
|
rc = RetryFinitelyForTargetLocked(in closure,
|
||||||
|
(in RetryClosure c) => c.This._baseFs.RenameFile(in c.SynchronizingPath, in c.CommittedPath));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
closure.Dispose();
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1056,14 +1041,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId,
|
|
||||||
ulong saveDataId)
|
|
||||||
{
|
|
||||||
_cacheObserver = observer;
|
|
||||||
_spaceId = spaceId;
|
|
||||||
_saveDataId = saveDataId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SaveDataSpaceId GetSaveDataSpaceId() => _spaceId;
|
public SaveDataSpaceId GetSaveDataSpaceId() => _spaceId;
|
||||||
public ulong GetSaveDataId() => _saveDataId;
|
public ulong GetSaveDataId() => _saveDataId;
|
||||||
}
|
}
|
|
@ -6,11 +6,11 @@ namespace LibHac.FsSystem;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides read/write access to a save data file system's extra data.
|
/// Provides read/write access to a save data file system's extra data.
|
||||||
/// </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 ISaveDataExtraDataAccessor : IDisposable
|
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(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
|
void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ public abstract class ISaveDataFileSystem : IFileSystem, ICacheableSaveDataFileS
|
||||||
public abstract Result WriteExtraData(in SaveDataExtraData extraData);
|
public abstract Result WriteExtraData(in SaveDataExtraData extraData);
|
||||||
public abstract Result CommitExtraData(bool updateTimeStamp);
|
public abstract Result CommitExtraData(bool updateTimeStamp);
|
||||||
public abstract Result ReadExtraData(out SaveDataExtraData extraData);
|
public abstract Result ReadExtraData(out SaveDataExtraData extraData);
|
||||||
public abstract void RegisterCacheObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
|
public abstract void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ICacheableSaveDataFileSystem
|
public interface ICacheableSaveDataFileSystem
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using LibHac.Bcat;
|
using System;
|
||||||
|
using LibHac.Bcat;
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
namespace LibHac;
|
namespace LibHac;
|
||||||
|
|
||||||
|
@ -15,13 +17,17 @@ public static class HorizonFactory
|
||||||
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
|
|
||||||
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFileSystem, keySet, fsServer);
|
var random = new Random();
|
||||||
|
RandomDataGenerator randomGenerator = buffer => random.NextBytes(buffer);
|
||||||
|
|
||||||
|
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFileSystem, keySet, fsServer, randomGenerator);
|
||||||
|
|
||||||
var fsServerConfig = new FileSystemServerConfig
|
var fsServerConfig = new FileSystemServerConfig
|
||||||
{
|
{
|
||||||
DeviceOperator = defaultObjects.DeviceOperator,
|
DeviceOperator = defaultObjects.DeviceOperator,
|
||||||
ExternalKeySet = keySet.ExternalKeySet,
|
ExternalKeySet = keySet.ExternalKeySet,
|
||||||
FsCreators = defaultObjects.FsCreators,
|
FsCreators = defaultObjects.FsCreators,
|
||||||
|
RandomGenerator = randomGenerator
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
|
|
||||||
namespace LibHac.Tests.Fs.FileSystemClientTests;
|
namespace LibHac.Tests.Fs.FileSystemClientTests;
|
||||||
|
@ -18,7 +19,10 @@ public static class FileSystemServerFactory
|
||||||
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
|
|
||||||
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, keySet, fsServer);
|
var random = new Random(12345);
|
||||||
|
RandomDataGenerator randomGenerator = buffer => random.NextBytes(buffer);
|
||||||
|
|
||||||
|
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, keySet, fsServer, randomGenerator);
|
||||||
|
|
||||||
defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted);
|
defaultObjects.SdCard.SetSdCardInsertionStatus(sdCardInserted);
|
||||||
|
|
||||||
|
@ -26,6 +30,7 @@ public static class FileSystemServerFactory
|
||||||
config.FsCreators = defaultObjects.FsCreators;
|
config.FsCreators = defaultObjects.FsCreators;
|
||||||
config.DeviceOperator = defaultObjects.DeviceOperator;
|
config.DeviceOperator = defaultObjects.DeviceOperator;
|
||||||
config.ExternalKeySet = new ExternalKeySet();
|
config.ExternalKeySet = new ExternalKeySet();
|
||||||
|
config.RandomGenerator = randomGenerator;
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
|
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
|
||||||
return horizon;
|
return horizon;
|
||||||
|
|
|
@ -48,8 +48,8 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
|
||||||
FileSystemClient fsClient)
|
FileSystemClient fsClient)
|
||||||
{
|
{
|
||||||
var obj = new DirectorySaveDataFileSystem(baseFileSystem, fsClient);
|
var obj = new DirectorySaveDataFileSystem(baseFileSystem, fsClient);
|
||||||
Result rc = obj.Initialize(timeStampGetter, randomGenerator, isJournalingSupported, isMultiCommitSupported,
|
Result rc = obj.Initialize(isJournalingSupported, isMultiCommitSupported, isJournalingEnabled, timeStampGetter,
|
||||||
isJournalingEnabled);
|
randomGenerator);
|
||||||
|
|
||||||
if (rc.IsSuccess())
|
if (rc.IsSuccess())
|
||||||
{
|
{
|
||||||
|
@ -521,7 +521,7 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
|
||||||
|
|
||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
public Result GenerateRandom(Span<byte> output)
|
public void GenerateRandom(Span<byte> output)
|
||||||
{
|
{
|
||||||
if (output.Length != 8)
|
if (output.Length != 8)
|
||||||
throw new ArgumentException();
|
throw new ArgumentException();
|
||||||
|
@ -529,7 +529,6 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
|
||||||
Unsafe.As<byte, long>(ref MemoryMarshal.GetReference(output)) = Values[_index];
|
Unsafe.As<byte, long>(ref MemoryMarshal.GetReference(output)) = Values[_index];
|
||||||
|
|
||||||
_index = (_index + 1) % Values.Length;
|
_index = (_index + 1) % Values.Length;
|
||||||
return Result.Success;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using LibHac.Tools.Fs;
|
using LibHac.Tools.Fs;
|
||||||
|
|
||||||
namespace LibHac.Tests;
|
namespace LibHac.Tests;
|
||||||
|
@ -18,12 +19,16 @@ public static class HorizonFactory
|
||||||
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
|
|
||||||
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, keySet, fsServer);
|
var random = new Random(12345);
|
||||||
|
RandomDataGenerator randomGenerator = buffer => random.NextBytes(buffer);
|
||||||
|
|
||||||
|
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, keySet, fsServer, randomGenerator);
|
||||||
|
|
||||||
var config = new FileSystemServerConfig();
|
var config = new FileSystemServerConfig();
|
||||||
config.FsCreators = defaultObjects.FsCreators;
|
config.FsCreators = defaultObjects.FsCreators;
|
||||||
config.DeviceOperator = defaultObjects.DeviceOperator;
|
config.DeviceOperator = defaultObjects.DeviceOperator;
|
||||||
config.ExternalKeySet = new ExternalKeySet();
|
config.ExternalKeySet = new ExternalKeySet();
|
||||||
|
config.RandomGenerator = randomGenerator;
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
|
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue