| 660 | } |
| 661 | |
| 662 | void MainScreen::update(const InputState& input) |
| 663 | { |
| 664 | // Deliver a finished backup/restore: the worker ran the copy off the main |
| 665 | // loop, so the result comes back here. Refresh the backup lists the worker |
| 666 | // touched (it cannot, having no catalog mutex), raise the result overlay, and |
| 667 | // skip input this frame (next frame routes to the overlay). |
| 668 | if (auto result = TransferJob::get().takeResult()) { |
| 669 | for (u64 id : result->refreshIds) { |
| 670 | TitleCatalog::get().refreshDirectories(id); |
| 671 | BackupSizeCache::get().invalidate(id); // folders changed → recompute sizes |
| 672 | } |
| 673 | if (result->send) { |
| 674 | // A network send: map the outcome to a message. EmptyBackup/Cancelled |
| 675 | // are neutral info; every other stage is an error. |
| 676 | if (result->send->stage == Transfer::SendStage::EmptyBackup) { |
| 677 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("main.backup_empty")); |
| 678 | } |
| 679 | else if (result->send->stage == Transfer::SendStage::Cancelled) { |
| 680 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("transfer.cancelled")); |
| 681 | } |
| 682 | else if (result->send->ok) { |
| 683 | blinkLed(4); |
| 684 | currentOverlay = std::make_shared<InfoOverlay>(*this, result->successMsg); |
| 685 | } |
| 686 | else { |
| 687 | currentOverlay = std::make_shared<ErrorOverlay>(*this, -1, sendErrorMessage(*result->send)); |
| 688 | } |
| 689 | return; |
| 690 | } |
| 691 | if (result->cancelled) { |
| 692 | currentOverlay = std::make_shared<InfoOverlay>(*this, i18n::t("main.backup_cancelled")); |
| 693 | } |
| 694 | else if (result->ok) { |
| 695 | blinkLed(4); |
| 696 | currentOverlay = std::make_shared<InfoOverlay>(*this, result->successMsg); |
| 697 | } |
| 698 | else { |
| 699 | std::string message = result->isRestore ? restoreErrorMessage(result->stage) : backupErrorMessage(result->stage); |
| 700 | currentOverlay = std::make_shared<ErrorOverlay>(*this, result->res, message); |
| 701 | } |
| 702 | return; |
| 703 | } |
| 704 | |
| 705 | // While a transfer runs on the worker, the loop keeps drawing the modal from |
| 706 | // TransferStatus; ignore input so nothing mutates underneath the copy, except a |
| 707 | // B press on a backup in flight, which requests a cancel (a restore can't be |
| 708 | // cancelled: aborting mid-write could leave a truncated save on the cartridge). |
| 709 | if (TransferJob::get().active()) { |
| 710 | TransferSnapshot ts = TransferStatus::snapshot(); |
| 711 | if (ts.active && ts.kind == TransferKind::Network) { |
| 712 | // A network send is cancelled by holding B (parity with the receive |
| 713 | // overlay); a stray tap won't kill a long transfer. |
| 714 | if (input.kHeld & HidNpadButton_B) { |
| 715 | if (++mCancelHoldFrames >= 45 && !TransferStatus::cancelRequested()) { |
| 716 | TransferStatus::requestCancel(); |
| 717 | } |
| 718 | } |
| 719 | else { |
no test coverage detected