From 74ed435deec08439bf1c58aaf336ebe6ae3375cf Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 18 Jan 2022 20:45:48 -0700 Subject: [PATCH] Ensure all of Shim.SaveDataManagement is updated for 13.1.0 --- src/LibHac/Fs/AccessLog.cs | 28 + .../Fs/ApplicationSaveDataManagement.cs | 7 +- src/LibHac/Fs/CacheStorageTypes.cs | 19 + src/LibHac/Fs/Shim/SaveDataManagement.cs | 3877 +++++++++-------- src/LibHac/Fs/Utility.cs | 50 + src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs | 3 +- .../FsCreator/EmulatedBisFileSystemCreator.cs | 5 +- .../EmulatedSdCardFileSystemCreator.cs | 5 +- .../Tools/FsSystem/FileSystemExtensions.cs | 1 + .../ApplicationSaveDataManagementTests.cs | 2 +- .../ShimTests/SaveDataManagement.cs | 67 + tests/LibHac.Tests/Fs/TypeLayoutTests.cs | 11 + 12 files changed, 2230 insertions(+), 1845 deletions(-) create mode 100644 src/LibHac/Fs/CacheStorageTypes.cs create mode 100644 src/LibHac/Fs/Utility.cs diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index 693766dc..bfed8313 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -1135,5 +1135,33 @@ namespace LibHac.Fs.Impl (byte)',', (byte)' ', (byte)'f', (byte)'u', (byte)'n', (byte)'c', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ', (byte)'"' }; + + /// ", cachestoragelist_handle: 0x" + public static ReadOnlySpan LogCacheStorageListHandle => + new[] + { + (byte)',', (byte)' ', (byte)'c', (byte)'a', (byte)'c', (byte)'h', (byte)'e', (byte)'s', + (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', (byte)'l', (byte)'i', + (byte)'s', (byte)'t', (byte)'_', (byte)'h', (byte)'a', (byte)'n', (byte)'d', (byte)'l', + (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + /// ", infobuffercount: 0x" + public static ReadOnlySpan LogInfoBufferCount => + new[] + { + (byte)',', (byte)' ', (byte)'i', (byte)'n', (byte)'f', (byte)'o', (byte)'b', (byte)'u', + (byte)'f', (byte)'f', (byte)'e', (byte)'r', (byte)'c', (byte)'o', (byte)'u', (byte)'n', + (byte)'t', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + /// ", cache_storage_count: " + public static ReadOnlySpan LogCacheStorageCount => + new[] + { + (byte)',', (byte)' ', (byte)'c', (byte)'a', (byte)'c', (byte)'h', (byte)'e', (byte)'_', + (byte)'s', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', (byte)'_', + (byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' + }; } } \ No newline at end of file diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index 2e13c0d0..61e64ca4 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -29,7 +29,7 @@ public static class ApplicationSaveDataManagement Result CreateAccountSaveFunc() { - UserId userId = ConvertAccountUidToFsUserId(uidLocal); + UserId userId = Utility.ConvertAccountUidToFsUserId(uidLocal); return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize, accountSaveJournalSize, SaveDataFlags.None); } @@ -465,9 +465,4 @@ public static class ApplicationSaveDataManagement return rc; } - - public static UserId ConvertAccountUidToFsUserId(Uid uid) - { - return new UserId(uid.Id.High, uid.Id.Low); - } } \ No newline at end of file diff --git a/src/LibHac/Fs/CacheStorageTypes.cs b/src/LibHac/Fs/CacheStorageTypes.cs new file mode 100644 index 00000000..d9b5eaa9 --- /dev/null +++ b/src/LibHac/Fs/CacheStorageTypes.cs @@ -0,0 +1,19 @@ +using LibHac.Common.FixedArrays; + +namespace LibHac.Fs; + +public readonly struct CacheStorageListHandle +{ + internal readonly object Cache; + + internal CacheStorageListHandle(object cache) + { + Cache = cache; + } +} + +public struct CacheStorageInfo +{ + public int Index; + public Array28 Reserved; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/SaveDataManagement.cs b/src/LibHac/Fs/Shim/SaveDataManagement.cs index c785a48e..6c9444ba 100644 --- a/src/LibHac/Fs/Shim/SaveDataManagement.cs +++ b/src/LibHac/Fs/Shim/SaveDataManagement.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; @@ -12,682 +14,290 @@ using LibHac.Sf; using LibHac.Time; using static LibHac.Fs.Impl.AccessLogStrings; -namespace LibHac.Fs.Shim; - -public class SaveDataIterator : IDisposable +namespace LibHac.Fs { - private readonly FileSystemClient _fsClient; - private SharedRef _reader; - - internal SaveDataIterator(FileSystemClient fsClient, ref SharedRef reader) + /// + /// Allows iterating through the of a list of save data. + /// + /// Based on nnSdk 13.4.0 + public class SaveDataIterator : IDisposable { - _reader = SharedRef.CreateMove(ref reader); - _fsClient = fsClient; - } + private readonly FileSystemClient _fsClient; + private SharedRef _reader; - public void Dispose() - { - _reader.Destroy(); - } - - private Result ReadSaveDataInfoImpl(out long readCount, Span buffer) - { - Result rc = _reader.Get.Read(out readCount, OutBuffer.FromSpan(buffer)); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public Result ReadSaveDataInfo(out long readCount, Span buffer) - { - Result rc; - FileSystemClient fs = _fsClient; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + internal SaveDataIterator(FileSystemClient fsClient, ref SharedRef reader) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = ReadSaveDataInfoImpl(out readCount, buffer); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSize).AppendFormat(buffer.Length, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = ReadSaveDataInfoImpl(out readCount, buffer); + _reader = SharedRef.CreateMove(ref reader); + _fsClient = fsClient; } - fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc.Miss(); + public void Dispose() + { + _reader.Destroy(); + } - return Result.Success; + private Result ReadSaveDataInfoImpl(out long readCount, Span buffer) + { + Result rc = _reader.Get.Read(out readCount, OutBuffer.FromSpan(buffer)); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public Result ReadSaveDataInfo(out long readCount, Span buffer) + { + Result rc; + FileSystemClient fs = _fsClient; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = ReadSaveDataInfoImpl(out readCount, buffer); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSize).AppendFormat(buffer.Length, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = ReadSaveDataInfoImpl(out readCount, buffer); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } } } -[SkipLocalsInit] -public static class SaveDataManagement +namespace LibHac.Fs.Shim { - public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, - out SaveDataExtraData extraData, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out extraData); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraData(OutBuffer.FromStruct(ref extraData), saveDataId); - if (rc.IsFailure()) return rc; - - return Result.Success; - } - - public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, - out SaveDataExtraData extraData, SaveDataSpaceId spaceId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out extraData); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataSpaceId( - OutBuffer.FromStruct(ref extraData), spaceId, saveDataId); - if (rc.IsFailure()) return rc; - - return Result.Success; - } - - public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, - out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute) - { - UnsafeHelpers.SkipParamInit(out extraData); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataAttribute( - OutBuffer.FromStruct(ref extraData), spaceId, in attribute); - if (rc.IsFailure()) return rc; - - return Result.Success; - } - - public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, - out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, - in SaveDataExtraData extraDataMask) - { - UnsafeHelpers.SkipParamInit(out extraData); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( - OutBuffer.FromStruct(ref extraData), spaceId, in attribute, InBuffer.FromStruct(in extraDataMask)); - if (rc.IsFailure()) return rc; - - return Result.Success; - } - /// - /// Writes the of the provided - /// to the save data in the specified with the provided save data ID. + /// Contains functions for creating, deleting, and otherwise managing save data. /// - /// The to use. - /// The containing the save data to be written to. - /// The save data ID of the save data to be written to. - /// The containing the data to write. - /// : The operation was successful.
- /// : The save data was not found.
- /// : Insufficient permissions.
- public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, - ulong saveDataId, in SaveDataExtraData extraData) + /// Based on nnSdk 13.4.0 + [SkipLocalsInit] + public static class SaveDataManagement { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraData(saveDataId, spaceId, - InBuffer.FromStruct(in extraData)); - fs.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - /// - /// Writes the provided to the save data in the specified - /// with the provided save data ID. - /// - /// The to use. - /// The containing the save data to be written to. - /// The save data ID of the save data to be written to. - /// The to write to the save data. - /// A mask specifying which bits of - /// to write to the save's extra data. 0 = don't write, 1 = write. - /// : The operation was successful.
- /// : The save data was not found.
- /// : Insufficient permissions.
- /// - /// Calling programs may have permission to write to all, some or none of the save data's extra data. - /// If any bits are set in that the caller does not have the permissions - /// to write to, nothing will be written and will be returned. - /// - public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, - ulong saveDataId, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMask(saveDataId, spaceId, - InBuffer.FromStruct(in extraData), InBuffer.FromStruct(in extraDataMask)); - fs.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - /// - /// Writes the provided to the save data in the specified - /// that matches the provided key. - /// The mask specifies which parts of the extra data will be written to the save data. - /// - /// The to use. - /// The containing the save data to be written to. - /// The key for the save data. - /// The to write to the save data. - /// A mask specifying which bits of - /// to write to the save's extra data. 0 = don't write, 1 = write. - /// : The operation was successful.
- /// : The save data was not found.
- /// : Insufficient permissions.
- /// - /// If is set to , - /// the program ID of will be resolved to the default save data program ID of the calling program.
- /// Calling programs may have permission to write to all, some or none of the save data's extra data. - /// If any bits are set in that the caller does not have the permissions - /// to write to, nothing will be written and will be returned. - ///
- public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, - in SaveDataAttribute attribute, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in attribute, - spaceId, InBuffer.FromStruct(in extraData), InBuffer.FromStruct(in extraDataMask)); - fs.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public static Result FindSaveDataWithFilter(this FileSystemClientImpl fs, out SaveDataInfo saveInfo, - SaveDataSpaceId spaceId, in SaveDataFilter filter) - { - UnsafeHelpers.SkipParamInit(out saveInfo); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Unsafe.SkipInit(out SaveDataInfo tempInfo); - OutBuffer saveInfoBuffer = OutBuffer.FromStruct(ref tempInfo); - - Result rc = fileSystemProxy.Get.FindSaveDataWithFilter(out long count, saveInfoBuffer, spaceId, in filter); - if (rc.IsFailure()) return rc; - - if (count == 0) - return ResultFs.TargetNotFound.Log(); - - saveInfo = tempInfo; - return Result.Success; - } - - public static Result CreateSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, - UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, - userId, 0); - if (rc.IsFailure()) return rc; - - rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, - SaveDataSpaceId.User); - if (rc.IsFailure()) return rc; - - var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Account); - metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); - - rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public static Result CreateBcatSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, - long size) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, - Fs.SaveData.InvalidUserId, 0); - if (rc.IsFailure()) return rc; - - rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, - SaveDataProperties.BcatSaveDataJournalSize, SystemProgramId.Bcat.Value, SaveDataFlags.None, - SaveDataSpaceId.User); - if (rc.IsFailure()) return rc; - - var metaInfo = new SaveDataMetaInfo + private class CacheStorageListCache : IDisposable { - Type = SaveDataMetaType.None, - Size = 0 - }; + public readonly struct CacheEntry + { + private readonly int _index; - rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); - if (rc.IsFailure()) return rc.Miss(); + public CacheEntry(int index) => _index = index; + public int GetCacheStorageIndex() => _index; + } - return Result.Success; - } + private int _position; + private List _entryList; - public static Result CreateDeviceSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + public CacheStorageListCache() + { + _position = 0; + _entryList = new List(); + } - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device, - Fs.SaveData.InvalidUserId, 0); - if (rc.IsFailure()) return rc; + public void Dispose() { } - rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, - SaveDataSpaceId.User); - if (rc.IsFailure()) return rc; + public Result PushBack(in CacheEntry entry) + { + _entryList.Add(entry); - var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Device); - metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + // The original code can have allocation failures here + return Result.Success; + } - rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); - if (rc.IsFailure()) return rc.Miss(); + public ref readonly CacheEntry PopFront() + { + if (_position >= _entryList.Count) + return ref Unsafe.NullRef(); - return Result.Success; - } + return ref CollectionsMarshal.AsSpan(_entryList)[_position++]; + } - public static Result CreateCacheStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, - SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache, - Fs.SaveData.InvalidUserId, 0, index); - if (rc.IsFailure()) return rc; - - rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, - spaceId); - if (rc.IsFailure()) return rc; - - var metaInfo = new SaveDataMetaInfo - { - Type = SaveDataMetaType.None, - Size = 0 - }; - - rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public static Result CreateSaveData(this FileSystemClientImpl fs, in SaveDataAttribute attribute, - in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - return fileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, - in metaInfo, in hashSalt); - } - - public static Result DeleteSaveData(this FileSystemClientImpl fs, ulong saveDataId) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - return fileSystemProxy.Get.DeleteSaveDataFileSystem(saveDataId); - } - - public static Result DeleteSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); - } - - public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x30]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.DeleteSaveData(saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.DeleteSaveData(saveDataId); + public static CacheStorageListCache GetCacheStorageListCache(CacheStorageListHandle handle) + { + return (CacheStorageListCache)handle.Cache; + } } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, + out SaveDataExtraData extraData, ulong saveDataId) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); + UnsafeHelpers.SkipParamInit(out extraData); - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result DeleteSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - UserId userId) - { - Result rc; - Span logBuffer = stackalloc byte[0x80]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = Delete(fs, spaceId, saveDataId, userId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = Delete(fs, spaceId, saveDataId, userId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result Delete(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, - SaveDataType.System, userId, saveDataId); + Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraData(OutBuffer.FromStruct(ref extraData), saveDataId); if (rc.IsFailure()) return rc; - return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(spaceId, in attribute); - } - } - - public static Result DeleteDeviceSaveData(this FileSystemClient fs, ApplicationId applicationId) - { - Result rc; - Span logBuffer = stackalloc byte[0x30]; - - if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = Delete(fs, applicationId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = Delete(fs, applicationId); + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result Delete(FileSystemClient fs, ApplicationId applicationId) + public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, + out SaveDataExtraData extraData, SaveDataSpaceId spaceId, ulong saveDataId) { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + UnsafeHelpers.SkipParamInit(out extraData); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), - SaveDataType.Device, Fs.SaveData.InvalidUserId, 0); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataSpaceId( + OutBuffer.FromStruct(ref extraData), spaceId, saveDataId); if (rc.IsFailure()) return rc; - return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute); - } - } - - public static Result RegisterSaveDataAtomicDeletion(this FileSystemClient fs, - ReadOnlySpan saveDataIdList) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.RegisterSaveDataFileSystemAtomicDeletion(InBuffer.FromSpan(saveDataIdList)); - fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, - ref UniqueRef outIterator, SaveDataSpaceId spaceId) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - using var reader = new SharedRef(); - - Result rc = fileSystemProxy.Get.OpenSaveDataInfoReaderBySaveDataSpaceId(ref reader.Ref(), spaceId); - if (rc.IsFailure()) return rc; - - using var iterator = new UniqueRef(new SaveDataIterator(fs.Fs, ref reader.Ref())); - - if (!iterator.HasValue) - return ResultFs.AllocationMemoryFailedInSaveDataManagementA.Log(); - - outIterator.Set(ref iterator.Ref()); - - return Result.Success; - } - - public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, - ref UniqueRef outIterator, SaveDataSpaceId spaceId, in SaveDataFilter filter) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - using var reader = new SharedRef(); - - Result rc = fileSystemProxy.Get.OpenSaveDataInfoReaderWithFilter(ref reader.Ref(), spaceId, in filter); - if (rc.IsFailure()) return rc; - - using var iterator = new UniqueRef(new SaveDataIterator(fs.Fs, ref reader.Ref())); - - if (!iterator.HasValue) - return ResultFs.AllocationMemoryFailedInSaveDataManagementA.Log(); - - outIterator.Set(ref iterator.Ref()); - - return Result.Success; - } - - public static Result ReadSaveDataIteratorSaveDataInfo(out long readCount, Span buffer, - in SaveDataIterator iterator) - { - Result rc = iterator.ReadSaveDataInfo(out readCount, buffer); - if (rc.IsFailure()) return rc.Miss(); - - return Result.Success; - } - - public static Result OpenSaveDataIterator(this FileSystemClient fs, ref UniqueRef outIterator, - SaveDataSpaceId spaceId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId); + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result OpenSaveDataIterator(this FileSystemClient fs, ref UniqueRef outIterator, - SaveDataSpaceId spaceId, in SaveDataFilter filter) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, + out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId, in filter); - Tick end = fs.Hos.Os.GetSystemTick(); + UnsafeHelpers.SkipParamInit(out extraData); - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId, in filter); + Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataBySaveDataAttribute( + OutBuffer.FromStruct(ref extraData), spaceId, in attribute); + if (rc.IsFailure()) return rc; + + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, - SaveDataSpaceId spaceId, in SaveDataFilter filter) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, + out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, + in SaveDataExtraData extraDataMask) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); - Tick end = fs.Hos.Os.GetSystemTick(); + UnsafeHelpers.SkipParamInit(out extraData); - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); + Result rc = fileSystemProxy.Get.ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( + OutBuffer.FromStruct(ref extraData), spaceId, in attribute, InBuffer.FromStruct(in extraDataMask)); + if (rc.IsFailure()) return rc; + + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + /// + /// Writes the of the provided + /// to the save data in the specified with the provided save data ID. + /// + /// The to use. + /// The containing the save data to be written to. + /// The save data ID of the save data to be written to. + /// The containing the data to write. + /// : The operation was successful.
+ /// : The save data was not found.
+ /// : Insufficient permissions.
+ public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, + ulong saveDataId, in SaveDataExtraData extraData) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); - Tick end = fs.Hos.Os.GetSystemTick(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraData(saveDataId, spaceId, + InBuffer.FromStruct(in extraData)); + fs.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, - ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + /// + /// Writes the provided to the save data in the specified + /// with the provided save data ID. + /// + /// The to use. + /// The containing the save data to be written to. + /// The save data ID of the save data to be written to. + /// The to write to the save data. + /// A mask specifying which bits of + /// to write to the save's extra data. 0 = don't write, 1 = write. + /// : The operation was successful.
+ /// : The save data was not found.
+ /// : Insufficient permissions.
+ /// + /// Calling programs may have permission to write to all, some or none of the save data's extra data. + /// If any bits are set in that the caller does not have the permissions + /// to write to, nothing will be written and will be returned. + /// + public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, + ulong saveDataId, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); - Tick end = fs.Hos.Os.GetSystemTick(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMask(saveDataId, spaceId, + InBuffer.FromStruct(in extraData), InBuffer.FromStruct(in extraDataMask)); + fs.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result CreateSave(FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, - ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) + /// + /// Writes the provided to the save data in the specified + /// that matches the provided key. + /// The mask specifies which parts of the extra data will be written to the save data. + /// + /// The to use. + /// The containing the save data to be written to. + /// The key for the save data. + /// The to write to the save data. + /// A mask specifying which bits of + /// to write to the save's extra data. 0 = don't write, 1 = write. + /// : The operation was successful.
+ /// : The save data was not found.
+ /// : Insufficient permissions.
+ /// + /// If is set to , + /// the program ID of will be resolved to the default save data program ID of the calling program.
+ /// Calling programs may have permission to write to all, some or none of the save data's extra data. + /// If any bits are set in that the caller does not have the permissions + /// to write to, nothing will be written and will be returned. + ///
+ public static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, + in SaveDataAttribute attribute, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask) { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in attribute, + spaceId, InBuffer.FromStruct(in extraData), InBuffer.FromStruct(in extraDataMask)); + fs.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result FindSaveDataWithFilter(this FileSystemClientImpl fs, out SaveDataInfo saveInfo, + SaveDataSpaceId spaceId, in SaveDataFilter filter) + { + UnsafeHelpers.SkipParamInit(out saveInfo); + + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Unsafe.SkipInit(out SaveDataInfo tempInfo); + OutBuffer saveInfoBuffer = OutBuffer.FromStruct(ref tempInfo); + + Result rc = fileSystemProxy.Get.FindSaveDataWithFilter(out long count, saveInfoBuffer, spaceId, in filter); + if (rc.IsFailure()) return rc; + + if (count == 0) + return ResultFs.TargetNotFound.Log(); + + saveInfo = tempInfo; + return Result.Success; + } + + public static Result CreateSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, - userId, 0); + userId, staticSaveDataId: 0); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, @@ -697,1349 +307,1950 @@ public static class SaveDataManagement var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Account); metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); - return fileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaInfo, - in hashSalt); - } - } + rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + if (rc.IsFailure()) return rc.Miss(); - public static Result CreateBcatSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, long size) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.CreateBcatSaveData(applicationId, size); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.CreateBcatSaveData(applicationId, size); + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateDeviceSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result CreateBcatSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + long size) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); - Tick end = fs.Hos.Os.GetSystemTick(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, + Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, + SaveDataProperties.BcatSaveDataJournalSize, SystemProgramId.Bcat.Value, SaveDataFlags.None, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; + + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; + + rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateTemporaryStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, - ulong ownerId, long size, SaveDataFlags flags) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary, - Fs.SaveData.InvalidUserId, 0); - if (rc.IsFailure()) return rc; - - rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, 0, ownerId, flags, - SaveDataSpaceId.Temporary); - if (rc.IsFailure()) return rc; - - var metaInfo = new SaveDataMetaInfo - { - Type = SaveDataMetaType.None, - Size = 0 - }; - - return fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); - } - - public static Result CreateTemporaryStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, - ulong ownerId, long size, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, - SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, - SaveDataSpaceId spaceId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - return CreateCacheStorage(fs, applicationId, spaceId, ownerId, 0, size, journalSize, flags); - } - - public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - return CreateCacheStorage(fs, applicationId, SaveDataSpaceId.User, ownerId, size, journalSize, flags); - } - - public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, - ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x100]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) - .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') - .Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result CreateSave(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + public static Result CreateDeviceSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, - SaveDataType.System, userId, saveDataId); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device, + Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; + + var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Device); + metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + + rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result CreateCacheStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) + { + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache, + Fs.SaveData.InvalidUserId, staticSaveDataId: 0, index); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, spaceId); if (rc.IsFailure()) return rc; - return fileSystemProxy.Get.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo); - } - } + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; - public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, - flags); - } + rc = fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + if (rc.IsFailure()) return rc.Miss(); - public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size, - long journalSize, SaveDataFlags flags) - { - return CreateSystemSaveData(fs, saveDataId, userId, 0, size, journalSize, flags); - } - - public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, - long journalSize, SaveDataFlags flags) - { - return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, flags); - } - - public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size, - long journalSize, SaveDataFlags flags) - { - return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, 0, size, journalSize, flags); - } - - public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - ulong ownerId, long size, long journalSize, SaveDataFlags flags) - { - return CreateSystemSaveData(fs, spaceId, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, - flags); - } - - public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId, - long saveDataSize, long journalSize) - { - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - return fileSystemProxy.Get.ExtendSaveDataFileSystem(spaceId, saveDataId, saveDataSize, journalSize); - } - - public static Result ExtendSaveData(this FileSystemClient fs, ulong saveDataId, long saveDataSize, - long journalSize) - { - Result rc; - Span logBuffer = stackalloc byte[0x90]; - - var spaceId = SaveDataSpaceId.System; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result ExtendSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - long saveDataSize, long journalSize) - { - Result rc; - Span logBuffer = stackalloc byte[0x90]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long size, - long journalSize) - { - UnsafeHelpers.SkipParamInit(out totalSize); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.QuerySaveDataTotalSize(out long tempTotalSize, size, journalSize); - if (rc.IsFailure()) return rc.Miss(); - - totalSize = tempTotalSize; - return Result.Success; - } - - public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size, - long journalSize) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataSize).AppendFormat(size, 'd') - .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataOwnerId(this FileSystemClient fs, out ulong ownerId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x40]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetOwnerId(fs, out ownerId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - // Note: Nintendo accidentally uses ", save_data_size: %ld" instead of ", savedataid: 0x%lX" - // for the format string. - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetOwnerId(fs, out ownerId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetOwnerId(FileSystemClient fs, out ulong ownerId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out ownerId); - - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); - if (rc.IsFailure()) return rc; - - ownerId = extraData.OwnerId; return Result.Success; } - } - public static Result GetSaveDataOwnerId(this FileSystemClient fs, out ulong ownerId, SaveDataSpaceId spaceId, - ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result CreateSaveData(this FileSystemClientImpl fs, in SaveDataAttribute attribute, + in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetOwnerId(fs, out ownerId, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetOwnerId(fs, out ownerId, spaceId, saveDataId); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + return fileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, + in metaInfo, in hashSalt); } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetOwnerId(FileSystemClient fs, out ulong ownerId, SaveDataSpaceId spaceId, ulong saveDataId) + public static Result DeleteSaveData(this FileSystemClientImpl fs, ulong saveDataId) { - UnsafeHelpers.SkipParamInit(out ownerId); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + return fileSystemProxy.Get.DeleteSaveDataFileSystem(saveDataId); + } - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, - saveDataId); - if (rc.IsFailure()) return rc; + public static Result DeleteSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId) + { + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); + } + + public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.DeleteSaveData(saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.DeleteSaveData(saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result DeleteSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + UserId userId) + { + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, spaceId, saveDataId, userId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Delete(fs, spaceId, saveDataId, userId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(spaceId, in attribute); + } + } + + public static Result DeleteDeviceSaveData(this FileSystemClient fs, ApplicationId applicationId) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, applicationId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Delete(fs, applicationId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, ApplicationId applicationId) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), + SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; + + return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute); + } + } + + public static Result RegisterSaveDataAtomicDeletion(this FileSystemClient fs, + ReadOnlySpan saveDataIdList) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.RegisterSaveDataFileSystemAtomicDeletion(InBuffer.FromSpan(saveDataIdList)); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); - ownerId = extraData.OwnerId; return Result.Success; } - } - public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x40]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, + ref UniqueRef outIterator, SaveDataSpaceId spaceId) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetFlags(fs, out flags, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + using var reader = new SharedRef(); - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetFlags(fs, out flags, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out flags); - - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + Result rc = fileSystemProxy.Get.OpenSaveDataInfoReaderBySaveDataSpaceId(ref reader.Ref(), spaceId); if (rc.IsFailure()) return rc; - flags = extraData.Flags; + using var iterator = new UniqueRef(new SaveDataIterator(fs.Fs, ref reader.Ref())); + + if (!iterator.HasValue) + return ResultFs.AllocationMemoryFailedInSaveDataManagementA.Log(); + + outIterator.Set(ref iterator.Ref()); + return Result.Success; } - } - public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, - SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, + ref UniqueRef outIterator, SaveDataSpaceId spaceId, in SaveDataFilter filter) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetFlags(fs, out flags, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + using var reader = new SharedRef(); - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); + Result rc = fileSystemProxy.Get.OpenSaveDataInfoReaderWithFilter(ref reader.Ref(), spaceId, in filter); + if (rc.IsFailure()) return rc; - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + using var iterator = new UniqueRef(new SaveDataIterator(fs.Fs, ref reader.Ref())); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetFlags(fs, out flags, spaceId, saveDataId); + if (!iterator.HasValue) + return ResultFs.AllocationMemoryFailedInSaveDataManagementA.Log(); + + outIterator.Set(ref iterator.Ref()); + + return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - return rc; + public static Result ReadSaveDataIteratorSaveDataInfo(this FileSystemClientImpl fs, out long readCount, + Span buffer, in SaveDataIterator iterator) + { + Result rc = iterator.ReadSaveDataInfo(out readCount, buffer); + if (rc.IsFailure()) return rc.Miss(); - static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, + return Result.Success; + } + + public static Result OpenSaveDataIterator(this FileSystemClient fs, ref UniqueRef outIterator, + SaveDataSpaceId spaceId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result OpenSaveDataIterator(this FileSystemClient fs, ref UniqueRef outIterator, + SaveDataSpaceId spaceId, in SaveDataFilter filter) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId, in filter); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.OpenSaveDataIterator(ref outIterator, spaceId, in filter); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, + SaveDataSpaceId spaceId, in SaveDataFilter filter) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result CreateSave(FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, + userId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; + + var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Account); + metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + + return fileSystemProxy.Get.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaInfo, + in hashSalt); + } + } + + public static Result CreateBcatSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, long size) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateBcatSaveData(applicationId, size); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateBcatSaveData(applicationId, size); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateDeviceSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateTemporaryStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, SaveDataFlags flags) + { + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary, + Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize: 0, ownerId, flags, + SaveDataSpaceId.Temporary); + if (rc.IsFailure()) return rc; + + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; + + return fileSystemProxy.Get.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + } + + public static Result CreateTemporaryStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, + SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, + SaveDataSpaceId spaceId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + return CreateCacheStorage(fs, applicationId, spaceId, ownerId, index: 0, size, journalSize, flags); + } + + public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + return CreateCacheStorage(fs, applicationId, SaveDataSpaceId.User, ownerId, size, journalSize, flags); + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, + ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd') + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result CreateSave(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + spaceId); + if (rc.IsFailure()) return rc; + + return fileSystemProxy.Get.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo); + } + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, + flags); + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size, + long journalSize, SaveDataFlags flags) + { + return CreateSystemSaveData(fs, saveDataId, userId, ownerId: 0, size, journalSize, flags); + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, + long journalSize, SaveDataFlags flags) + { + return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, flags); + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size, + long journalSize, SaveDataFlags flags) + { + return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, ownerId: 0, size, journalSize, flags); + } + + public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + return CreateSystemSaveData(fs, spaceId, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, + flags); + } + + public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId, + long saveDataSize, long journalSize) + { + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + return fileSystemProxy.Get.ExtendSaveDataFileSystem(spaceId, saveDataId, saveDataSize, journalSize); + } + + public static Result ExtendSaveData(this FileSystemClient fs, ulong saveDataId, long saveDataSize, + long journalSize) + { + Result rc; + Span logBuffer = stackalloc byte[0x90]; + + var spaceId = SaveDataSpaceId.System; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result ExtendSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + long saveDataSize, long journalSize) + { + Result rc; + Span logBuffer = stackalloc byte[0x90]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(journalSize, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, saveDataSize, journalSize); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long saveDataSize, + long saveDataJournalSize) + { + UnsafeHelpers.SkipParamInit(out totalSize); + + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.QuerySaveDataTotalSize(out long tempTotalSize, saveDataSize, + saveDataJournalSize); + if (rc.IsFailure()) return rc.Miss(); + + totalSize = tempTotalSize; + return Result.Success; + } + + public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long saveDataSize, + long saveDataJournalSize) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, saveDataSize, saveDataJournalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSize).AppendFormat(saveDataSize, 'd') + .Append(LogSaveDataJournalSize).AppendFormat(saveDataJournalSize, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, saveDataSize, saveDataJournalSize); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result GetSaveDataOwnerId(this FileSystemClient fs, out ulong ownerId, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetOwnerId(fs, out ownerId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + // Note: Nintendo accidentally uses ", save_data_size: %ld" instead of ", savedataid: 0x%lX" + // for the format string. + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetOwnerId(fs, out ownerId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetOwnerId(FileSystemClient fs, out ulong ownerId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out ownerId); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + ownerId = extraData.OwnerId; + return Result.Success; + } + } + + public static Result GetSaveDataOwnerId(this FileSystemClient fs, out ulong ownerId, SaveDataSpaceId spaceId, ulong saveDataId) { - UnsafeHelpers.SkipParamInit(out flags); + Result rc; + Span logBuffer = stackalloc byte[0x50]; - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, - saveDataId); - if (rc.IsFailure()) return rc; + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetOwnerId(fs, out ownerId, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); - flags = extraData.Flags; - return Result.Success; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetOwnerId(fs, out ownerId, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetOwnerId(FileSystemClient fs, out ulong ownerId, SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out ownerId); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + ownerId = extraData.OwnerId; + return Result.Success; + } } - } - public static Result GetSystemSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, - SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) - { - Result rc; - Span logBuffer = stackalloc byte[0x80]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); - Tick end = fs.Hos.Os.GetSystemTick(); + Result rc; + Span logBuffer = stackalloc byte[0x40]; - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } - else + + public static Result GetSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, + SaveDataSpaceId spaceId, ulong saveDataId) { - rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, + ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, - ulong saveDataId, UserId userId) + public static Result GetSystemSaveDataFlags(this FileSystemClient fs, out SaveDataFlags flags, + SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { - UnsafeHelpers.SkipParamInit(out flags); + Result rc; + Span logBuffer = stackalloc byte[0x80]; - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, - SaveDataType.System, userId, saveDataId); - if (rc.IsFailure()) return rc; + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); + Tick end = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, in attribute); - if (rc.IsFailure()) return rc; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); - flags = extraData.Flags; - return Result.Success; + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetFlags(fs, out flags, spaceId, saveDataId, userId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetFlags(FileSystemClient fs, out SaveDataFlags flags, SaveDataSpaceId spaceId, + ulong saveDataId, UserId userId) + { + UnsafeHelpers.SkipParamInit(out flags); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, in attribute); + if (rc.IsFailure()) return rc; + + flags = extraData.Flags; + return Result.Success; + } } - } - public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataFlags flags) - { - return SetSaveDataFlags(fs, saveDataId, SaveDataSpaceId.System, flags); - } - - public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, - SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0x70]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataFlags flags) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = SetFlags(fs, saveDataId, spaceId, flags); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = SetFlags(fs, saveDataId, spaceId, flags); + return SetSaveDataFlags(fs, saveDataId, SaveDataSpaceId.System, flags); } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result SetFlags(FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataFlags flags) - { - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, - saveDataId); - if (rc.IsFailure()) return rc; - - extraData.Flags = flags; - - return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData); - } - } - - public static Result SetSystemSaveDataFlags(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - UserId userId, SaveDataFlags flags) - { - Result rc; - Span logBuffer = stackalloc byte[0xA0]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = SetFlags(fs, spaceId, saveDataId, userId, flags); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) - .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = SetFlags(fs, spaceId, saveDataId, userId, flags); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + public static Result SetSaveDataFlags(this FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataFlags flags) { - var extraDataMask = new SaveDataExtraData(); - extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF); + Result rc; + Span logBuffer = stackalloc byte[0x70]; - var extraData = new SaveDataExtraData(); - extraData.Flags = flags; + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetFlags(fs, saveDataId, spaceId, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, - SaveDataType.System, userId, saveDataId); - if (rc.IsFailure()) return rc; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); - return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, in attribute, in extraData, in extraDataMask); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetFlags(fs, saveDataId, spaceId, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetFlags(FileSystemClient fs, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataFlags flags) + { + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + extraData.Flags = flags; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData); + } } - } - public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x30]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result SetSystemSaveDataFlags(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + UserId userId, SaveDataFlags flags) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetTimeStamp(fs, out timeStamp, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); + Result rc; + Span logBuffer = stackalloc byte[0xA0]; - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetFlags(fs, spaceId, saveDataId, userId, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetFlags(fs, spaceId, saveDataId, userId, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetFlags(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + SaveDataFlags flags) + { + SaveDataExtraData extraDataMask = default; + extraDataMask.Flags = unchecked((SaveDataFlags)0xFFFFFFFF); + + SaveDataExtraData extraData = default; + extraData.Flags = flags; + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, in attribute, in extraData, in extraDataMask); + } } - else + + public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) { - rc = GetTimeStamp(fs, out timeStamp, saveDataId); + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetTimeStamp(fs, out timeStamp, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetTimeStamp(fs, out timeStamp, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out timeStamp); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); + if (rc.IsFailure()) return rc; + + timeStamp = new PosixTime(extraData.TimeStamp); + return Result.Success; + } } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out timeStamp); - - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); - if (rc.IsFailure()) return rc; - - timeStamp = new PosixTime(extraData.TimeStamp); - return Result.Success; - } - } - - public static Result SetSaveDataTimeStamp(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - PosixTime timeStamp) - { - Result rc; - Span logBuffer = stackalloc byte[0x80]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataTimeStamp).AppendFormat(timeStamp.Value, 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result SetTimeStamp(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + public static Result SetSaveDataTimeStamp(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, PosixTime timeStamp) { - var extraDataMask = new SaveDataExtraData(); - extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF); + Result rc; + Span logBuffer = stackalloc byte[0x80]; - var extraData = new SaveDataExtraData(); - extraData.TimeStamp = timeStamp.Value; + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); + Tick end = fs.Hos.Os.GetSystemTick(); - return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataTimeStamp).AppendFormat(timeStamp.Value, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetTimeStamp(fs, spaceId, saveDataId, timeStamp); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetTimeStamp(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + PosixTime timeStamp) + { + SaveDataExtraData extraDataMask = default; + extraDataMask.TimeStamp = unchecked((long)0xFFFFFFFFFFFFFFFF); + + SaveDataExtraData extraData = default; + extraData.TimeStamp = timeStamp.Value; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); + } } - } - public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, - SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result GetSaveDataTimeStamp(this FileSystemClient fs, out PosixTime timeStamp, + SaveDataSpaceId spaceId, ulong saveDataId) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetTimeStamp(fs, out timeStamp, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, SaveDataSpaceId spaceId, + ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out timeStamp); + + Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, + saveDataId); + if (rc.IsFailure()) return rc; + + timeStamp = new PosixTime(extraData.TimeStamp); + return Result.Success; + } } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetTimeStamp(FileSystemClient fs, out PosixTime timeStamp, SaveDataSpaceId spaceId, + public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, ulong saveDataId) { - UnsafeHelpers.SkipParamInit(out timeStamp); + UnsafeHelpers.SkipParamInit(out availableSize); - Result rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, - saveDataId); + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); if (rc.IsFailure()) return rc; - timeStamp = new PosixTime(extraData.TimeStamp); - return Result.Success; - } - } - - public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, - ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out availableSize); - - Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); - if (rc.IsFailure()) return rc; - - availableSize = extraData.DataSize; - return Result.Success; - } - - public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, - SaveDataSpaceId spaceId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out availableSize); - - Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); - if (rc.IsFailure()) return rc; - - availableSize = extraData.DataSize; - return Result.Success; - } - - public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, - ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x40]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, - SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, - ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out journalSize); - - Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); - if (rc.IsFailure()) return rc; - - journalSize = extraData.JournalSize; - return Result.Success; - } - - public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, - SaveDataSpaceId spaceId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out journalSize); - - Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); - if (rc.IsFailure()) return rc; - - journalSize = extraData.JournalSize; - return Result.Success; - } - - public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x40]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, - SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataCommitId(this FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, - ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetCommitId(fs, out commitId, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetCommitId(fs, out commitId, spaceId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetCommitId(FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out commitId); - - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - return fileSystemProxy.Get.GetSaveDataCommitId(out commitId, spaceId, saveDataId); - } - } - - public static Result SetSaveDataCommitId(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, - long commitId) - { - Result rc; - Span logBuffer = stackalloc byte[0x80]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = SetCommitId(fs, spaceId, saveDataId, commitId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') - .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataCommitId).AppendFormat(commitId, 'X', 16); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = SetCommitId(fs, spaceId, saveDataId, commitId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result SetCommitId(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId) - { - var extraDataMask = new SaveDataExtraData(); - extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF); - - var extraData = new SaveDataExtraData(); - extraData.CommitId = commitId; - - return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); - } - } - - public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClientImpl fs, out long size, - SaveDataSpaceId spaceId, ulong saveDataId) - { - UnsafeHelpers.SkipParamInit(out size); - - using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - - return fileSystemProxy.Get.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); - } - - public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClient fs, out long size, - SaveDataSpaceId spaceId, ulong saveDataId) - { - Result rc; - Span logBuffer = stackalloc byte[0x50]; - - if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var idString = new IdString(); - var sb = new U8StringBuilder(logBuffer, true); - - sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) - .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, ulong saveDataId, - Span workBuffer) - { - return VerifySaveData(fs, out isValid, SaveDataSpaceId.System, saveDataId, workBuffer); - } - - public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, SaveDataSpaceId spaceId, - ulong saveDataId, Span workBuffer) - { - UnsafeHelpers.SkipParamInit(out isValid); - - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.VerifySaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId, - new OutBuffer(workBuffer)); - - if (ResultFs.DataCorrupted.Includes(rc)) - { - isValid = false; + availableSize = extraData.DataSize; return Result.Success; } - fs.Impl.AbortIfNeeded(rc); - - if (rc.IsSuccess()) + public static Result GetSaveDataAvailableSize(this FileSystemClientImpl fs, out long availableSize, + SaveDataSpaceId spaceId, ulong saveDataId) { - isValid = true; + UnsafeHelpers.SkipParamInit(out availableSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); + if (rc.IsFailure()) return rc; + + availableSize = extraData.DataSize; return Result.Success; } - return rc; - } - - public static Result CorruptSaveDataForDebug(this FileSystemClient fs, ulong saveDataId) - { - return CorruptSaveDataForDebug(fs, SaveDataSpaceId.System, saveDataId); - } - - public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, - ulong saveDataId) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.CorruptSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, - ulong saveDataId, long offset) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.CorruptSaveDataFileSystemByOffset(spaceId, saveDataId, offset); - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static void DisableAutoSaveDataCreation(this FileSystemClient fs) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fileSystemProxy.Get.DisableAutoSaveDataCreation(); - - fs.Impl.LogResultErrorMessage(rc); - Abort.DoAbortUnless(rc.IsSuccess()); - } - - public static Result DeleteCacheStorage(this FileSystemClient fs, int index) - { - Result rc; - Span logBuffer = stackalloc byte[0x20]; - - if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, + ulong saveDataId) { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = Delete(fs, index); - Tick end = fs.Hos.Os.GetSystemTick(); + Result rc; + Span logBuffer = stackalloc byte[0x40]; - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogIndex).AppendFormat(index, 'd'); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = Delete(fs, index); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result Delete(FileSystemClient fs, int index) + public static Result GetSaveDataAvailableSize(this FileSystemClient fs, out long availableSize, + SaveDataSpaceId spaceId, ulong saveDataId) { - if (index < 0) - return ResultFs.InvalidArgument.Log(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); - return fileSystemProxy.Get.DeleteCacheStorage((ushort)index); - } - } + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); - public static Result GetCacheStorageSize(this FileSystemClient fs, out long saveSize, out long journalSize, - int index) - { - Result rc; - Span logBuffer = stackalloc byte[0x60]; + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); - if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetSize(fs, out saveSize, out journalSize, index); - Tick end = fs.Hos.Os.GetSystemTick(); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataAvailableSize(out availableSize, spaceId, saveDataId); + } - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogIndex).AppendFormat(index, 'd') - .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') - .Append(LogSaveDataJournalSize) - .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetSize(fs, out saveSize, out journalSize, index); + fs.Impl.AbortIfNeeded(rc); + return rc; } - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetSize(FileSystemClient fs, out long saveSize, out long journalSize, int index) + public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, + ulong saveDataId) { - UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); + UnsafeHelpers.SkipParamInit(out journalSize); - if (index < 0) - return ResultFs.InvalidArgument.Log(); - - // Note: Nintendo gets the service object in the outer function and captures it for the inner function - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - return fileSystemProxy.Get.GetCacheStorageSize(out saveSize, out journalSize, (ushort)index); - } - } - - public static Result UpdateSaveDataMacForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, - ulong saveDataId) - { - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - return fileSystemProxy.Get.UpdateSaveDataMacForDebug(spaceId, saveDataId); - } - - public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int readCount, - Span idBuffer, Ncm.ApplicationId applicationId, int programIndex, int startIndex) - { - if (idBuffer.IsEmpty) - { - readCount = 0; - return Result.Success; - } - - using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - var programId = new ProgramId(applicationId.Value + (uint)programIndex); - OutBuffer idOutBuffer = OutBuffer.FromSpan(idBuffer); - - Result rc = fileSystemProxy.Get.ListAccessibleSaveDataOwnerId(out readCount, idOutBuffer, programId, startIndex, - idBuffer.Length); - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result GetSaveDataRestoreFlag(this FileSystemClient fs, out bool isRestoreFlagSet, - U8Span mountName) - { - UnsafeHelpers.SkipParamInit(out isRestoreFlagSet); - - Result rc; - FileSystemAccessor fileSystem; - Span logBuffer = stackalloc byte[0x40]; - - if (fs.Impl.IsEnabledAccessLog()) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = fs.Impl.Find(out fileSystem, mountName); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogName).Append(mountName).Append(LogQuote); - - fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = fs.Impl.Find(out fileSystem, mountName); - } - - fs.Impl.AbortIfNeeded(rc); - if (rc.IsFailure()) return rc; - - if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); - Tick end = fs.Hos.Os.GetSystemTick(); - - ReadOnlySpan isSetString = - AccessLogImpl.ConvertFromBoolToAccessLogBooleanValue( - AccessLogImpl.DereferenceOutValue(in isRestoreFlagSet, rc)); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogName).Append(mountName).Append(LogQuote) - .Append(LogRestoreFlag).Append(isSetString); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetRestoreFlagValue(FileSystemClient fs, out bool isRestoreFlagSet, - FileSystemAccessor fileSystem) - { - Unsafe.SkipInit(out isRestoreFlagSet); - - if (fileSystem is null) - return ResultFs.NullptrArgument.Log(); - - Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute); + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, saveDataId); if (rc.IsFailure()) return rc; - if (attribute.ProgramId == Fs.SaveData.InvalidProgramId) - attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId; - - var extraDataMask = new SaveDataExtraData(); - extraDataMask.Flags = SaveDataFlags.Restore; - - rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, - in attribute, in extraDataMask); - if (rc.IsFailure()) return rc; - - isRestoreFlagSet = extraData.Flags.HasFlag(SaveDataFlags.Restore); - return Result.Success; - } - } - - public static Result GetDeviceSaveDataSize(this FileSystemClient fs, out long saveSize, - out long journalSize, ApplicationId applicationId) - { - Result rc; - Span logBuffer = stackalloc byte[0x70]; - - if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) - { - Tick start = fs.Hos.Os.GetSystemTick(); - rc = GetSize(fs, out saveSize, out journalSize, applicationId); - Tick end = fs.Hos.Os.GetSystemTick(); - - var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') - .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') - .Append(LogSaveDataJournalSize) - .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); - - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); - } - else - { - rc = GetSize(fs, out saveSize, out journalSize, applicationId); - } - - fs.Impl.AbortIfNeeded(rc); - return rc; - - static Result GetSize(FileSystemClient fs, out long saveSize, out long journalSize, - ApplicationId applicationId) - { - UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); - - var extraDataMask = new SaveDataExtraData(); - extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF); - extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF); - - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), - SaveDataType.Device, Fs.SaveData.InvalidUserId, 0); - if (rc.IsFailure()) return rc; - - rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, - in attribute, in extraDataMask); - if (rc.IsFailure()) return rc; - - saveSize = extraData.DataSize; journalSize = extraData.JournalSize; - return Result.Success; } + + public static Result GetSaveDataJournalSize(this FileSystemClientImpl fs, out long journalSize, + SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out journalSize); + + Result rc = fs.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, spaceId, saveDataId); + if (rc.IsFailure()) return rc; + + journalSize = extraData.JournalSize; + return Result.Success; + } + + public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result GetSaveDataJournalSize(this FileSystemClient fs, out long journalSize, + SaveDataSpaceId spaceId, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.GetSaveDataJournalSize(out journalSize, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result GetSaveDataCommitId(this FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, + ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetCommitId(fs, out commitId, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetCommitId(fs, out commitId, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetCommitId(FileSystemClient fs, out long commitId, SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out commitId); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fileSystemProxy.Get.GetSaveDataCommitId(out commitId, spaceId, saveDataId); + } + } + + public static Result SetSaveDataCommitId(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, + long commitId) + { + Result rc; + Span logBuffer = stackalloc byte[0x80]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = SetCommitId(fs, spaceId, saveDataId, commitId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataCommitId).AppendFormat(commitId, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = SetCommitId(fs, spaceId, saveDataId, commitId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result SetCommitId(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, long commitId) + { + SaveDataExtraData extraDataMask = default; + extraDataMask.CommitId = unchecked((long)0xFFFFFFFFFFFFFFFF); + + SaveDataExtraData extraData = default; + extraData.CommitId = commitId; + + return fs.Impl.WriteSaveDataFileSystemExtraData(spaceId, saveDataId, in extraData, in extraDataMask); + } + } + + public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClientImpl fs, out long size, + SaveDataSpaceId spaceId, ulong saveDataId) + { + UnsafeHelpers.SkipParamInit(out size); + + using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); + + return fileSystemProxy.Get.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); + } + + public static Result QuerySaveDataInternalStorageTotalSize(this FileSystemClient fs, out long size, + SaveDataSpaceId spaceId, ulong saveDataId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.QuerySaveDataInternalStorageTotalSize(out size, spaceId, saveDataId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, ulong saveDataId, + Span workBuffer) + { + return VerifySaveData(fs, out isValid, SaveDataSpaceId.System, saveDataId, workBuffer); + } + + public static Result VerifySaveData(this FileSystemClient fs, out bool isValid, SaveDataSpaceId spaceId, + ulong saveDataId, Span workBuffer) + { + UnsafeHelpers.SkipParamInit(out isValid); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.VerifySaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId, + new OutBuffer(workBuffer)); + + if (ResultFs.DataCorrupted.Includes(rc)) + { + isValid = false; + return Result.Success; + } + + fs.Impl.AbortIfNeeded(rc); + + if (rc.IsSuccess()) + { + isValid = true; + return Result.Success; + } + + return rc; + } + + public static Result CorruptSaveDataForDebug(this FileSystemClient fs, ulong saveDataId) + { + return CorruptSaveDataForDebug(fs, SaveDataSpaceId.System, saveDataId); + } + + public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, + ulong saveDataId) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.CorruptSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CorruptSaveDataForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, + ulong saveDataId, long offset) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.CorruptSaveDataFileSystemByOffset(spaceId, saveDataId, offset); + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static void DisableAutoSaveDataCreation(this FileSystemClient fs) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.DisableAutoSaveDataCreation(); + + fs.Impl.LogResultErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + } + + public static Result DeleteCacheStorage(this FileSystemClient fs, int index) + { + Result rc; + Span logBuffer = stackalloc byte[0x20]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Delete(fs, index); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogIndex).AppendFormat(index, 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Delete(fs, index); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Delete(FileSystemClient fs, int index) + { + if (index < 0) + return ResultFs.InvalidArgument.Log(); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fileSystemProxy.Get.DeleteCacheStorage((ushort)index); + } + } + + public static Result GetCacheStorageSize(this FileSystemClient fs, out long saveSize, out long journalSize, + int index) + { + Result rc; + Span logBuffer = stackalloc byte[0x60]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetSize(fs, out saveSize, out journalSize, index); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogIndex).AppendFormat(index, 'd') + .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') + .Append(LogSaveDataJournalSize) + .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetSize(fs, out saveSize, out journalSize, index); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetSize(FileSystemClient fs, out long saveSize, out long journalSize, int index) + { + UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); + + if (index < 0) + return ResultFs.InvalidArgument.Log(); + + // Note: Nintendo gets the service object in the outer function and captures it for the inner function + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return fileSystemProxy.Get.GetCacheStorageSize(out saveSize, out journalSize, (ushort)index); + } + } + + public static Result OpenCacheStorageList(this FileSystemClient fs, out CacheStorageListHandle handle) + { + UnsafeHelpers.SkipParamInit(out handle); + + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + CacheStorageListCache listCache; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Open(fs, out listCache); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogCacheStorageListHandle).AppendFormat(listCache.GetHashCode(), 'x'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Open(fs, out listCache); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + handle = new CacheStorageListHandle(listCache); + return Result.Success; + + static Result Open(FileSystemClient fs, out CacheStorageListCache listCache) + { + UnsafeHelpers.SkipParamInit(out listCache); + CacheStorageListCache tempListCache = null; + + Result rc = Utility.DoContinuouslyUntilSaveDataListFetched(fs.Hos, () => + { + // Note: Nintendo uses the same CacheStorageListCache for every attempt to fetch the save data list + // without clearing it between runs. This means that if it has to retry fetching the list, the + // CacheStorageListCache may contain duplicate entries if the save data indexer was reset while this + // function was running. + tempListCache = new CacheStorageListCache(); + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using var reader = new SharedRef(); + + Result result = fileSystemProxy.Get.OpenSaveDataInfoReaderOnlyCacheStorage(ref reader.Ref()); + if (result.IsFailure()) return result.Miss(); + + while (true) + { + Unsafe.SkipInit(out SaveDataInfo info); + + result = reader.Get.Read(out long readCount, new OutBuffer(SpanHelpers.AsByteSpan(ref info))); + if (result.IsFailure()) return result.Miss(); + + if (readCount == 0) + break; + + var cacheEntry = new CacheStorageListCache.CacheEntry(info.Index); + result = tempListCache.PushBack(in cacheEntry); + if (result.IsFailure()) return result.Miss(); + } + + return Result.Success; + }); + if (rc.IsFailure()) return rc.Miss(); + + Assert.SdkRequiresNotNull(tempListCache); + + listCache = tempListCache; + return Result.Success; + } + } + + public static Result ReadCacheStorageList(this FileSystemClient fs, out int storageInfoReadCount, + Span storageInfoBuffer, CacheStorageListHandle handle) + { + UnsafeHelpers.SkipParamInit(out storageInfoReadCount); + + Result rc; + Span logBuffer = stackalloc byte[0x70]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Read(out storageInfoReadCount, storageInfoBuffer, handle); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogCacheStorageListHandle).AppendFormat(handle.Cache.GetHashCode(), 'x') + .Append(LogInfoBufferCount).AppendFormat(storageInfoBuffer.Length, 'X') + .Append(LogCacheStorageCount).AppendFormat(AccessLogImpl.DereferenceOutValue(in storageInfoReadCount, rc), 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Read(out storageInfoReadCount, storageInfoBuffer, handle); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + + static Result Read(out int storageCount, Span infoBuffer, CacheStorageListHandle handle) + { + int count = 0; + + var listCache = CacheStorageListCache.GetCacheStorageListCache(handle); + + while (count < infoBuffer.Length) + { + ref readonly CacheStorageListCache.CacheEntry entry = ref listCache.PopFront(); + + // We're done iterating if we get a null ref + if (Unsafe.IsNullRef(ref Unsafe.AsRef(in entry))) + break; + + infoBuffer[count] = default; + infoBuffer[count].Index = entry.GetCacheStorageIndex(); + count++; + } + + storageCount = count; + return Result.Success; + } + } + + public static void CloseCacheStorageList(this FileSystemClient fs, CacheStorageListHandle handle) + { + Span logBuffer = stackalloc byte[0x40]; + + var listCache = CacheStorageListCache.GetCacheStorageListCache(handle); + + listCache?.Dispose(); + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogCacheStorageListHandle).AppendFormat(handle.Cache.GetHashCode(), 'x'); + + fs.Impl.OutputAccessLog(Result.Success, start, end, null, new U8Span(sb.Buffer)); + } + } + + public static Result UpdateSaveDataMacForDebug(this FileSystemClient fs, SaveDataSpaceId spaceId, + ulong saveDataId) + { + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fileSystemProxy.Get.UpdateSaveDataMacForDebug(spaceId, saveDataId); + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result ListApplicationAccessibleSaveDataOwnerId(this FileSystemClient fs, out int readCount, + Span idBuffer, Ncm.ApplicationId applicationId, int programIndex, int startIndex) + { + if (idBuffer.IsEmpty) + { + readCount = 0; + return Result.Success; + } + + using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + var programId = new ProgramId(applicationId.Value + (uint)programIndex); + OutBuffer idOutBuffer = OutBuffer.FromSpan(idBuffer); + + Result rc = fileSystemProxy.Get.ListAccessibleSaveDataOwnerId(out readCount, idOutBuffer, programId, startIndex, + idBuffer.Length); + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result GetSaveDataRestoreFlag(this FileSystemClient fs, out bool isRestoreFlagSet, + U8Span mountName) + { + UnsafeHelpers.SkipParamInit(out isRestoreFlagSet); + + Result rc; + FileSystemAccessor fileSystem; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog()) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.Find(out fileSystem, mountName); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.Find(out fileSystem, mountName); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); + + ReadOnlySpan isSetString = + AccessLogImpl.ConvertFromBoolToAccessLogBooleanValue( + AccessLogImpl.DereferenceOutValue(in isRestoreFlagSet, rc)); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogRestoreFlag).Append(isSetString); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetRestoreFlagValue(fs, out isRestoreFlagSet, fileSystem); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetRestoreFlagValue(FileSystemClient fs, out bool isRestoreFlagSet, + FileSystemAccessor fileSystem) + { + Unsafe.SkipInit(out isRestoreFlagSet); + + if (fileSystem is null) + return ResultFs.NullptrArgument.Log(); + + Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute); + if (rc.IsFailure()) return rc; + + if (attribute.ProgramId == Fs.SaveData.InvalidProgramId) + attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId; + + SaveDataExtraData extraDataMask = default; + extraDataMask.Flags = SaveDataFlags.Restore; + + rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, + in attribute, in extraDataMask); + if (rc.IsFailure()) return rc; + + isRestoreFlagSet = extraData.Flags.HasFlag(SaveDataFlags.Restore); + return Result.Success; + } + } + + public static Result GetDeviceSaveDataSize(this FileSystemClient fs, out long saveSize, + out long journalSize, ApplicationId applicationId) + { + Result rc; + Span logBuffer = stackalloc byte[0x70]; + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = GetSize(fs, out saveSize, out journalSize, applicationId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in saveSize, rc), 'd') + .Append(LogSaveDataJournalSize) + .AppendFormat(AccessLogImpl.DereferenceOutValue(in journalSize, rc), 'd'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = GetSize(fs, out saveSize, out journalSize, applicationId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result GetSize(FileSystemClient fs, out long saveSize, out long journalSize, + ApplicationId applicationId) + { + UnsafeHelpers.SkipParamInit(out saveSize, out journalSize); + + SaveDataExtraData extraDataMask = default; + extraDataMask.DataSize = unchecked((long)0xFFFFFFFFFFFFFFFF); + extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), + SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + if (rc.IsFailure()) return rc; + + rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, + in attribute, in extraDataMask); + if (rc.IsFailure()) return rc; + + saveSize = extraData.DataSize; + journalSize = extraData.JournalSize; + + return Result.Success; + } + } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Utility.cs b/src/LibHac/Fs/Utility.cs new file mode 100644 index 00000000..955c983f --- /dev/null +++ b/src/LibHac/Fs/Utility.cs @@ -0,0 +1,50 @@ +using System; +using LibHac.Account; +using LibHac.Os; + +namespace LibHac.Fs; + +/// +/// Contains various utility functions used by FS client code. +/// +/// Based on nnSdk 13.4.0 +public static class Utility +{ + public static UserId ConvertAccountUidToFsUserId(Uid uid) + { + return new UserId(uid.Id.High, uid.Id.Low); + } + + public static Result CheckUid(HorizonClient hos, Uid uid) + { + throw new NotImplementedException(); + } + + public static Result DoContinuouslyUntilSaveDataListFetched(HorizonClient hos, Func listGetter) + { + const int maxTryCount = 5; + const int initialSleepTimeMs = 5; + const int sleepTimeMultiplier = 2; + + Result lastResult = Result.Success; + long sleepTime = initialSleepTimeMs; + + for (int i = 0; i < maxTryCount; i++) + { + Result rc = listGetter(); + + if (rc.IsSuccess()) + return rc; + + // Try again if any save data were added or removed while getting the list + if (!ResultFs.InvalidHandle.Includes(rc)) + return rc.Miss(); + + lastResult = rc; + hos.Os.SleepThread(TimeSpan.FromMilliSeconds(sleepTime)); + sleepTime *= sleepTimeMultiplier; + } + + return lastResult.Log(); + } +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs index c4e30339..46872992 100644 --- a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs +++ b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs @@ -6,7 +6,8 @@ using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.Fs.Shim; using LibHac.FsSrv.FsCreator; -using LibHac.FsSrv.Impl; + +using Utility = LibHac.FsSrv.Impl.Utility; namespace LibHac.FsSrv; diff --git a/src/LibHac/FsSrv/FsCreator/EmulatedBisFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/EmulatedBisFileSystemCreator.cs index 9255f1b2..368d66ff 100644 --- a/src/LibHac/FsSrv/FsCreator/EmulatedBisFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/EmulatedBisFileSystemCreator.cs @@ -2,7 +2,8 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.FsSrv.Impl; + +using Utility = LibHac.FsSrv.Impl.Utility; namespace LibHac.FsSrv.FsCreator; @@ -134,4 +135,4 @@ public class EmulatedBisFileSystemCreator : IBuiltInStorageFileSystemCreator return string.Empty; } } -} +} \ No newline at end of file diff --git a/src/LibHac/FsSrv/FsCreator/EmulatedSdCardFileSystemCreator.cs b/src/LibHac/FsSrv/FsCreator/EmulatedSdCardFileSystemCreator.cs index ef4a5234..b7359146 100644 --- a/src/LibHac/FsSrv/FsCreator/EmulatedSdCardFileSystemCreator.cs +++ b/src/LibHac/FsSrv/FsCreator/EmulatedSdCardFileSystemCreator.cs @@ -2,9 +2,10 @@ using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.FsSrv.Impl; using LibHac.Util; +using Utility = LibHac.FsSrv.Impl.Utility; + namespace LibHac.FsSrv.FsCreator; public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, IDisposable @@ -85,4 +86,4 @@ public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator, ID { throw new NotImplementedException(); } -} +} \ No newline at end of file diff --git a/src/LibHac/Tools/FsSystem/FileSystemExtensions.cs b/src/LibHac/Tools/FsSystem/FileSystemExtensions.cs index 4df57c9d..72c0c451 100644 --- a/src/LibHac/Tools/FsSystem/FileSystemExtensions.cs +++ b/src/LibHac/Tools/FsSystem/FileSystemExtensions.cs @@ -10,6 +10,7 @@ using LibHac.FsSystem; using LibHac.Tools.Fs; using LibHac.Util; using Path = LibHac.Fs.Path; +using Utility = LibHac.FsSystem.Utility; namespace LibHac.Tools.FsSystem; diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs index 560d8dae..97f3d8d5 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs @@ -35,7 +35,7 @@ public class ApplicationSaveDataManagementTests Assert.Equal(1, entriesRead); Assert.Equal(applicationId, info[0].ProgramId); - Assert.Equal(ConvertAccountUidToFsUserId(userId), info[0].UserId); + Assert.Equal(Utility.ConvertAccountUidToFsUserId(userId), info[0].UserId); Assert.Equal(SaveDataType.Account, info[0].Type); } diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs index 5bf97d8c..dda8a063 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs @@ -577,6 +577,73 @@ public class SaveDataManagement Assert.Equal(timeStamp, actualTimeStamp); } + [Fact] + public void OpenCacheStorageList_ReadIntoLargeBuffer_AllIndexesAreRead() + { + var applicationId = new Ncm.ApplicationId(1); + + Horizon hos = HorizonFactory.CreateBasicHorizon(); + + HorizonClient client = hos.CreateHorizonClient(new ProgramLocation(applicationId, StorageId.BuiltInSystem), + (AccessControlBits.Bits)ulong.MaxValue); + + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 0, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 6, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 2, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 3, 0, 0, SaveDataFlags.None)); + + Assert.Success(client.Fs.OpenCacheStorageList(out CacheStorageListHandle handle)); + var infoBuffer = new CacheStorageInfo[5]; + + Assert.Success(client.Fs.ReadCacheStorageList(out int readCount, infoBuffer, handle)); + + Assert.Equal(4, readCount); + + Assert.Equal(0, infoBuffer[0].Index); + Assert.Equal(2, infoBuffer[1].Index); + Assert.Equal(3, infoBuffer[2].Index); + Assert.Equal(6, infoBuffer[3].Index); + + client.Fs.CloseCacheStorageList(handle); + } + + [Fact] + public void OpenCacheStorageList_ReadIntoMultipleBuffers_AllIndexesAreRead() + { + var applicationId = new Ncm.ApplicationId(1); + + Horizon hos = HorizonFactory.CreateBasicHorizon(); + + HorizonClient client = hos.CreateHorizonClient(new ProgramLocation(applicationId, StorageId.BuiltInSystem), + (AccessControlBits.Bits)ulong.MaxValue); + + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 0, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 6, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 2, 0, 0, SaveDataFlags.None)); + Assert.Success(client.Fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, applicationId.Value, 3, 0, 0, SaveDataFlags.None)); + + Assert.Success(client.Fs.OpenCacheStorageList(out CacheStorageListHandle handle)); + var infoBuffer = new CacheStorageInfo[2]; + + Assert.Success(client.Fs.ReadCacheStorageList(out int readCount, infoBuffer, handle)); + + Assert.Equal(2, readCount); + Assert.Equal(0, infoBuffer[0].Index); + Assert.Equal(2, infoBuffer[1].Index); + + Assert.Success(client.Fs.ReadCacheStorageList(out readCount, infoBuffer, handle)); + + Assert.Equal(2, readCount); + Assert.Equal(3, infoBuffer[0].Index); + Assert.Equal(6, infoBuffer[1].Index); + + Assert.Success(client.Fs.ReadCacheStorageList(out readCount, infoBuffer, handle)); + + Assert.Equal(0, readCount); + + client.Fs.CloseCacheStorageList(handle); + } + private static Result PopulateSaveData(FileSystemClient fs, int count, int seed = -1) { if (seed == -1) diff --git a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs index bbef6c80..363850a6 100644 --- a/tests/LibHac.Tests/Fs/TypeLayoutTests.cs +++ b/tests/LibHac.Tests/Fs/TypeLayoutTests.cs @@ -354,4 +354,15 @@ public class TypeLayoutTests Assert.Equal(3, GetOffset(in s, in s.CompressionRate)); Assert.Equal(4, GetOffset(in s, in s.Reserved)); } + + [Fact] + public static void CacheStorageInfo_Layout() + { + var s = new CacheStorageInfo(); + + Assert.Equal(0x20, Unsafe.SizeOf()); + + Assert.Equal(0, GetOffset(in s, in s.Index)); + Assert.Equal(4, GetOffset(in s, in s.Reserved)); + } } \ No newline at end of file