Implement SaveDataExtender

This commit is contained in:
Alex Barney 2024-02-19 23:04:19 -07:00
parent 164382f998
commit 2501cd24d0
4 changed files with 123 additions and 56 deletions

View file

@ -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;
/// <summary>
/// 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.
/// </summary>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
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;
}
}

View file

@ -10,13 +10,13 @@ namespace LibHac.FsSrv.Impl;
/// <summary>
/// Holds the <see cref="ISaveDataExtraDataAccessor"/>s for opened save data file systems.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataExtraDataAccessorCacheManager : ISaveDataExtraDataAccessorObserver
{
/// <summary>
/// Holds a single cached extra data accessor identified by its save data ID and save data space ID.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
[NonCopyable]
private struct Cache : IDisposable
{

View file

@ -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.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataFileSystemCacheManager : IDisposable
{
[NonCopyable]

View file

@ -9,7 +9,7 @@ namespace LibHac.FsSrv.Impl;
/// Wraps an <see cref="ISaveDataFileSystem"/>.
/// Upon disposal the base file system is returned to the provided <see cref="SaveDataFileSystemCacheManager"/>.
/// </summary>
/// <remarks>Based on nnSdk 14.3.0 (FS 14.1.0)</remarks>
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
public class SaveDataFileSystemCacheRegister : IFileSystem
{
private SharedRef<ISaveDataFileSystem> _baseFileSystem;
@ -36,87 +36,87 @@ public class SaveDataFileSystemCacheRegister : IFileSystem
protected override Result DoOpenFile(ref UniqueRef<IFile> 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<IDirectory> 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();
}
}