diff --git a/src/LibHac/Fs/ApplicationSaveDataManagement.cs b/src/LibHac/Fs/ApplicationSaveDataManagement.cs index f73cf2fd..e4798644 100644 --- a/src/LibHac/Fs/ApplicationSaveDataManagement.cs +++ b/src/LibHac/Fs/ApplicationSaveDataManagement.cs @@ -13,127 +13,55 @@ namespace LibHac.Fs ref ApplicationControlProperty nacp, ref Uid uid) { requiredSize = default; - long requiredSizeSum = 0; - // If the application needs a user save + // Create local variable for use in closures + TitleId 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 = 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)); - Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); + // The 0x4c000 includes the save meta and other stuff + Result rc = EnsureAndExtendSaveData(fs, CreateAccountSaveFunc, ref requiredSizeSum, ref filter, 0x4c000, + accountSaveDataSize, accountSaveJournalSize); - // If the save already exists - if (rc.IsSuccess()) - { - // Make sure the save is large enough - rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeUser, SaveDataSpaceId.User, - saveDataInfo.SaveDataId, nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize); - - if (rc.IsFailure()) - { - if (!ResultFs.InsufficientFreeSpace.Includes(rc)) - { - return rc; - } - - requiredSizeSum = requiredSizeUser; - } - } - else if (!ResultFs.TargetNotFound.Includes(rc)) - { - return rc; - } - else - { - // The save doesn't exist, so try to create it - UserId userId = ConvertAccountUidToFsUserId(uid); - - Result createRc = fs.CreateSaveData(applicationId, userId, nacp.SaveDataOwnerId, - nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize, 0); - - if (createRc.IsFailure()) - { - // If there's insufficient free space, calculate the space required to create the save - if (ResultFs.InsufficientFreeSpace.Includes(createRc)) - { - Result queryRc = fs.QuerySaveDataTotalSize(out long userAccountTotalSize, - nacp.UserAccountSaveDataSize, nacp.UserAccountSaveDataJournalSize); - - if (queryRc.IsFailure()) return queryRc; - - // The 0x4c000 includes the save meta and other stuff - requiredSizeSum = Util.AlignUp(userAccountTotalSize, 0x4000) + 0x4c000; - } - else if (ResultFs.PathAlreadyExists.Includes(createRc)) - { - requiredSizeSum = 0; - } - else - { - return createRc; - } - } - } + 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 = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); + Result rc = EnsureAndExtendSaveData(fs, CreateDeviceSaveFunc, ref requiredSizeSum, ref filter, 0x4000, + deviceSaveDataSize, deviceSaveJournalSize); - if (rc.IsSuccess()) - { - rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeDevice, SaveDataSpaceId.User, - saveDataInfo.SaveDataId, nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize); - - if (rc.IsFailure()) - { - if (!ResultFs.InsufficientFreeSpace.Includes(rc)) - { - return rc; - } - - requiredSizeSum += requiredSizeDevice; - } - } - else if (!ResultFs.TargetNotFound.Includes(rc)) - { - return rc; - } - else - { - Result createRc = fs.CreateDeviceSaveData(applicationId, nacp.SaveDataOwnerId, - nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize, 0); - - if (createRc.IsFailure()) - { - if (ResultFs.InsufficientFreeSpace.Includes(createRc)) - { - Result queryRc = fs.QuerySaveDataTotalSize(out long deviceSaveTotalSize, - nacp.DeviceSaveDataSize, nacp.DeviceSaveDataJournalSize); - - if (queryRc.IsFailure()) return queryRc; - - // Not sure what the additional 0x4000 is - requiredSizeSum += Util.AlignUp(deviceSaveTotalSize, 0x4000) + 0x4000; - } - else if (ResultFs.PathAlreadyExists.Includes(createRc)) - { - requiredSizeSum += 0; - } - else - { - return createRc; - } - } - } + if (rc.IsFailure()) return rc; } Result bcatRc = EnsureApplicationBcatDeliveryCacheStorageImpl(fs, @@ -244,71 +172,63 @@ namespace LibHac.Fs return Result.Success; } + private static Result EnsureAndExtendSaveData(FileSystemClient fs, Func createFunc, + ref long requiredSize, ref SaveDataFilter filter, long baseSize, long dataSize, long journalSize) + { + Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, ref filter); + + if (rc.IsFailure()) + { + if (ResultFs.TargetNotFound.Includes(rc)) + { + rc = CreateSaveData(fs, createFunc, ref requiredSize, baseSize, dataSize, journalSize); + } + + return rc; + } + + rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeExtend, SaveDataSpaceId.User, info.SaveDataId, + dataSize, journalSize); + + if (rc.IsFailure()) + { + if (!ResultFs.InsufficientFreeSpace.Includes(rc)) + return rc; + + requiredSize += requiredSizeExtend; + } + + return Result.Success; + } + private static Result EnsureApplicationBcatDeliveryCacheStorageImpl(FileSystemClient fs, out long requiredSize, TitleId applicationId, ref ApplicationControlProperty nacp) { const long bcatDeliveryCacheJournalSize = 0x200000; - requiredSize = default; - - if (nacp.BcatDeliveryCacheStorageSize <= 0) + long bcatStorageSize = nacp.BcatDeliveryCacheStorageSize; + if (bcatStorageSize <= 0) { requiredSize = 0; return Result.Success; } + requiredSize = default; + long requiredSizeBcat = 0; + var filter = new SaveDataFilter(); filter.SetProgramId(applicationId); filter.SetSaveDataType(SaveDataType.Bcat); - Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, ref filter); + Result CreateBcatStorageFunc() => fs.CreateBcatSaveData(applicationId, bcatStorageSize); - if (rc.IsSuccess()) - { - rc = ExtendSaveDataIfNeeded(fs, out long requiredSizeBcat, SaveDataSpaceId.User, - saveDataInfo.SaveDataId, nacp.BcatDeliveryCacheStorageSize, bcatDeliveryCacheJournalSize); + Result rc = EnsureAndExtendSaveData(fs, CreateBcatStorageFunc, + ref requiredSizeBcat, ref filter, 0x4000, bcatStorageSize, bcatDeliveryCacheJournalSize); - if (rc.IsFailure()) - { - if (!ResultFs.InsufficientFreeSpace.Includes(rc)) - { - return rc; - } + if (rc.IsFailure()) return rc; - requiredSize = requiredSizeBcat; - } - } - else if (!ResultFs.TargetNotFound.Includes(rc)) - { - return rc; - } - else - { - Result createRc = fs.CreateBcatSaveData(applicationId, nacp.BcatDeliveryCacheStorageSize); - - if (createRc.IsFailure()) - { - if (ResultFs.InsufficientFreeSpace.Includes(createRc)) - { - Result queryRc = fs.QuerySaveDataTotalSize(out long saveTotalSize, - nacp.BcatDeliveryCacheStorageSize, bcatDeliveryCacheJournalSize); - - if (queryRc.IsFailure()) return queryRc; - - requiredSize = Util.AlignUp(saveTotalSize, 0x4000) + 0x4000; - } - else if (ResultFs.PathAlreadyExists.Includes(createRc)) - { - requiredSize = 0; - } - else - { - return createRc; - } - } - } - - return requiredSize > 0 ? ResultFs.InsufficientFreeSpace.Log() : Result.Success; + requiredSize = requiredSizeBcat; + return requiredSizeBcat > 0 ? ResultFs.InsufficientFreeSpace.Log() : Result.Success; } private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize, @@ -407,6 +327,12 @@ namespace LibHac.Fs nacp.SaveDataOwnerId, 0, nacp.CacheStorageSize, nacp.CacheStorageJournalSize, true); } + public static Result EnsureApplicationBcatDeliveryCacheStorage(this FileSystemClient fs, out long requiredSize, + TitleId applicationId, ref ApplicationControlProperty nacp) + { + return EnsureApplicationBcatDeliveryCacheStorageImpl(fs, out requiredSize, applicationId, ref nacp); + } + public static Result TryCreateCacheStorage(this FileSystemClient fs, out long requiredSize, SaveDataSpaceId spaceId, TitleId applicationId, TitleId saveDataOwnerId, short index, long dataSize, long journalSize, bool allowExisting) @@ -464,44 +390,35 @@ namespace LibHac.Fs private static Result GetCacheStorageTargetMediaImpl(this FileSystemClient fs, out CacheStorageTargetMedia target, TitleId applicationId) { - target = default; + target = CacheStorageTargetMedia.None; + + var filter = new SaveDataFilter(); + filter.SetProgramId(applicationId); + filter.SetSaveDataType(SaveDataType.Cache); if (fs.IsSdCardAccessible()) { - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Cache); - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, ref filter); + if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc; if (rc.IsSuccess()) { target = CacheStorageTargetMedia.SdCard; - return Result.Success; } - - if (!ResultFs.TargetNotFound.Includes(rc)) - return rc; } + // Not on the SD card. Check it it's in NAND + if (target == CacheStorageTargetMedia.None) { - var filter = new SaveDataFilter(); - filter.SetProgramId(applicationId); - filter.SetSaveDataType(SaveDataType.Cache); - Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, ref filter); + if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc; if (rc.IsSuccess()) { target = CacheStorageTargetMedia.Nand; - return Result.Success; } - - if (!ResultFs.TargetNotFound.Includes(rc)) - return rc; } - target = CacheStorageTargetMedia.None; return Result.Success; }