From 0ba0a9ad9c1f1776d549df111a92c868f90561aa Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 24 Jan 2022 15:02:32 -0700 Subject: [PATCH] Fixup ApplicationSaveDataManagement and update to 13.1.0 --- src/LibHac/Account/Uid.cs | 4 +- src/LibHac/Common/UniqueRef.cs | 17 +- .../Fs/ApplicationSaveDataManagement.cs | 1152 +++++++++++------ src/LibHac/Fs/SaveData.cs | 6 +- src/LibHac/Fs/SaveDataStructs.cs | 52 +- src/LibHac/Fs/Shim/BcatSaveData.cs | 4 +- src/LibHac/Fs/Shim/SaveData.cs | 46 +- src/LibHac/Fs/Shim/SaveDataManagement.cs | 42 +- .../Fs/Shim/SaveDataTransferVersion2.cs | 4 +- src/LibHac/Fs/Shim/SystemSaveData.cs | 8 +- src/LibHac/FsSrv/SaveDataFileSystemService.cs | 45 +- src/LibHac/FsSrv/SaveDataIndexer.cs | 7 +- .../ApplicationSaveDataManagementTests.cs | 81 +- .../ShimTests/SaveDataManagement.cs | 11 +- .../Fs/FsaTests/MultiCommitTests.cs | 10 +- 15 files changed, 958 insertions(+), 531 deletions(-) diff --git a/src/LibHac/Account/Uid.cs b/src/LibHac/Account/Uid.cs index dba9c683..17047878 100644 --- a/src/LibHac/Account/Uid.cs +++ b/src/LibHac/Account/Uid.cs @@ -9,7 +9,7 @@ namespace LibHac.Account; [StructLayout(LayoutKind.Sequential, Size = 0x10)] public struct Uid : IEquatable, IComparable, IComparable { - public static Uid Zero => default; + public static Uid InvalidUid => default; public readonly Id128 Id; @@ -55,4 +55,4 @@ public struct Uid : IEquatable, IComparable, IComparable public static bool operator >(Uid left, Uid right) => left.CompareTo(right) > 0; public static bool operator <=(Uid left, Uid right) => left.CompareTo(right) <= 0; public static bool operator >=(Uid left, Uid right) => left.CompareTo(right) >= 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Common/UniqueRef.cs b/src/LibHac/Common/UniqueRef.cs index 617f132f..87505f36 100644 --- a/src/LibHac/Common/UniqueRef.cs +++ b/src/LibHac/Common/UniqueRef.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using InlineIL; using static InlineIL.IL.Emit; namespace LibHac.Common; @@ -11,7 +12,7 @@ public static class UniqueRefExtensions { Ldarg(nameof(value)); Ret(); - throw InlineIL.IL.Unreachable(); + throw IL.Unreachable(); } } @@ -20,7 +21,17 @@ public struct UniqueRef : IDisposable where T : class, IDisposable { private T _value; - public readonly T Get => _value; + public readonly ref T Get + { + get + { + Ldarg_0(); + Ldflda(new FieldRef(typeof(UniqueRef), nameof(_value))); + Ret(); + throw IL.Unreachable(); + } + } + public readonly bool HasValue => Get is not null; public UniqueRef(T value) @@ -87,4 +98,4 @@ public struct UniqueRef : IDisposable where T : class, IDisposable return oldValue; } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index 61e64ca4..908b73f8 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -1,157 +1,96 @@ using System; -using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using LibHac.Account; using LibHac.Common; +using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.Ns; using LibHac.Util; namespace LibHac.Fs; +/// +/// Contains functions for ensuring that an application's save data exists and is the correct size. +/// +/// Based on nnSdk 13.4.0 public static class ApplicationSaveDataManagement { - public static Result EnsureApplicationSaveData(FileSystemClient fs, out long requiredSize, Ncm.ApplicationId applicationId, - ref ApplicationControlProperty nacp, ref Uid uid) + private const int SaveDataBlockSize = 0x4000; + private const int SaveDataExtensionSizeAlignment = 0x100000; // 1 MiB + + private static long RoundUpOccupationSize(long size) { - UnsafeHelpers.SkipParamInit(out requiredSize); - long requiredSizeSum = 0; - - // Create local variable for use in closures - ulong saveDataOwnerId = nacp.SaveDataOwnerId; - - // Ensure the user account save exists - if (uid != Uid.Zero && nacp.UserAccountSaveDataSize > 0) - { - // More local variables for use in closures - Uid uidLocal = uid; - long accountSaveDataSize = nacp.UserAccountSaveDataSize; - long accountSaveJournalSize = nacp.UserAccountSaveDataJournalSize; - - Result CreateAccountSaveFunc() - { - UserId userId = Utility.ConvertAccountUidToFsUserId(uidLocal); - return fs.CreateSaveData(applicationId, userId, saveDataOwnerId, accountSaveDataSize, - accountSaveJournalSize, SaveDataFlags.None); - } - - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Account); - filter.SetUserId(new UserId(uid.Id.High, uid.Id.Low)); - - // The 0x4c000 includes the save meta and other stuff - Result rc = EnsureAndExtendSaveData(fs, CreateAccountSaveFunc, ref requiredSizeSum, ref filter, 0x4c000, - accountSaveDataSize, accountSaveJournalSize); - - if (rc.IsFailure()) return rc; - } - - // Ensure the device save exists - if (nacp.DeviceSaveDataSize > 0) - { - long deviceSaveDataSize = nacp.DeviceSaveDataSize; - long deviceSaveJournalSize = nacp.DeviceSaveDataJournalSize; - - Result CreateDeviceSaveFunc() => fs.CreateDeviceSaveData(applicationId, saveDataOwnerId, deviceSaveDataSize, - deviceSaveJournalSize, 0); - - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Device); - - Result rc = EnsureAndExtendSaveData(fs, CreateDeviceSaveFunc, ref requiredSizeSum, ref filter, 0x4000, - deviceSaveDataSize, deviceSaveJournalSize); - - if (rc.IsFailure()) return rc; - } - - Result bcatRc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs, - out long requiredSizeBcat, applicationId, ref nacp); - - if (bcatRc.IsFailure()) - { - if (!ResultFs.UsableSpaceNotEnough.Includes(bcatRc)) - { - return bcatRc; - } - - requiredSizeSum += requiredSizeBcat; - } - - if (nacp.TemporaryStorageSize > 0) - { - if (requiredSizeSum > 0) - { - // If there was already insufficient space to create the previous saves, check if the temp - // save already exists instead of trying to create a new one. - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Temporary); - - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, in filter); - - if (rc.IsFailure()) - { - if (!ResultFs.TargetNotFound.Includes(rc)) - { - return rc; - } - - Result queryRc = fs.QuerySaveDataTotalSize(out long tempSaveTotalSize, - nacp.TemporaryStorageSize, 0); - - if (queryRc.IsFailure()) return queryRc; - - requiredSizeSum += Alignment.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; - } - } - else - { - Result createRc = fs.CreateTemporaryStorage(applicationId, nacp.SaveDataOwnerId, - nacp.TemporaryStorageSize, 0); - - if (createRc.IsFailure()) - { - if (ResultFs.UsableSpaceNotEnough.Includes(createRc)) - { - Result queryRc = fs.QuerySaveDataTotalSize(out long tempSaveTotalSize, - nacp.TemporaryStorageSize, 0); - - if (queryRc.IsFailure()) return queryRc; - - requiredSizeSum += Alignment.AlignUp(tempSaveTotalSize, 0x4000) + 0x4000; - } - else if (ResultFs.PathAlreadyExists.Includes(createRc)) - { - requiredSizeSum += 0; - } - else - { - return createRc; - } - } - } - } - - requiredSize = requiredSizeSum; - - return requiredSize == 0 ? Result.Success : ResultFs.UsableSpaceNotEnough.Log(); + return Alignment.AlignUpPow2(size, SaveDataBlockSize); } - [SuppressMessage("ReSharper", "UnusedParameter.Local")] - private static Result ExtendSaveDataIfNeeded(FileSystemClient fs, out long requiredSize, - SaveDataSpaceId spaceId, ulong saveDataId, long requiredDataSize, long requiredJournalSize) + private static long CalculateSaveDataExtensionContextFileSize(long saveDataSize, long saveDataJournalSize) { - // Checks the current save data size and extends it if needed. - // If there is not enough space to do the extension, the amount of space - // the extension requires will be written to requiredSize. + return RoundUpOccupationSize((saveDataSize + saveDataJournalSize) / 0x400 + 0x100000); + } - requiredSize = 0; + private static Result ExtendSaveDataIfNeeded(FileSystemClient fs, ref long outRequiredSize, SaveDataSpaceId spaceId, + ulong saveDataId, long saveDataSize, long saveDataJournalSize) + { return Result.Success; + + // Todo: Remove early return once extending save data is implemented +#pragma warning disable CS0162 + // ReSharper disable HeuristicUnreachableCode + + // Get the current save data size + Result rc = fs.Impl.GetSaveDataAvailableSize(out long availableSize, spaceId, saveDataId); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.GetSaveDataJournalSize(out long journalSize, spaceId, saveDataId); + if (rc.IsFailure()) return rc.Miss(); + + // Check if the save data needs to be extended + if (availableSize < saveDataSize || journalSize < saveDataJournalSize) + { + // Make sure the new sizes are valid + if (availableSize < saveDataSize && !Alignment.IsAlignedPow2(saveDataSize, SaveDataExtensionSizeAlignment)) + return ResultFs.ExtensionSizeInvalid.Log(); + + if (journalSize < saveDataJournalSize && !Alignment.IsAlignedPow2(saveDataJournalSize, SaveDataExtensionSizeAlignment)) + return ResultFs.ExtensionSizeInvalid.Log(); + + long newSaveDataSize = Math.Max(saveDataSize, availableSize); + long newSaveDataJournalSize = Math.Max(saveDataJournalSize, journalSize); + + rc = fs.Impl.ExtendSaveData(spaceId, saveDataId, newSaveDataSize, newSaveDataJournalSize); + + if (rc.IsFailure()) + { + if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + // Calculate how much space we need to extend the save data + rc = fs.QuerySaveDataTotalSize(out long currentSaveDataTotalSize, availableSize, journalSize); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.QuerySaveDataTotalSize(out long newSaveDataTotalSize, newSaveDataSize, newSaveDataJournalSize); + if (rc.IsFailure()) return rc.Miss(); + + long newSaveDataSizeDifference = RoundUpOccupationSize(newSaveDataTotalSize) - + RoundUpOccupationSize(currentSaveDataTotalSize); + + outRequiredSize += newSaveDataSizeDifference + + CalculateSaveDataExtensionContextFileSize(newSaveDataSize, + newSaveDataJournalSize) + 0x8000; + + return ResultFs.UsableSpaceNotEnough.Log(); + } + + return rc.Miss(); + } + } + + return Result.Success; + // ReSharper restore HeuristicUnreachableCode +#pragma warning restore CS0162 } - private static Result CreateSaveData(FileSystemClient fs, Func createFunc, ref long requiredSize, long baseSize, - long dataSize, long journalSize) + private static Result CreateSaveData(FileSystemClient fs, ref long inOutRequiredSize, Func createFunc, + long saveDataSize, long saveDataJournalSize, long saveDataBaseSize) { Result rc = createFunc(); @@ -160,21 +99,22 @@ public static class ApplicationSaveDataManagement if (ResultFs.UsableSpaceNotEnough.Includes(rc)) { - Result queryRc = fs.QuerySaveDataTotalSize(out long totalSize, dataSize, journalSize); - if (queryRc.IsFailure()) return queryRc; + rc = fs.QuerySaveDataTotalSize(out long saveDataTotalSize, saveDataSize, saveDataJournalSize); + if (rc.IsFailure()) return rc; - requiredSize += Alignment.AlignUp(totalSize, 0x4000) + baseSize; + inOutRequiredSize += RoundUpOccupationSize(saveDataTotalSize) + saveDataBaseSize; } else if (!ResultFs.PathAlreadyExists.Includes(rc)) { - return rc; + return rc.Miss(); } return Result.Success; } - private static Result EnsureAndExtendSaveData(FileSystemClient fs, Func createFunc, - ref long requiredSize, ref SaveDataFilter filter, long baseSize, long dataSize, long journalSize) + private static Result EnsureAndExtendSaveData(FileSystemClient fs, ref long inOutRequiredSize, + in SaveDataFilter filter, Func createFunc, long saveDataSize, long saveDataJournalSize, + long saveDataBaseSize) { Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, in filter); @@ -182,287 +122,751 @@ public static class ApplicationSaveDataManagement { if (ResultFs.TargetNotFound.Includes(rc)) { - rc = CreateSaveData(fs, createFunc, ref requiredSize, baseSize, dataSize, journalSize); + rc = CreateSaveData(fs, ref inOutRequiredSize, createFunc, saveDataSize, saveDataJournalSize, + saveDataBaseSize); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; } - return rc; + return rc.Miss(); } - rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeExtend, SaveDataSpaceId.User, info.SaveDataId, - dataSize, journalSize); + long requiredSize = 0; + rc = ExtendSaveDataIfNeeded(fs, ref requiredSize, SaveDataSpaceId.User, info.SaveDataId, saveDataSize, + saveDataJournalSize); if (rc.IsFailure()) { if (!ResultFs.UsableSpaceNotEnough.Includes(rc)) - return rc; + return rc.Miss(); - requiredSize += requiredSizeExtend; + inOutRequiredSize += requiredSize; } return Result.Success; } - private static Result EnsureApplicationBcatDeliveryCacheStorageImpl(FileSystemClient fs, out long requiredSize, - Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) + private static Result CheckSaveDataType(SaveDataType type, in Uid user) + { + switch (type) + { + case SaveDataType.Account: + { + if (user == Uid.InvalidUid) + return ResultFs.InvalidArgument.Log(); + + return Result.Success; + } + case SaveDataType.Device: + { + if (user == Uid.InvalidUid) + return ResultFs.InvalidArgument.Log(); + + return Result.Success; + } + default: + return ResultFs.InvalidArgument.Log(); + } + } + + private static Result CheckExtensionSizeUnderMax(SaveDataType type, in ApplicationControlProperty controlProperty, + long saveDataSize, long saveDataJournalSize) + { + switch (type) + { + case SaveDataType.Account: + { + if (saveDataSize > controlProperty.UserAccountSaveDataSizeMax || + saveDataJournalSize > controlProperty.UserAccountSaveDataJournalSizeMax) + { + return ResultFs.ExtensionSizeTooLarge.Log(); + } + + return Result.Success; + } + case SaveDataType.Device: + { + if (saveDataSize > controlProperty.DeviceSaveDataSizeMax || + saveDataJournalSize > controlProperty.DeviceSaveDataJournalSizeMax) + { + return ResultFs.ExtensionSizeTooLarge.Log(); + } + + return Result.Success; + } + default: + return ResultFs.InvalidArgument.Log(); + } + } + + private static Result EnsureApplicationBcatDeliveryCacheStorageImpl(FileSystemClient fs, ref long outRequiredSize, + Ncm.ApplicationId applicationId, in ApplicationControlProperty controlProperty) { const long bcatDeliveryCacheJournalSize = 0x200000; - long bcatStorageSize = nacp.BcatDeliveryCacheStorageSize; - if (bcatStorageSize <= 0) + long requiredSize = 0; + long bcatDeliveryCacheStorageSize = controlProperty.BcatDeliveryCacheStorageSize; + + if (bcatDeliveryCacheStorageSize > 0) { - requiredSize = 0; + Result rc = SaveDataFilter.Make(out SaveDataFilter filter, programId: default, SaveDataType.Bcat, + userId: default, saveDataId: default, index: default); + if (rc.IsFailure()) return rc.Miss(); + + Result CreateBcatStorageFunc() => fs.CreateBcatSaveData(applicationId, bcatDeliveryCacheStorageSize); + + rc = EnsureAndExtendSaveData(fs, ref requiredSize, in filter, CreateBcatStorageFunc, + bcatDeliveryCacheStorageSize, bcatDeliveryCacheJournalSize, 0x4000); + if (rc.IsFailure()) return rc.Miss(); + } + + if (requiredSize == 0) + { + outRequiredSize = 0; return Result.Success; } - UnsafeHelpers.SkipParamInit(out requiredSize); - long requiredSizeBcat = 0; - - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Bcat); - - Result CreateBcatStorageFunc() => fs.CreateBcatSaveData(applicationId, bcatStorageSize); - - Result rc = EnsureAndExtendSaveData(fs, CreateBcatStorageFunc, - ref requiredSizeBcat, ref filter, 0x4000, bcatStorageSize, bcatDeliveryCacheJournalSize); - - if (rc.IsFailure()) return rc; - - requiredSize = requiredSizeBcat; - return requiredSizeBcat > 0 ? ResultFs.UsableSpaceNotEnough.Log() : Result.Success; + outRequiredSize = requiredSize; + return ResultFs.UsableSpaceNotEnough.Log(); } - private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize, - out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, - long dataSize, long journalSize, bool allowExisting) - { - UnsafeHelpers.SkipParamInit(out requiredSize); - target = CacheStorageTargetMedia.SdCard; - - Result rc = fs.GetCacheStorageTargetMediaImpl(out CacheStorageTargetMedia targetMedia, applicationId); - if (rc.IsFailure()) return rc; - - long requiredSizeLocal = 0; - - if (targetMedia == CacheStorageTargetMedia.Nand) - { - rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.User, applicationId, - saveDataOwnerId, index, dataSize, journalSize, allowExisting); - if (rc.IsFailure()) return rc; - } - else if (targetMedia == CacheStorageTargetMedia.SdCard) - { - rc = TryCreateCacheStorage(fs, out requiredSizeLocal, SaveDataSpaceId.SdUser, applicationId, - saveDataOwnerId, index, dataSize, journalSize, allowExisting); - if (rc.IsFailure()) return rc; - } - // Savedata doesn't exist. Try to create a new one. - else - { - // Try to create the savedata on the SD card first - if (fs.IsSdCardAccessible()) - { - target = CacheStorageTargetMedia.SdCard; - - Result CreateFuncSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, - saveDataOwnerId, index, dataSize, journalSize, SaveDataFlags.None); - - rc = CreateSaveData(fs, CreateFuncSdCard, ref requiredSizeLocal, 0x4000, dataSize, journalSize); - if (rc.IsFailure()) return rc; - - if (requiredSizeLocal == 0) - { - requiredSize = 0; - return Result.Success; - } - } - - // If the save can't be created on the SD card, try creating it on the User BIS partition - requiredSizeLocal = 0; - target = CacheStorageTargetMedia.Nand; - - Result CreateFuncNand() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, saveDataOwnerId, - index, dataSize, journalSize, SaveDataFlags.None); - - rc = CreateSaveData(fs, CreateFuncNand, ref requiredSizeLocal, 0x4000, dataSize, journalSize); - if (rc.IsFailure()) return rc; - - if (requiredSizeLocal != 0) - { - target = CacheStorageTargetMedia.None; - requiredSize = requiredSizeLocal; - return ResultFs.UsableSpaceNotEnough.Log(); - } - } - - requiredSize = 0; - return Result.Success; - } - - public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, - out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, - long dataSize, long journalSize, bool allowExisting) - { - return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, saveDataOwnerId, - index, dataSize, journalSize, allowExisting); - } - - public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, - Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) - { - return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out _, applicationId, nacp.SaveDataOwnerId, - 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); - } - - public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, - out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) - { - UnsafeHelpers.SkipParamInit(out requiredSize, out target); - - if (nacp.CacheStorageSize <= 0) - return Result.Success; - - return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, - nacp.SaveDataOwnerId, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); - - } - - public static Result CreateApplicationCacheStorage(this FileSystemClient fs, out long requiredSize, - out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp, - ushort index, long dataSize, long journalSize) - { - UnsafeHelpers.SkipParamInit(out requiredSize, out target); - - if (index > nacp.CacheStorageIndexMax) - return ResultFs.CacheStorageIndexTooLarge.Log(); - - if (dataSize + journalSize > nacp.CacheStorageDataAndJournalSizeMax) - return ResultFs.CacheStorageSizeTooLarge.Log(); - - Result rc = fs.EnsureApplicationCacheStorage(out requiredSize, out target, applicationId, - nacp.SaveDataOwnerId, index, dataSize, journalSize, false); - - fs.Impl.AbortIfNeeded(rc); - return rc; - } - - public static Result EnsureApplicationBcatDeliveryCacheStorage(this FileSystemClient fs, out long requiredSize, - Ncm.ApplicationId applicationId, ref ApplicationControlProperty nacp) - { - return EnsureApplicationBcatDeliveryCacheStorageImpl(fs, out requiredSize, applicationId, ref nacp); - } - - public static Result TryCreateCacheStorage(this FileSystemClient fs, out long requiredSize, - SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, - long dataSize, long journalSize, bool allowExisting) - { - UnsafeHelpers.SkipParamInit(out requiredSize); - long requiredSizeLocal = 0; - - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetIndex(index); - filter.SetSaveDataType(SaveDataType.Cache); - - Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, spaceId, in filter); - - if (rc.IsFailure()) - { - if (!ResultFs.TargetNotFound.Includes(rc)) - return rc; - - Result CreateCacheFunc() => fs.CreateCacheStorage(applicationId, spaceId, saveDataOwnerId, index, - dataSize, journalSize, SaveDataFlags.None); - - rc = CreateSaveData(fs, CreateCacheFunc, ref requiredSizeLocal, 0x4000, dataSize, journalSize); - if (rc.IsFailure()) return rc; - - requiredSize = requiredSizeLocal; - return Result.Success; - } - - if (!allowExisting) - { - return ResultFs.AlreadyExists.Log(); - } - - rc = ExtendSaveDataIfNeeded(fs, out requiredSizeLocal, spaceId, info.SaveDataId, dataSize, journalSize); - - if (rc.IsSuccess() || ResultFs.UsableSpaceNotEnough.Includes(rc)) - { - requiredSize = requiredSizeLocal; - return Result.Success; - } - - if (ResultFs.SaveDataExtending.Includes(rc)) - { - return ResultFs.SaveDataCorrupted.LogConverted(rc); - } - - return rc; - } - - public static Result GetCacheStorageTargetMedia(this FileSystemClient fs, out CacheStorageTargetMedia target, + private static Result GetCacheStorageTargetMediaImpl(FileSystemClient fs, out CacheStorageTargetMedia targetMedia, Ncm.ApplicationId applicationId) { - return GetCacheStorageTargetMediaImpl(fs, out target, applicationId); - } - - private static Result GetCacheStorageTargetMediaImpl(this FileSystemClient fs, - out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId) - { - target = CacheStorageTargetMedia.None; - - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Cache); + UnsafeHelpers.SkipParamInit(out targetMedia); + Result rc; if (fs.IsSdCardAccessible()) { - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdUser, in filter); - if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc; + rc = DoesCacheStorageExist(out bool existsOnSd, SaveDataSpaceId.SdUser, fs, applicationId); + if (rc.IsFailure()) return rc.Miss(); - if (rc.IsSuccess()) + if (existsOnSd) { - target = CacheStorageTargetMedia.SdCard; + targetMedia = CacheStorageTargetMedia.SdCard; + return Result.Success; } } - // Not on the SD card. Check it it's in NAND - if (target == CacheStorageTargetMedia.None) - { - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, in filter); - if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc; + rc = DoesCacheStorageExist(out bool existsOnNand, SaveDataSpaceId.User, fs, applicationId); + if (rc.IsFailure()) return rc.Miss(); - if (rc.IsSuccess()) + targetMedia = existsOnNand ? CacheStorageTargetMedia.Nand : CacheStorageTargetMedia.None; + return Result.Success; + + static Result DoesCacheStorageExist(out bool exists, SaveDataSpaceId spaceId, FileSystemClient fs, + Ncm.ApplicationId applicationId) + { + UnsafeHelpers.SkipParamInit(out exists); + + bool doesStorageExist = true; + + Result rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Cache, + userId: default, saveDataId: default, index: default); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo _, spaceId, in filter); + + if (rc.IsFailure()) { - target = CacheStorageTargetMedia.Nand; + if (!ResultFs.TargetNotFound.Includes(rc)) + return rc.Miss(); + + doesStorageExist = false; } + + exists = doesStorageExist; + return Result.Success; + } + } + + private static Result TryCreateCacheStorage(this FileSystemClient fs, ref long outRequiredSize, + SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, + long cacheStorageSize, long cacheStorageJournalSize, bool allowExisting) + { + long requiredSize = 0; + + Result rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Cache, + userId: default, saveDataId: default, index); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + // Check if the cache storage already exists or not. + bool doesStorageExist = true; + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo info, spaceId, in filter); + + if (rc.IsFailure()) + { + if (ResultFs.TargetNotFound.Includes(rc)) + { + doesStorageExist = false; + } + else + { + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } + } + + if (doesStorageExist) + { + // The cache storage already exists. Ensure it's large enough. + if (!allowExisting) + { + rc = ResultFs.AlreadyExists.Value; + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } + + rc = ExtendSaveDataIfNeeded(fs, ref requiredSize, spaceId, info.SaveDataId, cacheStorageSize, + cacheStorageJournalSize); + + if (rc.IsFailure()) + { + if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + // Don't return this error. If there's not enough space we return Success along with + // The amount of space required to create the cache storage. + } + else if (ResultFs.SaveDataExtending.Includes(rc)) + { + rc = ResultFs.SaveDataCorrupted.LogConverted(rc); + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } + else + { + rc.Miss(); + fs.Impl.AbortIfNeeded(rc); + return rc; + } + } + } + else + { + // The cache storage doesn't exist. Try to create it. + Result CreateCacheFunc() => fs.CreateCacheStorage(applicationId, spaceId, saveDataOwnerId, index, + cacheStorageSize, cacheStorageJournalSize, SaveDataFlags.None); + + rc = CreateSaveData(fs, ref requiredSize, CreateCacheFunc, cacheStorageSize, cacheStorageJournalSize, + 0x4000); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + } + + outRequiredSize = requiredSize; + return Result.Success; + } + + private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, ref long outRequiredSize, + out CacheStorageTargetMedia targetMedia, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, + long cacheStorageSize, long cacheStorageJournalSize, bool allowExisting) + { + targetMedia = CacheStorageTargetMedia.SdCard; + long requiredSize = 0; + + // Check if the cache storage already exists + Result rc = GetCacheStorageTargetMediaImpl(fs, out CacheStorageTargetMedia media, applicationId); + if (rc.IsFailure()) return rc.Miss(); + + if (media == CacheStorageTargetMedia.SdCard) + { + // If it exists on the SD card, ensure it's large enough. + targetMedia = CacheStorageTargetMedia.SdCard; + + rc = TryCreateCacheStorage(fs, ref requiredSize, SaveDataSpaceId.SdUser, applicationId, saveDataOwnerId, + index, cacheStorageSize, cacheStorageJournalSize, allowExisting); + if (rc.IsFailure()) return rc.Miss(); + } + else if (media == CacheStorageTargetMedia.Nand) + { + // If it exists on the BIS, ensure it's large enough. + targetMedia = CacheStorageTargetMedia.Nand; + + rc = TryCreateCacheStorage(fs, ref requiredSize, SaveDataSpaceId.User, applicationId, saveDataOwnerId, + index, cacheStorageSize, cacheStorageJournalSize, allowExisting); + if (rc.IsFailure()) return rc.Miss(); + } + else + { + // The cache storage doesn't exist. Try to create it on the SD card first. + bool isSdCardAccessible = fs.IsSdCardAccessible(); + if (isSdCardAccessible) + { + targetMedia = CacheStorageTargetMedia.SdCard; + + Result CreateStorageOnSdCard() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.SdUser, + saveDataOwnerId, index, cacheStorageSize, cacheStorageJournalSize, SaveDataFlags.None); + + rc = CreateSaveData(fs, ref requiredSize, CreateStorageOnSdCard, cacheStorageSize, cacheStorageJournalSize, + 0x4000); + if (rc.IsFailure()) return rc.Miss(); + + // Don't use the SD card if it doesn't have enough space. + if (requiredSize != 0) + isSdCardAccessible = false; + } + + // If the cache storage can't be created on the SD card, try creating it on the User BIS partition. + if (!isSdCardAccessible) + { + requiredSize = 0; + targetMedia = CacheStorageTargetMedia.Nand; + + Result CreateStorageOnNand() => fs.CreateCacheStorage(applicationId, SaveDataSpaceId.User, saveDataOwnerId, + index, cacheStorageSize, cacheStorageSize, SaveDataFlags.None); + + rc = CreateSaveData(fs, ref requiredSize, CreateStorageOnNand, cacheStorageSize, cacheStorageJournalSize, + 0x4000); + if (rc.IsFailure()) return rc.Miss(); + + if (requiredSize != 0) + targetMedia = CacheStorageTargetMedia.None; + } + } + + outRequiredSize = requiredSize; + + if (requiredSize != 0) + return ResultFs.UsableSpaceNotEnough.Log(); + + return Result.Success; + } + + public static Result EnsureApplicationDeviceSaveData(this FileSystemClientImpl fs, out long outRequiredSize, + Ncm.ApplicationId applicationId, long saveDataSize, long saveDataJournalSize) + { + UnsafeHelpers.SkipParamInit(out outRequiredSize); + + long requiredSize = 0; + + Result rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Device, + userId: default, saveDataId: default, index: default); + if (rc.IsFailure()) return rc.Miss(); + + const SaveDataFlags flags = SaveDataFlags.None; + + Result CreateSave() => + fs.CreateDeviceSaveData(applicationId, applicationId.Value, saveDataSize, saveDataJournalSize, flags); + + rc = EnsureAndExtendSaveData(fs.Fs, ref requiredSize, in filter, CreateSave, saveDataSize, saveDataJournalSize, + 0x4000); + if (rc.IsFailure()) return rc.Miss(); + + outRequiredSize = requiredSize; + return Result.Success; + } + + public static Result GetCacheStorageTargetMedia(this FileSystemClient fs, out CacheStorageTargetMedia targetMedia, + Ncm.ApplicationId applicationId) + { + Result rc = GetCacheStorageTargetMediaImpl(fs, out targetMedia, applicationId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long outRequiredSize, + out CacheStorageTargetMedia targetMedia, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index, + long cacheStorageSize, long cacheStorageJournalSize, bool allowExisting) + { + outRequiredSize = 0; + + Result rc = EnsureApplicationCacheStorageImpl(fs, ref outRequiredSize, out targetMedia, applicationId, + saveDataOwnerId, index, cacheStorageSize, cacheStorageJournalSize, allowExisting); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long outRequiredSize, + Ncm.ApplicationId applicationId, in ApplicationControlProperty controlProperty) + { + outRequiredSize = 0; + + Result rc = EnsureApplicationCacheStorageImpl(fs, ref outRequiredSize, out _, applicationId, + controlProperty.SaveDataOwnerId, index: 0, controlProperty.CacheStorageSize, + controlProperty.CacheStorageJournalSize, true); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long outRequiredSize, + out CacheStorageTargetMedia targetMedia, Ncm.ApplicationId applicationId, + in ApplicationControlProperty controlProperty) + { + targetMedia = CacheStorageTargetMedia.None; + outRequiredSize = 0; + + if (controlProperty.CacheStorageSize > 0) + { + Result rc = EnsureApplicationCacheStorageImpl(fs, ref outRequiredSize, out targetMedia, applicationId, + controlProperty.SaveDataOwnerId, index: 0, controlProperty.CacheStorageSize, + controlProperty.CacheStorageJournalSize, true); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); } return Result.Success; } - public static Result CleanUpTemporaryStorage(FileSystemClient fs) + public static Result CreateApplicationCacheStorage(this FileSystemClient fs, out long outRequiredSize, + out CacheStorageTargetMedia targetMedia, Ncm.ApplicationId applicationId, + in ApplicationControlProperty controlProperty, ushort index, long cacheStorageSize, + long cacheStorageJournalSize) { - var filter = new SaveDataFilter(); - filter.SetSaveDataType(SaveDataType.Temporary); + UnsafeHelpers.SkipParamInit(out outRequiredSize, out targetMedia); Result rc; - while (true) + if (index > controlProperty.CacheStorageIndexMax) { - rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveInfo, SaveDataSpaceId.Temporary, in filter); - - if (rc.IsFailure()) break; - - rc = fs.DeleteSaveData(SaveDataSpaceId.Temporary, saveInfo.SaveDataId); - if (rc.IsFailure()) return rc; + rc = ResultFs.CacheStorageIndexTooLarge.Value; + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); } - if (ResultFs.TargetNotFound.Includes(rc)) - return Result.Success; + if (cacheStorageSize + cacheStorageJournalSize > controlProperty.CacheStorageDataAndJournalSizeMax) + { + rc = ResultFs.CacheStorageSizeTooLarge.Value; + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } - return rc; + rc = EnsureApplicationCacheStorageImpl(fs, ref outRequiredSize, out targetMedia, applicationId, + controlProperty.SaveDataOwnerId, index, cacheStorageSize, cacheStorageJournalSize, false); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result CleanUpTemporaryStorage(this FileSystemClient fs) + { + while (true) + { + Result rc = SaveDataFilter.Make(out SaveDataFilter filter, programId: default, SaveDataType.Temporary, + userId: default, saveDataId: default, index: default); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + // Try to find any temporary save data. + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.Temporary, in filter); + + if (rc.IsFailure()) + { + if (ResultFs.TargetNotFound.Includes(rc)) + { + // No more save data found. We're done cleaning. + return Result.Success; + } + + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } + + // Delete the found save data. + rc = fs.Impl.DeleteSaveData(SaveDataSpaceId.Temporary, info.SaveDataId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + } + } + + public static Result EnsureApplicationBcatDeliveryCacheStorage(this FileSystemClient fs, out long outRequiredSize, + Ncm.ApplicationId applicationId, in ApplicationControlProperty controlProperty) + { + outRequiredSize = 0; + + Result rc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs, ref outRequiredSize, applicationId, + in controlProperty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + return Result.Success; + } + + public static Result EnsureApplicationSaveData(this FileSystemClient fs, out long outRequiredSize, + Ncm.ApplicationId applicationId, in ApplicationControlProperty controlProperty, in Uid user) + { + UnsafeHelpers.SkipParamInit(out outRequiredSize); + + using var prohibiter = new UniqueRef(); + Result rc = fs.Impl.OpenSaveDataTransferProhibiterForCloudBackUp(ref prohibiter.Ref(), applicationId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + long requiredSize = 0; + + // Create local variables for use in closures + Uid uid = user; + ulong saveDataOwnerId = controlProperty.SaveDataOwnerId; + long accountSaveDataSize = controlProperty.UserAccountSaveDataSize; + long accountSaveJournalSize = controlProperty.UserAccountSaveDataJournalSize; + long deviceSaveDataSize = controlProperty.DeviceSaveDataSize; + long deviceSaveJournalSize = controlProperty.DeviceSaveDataJournalSize; + + // Ensure the user account save exists + if (user != Uid.InvalidUid && controlProperty.UserAccountSaveDataSize > 0) + { + Result CreateAccountSaveFunc() + { + UserId fsUserId = Utility.ConvertAccountUidToFsUserId(uid); + return fs.Impl.CreateSaveData(applicationId, fsUserId, saveDataOwnerId, accountSaveDataSize, + accountSaveJournalSize, SaveDataFlags.None); + } + + UserId userId = Unsafe.As(ref Unsafe.AsRef(in user)); + rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Account, userId, + saveDataId: default, index: default); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + long baseSize = RoundUpOccupationSize(new SaveDataMetaPolicy(SaveDataType.Account).GetSaveDataMetaSize()) + + 0x8000; + + rc = EnsureAndExtendSaveData(fs, ref requiredSize, in filter, CreateAccountSaveFunc, accountSaveDataSize, + accountSaveJournalSize, baseSize); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + } + + // Ensure the device save exists + if (controlProperty.DeviceSaveDataSize > 0) + { + Result CreateDeviceSaveFunc() => fs.Impl.CreateDeviceSaveData(applicationId, saveDataOwnerId, + deviceSaveDataSize, deviceSaveJournalSize, SaveDataFlags.None); + + rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Device, + userId: default, saveDataId: default, index: default); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + long baseSize = RoundUpOccupationSize(new SaveDataMetaPolicy(SaveDataType.Device).GetSaveDataMetaSize()) + + 0x8000; + + long requiredSizeForDeviceSaveData = 0; + rc = EnsureAndExtendSaveData(fs, ref requiredSizeForDeviceSaveData, in filter, CreateDeviceSaveFunc, + deviceSaveDataSize, deviceSaveJournalSize, baseSize); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + if (requiredSizeForDeviceSaveData == 0) + { + rc = fs.Impl.CreateDeviceSaveData(applicationId, saveDataOwnerId, deviceSaveDataSize, + deviceSaveJournalSize, SaveDataFlags.None); + + if (rc.IsFailure()) + { + if (ResultFs.PathAlreadyExists.Includes(rc)) + { + // Nothing to do if the save already exists. + } + else if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + requiredSizeForDeviceSaveData += + RoundUpOccupationSize(new SaveDataMetaPolicy(SaveDataType.Device).GetSaveDataMetaSize()) + + 0x4000; + } + else + { + return rc.Miss(); + } + } + } + + requiredSize += requiredSizeForDeviceSaveData; + } + + long requiredSizeForBcat = 0; + rc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs, ref requiredSizeForBcat, applicationId, + in controlProperty); + + if (rc.IsFailure()) + { + if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + requiredSize += requiredSizeForBcat; + } + else + { + return rc.Miss(); + } + } + + if (controlProperty.TemporaryStorageSize > 0) + { + static Result CalculateRequiredSizeForTemporaryStorage(FileSystemClient fs, out long requiredSize, + in ApplicationControlProperty controlProperty) + { + UnsafeHelpers.SkipParamInit(out requiredSize); + + Result rc = fs.Impl.QuerySaveDataTotalSize(out long saveDataTotalSize, + controlProperty.TemporaryStorageSize, saveDataJournalSize: 0); + if (rc.IsFailure()) return rc.Miss(); + + requiredSize = RoundUpOccupationSize(saveDataTotalSize) + 0x4000; + return Result.Success; + } + + if (requiredSize == 0) + { + rc = fs.Impl.CreateTemporaryStorage(applicationId, saveDataOwnerId, + controlProperty.TemporaryStorageSize, SaveDataFlags.None); + + if (rc.IsFailure()) + { + if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + rc = CalculateRequiredSizeForTemporaryStorage(fs, out long temporaryStorageSize, + in controlProperty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + requiredSize += temporaryStorageSize; + } + else + { + return rc.Miss(); + } + } + } + else + { + // If there was already insufficient space to create the previous saves, check if the temp + // save already exists instead of trying to create a new one. + rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, SaveDataType.Temporary, + userId: default, saveDataId: default, index: default); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + // Check if the temporary save exists + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo _, SaveDataSpaceId.Temporary, in filter); + + if (rc.IsFailure()) + { + if (ResultFs.TargetNotFound.Includes(rc)) + { + // If it doesn't exist, calculate the size required to create it + rc = CalculateRequiredSizeForTemporaryStorage(fs, out long temporaryStorageSize, + in controlProperty); + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + requiredSize += temporaryStorageSize; + } + else + { + return rc.Miss(); + } + } + } + } + + if (requiredSize == 0) + { + outRequiredSize = 0; + return Result.Success; + } + + outRequiredSize = requiredSize; + return ResultFs.UsableSpaceNotEnough.Log(); + } + + public static Result ExtendApplicationSaveData(this FileSystemClient fs, out long outRequiredSize, + Ncm.ApplicationId applicationId, in ApplicationControlProperty controlProperty, SaveDataType type, in Uid user, + long saveDataSize, long saveDataJournalSize) + { + UnsafeHelpers.SkipParamInit(out outRequiredSize); + + Result rc = CheckSaveDataType(type, in user); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + UserId userId = Unsafe.As(ref Unsafe.AsRef(in user)); + rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, type, userId, saveDataId: default, + index: default); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, in filter); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = CheckExtensionSizeUnderMax(type, in controlProperty, saveDataSize, saveDataJournalSize); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + long requiredSize = 0; + rc = ExtendSaveDataIfNeeded(fs, ref requiredSize, SaveDataSpaceId.User, info.SaveDataId, saveDataSize, + saveDataJournalSize); + + if (rc.IsFailure()) + { + if (ResultFs.UsableSpaceNotEnough.Includes(rc)) + { + outRequiredSize = requiredSize; + + fs.Impl.AbortIfNeeded(rc); + return rc.Rethrow(); + } + + fs.Impl.AbortIfNeeded(rc); + return rc.Miss(); + } + + return Result.Success; + } + + public static Result GetApplicationSaveDataSize(this FileSystemClient fs, out long outSaveDataSize, + out long outSaveDataJournalSize, Ncm.ApplicationId applicationId, SaveDataType type, in Uid user) + { + UnsafeHelpers.SkipParamInit(out outSaveDataSize, out outSaveDataJournalSize); + + Result rc = CheckSaveDataType(type, in user); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + UserId userId = Unsafe.As(ref Unsafe.AsRef(in user)); + rc = SaveDataFilter.Make(out SaveDataFilter filter, applicationId.Value, type, userId, saveDataId: default, + index: default); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, in filter); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.GetSaveDataAvailableSize(out long saveDataSize, info.SaveDataId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + rc = fs.Impl.GetSaveDataJournalSize(out long saveDataJournalSize, info.SaveDataId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc.Miss(); + + outSaveDataSize = saveDataSize; + outSaveDataJournalSize = saveDataJournalSize; + + return Result.Success; } } \ No newline at end of file diff --git a/src/LibHac/Fs/SaveData.cs b/src/LibHac/Fs/SaveData.cs index 6e4bfc54..702a6a88 100644 --- a/src/LibHac/Fs/SaveData.cs +++ b/src/LibHac/Fs/SaveData.cs @@ -5,8 +5,8 @@ namespace LibHac.Fs; public static class SaveData { public static readonly ulong SaveIndexerId = 0x8000000000000000; - public static ProgramId InvalidProgramId => ProgramId.InvalidId; + public static ProgramId InvalidProgramId => default; public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1); - public static UserId InvalidUserId => UserId.InvalidId; + public static UserId InvalidUserId => default; public static ulong InvalidSystemSaveDataId => 0; -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index 1678098b..af163d32 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -17,30 +17,10 @@ public struct SaveDataAttribute : IEquatable, IComparable Reserved; - public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this( - programId, type, userId, saveDataId, 0, SaveDataRank.Primary) - { } - - public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, - ushort index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary) - { } - - public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, ushort index, - SaveDataRank rank) - { - ProgramId = programId; - Type = type; - UserId = userId; - StaticSaveDataId = saveDataId; - Index = index; - Rank = rank; - Reserved = new Array24(); - } - public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type, UserId userId, ulong staticSaveDataId) { - return Make(out attribute, programId, type, userId, staticSaveDataId, 0, SaveDataRank.Primary); + return Make(out attribute, programId, type, userId, staticSaveDataId, index: 0, SaveDataRank.Primary); } public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type, @@ -154,36 +134,6 @@ public struct SaveDataFilter public SaveDataAttribute Attribute; - public void SetProgramId(ProgramId value) - { - FilterByProgramId = true; - Attribute.ProgramId = value; - } - - public void SetSaveDataType(SaveDataType value) - { - FilterBySaveDataType = true; - Attribute.Type = value; - } - - public void SetUserId(UserId value) - { - FilterByUserId = true; - Attribute.UserId = value; - } - - public void SetSaveDataId(ulong value) - { - FilterBySaveDataId = true; - Attribute.StaticSaveDataId = value; - } - - public void SetIndex(ushort value) - { - FilterByIndex = true; - Attribute.Index = value; - } - public static Result Make(out SaveDataFilter filter, Optional programId, Optional saveType, Optional userId, Optional saveDataId, Optional index) { diff --git a/src/LibHac/Fs/Shim/BcatSaveData.cs b/src/LibHac/Fs/Shim/BcatSaveData.cs index 7dd3fa64..8f924186 100644 --- a/src/LibHac/Fs/Shim/BcatSaveData.cs +++ b/src/LibHac/Fs/Shim/BcatSaveData.cs @@ -5,7 +5,9 @@ using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Os; + using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.SaveData; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -58,7 +60,7 @@ public static class BcatSaveData using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, - Fs.SaveData.InvalidUserId, 0); + InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; using var fileSystem = new SharedRef(); diff --git a/src/LibHac/Fs/Shim/SaveData.cs b/src/LibHac/Fs/Shim/SaveData.cs index 3a9b387d..d64bede5 100644 --- a/src/LibHac/Fs/Shim/SaveData.cs +++ b/src/LibHac/Fs/Shim/SaveData.cs @@ -6,7 +6,9 @@ using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Ncm; using LibHac.Os; + using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.SaveData; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -48,7 +50,8 @@ public static class SaveData using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, programId, type, userId, 0, index); + rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, programId, type, userId, InvalidSystemSaveDataId, + index); if (rc.IsFailure()) return rc; using var fileSystem = new SharedRef(); @@ -86,7 +89,7 @@ public static class SaveData using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, ProgramId.InvalidId, SaveDataType.Account, - UserId.InvalidId, 0); + InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, SaveDataSizeForDebug, @@ -127,7 +130,7 @@ public static class SaveData if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId); + rc = MountSaveData(fs.Impl, mountName, InvalidUserId); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -138,7 +141,7 @@ public static class SaveData } else { - rc = MountSaveData(fs.Impl, mountName, UserId.InvalidId); + rc = MountSaveData(fs.Impl, mountName, InvalidUserId); } fs.Impl.AbortIfNeeded(rc); @@ -238,7 +241,8 @@ public static class SaveData using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, type, UserId.InvalidId, 0); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, type, userId, + InvalidSystemSaveDataId); if (rc.IsFailure()) return rc.Miss(); using var fileSystem = new SharedRef(); @@ -267,8 +271,8 @@ public static class SaveData if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, InvalidProgramId, InvalidUserId, + SaveDataType.Temporary, openReadOnly: false, index: 0); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -278,8 +282,8 @@ public static class SaveData } else { - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, InvalidProgramId, InvalidUserId, + SaveDataType.Temporary, openReadOnly: false, index: 0); } fs.Impl.AbortIfNeeded(rc); @@ -299,8 +303,8 @@ public static class SaveData if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId, + InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -310,8 +314,8 @@ public static class SaveData } else { - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId, + InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); } fs.Impl.AbortIfNeeded(rc); @@ -332,8 +336,8 @@ public static class SaveData if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { Tick start = fs.Hos.Os.GetSystemTick(); - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId, + InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -344,8 +348,8 @@ public static class SaveData } else { - rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, InvalidProgramId, + InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); } fs.Impl.AbortIfNeeded(rc); @@ -366,7 +370,7 @@ public static class SaveData { Tick start = fs.Hos.Os.GetSystemTick(); rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -378,7 +382,7 @@ public static class SaveData else { rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); } fs.Impl.AbortIfNeeded(rc); @@ -400,7 +404,7 @@ public static class SaveData { Tick start = fs.Hos.Os.GetSystemTick(); rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); @@ -413,7 +417,7 @@ public static class SaveData else { rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, - Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); } fs.Impl.AbortIfNeeded(rc); diff --git a/src/LibHac/Fs/Shim/SaveDataManagement.cs b/src/LibHac/Fs/Shim/SaveDataManagement.cs index 6c9444ba..2c978a9e 100644 --- a/src/LibHac/Fs/Shim/SaveDataManagement.cs +++ b/src/LibHac/Fs/Shim/SaveDataManagement.cs @@ -12,7 +12,9 @@ using LibHac.Ncm; using LibHac.Os; using LibHac.Sf; using LibHac.Time; + using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.SaveData; namespace LibHac.Fs { @@ -297,7 +299,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, - userId, staticSaveDataId: 0); + userId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, @@ -319,7 +321,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, - Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, @@ -345,7 +347,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device, - Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, @@ -367,7 +369,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache, - Fs.SaveData.InvalidUserId, staticSaveDataId: 0, index); + InvalidUserId, InvalidSystemSaveDataId, index); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, @@ -492,7 +494,7 @@ namespace LibHac.Fs.Shim { using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System, userId, saveDataId); if (rc.IsFailure()) return rc; @@ -529,7 +531,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), - SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + SaveDataType.Device, InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; return fileSystemProxy.Get.DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId.User, in attribute); @@ -587,7 +589,7 @@ namespace LibHac.Fs.Shim } public static Result ReadSaveDataIteratorSaveDataInfo(this FileSystemClientImpl fs, out long readCount, - Span buffer, in SaveDataIterator iterator) + Span buffer, ref SaveDataIterator iterator) { Result rc = iterator.ReadSaveDataInfo(out readCount, buffer); if (rc.IsFailure()) return rc.Miss(); @@ -743,7 +745,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, - userId, staticSaveDataId: 0); + userId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, @@ -820,7 +822,7 @@ namespace LibHac.Fs.Shim using SharedRef fileSystemProxy = fs.GetFileSystemProxyServiceObject(); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary, - Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize: 0, ownerId, flags, @@ -946,7 +948,7 @@ namespace LibHac.Fs.Shim { using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System, userId, saveDataId); if (rc.IsFailure()) return rc; @@ -961,8 +963,7 @@ namespace LibHac.Fs.Shim 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); + return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size, @@ -974,20 +975,19 @@ namespace LibHac.Fs.Shim 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); + return CreateSystemSaveData(fs, saveDataId, 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); + return CreateSystemSaveData(fs, saveDataId, 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); + return CreateSystemSaveData(fs, spaceId, saveDataId, InvalidUserId, ownerId, size, journalSize, flags); } public static Result ExtendSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId, @@ -1295,7 +1295,7 @@ namespace LibHac.Fs.Shim { UnsafeHelpers.SkipParamInit(out flags); - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System, userId, saveDataId); if (rc.IsFailure()) return rc; @@ -1392,7 +1392,7 @@ namespace LibHac.Fs.Shim SaveDataExtraData extraData = default; extraData.Flags = flags; - Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System, userId, saveDataId); if (rc.IsFailure()) return rc; @@ -2186,8 +2186,8 @@ namespace LibHac.Fs.Shim Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute); if (rc.IsFailure()) return rc; - if (attribute.ProgramId == Fs.SaveData.InvalidProgramId) - attribute.ProgramId = Fs.SaveData.AutoResolveCallerProgramId; + if (attribute.ProgramId == InvalidProgramId) + attribute.ProgramId = AutoResolveCallerProgramId; SaveDataExtraData extraDataMask = default; extraDataMask.Flags = SaveDataFlags.Restore; @@ -2239,7 +2239,7 @@ namespace LibHac.Fs.Shim extraDataMask.JournalSize = unchecked((long)0xFFFFFFFFFFFFFFFF); Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, new ProgramId(applicationId.Value), - SaveDataType.Device, Fs.SaveData.InvalidUserId, staticSaveDataId: 0); + SaveDataType.Device, InvalidUserId, InvalidSystemSaveDataId); if (rc.IsFailure()) return rc; rc = fs.Impl.ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId.User, diff --git a/src/LibHac/Fs/Shim/SaveDataTransferVersion2.cs b/src/LibHac/Fs/Shim/SaveDataTransferVersion2.cs index 6dc218c5..00d85bf6 100644 --- a/src/LibHac/Fs/Shim/SaveDataTransferVersion2.cs +++ b/src/LibHac/Fs/Shim/SaveDataTransferVersion2.cs @@ -178,7 +178,7 @@ namespace LibHac.Fs public Result OpenSaveDataImporter(ref UniqueRef outImporter, in InitialDataVersion2 initialData, SaveDataSpaceId spaceId, bool useSwap) { - return OpenSaveDataImporterImpl(ref outImporter, in initialData, UserId.InvalidId, spaceId, useSwap); + return OpenSaveDataImporterImpl(ref outImporter, in initialData, InvalidUserId, spaceId, useSwap); } public Result OpenSaveDataImporterByContext(ref UniqueRef outImporter, @@ -354,7 +354,7 @@ namespace LibHac.Fs.Shim Unsafe.SkipInit(out SaveDataInfo info); rc = fs.Impl.ReadSaveDataIteratorSaveDataInfo(out long count, SpanHelpers.AsSpan(ref info), - iterator.Get); + ref iterator.Get); fs.Impl.LogResultErrorMessage(rc); if (rc.IsFailure()) return rc.Miss(); diff --git a/src/LibHac/Fs/Shim/SystemSaveData.cs b/src/LibHac/Fs/Shim/SystemSaveData.cs index 880604d9..444ba221 100644 --- a/src/LibHac/Fs/Shim/SystemSaveData.cs +++ b/src/LibHac/Fs/Shim/SystemSaveData.cs @@ -5,7 +5,9 @@ using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Os; + using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.SaveData; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -20,7 +22,7 @@ public static class SystemSaveData { public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId) { - return fs.MountSystemSaveData(mountName, saveDataId, Fs.SaveData.InvalidUserId); + return fs.MountSystemSaveData(mountName, saveDataId, InvalidUserId); } public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId, @@ -32,7 +34,7 @@ public static class SystemSaveData public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId) { - return fs.MountSystemSaveData(mountName, spaceId, saveDataId, Fs.SaveData.InvalidUserId); + return fs.MountSystemSaveData(mountName, spaceId, saveDataId, InvalidUserId); } public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, @@ -77,7 +79,7 @@ public static class SystemSaveData using SharedRef fileSystemProxy = fs.Impl.GetFileSystemProxyServiceObject(); - rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, InvalidProgramId, SaveDataType.System, userId, saveDataId); if (rc.IsFailure()) return rc; diff --git a/src/LibHac/FsSrv/SaveDataFileSystemService.cs b/src/LibHac/FsSrv/SaveDataFileSystemService.cs index b1ed8f95..2038fea9 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemService.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemService.cs @@ -10,13 +10,15 @@ using LibHac.Kvdb; using LibHac.Ncm; using LibHac.Sf; using LibHac.Util; + +using static LibHac.Fs.SaveData; +using static LibHac.Fs.StringTraits; + using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; using IFile = LibHac.Fs.Fsa.IFile; using IFileSf = LibHac.FsSrv.Sf.IFile; using Path = LibHac.Fs.Path; -using SaveData = LibHac.Fs.SaveData; -using static LibHac.Fs.StringTraits; using Utility = LibHac.FsSystem.Utility; namespace LibHac.FsSrv; @@ -146,7 +148,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave return ResultFs.PermissionDenied.Log(); } } - else if (attribute.Type == SaveDataType.Account && attribute.UserId == UserId.InvalidId) + else if (attribute.Type == SaveDataType.Account && attribute.UserId == InvalidUserId) { bool canAccess = accessControl.CanCall(OperationType.CreateSaveData) || @@ -195,7 +197,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave } else if (attribute.Type == SaveDataType.Account) { - bool canAccess = attribute.UserId != UserId.InvalidId || + bool canAccess = attribute.UserId != InvalidUserId || accessControl.CanCall(OperationType.DebugSaveData); if (!canAccess) @@ -478,7 +480,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave private Result DeleteSaveDataFileSystemBySaveDataSpaceIdCore(SaveDataSpaceId spaceId, ulong saveDataId) { - if (saveDataId != SaveData.SaveIndexerId) + if (saveDataId != SaveIndexerId) { using var accessor = new UniqueRef(); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), spaceId); @@ -515,7 +517,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave SaveDataSpaceId actualSpaceId; // Only the FS process may delete the save indexer's save data. - if (saveDataId == SaveData.SaveIndexerId) + if (saveDataId == SaveIndexerId) { if (!_serviceImpl.FsServer.IsCurrentProcess(_processId)) return ResultFs.PermissionDenied.Log(); @@ -565,7 +567,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave // Remove the save data from the indexer. // The indexer doesn't track itself, so skip if deleting its save data. - if (saveDataId != SaveData.SaveIndexerId) + if (saveDataId != SaveIndexerId) { rc = accessor.Get.GetInterface().Delete(saveDataId); if (rc.IsFailure()) return rc; @@ -656,7 +658,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave // ReSharper disable once UnusedParameter.Global public Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId) { - if (saveDataId == SaveData.SaveIndexerId) + if (saveDataId == SaveIndexerId) return ResultFs.InvalidArgument.Log(); return ResultFs.NotImplemented.Log(); @@ -697,10 +699,10 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave try { // Add the new save data to the save indexer - if (attribute.StaticSaveDataId == SaveData.SaveIndexerId) + if (attribute.StaticSaveDataId == SaveIndexerId) { // The save indexer doesn't index itself - saveDataId = SaveData.SaveIndexerId; + saveDataId = SaveIndexerId; rc = _serviceImpl.DoesSaveDataEntityExist(out bool saveExists, creationInfo.SpaceId, saveDataId); if (rc.IsSuccess() && saveExists) @@ -720,7 +722,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave SaveDataAttribute indexerKey = attribute; // Add the new value to the indexer - if (attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.InvalidId) + if (attribute.StaticSaveDataId != 0 && attribute.UserId == InvalidUserId) { // If a static save data ID is specified that ID is always used saveDataId = attribute.StaticSaveDataId; @@ -823,7 +825,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave } // The indexer's save data isn't tracked, so we don't need to update its state. - if (attribute.StaticSaveDataId != SaveData.SaveIndexerId) + if (attribute.StaticSaveDataId != SaveIndexerId) { // Mark the save data as being successfully created rc = accessor.Get.GetInterface().SetState(saveDataId, SaveDataState.Normal); @@ -843,7 +845,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave { DeleteSaveDataFileSystemCore(creationInfo.SpaceId, saveDataId, false).IgnoreResult(); - if (accessorInitialized && saveDataId != SaveData.SaveIndexerId) + if (accessorInitialized && saveDataId != SaveIndexerId) { rc = accessor.Get.GetInterface().GetValue(out SaveDataIndexerValue value, saveDataId); @@ -922,7 +924,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave rc = SaveDataAccessibilityChecker.CheckCreate(in attribute, in creationInfo, programInfo, programId); if (rc.IsFailure()) return rc; - if (tempAttribute.Type == SaveDataType.Account && tempAttribute.UserId == UserId.InvalidId) + if (tempAttribute.Type == SaveDataType.Account && tempAttribute.UserId == InvalidUserId) { if (tempAttribute.ProgramId == ProgramId.InvalidId) { @@ -995,7 +997,8 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave using var accessor = new UniqueRef(); ulong tempSaveDataId; - bool isStaticSaveDataId = attribute.StaticSaveDataId != 0 && attribute.UserId == UserId.InvalidId; + bool isStaticSaveDataId = + attribute.StaticSaveDataId != InvalidSystemSaveDataId && attribute.UserId == InvalidUserId; // Get the ID of the save data if (isStaticSaveDataId) @@ -1052,7 +1055,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave Result RemoveSaveIndexerEntry() { - if (tempSaveDataId == SaveData.SaveIndexerId) + if (tempSaveDataId == SaveIndexerId) return Result.Success; if (isStaticSaveDataId) @@ -1379,7 +1382,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave SaveDataAttribute tempAttribute = attribute; - if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId) + if (tempAttribute.ProgramId == AutoResolveCallerProgramId) { tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId); } @@ -1428,7 +1431,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave SaveDataAttribute tempAttribute = attribute; - if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId) + if (tempAttribute.ProgramId == AutoResolveCallerProgramId) { tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId); } @@ -1505,7 +1508,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave SaveDataAttribute tempAttribute = attribute; - if (tempAttribute.ProgramId == SaveData.AutoResolveCallerProgramId) + if (tempAttribute.ProgramId == AutoResolveCallerProgramId) { tempAttribute.ProgramId = ResolveDefaultSaveDataReferenceProgramId(programInfo.ProgramId); } @@ -2042,7 +2045,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave { Index = 0, Type = SaveDataType.System, - UserId = UserId.InvalidId, + UserId = InvalidUserId, StaticSaveDataId = MultiCommitManager.SaveDataId, ProgramId = new ProgramId(MultiCommitManager.ProgramId) }; @@ -2067,7 +2070,7 @@ internal class SaveDataFileSystemService : ISaveDataTransferCoreInterface, ISave { Index = saveInfo.Index, Type = saveInfo.Type, - UserId = UserId.InvalidId, + UserId = InvalidUserId, StaticSaveDataId = saveInfo.StaticSaveDataId, ProgramId = saveInfo.ProgramId }; diff --git a/src/LibHac/FsSrv/SaveDataIndexer.cs b/src/LibHac/FsSrv/SaveDataIndexer.cs index ccc6ee27..b4a45dc9 100644 --- a/src/LibHac/FsSrv/SaveDataIndexer.cs +++ b/src/LibHac/FsSrv/SaveDataIndexer.cs @@ -12,7 +12,8 @@ using LibHac.Kvdb; using LibHac.Os; using LibHac.Sf; using LibHac.Util; -using SaveData = LibHac.Fs.SaveData; + +using static LibHac.Fs.SaveData; namespace LibHac.FsSrv; @@ -451,8 +452,8 @@ public class SaveDataIndexer : ISaveDataIndexer if (rc.IsFailure()) return rc; Assert.SdkRequires(_isLoaded); - Assert.SdkRequires(key.StaticSaveDataId != SaveData.InvalidSystemSaveDataId); - Assert.SdkRequires(key.UserId == SaveData.InvalidUserId); + Assert.SdkRequires(key.StaticSaveDataId != InvalidSystemSaveDataId); + Assert.SdkRequires(key.UserId == InvalidUserId); // Iterate through all existing values to check if the save ID is already in use. FlatMapKeyValueStore.Iterator iterator = _kvDatabase.GetBeginIterator(); diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs index 97f3d8d5..455b8dd1 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ApplicationSaveDataManagementTests.cs @@ -5,7 +5,7 @@ using LibHac.Fs.Shim; using LibHac.Ns; using Xunit; -using static LibHac.Fs.ApplicationSaveDataManagement; +using static LibHac.Fs.SaveData; namespace LibHac.Tests.Fs.FileSystemClientTests; @@ -19,13 +19,13 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); var userId = new Uid(2, 3); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { UserAccountSaveDataSize = 0x1000, UserAccountSaveDataJournalSize = 0x1000 }; - Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId)); + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); using var iterator = new UniqueRef(); fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User); @@ -47,13 +47,13 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); var userId = new Uid(2, 3); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { DeviceSaveDataSize = 0x1000, DeviceSaveDataJournalSize = 0x1000 }; - Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId)); + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); using var iterator = new UniqueRef(); fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User); @@ -63,7 +63,7 @@ public class ApplicationSaveDataManagementTests Assert.Equal(1, entriesRead); Assert.Equal(applicationId, info[0].ProgramId); - Assert.Equal(UserId.InvalidId, info[0].UserId); + Assert.Equal(InvalidUserId, info[0].UserId); Assert.Equal(SaveDataType.Device, info[0].Type); } @@ -75,12 +75,12 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); var userId = new Uid(2, 3); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { BcatDeliveryCacheStorageSize = 0x1000 }; - Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId)); + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); using var iterator = new UniqueRef(); fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User); @@ -90,7 +90,7 @@ public class ApplicationSaveDataManagementTests Assert.Equal(1, entriesRead); Assert.Equal(applicationId, info[0].ProgramId); - Assert.Equal(UserId.InvalidId, info[0].UserId); + Assert.Equal(InvalidUserId, info[0].UserId); Assert.Equal(SaveDataType.Bcat, info[0].Type); } @@ -102,12 +102,12 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); var userId = new Uid(2, 3); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { TemporaryStorageSize = 0x1000 }; - Assert.Success(EnsureApplicationSaveData(fs, out _, applicationId, ref nacp, ref userId)); + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); using var iterator = new UniqueRef(); fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.Temporary); @@ -117,9 +117,53 @@ public class ApplicationSaveDataManagementTests Assert.Equal(1, entriesRead); Assert.Equal(applicationId, info[0].ProgramId); - Assert.Equal(UserId.InvalidId, info[0].UserId); + Assert.Equal(InvalidUserId, info[0].UserId); Assert.Equal(SaveDataType.Temporary, info[0].Type); } + [Fact] + public static void EnsureApplicationSaveData_NeedsExtension_IsExtended() + { + FileSystemClient fs = FileSystemServerFactory.CreateClient(true); + + var applicationId = new Ncm.ApplicationId(11); + var userId = new Uid(2, 3); + + var controlProperty = new ApplicationControlProperty + { + UserAccountSaveDataSize = 0x1000, + UserAccountSaveDataJournalSize = 0x1000 + }; + + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); + + const int newAvailableSize = 1024 * 1024 * 2; + const int newJournalSize = 1024 * 1024; + + controlProperty.UserAccountSaveDataSize = newAvailableSize; + controlProperty.UserAccountSaveDataJournalSize = newJournalSize; + + Assert.Success(fs.EnsureApplicationSaveData(out _, applicationId, in controlProperty, in userId)); + + using var iterator = new UniqueRef(); + fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User); + + var info = new SaveDataInfo[2]; + Assert.Success(iterator.Get.ReadSaveDataInfo(out long entriesRead, info)); + + Assert.Equal(1, entriesRead); + Assert.Equal(applicationId, info[0].ProgramId); + Assert.Equal(Utility.ConvertAccountUidToFsUserId(userId), info[0].UserId); + Assert.Equal(SaveDataType.Account, info[0].Type); + + // ReSharper disable UnusedVariable + Assert.Success(fs.GetSaveDataAvailableSize(out long availableSize, SaveDataSpaceId.User, info[0].SaveDataId)); + Assert.Success(fs.GetSaveDataJournalSize(out long journalSize, SaveDataSpaceId.User, info[0].SaveDataId)); + // ReSharper restore UnusedVariable + + // Todo: Remove once save data extension is implemented + // Assert.Equal(newAvailableSize, availableSize); + // Assert.Equal(newJournalSize, journalSize); + } [Fact] public static void EnsureApplicationCacheStorage_SdCardAvailable_CreatesCacheStorageOnSd() @@ -128,14 +172,14 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { CacheStorageSize = 0x1000, CacheStorageJournalSize = 0x1000 }; Assert.Success(fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia target, applicationId, - ref nacp)); + in controlProperty)); Assert.Equal(CacheStorageTargetMedia.SdCard, target); @@ -157,14 +201,14 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { CacheStorageSize = 0x1000, CacheStorageJournalSize = 0x1000 }; Assert.Success(fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia target, applicationId, - ref nacp)); + in controlProperty)); Assert.Equal(CacheStorageTargetMedia.Nand, target); @@ -188,13 +232,14 @@ public class ApplicationSaveDataManagementTests var applicationId = new Ncm.ApplicationId(11); - var nacp = new ApplicationControlProperty + var controlProperty = new ApplicationControlProperty { CacheStorageSize = 0x1000, CacheStorageJournalSize = 0x1000 }; - fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia targetFromCreation, applicationId, ref nacp); + fs.EnsureApplicationCacheStorage(out _, out CacheStorageTargetMedia targetFromCreation, applicationId, + in controlProperty); Assert.Success(fs.GetCacheStorageTargetMedia(out CacheStorageTargetMedia target, applicationId)); Assert.Equal(targetFromCreation, target); diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs index dda8a063..f4c2512a 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/ShimTests/SaveDataManagement.cs @@ -8,6 +8,8 @@ using LibHac.Ncm; using LibHac.Time; using Xunit; +using static LibHac.Fs.SaveData; + namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests; public class SaveDataManagement @@ -322,7 +324,7 @@ public class SaveDataManagement Assert.Success(client.Fs.RegisterProgramIndexMapInfo(mapInfo)); - Assert.Success(subProgramClient.Fs.CreateSaveData(Ncm.ApplicationId.InvalidId, UserId.InvalidId, 0, 0x4000, + Assert.Success(subProgramClient.Fs.CreateSaveData(Ncm.ApplicationId.InvalidId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None)); // Get the created save data's ID @@ -497,7 +499,8 @@ public class SaveDataManagement Assert.Success(fs.CreateSaveData(applicationId, user2Id, 0, 0x4000, 0x4000, SaveDataFlags.None)); } - Assert.Success(SaveDataFilter.Make(out SaveDataFilter filter, default, default, user2Id, default, default)); + Assert.Success(SaveDataFilter.Make(out SaveDataFilter filter, programId: default, saveType: default, user2Id, + saveDataId: default, index: default)); using var iterator = new UniqueRef(); Assert.Success(fs.OpenSaveDataIterator(ref iterator.Ref(), SaveDataSpaceId.User, in filter)); @@ -651,7 +654,7 @@ public class SaveDataManagement for (int i = 1; i <= count; i++) { var applicationId = new Ncm.ApplicationId((uint)i); - Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None); + Result rc = fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None); if (rc.IsFailure()) return rc; } } @@ -662,7 +665,7 @@ public class SaveDataManagement for (int i = 1; i <= count; i++) { var applicationId = new Ncm.ApplicationId((uint)rng.Next()); - Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None); + Result rc = fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None); if (rc.IsFailure()) return rc; } } diff --git a/tests/LibHac.Tests/Fs/FsaTests/MultiCommitTests.cs b/tests/LibHac.Tests/Fs/FsaTests/MultiCommitTests.cs index 840cb7da..7bf09997 100644 --- a/tests/LibHac.Tests/Fs/FsaTests/MultiCommitTests.cs +++ b/tests/LibHac.Tests/Fs/FsaTests/MultiCommitTests.cs @@ -6,6 +6,8 @@ using LibHac.Fs.Shim; using LibHac.Tests.Fs.FileSystemClientTests; using Xunit; +using static LibHac.Fs.SaveData; + namespace LibHac.Tests.Fs.FsaTests; public class MultiCommitTests @@ -25,8 +27,8 @@ public class MultiCommitTests foreach ((int id, string name) info in saveInfo) { var applicationId = new Ncm.ApplicationId((uint)info.id); - fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None); - fs.MountSaveData(info.name.ToU8Span(), applicationId, UserId.InvalidId); + fs.CreateSaveData(applicationId, InvalidUserId, 0, 0x4000, 0x4000, SaveDataFlags.None); + fs.MountSaveData(info.name.ToU8Span(), applicationId, InvalidUserId); } foreach ((int id, string name) info in saveInfo) @@ -51,7 +53,7 @@ public class MultiCommitTests foreach ((int id, string name) info in saveInfo) { var applicationId = new Ncm.ApplicationId((uint)info.id); - fs.MountSaveData(info.name.ToU8Span(), applicationId, UserId.InvalidId); + fs.MountSaveData(info.name.ToU8Span(), applicationId, InvalidUserId); } foreach ((int id, string name) info in saveInfo) @@ -59,4 +61,4 @@ public class MultiCommitTests Assert.Success(fs.GetEntryType(out _, $"{info.name}:/file{info.id}".ToU8Span())); } } -} +} \ No newline at end of file