mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-12 09:31:29 -10:00
dependabot/github_actions/develop/actions/setup-python-7
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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]> |
||
|
|
e6fa96e1d0 | feat(cheats): convert community source freezes with revision guards | ||
|
|
0085c4485d |
feat(app): emulation thread phase 2 — achievements, netplay and busy calls (#967) (#1031)
* docs(app): emulation thread phase 2 plan (#967) Achievements and netplay back on with the emulation thread (core-event stream, rc_client_do_frame on the emulation thread, netplay as a UI-thread object fed by the stream), the cheat batch as a regular command, the 250 ms busy spinner, and the Phase 1 open items (MinGW waits, limits after a failed start, the vsync-off nap). Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): EmuSnapshot in its own file, with the core-event stream (#967) CoreEventQueue carries log lines, vj_events and option writes from the emulation thread to the UI thread in order; log lines are bounded, events and option writes never dropped. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): core log lines, events and owner option writes reach the UI thread through the stream (#967) CoreHost::onUiEvent gets every log line and vj_event on the UI thread (pinSnapshot), with an epoch that rides the command queue; option writes made on the emulation thread inside a call() are applied to the UI's mirror and the config there too, never on the emulation thread. A content switch drops queued lines and events. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): netplay works with the emulation thread on (#967) Netplay stays a UI-thread object: the core's link lines and events reach it through CoreHost::onUiEvent, filtered by an epoch bumped at every start/stop/bounce, and the endpoint bounce completes after the core has read "disabled" for 2 emulated frames (3 threaded), counted with frameCount(). Threaded twins of netplay_pair (both sides, voice modem, a mixed threaded/inline pair) and of netplay_no_partner, which must exit 1, never 3. Co-Authored-By: Claude Opus 5.5 <[email protected]> * refactor(app): the cheat list reaches the core as one regular command (#967) CheatManager::apply() builds one CheatBatch (reset, then every set) and hands it to commit, which the app posts to the emulation thread; the three sinks and the UI-thread batch collector are gone. Co-Authored-By: Claude Opus 5.5 <[email protected]> * refactor(app): RetroAchievements safe on two threads (#967) rc_client events are copied into AchEvents and applied in pump(); core RAM, the memory map and the cheat resets are reached on the emulation thread (call/post); read_memory off it answers address checks from the map without touching RAM; the rich-presence line is computed with the frame and published; the mock server is locked. Inline runs behave as before. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): high-resolution short waits for the emulation thread on Windows (#967) The MinGW runner paced threaded fast-forward at one frame per ~15.6 ms (63.4 vs 96.6 frames/s); develop's newest completed MinGW runs (x64 and ARM64) pass emu_thread_unit, but ctest hides a passing test's output so the timer note is not visible there, and no MinGW run on develop has yet carried the 1 ms precise-wait check. preciseSleepNs uses a high-resolution waitable timer, and every app job now prints emu_thread_unit's timer report. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): the precise-sleep check uses the shortest wake (#967) A starved host still has some on-time wakes, while timers that are coarse by platform (every wake ~15.6 ms on MinGW) have none, so the check asserts on the shortest of 20 preciseSleepNs(1 ms) waits; the note prints mean and min. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): RetroAchievements works with the emulation thread on (#967) rc_client_do_frame (and with it rc_client_idle) runs after each frame on the emulation thread; the UI pump posts the idle while paused. A threaded headless RA run steps one frame at a time in the inline order, so every ra_* test has a _threaded twin that lands its steps on the same frames. Only the debugger tools and the GDB stub stay off. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): precise_sleep.h includes a lean windows.h (#967) emu_commands.h reaches ~24 MinGW translation units through app.h; WIN32_LEAN_AND_MEAN keeps legacy winsock and macro collisions out of them (the waitable-timer and Sleep calls need nothing outside it). A WaitForSingleObject that does not return WAIT_OBJECT_0 now falls back to Sleep(1), so a failed wait cannot make waitUntil spin. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): quiesce the emulation thread before RetroAchievements shuts down (#967) App::shutdown destroyed the rc_client while a free-running emulation thread could still be inside rc_client_do_frame or a posted emuIdle: it now unloads and stops the thread first, then ach.shutdown() (still before video.shutdown(), images.stop() needs the GL context). AchHttp::ensureWorker runs inside request()'s locked block, and shutdown() moves the worker out under the lock and joins outside it, so request() is safe from the UI and the emulation thread (new two-thread case in ach_http_test). onContentLoaded and onReset resync framesSeen_ so frames that ran before the load are not counted as the new game's. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): a call() that takes over 250 ms shows a busy spinner (#967) EmuThread::call() runs a wait hook on the waiting thread from 250 ms on; the app's hook pumps OS events and swaps a spinner drawn with scissored clears -- no ImGui frame, no call(), no read of a field only a call() writes (CoreHost counts those while the UI waits). Headless, it logs once per call. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): a failed emulation-thread start lifts the feature limits; vsync off naps to the next refresh (#967) Phase 1 final-review minors 2 and 3. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(app): emulation thread phase 2 -- achievements, netplay, the busy spinner (#967) Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): drop the previous game's achievement trackers and indicators after a content switch (#967) Events carry the generation they were queued in; onContentLoaded and onContentClosed bump it, and drainEvents drops the stale tracker, challenge, progress, attempt started/failed and reset events (no HIDE would ever come for them). A real unlock, completions, scoreboards and the connection state are kept. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): drive rc_client's mutating calls from the emulation thread only (#967) App::reset runs ach.onReset() inside the emulation-thread call (rc_client_reset raises events); applyHardcore, setEnabled(false), logout and the pause check (rc_client_can_pause advances the pause-decay state) go through coreCall, so none of them runs while rc_client_do_frame does. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): the vsync-off present loop runs on a display-period deadline grid and handles input while it naps (#967) The nap was anchored at the swap end, so the period was refresh + build + swap (~54-57 Hz on a 60 Hz display) and no input was published during it. LateLatch::advanceGridNs / gridNapNs keep a deadline grid; App::presentNap waits for events as latchWait does and hands them to the emulation thread at once. (SDL 3.4 has no SDL_WaitEventTimeoutNS.) Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): the emulation-thread setting hint names only what is still switched off (#967) RetroAchievements and netplay work with the thread on; the Tools (debugger) menu and the GDB stub do not. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): pin that a reload drops the previous game's queued core log lines (#967) The block had no onUiEvent, so it only proved option survival. It now queues a log line inside the same call as the reload, and asserts it is not delivered while the option write still is. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): final-review minors -- ci report step, inline pin, display mode, RAII guards, stale docs (#967) ci: the timer-report step is informational (continue-on-error). CoreHost::pinSnapshot applies queued option events when the UI thread is the owner (thread stopped / inline). The display period is re-read on SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED. busyFrame's UI-waiting flag and call()'s wait-hook flag are RAII. netplay.h and cheats.h comments, docs/agent/testing.md (TSan recipe) and the ach_event_unit label are brought up to date. The off-thread read_memory answer notes the rcheevos v12.5 assumption. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): serialize achievement replies and synchronize scope capture (#967) Run HTTP and mock rc_client replies on the core owner; guard scope visibility and its sample ring. Add fail-first ownership/concurrency regressions to the sanitizer suite and prevent Windows GL-loader macros from breaking the busy spinner. * fix(app): synchronize achievement UI client access (#967) --------- Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
df0f6d271b |
feat(app): emulation on its own thread, phase 1 (#967) (#1000)
* feat(app): FrameExchange triple buffer for the emulation thread (#967) Frame moves to frame.h so CoreHost, FrameExchange and VideoOut share it. Content epochs keep a previous game's frame from ever being shown. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): EmuCommands, the emulation thread's ordered command queue (#967) Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): PacingClock mode A, the audio clock as master with an even frame grid (#967) A wall-clock grid at the core's period, trimmed at most 0.5% by the smoothed SDL queue level: true Jaguar rate on the sound card's crystal without the 10.7 ms device-pull jitter a raw queue trigger would add. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): PacingClock refill rebases the grid so it converges (#967) During refill frames start at up to 2x the core rate but the grid still advanced a full period per frame, leaving it ahead of the clock by the extra audio just queued. The loop then slept that long, drained the queue below the low-water mark and re-armed refill: bursts and ~66 ms stalls forever, with underruns. Rebase the grid on each early start while refilling, and make testRefillFromEmpty check evenness, rate and underruns after the refill. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): EmuThread, the single owner thread of the core (#967) post() at frame boundaries in order, call() that runs inline on the thread itself and after a stop, callFor() and a join timeout for a core stuck in retro_run, advance() for headless and tests, inline mode for everything else. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): InputSnapshot, the newest input for the emulation thread's poll (#967) States are newest-wins; pointer motion is summed until a frame takes it and is delivered exactly once. InputManager::poll can run on the UI thread with the keypad overlay ticking per emulated frame. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): EmuThread: pending call returns on abandon, exact advance, rate-based pacing test (#967) call() polls for abandonment instead of blocking forever on a queued task; advance(n) counts only budgeted frames so a frame already in flight cannot satisfy it; the pacing test checks rates over measured windows. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): AudioOut safe across the emulation and UI threads; mode-A update (#967) update(false) counts underruns without nudging the playback ratio: in mode A the pacer follows the audio clock and the two controllers must not fight. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): CoreHost owner thread, EmuSnapshot and queued writes (#967) Inline nothing changes. With an owner, the UI reads options, AV info and discs from a pinned EmuSnapshot, option values through a UI-side mirror (the config is still written on the UI thread), and its writes reach the core at the next frame boundary. Any other core call off the owner is refused, counted and logged once. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): the real core inline vs on the emulation thread, frame for frame (#967) Also pins a load while frames run (no stale frame, empty buffer inside the load call, rising serials) and the owner rule. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): CoreHost publishes the snapshot before a load call returns (#967) A load or unload made through EmuThread::call() returned before the batch ended, so the UI could pin the previous option table, AV info or disc list. finishLoad, stopSession, a failed startSession and the disc eject / index commands now publish themselves. With no post set while threaded, queued writers are counted and dropped instead of recursing into themselves. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): Settings > Interface > Emulation thread and --emu-thread (#967) Off by default in every build for now (nightlies default it on in phase 3, once nothing is switched off while threaded). Decided once per run; headless runs stay inline unless --emu-thread on. Nothing starts a thread yet. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): VJ_PERF_CSV input-latency and underrun columns, --perf-input (#967) Latency is the oldest input event's SDL timestamp to the core's poll; the first three columns are unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): content and state actions through EmuThread::call; headless --emu-thread on (#967) Load, boot BIOS, unload, reset, save and load state, SRAM flushes and the shutdown unload run where the core lives; inline they run in place as before. The save-state thumbnail is the frame the state was taken on. UI checks ask "halted?" instead of CoreHost::inFrame(). The headless smoke tests pass with --emu-thread on with the inline hashes. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): switch off what is not thread-safe yet while the emulation thread is on (#967) RetroAchievements, netplay, the Tools windows and the GDB stub option are off for a threaded run (phases 2 and 3 bring them over). Cheat changes from the Cheats window reach the core as one command, never half a list. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): windowed emulation-thread check for the Mac Studio (#967) Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): the interactive loop with the emulation on its own thread (#967) The UI thread polls and publishes input, takes the newest frame, drains messages and counters and presents; it never waits for the emulation. The 5 s SRAM flush is posted. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): VJ_PERF_CSV event-to-present column (#967) The swap that first shows a frame which polled an input event, timed from the event: inline from the iteration's oldest polled event, threaded through the InputSnapshot publish number each frame carries. --perf-input now injects its key from an SDL timer at a swept delay, so events land evenly over the display period in both loops. Co-Authored-By: Claude Opus 5.5 <[email protected]> * feat(app): late latch for the threaded loop (#967) With the emulation thread and vsync on, the UI thread waits until just before the next vblank (the slowest recent UI build plus a pad that grows on a missed vblank), publishing input as each event arrives, then takes the newest frame. It tracks the vblank grid from swaps that block and stays off while it cannot. The fake-clock model: event -> present 25.3 ms for 5 ms core frames (inline 25.0, unlatched 46.4); 34.2 ms for 14 ms frames, which only mode B can fix. --late-latch off for A/B measurement. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): late latch backs off on persistent misses, hysteresis (#967) The latch now releases (with a 300-swap cool-down) when more than a quarter of its last 60 waits missed the vblank they aimed at, so a build whose GPU work lands late cannot keep every frame a vblank behind. Once locked it lets go on jitter only at 2 ms (locks below 1 ms), and a wait that itself overran its wake time no longer counts as a miss. The locked/released log line and the perf note show misses, releases and the pad. Co-Authored-By: Claude Opus 5.5 <[email protected]> * ci(app): ThreadSanitizer job over the emulation-thread tests (#967) VJ_APP_TSAN instruments the app, the core, SDL and the tests; the job runs ctest -L emu_thread and fails on any race report. tsan_canary proves the sanitizer is really on. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(app): the Emulation thread setting (#967) Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): route UI disc commands through call() so staged_ is never written off the UI wait (#967) A disc insert can re-stage the CD BIOS in the core (stage_cd_bios via open_disc_and_resolve_boot), which writes CoreHost::staged_ from the VJ_EVT_BIOS_STAGED/REJECTED event. Posted from the UI, that write ran on the emulation thread while the BIOS panel read stagedBios() every frame. The File menu tray/disc items and desktop.cpp toggleDiscTray/selectDisc now run inside EmuThread::call (in place when inline), like reset. core_host.h's threading comment and the plan's boundary table say so. Latent today: the app never registers disc images with the core. Co-Authored-By: Claude Opus 5.5 <[email protected]> * docs(app): thread-test fail patterns, README frame-skip wording, two comments (#967) - README: the drift alone skips a frame about every 18 s, but frames finishing near the take point also repeat/skip (~1-2.5/s measured at 60 Hz); the display-locked mode planned later addresses it, hence off by default. - emu_thread_flag_rejects, emu_thread_limits, netplay_refused_threaded and gdb_locked_threaded only had a pass regex (ctest then ignores the exit code): they now also fail on "ThreadSanitizer|owner violation". - late_latch.h: kLockAfter counts tracked observations; single outliers do not reset it. emu_thread.h: a call queued before an abandon may still run later on the detached thread, so captures must be by value. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): emu-thread tests on CI runners: Windows timer, MSVC capture, libdbus TSan (#967) - emu_thread_unit raises the Windows timer resolution to 1 ms (timeBeginPeriod, winmm), as SDL_Init does in the app; at the default ~15.6 ms, fast-forward could not beat normal speed (63 vs 97 frames/s on the Windows runners). - Its "keeps running" check is a deadline poll instead of a fixed-window quarter-rate floor, which a starved macOS runner missed (6 of 36 frames). - emu_commands_test: kEach is static constexpr, so MSVC does not need it captured (C3493). - tsan.supp: suppress libdbus's own lock-order-inversion between SDL_Init and SDL_Quit on the main thread (system library, not instrumented); it was the only report in the Linux TSan job, on every threaded app run. Co-Authored-By: Claude Opus 5.5 <[email protected]> * fix(app): precise emulation-thread waits on MinGW (#967) MinGW's timed condition wait (winpthreads) rounds short timeouts up to the scheduler tick (~10-16 ms) even with the 1 ms timer resolution SDL sets, so threaded fast-forward ran below normal speed on the MinGW Windows runner (67.5 vs 98.2 frames/s; MSVC was fine) -- and the shipped Windows packages are MinGW builds. EmuCommands::waitUntil now waits on the condition only while more than 20 ms remain, then polls in 1 ms sleeps until the deadline or a post()/notify(). Other platforms keep the plain condition wait. Co-Authored-By: Claude Opus 5.5 <[email protected]> * test(app): emu_thread_unit reports the host's timer precision for the fast-forward check (#967) The MinGW runner still showed fast-forward at one frame per ~15.6 ms (64 vs 98 frames/s) after the polling wait, which points at the platform's timers rather than EmuThread. The test now measures sleep_for(1 ms) and the steady_clock step and prints them with the rates. The fast-forward ratio is still asserted whenever it holds or the host can pace 2.5 ms frames; it is reported instead only when it misses on a host whose sleeps exceed 6 ms or whose clock steps exceed 1 ms (or that cannot even sustain normal speed). Co-Authored-By: Claude Opus 5.5 <[email protected]> --------- Co-authored-by: Claude Opus 5.5 <[email protected]> |
||
|
|
cfbf66fcb7 |
feat(app): per-game cheat browser fed by the libretro cheat database
Cheats window (Emulation > Cheats...): the .cht set for the running game is matched by name against the core's own title identity (or the file name), its cheats toggle on and off, manual hex codes can be added, and the choices persist per game under <data>/cheats/games/<key>.ini, keyed by the cartridge CRC32. Everything is re-sent to the core after each load and state restore (the core drops its list on every unload). The Jaguar database is RetroArch memory-style (decimal address/value, width in memory_search_size): converted to the core's hex ADDR:VALUE codes with the value little-endian in memory, ~10% already carry a code and pass through unchanged, anything else is shown unsupported with the reason. cheatN_enable is ignored so nothing is on by itself. The data is not vendored (CC BY-SA 4.0): -DVJ_APP_FETCH_CHEATS=ON downloads the 41 Jaguar files at configure time, pinned by commit and per-file SHA-256; a folder of .cht files can also be set in the window or with --cheats-dir. Refs #835 Co-Authored-By: Claude Opus 5.5 <[email protected]> |