From 2501cd24d0a04c7ce497f8c2c595a1fdd2633152 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 19 Feb 2024 23:04:19 -0700 Subject: [PATCH] Implement SaveDataExtender --- src/LibHac/FsSrv/Impl/SaveDataExtender.cs | 137 +++++++++++++----- .../SaveDataExtraDataAccessorCacheManager.cs | 4 +- .../Impl/SaveDataFileSystemCacheManager.cs | 2 +- .../Impl/SaveDataFileSystemCacheRegister.cs | 36 ++--- 4 files changed, 123 insertions(+), 56 deletions(-) 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