Add some save data transfer client classes

- Fs.ISaveDataChunkIterator
- Fs.ISaveDataChunkExporter
- Fs.ISaveDataChunkImporter
- Fs.ISaveDataDivisionExporter
- Fs.ISaveDataDivisionImporter
- Fs.SaveDataTransferManagerVersion2
- Fs.SaveDataTransferProhibiterForCloudBackUp
- Fs.Impl.SaveDataChunkIterator
- Fs.Impl.SaveDataChunkExporter
- Fs.Impl.SaveDataChunkImporter
- Fs.Impl.SaveDataExporterVersion2
- Fs.Impl.SaveDataImporterVersion2
- Fs.Shim.SaveDataTransferVersion2Shim
This commit is contained in:
Alex Barney 2022-01-24 13:39:39 -07:00
parent ebc6e1b23d
commit dea3b3a8b0
12 changed files with 1082 additions and 3 deletions

View file

@ -0,0 +1,31 @@
#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays;
public struct Array16384<T>
{
public const int Length = 16384;
private Array8192<T> _0;
private Array8192<T> _8192;
public ref T this[int i] => ref Items[i];
public Span<T> Items
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
}
public readonly ReadOnlySpan<T> ItemsRo
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array16384<T> value) => value.ItemsRo;
}

View file

@ -0,0 +1,31 @@
#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays;
public struct Array4096<T>
{
public const int Length = 4096;
private Array2048<T> _0;
private Array2048<T> _2048;
public ref T this[int i] => ref Items[i];
public Span<T> Items
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
}
public readonly ReadOnlySpan<T> ItemsRo
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array4096<T> value) => value.ItemsRo;
}

View file

@ -0,0 +1,31 @@
#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays;
public struct Array8192<T>
{
public const int Length = 8192;
private Array4096<T> _0;
private Array4096<T> _4096;
public ref T this[int i] => ref Items[i];
public Span<T> Items
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
}
public readonly ReadOnlySpan<T> ItemsRo
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array8192<T> value) => value.ItemsRo;
}

View file

@ -17,6 +17,15 @@ public struct InitialDataMac
public Array16<byte> Value;
}
public struct ExportReportInfo
{
public byte DiffChunkCount;
public byte DoubleDivisionDiffChunkCount;
public byte HalfDivisionDiffChunkCount;
public byte CompressionRate;
public Array28<byte> Reserved;
}
public struct ImportReportInfo
{
public byte DiffChunkCount;

View file

@ -8,5 +8,5 @@ public static class SaveData
public static ProgramId InvalidProgramId => ProgramId.InvalidId;
public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1);
public static UserId InvalidUserId => UserId.InvalidId;
public static readonly ulong InvalidSystemSaveDataId = 0;
public static ulong InvalidSystemSaveDataId => 0;
}

View file

@ -204,10 +204,16 @@ public struct SaveDataFilter
return Result.Success;
}
public static SaveDataFilter Make(Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index)
{
return Make(programId, saveType, userId, saveDataId, index, SaveDataRank.Primary);
}
public static SaveDataFilter Make(Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index, SaveDataRank rank)
{
var filter = new SaveDataFilter();
SaveDataFilter filter = default;
if (programId.HasValue)
{

View file

@ -0,0 +1,69 @@
using System;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Fs.Impl;
using LibHac.Time;
namespace LibHac.Fs;
public interface ISaveDataChunkIterator : IDisposable
{
ushort GetId();
void Next();
bool IsEnd();
}
public interface ISaveDataChunkExporter : IDisposable
{
Result Pull(out ulong outPulledSize, Span<byte> destination);
long GetRestRawDataSize();
}
public interface ISaveDataChunkImporter : IDisposable
{
Result Push(ReadOnlySpan<byte> source);
}
public interface ISaveDataDivisionExporter : IDisposable
{
Result SetDivisionCount(int divisionCount);
Result OpenSaveDataDiffChunkIterator(ref UniqueRef<ISaveDataChunkIterator> outIterator);
Result OpenSaveDataChunkExporter(ref UniqueRef<ISaveDataChunkExporter> outExporter, ushort chunkId);
Result GetKeySeed(out KeySeed outKeySeed);
Result GetInitialDataMac(out InitialDataMac outInitialDataMac);
Result GetInitialDataMacKeyGeneration(out int outKeyGeneration);
Result FinalizeExport();
Result CancelExport();
Result SuspendExport(out ExportContext outContext);
Result GetImportInitialDataAad(out InitialDataAad outInitialDataAad);
Result SetExportInitialDataAad(in InitialDataAad initialDataAad);
Result GetSaveDataCommitId(out long outCommitId);
Result GetSaveDataTimeStamp(out PosixTime outTimeStamp);
Result GetReportInfo(out ExportReportInfo outReportInfo);
public struct ExportContext
{
public Array16384<byte> Value;
}
}
public interface ISaveDataDivisionImporter : IDisposable
{
Result OpenSaveDataDiffChunkIterator(ref UniqueRef<ISaveDataChunkIterator> outIterator);
Result InitializeImport(out long remaining, long sizeToProcess);
Result FinalizeImport();
Result FinalizeImportWithoutSwap();
Result CancelImport();
Result GetImportContext(out ImportContext outContext);
Result SuspendImport();
Result OpenSaveDataChunkImporter(ref UniqueRef<ISaveDataChunkImporter> outImporter, ushort chunkId);
Result GetImportInitialDataAad(out InitialDataAad outInitialDataAad);
Result GetSaveDataCommitId(out long outCommitId);
Result GetSaveDataTimeStamp(out PosixTime outTimeStamp);
Result GetReportInfo(out ImportReportInfo outReportInfo);
public struct ImportContext
{
public Array16384<byte> Value;
}
}

View file

@ -11,3 +11,8 @@ public struct AesKey
{
public Array16<byte> Value;
}
public struct InitialDataVersion2
{
public Array8192<byte> Value;
}

View file

@ -0,0 +1,469 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Sf;
using LibHac.Time;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl;
/// <summary>
/// An adapter for interacting with an <see cref="FsSrv.Sf.ISaveDataChunkIterator"/>
/// IPC service object via the non-IPC <see cref="ISaveDataChunkIterator"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataChunkIterator : ISaveDataChunkIterator
{
private SharedRef<FsSrv.Sf.ISaveDataChunkIterator> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public SaveDataChunkIterator(FileSystemClient fs, ref SharedRef<FsSrv.Sf.ISaveDataChunkIterator> baseInterface)
{
_baseInterface = SharedRef<FsSrv.Sf.ISaveDataChunkIterator>.CreateMove(ref baseInterface);
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public ushort GetId()
{
Result rc = _baseInterface.Get.GetId(out uint id);
_fsClient.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return (ushort)id;
}
public void Next()
{
Result rc = _baseInterface.Get.Next();
_fsClient.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}
public bool IsEnd()
{
Result rc = _baseInterface.Get.IsEnd(out bool isEnd);
_fsClient.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isEnd;
}
}
/// <summary>
/// An adapter for interacting with an <see cref="FsSrv.Sf.ISaveDataChunkExporter"/>
/// IPC service object via the non-IPC <see cref="ISaveDataChunkExporter"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataChunkExporter : ISaveDataChunkExporter
{
private SharedRef<FsSrv.Sf.ISaveDataChunkExporter> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public SaveDataChunkExporter(FileSystemClient fs, ref SharedRef<FsSrv.Sf.ISaveDataChunkExporter> baseInterface)
{
_baseInterface = SharedRef<FsSrv.Sf.ISaveDataChunkExporter>.CreateMove(ref baseInterface);
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public Result Pull(out ulong outPulledSize, Span<byte> destination)
{
UnsafeHelpers.SkipParamInit(out outPulledSize);
Result rc = _baseInterface.Get.Pull(out ulong pulledSize, new OutBuffer(destination),
(ulong)destination.Length);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outPulledSize = pulledSize;
return Result.Success;
}
public long GetRestRawDataSize()
{
Result rc = _baseInterface.Get.GetRestRawDataSize(out long restSize);
_fsClient.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return restSize;
}
}
/// <summary>
/// An adapter for interacting with an <see cref="FsSrv.Sf.ISaveDataChunkImporter"/>
/// IPC service object via the non-IPC <see cref="ISaveDataChunkImporter"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataChunkImporter : ISaveDataChunkImporter
{
private SharedRef<FsSrv.Sf.ISaveDataChunkImporter> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public SaveDataChunkImporter(FileSystemClient fs, ref SharedRef<FsSrv.Sf.ISaveDataChunkImporter> baseInterface)
{
_baseInterface = SharedRef<FsSrv.Sf.ISaveDataChunkImporter>.CreateMove(ref baseInterface);
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public Result Push(ReadOnlySpan<byte> source)
{
Result rc = _baseInterface.Get.Push(new InBuffer(source), (ulong)source.Length);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
}
/// <summary>
/// An adapter for interacting with an <see cref="FsSrv.Sf.ISaveDataDivisionExporter"/>
/// IPC service object via the non-IPC <see cref="ISaveDataDivisionExporter"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataExporterVersion2 : ISaveDataDivisionExporter
{
private SharedRef<FsSrv.Sf.ISaveDataDivisionExporter> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public SaveDataExporterVersion2(FileSystemClient fs,
ref SharedRef<FsSrv.Sf.ISaveDataDivisionExporter> baseInterface)
{
_baseInterface = SharedRef<FsSrv.Sf.ISaveDataDivisionExporter>.CreateMove(ref baseInterface);
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public Result SetDivisionCount(int divisionCount)
{
Result rc = _baseInterface.Get.SetDivisionCount(divisionCount);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result OpenSaveDataDiffChunkIterator(ref UniqueRef<ISaveDataChunkIterator> outIterator)
{
using var iteratorObject = new SharedRef<FsSrv.Sf.ISaveDataChunkIterator>();
Result rc = _baseInterface.Get.OpenSaveDataDiffChunkIterator(ref iteratorObject.Ref());
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outIterator.Reset(new SaveDataChunkIterator(_fsClient, ref iteratorObject.Ref()));
return Result.Success;
}
public Result OpenSaveDataChunkExporter(ref UniqueRef<ISaveDataChunkExporter> outExporter, ushort chunkId)
{
using var exporterObject = new SharedRef<FsSrv.Sf.ISaveDataChunkExporter>();
Result rc = _baseInterface.Get.OpenSaveDataChunkExporter(ref exporterObject.Ref(), chunkId);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outExporter.Reset(new SaveDataChunkExporter(_fsClient, ref exporterObject.Ref()));
return Result.Success;
}
public Result GetKeySeed(out KeySeed outKeySeed)
{
UnsafeHelpers.SkipParamInit(out outKeySeed);
Result rc = _baseInterface.Get.GetKeySeed(out KeySeed keySeed);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outKeySeed = keySeed;
return Result.Success;
}
public Result GetInitialDataMac(out InitialDataMac outInitialDataMac)
{
UnsafeHelpers.SkipParamInit(out outInitialDataMac);
Result rc = _baseInterface.Get.GetInitialDataMac(out InitialDataMac initialDataMac);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outInitialDataMac = initialDataMac;
return Result.Success;
}
public Result GetInitialDataMacKeyGeneration(out int outKeyGeneration)
{
UnsafeHelpers.SkipParamInit(out outKeyGeneration);
Result rc = _baseInterface.Get.GetInitialDataMacKeyGeneration(out int initialDataMacKeyGeneration);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outKeyGeneration = initialDataMacKeyGeneration;
return Result.Success;
}
public Result FinalizeExport()
{
Result rc = _baseInterface.Get.FinalizeExport();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result CancelExport()
{
Result rc = _baseInterface.Get.CancelExport();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result SuspendExport(out ISaveDataDivisionExporter.ExportContext outContext)
{
UnsafeHelpers.SkipParamInit(out outContext);
Result rc = _baseInterface.Get.SuspendExport(OutBuffer.FromStruct(ref outContext));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result GetImportInitialDataAad(out InitialDataAad outInitialDataAad)
{
UnsafeHelpers.SkipParamInit(out outInitialDataAad);
Result rc = _baseInterface.Get.GetImportInitialDataAad(out InitialDataAad initialDataAad);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outInitialDataAad = initialDataAad;
return Result.Success;
}
public Result SetExportInitialDataAad(in InitialDataAad initialDataAad)
{
Result rc = _baseInterface.Get.SetExportInitialDataAad(in initialDataAad);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result GetSaveDataCommitId(out long outCommitId)
{
UnsafeHelpers.SkipParamInit(out outCommitId);
Unsafe.SkipInit(out SaveDataExtraData extraData);
Result rc = _baseInterface.Get.ReadSaveDataExtraData(OutBuffer.FromStruct(ref extraData));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outCommitId = extraData.CommitId;
return Result.Success;
}
public Result GetSaveDataTimeStamp(out PosixTime outTimeStamp)
{
UnsafeHelpers.SkipParamInit(out outTimeStamp);
Unsafe.SkipInit(out SaveDataExtraData extraData);
Result rc = _baseInterface.Get.ReadSaveDataExtraData(OutBuffer.FromStruct(ref extraData));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outTimeStamp = new PosixTime(extraData.TimeStamp);
return Result.Success;
}
public Result GetReportInfo(out ExportReportInfo outReportInfo)
{
Result rc = _baseInterface.Get.GetReportInfo(out outReportInfo);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
}
/// <summary>
/// An adapter for interacting with an <see cref="FsSrv.Sf.ISaveDataDivisionImporter"/>
/// IPC service object via the non-IPC <see cref="ISaveDataDivisionImporter"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 13.4.0</remarks>
public class SaveDataImporterVersion2 : ISaveDataDivisionImporter
{
private SharedRef<FsSrv.Sf.ISaveDataDivisionImporter> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public SaveDataImporterVersion2(FileSystemClient fs,
ref SharedRef<FsSrv.Sf.ISaveDataDivisionImporter> baseInterface)
{
_baseInterface = SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>.CreateMove(ref baseInterface);
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public Result InitializeImport(out long remaining, long sizeToProcess)
{
Result rc = _baseInterface.Get.InitializeImport(out remaining, sizeToProcess);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result FinalizeImport()
{
Result rc = _baseInterface.Get.FinalizeImport();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result FinalizeImportWithoutSwap()
{
Result rc = _baseInterface.Get.FinalizeImportWithoutSwap();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result CancelImport()
{
Result rc = _baseInterface.Get.CancelImport();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result GetImportContext(out ISaveDataDivisionImporter.ImportContext outContext)
{
UnsafeHelpers.SkipParamInit(out outContext);
Result rc = _baseInterface.Get.GetImportContext(OutBuffer.FromStruct(ref outContext));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result SuspendImport()
{
Result rc = _baseInterface.Get.SuspendImport();
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result OpenSaveDataDiffChunkIterator(ref UniqueRef<ISaveDataChunkIterator> outIterator)
{
using var iteratorObject = new SharedRef<FsSrv.Sf.ISaveDataChunkIterator>();
Result rc = _baseInterface.Get.OpenSaveDataDiffChunkIterator(ref iteratorObject.Ref());
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outIterator.Reset(new SaveDataChunkIterator(_fsClient, ref iteratorObject.Ref()));
return Result.Success;
}
public Result OpenSaveDataChunkImporter(ref UniqueRef<ISaveDataChunkImporter> outImporter, ushort chunkId)
{
using var importerObject = new SharedRef<FsSrv.Sf.ISaveDataChunkImporter>();
Result rc = _baseInterface.Get.OpenSaveDataChunkImporter(ref importerObject.Ref(), chunkId);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataChunkImporter(_fsClient, ref importerObject.Ref()));
return Result.Success;
}
public Result GetImportInitialDataAad(out InitialDataAad outInitialDataAad)
{
UnsafeHelpers.SkipParamInit(out outInitialDataAad);
Result rc = _baseInterface.Get.GetImportInitialDataAad(out InitialDataAad initialDataAad);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outInitialDataAad = initialDataAad;
return Result.Success;
}
public Result GetSaveDataCommitId(out long outCommitId)
{
UnsafeHelpers.SkipParamInit(out outCommitId);
Unsafe.SkipInit(out SaveDataExtraData extraData);
Result rc = _baseInterface.Get.ReadSaveDataExtraData(OutBuffer.FromStruct(ref extraData));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outCommitId = extraData.CommitId;
return Result.Success;
}
public Result GetSaveDataTimeStamp(out PosixTime outTimeStamp)
{
UnsafeHelpers.SkipParamInit(out outTimeStamp);
Unsafe.SkipInit(out SaveDataExtraData extraData);
Result rc = _baseInterface.Get.ReadSaveDataExtraData(OutBuffer.FromStruct(ref extraData));
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
outTimeStamp = new PosixTime(extraData.TimeStamp);
return Result.Success;
}
public Result GetReportInfo(out ImportReportInfo outReportInfo)
{
Result rc = _baseInterface.Get.GetReportInfo(out outReportInfo);
_fsClient.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
}

View file

@ -0,0 +1,374 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Diag;
using LibHac.Fs.Impl;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Sf;
using LibHac.Sf;
using LibHac.Util;
using static LibHac.Fs.SaveData;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs
{
public class SaveDataTransferManagerVersion2 : IDisposable
{
private SharedRef<ISaveDataTransferManagerWithDivision> _baseInterface;
// LibHac addition
private FileSystemClient _fsClient;
public struct Challenge
{
public Array16<byte> Value;
}
public struct SaveDataTag
{
public Array64<byte> Value;
}
public struct KeySeedPackage
{
public Array512<byte> Value;
}
public SaveDataTransferManagerVersion2(FileSystemClient fs)
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fileSystemProxy.Get.OpenSaveDataTransferManagerVersion2(ref _baseInterface);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
_fsClient = fs;
}
public void Dispose()
{
_baseInterface.Destroy();
}
public Result GetChallenge(out Challenge outChallenge)
{
UnsafeHelpers.SkipParamInit(out outChallenge);
Result rc = _baseInterface.Get.GetChallenge(OutBuffer.FromStruct(ref outChallenge));
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result SetKeySeedPackage(in KeySeedPackage keySeedPackage)
{
Result rc = _baseInterface.Get.SetKeySeedPackage(InBuffer.FromStruct(in keySeedPackage));
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result OpenSaveDataFullExporter(ref UniqueRef<ISaveDataDivisionExporter> outExporter,
SaveDataSpaceId spaceId, ulong saveDataId)
{
using var exporterInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionExporter>();
Result rc = _baseInterface.Get.OpenSaveDataExporter(ref exporterInterface.Ref(), spaceId, saveDataId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outExporter.Reset(new SaveDataExporterVersion2(_fsClient, ref exporterInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataDiffExporter(ref UniqueRef<ISaveDataDivisionExporter> outExporter,
in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, ulong saveDataId)
{
using var exporterInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionExporter>();
Result rc = _baseInterface.Get.OpenSaveDataExporterForDiffExport(ref exporterInterface.Ref(),
InBuffer.FromStruct(in initialData), spaceId, saveDataId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outExporter.Reset(new SaveDataExporterVersion2(_fsClient, ref exporterInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataExporterByContext(ref UniqueRef<ISaveDataDivisionExporter> outExporter,
ISaveDataDivisionExporter.ExportContext exportContext)
{
using var exporterInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionExporter>();
Result rc = _baseInterface.Get.OpenSaveDataExporterByContext(ref exporterInterface.Ref(),
InBuffer.FromStruct(in exportContext));
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outExporter.Reset(new SaveDataExporterVersion2(_fsClient, ref exporterInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataFullImporter(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, in UserId userId, SaveDataSpaceId spaceId)
{
using var importerInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>();
Result rc = _baseInterface.Get.OpenSaveDataImporterDeprecated(ref importerInterface.Ref(),
InBuffer.FromStruct(in initialData), in userId, spaceId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataImporterVersion2(_fsClient, ref importerInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataDiffImporter(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, ulong saveDataId)
{
using var importerInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>();
Result rc = _baseInterface.Get.OpenSaveDataImporterForDiffImport(ref importerInterface.Ref(),
InBuffer.FromStruct(in initialData), spaceId, saveDataId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataImporterVersion2(_fsClient, ref importerInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataDuplicateDiffImporter(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, ulong saveDataId)
{
using var importerInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>();
Result rc = _baseInterface.Get.OpenSaveDataImporterForDuplicateDiffImport(ref importerInterface.Ref(),
InBuffer.FromStruct(in initialData), spaceId, saveDataId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataImporterVersion2(_fsClient, ref importerInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataImporterImpl(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, in UserId userId, SaveDataSpaceId spaceId, bool useSwap)
{
using var importerInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>();
Result rc = _baseInterface.Get.OpenSaveDataImporter(ref importerInterface.Ref(),
InBuffer.FromStruct(in initialData), in userId, spaceId, useSwap);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataImporterVersion2(_fsClient, ref importerInterface.Ref()));
return Result.Success;
}
public Result OpenSaveDataImporter(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, bool useSwap)
{
return OpenSaveDataImporterImpl(ref outImporter, in initialData, UserId.InvalidId, spaceId, useSwap);
}
public Result OpenSaveDataImporterByContext(ref UniqueRef<ISaveDataDivisionImporter> outImporter,
in ISaveDataDivisionImporter.ImportContext importContext)
{
using var importerInterface = new SharedRef<FsSrv.Sf.ISaveDataDivisionImporter>();
Result rc = _baseInterface.Get.OpenSaveDataImporterByContext(ref importerInterface.Ref(),
InBuffer.FromStruct(in importContext));
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
outImporter.Reset(new SaveDataImporterVersion2(_fsClient, ref importerInterface.Ref()));
return Result.Success;
}
public static SaveDataTag MakeUserAccountSaveDataTag(Ncm.ApplicationId applicationId, in UserId userId)
{
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account,
userId, InvalidSystemSaveDataId, index: 0, SaveDataRank.Primary);
Abort.DoAbortUnless(rc.IsSuccess());
return Unsafe.As<SaveDataAttribute, SaveDataTag>(ref attribute);
}
public static SaveDataTag MakeDeviceSaveDataTag(Ncm.ApplicationId applicationId)
{
Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device,
InvalidUserId, InvalidSystemSaveDataId, index: 0, SaveDataRank.Primary);
Abort.DoAbortUnless(rc.IsSuccess());
return Unsafe.As<SaveDataAttribute, SaveDataTag>(ref attribute);
}
public Result CancelSuspendingImport(in SaveDataTag tag)
{
ref readonly SaveDataAttribute attribute =
ref Unsafe.As<SaveDataTag, SaveDataAttribute>(ref Unsafe.AsRef(in tag));
Result rc = _baseInterface.Get.CancelSuspendingImportByAttribute(in attribute);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result CancelSuspendingImport(Ncm.ApplicationId applicationId, in UserId userId)
{
Result rc = _baseInterface.Get.CancelSuspendingImport(applicationId, in userId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public Result SwapSecondary(in SaveDataTag tag, Optional<long> primaryCommitId)
{
long commitId = primaryCommitId.HasValue ? primaryCommitId.Value : 0;
bool doSwap = primaryCommitId.HasValue;
ref readonly SaveDataAttribute attribute =
ref Unsafe.As<SaveDataTag, SaveDataAttribute>(ref Unsafe.AsRef(in tag));
Result rc = _baseInterface.Get.SwapSecondary(in attribute, doSwap, commitId);
_fsClient.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
}
public class SaveDataTransferProhibiterForCloudBackUp : IDisposable
{
private SharedRef<ISaveDataTransferProhibiter> _prohibiter;
public SaveDataTransferProhibiterForCloudBackUp(ref SharedRef<ISaveDataTransferProhibiter> prohibiter)
{
_prohibiter = SharedRef<ISaveDataTransferProhibiter>.CreateMove(ref prohibiter);
}
public void Dispose()
{
_prohibiter.Destroy();
}
}
}
namespace LibHac.Fs.Shim
{
public static class SaveDataTransferVersion2Shim
{
public static Result OpenSaveDataTransferProhibiterForCloudBackUp(this FileSystemClientImpl fs,
ref UniqueRef<SaveDataTransferProhibiterForCloudBackUp> outProhibiter, Ncm.ApplicationId applicationId)
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.GetFileSystemProxyServiceObject();
using var prohibiter = new SharedRef<ISaveDataTransferProhibiter>();
// Todo: Uncomment once opening transfer prohibiters is implemented
// Result rc = fileSystemProxy.Get.OpenSaveDataTransferProhibiter(ref prohibiter.Ref(), applicationId);
// if (rc.IsFailure()) return rc.Miss();
outProhibiter.Reset(new SaveDataTransferProhibiterForCloudBackUp(ref prohibiter.Ref()));
return Result.Success;
}
public static Result OpenSaveDataTransferProhibiterForCloudBackUp(this FileSystemClient fs,
ref UniqueRef<SaveDataTransferProhibiterForCloudBackUp> outProhibiter, Ncm.ApplicationId applicationId)
{
Result rc = fs.Impl.OpenSaveDataTransferProhibiterForCloudBackUp(ref outProhibiter, applicationId);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public static Result OpenSaveDataTransferProhibiterForCloudBackUp(this FileSystemClient fs,
Span<UniqueRef<SaveDataTransferProhibiterForCloudBackUp>> outProhibiters,
ReadOnlySpan<Ncm.ApplicationId> applicationIds)
{
for (int i = 0; i < applicationIds.Length; i++)
{
Result rc = fs.Impl.OpenSaveDataTransferProhibiterForCloudBackUp(ref outProhibiters[i],
applicationIds[i]);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
}
return Result.Success;
}
public static Result GetCountOfApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int outCount,
Ncm.ApplicationId applicationId, int programIndex)
{
using SharedRef<IFileSystemProxy> fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject();
ulong tempAppId = 0;
var programId = new Ncm.ProgramId(applicationId.Value + (uint)programIndex);
Result rc = fileSystemProxy.Get.ListAccessibleSaveDataOwnerId(out outCount,
OutBuffer.FromStruct(ref tempAppId), programId, startIndex: 0, bufferIdCount: 0);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
return Result.Success;
}
public static Result GetOccupiedWorkSpaceSizeForCloudBackUp(this FileSystemClient fs, out long outSize)
{
UnsafeHelpers.SkipParamInit(out outSize);
using var iterator = new UniqueRef<SaveDataIterator>();
// We want to iterate every save with a Secondary rank
Result rc = SaveDataFilter.Make(out SaveDataFilter filter, programId: default, saveType: default,
userId: default, saveDataId: default, index: default, SaveDataRank.Secondary);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
rc = fs.Impl.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User, in filter);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
long workSize = 0;
while (true)
{
Unsafe.SkipInit(out SaveDataInfo info);
rc = fs.Impl.ReadSaveDataIteratorSaveDataInfo(out long count, SpanHelpers.AsSpan(ref info),
iterator.Get);
fs.Impl.LogResultErrorMessage(rc);
if (rc.IsFailure()) return rc.Miss();
// Break once we've iterated all saves
if (count == 0)
break;
if (info.Rank == SaveDataRank.Secondary)
workSize += info.Size;
}
outSize = workSize;
return Result.Success;
}
}
}

View file

@ -19,5 +19,5 @@ public interface ISaveDataDivisionExporter : IDisposable
public Result GetInitialDataMacKeyGeneration(out int keyGeneration);
public Result GetImportInitialDataAad(out InitialDataAad initialDataAad);
public Result SetExportInitialDataAad(in InitialDataAad initialDataAad);
public Result GetReportInfo(out ImportReportInfo reportInfo);
public Result GetReportInfo(out ExportReportInfo reportInfo);
}

View file

@ -341,6 +341,20 @@ public class TypeLayoutTests
Assert.Equal(0x00, GetOffset(in s, in s.Value));
}
[Fact]
public static void ExportReportInfo_Layout()
{
var s = new ExportReportInfo();
Assert.Equal(0x20, Unsafe.SizeOf<ExportReportInfo>());
Assert.Equal(0, GetOffset(in s, in s.DiffChunkCount));
Assert.Equal(1, GetOffset(in s, in s.DoubleDivisionDiffChunkCount));
Assert.Equal(2, GetOffset(in s, in s.HalfDivisionDiffChunkCount));
Assert.Equal(3, GetOffset(in s, in s.CompressionRate));
Assert.Equal(4, GetOffset(in s, in s.Reserved));
}
[Fact]
public static void ImportReportInfo_Layout()
{
@ -365,4 +379,44 @@ public class TypeLayoutTests
Assert.Equal(0, GetOffset(in s, in s.Index));
Assert.Equal(4, GetOffset(in s, in s.Reserved));
}
[Fact]
public static void Challenge_Layout()
{
var s = new SaveDataTransferManagerVersion2.Challenge();
Assert.Equal(0x10, Unsafe.SizeOf<SaveDataTransferManagerVersion2.Challenge>());
Assert.Equal(0, GetOffset(in s, in s.Value));
}
[Fact]
public static void SaveDataTag_Layout()
{
var s = new SaveDataTransferManagerVersion2.SaveDataTag();
Assert.Equal(0x40, Unsafe.SizeOf<SaveDataTransferManagerVersion2.SaveDataTag>());
Assert.Equal(0, GetOffset(in s, in s.Value));
}
[Fact]
public static void KeySeedPackage_Layout()
{
var s = new SaveDataTransferManagerVersion2.KeySeedPackage();
Assert.Equal(0x200, Unsafe.SizeOf<SaveDataTransferManagerVersion2.KeySeedPackage>());
Assert.Equal(0, GetOffset(in s, in s.Value));
}
[Fact]
public static void InitialDataVersion2_Layout()
{
var s = new InitialDataVersion2();
Assert.Equal(0x2000, Unsafe.SizeOf<InitialDataVersion2>());
Assert.Equal(0, GetOffset(in s, in s.Value));
}
}