diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv
index 6df4f75b..520d2e68 100644
--- a/build/CodeGen/results.csv
+++ b/build/CodeGen/results.csv
@@ -1068,6 +1068,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,6395,,,,UnsupportedRollbackOnlyModifiedForApplicationTemporaryFileSystem,
2,6396,,,,UnsupportedRollbackOnlyModifiedForDirectorySaveDataFileSystem,
2,6397,,,,UnsupportedOperateRangeForRegionSwitchStorage,
+2,6398,,,,UnsupportedOperateRangeForSaveDataFile,
2,6400,6449,,,PermissionDenied,
2,6403,,,,HostFileSystemOperationDisabled,Returned when opening a host FS on a retail device.
diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs
index b9d97bcb..ad9fb24f 100644
--- a/src/LibHac/Fs/ResultFs.cs
+++ b/src/LibHac/Fs/ResultFs.cs
@@ -1960,6 +1960,8 @@ public static class ResultFs
public static Result.Base UnsupportedRollbackOnlyModifiedForDirectorySaveDataFileSystem => new Result.Base(ModuleFs, 6396);
/// Error code: 2002-6397; Inner value: 0x31fa02
public static Result.Base UnsupportedOperateRangeForRegionSwitchStorage => new Result.Base(ModuleFs, 6397);
+ /// Error code: 2002-6398; Inner value: 0x31fc02
+ public static Result.Base UnsupportedOperateRangeForSaveDataFile => new Result.Base(ModuleFs, 6398);
/// Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002
public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); }
diff --git a/src/LibHac/FsSystem/IResultConvertFileSystem.cs b/src/LibHac/FsSystem/IResultConvertFileSystem.cs
index a8a4c6b5..69c4b3d3 100644
--- a/src/LibHac/FsSystem/IResultConvertFileSystem.cs
+++ b/src/LibHac/FsSystem/IResultConvertFileSystem.cs
@@ -10,7 +10,7 @@ namespace LibHac.FsSystem;
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public abstract class IResultConvertFile : IFile
{
private UniqueRef _baseFile;
@@ -66,7 +66,7 @@ public abstract class IResultConvertFile : IFile
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public abstract class IResultConvertDirectory : IDirectory
{
private UniqueRef _baseDirectory;
@@ -101,7 +101,7 @@ public abstract class IResultConvertDirectory : IDirectory
/// Wraps an , converting its returned s to different
/// s based on the function.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public abstract class IResultConvertFileSystem : ISaveDataFileSystem where T : IFileSystem
{
private SharedRef _baseFileSystem;
diff --git a/src/LibHac/FsSystem/SaveDataFileSystem.cs b/src/LibHac/FsSystem/SaveDataFileSystem.cs
index ea9988d9..66a433ac 100644
--- a/src/LibHac/FsSystem/SaveDataFileSystem.cs
+++ b/src/LibHac/FsSystem/SaveDataFileSystem.cs
@@ -1,7 +1,7 @@
-// ReSharper disable UnusedMember.Local UnusedType.Local
-#pragma warning disable CS0169 // Field is never used
-using System;
+using System;
+using System.Runtime.CompilerServices;
using LibHac.Common;
+using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem.Save;
@@ -9,52 +9,92 @@ using LibHac.Os;
namespace LibHac.FsSystem;
+///
+/// Wraps an opened by a
+///
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
file class SaveDataFile : IFile
{
private UniqueRef _file;
public SaveDataFile(ref UniqueRef file)
{
- throw new NotImplementedException();
+ _file = new UniqueRef(ref file);
+
+ Assert.SdkRequiresNotNull(in _file);
}
public override void Dispose()
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ _file.Destroy();
+ base.Dispose();
}
protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ Result res = _file.Get.Read(out bytesRead, offset, destination);
+
+ if (res.IsFailure())
+ {
+ if (ResultFs.InvalidSaveDataFileReadOffset.Includes(res))
+ {
+ return ResultFs.OutOfRange.LogConverted(res);
+ }
+
+ return res.Miss();
+ }
+
+ return Result.Success;
}
protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ return _file.Get.Write(offset, source, in option).Ret();
}
protected override Result DoFlush()
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ return _file.Get.Flush().Ret();
}
protected override Result DoSetSize(long size)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ return _file.Get.SetSize(size).Ret();
}
protected override Result DoGetSize(out long size)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ return _file.Get.GetSize(out size).Ret();
}
protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan inBuffer)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(in _file);
+
+ if (operationId == OperationId.InvalidateCache)
+ return ResultFs.UnsupportedOperateRangeForSaveDataFile.Log();
+
+ return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer).Ret();
}
}
+///
+/// Reads and writes to the file system inside a journal integrity save data image file.
+///
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataFileSystem : ISaveDataFileSystem, InternalStorageFileSystemHolder
{
private SharedRef _baseStorage;
@@ -69,39 +109,75 @@ public class SaveDataFileSystem : ISaveDataFileSystem, InternalStorageFileSystem
public SaveDataFileSystem()
{
- throw new NotImplementedException();
+ _baseStorage = new SharedRef();
+ _saveFsDriver = new JournalIntegritySaveDataFileSystemDriver();
+ _cacheObserver = null;
+ _mutex = new SdkMutex();
+ _canCommitProvisionally = false;
}
public override void Dispose()
{
- throw new NotImplementedException();
+ _saveFsDriver.FinalizeObject();
+ _cacheObserver?.Unregister(_spaceId, _saveDataId);
+
+ _saveFsDriver.Dispose();
+ _baseStorage.Destroy();
+ base.Dispose();
}
public static Result ExtractParameters(out JournalIntegritySaveDataParameters outParam, IStorage saveStorage,
IBufferManager bufferManager, IMacGenerator macGenerator,
IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out outParam);
+
+ using var fileSystem = new JournalIntegritySaveDataFileSystemDriver();
+
+ Result res = saveStorage.GetSize(out long size);
+ if (res.IsFailure()) return res.Miss();
+
+ using var saveSubStorage = new ValueSubStorage(saveStorage, 0, size);
+ res = fileSystem.Initialize(in saveSubStorage, bufferManager, macGenerator, hashGeneratorFactorySelector, minimumVersion);
+ if (res.IsFailure()) return res.Miss();
+
+ fileSystem.ExtractParameters(out outParam);
+ return Result.Success;
}
public Result Initialize(IStorage baseStorage, IBufferManager bufferManager, IMacGenerator macGenerator,
IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion, bool canCommitProvisionally)
{
- throw new NotImplementedException();
+ return Initialize(baseStorage, bufferManager, macGenerator, hashGeneratorFactorySelector, timeStampGetter: null,
+ randomGenerator: null, minimumVersion, canCommitProvisionally).Ret();
}
public Result Initialize(IStorage baseStorage, IBufferManager bufferManager, IMacGenerator macGenerator,
IHash256GeneratorFactorySelector hashGeneratorFactorySelector, ISaveDataCommitTimeStampGetter timeStampGetter,
RandomDataGenerator randomGenerator, uint minimumVersion, bool canCommitProvisionally)
{
- throw new NotImplementedException();
+ Result res = baseStorage.GetSize(out long size);
+ if (res.IsFailure()) return res.Miss();
+
+ using var baseSubStorage = new ValueSubStorage(baseStorage, 0, size);
+ res = _saveFsDriver.Initialize(in baseSubStorage, bufferManager, macGenerator, hashGeneratorFactorySelector, minimumVersion);
+ if (res.IsFailure()) return res.Miss();
+
+ _commitTimeStampGetter = timeStampGetter;
+ _randomGeneratorForCommit = randomGenerator;
+ _canCommitProvisionally = canCommitProvisionally;
+
+ return Result.Success;
}
public Result Initialize(ref readonly SharedRef baseStorage, IBufferManager bufferManager,
IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion,
bool canCommitProvisionally)
{
- throw new NotImplementedException();
+ _baseStorage.SetByCopy(in baseStorage);
+
+ return Initialize(_baseStorage.Get, bufferManager, macGenerator, hashGeneratorFactorySelector, minimumVersion,
+ canCommitProvisionally).Ret();
}
public Result Initialize(ref readonly SharedRef baseStorage, IBufferManager bufferManager,
@@ -109,136 +185,184 @@ public class SaveDataFileSystem : ISaveDataFileSystem, InternalStorageFileSystem
ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator, uint minimumVersion,
bool canCommitProvisionally)
{
- throw new NotImplementedException();
+ _baseStorage.SetByCopy(in baseStorage);
+
+ return Initialize(_baseStorage.Get, bufferManager, macGenerator, hashGeneratorFactorySelector, timeStampGetter,
+ randomGenerator, minimumVersion, canCommitProvisionally).Ret();
}
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
{
- throw new NotImplementedException();
+ return _saveFsDriver.CreateFile(in path, size, option).Ret();
}
protected override Result DoDeleteFile(ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.DeleteFile(in path).Ret();
}
protected override Result DoCreateDirectory(ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.CreateDirectory(in path).Ret();
}
protected override Result DoDeleteDirectory(ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.DeleteDirectory(in path).Ret();
}
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.DeleteDirectoryRecursively(in path).Ret();
}
protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.CleanDirectoryRecursively(in path).Ret();
}
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
{
- throw new NotImplementedException();
+ return _saveFsDriver.RenameFile(in currentPath, in newPath).Ret();
}
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
{
- throw new NotImplementedException();
+ return _saveFsDriver.RenameDirectory(in currentPath, in newPath).Ret();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.GetEntryType(out entryType, in path).Ret();
}
protected override Result DoOpenFile(ref UniqueRef outFile, ref readonly Path path, OpenMode mode)
{
- throw new NotImplementedException();
+ using var file = new UniqueRef();
+ Result res = _saveFsDriver.OpenFile(ref file.Ref, in path, mode);
+ if (res.IsFailure()) return res.Miss();
+
+ using var wrapperFile = new UniqueRef(new SaveDataFile(ref file.Ref));
+
+ outFile.Set(ref wrapperFile.Ref);
+ return Result.Success;
}
protected override Result DoOpenDirectory(ref UniqueRef outDirectory, ref readonly Path path, OpenDirectoryMode mode)
{
- throw new NotImplementedException();
+ return _saveFsDriver.OpenDirectory(ref outDirectory, in path, mode).Ret();
}
private Result DoCommit(bool updateTimeStamp)
{
- throw new NotImplementedException();
+ if (updateTimeStamp && _commitTimeStampGetter is not null)
+ {
+ Assert.SdkNotNull(_randomGeneratorForCommit);
+
+ Result res = ReadExtraData(out SaveDataExtraData extraData);
+ if (res.IsFailure()) return res.Miss();
+
+ res = _commitTimeStampGetter.Get(out long timeStamp);
+ if (res.IsSuccess())
+ extraData.TimeStamp = timeStamp;
+
+ long commitId = 0;
+ do
+ {
+ _randomGeneratorForCommit(SpanHelpers.AsByteSpan(ref commitId));
+ } while (commitId == 0 || commitId == extraData.CommitId);
+
+ extraData.CommitId = commitId;
+
+ res = WriteExtraData(in extraData);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ return _saveFsDriver.Commit().Ret();
}
protected override Result DoCommit()
{
- throw new NotImplementedException();
+ return DoCommit(updateTimeStamp: true).Ret();
}
public long GetCounterForBundledCommit()
{
- throw new NotImplementedException();
+ return _saveFsDriver.GetCounterForBundledCommit();
}
protected override Result DoCommitProvisionally(long counter)
{
- throw new NotImplementedException();
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ if (!_canCommitProvisionally)
+ return ResultFs.UnsupportedCommitProvisionallyForSaveDataFileSystem.Log();
+
+ return _saveFsDriver.CommitProvisionally(counter).Ret();
}
protected override Result DoRollback()
{
- throw new NotImplementedException();
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ return _saveFsDriver.Rollback().Ret();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.GetFreeSpaceSize(out freeSpace, in path).Ret();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path)
{
- throw new NotImplementedException();
+ return _saveFsDriver.GetTotalSpaceSize(out totalSpace, in path).Ret();
}
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
{
- throw new NotImplementedException();
+ return _saveFsDriver.GetFileSystemAttribute(out outAttribute).Ret();
}
public override Result WriteExtraData(in SaveDataExtraData extraData)
{
- throw new NotImplementedException();
+ return _saveFsDriver.WriteExtraData(in Unsafe.As(ref Unsafe.AsRef(in extraData))).Ret();
}
public override Result CommitExtraData(bool updateTimeStamp)
{
- throw new NotImplementedException();
+ return DoCommit(updateTimeStamp).Ret();
}
public override Result ReadExtraData(out SaveDataExtraData extraData)
{
- throw new NotImplementedException();
+ UnsafeHelpers.SkipParamInit(out extraData);
+ _saveFsDriver.ReadExtraData(out Unsafe.As(ref extraData));
+
+ return Result.Success;
}
- public override void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer, SaveDataSpaceId spaceId, ulong saveDataId)
+ public override void RegisterExtraDataAccessorObserver(ISaveDataExtraDataAccessorObserver observer,
+ SaveDataSpaceId spaceId, ulong saveDataId)
{
- throw new NotImplementedException();
+ _cacheObserver = observer;
+ _spaceId = spaceId;
+ _saveDataId = saveDataId;
}
public override bool IsSaveDataFileSystemCacheEnabled()
{
- throw new NotImplementedException();
+ return true;
}
public override Result RollbackOnlyModified()
{
- throw new NotImplementedException();
+ using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex);
+
+ return _saveFsDriver.RollbackOnlyModified().Ret();
}
public IInternalStorageFileSystem GetInternalStorageFileSystem()
{
- throw new NotImplementedException();
+ return _saveFsDriver;
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs b/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs
index 12c3f159..7d4c97d1 100644
--- a/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs
+++ b/src/LibHac/FsSystem/SaveDataResultConvertFileSystem.cs
@@ -9,7 +9,7 @@ namespace LibHac.FsSystem;
/// Wraps an , converting its returned s
/// to save-data-specific s.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataResultConvertFile : IResultConvertFile
{
private bool _isReconstructible;
@@ -29,13 +29,13 @@ public class SaveDataResultConvertFile : IResultConvertFile
/// Wraps an , converting its returned s
/// to save-data-specific s.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataResultConvertDirectory : IResultConvertDirectory
{
private bool _isReconstructible;
- public SaveDataResultConvertDirectory(ref UniqueRef baseDirectory, bool isReconstructible) : base(
- ref baseDirectory)
+ public SaveDataResultConvertDirectory(ref UniqueRef baseDirectory, bool isReconstructible)
+ : base(ref baseDirectory)
{
_isReconstructible = isReconstructible;
}
@@ -50,7 +50,7 @@ public class SaveDataResultConvertDirectory : IResultConvertDirectory
/// Wraps an , converting its returned s
/// to save-data-specific s.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataResultConvertFileSystem : IResultConvertFileSystem
{
private bool _isReconstructible;
diff --git a/src/LibHac/FsSystem/SwitchStorage.cs b/src/LibHac/FsSystem/SwitchStorage.cs
index 5561f4a4..51c75f2e 100644
--- a/src/LibHac/FsSystem/SwitchStorage.cs
+++ b/src/LibHac/FsSystem/SwitchStorage.cs
@@ -112,7 +112,7 @@ public class SwitchStorage : IStorage
/// the provided will be forwarded to one , and requests outside
/// will be forwarded to the other.
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class RegionSwitchStorage : IStorage
{
public struct Region