| 697 | } |
| 698 | |
| 699 | void MainScreen::update(const InputState& input) |
| 700 | { |
| 701 | // Deliver a finished backup/restore: the worker ran the copy off the main |
| 702 | // loop, so the result comes back here. Refresh the backup lists the worker |
| 703 | // touched (it cannot, having no catalog mutex), raise the result overlay, and |
| 704 | // skip input this frame (next frame routes to the overlay). |
| 705 | if (auto result = TransferJob::get().takeResult()) { |
| 706 | for (u64 id : result->refreshIds) { |
| 707 | TitleCatalog::get().refreshDirectories(id); |
| 708 | BackupSizeCache::get().invalidate(id); // folders changed → recompute sizes |
| 709 | } |
| 710 | if (result->send) { |
| 711 | // A network send: map the outcome to a message. EmptyBackup/Cancelled |
| 712 | // are neutral info; every other stage is an error. |
| 713 | if (result->send->stage == Transfer::SendStage::EmptyBackup) { |
| 714 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("main.backup_empty")); |
| 715 | } |
| 716 | else if (result->send->stage == Transfer::SendStage::Cancelled) { |
| 717 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("transfer.cancelled")); |
| 718 | } |
| 719 | else if (result->send->ok) { |
| 720 | blinkLed(4); |
| 721 | currentOverlay = std::make_shared<InfoOverlay>(*this, result->successMsg); |
| 722 | } |
| 723 | else { |
| 724 | currentOverlay = std::make_shared<ErrorOverlay>(*this, -1, sendErrorMessage(*result->send)); |
| 725 | } |
| 726 | return; |
| 727 | } |
| 728 | if (result->cancelled) { |
| 729 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("main.backup_cancelled")); |
| 730 | } |
| 731 | else if (result->ok) { |
| 732 | blinkLed(4); |
| 733 | currentOverlay = std::make_shared<InfoOverlay>(*this, result->successMsg); |
| 734 | } |
| 735 | else { |
| 736 | std::string message = result->isRestore ? restoreErrorMessage(result->stage) : backupErrorMessage(result->stage); |
| 737 | currentOverlay = std::make_shared<ErrorOverlay>(*this, result->res, message); |
| 738 | } |
| 739 | return; |
| 740 | } |
| 741 | |
| 742 | // While a transfer runs on the worker, the loop keeps drawing the modal from |
| 743 | // TransferStatus; ignore input so nothing mutates underneath the copy, except a |
| 744 | // B press on a backup in flight, which requests a cancel (a restore can't be |
| 745 | // cancelled: aborting mid-write could leave a truncated save on the cartridge). |
| 746 | if (TransferJob::get().active()) { |
| 747 | TransferSnapshot ts = TransferStatus::snapshot(); |
| 748 | if (ts.active && ts.kind == TransferKind::Network) { |
| 749 | // A network send is cancelled by holding B (parity with the receive |
| 750 | // overlay); a stray tap won't kill a long transfer. |
| 751 | if (input.kHeld & HidNpadButton_B) { |
| 752 | if (++mCancelHoldFrames >= 45 && !TransferStatus::cancelRequested()) { |
| 753 | TransferStatus::requestCancel(); |
| 754 | } |
| 755 | } |
| 756 | else { |
no test coverage detected