Files
Joe MattielloandClaude Opus 5.5 a1d9c4807a feat(app): Community cheats in the Cheats window (#1071, part 2) (#1076)
* feat(app): sha256 for verified downloads

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(app): CRC-keyed community cheat set in CheatManager

CheatManager loads <communityDir>/<CRC32>.cht for the running cartridge by CRC,
independent of the filename/title-matched database set. The pack is guarded by
vj_rom_crc32 like a revision-bound database set (unbound or mismatched: every
cheat unsupported plus a restriction), crc 0 (discs) never loads one.

Toggles persist under cp.<id> (database keeps on.<id>); ids are unchanged so an
updated pack keeps toggles for unchanged cheats and a changed cheat comes back
off, and cp.* keys that match no cheat are pruned on load. apply() orders
database, community (5000+i), then manual codes (10000+i). New public API:
loadChtText, setCommunityDir, reloadCommunity, communitySet,
removeCommunityToggles, CheatOrigin on isEnabled/setEnabled.

Also corrects two stale comments in cheats_test.cpp (sha256 vectors, linked
sources).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(app): drop dead prune loop, document setEnabledByName fall-through

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(app): community cheat store service

CheatStore fetches the community manifest, installs a cartridge's pack after
verifying it (size, sha256, vj_rom_crc32 guard, parse), tracks per-pack state,
and feeds CheatManager. Network and worker are injected hooks; results are
applied on the main thread through pump().

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(app): harden cheat store against throwing hooks, racing remove, message floods

A throwing fetch hook can no longer leave stop() hanging (RAII job slot plus
catch); remove() refuses while an install for the same CRC is in flight; and
manifest messages are capped at 8 entries of 200 bytes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(app): Community cheats section in the Cheats window

Wires CheatStore into the app (#1071): the store, its one-request worker and
the http::get fetch hook (any status but 200 is an error, the store's cancel
flag is passed through) are set up in cheatsInit with downloads gated by the
single Library "Download art and cheat packs" switch (library.art, label
renamed) and cheats.community.url as the base URL override. The store is
pumped every frame, told of game load/close, and stopped (cancel first, then
the worker) before the cheat manager goes.

The Cheats window gains a "Community cheats" section between Database cheats
and Manual codes (install / update / remove / check states, credit line,
collapsed conversion notes, 256-write warning, hardcore line); both sections
share drawCheatRows, which now passes CheatOrigin. Buttons are disabled while
a request is in flight and stay live in hardcore, where only toggles are off.

--cheats-community-install is the headless twin (fetch the manifest, install
the loaded cartridge's pack); cheat_community_install runs it against a
loopback server serving site/cheats, then offline, then with nothing to
install from.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(cheats): describe the community cheat store (#1071)

Privacy page: a row for the project website's cheat store (when it is asked,
what is sent: the pack index and the one pack named by the cartridge's CRC32,
nothing else) and cheats/community/ in the local-files list; the art row
follows the renamed "Download art and cheat packs" switch. App page, app
README, the converter README and WHATSNEW describe installing packs from the
Cheats window.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(app): marshal cheat store game events to the main thread, report turned-off cheats

The store is main-thread only but cheatsAfterLoad / cheatsOnClose run on the
emulation thread in threaded mode. They now post the load / close
(CheatStore::postGameLoaded / postGameClosed, last post wins) and pump()
applies it before finished requests, so refresh(), announce() and the message
toasts stay on the main thread.

After an Update, the section says how many cheats that were on changed and
came back off (CheatManager::lastPrunedEnabled, CheatStore::turnedOff; cleared
by the next load or Remove). A store that was never reached says so instead of
"No community pack", and Check for updates is shown disabled rather than
hidden while checking or with downloads off.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(app): keep an unreadable community pack removable, pump the store during a halt

The Cheats window derived Remove (and Update) from cm.communitySet(), so a
<CRC>.cht that exists but does not parse showed sidecar text with no way out.
Remove and Update now follow the store state alone; only the cheat rows need
the parsed set, and an unreadable or restricted pack gets a one-line notice.
The database "no set matches" status is dimmed when a community pack already
supplies cheats, a greyed Update says downloads are off, and haltedFrame pumps
the store so installs finish during a debugger halt.

Also documents the emulation-thread toast/notice contract in cheatsAfterLoad
and tidies style (comma-operator statements, std::set, include order).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* test(app): keep the community store fixtures byte-exact on Windows

The fixture pack's sha256 and size are pinned in the fixture manifest.
Git for Windows' autocrlf turned its LF into CRLF on checkout, so
cheats_unit failed verifyPack on every Windows app job. Mark the
fixtures -text so they are checked out exactly as committed.

Refs #1071

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(app): headless community install repairs a locally edited pack

--cheats-community-install skipped install() when the installed pack's
bytes no longer matched its sidecar (EditedLocally), then failed its own
Installed check, although the Cheats window offers Update in that state.
Install there too when the store offers the pack. The loopback smoke test
now hand-edits the installed pack and expects the command to replace it.

Refs #1071

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-23 03:23:05 -04:00

4 lines
106 B
Plaintext

tools/cheat-import/** text eol=lf
site/cheats/** text eol=lf
app/tests/fixtures/community/** -text