| 445 | } |
| 446 | |
| 447 | void MainScreen::update(const InputState& input) |
| 448 | { |
| 449 | // Snapshot the live transfer state once for this frame; both draw*() read the |
| 450 | // member instead of re-locking TransferStatus. Done before any early return |
| 451 | // so the modal keeps updating even while a TransferJob is active. |
| 452 | mTransfer = TransferStatus::snapshot(); |
| 453 | |
| 454 | // Hold-B-to-cancel for the transfer modal. Must run before the TransferJob |
| 455 | // early returns below: while a transfer is in flight update() bails out right |
| 456 | // after the result poll, so this is the only input the modal gets. Cancel is |
| 457 | // offered for a network send and for a local backup (mode == "Backup"), never |
| 458 | // for a restore — a half-written restore must never be left on the cartridge. |
| 459 | const bool cancellable = mTransfer.active && (mTransfer.kind == TransferKind::Network || mTransfer.mode == "Backup"); |
| 460 | if (cancellable) { |
| 461 | if (hidKeysHeld() & KEY_B) { |
| 462 | if (++mCancelHoldFrames >= 45 && !TransferStatus::cancelRequested()) { |
| 463 | TransferStatus::requestCancel(); |
| 464 | } |
| 465 | } |
| 466 | else { |
| 467 | mCancelHoldFrames = 0; |
| 468 | } |
| 469 | } |
| 470 | else { |
| 471 | mCancelHoldFrames = 0; |
| 472 | } |
| 473 | |
| 474 | // Re-read in case the Settings page toggled it while this screen was parked. |
| 475 | transferEnabled = Configuration::getInstance().transferEnabled(); |
| 476 | |
| 477 | if (auto result = TransferJob::get().takeResult()) { |
| 478 | // A backup/restore changed one or more folders; drop every cached total so |
| 479 | // they get re-walked off-thread (a batch may have touched many titles). |
| 480 | BackupSizeCache::get().invalidateAll(); |
| 481 | if (result->cancelled) { |
| 482 | // User-requested stop of a backup: neutral info, not an error. |
| 483 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("transfer.cancelled")); |
| 484 | } |
| 485 | else if (result->ok) { |
| 486 | currentOverlay = std::make_shared<InfoOverlay>(*this, result->successMsg); |
| 487 | } |
| 488 | else if (result->send) { |
| 489 | if (result->send->stage == Transfer::SendStage::EmptyBackup) { |
| 490 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("main.backup_empty")); |
| 491 | } |
| 492 | else if (result->send->stage == Transfer::SendStage::Cancelled) { |
| 493 | // User-requested stop: neutral info, not an error. |
| 494 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("transfer.cancelled")); |
| 495 | } |
| 496 | else { |
| 497 | currentOverlay = std::make_shared<ErrorOverlay>(*this, -1, OutcomeMessages::sendError(*result->send)); |
| 498 | } |
| 499 | } |
| 500 | else { |
| 501 | std::string message = result->isRestore ? OutcomeMessages::restoreError(result->stage, result->dataType, result->res) |
| 502 | : OutcomeMessages::backupError(result->stage, result->dataType, result->res); |
| 503 | currentOverlay = std::make_shared<ErrorOverlay>(*this, result->res, message); |
| 504 | } |
no test coverage detected