diff --git a/src/LibHac/FsSrv/Impl/SaveDataExtender.cs b/src/LibHac/FsSrv/Impl/SaveDataExtender.cs
index 0a05bfbb..7c3507ec 100644
--- a/src/LibHac/FsSrv/Impl/SaveDataExtender.cs
+++ b/src/LibHac/FsSrv/Impl/SaveDataExtender.cs
@@ -1,16 +1,25 @@
-// ReSharper disable UnusedMember.Local UnusedType.Local
-#pragma warning disable CS0169 // Field is never used
-#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
-using System;
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using LibHac.Common;
+using LibHac.Diag;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.FsSystem.Save;
+using LibHac.Util;
namespace LibHac.FsSrv.Impl;
+///
+/// Extends a save data image to a larger size. Keeps track of the extension progress by writing information about
+/// the extension to an "extension context" storage.
+///
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataExtender
{
+ private const uint MagicCode = 0x43545845; // EXTC
+ private const uint Version1 = 0x10000;
+ private const uint Version2 = 0x20000;
+
private enum State
{
Initial = 1,
@@ -18,6 +27,7 @@ public class SaveDataExtender
Committed = 3
}
+ [StructLayout(LayoutKind.Sequential)]
private struct Context
{
public uint Magic;
@@ -36,62 +46,119 @@ public class SaveDataExtender
public SaveDataExtender()
{
- throw new NotImplementedException();
+ _contextStorage = null;
}
+ public long GetLogSize() => JournalIntegritySaveDataFileSystemDriver.QueryExpandLogSize(_context.BlockSize,
+ GetJournalBlockCount(), GetAvailableBlockCount());
+
+ public long GetAvailableSize() => _context.AvailableSize;
+ public long GetJournalSize() => _context.JournalSize;
+ public long GetExtendedSaveDataSize() => _context.ExtendedSaveDataSize;
+
+ private uint GetAvailableBlockCount() => (uint)BitUtil.DivideUp(_context.AvailableSize, _context.BlockSize);
+ private uint GetJournalBlockCount() => (uint)BitUtil.DivideUp(_context.JournalSize, _context.BlockSize);
+
public Result InitializeContext(in JournalIntegritySaveDataParameters param, long sizeAvailable, long sizeReserved)
{
- throw new NotImplementedException();
+ _context.Magic = MagicCode;
+ _context.Version = Version2;
+ _context.State = State.Initial;
+ _context.AvailableSize = sizeAvailable;
+ _context.JournalSize = sizeReserved;
+ _context.BlockSize = param.BlockSize;
+
+ Result res = JournalIntegritySaveDataFileSystemDriver.QueryTotalSize(out _context.ExtendedSaveDataSize,
+ param.BlockSize, GetAvailableBlockCount(), GetJournalBlockCount(), param.CountExpandMax, param.Version);
+ if (res.IsFailure()) return res.Miss();
+
+ return Result.Success;
}
public Result WriteContext(IStorage contextStorage)
{
- throw new NotImplementedException();
+ Result res = contextStorage.Write(0, SpanHelpers.AsReadOnlyByteSpan(in _context));
+ if (res.IsFailure()) return res.Miss();
+
+ res = contextStorage.Flush();
+ if (res.IsFailure()) return res.Miss();
+
+ return Result.Success;
}
public Result ReadContext(IStorage contextStorage)
{
- throw new NotImplementedException();
- }
+ Assert.SdkRequiresNull(_contextStorage);
+ Assert.SdkRequiresNotNull(contextStorage);
- public long GetLogSize()
- {
- throw new NotImplementedException();
- }
+ Result res = contextStorage.Read(0, SpanHelpers.AsByteSpan(ref _context));
+ if (res.IsFailure()) return res.Miss();
- public long GetAvailableSize()
- {
- throw new NotImplementedException();
- }
+ if (_context.Magic != MagicCode)
+ return ResultFs.IncorrectSaveDataExtensionContextMagicCode.Log();
- public long GetJournalSize()
- {
- throw new NotImplementedException();
- }
+ if (_context.Version == Version1)
+ {
+ UpdateContextV1ToV2(ref _context);
+ }
- public long GetExtendedSaveDataSize()
- {
- throw new NotImplementedException();
- }
+ if (_context.Version != Version2)
+ return ResultFs.UnsupportedSaveDataVersion.Log();
- private uint GetAvailableBlockCount()
- {
- throw new NotImplementedException();
- }
+ State state = _context.State;
+ if (state != State.Initial && state != State.Extended && state != State.Committed)
+ return ResultFs.InvalidSaveDataExtensionContextState.Log();
- private uint GetJournalBlockCount()
- {
- throw new NotImplementedException();
+ if (_context.BlockSize <= 0)
+ return ResultFs.InvalidSaveDataExtensionContextParameter.Log();
+
+ if (_context.ExtendedSaveDataSize <= 0)
+ return ResultFs.InvalidSaveDataExtensionContextParameter.Log();
+
+ _contextStorage = contextStorage;
+ return Result.Success;
}
public Result Extend(in ValueSubStorage saveDataStorage, in ValueSubStorage logStorage, IBufferManager bufferManager,
IMacGenerator macGenerator, IHash256GeneratorFactorySelector hashGeneratorFactorySelector, uint minimumVersion)
{
- throw new NotImplementedException();
+ Assert.SdkRequiresNotNull(_contextStorage);
+
+ if (_context.State == State.Initial)
+ {
+ Result res = JournalIntegritySaveDataFileSystemDriver.OperateExpand(in saveDataStorage, in logStorage,
+ _context.BlockSize, GetAvailableBlockCount(), GetJournalBlockCount(), bufferManager, macGenerator,
+ hashGeneratorFactorySelector, minimumVersion);
+ if (res.IsFailure()) return res.Miss();
+
+ _context.State = State.Extended;
+ res = WriteContext(_contextStorage);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ if (_context.State == State.Extended)
+ {
+ Result res = JournalIntegritySaveDataFileSystemDriver.CommitExpand(in saveDataStorage, in logStorage,
+ _context.BlockSize, bufferManager);
+ if (res.IsFailure()) return res.Miss();
+
+ _context.State = State.Committed;
+ res = WriteContext(_contextStorage);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ if (_context.State != State.Committed)
+ return ResultFs.BadState.Log();
+
+ return Result.Success;
}
private void UpdateContextV1ToV2(ref Context context)
{
- throw new NotImplementedException();
+ Assert.SdkAssert(context.Version == Version1);
+
+ context.AvailableSize *= context.BlockSize;
+ context.JournalSize *= context.BlockSize;
+ context.Version = Version2;
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataExtraDataAccessorCacheManager.cs
index 5e16e00c..c0fa512c 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 nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.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 nnSdk 14.3.0 (FS 14.1.0)
+ /// Based on nnSdk 17.5.0 (FS 17.0.0)
[NonCopyable]
private struct Cache : IDisposable
{
diff --git a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
index 1259e327..7e15c7df 100644
--- a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
+++ b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheManager.cs
@@ -11,7 +11,7 @@ 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 nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataFileSystemCacheManager : IDisposable
{
[NonCopyable]
diff --git a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs
index c6b2993b..4f9d6122 100644
--- a/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs
+++ b/src/LibHac/FsSrv/Impl/SaveDataFileSystemCacheRegister.cs
@@ -9,7 +9,7 @@ namespace LibHac.FsSrv.Impl;
/// Wraps an .
/// Upon disposal the base file system is returned to the provided .
///
-/// Based on nnSdk 14.3.0 (FS 14.1.0)
+/// Based on nnSdk 17.5.0 (FS 17.0.0)
public class SaveDataFileSystemCacheRegister : IFileSystem
{
private SharedRef _baseFileSystem;
@@ -36,87 +36,87 @@ public class SaveDataFileSystemCacheRegister : IFileSystem
protected override Result DoOpenFile(ref UniqueRef outFile, ref readonly Path path, OpenMode mode)
{
- return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode);
+ return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode).Ret();
}
protected override Result DoOpenDirectory(ref UniqueRef outDirectory, ref readonly Path path,
OpenDirectoryMode mode)
{
- return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode);
+ return _baseFileSystem.Get.OpenDirectory(ref outDirectory, in path, mode).Ret();
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, ref readonly Path path)
{
- return _baseFileSystem.Get.GetEntryType(out entryType, in path);
+ return _baseFileSystem.Get.GetEntryType(out entryType, in path).Ret();
}
protected override Result DoCreateFile(ref readonly Path path, long size, CreateFileOptions option)
{
- return _baseFileSystem.Get.CreateFile(in path, size, option);
+ return _baseFileSystem.Get.CreateFile(in path, size, option).Ret();
}
protected override Result DoDeleteFile(ref readonly Path path)
{
- return _baseFileSystem.Get.DeleteFile(in path);
+ return _baseFileSystem.Get.DeleteFile(in path).Ret();
}
protected override Result DoCreateDirectory(ref readonly Path path)
{
- return _baseFileSystem.Get.CreateDirectory(in path);
+ return _baseFileSystem.Get.CreateDirectory(in path).Ret();
}
protected override Result DoDeleteDirectory(ref readonly Path path)
{
- return _baseFileSystem.Get.DeleteDirectory(in path);
+ return _baseFileSystem.Get.DeleteDirectory(in path).Ret();
}
protected override Result DoDeleteDirectoryRecursively(ref readonly Path path)
{
- return _baseFileSystem.Get.DeleteDirectoryRecursively(in path);
+ return _baseFileSystem.Get.DeleteDirectoryRecursively(in path).Ret();
}
protected override Result DoCleanDirectoryRecursively(ref readonly Path path)
{
- return _baseFileSystem.Get.CleanDirectoryRecursively(in path);
+ return _baseFileSystem.Get.CleanDirectoryRecursively(in path).Ret();
}
protected override Result DoRenameFile(ref readonly Path currentPath, ref readonly Path newPath)
{
- return _baseFileSystem.Get.RenameFile(in currentPath, in newPath);
+ return _baseFileSystem.Get.RenameFile(in currentPath, in newPath).Ret();
}
protected override Result DoRenameDirectory(ref readonly Path currentPath, ref readonly Path newPath)
{
- return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath);
+ return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret();
}
protected override Result DoCommit()
{
- return _baseFileSystem.Get.Commit();
+ return _baseFileSystem.Get.Commit().Ret();
}
protected override Result DoCommitProvisionally(long counter)
{
- return _baseFileSystem.Get.CommitProvisionally(counter);
+ return _baseFileSystem.Get.CommitProvisionally(counter).Ret();
}
protected override Result DoRollback()
{
- return _baseFileSystem.Get.Rollback();
+ return _baseFileSystem.Get.Rollback().Ret();
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, ref readonly Path path)
{
- return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path);
+ return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, in path).Ret();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, ref readonly Path path)
{
- return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path);
+ return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path).Ret();
}
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
{
- return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute);
+ return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute).Ret();
}
}
\ No newline at end of file