fix(saves): import from and export to NX2navi work again: only a finished commit in save-tx is a save waiting, and a copy that worked says so on the Saves page #33

Merged
HumanBean merged 1 commit from HumanBean/LongshotLauncher:fix/save-import into main 2026-11-13 11:32:33 +00:00
Owner

Importing saves from NX2navi (and exporting to it) was refused every time with "NX2navi has a save waiting", even right after starting and quitting NX2navi as the message asks. This makes the check look only for a save NX2navi really has waiting, and makes a copy that worked say so on the Saves page too.

Why it was refused

Saves.PendingCommit (Core/Saves.cs) counted any file anywhere under profile/save-tx as a save waiting. But NX2navi keeps more than waiting saves there (its runtime/fs_host.c, "save transactions"):

In profile/save-tx What it is
work/ the save mount's working copy. The exit waits for a commit but does not unmount the save, so a quit leaves it, full of save files, and the next start deletes it
new/ an unfinished commit: rolled back at the next start
ready-<n>/ a finished commit not swapped in yet: this is the save NX2navi loads next
old/ the save a commit replaced, on its way to save-backup

So after any normal quit work/ held files, and every copy from or to NX2navi was refused; starting and quitting again just made a new work/. Now only a ready-<n> folder (n from 1, as NX2navi's own ready_number reads it) counts as a save waiting.

What changed

  • Core/Saves.cs: PendingCommit looks for a save-tx/ready-<n> folder instead of any file under save-tx.
  • App/ViewModels/SavesViewModel.cs: after a copy, import or export that worked, the result page already said so ("Saves imported from NX2navi"). Now Back to saves also shows a toast on the Saves page ("Imported 3 save slots from NX2navi."), as the Hookshot import does.
  • App/ViewModels/HookshotImportViewModel.cs: an import from Hookshot opened from the Saves page reloads that page, so the imported slots show at once (it showed the old ones until you left the page).
  • docs/saves.md: what counts as a save waiting in NX2navi's save-tx, and the success message.
  • tests/launcher_core/SavesChecks.cs, ScreenChecks.cs: the NX2navi checks now put a full work/ (and new/, old/, and names that aren't a finished commit, like ready-0 or a file ready-2) in save-tx and require import and export to go through; the waiting check uses ready-0000000003/. With the old rule these checks fail.

Screenshots

Made with a headless harness on the real MainWindow (Linux layout), with a fake NX2navi game folder of one-line text files, with save-tx/work as NX2navi leaves it.

Before: import from NX2navi, refused

import refused

After: the import goes through

import done

Back on the Saves page: the imported slots, and the toast

saves page with toast

Export to NX2navi

export done

A real finished commit waiting (save-tx/ready-0000000004) is still refused

a finished commit waiting

Testing

  • python3 -I tests/test_launcher_core.py --longshot <a Longshot checkout>: launcher core: 634 checks passed. With PendingCommit put back to the old rule, the new NX2navi check fails.
  • python3 -I tests/test_release.py: Ran 28 tests, OK.
  • The harness above (Debug build): import and export with save-tx/work present succeed, with a ready-<n> they're refused. The harness runs the copy with no game counted as running, because a Longshot game was running on the PC at the time; everything else is the app's own code path.

Not tested:

  • A real NX2navi install: none on this PC, so the folder layout comes from NX2navi's runtime/fs_host.c and docs/launcher.md, not from a folder NX2navi wrote. Worth one import on the reporter's PC (Windows or Linux) before release.
  • Windows: the change is platform-neutral, but no Windows run.

Follow-ups (not in this PR)

  • Longshot's scripts/oot_saves.py pending_commit has the same rule (any file under save-tx), and so has the same false refusal; its tests/test_oot_saves.py NX2navi case puts a loose file save-tx/commit in, which NX2navi never writes. Both should take the same ready-<n> rule in a Longshot PR, to keep the two ports in step.
  • NX2navi's in-game menu import waits in profile/save-import until its next start, which then replaces the save. Neither port treats that as a save waiting yet.

🤖 Generated with Claude Code

Importing saves from NX2navi (and exporting to it) was refused every time with "NX2navi has a save waiting", even right after starting and quitting NX2navi as the message asks. This makes the check look only for a save NX2navi really has waiting, and makes a copy that worked say so on the Saves page too. ## Why it was refused `Saves.PendingCommit` (Core/Saves.cs) counted **any file anywhere under `profile/save-tx`** as a save waiting. But NX2navi keeps more than waiting saves there (its `runtime/fs_host.c`, "save transactions"): | In `profile/save-tx` | What it is | |---|---| | `work/` | the save mount's working copy. The exit waits for a commit but does not unmount the save, so a quit leaves it, full of save files, and the next start deletes it | | `new/` | an unfinished commit: rolled back at the next start | | `ready-<n>/` | a **finished commit not swapped in yet**: this is the save NX2navi loads next | | `old/` | the save a commit replaced, on its way to `save-backup` | So after any normal quit `work/` held files, and every copy from or to NX2navi was refused; starting and quitting again just made a new `work/`. Now only a `ready-<n>` folder (n from 1, as NX2navi's own `ready_number` reads it) counts as a save waiting. ## What changed - **Core/Saves.cs**: `PendingCommit` looks for a `save-tx/ready-<n>` folder instead of any file under `save-tx`. - **App/ViewModels/SavesViewModel.cs**: after a copy, import or export that worked, the result page already said so ("Saves imported from NX2navi"). Now **Back to saves** also shows a toast on the Saves page ("Imported 3 save slots from NX2navi."), as the Hookshot import does. - **App/ViewModels/HookshotImportViewModel.cs**: an import from Hookshot opened from the Saves page reloads that page, so the imported slots show at once (it showed the old ones until you left the page). - **docs/saves.md**: what counts as a save waiting in NX2navi's `save-tx`, and the success message. - **tests/launcher_core/SavesChecks.cs**, **ScreenChecks.cs**: the NX2navi checks now put a full `work/` (and `new/`, `old/`, and names that aren't a finished commit, like `ready-0` or a file `ready-2`) in `save-tx` and require import and export to go through; the waiting check uses `ready-0000000003/`. With the old rule these checks fail. ## Screenshots Made with a headless harness on the real `MainWindow` (Linux layout), with a fake NX2navi game folder of one-line text files, with `save-tx/work` as NX2navi leaves it. Before: import from NX2navi, refused ![import refused](https://git.magicbean.dev/attachments/c983a7f0-e035-41c8-88dc-8b21fb0a3aa4) After: the import goes through ![import done](https://git.magicbean.dev/attachments/dee8dc00-918d-4218-9ed0-370b55d1bf23) Back on the Saves page: the imported slots, and the toast ![saves page with toast](https://git.magicbean.dev/attachments/374888ed-2f5a-46f7-bdb7-5a39ea297a54) Export to NX2navi ![export done](https://git.magicbean.dev/attachments/ff8ce7fc-273b-40ab-bd73-5f2566752a5a) A real finished commit waiting (`save-tx/ready-0000000004`) is still refused ![a finished commit waiting](https://git.magicbean.dev/attachments/7e669124-4f4e-412f-b249-0860bf2e4664) ## Testing - `python3 -I tests/test_launcher_core.py --longshot <a Longshot checkout>`: launcher core: 634 checks passed. With `PendingCommit` put back to the old rule, the new NX2navi check fails. - `python3 -I tests/test_release.py`: Ran 28 tests, OK. - The harness above (Debug build): import and export with `save-tx/work` present succeed, with a `ready-<n>` they're refused. The harness runs the copy with no game counted as running, because a Longshot game was running on the PC at the time; everything else is the app's own code path. Not tested: - A real NX2navi install: none on this PC, so the folder layout comes from NX2navi's `runtime/fs_host.c` and `docs/launcher.md`, not from a folder NX2navi wrote. Worth one import on the reporter's PC (Windows or Linux) before release. - Windows: the change is platform-neutral, but no Windows run. ## Follow-ups (not in this PR) - Longshot's `scripts/oot_saves.py` `pending_commit` has the same rule (any file under `save-tx`), and so has the same false refusal; its `tests/test_oot_saves.py` NX2navi case puts a loose file `save-tx/commit` in, which NX2navi never writes. Both should take the same `ready-<n>` rule in a Longshot PR, to keep the two ports in step. - NX2navi's in-game menu import waits in `profile/save-import` until its next start, which then replaces the save. Neither port treats that as a save waiting yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(saves): import from and export to NX2navi work again: only a finished commit in save-tx is a save waiting, and a copy that worked says so on the Saves page
All checks were successful
CI / Compile and test (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
b711632400
NX2navi leaves its working copy in profile/save-tx/work after a quit, so the old rule (any file under save-tx) refused
every copy from or to it. Only save-tx/ready-<n>, a finished commit NX2navi has not swapped in yet, is the save it
loads next (its runtime/fs_host.c). Back on the Saves page after a copy that worked, a toast says what was copied, and
an import from Hookshot opened from Saves shows the imported slots at once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
HumanBean deleted branch fix/save-import 2026-11-13 11:32:33 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/LongshotLauncher!33
No description provided.