The site's one-liners pointed at develop, so any installer change went live
to users the moment it merged, before a release. Point every installer URL at
master: the site's RAW_URL and source links, the README, the scripts' own usage
text, the nightly notes, and install.sh's RAW, which --auto-update re-fetches.
Co-authored-by: Claude Opus 5.5 <[email protected]>
* 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]>
* docs(cheats): community cheat store design and plan (#1071)
Refs #1071
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(cheats): map printf_notify to app toasts
Refs #1071
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(cheat-import): BigPEmu API map; int_full settings omitted; printf_notify ignored
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cheat-import): refuse side effects in message args; scope Unsupported.call to API gaps
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(cheat-import): committed cartridge identity table (hashes only)
scan_cartridges.py walks a ROM directory (read-only) and writes a sorted
FNV1a64 -> CRC32 table with size, title and name; no ROM bytes or paths.
load_cartridges rejects duplicate fnv/crc, bad hex and format != 1. The
generated cartridges.json (152 rows) lets CI map community script hashes
to the CRC32 the app binds packs to without any ROMs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(cheat-import): community cheat pack generator (#1071)
build_packs.py converts every pinned community C cheat source with convert.py
and writes the site/cheats tree: per-cartridge .cht packs (one per CRC32, sources
merged, identical cheats deduped, same-name cheats suffixed), manifest.json,
the unsupported.json worklist with per-call API usage, index.html and README.
Deterministic, bounded fetch of one codeload tarball per source, --source-dir for
local checkouts, --summary and --bump for the bump workflow. sources.json pins
jguffer/BigPEmu_Scripts at 2421150.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* data(cheats): generate community packs from jguffer/BigPEmu_Scripts@2421150
23 cartridge packs (72 cheats, 9 partial) plus the unsupported.json worklist
(55 entries), manifest and index, generated with build_packs.py from the pinned
codeload tarball; byte-identical to a local checkout run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cheat-import): worklist unmatched literals beside a hit; bound fetch errors and member count
- identity row for table-missing literals in a file that also has a hit
- http.client.HTTPException during fetch is a FetchError (exit 2)
- tar expansion cap counts a header block per member and caps member count
- Collector uses the api_map given to build()
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* data(cheats): regenerate for generator fix (unmatched-literal identity rows)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(site): publish community cheat packs under /cheats/
build_site.py copies the committed site/cheats tree verbatim; check_site.py
gains check_cheats (manifest format/sha256/bytes/CRC guard, unlisted .cht,
unsupported.json, index.html links, 256 KiB manifest and 1 MiB tree limits)
with unit tests. pages.yml watches site/cheats and tools/cheat-import. The
converter README now documents the author's permission, the credit, the
sources -> cartridges -> packs workflow and how to add a source; the
community_test harness and its fixtures are removed (the committed packs and
the regeneration diff are the conversion contract).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(cheats): regen-diff job, concurrency, weekly pin-bump PR
cheat-import.yml: add site/cheats/** to the path filters, a per-ref
concurrency group, drop the community_test.py fetch step (the files are
gone), and add a packs job that regenerates twice, diffs the runs and the
committed tree, flags unmapped BigPEmu calls, then runs the site-check tests
and build_site + check_site.
cheat-packs-bump.yml: weekly (Mon 06:23 UTC) poll of each pinned github
source; on movement, bump the pin, regenerate, validate, force-push the fixed
branch cheats/pin-bump and open or refresh a develop PR (validation failures
head the body instead of being dropped), then dispatch cheat-import.yml on the
branch because GITHUB_TOKEN PRs do not fire pull_request workflows.
test_committed_sources no longer pins the commit/date value: every bump PR
would otherwise fail the unit tests; only the shape is asserted.
Refs #1071
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(cheats): gate bump workflow to the upstream repo, drop bot trailer
Only libretro/virtualjaguar-libretro opens bump PRs (the Provenance-Emu
fork runs crons too), with a 30 minute job timeout. The bot-authored bump
commit no longer carries a Claude co-author trailer.
Refs #1071
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(cheats): pin pycparser, force LF, add job timeouts
Pin pycparser==3.11 (newest release that installs on the Python 3.10 CI leg;
2.22, 2.23, 3.0, 3.11 and 3.12 all regenerate byte-identical packs). Add a
root .gitattributes forcing LF for the generator and generated tree so pack
bytes cannot drift by checkout. Give both jobs a 20 minute timeout.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(cheats): dedupe pack header title, refuse control-character paths
The pack's first line printed the title twice when the cartridge name began
with the title. Print the name alone in that case. Fetch and local-dir reads
now fail closed on any path containing a control character, since names can
reach ::warning:: annotations and PR bodies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* data(cheats): regenerate packs with deduplicated header title
Line 1 of each pack no longer repeats the title when the cartridge name
already starts with it. Regenerated from the pinned source commit; two runs
and a --source-dir run are byte-identical.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* ci(cheats): even indentation on a bump-workflow continuation line
editorconfig-checker wants multiples of 2 in workflow files.
Refs #1071
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
- Agent: a dropped or failed link clears the queue and any hold_buttons (a held
button no longer outlives the server); stop() clears stale events.
- WsClient: the name lookup runs on an abandoned-on-stop helper thread, so stop()
never waits on getaddrinfo; reject masked server frames and reserved bits;
1 MiB message cap; byte caps on the event and send queues; jittered backoff;
a "://" in the URL path is not a scheme.
- RetroAchievements hardcore blocks Neuro (disconnect + release, Settings notice,
toast); connects again when hardcore ends.
- Toasts on connect/disconnect and an "AI plays P1" chip on the background draw
list; the server is told (and actions fail) while the in-game menu has the pads
or P1 is a mouse.
- Settings > Neuro: URL field follows the config and applies on click-away; warns
on a non-loopback ws:// server; settings are re-read 4x a second, not per frame.
- JSON: a lone high surrogate no longer swallows the pair behind it.
- privacy page row, docs/neuro.md (hardcore, indicators, emulation-thread note).
- tests: wrong Accept, oversize/masked/reserved frames, flood cap, hold released on
disconnect, hardcore no-connect/release/reconnect, input gating, surrogates, URLs.
Co-authored-by: Claude Sonnet 5.5 <[email protected]>
build_site.py now writes "thumbnails/Atari - Jaguar CD.zip" next to the
mirror. It holds only Named_*/*.png under an "Atari - Jaguar CD/" folder,
so it unzips straight into RetroArch's thumbnails directory and can be
offered to libretro-thumbnails#141 as-is. Entries are sorted with a fixed
timestamp, so the same images always give the same bytes.
The core info now lists "Atari - Jaguar CD" alongside "Atari - Jaguar",
so once libretro-super builds that rdb, RetroArch offers this core for
discs a scan matches. The mirror README records what still has to land
upstream: the thumbnail repo, the rdb build line, and a RetroArch
scanner fix (every Jaguar CD track is AUDIO, and the scanner only hashes
data tracks).
Co-authored-by: Claude Opus 5.5 <[email protected]>
The compatibility page printed "Not yet verified: BIOS_REJECT" for the
real boot ROM's red reject screen, "Not yet verified: ? (trap_loop)" for
titles whose 68K crashed at start (#975), and "Boots and runs" in the
real-BIOS column for RAM-loaded programs that never touch the boot ROM.
classify_cart_stage now maps:
- BIOS_REJECT -> plain "Rejected by the boot ROM" (authentic, #852/#744)
- ? (trap_loop) -> bad "Known issue: crashes at start"
- ? (bios_trap) -> bad "Known issue: crashes after the boot ROM"
- GAME_CODE in the BIOS column with no handoff in its notes
(cart_classify.sh bios_ran=0) -> "Boot ROM not used; runs"
- GAME_CODE + black video -> "Runs; no picture in the test"
The legend now explains these labels instead of quoting a stale count
("14 rows ... v3.8.0").
On the current matrix this is 202 "Boots and runs", 81 "Boot ROM not
used" (all homebrew/BJL/Alpine images), 14 "crashes at start", 9
"Rejected by the boot ROM" and 4 "no picture". Splitting rejects into
bad dump / unverifiable / unsigned, and merging .jag/.rom twins, needs
new probe fields (crc32, header byte 0, filedb flags) and stays on #976.
Refs #976
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since #955 and #953, librashader is built from source in CI, so the macOS
app (both slices), the AppImage, the .deb and the Flatpak all ship it,
and Windows already did. The app page still told Linux users to install
librashader themselves and warned that Intel Macs had no shaders.
app/packaging/README.md had the same stale "Known gap" bullet. The
rewritten Universal bullet had been appended to the end of the OpenGL
bullet instead of replacing it, so it is now moved back into place.
Refs #950
Co-authored-by: Claude Opus 5.5 <[email protected]>
Since #955 and #953, librashader is built from source in CI, so the macOS
app (both slices), the AppImage, the .deb and the Flatpak all ship it,
and Windows already did. The app page still told Linux users to install
librashader themselves and warned that Intel Macs had no shaders.
app/packaging/README.md had the same stale "Known gap" bullet. The
rewritten Universal bullet had been appended to the end of the OpenGL
bullet instead of replacing it, so it is now moved back into place.
Refs #950
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* feat(site): per-page share cards and a favicon set from the app icon
Every page shared one og:image, the Cybermorph true-color comparison,
which reads as a small low-contrast strip in a link preview; the site had
no favicon at all.
- scripts/make_share_cards.py (Pillow + rsvg-convert) composes two
1200x630 cards: home.png, eight game captures from site/assets/home/
over the icon and wordmark, and app.png, zoomed crops of the desktop app
captures (library, Rayman under the Lottes CRT shader, palette debugger,
light theme). It refuses to write a card that is not exactly 1200x630 or
is 1 MB or more. It also renders favicon.svg/.ico, icon-192/512.png and
an opaque full-bleed apple-touch-icon from
app/packaging/icons/virtualjaguar.svg. Outputs are committed; the
stdlib-only site build only copies them.
- build_site.py: SHARE_DEFAULT plus a per-page SHARE_IMAGES map replace
the global SHARE_IMAGE; each page gets its own og:image, twitter:image,
alt, type, width and height (read from the PNG), and the home JSON-LD
image follows. Every page head links the favicon set; favicon.ico is
also copied to the output root.
- check_site.py: twitter:image must equal og:image, the width/height tags
must match the PNG, the card must be under 1 MB, and every page must
carry rel=icon and rel=apple-touch-icon links.
Closes#984
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(site): opaque touch icon without the outline ring; list the new checks in check_site's docstring
Refs #984
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
site/pages/app.html: took develop's generated {{APP_DOWNLOADS}} picker (#970) over the cut's hand-edited 3.9.1 filename table; the picker reads file names from the GitHub releases.
Replaces the 2x2 hero mosaic with a sideways-scrolling strip of gameplay
frames (six visible on desktop, three on tablets, two on phones; scroll
snap, beveled scrollbar, Prev/Next buttons from gallery.js). Every card
opens in the gallery lightbox with the same red hover as the app page.
Twelve new captures from core 5d2ea0b8 at 2x internal resolution, true
colour and the Accurate blitter: Doom, Wolfenstein 3D, Missile Command 3D,
Atari Karts, Iron Soldier, Cybermorph, Super Burnout and Checkered Flag
from the headless harness with scripted input, and Tempest 2000, Raiden,
Defender 2000 and Ultra Vortek through crt-lottes, crt-aperture,
crt-easymode and crt-royale in the desktop app (slang-shaders e1d75632).
The four mosaic frames (Alien vs Predator, Iron Soldier, Skyhammer, Hover
Strike) stay as cards. WebP: 480px thumbnails, 960px full size.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The app page's Get it window is now the Download page's widget, limited to
the app: Nightly / Latest / older releases that ship it, platform and arch
icons, file sizes, SHA256SUMS link, and the one-line installer preset to
--what app (download.js composes --what and --channel, and finds its root by
the .dl class so it runs on both pages). The hard-coded 3.9.0 file table is
gone; the per-package notes move to an About the packages window.
Captured frames (figure.shot) on the enhancements and app pages open in the
gallery lightbox and take the same red hover outline as the app gallery
cards. gallery.js reads a shot's window title and figcaption for the
lightbox caption.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(site): generate the release log from docs/RELEASE_NOTES_v*.md
The home-page release log was hand-written and stopped at v3.7.0. It is now
the {{RELEASE_LOG}} placeholder: build_site.py reads every v3+ notes file
(summary = the file's opening tagline paragraph), shows the newest five rows
and folds the rest, and pages.yml rebuilds when a notes file changes. New
releases appear with no page edit. Also refreshes the home page for v3.9.0
(app feature list, savestate v17, RetroAchievements, debug tools, GameDrive
SD card) and drops the stale 'until the first release with the app exists'
installer sentence.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(site): bring compatibility, enhancements, performance and why-this-core up to v3.9.0
Compatibility: current as of v3.9.0; drop known problems that v3.7.1 and
v3.8.0 fixed or closed (Club Drive, #800 carts, Music Demo BIOS, Hover Strike
real-BIOS run-ahead, #737, #633); add the v3.7.1 and v3.8.0 fixes (older
ones folded); add JaGol, Ruiner Pinball and Doom link-play notes; replace the
'matrices predate v3.7.0' caveats with the current ones (14 BIOS_REJECT rows).
Enhancements: compatibility-class presets and load notice (v3.8.0), clock
percentages and idle-skip at any RISC scale (v3.9.0), DSP runaways in the
watchdog. Performance: GPU/DSP Pipeline Timing name. Why this core: the
desktop app, app RetroAchievements modes, GameDrive SD card.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(site): hide release-log rows newer than the latest release; trim home page duplication
Release notes land before the tag exists, so with release data in hand the
generated log drops versions above releases/latest (offline builds show all).
The home page keeps the app feature list in one place and shortens the
installer paragraph, so it is no taller than before despite three new rows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* docs(site): refresh the app gallery: RetroAchievements art, Jaguar CD shots, gdb recipe, tighter layout
RetroAchievements: the grey badge squares came from the mock session itself,
not from a rendering bug. A --ra-mock session never uses the network
(updateImageNetwork() forces it off) and the image cache only reads
<data-dir>/ra-cache/, so a fresh capture data dir had nothing to draw. The shots
are retaken with that cache seeded from the public media.retroachievements.org
images of a real Jaguar set (Ultra Vortek, 18 achievements, in the demo
library), and a mock fixture dir holding the real public achievement list and
Jaguar / Jaguar CD game lists. Account, progress and unlock dates are invented;
no real account is involved.
Jaguar CD: discs boot and run in the current app, and box art now downloads
for the CD tab, so app_library_cd.png is retaken and a Blue Lightning CD
gameplay card is added.
Galleries now hold an even number of cards (Playing 8, Look & feel 8,
RetroAchievements & Discord 4 after dropping the empty-looking Profile card,
Developer tools 8) and the grid is fixed at 4 columns (2 below 60rem, 1 below
34rem) in a marked "/* app page */" block at the end of style.css, with
tighter gaps and captions and two-column bullet intros. Developer tools gains
the gdb recipe and a link to docs/gdb-stub-guide.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(site): scope the app-page h3 spacing, fix CD art and BIOS wording, README alt text
The h3 margin override was site-wide; it is now limited to #gallery. The Jaguar
CD box art comes from the project's thumbnail mirror (libretro-thumbnails has
no Jaguar CD repository), and a CD boots without any BIOS file, so the captions
say that instead of naming libretro-thumbnails and the built-in CD BIOS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
* docs(site): redesign the Download page around a release picker
One Downloads window instead of five sections. A Nightly / Latest / Older
releases picker (buttons plus a select, progressive enhancement) switches
between per-release panels rendered by build_site.py; without JS the latest
release shows with links to the GitHub release list. App and core builds are
grouped by platform then arch with generic inline-SVG glyphs; the core's long
tail is collapsed per platform. The installer one-liners become a compact tabbed
widget that adds --channel for the chosen release, and tools get a proper table.
fetch_releases also fetches the last 10 tagged releases; --releases accepts an
optional older[] list. Adds scripts/check_downloads.py, an offline fixture under
scripts/testdata and a CI step that builds from it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* docs(site): end the releases.json fixture with a newline (EditorConfig)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
New captures of the v3.9.x app (demo library, fresh temp data dir, no
accounts): Light and two custom themes over the same library view, the theme
editor, Settings > Interface and the font picker (installed fonts drawn in
their own typeface, favorites, recents, debugger font samples), the
RetroAchievements sign-in, This game, Jaguar games and Profile views from the
built-in mock backend, the Settings > Discord tab with Discord off, and the
palette, blitter log, texture browser and audio scope tools. Video settings
is retaken now that theme and font moved to the Interface tab.
The font shots come from the feat/app-font-picker branch (#943 was not merged
yet); the README row and app page get matching cards and captions.
Closes#938
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The token moved to Keychain / Credential Manager / libsecret in #923 with a
config.ini fallback; the page still described plain-text storage. Discord and
the app shipped as 3.9.0, not 3.8.1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* site: Download page with direct links to every release file
A new Download page, second in the nav, lists the desktop app packages,
the RetroArch core for every platform, the tools and checksums, and the
nightly, each as a direct link to the asset. The home page's Download button
and the app page's "Get it" point at it; the home page no longer hardcodes
"latest: v3.7.0".
build_site.py takes the release data as --releases FILE or fetches it with
--releases-from-github (stdlib urllib, GH_TOKEN/GITHUB_TOKEN sent when set),
and dies on a failed fetch so a publisher keeps its previous deployment.
With neither flag the page links the release pages instead, so local builds
and check_site.py keep working offline. pages.yml uses the fetch flag and
gains a 6-hourly schedule, since a new release changes the app package names.
The v3.8.0 release carries no app packages, so until the next tag the app
section links the nightly's, with a note saying so.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
* site: signing text matches CI: macOS notarized, Windows installer not signed yet
The Download page and the app page say the macOS app is Developer ID-signed
and notarized (the release workflow's notarize step now runs and passes),
with no right-click > Open caveat. The README and app page from #929 said
the Windows installer is signed as well; the SignPath steps are still
skipped in every release run, so both now say it is not code-signed yet and
SmartScreen may warn. The "SIGNING: confirm" markers are resolved and gone.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
---------
Co-authored-by: Claude Fable 5.1 <[email protected]>
Replace the long feature list with a responsive grid of screenshot cards
grouped by Library and artwork, Playing, Look and feel, RetroAchievements
and Discord, and Developer tools, each with a one-line feature summary.
Click-to-enlarge uses a small dialog-based lightbox in assets/js/gallery.js
(no library; Esc closes, arrow keys move, focus returns to the card).
Fresh captures of the v3.9.0 app (demo library, no personal data): library
tabs, resume prompt, state manager, controls overlay, input, cheats, video
settings and four debug tools including the CPU debugger halted. Adds
Windows ARM64 and Flatpak downloads, system requirements, Discord Rich
Presence and privacy/terms links; the signing text carries a SIGNING:
confirm marker.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The release grew from a fix release into the desktop app debut, debug tools and timing
work, so it ships as v3.9.0. Version bumped in all three places; release notes renamed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Regenerate the plain / CRT pair from one saved game moment and one window
config (aspect fit, no integer scaling, no stretch; only shader.* differs),
and use the bundled crt-gdv-mini-ultra-trinitron preset instead of CRT-Royale's
default: visible vertical aperture-grille stripes, mild bloom, not too dark.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The token used to sit in plain text in config.ini. It now goes to the macOS
Keychain, the Windows Credential Manager or the Linux Secret Service (libsecret,
loaded with dlopen so there is no build or run dependency). A token already in
config.ini is written to the store, read back, then removed from the file
(atomic rewrite) at start-up in the first copy of the app; Sign out erases it
from both. Secondary copies (#902) only read the store and write it on an
explicit sign-in/out. Where no store answers the token stays in config.ini as
before, with a log note and a Settings hint. --ra-mock and headless runs use an
in-memory backend and never reach the real store.
Refs #836
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Plain-language Privacy Policy and Terms of Service for the Virtual Jaguar
app and core, with footer links. The pages are built and in the sitemap
but stay out of the primary nav (LEGAL_PAGES in build_site.py).
Refs #833
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Settings > Reset treats the two new Desktop settings as Storage keys, and the tray follows a reset.
Refs #833
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Star a game on its tile (or right-click > Add to Favorites); the games that boot
are kept under Recently played (last 20). Each list shows as its own tab, as a
strip at the top of the Cartridges and Jaguar CD tabs, both, or hidden. A new
Save states tab lists every game with states and the screenshot saved with each;
clicking one boots the game and loads it (or just loads it into the same game
already running). The tile context menu gains Play, Load state, favorites,
Remove from Recently Played, Show in Finder / Explorer and Copy path.
Favorites and recents live in library/user.tsv, written as atomic operations
(re-read, apply one change, rename) so several running copies cannot clobber
each other. App state file names now come from one helper shared with the tab.
Refs #838
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Mirrors the libretro-thumbnails URL layout (<System>/<Type>/<Name>.png) so the
desktop app can use the same URL builder with a different base. libretro-thumbnails
has no Jaguar CD repository yet (libretro-thumbnails#141). build_site.py copies
site/thumbnails verbatim.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
File > New window (Ctrl/Cmd+N) starts another copy: `open -n` on a macOS bundle,
the executable elsewhere ($APPIMAGE on AppImage). Copies take a number from an OS
file lock in the data folder (instance-N.lock); the first behaves exactly as before.
Instance 2+ never rewrites the shared config.ini (own config-N.ini holds only window
size and controller choice), keeps game saves in saves/instance-N/ (first run reads
the shared save), uses imgui-N.ini, and hands the netplay address to the core through
VJ_NETLINK_HOST instead of the one shared vj_netlink.txt. Atomic writes now use a
per-process temp name. Settings > Input pins a window to one controller (or keyboard
only) and keeps reading it while another window has focus.
Refs #833
Refs #888
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The library skipped .chd and .cdi ("nothing cheap to hash") and name-matched
only on an exact file stem, so most disc rips showed no title.
- .chd: CRC32 of track 1 rebuilt the way the core reads it (2352 of each
2448-byte frame, audio put back in .bin byte order, virtual pregap left
out, tracks padded to 4 frames); CHDs the core refuses (several audio
tracks, no CHSE) are not hashed.
- .cdi: best-effort track 1 CRC from the descriptor layout.
- A disc whose checksum is not in the dat falls back to a normalized name
match of the file name, then the folder name (case, punctuation, region and
Track/Disc tags ignored; exact title first, then a unique longer title,
never a guess between two games).
- Tiles say how a disc was identified in the tooltip.
- Scan cache v2: old caches re-hash only disc images that had no checksum.
Verified against synthetic uncompressed CHD/CDI images; real Redump CHDs are
still to be checked.
Refs #838
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
libretro-thumbnails has no Atari - Jaguar CD repository, so no disc could ever
get art. Tiles with no art now draw a card with the game title and a CD / Cart
badge, and cover art can be supplied locally:
- right-click a tile > Set cover image... (PNG) / Remove cover image
- drop a PNG on a tile while the library is showing
- <data>/covers/<Title>.png (dat name, then the shown name), picked up on Rescan
- precedence: tile cover > covers folder > downloaded > card
Unidentified discs get a cleaned title (trailing "(Track N)" and doubled
extensions dropped; "(Disc N)" kept) and now have art slots keyed by dat name,
checksum or path; only identified titles ever reach the network. The Jaguar CD
tab says why discs have no art.
Refs #838
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>