/// <summary> /// To be called once the multi-commit has been successfully completed. Deletes the commit context file. /// </summary> /// <returns>The <see cref="Result"/> of the operation.</returns> public Result CommitDone() { Result rc = _fileSystem.DeleteFile(CommitContextFileName); if (rc.IsFailure()) { return(rc); } rc = _fileSystem.Commit(); if (rc.IsFailure()) { return(rc); } _fileSystem = null; return(Result.Success); }
// DeleteFile(buffer<bytes<0x301>, 0x19, 0x301> path) public ResultCode DeleteFile(ServiceCtx context) { U8Span name = ReadUtf8Span(context); return((ResultCode)_fileSystem.DeleteFile(name).Value); }
/// <summary> /// Tries to recover a multi-commit using the context in the provided file system. /// </summary> /// <param name="multiCommitInterface">The core interface used for multi-commits.</param> /// <param name="contextFs">The file system containing the multi-commit context file.</param> /// <param name="saveService">The save data service.</param> /// <returns></returns> private static Result Recover(ISaveDataMultiCommitCoreInterface multiCommitInterface, IFileSystem contextFs, SaveDataFileSystemServiceImpl saveService) { if (multiCommitInterface is null) { return(ResultFs.InvalidArgument.Log()); } if (contextFs is null) { return(ResultFs.InvalidArgument.Log()); } // Keep track of the first error that occurs during the recovery Result recoveryResult = Result.Success; Result rc = RecoverCommit(multiCommitInterface, contextFs, saveService); if (rc.IsFailure()) { // Note: Yes, the next ~50 lines are exactly the same as the code in RecoverCommit except // for a single bool value. No, Nintendo doesn't split it out into its own function. int saveCount = 0; Span <SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount]; SaveDataIndexerAccessor accessor = null; ReferenceCountedDisposable <SaveDataInfoReaderImpl> infoReader = null; try { rc = saveService.OpenSaveDataIndexerAccessor(out accessor, out _, SaveDataSpaceId.User); if (rc.IsFailure()) { return(rc); } rc = accessor.Indexer.OpenSaveDataInfoReader(out infoReader); if (rc.IsFailure()) { return(rc); } // Iterate through all the saves to find any provisionally committed save data while (true) { Unsafe.SkipInit(out SaveDataInfo info); rc = infoReader.Target.Read(out long readCount, OutBuffer.FromStruct(ref info)); if (rc.IsFailure()) { return(rc); } // Break once we're done iterating all save data if (readCount == 0) { break; } rc = multiCommitInterface.IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in info); // Note: Some saves could be missed if there are more than MaxFileSystemCount // provisionally committed saves. Not sure why Nintendo doesn't catch this. if (rc.IsSuccess() && isProvisionallyCommitted && saveCount < MaxFileSystemCount) { savesToRecover[saveCount] = info; saveCount++; } } } finally { accessor?.Dispose(); infoReader?.Dispose(); } // Recover the saves by rolling them back to the previous commit. // All file systems will try to be recovered, even if one fails. // If any commits fail, the result from the first failed recovery will be returned. for (int i = 0; i < saveCount; i++) { rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], true); if (recoveryResult.IsSuccess() && rc.IsFailure()) { recoveryResult = rc; } } } // Delete the commit context file rc = contextFs.DeleteFile(CommitContextFileName); if (rc.IsFailure()) { return(rc); } rc = contextFs.Commit(); if (rc.IsFailure()) { return(rc); } return(recoveryResult); }