diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv
index bba44f13..9b30f760 100644
--- a/build/CodeGen/results.csv
+++ b/build/CodeGen/results.csv
@@ -672,6 +672,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,4301,4499,,,SaveDataCorrupted,
2,4302,,,,UnsupportedSaveDataVersion,
2,4303,,,,InvalidSaveDataEntryType,
+2,4304,,,,ReconstructibleSaveDataCorrupted,
2,4311,4319,,,SaveDataFileSystemCorrupted,
2,4312,,,,InvalidJournalIntegritySaveDataHashSize,
diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs
index dc10a21e..d0e2d0d7 100644
--- a/src/LibHac/Fs/FsEnums.cs
+++ b/src/LibHac/Fs/FsEnums.cs
@@ -170,6 +170,12 @@ public enum SaveDataRank : byte
Secondary = 1
}
+public enum SaveDataFormatType : byte
+{
+ Normal = 0,
+ NoJournal = 1
+}
+
[Flags]
public enum SaveDataFlags
{
diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs
index 047d884b..20ee475b 100644
--- a/src/LibHac/Fs/ResultFs.cs
+++ b/src/LibHac/Fs/ResultFs.cs
@@ -1210,6 +1210,8 @@ public static class ResultFs
public static Result.Base UnsupportedSaveDataVersion => new Result.Base(ModuleFs, 4302);
/// Error code: 2002-4303; Inner value: 0x219e02
public static Result.Base InvalidSaveDataEntryType => new Result.Base(ModuleFs, 4303);
+ /// Error code: 2002-4304; Inner value: 0x21a002
+ public static Result.Base ReconstructibleSaveDataCorrupted => new Result.Base(ModuleFs, 4304);
/// Error code: 2002-4311; Range: 4311-4319; Inner value: 0x21ae02
public static Result.Base SaveDataFileSystemCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4311, 4319); }
diff --git a/src/LibHac/FsSrv/DefaultFsServerObjects.cs b/src/LibHac/FsSrv/DefaultFsServerObjects.cs
index 9f0317cd..91d4c3ee 100644
--- a/src/LibHac/FsSrv/DefaultFsServerObjects.cs
+++ b/src/LibHac/FsSrv/DefaultFsServerObjects.cs
@@ -2,6 +2,7 @@
using LibHac.Common.Keys;
using LibHac.FsSrv.FsCreator;
using LibHac.FsSrv.Sf;
+using LibHac.FsSystem;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.FsSrv;
@@ -14,7 +15,7 @@ public class DefaultFsServerObjects
public EmulatedSdCard SdCard { get; set; }
public static DefaultFsServerObjects GetDefaultEmulatedCreators(IFileSystem rootFileSystem, KeySet keySet,
- FileSystemServer fsServer)
+ FileSystemServer fsServer, RandomDataGenerator randomGenerator)
{
var creators = new FileSystemCreatorInterfaces();
var gameCard = new EmulatedGameCard(keySet);
@@ -31,7 +32,7 @@ public class DefaultFsServerObjects
creators.StorageOnNcaCreator = new StorageOnNcaCreator(keySet);
creators.TargetManagerFileSystemCreator = new TargetManagerFileSystemCreator();
creators.SubDirectoryFileSystemCreator = new SubDirectoryFileSystemCreator();
- creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, keySet, null, null);
+ creators.SaveDataFileSystemCreator = new SaveDataFileSystemCreator(fsServer, keySet, null, randomGenerator);
creators.GameCardStorageCreator = gcStorageCreator;
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
@@ -48,4 +49,4 @@ public class DefaultFsServerObjects
SdCard = sdCard
};
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FileSystemServerInitializer.cs b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
index 03c6a330..0f02dcf8 100644
--- a/src/LibHac/FsSrv/FileSystemServerInitializer.cs
+++ b/src/LibHac/FsSrv/FileSystemServerInitializer.cs
@@ -63,13 +63,6 @@ public static class FileSystemServerInitializer
private static FileSystemProxyConfiguration InitializeFileSystemProxy(FileSystemServer server,
FileSystemServerConfig config)
{
- var random = new Random();
- RandomDataGenerator randomGenerator = buffer =>
- {
- random.NextBytes(buffer);
- return Result.Success;
- };
-
var bufferManager = new FileSystemBufferManager();
Memory heapBuffer = GC.AllocateArray(BufferManagerHeapSize, true);
bufferManager.Initialize(BufferManagerCacheSize, heapBuffer, BufferManagerBlockSize);
@@ -141,7 +134,7 @@ public static class FileSystemServerInitializer
saveFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
saveFsServiceConfig.ProgramRegistryService = programRegistryService;
saveFsServiceConfig.BufferManager = bufferManager;
- saveFsServiceConfig.GenerateRandomData = randomGenerator;
+ saveFsServiceConfig.GenerateRandomData = config.RandomGenerator;
saveFsServiceConfig.IsPseudoSaveData = () => true;
saveFsServiceConfig.MaxSaveFsCacheCount = 1;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
@@ -278,4 +271,9 @@ public class FileSystemServerConfig
/// If null, an empty set will be created.
///
public ExternalKeySet ExternalKeySet { get; set; }
+
+ ///
+ /// Used for generating random data for save data.
+ ///
+ public RandomDataGenerator RandomGenerator { get; set; }
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs
index ac66ca69..be6a6c27 100644
--- a/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/ISaveDataFileSystemCreator.cs
@@ -10,12 +10,10 @@ public interface ISaveDataFileSystemCreator
{
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
- Result Create(ref SharedRef outFileSystem,
- ref SharedRef outExtraDataAccessor,
- ISaveDataFileSystemCacheManager cacheManager, ref SharedRef baseFileSystem,
- SaveDataSpaceId spaceId, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
+ Result Create(ref SharedRef outFileSystem, ref SharedRef baseFileSystem,
+ SaveDataSpaceId spaceId, ulong saveDataId, bool allowDirectorySaveData, bool isDeviceUniqueMac,
bool isJournalingSupported, bool isMultiCommitSupported, bool openReadOnly, bool openShared,
- ISaveDataCommitTimeStampGetter timeStampGetter);
+ ISaveDataCommitTimeStampGetter timeStampGetter, bool isReconstructible);
Result CreateExtraDataAccessor(ref SharedRef outExtraDataAccessor,
ref SharedRef baseFileSystem);
diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs
index e649e72c..ca94f92d 100644
--- a/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs
+++ b/src/LibHac/FsSrv/FsCreator/SaveDataFileSystemCreator.cs
@@ -3,12 +3,9 @@ using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Common.Keys;
-using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
-using LibHac.Tools.FsSystem;
-using LibHac.Tools.FsSystem.Save;
using LibHac.Util;
using OpenType = LibHac.FsSrv.SaveDataOpenTypeSetFileStorage.OpenType;
@@ -17,10 +14,15 @@ namespace LibHac.FsSrv.FsCreator;
public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
{
+ // Option to disable some restrictions enforced in actual FS.
+ private static readonly bool EnforceSaveTypeRestrictions = false;
+
+ // ReSharper disable once NotAccessedField.Local
private IBufferManager _bufferManager;
private RandomDataGenerator _randomGenerator;
// LibHac Additions
+ // ReSharper disable once NotAccessedField.Local
private KeySet _keySet;
private FileSystemServer _fsServer;
@@ -38,54 +40,56 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
throw new NotImplementedException();
}
- public Result Create(ref SharedRef outFileSystem,
- ref SharedRef outExtraDataAccessor,
- ISaveDataFileSystemCacheManager cacheManager, ref SharedRef baseFileSystem,
- SaveDataSpaceId spaceId, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
+ public Result Create(ref SharedRef outFileSystem, ref SharedRef baseFileSystem,
+ SaveDataSpaceId spaceId, ulong saveDataId, bool allowDirectorySaveData, bool isDeviceUniqueMac,
bool isJournalingSupported, bool isMultiCommitSupported, bool openReadOnly, bool openShared,
- ISaveDataCommitTimeStampGetter timeStampGetter)
+ ISaveDataCommitTimeStampGetter timeStampGetter, bool isReconstructible)
{
Unsafe.SkipInit(out Array18 saveImageNameBuffer);
- Assert.SdkRequiresNotNull(cacheManager);
-
using var saveImageName = new Path();
Result rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer.Items, saveDataId);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
rc = baseFileSystem.Get.GetEntryType(out DirectoryEntryType type, in saveImageName);
if (rc.IsFailure())
{
- return ResultFs.PathNotFound.Includes(rc) ? ResultFs.TargetNotFound.LogConverted(rc) : rc;
+ return ResultFs.PathNotFound.Includes(rc) ? ResultFs.TargetNotFound.LogConverted(rc) : rc.Miss();
}
+ using var saveDataFs = new SharedRef();
+
if (type == DirectoryEntryType.Directory)
{
- if (!allowDirectorySaveData)
- return ResultFs.InvalidSaveDataEntryType.Log();
+ if (EnforceSaveTypeRestrictions)
+ {
+ if (!allowDirectorySaveData)
+ return ResultFs.InvalidSaveDataEntryType.Log();
+ }
- using var baseFs =
- new UniqueRef(new SubdirectoryFileSystem(ref baseFileSystem));
+ // Get a file system over the save directory
+ using var baseFs = new UniqueRef(new SubdirectoryFileSystem(ref baseFileSystem));
if (!baseFs.HasValue)
return ResultFs.AllocationMemoryFailedInSaveDataFileSystemCreatorA.Log();
rc = baseFs.Get.Initialize(in saveImageName);
- if (rc.IsFailure()) return rc;
+ if (rc.IsFailure()) return rc.Miss();
+ // Create and initialize the directory save data FS
using UniqueRef tempFs = UniqueRef.Create(ref baseFs.Ref());
using var saveDirFs = new SharedRef(
new DirectorySaveDataFileSystem(ref tempFs.Ref(), _fsServer.Hos.Fs));
- rc = saveDirFs.Get.Initialize(timeStampGetter, _randomGenerator, isJournalingSupported,
- isMultiCommitSupported, !openReadOnly);
- if (rc.IsFailure()) return rc;
+ if (!saveDirFs.HasValue)
+ return ResultFs.AllocationMemoryFailedInSaveDataFileSystemCreatorB.Log();
- outFileSystem.SetByCopy(in saveDirFs);
- outExtraDataAccessor.SetByCopy(in saveDirFs);
+ rc = saveDirFs.Get.Initialize(isJournalingSupported, isMultiCommitSupported, !openReadOnly,
+ timeStampGetter, _randomGenerator);
+ if (rc.IsFailure()) return rc.Miss();
- return Result.Success;
+ saveDataFs.SetByMove(ref saveDirFs.Ref());
}
else
{
@@ -98,18 +102,16 @@ public class SaveDataFileSystemCreator : ISaveDataFileSystemCreator
OpenMode.ReadWrite, openType);
if (rc.IsFailure()) return rc;
- if (!isJournalingSupported)
- {
- throw new NotImplementedException();
- }
-
- using var saveFs = new SharedRef(new SaveDataFileSystem(_keySet, fileStorage.Get,
- IntegrityCheckLevel.ErrorOnInvalid, false));
-
- // Todo: ISaveDataExtraDataAccessor
-
- return Result.Success;
+ throw new NotImplementedException();
}
+
+ // Wrap the save FS in a result convert FS and set it as the output FS
+ using var resultConvertFs = new SharedRef(
+ new SaveDataResultConvertFileSystem(ref saveDataFs.Ref(), isReconstructible));
+
+ outFileSystem.SetByMove(ref resultConvertFs.Ref());
+
+ return Result.Success;
}
public Result CreateExtraDataAccessor(ref SharedRef outExtraDataAccessor,
diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs
new file mode 100644
index 00000000..a9bcb457
--- /dev/null
+++ b/src/LibHac/FsSrv/FsCreator/SaveDataResultConvertFileSystem.cs
@@ -0,0 +1,127 @@
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using static LibHac.FsSrv.FsCreator.SaveDataResultConverter;
+
+namespace LibHac.FsSrv.FsCreator;
+
+///
+/// Wraps an , converting its returned s
+/// to save-data-specific s.
+///
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public class SaveDataResultConvertFile : IResultConvertFile
+{
+ private bool _isReconstructible;
+
+ public SaveDataResultConvertFile(ref UniqueRef baseFile, bool isReconstructible) : base(ref baseFile)
+ {
+ _isReconstructible = isReconstructible;
+ }
+
+ protected override Result ConvertResult(Result result)
+ {
+ return ConvertSaveDataFsResult(result, _isReconstructible).Ret();
+ }
+}
+
+///
+/// Wraps an , converting its returned s
+/// to save-data-specific s.
+///
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public class SaveDataResultConvertDirectory : IResultConvertDirectory
+{
+ private bool _isReconstructible;
+
+ public SaveDataResultConvertDirectory(ref UniqueRef baseDirectory, bool isReconstructible) : base(
+ ref baseDirectory)
+ {
+ _isReconstructible = isReconstructible;
+ }
+
+ protected override Result ConvertResult(Result result)
+ {
+ return ConvertSaveDataFsResult(result, _isReconstructible).Ret();
+ }
+}
+
+///
+/// Wraps an , converting its returned s
+/// to save-data-specific s.
+///
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
+{
+ private bool _isReconstructible;
+
+ public SaveDataResultConvertFileSystem(ref SharedRef baseFileSystem, bool isReconstructible) :
+ base(ref baseFileSystem)
+ {
+ _isReconstructible = isReconstructible;
+ }
+
+ public override Result WriteExtraData(in SaveDataExtraData extraData)
+ {
+ return ConvertSaveDataFsResult(GetFileSystem().WriteExtraData(in extraData), _isReconstructible).Ret();
+ }
+
+ public override Result CommitExtraData(bool updateTimeStamp)
+ {
+ return ConvertSaveDataFsResult(GetFileSystem().CommitExtraData(updateTimeStamp), _isReconstructible).Ret();
+ }
+
+ public override Result ReadExtraData(out SaveDataExtraData extraData)
+ {
+ return ConvertSaveDataFsResult(GetFileSystem().ReadExtraData(out extraData), _isReconstructible).Ret();
+ }
+
+ public override Result RollbackOnlyModified()
+ {
+ return ConvertSaveDataFsResult(GetFileSystem().RollbackOnlyModified(), _isReconstructible).Ret();
+ }
+
+ protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode)
+ {
+ using var file = new UniqueRef();
+ Result rc = ConvertResult(GetFileSystem().OpenFile(ref file.Ref(), in path, mode));
+ if (rc.IsFailure()) return rc.Miss();
+
+ using UniqueRef resultConvertFile =
+ new(new SaveDataResultConvertFile(ref file.Ref(), _isReconstructible));
+
+ outFile.Set(ref resultConvertFile.Ref());
+ return Result.Success;
+ }
+
+ protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
+ OpenDirectoryMode mode)
+ {
+ using var directory = new UniqueRef();
+ Result rc = ConvertResult(GetFileSystem().OpenDirectory(ref directory.Ref(), in path, mode));
+ if (rc.IsFailure()) return rc.Miss();
+
+ using UniqueRef resultConvertDirectory =
+ new(new SaveDataResultConvertDirectory(ref directory.Ref(), _isReconstructible));
+
+ outDirectory.Set(ref resultConvertDirectory.Ref());
+ return Result.Success;
+ }
+
+ protected override Result ConvertResult(Result result)
+ {
+ return ConvertSaveDataFsResult(result, _isReconstructible).Ret();
+ }
+
+ public override bool IsSaveDataFileSystemCacheEnabled()
+ {
+ return GetFileSystem().IsSaveDataFileSystemCacheEnabled();
+ }
+
+ public override void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer,
+ SaveDataSpaceId spaceId, ulong saveDataId)
+ {
+ GetFileSystem().RegisterExtraDataAccessorObserver(observer, spaceId, saveDataId);
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs b/src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs
new file mode 100644
index 00000000..98df4f6b
--- /dev/null
+++ b/src/LibHac/FsSrv/FsCreator/SaveDataResultConverter.cs
@@ -0,0 +1,134 @@
+using LibHac.Diag;
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.FsCreator;
+
+///
+/// Contains functions for converting internal save data s to external s.
+///
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public static class SaveDataResultConverter
+{
+ private static Result ConvertCorruptedResult(Result result)
+ {
+ if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result))
+ {
+ if (ResultFs.IncorrectIntegrityVerificationMagicCode.Includes(result))
+ return ResultFs.IncorrectSaveDataIntegrityVerificationMagicCode.LogConverted(result);
+
+ if (ResultFs.InvalidZeroHash.Includes(result))
+ return ResultFs.InvalidSaveDataZeroHash.LogConverted(result);
+
+ if (ResultFs.NonRealDataVerificationFailed.Includes(result))
+ return ResultFs.SaveDataNonRealDataVerificationFailed.LogConverted(result);
+
+ if (ResultFs.ClearedRealDataVerificationFailed.Includes(result))
+ return ResultFs.ClearedSaveDataRealDataVerificationFailed.LogConverted(result);
+
+ if (ResultFs.UnclearedRealDataVerificationFailed.Includes(result))
+ return ResultFs.UnclearedSaveDataRealDataVerificationFailed.LogConverted(result);
+
+ Assert.SdkAssert(false);
+ }
+ else if (ResultFs.HostFileSystemCorrupted.Includes(result))
+ {
+ if (ResultFs.HostEntryCorrupted.Includes(result))
+ return ResultFs.SaveDataHostEntryCorrupted.LogConverted(result);
+
+ if (ResultFs.HostFileDataCorrupted.Includes(result))
+ return ResultFs.SaveDataHostFileDataCorrupted.LogConverted(result);
+
+ if (ResultFs.HostFileCorrupted.Includes(result))
+ return ResultFs.SaveDataHostFileCorrupted.LogConverted(result);
+
+ if (ResultFs.InvalidHostHandle.Includes(result))
+ return ResultFs.InvalidSaveDataHostHandle.LogConverted(result);
+
+ Assert.SdkAssert(false);
+ }
+ else if (ResultFs.DatabaseCorrupted.Includes(result))
+ {
+ if (ResultFs.InvalidAllocationTableBlock.Includes(result))
+ return ResultFs.InvalidSaveDataAllocationTableBlock.LogConverted(result);
+
+ if (ResultFs.InvalidKeyValueListElementIndex.Includes(result))
+ return ResultFs.InvalidSaveDataKeyValueListElementIndex.LogConverted(result);
+
+ if (ResultFs.InvalidAllocationTableChainEntry.Includes(result))
+ return ResultFs.InvalidSaveDataAllocationTableChainEntry.LogConverted(result);
+
+ if (ResultFs.InvalidAllocationTableOffset.Includes(result))
+ return ResultFs.InvalidSaveDataAllocationTableOffset.LogConverted(result);
+
+ if (ResultFs.InvalidAllocationTableBlockCount.Includes(result))
+ return ResultFs.InvalidSaveDataAllocationTableBlockCount.LogConverted(result);
+
+ if (ResultFs.InvalidKeyValueListEntryIndex.Includes(result))
+ return ResultFs.InvalidSaveDataKeyValueListEntryIndex.LogConverted(result);
+
+ if (ResultFs.InvalidBitmapIndex.Includes(result))
+ return ResultFs.InvalidSaveDataBitmapIndex.LogConverted(result);
+
+ Assert.SdkAssert(false);
+ }
+ else if (ResultFs.ZeroBitmapFileCorrupted.Includes(result))
+ {
+ if (ResultFs.IncompleteBlockInZeroBitmapHashStorageFile.Includes(result))
+ return ResultFs.IncompleteBlockInZeroBitmapHashStorageFileSaveData.LogConverted(result);
+
+ Assert.SdkAssert(false);
+ }
+
+ return result;
+ }
+
+ private static Result ConvertResult(Result result)
+ {
+ if (ResultFs.UnsupportedVersion.Includes(result))
+ return ResultFs.UnsupportedSaveDataVersion.LogConverted(result);
+
+ if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result) ||
+ ResultFs.BuiltInStorageCorrupted.Includes(result) ||
+ ResultFs.HostFileSystemCorrupted.Includes(result) ||
+ ResultFs.DatabaseCorrupted.Includes(result) ||
+ ResultFs.ZeroBitmapFileCorrupted.Includes(result))
+ {
+ return ConvertCorruptedResult(result).Miss();
+ }
+
+ if (ResultFs.FatFileSystemCorrupted.Includes(result))
+ return result;
+
+ if (ResultFs.NotFound.Includes(result))
+ return ResultFs.PathNotFound.LogConverted(result);
+
+ if (ResultFs.AllocationTableFull.Includes(result))
+ return ResultFs.UsableSpaceNotEnough.LogConverted(result);
+
+ if (ResultFs.AlreadyExists.Includes(result))
+ return ResultFs.PathAlreadyExists.LogConverted(result);
+
+ if (ResultFs.IncompatiblePath.Includes(result) ||
+ ResultFs.FileNotFound.Includes(result))
+ {
+ return ResultFs.PathNotFound.LogConverted(result);
+ }
+
+ return result;
+ }
+
+ public static Result ConvertSaveDataFsResult(Result result, bool isReconstructible)
+ {
+ if (result.IsSuccess())
+ return Result.Success;
+
+ Result convertedResult = ConvertResult(result);
+
+ if (isReconstructible && ResultFs.SaveDataCorrupted.Includes(convertedResult))
+ {
+ return ResultFs.ReconstructibleSaveDataCorrupted.LogConverted(convertedResult);
+ }
+
+ return convertedResult;
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs
index 1d3ce965..a895928c 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.RegisterExtraDataAccessorObserver(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/FsSystem/SaveDataFileSystemCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
similarity index 65%
rename from src/LibHac/FsSystem/SaveDataFileSystemCacheManager.cs
rename to src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
index 1c19a119..f2bf5858 100644
--- a/src/LibHac/FsSystem/SaveDataFileSystemCacheManager.cs
+++ b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
@@ -1,27 +1,23 @@
-using LibHac.Common;
+using System;
+using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs;
+using LibHac.FsSystem;
using LibHac.Os;
-namespace LibHac.FsSystem;
+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 13.1.0 (nnSdk 13.4.0)
-public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public class SaveDataFileSystemCacheManager : IDisposable
{
- ///
- /// Holds a single cached file system identified by its save data ID and save data space ID.
- ///
- /// Based on FS 13.1.0 (nnSdk 13.4.0)
[NonCopyable]
private struct Cache
{
- // Note: Nintendo only supports caching SaveDataFileSystem. We support DirectorySaveDataFileSystem too,
- // so we use a wrapper class to simplify the logic here.
- private SharedRef _fileSystem;
+ private SharedRef _fileSystem;
private ulong _saveDataId;
private SaveDataSpaceId _spaceId;
@@ -35,17 +31,16 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
return _fileSystem.HasValue && _spaceId == spaceId && _saveDataId == saveDataId;
}
- public SharedRef Move()
+ public SharedRef Move()
{
- return SharedRef.CreateMove(ref _fileSystem);
+ return SharedRef.CreateMove(ref _fileSystem);
}
- public void Register(ref SharedRef fileSystem)
+ public void Register(ref SharedRef fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
- _spaceId = fileSystem.Get.GetSaveDataSpaceId();
- _saveDataId = fileSystem.Get.GetSaveDataId();
-
_fileSystem.SetByMove(ref fileSystem);
+ _spaceId = spaceId;
+ _saveDataId = saveDataId;
}
public void Unregister()
@@ -95,8 +90,12 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
return Result.Success;
}
- public bool GetCache(ref SharedRef outFileSystem, SaveDataSpaceId spaceId,
- ulong saveDataId)
+ public UniqueLockRef GetScopedLock()
+ {
+ return new UniqueLockRef(ref _mutex);
+ }
+
+ public bool GetCache(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
Assert.SdkRequiresGreaterEqual(_maxCachedFileSystemCount, 0);
@@ -106,7 +105,7 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
{
if (_cachedFileSystems[i].IsCached(spaceId, saveDataId))
{
- using SharedRef cachedFs = _cachedFileSystems[i].Move();
+ using SharedRef cachedFs = _cachedFileSystems[i].Move();
outFileSystem.SetByMove(ref cachedFs.Ref());
return true;
@@ -116,36 +115,36 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
return false;
}
- public void Register(ref SharedRef fileSystem)
+ public void Register(ref SharedRef fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
{
- // Don't cache temporary save data
- using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
- fileSystem.Reset();
- }
+ Assert.SdkRequiresNotNull(in fileSystem);
- public void Register(ref SharedRef fileSystem)
- {
if (_maxCachedFileSystemCount <= 0)
return;
Assert.SdkRequiresGreaterEqual(_nextCacheIndex, 0);
Assert.SdkRequiresGreater(_maxCachedFileSystemCount, _nextCacheIndex);
- if (fileSystem.Get.GetSaveDataSpaceId() == SaveDataSpaceId.SdSystem)
+ if (!fileSystem.Get.IsSaveDataFileSystemCacheEnabled())
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+ fileSystem.Reset();
+ }
+ else if (spaceId == SaveDataSpaceId.SdSystem)
{
- // Don't cache system save data
using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
fileSystem.Reset();
}
else
{
Result rc = fileSystem.Get.RollbackOnlyModified();
- if (rc.IsFailure()) return;
+ if (rc.IsSuccess())
+ {
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
- using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
-
- _cachedFileSystems[_nextCacheIndex].Register(ref fileSystem);
- _nextCacheIndex = (_nextCacheIndex + 1) % _maxCachedFileSystemCount;
+ _cachedFileSystems[_nextCacheIndex].Register(ref fileSystem, spaceId, saveDataId);
+ _nextCacheIndex = (_nextCacheIndex + 1) % _maxCachedFileSystemCount;
+ }
}
}
@@ -163,9 +162,4 @@ public class SaveDataFileSystemCacheManager : ISaveDataFileSystemCacheManager
}
}
}
-
- public UniqueLockRef GetScopedLock()
- {
- return new UniqueLockRef(ref _mutex);
- }
}
\ 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..a1fc90ce
--- /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/SaveDataProperties.cs b/src/LibHac/FsSrv/Impl/SaveDataProperties.cs
index 82070e27..dec1956e 100644
--- a/src/LibHac/FsSrv/Impl/SaveDataProperties.cs
+++ b/src/LibHac/FsSrv/Impl/SaveDataProperties.cs
@@ -8,6 +8,20 @@ public static class SaveDataProperties
public const long DefaultSaveDataBlockSize = 0x4000;
public const long BcatSaveDataJournalSize = 0x200000;
+ public static bool IsJournalingSupported(SaveDataFormatType type)
+ {
+ switch (type)
+ {
+ case SaveDataFormatType.Normal:
+ return true;
+ case SaveDataFormatType.NoJournal:
+ return false;
+ default:
+ Abort.UnexpectedDefault();
+ return default;
+ }
+ }
+
public static bool IsJournalingSupported(SaveDataType type)
{
switch (type)
@@ -67,7 +81,6 @@ public static class SaveDataProperties
switch (type)
{
case SaveDataType.System:
- case SaveDataType.SystemBcat:
return true;
case SaveDataType.Account:
case SaveDataType.Bcat:
@@ -86,7 +99,6 @@ public static class SaveDataProperties
switch (type)
{
case SaveDataType.System:
- case SaveDataType.SystemBcat:
return true;
case SaveDataType.Account:
case SaveDataType.Bcat:
@@ -99,4 +111,36 @@ public static class SaveDataProperties
return default;
}
}
-}
+
+ public static bool IsReconstructible(SaveDataType type, SaveDataSpaceId spaceId)
+ {
+ switch (spaceId)
+ {
+ case SaveDataSpaceId.System:
+ case SaveDataSpaceId.User:
+ case SaveDataSpaceId.ProperSystem:
+ case SaveDataSpaceId.SafeMode:
+ switch (type)
+ {
+ case SaveDataType.System:
+ case SaveDataType.Account:
+ case SaveDataType.Device:
+ return false;
+ case SaveDataType.Bcat:
+ case SaveDataType.Temporary:
+ case SaveDataType.Cache:
+ return true;
+ default:
+ Abort.UnexpectedDefault();
+ return default;
+ }
+ case SaveDataSpaceId.SdSystem:
+ case SaveDataSpaceId.Temporary:
+ case SaveDataSpaceId.SdUser:
+ return true;
+ default:
+ Abort.UnexpectedDefault();
+ return default;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs
deleted file mode 100644
index d49eab27..00000000
--- a/src/LibHac/FsSrv/Impl/SaveDataResultConvertFileSystem.cs
+++ /dev/null
@@ -1,248 +0,0 @@
-using LibHac.Common;
-using LibHac.Diag;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
-
-namespace LibHac.FsSrv.Impl;
-
-///
-/// Contains functions for converting internal save data s to external s.
-///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
-public static class SaveDataResultConvert
-{
- private static Result ConvertCorruptedResult(Result result)
- {
- if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result))
- {
- if (ResultFs.IncorrectIntegrityVerificationMagicCode.Includes(result))
- return ResultFs.IncorrectSaveDataIntegrityVerificationMagicCode.LogConverted(result);
-
- if (ResultFs.InvalidZeroHash.Includes(result))
- return ResultFs.InvalidSaveDataZeroHash.LogConverted(result);
-
- if (ResultFs.NonRealDataVerificationFailed.Includes(result))
- return ResultFs.SaveDataNonRealDataVerificationFailed.LogConverted(result);
-
- if (ResultFs.ClearedRealDataVerificationFailed.Includes(result))
- return ResultFs.ClearedSaveDataRealDataVerificationFailed.LogConverted(result);
-
- if (ResultFs.UnclearedRealDataVerificationFailed.Includes(result))
- return ResultFs.UnclearedSaveDataRealDataVerificationFailed.LogConverted(result);
-
- Assert.SdkAssert(false);
- }
-
- if (ResultFs.HostFileSystemCorrupted.Includes(result))
- {
- if (ResultFs.HostEntryCorrupted.Includes(result))
- return ResultFs.SaveDataHostEntryCorrupted.LogConverted(result);
-
- if (ResultFs.HostFileDataCorrupted.Includes(result))
- return ResultFs.SaveDataHostFileDataCorrupted.LogConverted(result);
-
- if (ResultFs.HostFileCorrupted.Includes(result))
- return ResultFs.SaveDataHostFileCorrupted.LogConverted(result);
-
- if (ResultFs.InvalidHostHandle.Includes(result))
- return ResultFs.InvalidSaveDataHostHandle.LogConverted(result);
-
- Assert.SdkAssert(false);
- }
-
- if (ResultFs.DatabaseCorrupted.Includes(result))
- {
- if (ResultFs.InvalidAllocationTableBlock.Includes(result))
- return ResultFs.InvalidSaveDataAllocationTableBlock.LogConverted(result);
-
- if (ResultFs.InvalidKeyValueListElementIndex.Includes(result))
- return ResultFs.InvalidSaveDataKeyValueListElementIndex.LogConverted(result);
-
- if (ResultFs.InvalidAllocationTableChainEntry.Includes(result))
- return ResultFs.InvalidSaveDataAllocationTableChainEntry.LogConverted(result);
-
- if (ResultFs.InvalidAllocationTableOffset.Includes(result))
- return ResultFs.InvalidSaveDataAllocationTableOffset.LogConverted(result);
-
- if (ResultFs.InvalidAllocationTableBlockCount.Includes(result))
- return ResultFs.InvalidSaveDataAllocationTableBlockCount.LogConverted(result);
-
- if (ResultFs.InvalidKeyValueListEntryIndex.Includes(result))
- return ResultFs.InvalidSaveDataKeyValueListEntryIndex.LogConverted(result);
-
- if (ResultFs.InvalidBitmapIndex.Includes(result))
- return ResultFs.InvalidSaveDataBitmapIndex.LogConverted(result);
-
- Assert.SdkAssert(false);
- }
-
- if (ResultFs.ZeroBitmapFileCorrupted.Includes(result))
- {
- if (ResultFs.IncompleteBlockInZeroBitmapHashStorageFile.Includes(result))
- return ResultFs.IncompleteBlockInZeroBitmapHashStorageFileSaveData.LogConverted(result);
-
- Assert.SdkAssert(false);
- }
-
- return result;
- }
-
- public static Result ConvertSaveFsDriverPrivateResult(Result result)
- {
- if (result.IsSuccess())
- return result;
-
- if (ResultFs.UnsupportedVersion.Includes(result))
- return ResultFs.UnsupportedSaveDataVersion.LogConverted(result);
-
- if (ResultFs.IntegrityVerificationStorageCorrupted.Includes(result) ||
- ResultFs.BuiltInStorageCorrupted.Includes(result) ||
- ResultFs.HostFileSystemCorrupted.Includes(result) ||
- ResultFs.DatabaseCorrupted.Includes(result) ||
- ResultFs.ZeroBitmapFileCorrupted.Includes(result))
- {
- return ConvertCorruptedResult(result);
- }
-
- if (ResultFs.FatFileSystemCorrupted.Includes(result))
- return result;
-
- if (ResultFs.NotFound.Includes(result))
- return ResultFs.PathNotFound.LogConverted(result);
-
- if (ResultFs.AllocationTableFull.Includes(result))
- return ResultFs.UsableSpaceNotEnough.LogConverted(result);
-
- if (ResultFs.AlreadyExists.Includes(result))
- return ResultFs.PathAlreadyExists.LogConverted(result);
-
- if (ResultFs.InvalidOffset.Includes(result))
- return ResultFs.OutOfRange.LogConverted(result);
-
- if (ResultFs.IncompatiblePath.Includes(result) ||
- ResultFs.FileNotFound.Includes(result))
- {
- return ResultFs.PathNotFound.LogConverted(result);
- }
-
- return result;
- }
-}
-
-///
-/// Wraps an , converting its returned s
-/// to save-data-specific s.
-///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
-public class SaveDataResultConvertFile : IResultConvertFile
-{
- public SaveDataResultConvertFile(ref UniqueRef baseFile) : base(ref baseFile)
- {
- }
-
- protected override Result ConvertResult(Result result)
- {
- 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 SaveDataResultConvertDirectory : IResultConvertDirectory
-{
- public SaveDataResultConvertDirectory(ref UniqueRef baseDirectory) : base(ref baseDirectory)
- {
- }
-
- protected override Result ConvertResult(Result result)
- {
- 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 SaveDataResultConvertFileSystem : IResultConvertFileSystem
-{
- public SaveDataResultConvertFileSystem(ref SharedRef baseFileSystem)
- : base(ref baseFileSystem)
- {
- }
-
- protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode)
- {
- using var file = new UniqueRef();
- Result rc = ConvertResult(BaseFileSystem.Get.OpenFile(ref file.Ref(), path, mode));
- if (rc.IsFailure()) return rc;
-
- outFile.Reset(new SaveDataResultConvertFile(ref file.Ref()));
- return Result.Success;
- }
-
- protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
- OpenDirectoryMode mode)
- {
- using var directory = new UniqueRef();
- Result rc = ConvertResult(BaseFileSystem.Get.OpenDirectory(ref directory.Ref(), path, mode));
- if (rc.IsFailure()) return rc;
-
- outDirectory.Reset(new SaveDataResultConvertDirectory(ref directory.Ref()));
- return Result.Success;
- }
-
- protected override Result ConvertResult(Result result)
- {
- 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..0876cfaa 100644
--- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
+++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
@@ -116,6 +116,7 @@ public class SaveDataFileSystemServiceImpl
}
}
+ // 14.3.0
public Result OpenSaveDataFileSystem(ref SharedRef outFileSystem, SaveDataSpaceId spaceId,
ulong saveDataId, in Path saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
{
@@ -124,66 +125,65 @@ public class SaveDataFileSystemServiceImpl
Result rc = OpenSaveDataDirectoryFileSystem(ref fileSystem.Ref(), spaceId, in saveDataRootPath, true);
if (rc.IsFailure()) return rc.Miss();
- bool allowDirectorySaveData = IsAllowedDirectorySaveData2(spaceId, in saveDataRootPath);
+ bool isEmulatedOnHost = IsAllowedDirectorySaveData(spaceId, in saveDataRootPath);
- // Note: When directory save data is allowed, Nintendo creates the save directory if it doesn't exist.
- // This bypasses normal save data creation, leaving the save with empty extra data.
- // Instead, we return that the save doesn't exist if the directory is missing.
-
- using var saveDataFs = new SharedRef();
- using var cachedFs = new SharedRef();
-
- // Note: Nintendo doesn't cache directory save data
- // if (!allowDirectorySaveData)
+ if (isEmulatedOnHost)
{
- // Check if we have the requested file system cached
- if (_saveDataFsCacheManager.GetCache(ref cachedFs.Ref(), spaceId, saveDataId))
- {
- using var registerBase = new SharedRef(
- new SaveDataFileSystemCacheRegisterBase(ref cachedFs.Ref(),
- _saveDataFsCacheManager));
-
- using var resultConvertFs = new SharedRef(
- new SaveDataResultConvertFileSystem(ref registerBase.Ref()));
-
- saveDataFs.SetByMove(ref resultConvertFs.Ref());
- }
- }
-
- // Create a new file system if it's not in the cache
- if (!saveDataFs.HasValue)
- {
- using UniqueLockRef scopedLock = _extraDataCacheManager.GetScopedLock();
- using var extraDataAccessor = new SharedRef();
-
- bool openShared = SaveDataProperties.IsSharedOpenNeeded(type);
- bool isMultiCommitSupported = SaveDataProperties.IsMultiCommitSupported(type);
- bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(type);
- bool useDeviceUniqueMac = IsDeviceUniqueMac(spaceId);
-
- rc = _config.SaveFsCreator.Create(ref saveDataFs.Ref(), ref extraDataAccessor.Ref(),
- _saveDataFsCacheManager, ref fileSystem.Ref(), spaceId, saveDataId, allowDirectorySaveData,
- useDeviceUniqueMac, isJournalingSupported, isMultiCommitSupported, openReadOnly, openShared,
- _timeStampGetter);
+ // Create the save data directory on the host if needed.
+ Unsafe.SkipInit(out Array18 saveDirectoryNameBuffer);
+ using var saveDirectoryName = new Path();
+ rc = PathFunctions.SetUpFixedPathSaveId(ref saveDirectoryName.Ref(), saveDirectoryNameBuffer.Items, saveDataId);
if (rc.IsFailure()) return rc.Miss();
- // Cache the extra data accessor if needed
- if (cacheExtraData && extraDataAccessor.HasValue)
+ rc = FsSystem.Utility.EnsureDirectory(fileSystem.Get, in saveDirectoryName);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ using var saveDataFs = new SharedRef();
+
+ using (_saveDataFsCacheManager.GetScopedLock())
+ using (_extraDataCacheManager.GetScopedLock())
+ {
+ if (isEmulatedOnHost || !_saveDataFsCacheManager.GetCache(ref saveDataFs.Ref(), spaceId, saveDataId))
{
- extraDataAccessor.Get.RegisterCacheObserver(_extraDataCacheManager, spaceId, saveDataId);
+ bool isDeviceUniqueMac = IsDeviceUniqueMac(spaceId);
+ bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(type);
+ bool isMultiCommitSupported = SaveDataProperties.IsMultiCommitSupported(type);
+ bool openShared = SaveDataProperties.IsSharedOpenNeeded(type);
+ bool isReconstructible = SaveDataProperties.IsReconstructible(type, spaceId);
+
+ rc = _config.SaveFsCreator.Create(ref saveDataFs.Ref(), ref fileSystem.Ref(), spaceId, saveDataId,
+ isEmulatedOnHost, isDeviceUniqueMac, isJournalingSupported, isMultiCommitSupported,
+ openReadOnly, openShared, _timeStampGetter, isReconstructible);
+ if (rc.IsFailure()) return rc.Miss();
+ }
+
+ if (!isEmulatedOnHost && cacheExtraData)
+ {
+ using SharedRef extraDataAccessor =
+ SharedRef.CreateCopy(in saveDataFs);
rc = _extraDataCacheManager.Register(in extraDataAccessor, spaceId, saveDataId);
if (rc.IsFailure()) return rc.Miss();
}
}
+ using var registerFs = new SharedRef(
+ new SaveDataFileSystemCacheRegister(ref saveDataFs.Ref(), _saveDataFsCacheManager, spaceId, saveDataId));
+
if (openReadOnly)
{
- outFileSystem.Reset(new ReadOnlyFileSystem(ref saveDataFs.Ref()));
+ using SharedRef tempFs = SharedRef.CreateMove(ref registerFs.Ref());
+ using var readOnlyFileSystem = new SharedRef(new ReadOnlyFileSystem(ref tempFs.Ref()));
+
+ if (!readOnlyFileSystem.HasValue)
+ return ResultFs.AllocationMemoryFailedInSaveDataFileSystemServiceImplB.Log();
+
+ outFileSystem.SetByMove(ref readOnlyFileSystem.Ref());
}
else
{
- outFileSystem.SetByMove(ref saveDataFs.Ref());
+ outFileSystem.SetByMove(ref registerFs.Ref());
}
return Result.Success;
@@ -339,15 +339,15 @@ public class SaveDataFileSystemServiceImpl
rc = FsSystem.Utility.EnsureDirectory(fileSystem.Get, in saveImageName);
if (rc.IsFailure()) return rc.Miss();
- using var saveFileSystem = new SharedRef();
- using var extraDataAccessor = new SharedRef();
+ using var saveFileSystem = new SharedRef();
bool isJournalingSupported = SaveDataProperties.IsJournalingSupported(attribute.Type);
+ bool isReconstructible = SaveDataProperties.IsReconstructible(attribute.Type, creationInfo.SpaceId);
- rc = _config.SaveFsCreator.Create(ref saveFileSystem.Ref(), ref extraDataAccessor.Ref(),
- _saveDataFsCacheManager, ref fileSystem.Ref(), creationInfo.SpaceId, saveDataId,
- allowDirectorySaveData: true, useDeviceUniqueMac: false, isJournalingSupported,
- isMultiCommitSupported: false, openReadOnly: false, openShared: false, _timeStampGetter);
+ rc = _config.SaveFsCreator.Create(ref saveFileSystem.Ref(), ref fileSystem.Ref(), creationInfo.SpaceId,
+ saveDataId, allowDirectorySaveData: true, isDeviceUniqueMac: false, isJournalingSupported,
+ isMultiCommitSupported: false, openReadOnly: false, openShared: false, _timeStampGetter,
+ isReconstructible);
if (rc.IsFailure()) return rc.Miss();
var extraData = new SaveDataExtraData();
@@ -359,16 +359,16 @@ public class SaveDataFileSystemServiceImpl
extraData.TimeStamp = 0;
extraData.CommitId = 0;
- _config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId)).IgnoreResult();
+ _config.GenerateRandomData(SpanHelpers.AsByteSpan(ref extraData.CommitId));
extraData.Flags = creationInfo.Flags;
extraData.DataSize = creationInfo.Size;
extraData.JournalSize = creationInfo.JournalSize;
- rc = extraDataAccessor.Get.WriteExtraData(in extraData);
+ rc = saveFileSystem.Get.WriteExtraData(in extraData);
if (rc.IsFailure()) return rc.Miss();
- rc = extraDataAccessor.Get.CommitExtraData(true);
+ rc = saveFileSystem.Get.CommitExtraData(true);
if (rc.IsFailure()) return rc.Miss();
}
else
@@ -552,8 +552,8 @@ public class SaveDataFileSystemServiceImpl
using (var tmFileSystem = new SharedRef())
{
// Ensure the target save data directory exists
- rc = _config.TargetManagerFsCreator.Create(ref tmFileSystem.Ref(), in saveDataRootPath, false, true,
- ResultFs.SaveDataRootPathUnavailable.Value);
+ rc = _config.TargetManagerFsCreator.Create(ref tmFileSystem.Ref(), in saveDataRootPath,
+ openCaseSensitive: false, ensureRootPathExists: true, ResultFs.SaveDataRootPathUnavailable.Value);
if (rc.IsFailure()) return rc.Miss();
}
@@ -564,8 +564,8 @@ public class SaveDataFileSystemServiceImpl
rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, ref path.Ref());
if (rc.IsFailure()) return rc.Miss();
- rc = _config.TargetManagerFsCreator.Create(ref outFileSystem, in path, isTargetFsCaseSensitive, false,
- ResultFs.SaveDataRootPathUnavailable.Value);
+ rc = _config.TargetManagerFsCreator.Create(ref outFileSystem, in path, isTargetFsCaseSensitive,
+ ensureRootPathExists: false, ResultFs.SaveDataRootPathUnavailable.Value);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
@@ -696,20 +696,14 @@ public class SaveDataFileSystemServiceImpl
throw new NotImplementedException();
}
+ ///
+ /// Checks if a save is to be stored on a host device.
+ ///
public bool IsAllowedDirectorySaveData(SaveDataSpaceId spaceId, in Path saveDataRootPath)
{
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath);
}
- // Todo: remove once file save data is supported
- // Used to always allow directory save data in OpenSaveDataFileSystem
- public bool IsAllowedDirectorySaveData2(SaveDataSpaceId spaceId, in Path saveDataRootPath)
- {
- // Todo: remove "|| true" once file save data is supported
- // ReSharper disable once ConditionIsAlwaysTrueOrFalse
- return spaceId == SaveDataSpaceId.User && IsSaveEmulated(in saveDataRootPath) || true;
- }
-
public bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
{
return spaceId == SaveDataSpaceId.System ||
diff --git a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs
index c96ea630..87de79d4 100644
--- a/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs
+++ b/src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs
@@ -83,8 +83,9 @@ public class ApplicationTemporaryFileSystem : IFileSystem, ISaveDataExtraDataAcc
throw new NotImplementedException();
}
- public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId)
+ public void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId,
+ ulong saveDataId)
{
throw new NotImplementedException();
}
-}
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/Delegates.cs b/src/LibHac/FsSystem/Delegates.cs
index 89a10d26..d2a2976d 100644
--- a/src/LibHac/FsSystem/Delegates.cs
+++ b/src/LibHac/FsSystem/Delegates.cs
@@ -2,4 +2,4 @@
namespace LibHac.FsSystem;
-public delegate Result RandomDataGenerator(Span buffer);
\ No newline at end of file
+public delegate void RandomDataGenerator(Span buffer);
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs
index 7e610c01..dc657340 100644
--- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs
+++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs
@@ -14,7 +14,7 @@ internal struct DirectorySaveDataFileSystemGlobals
public void Initialize(FileSystemClient fsClient)
{
- SynchronizeDirectoryMutex.Initialize();
+ SynchronizeDirectoryMutex = new SdkMutexType();
}
}
@@ -24,9 +24,9 @@ internal struct DirectorySaveDataFileSystemGlobals
///
/// Transactional commits should be atomic as long as the function of the
/// underlying is atomic.
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
///
-public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccessor
+public class DirectorySaveDataFileSystem : ISaveDataFileSystem
{
private const int IdealWorkBufferSize = 0x100000; // 1 MiB
@@ -35,7 +35,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
private static ReadOnlySpan SynchronizingDirectoryName => new[] { (byte)'/', (byte)'_' };
private static ReadOnlySpan LockFileName => new[] { (byte)'/', (byte)'.', (byte)'l', (byte)'o', (byte)'c', (byte)'k' };
- private FileSystemClient _fsClient;
private IFileSystem _baseFs;
private SdkMutexType _mutex;
private UniqueRef _uniqueBaseFs;
@@ -45,16 +44,17 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
private bool _isMultiCommitSupported;
private bool _isJournalingEnabled;
- // Additions to support extra data
+ private ISaveDataExtraDataAccessorObserver _cacheObserver;
+ private ulong _saveDataId;
+ private SaveDataSpaceId _spaceId;
+
private ISaveDataCommitTimeStampGetter _timeStampGetter;
private RandomDataGenerator _randomGenerator;
- // Additions to support caching
- private ISaveDataExtraDataAccessorCacheObserver _cacheObserver;
- private SaveDataSpaceId _spaceId;
- private ulong _saveDataId;
+ // LibHac additions
+ private FileSystemClient _fsClient;
- // 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 _lockFile;
private class DirectorySaveDataFile : IFile
@@ -116,27 +116,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
}
}
- ///
- /// Create an uninitialized .
- ///
- /// The base to use.
- public DirectorySaveDataFileSystem(IFileSystem baseFileSystem)
- {
- _baseFs = baseFileSystem;
- _mutex.Initialize();
- }
-
- ///
- /// Create an uninitialized .
- ///
- /// The base to use.
- public DirectorySaveDataFileSystem(ref UniqueRef baseFileSystem)
- {
- _baseFs = baseFileSystem.Get;
- _mutex.Initialize();
- _uniqueBaseFs = new UniqueRef(ref baseFileSystem);
- }
-
///
/// Create an uninitialized .
/// If a is provided a global mutex will be used when synchronizing directories.
@@ -145,10 +124,10 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
///
/// The base to use.
/// The to use. May be null.
- public DirectorySaveDataFileSystem(IFileSystem baseFileSystem, FileSystemClient fsClient)
+ public DirectorySaveDataFileSystem(IFileSystem baseFileSystem, FileSystemClient fsClient = null)
{
_baseFs = baseFileSystem;
- _mutex.Initialize();
+ _mutex = new SdkMutexType();
_fsClient = fsClient;
}
@@ -160,10 +139,10 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
///
/// The base to use.
/// The to use. May be null.
- public DirectorySaveDataFileSystem(ref UniqueRef baseFileSystem, FileSystemClient fsClient)
+ public DirectorySaveDataFileSystem(ref UniqueRef baseFileSystem, FileSystemClient fsClient = null)
{
_baseFs = baseFileSystem.Get;
- _mutex.Initialize();
+ _mutex = new SdkMutexType();
_uniqueBaseFs = new UniqueRef(ref baseFileSystem);
_fsClient = fsClient;
}
@@ -185,6 +164,14 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
public Path ModifiedPath;
public Path SynchronizingPath;
+ public RetryClosure(DirectorySaveDataFileSystem fs)
+ {
+ This = fs;
+ CommittedPath = new Path();
+ ModifiedPath = new Path();
+ SynchronizingPath = new Path();
+ }
+
public void Dispose()
{
CommittedPath.Dispose();
@@ -195,7 +182,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
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 retryWaitTimeMs = 100;
@@ -228,22 +215,17 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
}
}
- public Result Initialize(bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
- {
- return Initialize(null, null, isJournalingSupported, isMultiCommitSupported, isJournalingEnabled);
- }
-
- public Result Initialize(ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator,
- bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
+ public Result Initialize(bool isJournalingSupported, bool isMultiCommitSupported,
+ bool isJournalingEnabled, ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator)
{
_isJournalingSupported = isJournalingSupported;
_isMultiCommitSupported = isMultiCommitSupported;
_isJournalingEnabled = isJournalingEnabled;
- _timeStampGetter = timeStampGetter ?? _timeStampGetter;
- _randomGenerator = randomGenerator ?? _randomGenerator;
+ _timeStampGetter = timeStampGetter;
+ _randomGenerator = randomGenerator;
// Open the lock file
- Result rc = GetFileSystemLock();
+ Result rc = AcquireLockFile();
if (rc.IsFailure()) return rc;
using var pathModifiedDirectory = new Path();
@@ -273,8 +255,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
{
rc = _baseFs.CreateDirectory(in pathCommittedDirectory);
- // Nintendo returns on all failures, but we'll keep going if committed already exists
- // to avoid confusing people manually creating savedata in emulators
+ // Changed: Nintendo returns on all failures, but we'll keep going if committed already
+ // exists to avoid confusing people manually creating savedata in emulators
if (rc.IsFailure() && !ResultFs.PathAlreadyExists.Includes(rc))
return rc;
}
@@ -316,7 +298,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
return Result.Success;
}
- private Result GetFileSystemLock()
+ private Result AcquireLockFile()
{
// Having an open lock file means we already have the lock for the file system.
if (_lockFile.HasValue)
@@ -326,7 +308,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
Result rc = PathFunctions.SetUpFixedPath(ref pathLockFile.Ref(), LockFileName);
if (rc.IsFailure()) return rc;
- rc = _baseFs.OpenFile(ref _lockFile, in pathLockFile, OpenMode.ReadWrite);
+ using var lockFile = new UniqueRef();
+ rc = _baseFs.OpenFile(ref lockFile.Ref(), in pathLockFile, OpenMode.ReadWrite);
if (rc.IsFailure())
{
@@ -335,15 +318,16 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
rc = _baseFs.CreateFile(in pathLockFile, 0);
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;
}
else
{
- return rc;
+ return rc.Miss();
}
}
+ _lockFile.Set(ref lockFile.Ref());
return Result.Success;
}
@@ -552,8 +536,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
// Delete destination dir and recreate it.
Result rc = _baseFs.DeleteDirectoryRecursively(destPath);
- // Nintendo returns all errors unconditionally because SynchronizeDirectory is always called in situations
- // where a PathNotFound error would mean the save directory was in an invalid state.
+ // Changed: Nintendo returns all errors unconditionally because SynchronizeDirectory is always called
+ // 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
// put the save directory in an invalid state.
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc)) return rc;
@@ -563,7 +547,7 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
var directoryEntry = new DirectoryEntry();
- // Lock only if initialized with a client
+ // Changed: Lock only if initialized with a client
if (_fsClient is not null)
{
using ScopedLock scopedLock =
@@ -585,63 +569,63 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
}
}
- protected override Result DoCommit()
+ private Result DoCommit(bool updateTimeStamp)
{
- using ScopedLock lk = ScopedLock.Lock(ref _mutex);
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
if (!_isJournalingEnabled || !_isJournalingSupported)
return Result.Success;
- var closure = new RetryClosure();
- closure.This = this;
+ using var closure = new RetryClosure(this);
- Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath, ModifiedDirectoryName);
+ Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath.Ref(), ModifiedDirectoryName);
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;
- rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath, SynchronizingDirectoryName);
+ rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath.Ref(), SynchronizingDirectoryName);
if (rc.IsFailure()) return rc;
+ // All files must be closed before commiting save data.
if (_openWritableFileCount > 0)
- {
- // All files must be closed before commiting save data.
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.
- 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 something goes wrong beyond this point, the commit will be
- // completed the next time the savedata is opened.
+ // If something goes wrong beyond this point, the commit of the main data
+ // 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;
- 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;
- closure.Dispose();
+ return Result.Success;
+ }
+
+ protected override Result DoCommit()
+ {
+ Result rc = DoCommit(updateTimeStamp: true);
+ if (rc.IsFailure()) return rc.Miss();
+
return Result.Success;
}
@@ -655,11 +639,15 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
protected override Result DoRollback()
{
- // No old data is kept for non-journaling save data, so there's nothing to rollback to
- if (!_isJournalingSupported)
- return Result.Success;
+ // No old data is kept for non-journaling save data, so there's nothing to rollback to in that case
+ if (_isJournalingSupported)
+ {
+ 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)
@@ -694,6 +682,46 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
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 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 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()
{
// Todo?: Calling OpenFile when outFile already contains a DirectorySaveDataFile
@@ -703,7 +731,8 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
_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.
private static ReadOnlySpan CommittedExtraDataName => // "/ExtraData0"
@@ -860,6 +889,15 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
return Result.Success;
}
+ private Result GetExtraDataPath(ref Path path)
+ {
+ ReadOnlySpan extraDataName = _isJournalingSupported && !_isJournalingEnabled
+ ? CommittedExtraDataName
+ : ModifiedExtraDataName;
+
+ return PathFunctions.SetUpFixedPath(ref path, extraDataName);
+ }
+
private Result EnsureExtraDataSize(in Path path)
{
using var file = new UniqueRef();
@@ -902,42 +940,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
return Result.Success;
}
- private Result GetExtraDataPath(ref Path path)
- {
- ReadOnlySpan extraDataName = _isJournalingSupported && !_isJournalingEnabled
- ? CommittedExtraDataName
- : ModifiedExtraDataName;
-
- return PathFunctions.SetUpFixedPath(ref path, extraDataName);
- }
-
- public Result WriteExtraData(in SaveDataExtraData extraData)
- {
- using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
-
- return WriteExtraDataImpl(in extraData);
- }
-
- public Result CommitExtraData(bool updateTimeStamp)
- {
- using ScopedLock 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 scopedLock = ScopedLock.Lock(ref _mutex);
-
- return ReadExtraDataImpl(out extraData);
- }
-
private Result UpdateExtraDataTimeStamp()
{
Assert.SdkRequires(_mutex.IsLockedByCurrentThread());
@@ -987,50 +989,33 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
if (!_isJournalingSupported || !_isJournalingEnabled)
return Result.Success;
- var closure = new RetryClosure();
- closure.This = this;
+ using var closure = new RetryClosure(this);
- Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath, ModifiedExtraDataName);
+ Result rc = PathFunctions.SetUpFixedPath(ref closure.ModifiedPath.Ref(), ModifiedExtraDataName);
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;
- rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath, SynchronizingExtraDataName);
+ rc = PathFunctions.SetUpFixedPath(ref closure.SynchronizingPath.Ref(), SynchronizingExtraDataName);
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.
- 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 something goes wrong beyond this point, the commit will be
// 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;
- 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;
- closure.Dispose();
return Result.Success;
}
@@ -1056,14 +1041,6 @@ public class DirectorySaveDataFileSystem : IFileSystem, ISaveDataExtraDataAccess
return Result.Success;
}
- public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId,
- ulong saveDataId)
- {
- _cacheObserver = observer;
- _spaceId = spaceId;
- _saveDataId = saveDataId;
- }
-
public SaveDataSpaceId GetSaveDataSpaceId() => _spaceId;
public ulong GetSaveDataId() => _saveDataId;
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/IResultConvertFileSystem.cs b/src/LibHac/FsSystem/IResultConvertFileSystem.cs
similarity index 54%
rename from src/LibHac/FsSrv/Impl/IResultConvertFileSystem.cs
rename to src/LibHac/FsSystem/IResultConvertFileSystem.cs
index 2f361d21..f65d3525 100644
--- a/src/LibHac/FsSrv/Impl/IResultConvertFileSystem.cs
+++ b/src/LibHac/FsSystem/IResultConvertFileSystem.cs
@@ -3,61 +3,62 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
-namespace LibHac.FsSrv.Impl;
+namespace LibHac.FsSystem;
// ReSharper disable once InconsistentNaming
///
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
public abstract class IResultConvertFile : IFile
{
- protected UniqueRef BaseFile;
+ private UniqueRef _baseFile;
protected IResultConvertFile(ref UniqueRef baseFile)
{
- BaseFile = new UniqueRef(ref baseFile);
+ _baseFile = new UniqueRef(ref baseFile);
}
public override void Dispose()
{
- BaseFile.Destroy();
+ _baseFile.Destroy();
+
base.Dispose();
}
+ protected abstract Result ConvertResult(Result result);
+
protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option)
{
- return ConvertResult(BaseFile.Get.Read(out bytesRead, offset, destination, option));
+ return ConvertResult(_baseFile.Get.Read(out bytesRead, offset, destination, in option)).Ret();
}
protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
{
- return ConvertResult(BaseFile.Get.Write(offset, source, option));
+ return ConvertResult(_baseFile.Get.Write(offset, source, in option)).Ret();
}
protected override Result DoFlush()
{
- return ConvertResult(BaseFile.Get.Flush());
+ return ConvertResult(_baseFile.Get.Flush()).Ret();
}
protected override Result DoSetSize(long size)
{
- return ConvertResult(BaseFile.Get.SetSize(size));
+ return ConvertResult(_baseFile.Get.SetSize(size)).Ret();
}
protected override Result DoGetSize(out long size)
{
- return ConvertResult(BaseFile.Get.GetSize(out size));
+ return ConvertResult(_baseFile.Get.GetSize(out size)).Ret();
}
protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan inBuffer)
{
- return ConvertResult(BaseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer));
+ return ConvertResult(_baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer)).Ret();
}
-
- protected abstract Result ConvertResult(Result result);
}
// ReSharper disable once InconsistentNaming
@@ -65,33 +66,34 @@ public abstract class IResultConvertFile : IFile
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
public abstract class IResultConvertDirectory : IDirectory
{
- protected UniqueRef BaseDirectory;
+ private UniqueRef _baseDirectory;
protected IResultConvertDirectory(ref UniqueRef baseDirectory)
{
- BaseDirectory = new UniqueRef(ref baseDirectory);
+ _baseDirectory = new UniqueRef(ref baseDirectory);
}
public override void Dispose()
{
- BaseDirectory.Destroy();
+ _baseDirectory.Destroy();
+
base.Dispose();
}
+ protected abstract Result ConvertResult(Result result);
+
protected override Result DoRead(out long entriesRead, Span entryBuffer)
{
- return ConvertResult(BaseDirectory.Get.Read(out entriesRead, entryBuffer));
+ return ConvertResult(_baseDirectory.Get.Read(out entriesRead, entryBuffer)).Ret();
}
protected override Result DoGetEntryCount(out long entryCount)
{
- return ConvertResult(BaseDirectory.Get.GetEntryCount(out entryCount));
+ return ConvertResult(_baseDirectory.Get.GetEntryCount(out entryCount)).Ret();
}
-
- protected abstract Result ConvertResult(Result result);
}
// ReSharper disable once InconsistentNaming
@@ -99,114 +101,110 @@ public abstract class IResultConvertDirectory : IDirectory
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
-public abstract class IResultConvertFileSystem : IFileSystem
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
+public abstract class IResultConvertFileSystem : ISaveDataFileSystem where T : IFileSystem
{
- protected SharedRef BaseFileSystem;
+ private SharedRef _baseFileSystem;
- protected IResultConvertFileSystem(ref SharedRef baseFileSystem)
+ protected IResultConvertFileSystem(ref SharedRef baseFileSystem)
{
- BaseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
+ _baseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
}
public override void Dispose()
{
- BaseFileSystem.Destroy();
+ _baseFileSystem.Destroy();
+
base.Dispose();
}
+ protected T GetFileSystem() => _baseFileSystem.Get;
+
+ protected abstract Result ConvertResult(Result result);
+
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
{
- return ConvertResult(BaseFileSystem.Get.CreateFile(path, size, option));
+ return ConvertResult(_baseFileSystem.Get.CreateFile(in path, size)).Ret();
}
protected override Result DoDeleteFile(in Path path)
{
- return ConvertResult(BaseFileSystem.Get.DeleteFile(path));
+ return ConvertResult(_baseFileSystem.Get.DeleteFile(in path)).Ret();
}
protected override Result DoCreateDirectory(in Path path)
{
- return ConvertResult(BaseFileSystem.Get.CreateDirectory(path));
+ return ConvertResult(_baseFileSystem.Get.CreateDirectory(in path)).Ret();
}
protected override Result DoDeleteDirectory(in Path path)
{
- return ConvertResult(BaseFileSystem.Get.DeleteDirectory(path));
+ return ConvertResult(_baseFileSystem.Get.DeleteDirectory(in path)).Ret();
}
protected override Result DoDeleteDirectoryRecursively(in Path path)
{
- return ConvertResult(BaseFileSystem.Get.DeleteDirectoryRecursively(path));
+ return ConvertResult(_baseFileSystem.Get.DeleteDirectoryRecursively(in path)).Ret();
}
protected override Result DoCleanDirectoryRecursively(in Path path)
{
- return ConvertResult(BaseFileSystem.Get.CleanDirectoryRecursively(path));
+ return ConvertResult(_baseFileSystem.Get.CleanDirectoryRecursively(in path)).Ret();
}
protected override Result DoRenameFile(in Path currentPath, in Path newPath)
{
- return ConvertResult(BaseFileSystem.Get.RenameFile(currentPath, newPath));
+ return ConvertResult(_baseFileSystem.Get.RenameFile(in currentPath, in newPath)).Ret();
}
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
{
- return ConvertResult(BaseFileSystem.Get.RenameDirectory(currentPath, newPath));
+ return ConvertResult(_baseFileSystem.Get.RenameDirectory(in currentPath, in newPath)).Ret();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{
- return ConvertResult(BaseFileSystem.Get.GetEntryType(out entryType, path));
+ return ConvertResult(_baseFileSystem.Get.GetEntryType(out entryType, in path)).Ret();
}
- // 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 outFile, in Path path, OpenMode mode);
-
- protected abstract override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
- OpenDirectoryMode mode);
-
protected override Result DoCommit()
{
- return ConvertResult(BaseFileSystem.Get.Commit());
+ return ConvertResult(_baseFileSystem.Get.Commit()).Ret();
}
protected override Result DoCommitProvisionally(long counter)
{
- return ConvertResult(BaseFileSystem.Get.CommitProvisionally(counter));
+ return ConvertResult(_baseFileSystem.Get.CommitProvisionally(counter)).Ret();
}
protected override Result DoRollback()
{
- return ConvertResult(BaseFileSystem.Get.Rollback());
+ return ConvertResult(_baseFileSystem.Get.Rollback()).Ret();
}
protected override Result DoFlush()
{
- return ConvertResult(BaseFileSystem.Get.Flush());
+ return ConvertResult(_baseFileSystem.Get.Flush()).Ret();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{
- return ConvertResult(BaseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, path));
+ return ConvertResult(_baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, in path)).Ret();
}
protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId,
in Path path)
{
- return ConvertResult(BaseFileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, path));
+ return ConvertResult(_baseFileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, in path)).Ret();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{
- return ConvertResult(BaseFileSystem.Get.GetFreeSpaceSize(out freeSpace, path));
+ return ConvertResult(_baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path)).Ret();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{
- return ConvertResult(BaseFileSystem.Get.GetTotalSpaceSize(out totalSpace, path));
+ return ConvertResult(_baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path)).Ret();
}
-
- protected abstract Result ConvertResult(Result result);
}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs b/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs
index 3fdc1341..6b1a71ff 100644
--- a/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs
+++ b/src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs
@@ -6,11 +6,11 @@ namespace LibHac.FsSystem;
///
/// Provides read/write access to a save data file system's extra data.
///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
+/// Based on FS 14.1.0 (nnSdk 14.3.0)
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 RegisterExtraDataAccessorObserver(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..57c993fb
--- /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 RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
+}
+
+public interface ICacheableSaveDataFileSystem
+{
+ bool IsSaveDataFileSystemCacheEnabled();
+ Result RollbackOnlyModified();
+}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/ISaveDataFileSystemCacheManager.cs b/src/LibHac/FsSystem/ISaveDataFileSystemCacheManager.cs
deleted file mode 100644
index 5f053e83..00000000
--- a/src/LibHac/FsSystem/ISaveDataFileSystemCacheManager.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using LibHac.Common;
-using LibHac.Fs;
-
-namespace LibHac.FsSystem;
-
-///
-/// Provides a mechanism for caching save data file systems.
-///
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
-public interface ISaveDataFileSystemCacheManager : IDisposable
-{
- bool GetCache(ref SharedRef outFileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
- void Register(ref SharedRef fileSystem);
- void Register(ref SharedRef fileSystem);
- void Unregister(SaveDataSpaceId spaceId, ulong saveDataId);
-}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/SaveDataFileSystemCacheRegisterBase.cs b/src/LibHac/FsSystem/SaveDataFileSystemCacheRegisterBase.cs
deleted file mode 100644
index 459ada71..00000000
--- a/src/LibHac/FsSystem/SaveDataFileSystemCacheRegisterBase.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using LibHac.Common;
-using LibHac.Diag;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-using LibHac.Tools.FsSystem.Save;
-
-namespace LibHac.FsSystem;
-
-///
-/// Wraps a save data .
-/// Upon disposal the base file system is returned to the provided .
-///
-/// The type of the base file system. Must be one of ,
-/// or .
-/// Based on FS 13.1.0 (nnSdk 13.4.0)
-public class SaveDataFileSystemCacheRegisterBase : IFileSystem where T : IFileSystem
-{
- private SharedRef _baseFileSystem;
- private ISaveDataFileSystemCacheManager _cacheManager;
-
- public SaveDataFileSystemCacheRegisterBase(ref SharedRef baseFileSystem,
- ISaveDataFileSystemCacheManager cacheManager)
- {
- if (typeof(T) != typeof(SaveDataFileSystemHolder) && typeof(T) != typeof(ApplicationTemporaryFileSystem))
- {
- throw new NotSupportedException(
- $"The file system type of a {nameof(SaveDataFileSystemCacheRegisterBase)} must be {nameof(SaveDataFileSystemHolder)} or {nameof(ApplicationTemporaryFileSystem)}.");
- }
-
- _baseFileSystem = SharedRef.CreateMove(ref baseFileSystem);
- _cacheManager = cacheManager;
- }
-
- public override void Dispose()
- {
- if (typeof(T) == typeof(SaveDataFileSystemHolder))
- {
- _cacheManager.Register(ref Unsafe.As, SharedRef>(ref _baseFileSystem));
- }
- else if (typeof(T) == typeof(ApplicationTemporaryFileSystem))
- {
- _cacheManager.Register(ref Unsafe.As, SharedRef>(ref _baseFileSystem));
- }
- else
- {
- Assert.SdkAssert(false, "Invalid save data file system type.");
- }
- }
-
- protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode)
- {
- return _baseFileSystem.Get.OpenFile(ref outFile, path, mode);
- }
-
- protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
- OpenDirectoryMode mode)
- {
- return _baseFileSystem.Get.OpenDirectory(ref outDirectory, path, mode);
- }
-
- protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
- {
- return _baseFileSystem.Get.GetEntryType(out entryType, path);
- }
-
- protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
- {
- return _baseFileSystem.Get.CreateFile(path, size, option);
- }
-
- protected override Result DoDeleteFile(in Path path)
- {
- return _baseFileSystem.Get.DeleteFile(path);
- }
-
- protected override Result DoCreateDirectory(in Path path)
- {
- return _baseFileSystem.Get.CreateDirectory(path);
- }
-
- protected override Result DoDeleteDirectory(in Path path)
- {
- return _baseFileSystem.Get.DeleteDirectory(path);
- }
-
- protected override Result DoDeleteDirectoryRecursively(in Path path)
- {
- return _baseFileSystem.Get.DeleteDirectoryRecursively(path);
- }
-
- protected override Result DoCleanDirectoryRecursively(in Path path)
- {
- return _baseFileSystem.Get.CleanDirectoryRecursively(path);
- }
-
- protected override Result DoRenameFile(in Path currentPath, in Path newPath)
- {
- return _baseFileSystem.Get.RenameFile(currentPath, newPath);
- }
-
- protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
- {
- return _baseFileSystem.Get.RenameDirectory(currentPath, 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, path);
- }
-
- protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
- {
- return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, path);
- }
-}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/SaveDataFileSystemHolder.cs b/src/LibHac/FsSystem/SaveDataFileSystemHolder.cs
deleted file mode 100644
index 5b5d35ff..00000000
--- a/src/LibHac/FsSystem/SaveDataFileSystemHolder.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using LibHac.Common;
-using LibHac.Diag;
-using LibHac.Fs;
-using LibHac.Fs.Fsa;
-
-namespace LibHac.FsSystem;
-
-///
-/// Holds a file system for adding to the save data file system cache.
-///
-/// Nintendo uses concrete types in instead of an interface.
-/// This class allows to be cached in a way that changes the original
-/// design as little as possible.
-///
-public class SaveDataFileSystemHolder : ForwardingFileSystem
-{
- public SaveDataFileSystemHolder(ref SharedRef baseFileSystem) : base(ref baseFileSystem)
- {
- Assert.SdkRequires(BaseFileSystem.Get.GetType() == typeof(SaveDataFileSystemHolder) ||
- BaseFileSystem.Get.GetType() == typeof(ApplicationTemporaryFileSystem));
- }
-
- public SaveDataSpaceId GetSaveDataSpaceId()
- {
- IFileSystem baseFs = BaseFileSystem.Get;
-
- if (baseFs.GetType() == typeof(DirectorySaveDataFileSystem))
- {
- return ((DirectorySaveDataFileSystem)baseFs).GetSaveDataSpaceId();
- }
-
- throw new NotImplementedException();
- }
-
- public ulong GetSaveDataId()
- {
- IFileSystem baseFs = BaseFileSystem.Get;
-
- if (baseFs.GetType() == typeof(DirectorySaveDataFileSystem))
- {
- return ((DirectorySaveDataFileSystem)baseFs).GetSaveDataId();
- }
-
- throw new NotImplementedException();
- }
-
- public Result RollbackOnlyModified()
- {
- IFileSystem baseFs = BaseFileSystem.Get;
-
- if (baseFs.GetType() == typeof(DirectorySaveDataFileSystem))
- {
- return ((DirectorySaveDataFileSystem)baseFs).Rollback();
- }
-
- throw new NotImplementedException();
- }
-}
\ No newline at end of file
diff --git a/src/LibHac/HorizonFactory.cs b/src/LibHac/HorizonFactory.cs
index b4beef80..02085a0b 100644
--- a/src/LibHac/HorizonFactory.cs
+++ b/src/LibHac/HorizonFactory.cs
@@ -1,7 +1,9 @@
-using LibHac.Bcat;
+using System;
+using LibHac.Bcat;
using LibHac.Common.Keys;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
+using LibHac.FsSystem;
namespace LibHac;
@@ -15,13 +17,17 @@ public static class HorizonFactory
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
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
{
DeviceOperator = defaultObjects.DeviceOperator,
ExternalKeySet = keySet.ExternalKeySet,
FsCreators = defaultObjects.FsCreators,
+ RandomGenerator = randomGenerator
};
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
@@ -31,4 +37,4 @@ public static class HorizonFactory
return horizon;
}
-}
+}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/Common/Layout.cs b/tests/LibHac.Tests/Common/Layout.cs
index 1a0cb743..dc1842ac 100644
--- a/tests/LibHac.Tests/Common/Layout.cs
+++ b/tests/LibHac.Tests/Common/Layout.cs
@@ -16,4 +16,17 @@ public class Layout
return offset;
}
+
+ public static int AlignOf() where T : unmanaged
+ {
+ return Unsafe.SizeOf>() - Unsafe.SizeOf();
+ }
+
+ private readonly struct AlignOfHelper where T : unmanaged
+ {
+#pragma warning disable CS0169 // Field is never used
+ private readonly byte _padding;
+ private readonly T _value;
+#pragma warning restore CS0169
+ }
}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs
index bbb61dff..185121da 100644
--- a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs
+++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs
@@ -2,6 +2,7 @@
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
+using LibHac.FsSystem;
using LibHac.Tools.Fs;
namespace LibHac.Tests.Fs.FileSystemClientTests;
@@ -18,7 +19,10 @@ public static class FileSystemServerFactory
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
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);
@@ -26,6 +30,7 @@ public static class FileSystemServerFactory
config.FsCreators = defaultObjects.FsCreators;
config.DeviceOperator = defaultObjects.DeviceOperator;
config.ExternalKeySet = new ExternalKeySet();
+ config.RandomGenerator = randomGenerator;
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);
return horizon;
diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
index 4a6ef886..5ac2ee69 100644
--- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
+++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using LibHac.Fs;
using LibHac.Fs.Impl;
using Xunit;
@@ -476,21 +475,10 @@ public class TypeLayoutTests
Assert.Equal(0, GetOffset(in s, in s.Value));
}
- [StructLayout(LayoutKind.Sequential)]
- private struct Int64AlignmentTest
- {
- public byte A;
- public Int64 B;
- }
-
[Fact]
- public static void Int64Test_Layout()
+ public static void Int64_Layout()
{
- var s = new Int64AlignmentTest();
-
- Assert.Equal(12, Unsafe.SizeOf());
-
- Assert.Equal(0, GetOffset(in s, in s.A));
- Assert.Equal(4, GetOffset(in s, in s.B));
+ Assert.Equal(8, Unsafe.SizeOf());
+ Assert.Equal(4, AlignOf());
}
}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/FsSystem/DirectorySaveDataFileSystemTests.cs b/tests/LibHac.Tests/FsSystem/DirectorySaveDataFileSystemTests.cs
index 8707dfdb..ee604bbc 100644
--- a/tests/LibHac.Tests/FsSystem/DirectorySaveDataFileSystemTests.cs
+++ b/tests/LibHac.Tests/FsSystem/DirectorySaveDataFileSystemTests.cs
@@ -48,8 +48,8 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
FileSystemClient fsClient)
{
var obj = new DirectorySaveDataFileSystem(baseFileSystem, fsClient);
- Result rc = obj.Initialize(timeStampGetter, randomGenerator, isJournalingSupported, isMultiCommitSupported,
- isJournalingEnabled);
+ Result rc = obj.Initialize(isJournalingSupported, isMultiCommitSupported, isJournalingEnabled, timeStampGetter,
+ randomGenerator);
if (rc.IsSuccess())
{
@@ -521,7 +521,7 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
private int _index;
- public Result GenerateRandom(Span output)
+ public void GenerateRandom(Span output)
{
if (output.Length != 8)
throw new ArgumentException();
@@ -529,7 +529,6 @@ public class DirectorySaveDataFileSystemTests : CommittableIFileSystemTests
Unsafe.As(ref MemoryMarshal.GetReference(output)) = Values[_index];
_index = (_index + 1) % Values.Length;
- return Result.Success;
}
}
diff --git a/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs
index c344430e..1ff05b94 100644
--- a/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs
+++ b/tests/LibHac.Tests/FsSystem/TypeLayoutTests.cs
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using LibHac.FsSystem;
using Xunit;
using static LibHac.Tests.Common.Layout;
@@ -287,6 +286,7 @@ public class TypeLayoutTests
HierarchicalIntegrityVerificationLevelInformation s = default;
Assert.Equal(0x18, Unsafe.SizeOf());
+ Assert.Equal(0x04, AlignOf());
Assert.Equal(0x00, GetOffset(in s, in s.Offset));
Assert.Equal(0x08, GetOffset(in s, in s.Size));
@@ -294,24 +294,6 @@ public class TypeLayoutTests
Assert.Equal(0x14, GetOffset(in s, in s.Reserved));
}
- [StructLayout(LayoutKind.Sequential)]
- private struct HierarchicalIntegrityVerificationLevelInformationAlignmentTest
- {
- public byte A;
- public HierarchicalIntegrityVerificationLevelInformation B;
- }
-
- [Fact]
- public static void HierarchicalIntegrityVerificationLevelInformation_Alignment()
- {
- var s = new HierarchicalIntegrityVerificationLevelInformationAlignmentTest();
-
- Assert.Equal(0x1C, Unsafe.SizeOf());
-
- Assert.Equal(0, GetOffset(in s, in s.A));
- Assert.Equal(4, GetOffset(in s, in s.B));
- }
-
[Fact]
public static void HierarchicalIntegrityVerificationInformation_Layout()
{
diff --git a/tests/LibHac.Tests/HorizonFactory.cs b/tests/LibHac.Tests/HorizonFactory.cs
index 1371d641..7eca909e 100644
--- a/tests/LibHac.Tests/HorizonFactory.cs
+++ b/tests/LibHac.Tests/HorizonFactory.cs
@@ -2,6 +2,7 @@
using LibHac.Common.Keys;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
+using LibHac.FsSystem;
using LibHac.Tools.Fs;
namespace LibHac.Tests;
@@ -18,12 +19,16 @@ public static class HorizonFactory
HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient();
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();
config.FsCreators = defaultObjects.FsCreators;
config.DeviceOperator = defaultObjects.DeviceOperator;
config.ExternalKeySet = new ExternalKeySet();
+ config.RandomGenerator = randomGenerator;
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, config);