Skip to content

fix(libSceNpTrophy): keep a trophy locked when its unlock state cannot be saved - #12290

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
MarcZX10:fix/npTrophy-save-state-failures
Oct 28, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
MarcZX10:fix/npTrophy-save-state-failures

Conversation

@MarcZX10

Copy link
Copy Markdown
Contributor

What

sceNpTrophyUnlockTrophy inserted the trophy id into the in-memory set and then saved the state file. When the save threw (the state directory or the rename failed), the trophy stayed unlocked in memory but not on disk, so a retry answered TROPHY_ALREADY_UNLOCKED and the unlock was lost at the next start. The new set is now built on a copy, written, and only then installed.

SaveState also never looked at the stream after writing, so a failed write (a full disk, for example) was ignored and the truncated temporary file was renamed over the good state file. It now flushes, checks the stream, removes the partial temporary file and throws, in the library's existing fail-loud style.

Two commits: the fix, then the test (GuestNpTrophy.cpp: a directory in place of the temporary state file makes the save fail; the unlock has to report it, leave trophy 0 locked, and succeed once the directory is gone).

Tested

Linux x86-64, a throwaway archive build with the host CPU gate patched. guest_np_trophy passes. I did not run the new assertions against the unfixed code. tools/check_conventions.py --base upstream/main and git diff --check pass.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

…t be saved

sceNpTrophyUnlockTrophy inserted the id into the in-memory set and then saved the
state file. A save that threw (the state directory or the rename failed) left the
trophy unlocked in memory but not on disk, so a retry answered
TROPHY_ALREADY_UNLOCKED and the unlock was lost at the next start. The new set is
now built on a copy, written, and only then installed.

SaveState also never looked at the stream after writing, so a failed write (for
example a full disk) was ignored and the truncated temporary file was renamed over
the good state file. It now flushes, checks the stream, removes the partial
temporary file and throws.
A directory in place of the temporary state file makes the save fail; the unlock
has to report it, leave trophy 0 locked, and succeed once the directory is gone.
@maskym

maskym commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Windows result for head 161c9d55c6, merged into main 87b810a1db (the PR's Tested section names no Windows run).

Host: Windows 11 26300, Intel i7-12700K, WinLibs GCC 15.2.0 (x86_64-ucrt-posix-seh, r6), Release, BUILD_TESTING=ON, Ninja. Build clean.

The tree also carried the open PRs #12288, #12285, #12269, #12265, #12232, #12206, #12201, #12199, #12197, #12165, #12146, all merged cleanly, so the run is a joint one; the tests of this PR are guest_np_trophy (0.04 s), all passed (every test registered from the tree's changed test files: 38/38). Not run on a PS5.

AI-assisted: yes (Claude Code), the runs and the numbers above are from this machine.

@B-Howell

Copy link
Copy Markdown
Contributor

Results in the project's real build on Windows and Linux, with the control the description lists as not run ("I did not run the new assertions against the unfixed code").

Head 161c9d55c68d8333073b651ffbfeabb832b8ab23 (4 behind main) merged onto main d7fd41699238337a84cc61de848b5523a938031d (clean) on both hosts, alone in the tree.

Windows: Windows 11 Pro 26300, AMD Ryzen 7 7800X3D, WinLibs GCC 15.2.0 r7, Ninja Release, BUILD_TESTING=ON, libs rebuilt.

  • ctest -R '^guest_np_trophy$' --repeat until-fail:10: passes all 10 runs.
  • Control: libSceNpTrophy/Export.cpp restored from main, test kept. It fails, and on Windows main's behaviour is worse than a wrong answer: the failed save throws std::filesystem::filesystem_error out of libSceNpTrophy.prx+0x4884, and the runtime reports it as an exception that cannot be delivered.

Linux: Ubuntu 22.04 under WSL2, GCC 11.4.0, Ninja Release.

  • The same command passes all 10 runs.
  • The same control aborts at the new check, NpTrophy: a failed save leaves the trophy locked.

tools/check_test_registration.py: no executables built and not run, none new, on either host.

Not tested: a real save-data failure outside the test's setup, macOS, any title.

AI-assisted (Claude Code).

@oneandonlydean oneandonlydean self-assigned this Oct 28, 2026
@oneandonlydean
oneandonlydean merged commit 928e677 into boykopovar:main Oct 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants