172 Commits
Author SHA1 Message Date
MihawkandClaude ed8e74142b Evidence: a WebUI rename and a delete on the Base PS5 (issues 55, 57)
evidence/webui-library-edits/check.py, run against build 191ff52c with a test
ROM it makes itself (a looping NES program): Add Game 201; a rename becomes
the game's RetroArch playlist label; Delete game takes the file, the playlist
entry and the details, and the game leaves the library. The console has no
RetroAchievements account, so issue 58's field is covered by the host test
only. The temporary playlist and the test files were removed afterwards.

Evidence: python3 tools/evidence.py compare evidence/: 146 replayed, 0 failed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 22:00:17 -04:00
MihawkandClaude eda7cb17ee Dolphin: texture packs get a 512 MiB budget on the PS5
Dolphin sizes its custom asset cache from MemPhysical(), whose FreeBSD branch
gives the PS5 no usable size, so the budget was 0 and every texture of a pack
was unloaded as soon as it loaded (issue 5: "Not enough system memory for
custom resources"). On __PROSPERO__ the cache now gets a fixed 512 MiB, a
share of the title's own memory (about 870 MB flexible memory free in play).
CustomAssetCache is MemPhysical's only user.

Evidence (python3 tools/evidence.py compare evidence/: 146 replayed, 0 failed):
- evidence/dolphin-texture-budget-before: build a2c7800a: the error at start.
- evidence/dolphin-texture-budget: build 191ff52c, Load Custom Textures on and
  the owner's pack in place, tests/pad/dolphin-texture-pack.txt: no error, no
  asset unloaded, the game to the script's STOP at about 60 frames a second.
Whether the pack's pictures show is the owner's check on the screen; Dolphin
does not log it at this level.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 22:00:17 -04:00
MihawkandClaude 48607e12da PPSSPP: a resolution change from the Quick Menu no longer restarts video and crashes
PPSSPP reported its current internal resolution as its maximum geometry, so
any increase made RetroArch restart the video driver (runloop.c compares
max_width/max_height). RetroArch keeps the cached Vulkan device across that
restart and calls neither context_destroy nor context_reset, so PPSSPP's render
thread kept the freed vk_t's interface and called a null function (issue 63:
signal 11, rip=0, [rsp] inside the core). The maximum is now always 10x
(4800x2720, the largest option and the PS5 default profile), so a change is a
geometry update; with hardware rendering RetroArch creates no textures from
the maximum, and default users already had it.

Evidence (python3 tools/evidence.py compare evidence/: 146 replayed, 0 failed):
- evidence/ppsspp-resolution-crash-before: build a2c7800a, tests/pad/
  ppsspp-resolution-change.txt (2880x1632 to 3840x2176 at 30 s): the second
  video_vulkan init, then the reported crash, [rsp]=0x000000040084aabb.
- evidence/ppsspp-resolution-change: build 191ff52c, the same script:
  SET_SYSTEM_AV_INFO 3840x2160 and NotifyDisplayResized 3840x2160 with one
  video_vulkan init; the 45 s screenshot and the 55 s STOP, no crash.
Gates: tools/verify.sh, all five (format, unit 215, build, integration 215,
evidence 141), before the evidence was added.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 22:00:16 -04:00
MihawkandClaude 3c23512537 Library: build Json strings without partial initialisers (-Wextra)
67b7fed made playlist labels with Json{Json::string, false, 0, text}, which the
WebUI daemon's -Wextra -Werror build refuses (missing-field-initializers for
items and fields); tools/verify.sh's unit gate failed at
test_webui_daemon's setUpClass. Json::of(text) makes a string value.

Test: python3 -m unittest tests.test_webui_daemon
tests.test_scraper.Scraper.test_delete_game
tests.test_scraper.Scraper.test_game_files_and_edited_metadata: 7 OK.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 21:17:57 -04:00
MihawkandClaude 1f4c78317a Games: delete a game from the WebUI, with its disc tracks and its playlist entries
A game added twice (a CHD and an ISO of one disc) could not be removed from the
library (issue 57). The game sheet's Files tab has a Delete game button, which
calls POST /api/library/delete: the game's file (inside the content folder or
a USB or extended drive only, as game_files allows), the files a .cue, .gdi or
.m3u names in its own folder, and its entries in every playlist. Its details
and media, keyed by its name, go only with the last copy of that name; an
archive that holds other games is refused, with nothing changed. Saves and
savestates stay. New texts in all 15 languages.

Test: python3 -m unittest tests.test_scraper (15 OK; test_delete_game: the
ISO goes and the CHD keeps the shared details, the last copy takes them, a
cue's tracks go but a name outside its folder stays, a two-game archive is
refused, a file outside content answers 404); tools/webui-strings.py --check:
821 strings, 0 problems.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 20:01:35 -04:00
MihawkandClaude 67b7fed0ac Library: a name edited in the WebUI reaches RetroArch's playlists and every ES-DE rename
A rename in the WebUI was written only to the game's metadata (issue 55).
RetroArch's menus (XMB, RGUI) show the playlist label, which never changed,
and ES-DE took the new name only while its own was still the label, so a
second rename was dropped. src/scraper.cpp now rewrites the game's entry in
every playlist that lists its file (edit_playlist_entries, also for removals),
with Json::dump in src/scraper_json.h; ES-DE's adapter lets every field the
owner edited win (frontends/es-de/ps5/library_ps5.cpp).

Tests: python3 -m unittest tests.test_esde_library tests.test_scraper: 15 OK
(the label changes, the other entries and the playlist's fields stay; two
renames in a row both reach ES-DE's gamelist, its favourite kept).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 19:47:34 -04:00
MihawkandClaude cc17179253 WebUI: a RetroAchievements sign-in shows as saved once its token replaces the password
A sign-in removes cheevos_password and keeps cheevos_token (src/ps5_cheevos.c),
so the settings page showed the password as "Not set" and owners typed it
again (issue 58). GET /api/config now reports cheevos_password as set while a
token is saved; neither value is ever sent.

Test: python3 -m unittest tests.test_webui_secrets (the token alone reads as
set, no token as not set, the token never in the answer): OK.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 19:41:27 -04:00
MihawkandClaude 7cc4fe9793 Evidence: SMB signs in with a password on the Base PS5
The owner added a Samba user and a share that refuses guests ([locked],
over the same folder) on the PC, saved it with the password in the console's
WebUI SMB page and started the homebrew NES ROM from Load Content > SMB.
config/smb.cfg holds the user and password; GET /api/smb answers only
password: true; retroarch.log shows smb://<pc>/locked/smb-test listed and
the ROM loaded (build a2c7800a, aa7f0b2's code).

Evidence: evidence/smb-password (python3 tools/evidence.py compare
evidence/: 141 replayed, 0 failed).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 17:51:09 -04:00
MihawkandClaude aa7f0b2fbe SMB: RetroArch's file browser opens a shared folder from the WebUI
The WebUI's new SMB page signs in to one server (guest or a user and
password), lists its shares, checks a share and folder and saves them in
config/smb.cfg on the console; the password is never answered back.
RetroArch's file browser shows the share as SMB, after INTERNAL and
EXTERNAL. smb:// paths go through RetroArch's VFS to src/ps5_smb_vfs.c
(patches/series 0136), read-only and only on the saved server: a core that
reads through the VFS streams the file, one that opens paths itself gets a
copy in /app0/smb-cache, removed when the content closes.

libsmb2 is pinned at the master revision itsPLK/ps5-pkg-manager runs on the
PS5 (LGPL-2.1-or-later), built by tools/build-libsmb2.sh for the PS5 and the
host and listed in tooling/notices/components.json: RetroArch 1.22.2 has no
SMB client. In a title, getaddrinfo is bound to the platform layer's, which
resolves nothing, so libsmb2's resolver calls go to RetroArch's own
(src/platform_wraps.c); readv, writev, getlogin_r and getprotobyname are
wrapped, so libsmb2 adds no import. SMB sockets set SO_NOSIGPIPE.

Evidence: evidence/smb-load-content (python3 tools/evidence.py compare
evidence/): on the Base PS5 the console's WebUI listed the PC's guest share
and saved share/smb-test; Load Content > SMB listed it and a homebrew NES
ROM on it ran under FCEUmm, read through the VFS (build a2c7800a,
retroarch.log read back over FTP).

Gates: tools/verify.sh (format, unit 214, build, integration, evidence 140)
with PS5_REUSE_STAGED_CORES=ps5sx2: in this worktree the SDK's prospero-lld
wrapper adds -pie to ps5sx2's link (clang passes its long object list in a
response file the wrapper cannot read), so ps5sx2 is the main checkout's
build of the same pinned revision. Host tests run a Samba server of their
own (tests/smb_server.py, no root).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-18 17:42:06 -04:00
Mihawk 54262814b6 Release note: v3.0.0-beta-3, with its contributors and a Game Forwarder demo
releases/v3.0.0-beta-3.md opens with the anti-piracy warning, then thanks
each contributor with what they brought (#34, #54, #53, #52, #50, #62),
then the release. assets/releases/v3.0.0-beta-3/game-forwarder.webp is the
owner's screencast as an animated WebP (1100x584, 95 frames, 6707 ms,
looping, 535016 bytes), shown inline on the release page.

Evidence: the note rendered through GitHub's Markdown API
(gh api /markdown, gfm, repository context) with github-markdown-css 5.5.1:
every icon and avatar at its set size on desktop and at 311 px of content
(a 375 px phone), no table wider than the page there; the at-a-glance
icons now carry their names, so they keep 40 px on a phone.
2026-10-18 09:34:02 -04:00
Mihawk e776467e3d Game Forwarder: a deleted tile leaves the home screen seconds after RetroArch closes
The owner's report: tiles deleted in the page stayed on the home screen after closing
RetroArch (from the home screen, and with the picker's Quit). The console's records showed
the removals done, but late: ShadowMountPlus refuses an uninstall while a game runs, and
the WebUI's daemon only tried again at its stop, 20 s after the title closed; a title
reopened within those 20 s left them for the next close.

The daemon now tries a tile's pending work (a removal, a new name or picture) as soon as
no program of the title is linked, and every 3 s while ShadowMountPlus still refuses (the
exiting app answers "game active" for a moment); the try at the stop stays for what is
left. ps5_forwarders::finish_pending says whether anything still waits, and runs under the
WebUI's lock (the page may be open meanwhile). README: "a few seconds after RetroArch
closes".

Evidence:
- evidence/game-forwarder-delete-on-close: on the console, a tile installed, deleted
  through the WebUI while RetroArch ran ({"state":"pending"}), RetroArch closed:
  /user/appmeta/PPSA99170 gone after 3.0 s; the daemon "13:18:48 not removed yet ...
  game_active", "13:18:51 forwarder PPSA99170: removed"; ShadowMountPlus "uninstall
  requested" at 13:18:51 (before: 20 s and more).
- tests/test_webui_daemon.py test_a_tile_removal_is_done_as_soon_as_the_title_closes:
  nothing while a program is linked, the uninstall within 1.5 s of the unlink, before
  the stop (2 s in the tests); test_webui_forwarders checks finish_pending's answer.
- gates: format, unit (211 tests), build, integration (211 tests), evidence (139
  captures, 0 failed) - PASS.
2026-10-18 08:31:58 -04:00
Mihawk 001bbcf9d1 Evidence: Game Forwarder on the console; README: make a tile in the WebUI
evidence/game-forwarder-webui (tools/evidence.py distil of klog/game-forwarder-webui/
record.txt, game names masked): on the owner's base PS5 (13.40, ShadowMountPlus
1.7-beta5fix1), builds 9adce7a0 (8a31a1f) and 57fc257a (2512f39):
- the routes from the PC: a tile made (PPSA99171, its files and param.json, in
  manual.lst), installed by ShadowMountPlus once RetroArch closed, opened (five
  arguments, "forwarder: /app0/content/snes/<game> with snes9x, closing after it"),
  renamed and re-pictured and the spike's tile deleted while RetroArch ran, both
  finished by the daemon once it closed ("removed", "installed again with its new name
  or icon"; /user/appmeta shows the new name and icon);
- the page in a browser: 33 systems in the picker, each with its icon; a tile made from a
  SNES game's 3D box and one from a Genesis game's logo with the system's icon in the
  corner ("Saving..." while ShadowMountPlus answered); an installed tile deleted ("leaves
  the home screen once RetroArch closes"), uninstalled and its folder gone once RetroArch
  closed; the home screen's icons are byte for byte the PNGs the page drew (512x512);
  the Genesis tile opened its game with genesis_plus_gx and closed after it.
Not checked (human_check): a tile opened on the home screen while RetroArch is open
should bring up the console's prompt to close it; the control payload cannot press the
home screen.

README: "Start a game from a home screen forwarder" opens with "Make a tile in the
WebUI" (the page, ShadowMountPlus 1.7 or later, why changes wait for RetroArch to
close, where games may be); the arguments follow as "Make a forwarder by hand",
unchanged. No line removed.

Gates: format, unit (210 tests), build, integration (210 tests), evidence (138
captures, 0 failed) - PASS.
2026-10-18 07:58:19 -04:00
Mihawk 2512f39519 Game Forwarder: the WebUI's own page for home screen tiles
A "Game Forwarder" entry in the WebUI's sidebar (webui/forwarders.js, its section in
webui/index.html): the tiles with their picture, name, game, core and state (on the
home screen, appears / changes / leaves once RetroArch closes), a New tile editor, Edit
and Delete. ShadowMountPlus's state heads the page; without it, nothing can be made.

The editor: the system first, each behind its icon as on the Games page (a listbox
like the language picker's, by mouse or keyboard), then one of its games by name; the
name on the home screen and the core, prefilled with the game's; "Close RetroArch when
the game ends"; and the picture, drawn here in a 512x512 square and sent as a PNG:
box art, 3D box, logo, cartridge or disc always offered (greyed out when the game has
no such file yet, with a pointer to Download Media), screenshot, title screen and fan
art when it has them, RetroArch's icon; a cutout fits the square, a picture fills it
(both can be changed), and the system's icon can sit in the bottom right corner on a
dark plate. Saving says "Saving..." while ShadowMountPlus answers.

A tile opens a game in the content folder (a path inside it) or on a USB or extended
storage drive (its own path), as the library lists them (src/ps5_forwarders.cpp).
36 new texts, in every language of the WebUI (tools/webui-strings.py --check).

Evidence:
- tests/test_webui_forwarders.py test_page_in_chromium (tests/webui_forwarders_browser.cjs,
  Chromium against the native server and the ShadowMountPlus stand-in): the system
  picker (icon, listbox, Escape, choice), the game, name and core prefilled, the
  pictures offered and greyed out, a logo fitted and a cover filling the square, the
  system icon's plate in the corner, a tile created (its icon a 512x512 PNG), edited
  (picture kept, corner and fill options off), deleted; no page error; nothing wider
  than a 390 px phone - OK. The route tests take the drive paths' rule (a "/mnt/usb0/.."
  path refused) - 4 tests OK.
- console (base PS5, ShadowMountPlus 1.7-beta5fix1), the page in a browser: the tile
  list read from the console; a SNES game's tile made with its 3D box fitted
  (PPSA99170, "appears on the home screen once RetroArch closes"); a second, waiting
  tile deleted from the page at once; 33 systems in the picker, each with its icon.
- gates: format, unit (210 tests), build, integration (210 tests), evidence - PASS.
2026-10-18 07:40:22 -04:00
Mihawk f25de5852e Tests: a WebUI server whose port was taken starts again on another
The host tests found a free port by binding port 0 and closing it, then started the
WebUI's server on it. In between, any connection on the machine can take that port as
its own (the same ephemeral range): the server then exits at once, "Failed to bind to
port N: Address already in use", and the test waited out its window and failed with
"the server did not start" (test_scraper, test_scraper_launchbox) or "HTTP server
exited" (test_webui). It happened in about one gate run in three, under JOBS=4 or not.

tests/webui_build.py start() starts the server, waits for its status and returns the
process, port and session token; a server that exits before answering is started again
on another port (five tries). Every harness that started the server itself uses it:
test_scraper, test_scraper_launchbox, test_scraper_screenscraper, test_webui (its start
and two restarts), test_webui_language, test_webui_transfer_browser. test_scraper's
free_port() had no other user and is gone.

Evidence:
- the cause: 40 sequential starts with each server's stderr kept: 1 failed, "Failed to
  bind to port 44505: Address already in use" (exit 1, after 0.02 s); through start(),
  40 of 40 started.
- python3 -m unittest tests.test_scraper tests.test_scraper_launchbox
  tests.test_scraper_screenscraper: OK; tests.test_webui tests.test_webui_language
  tests.test_webui_transfer_browser: 21 tests OK.
- gates on the tree with the next commit: format, unit (210 tests), build, integration
  (210 tests), evidence - PASS.
2026-10-18 07:39:56 -04:00
Mihawk 8a31a1f2b2 Game Forwarder tiles: the WebUI's routes and ShadowMountPlus, made, edited and removed
src/ps5_forwarders.{h,cpp}: a tile is /data/homebrew/<id> (PPSA99170 to PPSA99269, an
id no folder has and ShadowMountPlus knows nowhere) with the tile app and payload of
step 1, libc.prx, a param.json for its id and name, an icon and forward.txt (the
arguments, which also mark the folder as a tile: nothing else is listed, changed or
removed). ShadowMountPlus's HTTP API (127.0.0.1:10101, its docs/api.md 1.7) puts it on
the home screen: manual/add at once, installed when no game runs.

Routes (src/webui_ps5.cpp): GET/POST /api/forwarders (the list with ShadowMountPlus's
state, the cores, each tile's state; create), POST /api/forwarders/edit?id=,
PUT/GET /api/forwarders/icon?id= (a 512x512 PNG, up to 2 MiB), POST
/api/forwarders/delete?id=. The library JSON carries each game's core, to prefill it.

ShadowMountPlus refuses to uninstall while a game runs, and RetroArch is one whenever
the WebUI is up: a removal of an installed tile takes it out of the manual list at
once (so no scan installs it again) and is finished by the WebUI's daemon once the
title has closed (daemon/webui_daemon.cpp -> finish_pending): uninstall, then delete
the folder. A new name or icon on an installed tile is re-registered there the same
way (uninstall, back in the manual list, scan), and every tile's app and payload are
brought up to the build's.

Found on the console and handled: ShadowMountPlus 1.7-beta5fix1 can drop a source from
its manual list as it installs it (its next scan reads app.db before the install shows
there); the tile stays installed, and a re-registration adds it back first. Its game
list can take longer than 5 s after RetroArch starts: 20 s now, and a list that does not
arrive says why instead of showing every tile as waiting.

Evidence:
- host: tests/test_webui_forwarders.py against a stand-in for ShadowMountPlus's API:
  create (first free id past a plain folder and a title ShadowMountPlus knows), the
  files and param.json, validation (name, content path, "..", absolute path, newline,
  core), edit, icon (size and format refused, stored, served), removal while busy
  (pending, out of the manual list) finished by finish_pending, at once when not
  installed, re-registration after the source was dropped, binaries refreshed,
  ShadowMountPlus absent - 3 tests OK.
- console (base PS5 13.40, ShadowMountPlus 1.7-beta5fix1), through the WebUI from the PC:
  GET /api/forwarders: ShadowMountPlus answers, the test tile PPSA99170 installed;
  POST create -> PPSA99171 (eboot.bin, launch.elf, libc.prx, icon0.png, param.json
  UP9000-PPSA99171_00-FORWARDER0099171, forward.txt with five arguments), in the
  manual list; RetroArch closed -> ShadowMountPlus "installed game PPSA99171";
  the tile opened -> tile.log "110832 sent to 127.0.0.1:9021 (errno 0)", launch.log
  five arguments, RetroArch "forwarder: /app0/content/snes/<game> with
  /app0/cores/snes9x_libretro.so, closing after it"; rename and icon -> updating,
  delete PPSA99170 -> pending (manual list empty); RetroArch closed -> daemon
  "forwarder PPSA99170: removed", "forwarder PPSA99171: installed again with its new
  name or icon"; /user/appmeta/PPSA99171 titleName "Forwarder renamed" and the new
  icon (first pixel 20 60 d0), /user/appmeta/PPSA99170 and /data/homebrew/PPSA99170
  gone, app.db 33 -> 31 -> 32 entries; the daemon also replaced PPSA99170's older
  launch.elf with this build's.
- gates: format, unit (209 tests), build, integration (209 tests), evidence - PASS.
2026-10-18 00:56:35 -04:00
Mihawk a0ea678ee2 Game Forwarder tiles: the tile app and its launch payload, built into the title
A home screen tile is a title of its own (PPSA99170 to PPSA99269) that starts one
game in RetroArch. Titles cannot launch titles (sceSystemServiceLaunchApp returns
0x8094000F from one, sceLncUtilLaunchApp is a null import), so the tile hands a
payload to the console's ELF loader on 127.0.0.1:9021 and exits:

- forwarder/tile.c: the tile's eboot.bin. Reads its title id from
  /app0/sce_sys/param.json, stamps it into /app0/launch.elf's slot, sends it to
  the loader, logs /app0/tile.log, and leaves through LoadExec("exit") (a return
  from main ends in a SIGSYS error notice).
- forwarder/launch.c: the payload. Reads /data/homebrew/<tile>/forward.txt (one
  argument a line: --rom, --core, --exit-after-game, src/frontend_mode_ps5.cpp),
  waits for the tile to leave, and starts PPSA99169 with those arguments through
  sceLncUtilLaunchApp (sceSystemServiceLaunchApp as the fallback).
- tools/build-forwarder.sh builds both; tools/build-title.sh puts them in
  dist/PPSA99169/forwarder/ and counts them in the title's identity; the notices
  cover both files.

Evidence:
- host: python3 -m unittest tests.test_forwarder (the tile stamps and sends the
  payload byte for byte, refuses a lowercase id, a missing or a doubled slot; the
  payload passes the five arguments, falls back to SystemService, and an
  unstamped payload starts nothing) - OK.
- console (base PS5, 13.40, ShadowMountPlus 1.7-beta5fix1), the test tile
  PPSA99170 registered through ShadowMountPlus's manual list:
  tile.log "tile PPSA99170: launch payload 110832 bytes, 110832 sent to
  127.0.0.1:9021 (errno 0)"; launch.log five arguments, sceLncUtilLaunchApp
  00002018; RetroArch's trace "forwarder: /app0/content/nes/<game> with
  /app0/cores/fceumm_libretro.so, closing after it"; the tile exited cleanly
  (no error notice), back to the home screen after the game.
- gates: format, unit (206 tests), build (tile 11,123 B, payload 110,832 B),
  integration (206 tests; a first run timed out starting two scraper test
  servers under load, both passed alone and on the rerun), evidence - PASS.
2026-10-18 00:23:06 -04:00
Mihawk 459e194dc0 Parked workflow: the ScreenScraper developer account is never passed to a build
The workflow parked from mihawk-99/PS5_RetroArch#34 gave the title build
PS5_SCREENSCRAPER_DEVID and PS5_SCREENSCRAPER_DEVPASSWORD from repository
secrets. A public repository's workflow artifacts can be downloaded by
anyone, and the account is never published, so the parked copy no longer
passes them and its header says why. Nothing runs it; no value was ever
in the file.

Evidence: grep finds ScreenScraper only in that header line; format PASS.
2026-10-17 22:27:58 -04:00
Mihawk eb4a4919ce Evidence: this series on the console (0132 to 0135, SwanStation, melonDS DS, forwarders)
Build 838fd9a3 (20dd7fe with both frontends and both optional cores) on
the owner's base PS5 (1440p, 120 Hz, VRR), offline:

- framegen-off-presents-once (0132): frame generation off with a 2 left
  in retroarch.cfg logs "Off: each game frame is presented once (2
  presents were kept)" and runs 602 frames in 10 s; turned off from its
  XMB tab mid-game, the video output reinitializes (a 2.5 s gap) and the
  same happens, 602 frames in the next 10 s.
- chd-flac-hash (0133): a CHD chdman made from one of the owner's
  PlayStation backups (cdlz, cdzl, cdfl). Build 22b5d122: "Could not open
  track" for nine consoles, "hash generation failed". This build: the
  data track opens and RetroAchievements generates the hash, stopping
  only at the sign-in.
- swanstation-console: the core loads, negotiates Vulkan with
  RetroArch's context and boots the owner's BIOS dump; 574 frames in
  10 s while the disc boots.
- melondsds-microphone (0134): the ps5 driver opens the first user's
  AudioIn port at 48 kHz, captures while melonDS DS has its microphone
  on (866,816 samples), reports the mute state and closes cleanly; the
  game never read its microphone, so nothing was delivered (reads are
  host-tested). melonDS DS at 60 frames a second.
- forwarder-launch (#34): the relaunch test hands the title a
  forwarder's --rom, --core and --exit-after-game; the game runs in
  RetroArch through game mode, the title closes when it quits, and the
  next home launch opens the picker.
- pr-series-frontends: the picker and ES-DE of this build start; ES-DE's
  library counts 35 cores with no failures. Once, the launch right after
  a self-closing forwarder run ended before main(); not reproduced.

Game file names, the disc's serial and hash, and the console user id are
replaced in the records; pictures stay on this machine. The pad scripts
the commands name are in tests/pad/. Test files, the test CHD and its
memory card, framegen.cfg and melonDS DS.opt (both written by the runs)
were removed afterwards; retroarch.cfg is byte for byte as before.

Gates on this tree: format, unit (204), build (staged cores reused),
integration (204), evidence (137 captures, 0 failed) PASS.
2026-10-17 22:27:24 -04:00
Mihawk e3cb379be9 Markdown stays local: SwanStation's README leaves git, its build line joins README
.gitignore keeps every Markdown file but README.md and the release notes
on this machine, and c1ed6e5's cherry-pick added
tooling/swanstation/README.md past that rule. It is untracked again (the
file stays on disk), and README.md's build section now says how to
include SwanStation, as it does for melonDS DS. AngelXex's credit stays
where it was: native.patch's header, tooling/swanstation/notices.py and
the notices table.

Evidence: git ls-files '*.md' lists README.md and releases/ only;
format PASS; tests/test_notices.py PASS; no README line removed.
2026-10-17 21:43:21 -04:00
Martin Pham 20dd7fe082 Home screen forwarders start a game with --rom and --core
From mihawk-99/PS5_RetroArch#34 (Martin Pham). A forwarder, a small app
with a home screen tile of its own, starts the title with --rom <file>
[--core <core>] [--exit-after-game] (each also as --x=<value>). On a
launch that names no mode, src/frontend_mode_ps5.cpp takes it as game
mode takes a frontend's request: the content (absolute, or inside
/app0/content/ and never above it) and the core are checked by
ps5_game_check, RetroArch starts with -L, and afterwards the title
opens as from the home screen, or closes with --exit-after-game.
Without --core the playlists choose, else the one core whose info file
lists the extension (Genesis Plus GX for the Mega Drive extensions both
Sega cores list) or, for a folder, the one core that opens folders. A
refusal goes to trace.txt and the launch goes on as from the home
screen; a handover's --ps5-mode wins over --rom.

Changed from the pull request: its README examples and test fixtures no
longer name games and use the ES-DE folder names content/ has now; one
header comment is moved to its declaration. Its line-ending rewrites of
the PowerShell scripts are dropped (they are gone, 8f14452), and its
GitHub workflow is parked in parked/github-build-workflow, with what
would finish it at the top of build.yml: it cannot run here, and it
does not fetch PS5_MihawkGen, which the frontend compiles now.

To prove it on a console, whose control payload launches without
arguments: the relaunch test takes its restarts' arguments from the
lines after the arm line, and tools/run-title.sh --relaunch-arg writes
them.

Evidence: tests/frontend_mode_ps5_test.cpp (parsing, paths, cores by
playlist, extension and folder, refusals, after the game);
tests/relaunch_ps5_test.cpp (arguments carried; a control character or
a ninth argument arms nothing). Gates: format, unit (204), build
(staged cores reused), integration, evidence PASS. Console: with this
series' deploy.
2026-10-17 21:42:17 -04:00
Jorge Feitosa 6f03aa224a Microphone input, optional melonDS DS core and its touchpad stylus (0134, 0135)
From mihawk-99/PS5_RetroArch#53 and #52 (Jorge Feitosa), renumbered
(0131 to 0133 were taken), taken together: #52 fixes the read loop #53
turns on.

- src/microphone_ps5.cpp (0134): a native AudioIn microphone driver,
  the default one, for the first controller's user. It captures only
  while a core has its microphone on, sends silence under the DualSense
  mute, and keeps PCM only in a bounded queue in memory. RetroArch is
  configured with the microphone; the title links a host stub for
  libSceAudioIn's imports.
- 0135: a microphone read that gets no frames returns silence instead of
  looping forever (a paused core, an empty nonblocking device, a failed
  capture).
- tools/build-melondsds.sh: melonDS DS at pinned revisions (git objects
  by SHA), software renderer and a native-memory JIT, staged only with
  PS5_WITH_MELONDSDS=1; DeSmuME stays the DS default. The DualSense
  touchpad drives its stylus as it drives DeSmuME's.

Changed from the pull requests: the README and the 0134 entry describe
what happens, not an opt-in the code never had. RetroArch's microphone is
on by default, as upstream; melonDS DS listens only while its microphone
button is held (its default), and Settings > Audio > Microphone turns it
off for every core. tests/test_microphone_read.py applies the port's
edits to vendor/retroarch itself rather than reading build/ra-conf, which
a tree configured before 0135 would fail. The SwanStation and melonDS DS
title changes are merged into one licence-file rule.

Evidence: tests/test_microphone_ps5.py (the driver against a simulated
AudioIn: lifecycle, mute, input failure); tests/test_microphone_read.py
(it times out with upstream's loop); tests/test_touchpad_stylus.py
(melonDS DS's relative stylus); tools/build-melondsds.sh, core ABI PASS
(25 exports, kernel_web, sha256 b03d4c93...). A title built with both
optional cores: 61 notice parts, check-notices covers every shipped
executable, check-manifest PASS, 78 melonDS DS options in the catalog.
Gates (default build): format, unit (204), build (staged cores reused),
integration, evidence PASS. Console: with this series' deploy.
2026-10-17 21:25:33 -04:00
Jorge Feitosa c1ed6e5a2a Optional SwanStation core (PS5_WITH_SWANSTATION=1)
From mihawk-99/PS5_RetroArch#54 (Jorge Feitosa). tools/build-swanstation.sh
builds libretro/swanstation at a pinned revision (archive SHA-256 checked)
as a native shared core: Vulkan, the x64 recompiler on title executable
memory (ps5_exec_allocate) with LUT fastmem instead of the mmap path.
The platform changes are adapted from AngelXex's SwanStationPS5 1.3.4,
credited in the patch, the README and the notices. A title build stages it
only with PS5_WITH_SWANSTATION=1, with its licence file beside the info
file; Beetle PSX HW stays the PlayStation default, and the WebUI catalog
lists its options when it is staged.

Changed from the pull request: tooling/swanstation/README.md no longer
names the game it was tested with.

Evidence: tools/build-swanstation.sh, core ABI PASS (25 exports,
kernel_web, sha256 dfb1521f...); tests/test_optional_core_metadata.py
(catalog with and without the core). A title built with
PS5_WITH_SWANSTATION=1: 60 notice parts, check-notices covers every
shipped executable, check-manifest PASS, 113 SwanStation options in the
catalog. Gates (default build): format, unit (201), build (staged cores
reused), integration, evidence PASS. Console: with this series' deploy.
2026-10-17 21:06:25 -04:00
Jorge Feitosa a8bda031f0 FLAC for the frontend's CHD reader, so achievements hash CD FLAC discs (0133)
From mihawk-99/PS5_RetroArch#50 (Jorge Feitosa), renumbered: 0131 and
0132 were taken. chdman lists CD FLAC among a CD's codecs by default,
and libchdr refuses a CHD that declares a codec it was built without, so
the frontend could not open such a disc to hash it for RetroAchievements
even where the core read it with its own copy of libchdr. RetroArch is
now configured with builtin FLAC; build-title.sh leaves out the one FLAC
define whose value is a string, which tools/build.sh cannot take as a
name and the title's sources do not use.

The CHD reader's FLAC codec decodes with dr_flac (deps/dr, public domain
or MIT-0). libFLAC is compiled into libretroarch.a, but nothing calls
it: the linked title holds no FLAC__ symbol and 51 drflac ones, so the
notices are unchanged.

Evidence:
- tests/test_chd_flac.py: the configure flags enable builtin FLAC; the
  vendored reader, compiled as Makefile.common compiles it with builtin
  FLAC, opens tests/chd/tone-cdfl.chd (synthetic: a second of a 440 Hz
  tone and silence, chdman 0.289 -c cdfl, 3,783 bytes) and decodes its
  75 frames to the generated PCM byte for byte; without FLAC it fails.
- Host, with a CHD chdman made from one of the owner's PlayStation
  backups (cdlz, cdzl, cdfl; 2,338 of 31,002 hunks CD FLAC): the reader
  from build/ra-conf answers "unsupported format" without FLAC, and with
  it opens the disc and reads all 31,002 hunks with their CRCs.
- Console, before this change (build 22b5d122, offline, a test user):
  Beetle PSX HW loads that CHD, and RetroArch's hash answers "Could not
  open track" for each of nine candidate consoles, then "hash generation
  failed". After: with this series' deploy.
Gates: format, unit (199), build (staged cores reused), integration,
evidence PASS.
2026-10-17 20:52:55 -04:00
MihawkandMarco Tundo 72ed08b7b8 Frame generation off presents each game frame once (0132)
The two parts of mihawk-99/PS5_RetroArch#62 (Marco Tundo) that 0131 did
not already cover. Frame generation's plan writes the presents per game
frame into video_shader_subframes, which retroarch.cfg keeps. Once frame
generation was turned off, in its menus or in ES-DE's, the 2 it left
there held every game frame for two refreshes: half speed on a 60 Hz
output. retroarch.c now sets one present per game frame whenever frame
generation is off, and logs it when it replaces a larger number.
Turning frame generation on or off in the menus reinitializes the video
output, as a Target Framerate change already did, so the plan applies
at once (src/ps5_framegen_menu.c).

A Shader Sub-Frames value set by hand no longer survives a core load
while frame generation is off; sub-frame shaders have not been tested
on this console.

Evidence: tests/test_framegen_plan.py runs the plan as the script
inserts it. Two new cases start from a 2 left in retroarch.cfg with
frame generation off, on a 60 Hz output and on a VRR one; both fail
with 0132's edit left out. Gates: format PASS; unit PASS (197 tests);
build PASS; integration PASS; evidence PASS (131 captures). The build
gate ran with PS5_REUSE_STAGED_CORES naming the 33 shipped cores: core
stamps hash absolute paths, so in a worktree every core looks changed,
and the staged cores are main's. Console run: with this series' deploy.

Co-authored-by: Marco Tundo <marco@bymar.co>
2026-10-17 20:37:25 -04:00
Mihawk 8f14452e5d Linux only: the PowerShell scripts from the boilerplate go
The eight tools/*.ps1 scripts came in with 4df5c68 from
ps5-native-app-boilerplate and nothing in the build, the gates or the
docs runs them: the project builds and deploys from Linux only.
tools/prepare-assets.sh only wrapped prepare-assets.ps1, so it goes
too. .gitattributes loses the CRLF rule for *.ps1 and tools/lint.sh
no longer lists .ps1 among the files whose headers it checks.

Evidence: git grep finds no reference to any removed script;
tools/verify.sh format PASS; tools/verify.sh unit PASS (283 s,
klog/unit-gate.log).
2026-10-17 19:57:40 -04:00
Mihawk 247336399d Evidence: 0131 on a VRR display plans 119.88 Hz and generates at 120
The owner's base PS5 on a 1440p display with 120 Hz and VRR (48-120 Hz):
the driver lists only the 59.94 Hz mode (its idle vblank check read 20.873
ms, VRR's floor, and restored the default mode), and RetroArch's timed
flips came 8.32-8.35 ms apart, 119.877 and 119.898 a second. Frame
generation therefore planned at 119.88 Hz (Target Framerate 120, 1.99
pictures per game frame) and, on one of the owner's SNES game backups,
presented 119.9 pictures a second with 59.9 generated and the core at 59.9
calls a second (1.65 ms a picture). A self-made NES image looping on a
black screen gave the same timing and plan; its pictures never change, so
nothing was generated.

Evidence: evidence/framegen-flip-rate-vrr, distilled from the console's
retroarch.log (build 22b5d122); python3 tools/evidence.py compare evidence:
131 replayed, 0 failed.
2026-10-17 19:36:24 -04:00
Mihawk 0ebf7c7af5 ES-DE: its first start on a fresh install writes the library
ES-DE's library adapter writes custom_systems/es_systems.xml and the game
lists under /app0/es-de/ES-DE before ES-DE runs, but ES-DE makes that folder
itself only once it starts. On a fresh install's first start every mkdir
below it failed, all 29 writes failed (28 game lists and the systems file)
and ES-DE showed no games; from the second start on it worked. The earlier
ES-DE evidence never met this: those consoles had ES-DE's folder already.
The adapter makes the folder first.

Evidence: tests/esde_library_test.cpp's first start now begins without the
folder and checks 0 failures and a written systems file (it fails without
this change): python3 -m unittest tests.test_esde_library OK. On the
console, evidence/esde-folder-names: the first start before the change,
'28 systems, 255 games ... 29 failures', 'systems written -1'; the second,
0 failures; the first start after it (ES-DE's folder moved aside), '34
systems, 317 games ... 0 failures', 'systems written 34', every system's
path /app0/content/<name>; a home launch of a fresh install started the
picker (picker 1, es-de 1, remembered ask -> picker). python3
tools/evidence.py compare evidence: 131 replayed, 0 failed. tools/verify.sh:
all five gates PASS.
2026-10-17 19:36:17 -04:00
Mihawk a9cf1fbcc1 Library: a cartridge system's archives are games whatever else its core runs
The content scan counted a system's .zip and .7z as games only when none of
its cores took a .cue or .chd, so a core that also runs a disc system hid a
cartridge system's archives: Genesis Plus GX (Sega CD) for the Mega Drive,
Master System, SG-1000 and Game Gear, PicoDrive (Sega CD) for the 32X,
Beetle PCE (the PC Engine CD) for the PC Engine and SuperGrafx. Their
archived games were in neither ES-DE nor the WebUI's library. Whether a
system's archives are games now goes by the system: the disc systems (psx,
ps2, psp, saturn, dreamcast, segacd, pcfx, 3do, neogeocd, gc, wii) keep
theirs out, as before, since RetroArch extracts only the file it loads and a
.cue's tracks would be left behind.

Evidence: tests/ps5_library_test.c, an archived game in an ES-DE megadrive
folder whose core also takes cue and chd (counted; it was not before this
change: the test failed on the game count) beside an archived PlayStation
disc (still left out): python3 -m unittest tests.test_ps5_library OK. On
the console (evidence/esde-folder-names): ES-DE's library went from 28
systems and 255 games to 34 and 317 with the same 325 game backups, the six
systems above among the new ones. tools/verify.sh: all five gates PASS.
2026-10-17 19:36:00 -04:00
Mihawk 6b70c9b130 Content folders are named as ES-DE names its systems; older folders follow
Testers kept reporting folders EmulationStation did not detect. The title
made a folder in content/ for each system named as RetroArch's databases
are ("Sony - PlayStation 2"); it now names each as ES-DE names the system
("ps2", "snes", "genesis", the 50 platform ids, all of them ES-DE system
names checked against ES-DE 3.5.0's own systems file), so content/ is
ES-DE's own layout, <ROM folder>/<system>.

- ps5_library_make_system_folders makes content/ first when it is missing:
  a copy of the title without its empty folders had none at all, because
  the frontend made content/ only later and the step gave up.
- ps5_library_rename_system_folders, at each start before any frontend:
  a folder an earlier build made under RetroArch's name is renamed to the
  ES-DE one, or merged into it when none of its entries' names is taken
  there (else left as it is, still its system's). The paths in RetroArch's
  playlists (and their scan folders) and the per-folder overrides, core
  options, shader presets and remaps under config/ follow; a core whose
  folder bears the content folder's own name (MAME, ScummVM) keeps its
  files, which are that core's. Folders the user named (PS1) stay as they
  are and are still recognised.
- The WebUI's Add Game puts a game in the system's ES-DE folder.
- README: content/<system>/ with ES-DE's names (content/Saturn/ was out of
  date: only system/Saturn is made).

Evidence: tests/ps5_library_test.c (system folders by ES-DE name, a missing
content folder made, a rename, a merge, a clash left in place, a folder
whose name only begins the same, playlists, per-folder settings, a core
folder named as the content folder): python3 -m unittest
tests.test_ps5_library OK. On the console (evidence/esde-folder-names,
with the commits after this one): a home launch renamed three seeded
folders (a rename and two merges), the playlist and options file followed,
and every ES-DE system path is /app0/content/<name>. tools/verify.sh: all
five gates PASS.
2026-10-17 19:35:49 -04:00
Mihawk 43216d5d5a build: the WebUI's CA bundle no longer needs ES-DE's checkout first
build-title.sh copied Mozilla's CA bundle from .deps/es-de, which only
tools/build-esde.sh clones, and it does so after this step and only for a
build with frontends. With .deps emptied, neither the development build
(the build gate) nor the all-target build could pass: both stopped at the
missing bundle. The bundle is now fetched by itself from ES-DE at the commit
build-esde.sh pins (one pin, read from that script), checked against its
sha256 (f66dff1b..., the same file ES-DE's checkout holds), and kept in
.deps/downloads.

Evidence: the pinned URL returns sha256 f66dff1bdf8f9606...; the
all-target build (PS5_FRONTENDS='es-de picker') then ran from a fresh
.deps through ES-DE, the picker and the notices to a verified package
(dist/PS5_RetroArch.zip, python3 tools/check-notices.py dist/PPSA99169:
the licences cover every shipped executable).
2026-10-17 18:21:04 -04:00
Mihawk ae5edabb37 WebUI: a batch upload cut short removes its partial file before it answers
A batch stream that ends inside a file left that file's .upload- temporary
until the request was freed, after the 400 answer was sent: a client reading
the folder the moment the answer arrived could still find it. The gate's
test_batch_refuses_bad_names_and_bad_streams did, once, with the gates run on
four CPUs (taskset -c 0-3). BatchUpload::abandon() now closes and removes the
cut file before the answer; the destructor calls the same.

Evidence: JOBS=4 taskset -c 0-3 nice -n 10 bash tools/verify.sh: the unit
gate failed on that test before (a .upload- file left in content/batch-c)
and passed after (197 tests OK, 1 skipped); python3 -m unittest
tests.test_webui: 17 tests OK.
2026-10-17 18:20:49 -04:00
Mihawk 90848c40d9 Evidence: 0131's flip timing on a console output without 120 Hz
The owner's base PS5, on an output that does not offer 119.88 Hz, ran the
hotfix eboot.bin built from 5c4256b (identity 1f81446a). RetroArch timed
nine flips 16.67-16.69 ms apart, 59.943 flips a second in the 59.940 Hz
mode, and frame generation planned at 59.94 Hz (a 120 target becomes 60)
instead of the 119.88 it assumed before 0131.

Evidence: evidence/framegen-flip-rate-60hz, distilled from the console's
trace.txt and retroarch.log; python3 tools/evidence.py compare evidence:
129 replayed, 0 failed.
2026-10-17 18:20:40 -04:00
Mihawk 5c4256b202 Frame generation plans on the flips the output really shows (0131)
The 59.94 Hz mode takes 120 flips a second on a VRR display and 60 on an
output without 120 Hz. 0121 assumed 119.88 Hz for every 59.94 Hz mode, so
on a 60 Hz output each picture was presented twice and games ran at half
speed with broken audio (a tester's v2.0.0-beta-2 logs: "display 119.880
Hz ... each shown 2x", 58.8 presents/s, the core at 29.6 calls/s).

Patch 0131. khr_display_ctx.c times flips once per process, on the first
swapchain, where the mode is below 100 Hz: the median of nine acquire
intervals once every image is queued, 1 s timeouts, and on a failure a
warning and the mode is believed. vulkan_common.c keeps the rate. The plan
in retroarch.c takes 119.88 only above 100 Hz, and a Target Framerate
above the display's rate becomes the display's (120 on a 60 Hz output is
60, and a 30 fps game still gets a frame made between its frames).

Evidence: evidence/framegen-flip-rate (python3 tools/evidence.py compare
evidence: 128 replayed, 0 failed). tests/test_framegen_plan.py composes
the plan from EDITS as the script applies it and runs nine cases; with
0131's edits left out it fails on the 60 Hz case. The eboot.bin built from
this tree (identity 1f81446a..., sha256 d3dc2a31...) ran on the tester's
60 Hz output, and the owner reports it worked perfectly; no log of that run
was received. Not run yet: the owner's base PS5 with its VRR display (the
console did not answer).

format PASS; evidence PASS; unit 192 of 197 OK. The 5 errors are tests
that read .deps/{ppsspp,azahar,desmume}-src, deleted with .deps on
2026-10-15 along with every core source the build gate's catalog step
reads.
2026-10-17 17:02:49 -04:00
Mihawk 7d45412b4b Frame generation from public PS5_MihawkGen, pinned; the RetroArch shim in this repository
The generator (framegen/src, framegen/include) and its trained network now
come from PS5_MihawkGen (github.com/mihawk-99/PS5_MihawkGen, public),
pinned at c57232a; a release build refuses any other revision. Its sources
and the d5 network are byte-identical to what the title built before (the
network sha256 31ecc7d6..., the four generator sources and the compiled
shaders; framegen.h and nn_final.comp differ only in a comment). The
RetroArch driver shim (vulkan_framegen.cpp/.h, from PS5_FrameGen 9490b0d)
moves to tooling/framegen, so all of the frame generation code in
eboot.bin is published. tooling/notices: a mihawkgen component with its
source address.

format PASS; unit (196 tests) OK.
2026-10-14 20:48:51 -04:00
Mihawk 517673894c No game titles in the repository; the v2.0.0-beta-2 release note
Every game title in tracked text (test fixtures, comments, patch notes,
console evidence, the v1.0.0-beta.1 note's alt text) is now a made-up
stand-in that keeps the original's shape: its punctuation (apostrophes,
"&", " - "), region tags and, where a test depends on it, its sort order
and how a title relates to its alternate name. Evidence folders and the
Flycast pad script named after games are renamed. Only the committed
handoff/PPSA99002/eboot.bin still holds a title, inside the binary.

releases/v2.0.0-beta-2.md: PS5SX2, frame generation, RetroAchievements,
NetPlay, the WebUI in 16 languages, the library and frontend changes,
the systems tested on a Base PS5, upgrading from v1.0.0-beta.1, source
and credits. It opens with the anti-piracy banner and points, names no
game and shows no game picture: three WebUI screenshots in
assets/releases/v2.0.0-beta-2/.

Gates: format, unit (196 tests), build, evidence (127 replayed, 0
failed), check-manifest and check-notices: PASS.
2026-10-14 20:35:04 -04:00
Mihawk f37b5dbc41 tests: no game titles in fixtures (a made-up file name with "of" for the translation check)
test_webui_language: OK.
2026-10-14 20:10:32 -04:00
Mihawk ccb9da7fab PS5SX2: the fork's work is on its main branch 2026-10-14 19:53:03 -04:00
Mihawk 0bca2863e6 Gates: test stubs for frame generation; comment wrapping; TLS and password patches renumbered 0129/0130
- tests/input_ps5_test.cpp: ps5_fg_record_request stub (the pad script's
  RECORD calls PS5_FrameGen's recorder, not linked in the host test).
- tests/test_webui_qr.py: stubs for the frame generation entries 0120 binds
  beside the WebUI's (ps5_fg_menu_is_label/_is_folder, action_ok_ps5_fg,
  action_ok_push_default).
- src/main.cpp, src/ps5_framegen_menu.c: tools/lint-format.sh --write
  (comments re-wrapped to the line limit; no code changed).
- The frame generation code already names patches 0122-0128, which
  patches/series does not list; 47879d1's TLS and password patches took
  0122/0123 and are now 0129 (TLS verification) and 0130 (password).

lint-format PASS; tests test_input_ps5, test_webui_qr, test_frontend,
test_webui_secrets: OK.
2026-10-14 19:27:23 -04:00
Mihawk 47879d118e HTTPS verifies certificates; the RetroAchievements password is not kept; names are never translated
- 0122: HTTPS uses MBEDTLS_SSL_VERIFY_REQUIRED (upstream verified optionally
  and ignored the result), so nobody on the network path can stand in for
  RetroAchievements. Its chain (GTS Root R4, cross-signed by GlobalSign Root
  CA) verifies against the bundled 2017 roots.
- 0123 and src/ps5_cheevos.c: a sign-in that returns a token removes
  cheevos_password from /app0/config/webui.cfg (with the empty token the
  WebUI puts beside a new password) and from retroarch.cfg, which keeps its
  token. The WebUI clears the old token when a new password or account is
  saved, so the new account signs in with its password.
- WebUI: game, file, system and source names carry data-no-translate, and a
  value inside a translated sentence is translated only on an exact match:
  "Shadow of the Colossus" no longer reads as "{0} of {1}" ("Shadow sur the
  Colossus" in French).
- LRPS2 leftovers: the 34 orphan option texts in each catalog, the stale
  wording in ps5sx2_libretro.info and build-ps5sx2.sh. Patch count 255 -> 257.

Console (192.168.50.237, klog/retroarch-191705.log): DNS, "TLS connected",
mihawk99 logged in, game 2992 identified with verification required; after
the run webui.cfg holds no cheevos_password and retroarch.cfg an empty one
with its token kept.
Tests: test_webui_secrets (token cleared; helper removes the password, keeps
the token in retroarch.cfg), test_webui_language (a file named Shadow of the
Colossus stays as it is in French), test_frontend: OK. lint-shell PASS;
lint-format fails only in src/main.cpp and src/ps5_framegen_menu.c, not
touched here.
2026-10-14 19:19:14 -04:00
Mihawk 8289d7ee40 PS5SX2: pin the core at dba9ef4 (blocking-core protocol, dithering option) 2026-10-13 16:04:57 -04:00
Mihawk 5b2dc7f42c FrameGen: blocking-core protocol, floor opt-in, mvsplat in the build, recorder width hook
apply-port-patches 0122-0128: private env commands 0x25001 (the core blocks inside retro_run) and 0x25002 (emulated vsyncs a frame
covers, period in microseconds) size the generator's group from what the core says, never from a measured gap; the floor is opt-in
(PS5_FRAMEGEN_FLOOR=1); core run/cpu times in the log. The menu and its config follow. build-retroarch.sh compiles fg_splat.cpp.
main.cpp reads framegen-record-width.txt for the recorder. input_ps5.cpp: reflowed comments and calls.
2026-10-13 08:50:58 -04:00
Mihawk 6937678fdc PS2: PS5SX2 replaces LRPS2 (tools/build-ps5sx2.sh); LRPS2 removed from the build
The PS2 core is now PS5SX2 (Swordpdf's PCSX2 for the PS5) from the fork
../Libretro_PS5SX2, branch libretro, pinned at 308e8dc. build-ps5sx2.sh
fetches and pins its dependencies (rapidyaml 0cd25a1, glslang 275822a built
for the console, shaderc v2026.2 headers), builds the core with the fork's
ps5/libretro/Makefile.libretro, ABI-checks it and stages PCSX2's game
database in system/ps5sx2/resources. PS5SX2_DEV=1 builds the fork as it
stands. BIOS and memory cards stay in system/pcsx2/ (shared).

LRPS2 is gone: build-lrps2.sh and tooling/lrps2, its notices component,
WebUI catalog and settings-guide entries, and the title's core list.
build-title.sh empties dist/{cores,info} before staging, so a core taken
out of the list does not linger. The balanced 4K default (0102) now sets
ps5sx2_upscale to 4x; the core offers up to 8x (6x is 4K).

Also: patch 0118's tests (the block count, the XMB icon names test).

Console evidence (192.168.50.237, RADV; tools/run-title.sh --game=ps5sx2):
- Shadow of the Colossus (USA) boots and plays (klog/game-122302.png).
- tests/pad/ps5sx2-states-4k.txt: frames of 3072x2688 at 6x; a 46,562,360
  byte state saved and loaded from the Quick Menu (klog/retroarch-124621.log).
- RetroAchievements: SET_SUPPORT_ACHIEVEMENTS, account logged in, disc
  hashed, the core's memory map published (klog/retroarch-125231.log).
- pcsx2_libretro.{so,info} deleted from the console's cores/ and info/.

Gates: format PASS; unit and integration fail only in
test_input_ps5.test_joypad_bindings and test_webui_qr (the uncommitted
frame-generation and input work in src/, not part of this commit); notices
PASS (33 cores); manifest PASS; evidence 127 replayed, 0 failed. The title
was built with the other cores reused (PS5_REUSE_STAGED_CORES).
2026-10-12 13:05:52 -04:00
Mihawk 453049da68 Pad script RECORD: the present-stream recorder; run-title.sh brings the recordings back 2026-10-11 22:19:13 -04:00
Mihawk 4f3945f111 Statistics overlay names Base Rate and FrameGen; hardcore mode off by default (seed and compiled default) 2026-10-11 17:11:47 -04:00
Mihawk 32efd9b173 FPS line: Base and FrameGen rates when frames are generated; the counter shows the core's own rate otherwise 2026-10-11 17:06:15 -04:00
Mihawk ed396f39ea FPS counter: the core's own new frames a second, the presents a second only while frames are generated; the load-state trace 2026-10-11 16:52:01 -04:00
Mihawk 56ecefb831 Menu code: the osd's calls parameter no longer clashes 2026-10-11 14:13:15 -04:00
Mihawk 50a3d09ee7 Governor level, calls and floor in the driver; floor and honest overlay in the menu code; the early presents default fixed 2026-10-11 14:10:44 -04:00
Mihawk 72a3bac0b3 Target Framerate (60/120), the timeline of pictures, any-cadence generation, present sequence log, honest overlay 2026-10-11 14:03:38 -04:00
Mihawk da4cf320b9 Frame generation: real frames of pixel-art content come from the held copy; present spacing in the status log 2026-10-11 10:56:21 -04:00
Mihawk 3b59e0d59f Remove the small-change skip: every sprite moves at the output rate 2026-10-11 07:50:31 -04:00
Mihawk e707d3e30f Frame generation: frames where only a small part of the picture changed are shown as the core made them 2026-10-11 07:35:35 -04:00
Mihawk 328b7e7457 The on-screen frame rate counts presented frames (shader subframes) 2026-10-11 07:13:21 -04:00
Mihawk 472975d5e0 Frame generation: the cadence check also reads the clock (a core that does not hand over unchanged frames); lighter status display 2026-10-11 02:14:46 -04:00
Mihawk f02c762ad7 Frame generation: only a steady cadence is interpolated 2026-10-11 01:04:21 -04:00
Mihawk 490a9da74c Frame generation: Output Rate Auto (0121), pacing on the real 119.88 Hz, per-second pace log 2026-10-11 00:38:36 -04:00
Mihawk 0b581dd6b8 Frame generation menus: XMB leftmost tab, RGUI first entry, ES-DE settings entry (0120)
Options (on/off, output rate, method, cadence, working size, status on screen) are shared
through /app0/config/framegen.cfg, which the title appends when it starts RetroArch.
2026-10-10 23:27:48 -04:00
Mihawk 9096eedb9b Test switch: how many frames the generator dumps 2026-10-10 22:48:01 -04:00
Mihawk e19c2f31b7 Test switch: /app0/framegen-dump.txt dumps the frame generator's frames 2026-10-10 21:42:42 -04:00
Mihawk c85dd735b5 Frame generation: PS5_FrameGen's network shows the frames between content frames (0119)
The Vulkan driver's shader subframes present the generated frame at j/N
between the previous and the current content frame (video_framegen, with
video_shader_subframes 2 to 4). The hooks are anchor edits; the generator and
its shim are built from ../PS5_FrameGen and the trained network is staged at
assets/framegen/network.fgw.
2026-10-10 21:06:03 -04:00
Mihawk 489139260b tests: the language test waits for the Settings page instead of reading it early
After the click on Settings it waited for the Settings heading, which is static and
translated while still hidden, then read the page title before navigation had run
(seen once in a full run: "Vue d'ensemble" for "Réglages"). It now waits for the title.

Evidence: three consecutive passes of tests.test_webui_language.LanguageBrowser.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 14:27:18 -04:00
Mihawk fc1df57bfe XMB: playlist tabs show their system's hardware (issue 35)
tools/xmb-system-icons.py makes, at build time, one tab icon per RetroArch database the
title knows (src/ps5_library.c's platform table: 48), from the WebUI's system artwork
(webui/assets/systems, Linear ES-DE, MIT): each picture cropped to its hardware (the logo
and year under it removed; XMB prints the name), centred, 256 x 256 PNG, named
<database>.png in assets/xmb/monochrome/png, where XMB looks for a playlist's icon.
Game rows keep XMB's own content icon (no <database>-content.png). A playlist with
another name keeps the default icon.

Notices: the generated icons are their own part (MIT); retroarch-assets now names its
files exactly, so neither claims the other's (fnmatch's * crosses "/").

Evidence: python3 -m unittest tests.test_xmb_assets (one icon per database, 256 x 256
RGBA, the caption cropped off, no -content icons, each icon owned by one licence); the
build printed "[xmb] 48 system icons" and tools/check-notices.py passed. Contact sheet
checked by eye. Console screenshot not taken: the title refused to start on this
console ("startup refused: elevation or installation could not be verified").
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 14:27:18 -04:00
Mihawk bead80725c Start the bundled Lapy service when RetroArch launches
Package the qualified title-specific resident service and start it through
local elfldr before workers when no service is ready. Wait for preflight,
verify SDK elevation and UID 0, and reuse the service on subsequent launches.
Keep installed paths and offline fallback; no Payload Manager boot hook.

Console evidence: evidence/lapy-autostart/capture.json and expectation.json.
Reproduce with evidence/lapy-autostart/README.txt, then
python3 tools/evidence.py compare evidence/.

Verified on the base PS5: cold service startup, two authenticated achievement
sessions, reuse of one service across three requests, and NetPlay relay join
with 3837/3361 input packets and 52 matching state checks. Normal title exits;
root counts 73/72 before each request. Resident service remains running.
Installed binary hashes and unchanged Payload Manager configuration verified.

Validation: 194 unit and 194 integration tests; format; all-target build with
PS5_FRONTENDS='es-de picker' and the existing pinned build/picker-template;
manifest/notices/source bundle checks; 127 evidence captures replayed.
No secrets, saves or raw console captures included. No push.
2026-10-10 11:26:09 -04:00
Mihawk 56ecbf367f Verify RetroAchievements and NetPlay after cooperative elevation
Qualify the console build with authenticated achievement-set loading, a fresh
badge download, FCEUmm LAN hosting and relay joining. The relay session exchanges
scripted controls and passes 55 direct emulation-state CRC comparisons. Normal
quit returns the elevation service's root counts to baseline.

Expose DNS/TLS failures and add a debug-only trace at the upstream state CRC
comparison. This adds one port hook, so the intentional patch-count assertion
changes from 253 to 254. Keep the requested Online Updater removal and repeatable
RetroArch runner arguments. The verified build uses the standard SDK socket API.

Online startup still requires the qualified external Lapy service; no autoload
is installed. Document the force-close teardown timeout and untested lobby/NAT
scope. Credentials, settings, saves and raw console captures are not committed.

Validation: PS5_FRONTENDS='es-de picker' with the pinned picker template;
tools/verify.sh format unit build integration evidence passed (193 unit,
193 integration). After moving only the debug trace to the received-CRC path,
24 focused frontend/patch tests and format/build passed again, followed by the
final console runs. Final format/evidence, manifest and notices checks passed.

Evidence: evidence/online-features/capture.json and expectation.json.
Replay: python3 tools/evidence.py compare evidence/ (126 captures, zero failures).
Console reproduction: evidence/online-features/README.txt and console_netplay.py.
2026-10-10 10:53:45 -04:00
Mihawk 39ad097e6d Record saved username mismatch without account values
Read-only comparison confirms both saved passwords match the working pair, while the inherited username differs and no WebUI username override exists. Formatting does not explain the difference. Validation: tools/verify.sh evidence; 125 captures passed. Console settings unchanged.
2026-10-10 10:11:53 -04:00
Mihawk e1edcb0ee6 Record successful PC authentication of owner-provided pair
One authorized login2 request returned HTTP 200 and a token, which was discarded. No account values committed or console settings changed. Evidence: evidence/ra-host-login-provided. Validation: tools/verify.sh evidence; 125 captures passed.
2026-10-10 10:09:42 -04:00
Mihawk 75c20c8d3b Record PC RetroAchievements login rejection
One freshly authorized login2 POST using current console settings returned HTTP 401 / invalid_credentials. No credentials or tokens retained in evidence. Artifact: evidence/ra-host-login/capture.json. Validation: bash tools/verify.sh evidence; 124 captures passed. Authentication remains unresolved.
2026-10-10 10:08:09 -04:00
Mihawk b9260ae31c Keep existing installation paths usable after optional elevation
Request cooperative SDK elevation before RetroArch workers only when the
qualified external daemon is ready. Verify UID and executable identity, map
legacy filesystem paths, and retain shell LoadExec names. Picker and ES-DE
keep their normal sandbox startup. Missing daemon remains a normal launch.

Verification: tools/verify.sh format unit build integration evidence, run
with the pinned picker template. Format passed; 193 unit and 193 integration
tests passed; all shipped targets built; 123 evidence captures replayed.
Generated picker bootstrap repairs and earlier failed trials are recorded.

Console: console_test.py final-return-ra, final-esde and final-offline;
executables read-back verified; current settings preserved; test controls
removed. Badge PNG downloaded and decoded. Hosting reached player 2 once;
joining and account authentication remain unresolved. No WAN/lobby claim.
Artifact: evidence/installed-paths/{capture,expectation}.json and README.txt.
Reproduce comparison: bash tools/verify.sh evidence.

Pre-existing networking/updater/runner edits remain outside this commit;
the capture identifies the tested working-tree context.
2026-10-10 10:04:27 -04:00
Mihawk 4ca549b05f network: record console privilege and socket-mode comparison
On the base PS5, the same early-main RetroArch process fails F_SETFL with
EACCES before connect; after cooperative Lapy elevation both socket APIs
connect to both RetroAchievements endpoints with SO_ERROR=0. Record the
credential change, lost /app0 access, balanced daemon teardown, unresolved
LAN listener timeout, old-SDK startup failure and exact original restoration.
PS5SX2 comparison is source-based as requested; no production elevation.

Evidence: evidence/network-elevation/capture.json and expectation.json.
Console commands: python3 build/network-elevation/run.py baseline;
python3 build/network-elevation/run.py elevate (preparation and restoration
in evidence/network-elevation/README.txt; exact probe sources included).
Validation: bash tools/verify.sh PASS: 192 unit, 192 integration, PS5 build,
122 captures. Replay: python3 tools/evidence.py compare evidence/.
2026-10-10 08:37:33 -04:00
Mihawk 5e1b956a37 HTTPS: seed RetroArch's mbedTLS from arc4random_buf, not /dev/urandom
RetroArch's mbedTLS 2.6 registers /dev/urandom as its platform entropy source
unconditionally, and one failing source fails the random generator's seed, so every
TLS connection (RetroAchievements, HTTPS downloads) would fail if a title cannot open
it, which nothing here has shown it can. --wrap=mbedtls_platform_entropy_poll gives it
the title's arc4random_buf instead (the platform layer's, which the WebUI already
draws its session tokens from). No vendor change.

Evidence: evidence/network-build now records the registered source:
"<__wrap_mbedtls_platform_entropy_poll> -> arc4random_buf (= ps5_arc4random_buf)".
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 01:50:45 -04:00
Mihawk 9764579ed3 WebUI: Add Game no longer forgets a system chosen while the list loads
Reopened, the dialog kept the last session's systems until the new list arrived and
then replaced them, so a system chosen in between was lost and Add said "Choose a
system and a game backup." It now offers nothing but "Loading your installed systems…"
until the list is there. Found by the games page browser test, which failed this way
under load once its wait reported what the dialog said.

Evidence: tests.test_scraper.Scraper.test_games_page_in_a_browser passed three times in
a row and in the full run below.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 01:35:12 -04:00
Mihawk 7550870707 WebUI: Achievements and Netplay settings; passwords and tokens never leave the console
The guided global settings gain two categories: Achievements (RetroAchievements on/off,
user name, password, hardcore mode) and Netplay (nickname, public lobby announce, relay
server), shown with RetroArch's defaults before RetroArch has written them. Translated
into the 15 other languages.

With networking built in, retroarch.cfg holds cheevos_password, cheevos_token and the
netplay passwords, which the settings editor's Advanced view listed like any key. Any
key ending in _password or _token is now sent as an empty value marked secret (with
whether one is saved); a password can be set (an empty one keeps the saved one) and a
token, RetroArch's own, cannot be written.

Evidence: python3 -m unittest tests.test_webui_secrets (the saved password, token and
netplay password never appear in /api/config; an empty password keeps the saved one; a
new one reaches webui.cfg; a forged token and a non-boolean switch are refused);
tests.test_webui_language (the French Achievements page shows an empty password field
marked "Non défini", no English left).
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 01:35:12 -04:00
Mihawk 44143aba19 RetroArch with networking: netplay, RetroAchievements and HTTPS
configure now enables networking, netplay LAN discovery, RetroAchievements (rcheevos)
and SSL through RetroArch's own mbedTLS (deps/mbedtls, its bundled CA certificates):
nothing new is linked from outside the tree. tools/build-retroarch.sh passes
-DHAVE_BUILTINMBEDTLS, which libretro-common's TLS socket needs and only upstream's
console makefiles pass by hand.

Name lookups: the SDK links getaddrinfo to a resolver that refuses every lookup (the
real one is in a module titles do not load) and no module exports getnameinfo.
src/net_shims.c replaces libretro-common's getaddrinfo_retro, freeaddrinfo_retro and
getnameinfo_retro (--wrap; wrapping getaddrinfo itself still bound the SDK libc's
refuser) and resolves names with the console's own DNS (libSceNet's
sceNetResolverStartNtoa); getnameinfo is numeric. Every RetroArch caller (netplay, the
lobby, HTTP, NAT traversal) reaches it; only mbedTLS's unused mbedtls_net_connect/bind
keep libc's.

Evidence: evidence/network-build (from build/llvm-pie.elf and build/ra/libretroarch.a:
the configure switches, 28 rcheevos and 60 mbedTLS objects, the 9 resolver callers,
the sceNetResolver imports, no null imports); python3 -m unittest tests.test_net_shims.
Console behaviour (sign-in, netplay) is not yet verified: no launcher was available.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 01:16:31 -04:00
Mihawk bf9b897313 ES-DE: six more themes to choose from (Colorful, PlayStation-X, Art Book Next, ...)
EmulationStation now ships, beside Alekfull NX (still the default) and Linear:
Colorful (Simplified), Colorful (Revisited), PlayStation-X, Art Book Next, Retrofix
(Revisited) and Artflix (Revisited), all from ES-DE's own theme list. Each is pinned
by commit in frontends/es-de/themes.list, fetched by tools/build-esde.sh, copied by
frontends/es-de/stage.sh without artwork sources, and credited in the notices
(CC BY-NC-SA 2.0 or 4.0, non-commercial; Artflix states no licence of its own and
follows its origin, Alekfull's ARTFLIX). A user picks one in ES-DE's UI Settings.

Not included: Next-pixel and Not-so-epic exist only as legacy Recalbox/Batocera themes,
a format ES-DE 3 no longer loads; no ES-DE version of either was found (ES-DE's list,
GitHub, GitLab).

The title folder's es-de/themes is 847 MB; the release ZIP grows to 1.53 GB.
Evidence: python3 -m unittest tests.test_esde_themes (pins, notices, fetched commits);
the build printed "es-de: resources 17M, 8 themes in 847M" and the ZIP lists the eight
theme.xml files.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 01:16:31 -04:00
Mihawk 8bf6c14671 tests: the transfer test's flake was Chromium's blob storage, not the server
A failing run's request log (now kept: failed requests are printed) showed every part
of cancel.iso failing at once with net::ERR_BLOB_REFERENCED_BLOB_BROKEN: the 200 MiB
in-memory File, created beside the earlier 2 x 150 MiB ones, sometimes exceeded
Chromium's blob storage. The cancel step now sends 64 MiB (the smallest size that goes
as parts) throttled to 8 MiB/s, so the cancel lands mid-way every time. Real uploads
read files from disk and are unaffected.

The Add Game step of the games page test now waits as long as the page allows its
download start (60 s, plus the upload) and, on a timeout, says what the dialog said.

Evidence: six consecutive runs of tests.test_webui_transfer_browser passed (before:
one failure in two); the failure log is quoted above.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 00:47:48 -04:00
Mihawk 45f3718fdb WebUI: the page in 16 languages, chosen in the header or Settings, kept on the console
webui/i18n.js shows each English text of the page from webui/i18n/<code>.json:
English, Spanish, French, German, Italian, Portuguese (Brazil), Russian, Japanese,
Korean, Chinese (Simplified and Traditional), Vietnamese, Turkish, Polish, Dutch and
Indonesian, 776 texts each (tools/webui-strings.py lists them and checks every
catalog). Pickers with flags (lipis/flag-icons, MIT; vn, tr, pl, nl, id added): a
compact one in the header (the flag alone on a phone) and every language as a tile in
the Settings footer. The choice is POST /api/preferences, kept in
config/webui-preferences.cfg and reported by /api/status, so every device follows it
within a status check (10 s). Not covered: right-to-left languages; option texts the
cores provide stay as written.

Evidence: python3 -m unittest tests.test_webui_language: catalogs complete; the
preference refuses unknown codes and wrong tokens; Chromium finds no English text left
on any page in French, follows a choice made in another browser, works by keyboard,
and fits a 390 px phone with no sideways scroll (screenshots in
build/tmp/webui-language).
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-10 00:47:48 -04:00
Mihawk 7eca59662f tests: bound the transfer browser test and report what it saw
The transfer browser test once ran into the 600 s gate timeout in a full run
(it passes alone and under load). Its waits are now bounded (180 s to settle,
60 s for the cancel) and a timeout fails with the page's report and the
script's output instead of hanging, so a recurrence says where it stopped.

Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-09 23:14:22 -04:00
Mihawk 213a4321f7 WebUI: guided Settings for the 18 newer cores
tools/generate-core-metadata.py now extracts the option tables of a5200, the
Beetle NeoPop/PCE/PC-FX/VB/WonderSwan cores, DOSBox-pure, Flycast, Handy,
NeoCD, Opera, PicoDrive, PokeMini, ProSystem, PUAE, ScummVM, Stella and
Virtual Jaguar: table names are detected, v1 tables are dumped
(dump-options.cpp), DOSBox's namespace tables, NeoCD's runtime builder and
Stella's legacy variables are handled. The 15 existing catalogs are
byte-identical. build-title.sh fails when a shipped core (rpcs3 aside) has no
catalog.

Evidence: build/webui-core-metadata/index.cfg lists all 33 cores; the WebUI
in a browser lists 33 profiles and Beetle PCE shows Video, Audio, Input,
Emulation Hacks, PC Engine CD and General.
Gates: bash tools/verify.sh -> verify: PASS (format unit build integration evidence)
2026-10-09 23:14:22 -04:00
Mihawk 33d61f4ddf ES-DE: its ROM folder is the title's content folder, from the first start (issue 33)
The owner's console had ROMDirectory "" in es_settings.xml, which ES-DE reads as ~/ROMs,
/app0/es-de/ROMs: its no-games screen pointed there. And the adapter only set its
folders once ES-DE had written a settings file, so a first start never had them.

use_title_folders, before each start: ROMDirectory = /app0/content (an empty value or
ES-DE's ~/ROMs default replaced, a folder the user chose kept) and MediaDirectory = the
shared library; with no settings file yet, one with just these is written, which
ES-DE's Settings::loadFile reads and completes when it saves.

Evidence: evidence/issue-33-system-folders (the owner's first ES-DE start with its
settings set aside: ROMDirectory=/app0/content, MediaDirectory=/app0/library, 36 systems
and 3461 games; kept after ES-DE's exit; the owner's settings then restored with only
ROMDirectory changed); tests/esde_library_test.cpp (first start, ES-DE's default
replaced, the user's folder kept). tools/verify.sh: PASS (format unit build
integration evidence).
2026-10-09 22:29:13 -04:00
Mihawk 6fa72d46e7 Content: a folder for every system the installed cores run
content/ had only the folders people made (and a bare "Saturn"), so a fresh install
gave no hint where games go (issue 33). ps5_library_make_system_folders makes
content/<system> for each system the installed cores' databases name, with RetroArch's
names ("Nintendo - Nintendo Entertainment System"), which every frontend's library
reads as that system. A system with a folder already, under any name ("PS1",
"Genesis"), gets none: nothing is renamed or moved. A database no platform stands for
(Satellaview, Sufami Turbo, PICO, CD32, CDTV) gets none; variants go to their system
(Wii (Digital) -> Nintendo - Wii).

- src/main.cpp runs it at each title start, before the frontend dispatch (a home-screen
  launch goes to the picker from there; RetroArch's own init is too late), so a core
  added later gets its folder.
- tools/system-folders.c runs the same code on the staged title in build-title.sh:
  dist/PPSA99169/content holds the 46 folders of the 33 cores; video-assets.py keeps
  empty folders in the release ZIP.
- The bare content/Saturn the title made is dropped (Sega - Saturn replaces it).

Evidence: evidence/issue-33-system-folders (console: 45 folders -> 52 after a home-screen
restart, the 7 missing systems added 0777, the owner's kept; dist and the ZIP: 46);
tests/ps5_library_test.c (10 made, an existing folder honoured, unknown databases and
cores without .so skipped, a second run makes none). tools/verify.sh: PASS (format unit
build integration evidence).
2026-10-09 22:29:13 -04:00
Mihawk aa6a35e555 tests: wait for the game overlay's media rows instead of racing them
test_games_page_in_a_browser failed once in a full tools/verify.sh run ("downloaded
artwork is visible without reloading") and passed three times alone. The overlay's
title appears first and its media rows come from a second request; the assertion
counted the rows the moment the title showed. It now waits up to 10 s for the first
present row, then asserts as before.

Evidence: the test alone 3/3 before the change; tools/verify.sh: PASS (format unit
build integration evidence) after it.
2026-10-09 22:29:13 -04:00
Mihawk bb709b5bfb Verify completion of all beta release core publications
Read back all 26 public repositories, exact commits and main revisions. All deferred repositories preserve GitHub fork relationships. Release status and single ZIP digest unchanged.

Validation: bash tools/verify.sh format evidence passed; 119 captures, zero failures. Artifact: evidence/release-beta-publication/capture.json.
2026-10-08 14:36:58 -04:00
Mihawk 530a7843d2 Record beta.1 release publication and verified package
Published normal Latest release with exactly PS5_RetroArch-v1.0.0-beta1.zip. Downloaded SHA-256 and extracted notices/assets match. Owner requested remaining formal forks finish through watch job.

Validation: full verify.sh passed 185 unit and 185 integration tests; format/evidence replay passed 119 captures. Artifact: evidence/release-beta-publication/capture.json.
2026-10-08 14:21:37 -04:00
Mihawk 3e5c369647 Finalize approved beta release notes and download links
Owner approved optional opening warning in release notes; README warnings remain intact. Single-ZIP release points to public component revisions and the source tag.

Validation: git diff --check; bash tools/verify.sh format evidence (118 captures, zero failures). Artifacts: evidence/readme-galaxy, evidence/release-beta-illustrated-notes.
2026-10-08 13:43:15 -04:00
Mihawk 55e6878f42 Apply galaxy artwork to README banners and screenshot frames
Use the approved banner, four subtle section accents and self-contained SVG frames preserving original screenshot bytes. Keep all README notices, headings, system icons and renderer rows.

Verification: python3 tools/make-readme-frames.py; source and XML checks; browser loads 74 images without overflow; bash tools/verify.sh format evidence passes with 118 captures. Evidence: evidence/readme-galaxy. No new dependencies. Release-note changes excluded.
2026-10-08 13:26:36 -04:00
Mihawk 836f901fde Showcase frontend picker and restore README renderer column
Move the visible frontend section above At a glance, remove four requested subsections, and restore all 33 renderer entries. README warnings preserved. Validation: format/evidence passed with 117 captures and local preview reviewed. Release-note removal excluded from this commit after approval review rejection.
2026-10-08 13:16:08 -04:00
Mihawk a84965777b Refresh README for beta features with colorful system artwork
Replace the Overview hero with the Games capture, document current library/media/save and QR workflows, group 33 cores with system icons, and retain every permanent warning. Keep source-build status and known limits explicit.

Verification: bash tools/verify.sh format evidence — PASS, 116 captures. Source audit: all local links resolve and 33 cores retained. Local browser: 69 images loaded, no page overflow. Evidence: evidence/readme-beta-refresh. No publication.
2026-10-08 13:08:53 -04:00
Mihawk 3edf29f3ab Polish beta release notes with concise captions and system artwork
Keep all 18 core entries, ten screenshots and permanent warnings. Add illustrated highlights, linked contents and grouped system artwork; remove development internals and shorten public descriptions.

Validation: bash tools/verify.sh format evidence — PASS, 115 captures. Browser: 57 images loaded, no horizontal overflow. Evidence: evidence/release-beta-illustrated-notes/{capture,expectation}.json. Local preview only; no publication.
2026-10-08 12:59:21 -04:00
Mihawk b0904bc11e Enable automatic media downloads for new game drafts
Keep opt-out available and restore the enabled default on each new draft. Browser coverage verifies source visibility, labels, manual opt-out and the default download flow.

Validation: tools/verify.sh format unit build integration evidence; 185 unit and 185 integration tests pass. Build fd8f648f deployed; defaults and config preservation verified. Evidence: evidence/webui-auto-media-default/capture.json. Replay: tools/verify.sh evidence.
2026-10-08 12:53:14 -04:00
Mihawk 7301a9e92e Collapse game identities in the release download recap
Recapture Download Media with Nintendo - Game Boy Advance collapsed, hiding the game name and match. Validate updated screenshot in the local release preview. tools/verify.sh format evidence passes (113 captures). Evidence: evidence/webui-release-collapsed-recap; replay: tools/verify.sh evidence.
2026-10-08 12:33:28 -04:00
Mihawk eda2a170e7 Use Bust-A-Move for release game inspector screenshots
Replace four captures and their captions/catalog metadata. Validation: tools/verify.sh format evidence; 112 captures pass, all 15 preview images load. Evidence: evidence/webui-release-bust-a-move/capture.json. Replay: tools/verify.sh evidence.
2026-10-08 12:31:22 -04:00
Mihawk 0d4c8bfa91 Illustrate beta release notes with ten WebUI screenshots
Capture the requested Neo Geo B library, Add Game, game inspector and Download Media views from the console WebUI, with explanations and a screenshot digest catalog. Preserve the permanent warning and local QR preview.

Validation: tools/verify.sh format evidence; 111 evidence captures pass. All 15 local preview images load. Evidence: evidence/webui-release-gallery/capture.json; replay: tools/verify.sh evidence.
2026-10-08 12:26:47 -04:00
Mihawk 1e7edf55db Render media symbols inline to honor the WebUI image policy
Fix the blocked 3D box and disc icons without changing the CSP. Check all ten media selectors decode/render in the production-server browser regression.

Validation: tools/verify.sh format unit build integration evidence; 185 unit and 185 integration tests pass. Build 8135742c deployed at Picker; all ten icons visually verified; config hashes unchanged. Evidence: evidence/webui-media-icons/capture.json. Replay: python3 tools/evidence.py compare evidence (110 captures pass).
2026-10-08 11:47:20 -04:00
Mihawk c71bfa8adc Add game setup, live media progress and colorful WebUI artwork
Add a Media Inspector-based workflow for new backups, editable metadata,
LaunchBox lookup and manual/automatic media. Keep users in a live job view
until completion, then open freshly loaded artwork. Preserve prior metadata
edits and rank literal database titles above enhanced editions and variants.

Use the approved color icons, real region flags and hardware artwork for all
50 recognized systems. Restore RetroArch branding and bundle attribution.
Preview native savestate screenshots and invalidate them on replacement.

Validation: PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE="$PWD/build/picker-template"
bash tools/verify.sh format unit build integration evidence
185 unit and 185 integration tests; all shipped targets and evidence pass.
Base PS5: seven disposable-file/API checks, asset byte comparisons and
configuration preservation pass; test files removed, Picker restored.

Evidence: evidence/webui-add-game/capture.json
Replay: python3 tools/evidence.py compare evidence
2026-10-08 11:28:14 -04:00
Mihawk 28bfcc99b1 Add Media Inspector game overlay and native file transfers
Provide editable protected metadata, ROM Replace/Export and native save/savestate Import/Export with slots. Resolve RetroArch application-relative save paths; require exact library entries and confirmed atomic replacement.

Validation: PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE=build/picker-template bash tools/verify.sh format unit build integration evidence (183 unit, 183 integration); python3 tools/evidence.py compare evidence/ (108 captures).

Evidence: evidence/webui-game-overlay/{capture,expectation}.json. Deployed build 6717f521: live HTTP assets match staged, disposable console ROM/save/state round trips pass, all probes removed, config hashes unchanged. Desktop/phone and live-console UI reviewed. Native byte transfers only; private core databases and format conversion excluded.
2026-10-08 09:20:54 -04:00
Mihawk 6b7bc181d9 Record console captures of all four WebUI QR overlays
Verified actual Picker, XMB, RGUI, and ES-DE captures with zbarimg and live WebUI HTTP 200. Original Picker and configurations restored. Raw images stay ignored; sanitized hashes and procedure in evidence/webui-qr-console-captures. tools/verify.sh format evidence: 107 captures pass.
2026-10-08 08:12:55 -04:00
Mihawk afbd69eb72 Add ShadowMountPlus compatibility warning to beta release notes
Verified with tools/verify.sh format evidence (106 captures); evidence/release-note-beta1 records the updated Markdown digest. Local browser preview confirms yellow warning and linked 1.7beta4 release. Permanent warning diff audit passes.
2026-10-08 08:00:29 -04:00
Mihawk 0b6be46c51 Draft v1.0.0-beta.1 release notes
Present the 18 added cores, Games library, media downloads, shared artwork,
storage support, stylus controls and fixes since v0.6.7-alpha.6. Preserve the
README warning verbatim and keep paragraphs on single lines for release rendering.
Apply the owner's preview edits: omit the Beta notes section, overview image
and its caption. No release, tag or package is published.

Verification: tools/verify.sh format evidence (106 captures, zero failures).
Rendered Markdown inspected in the local browser: four tables, warning image
loaded, no horizontal overflow, requested removals confirmed.
Evidence: evidence/release-note-beta1/{capture,expectation}.json.
2026-10-08 07:48:25 -04:00
Mihawk ab4666cc5f Match Azahar's stylus to DeSmuME
Replace Azahar's inverted cursor in Vulkan and software rendering with the
same supersampled white pen, dark outline and lower-left nib as DeSmuME.
Keep clipping anchored to the touch point and preserve the input behavior.

Verify: tools/verify.sh format
PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE="$PWD/build/picker-template" tools/verify.sh unit build integration evidence
182 unit and 182 integration tests pass; native core ABI/provenance pass.
The parity regression fails on the old cursor and passes at 1x, 5x and 18x.

Evidence: evidence/azahar-stylus-parity/{capture,expectation}.json
Fresh base-PS5 launch draws the stylus in both captured frames and exits
cleanly; stored eboot and DS/3DS core hashes match the staged package.
105 evidence records replay successfully. Core-switch crash is unresolved.
2026-10-07 20:25:33 -04:00
Mihawk 63e11a3db0 Polish DeSmuME stylus and fix signed Azahar mouse motion
Rasterize a slim outlined, antialiased pen at the DS output resolution.
Cast Azahar's unsigned screen dimensions before multiplying signed deltas;
the production-type regression fails before this change and passes after it.

Verification: tools/verify.sh format; PS5_FRONTENDS='es-de picker'
PS5_VULKAN_TEMPLATE="$PWD/build/picker-template" tools/verify.sh unit build
integration evidence; tools/verify.sh format evidence. 182 unit and 182
integration tests passed, 104 evidence records replayed.

Evidence: evidence/stylus-polish/{capture,expectation}.json records native
core provenance, stored console SHA-256 readback, and clean production
fresh Azahar (600 frames) and DS Close Content -> Load Content -> Azahar
runs on build 9917ddb4. Reproduce host checks with tools/verify.sh and
recorded console commands with the ignored diagnostic fixtures.

The original owner load crash is unresolved, not claimed fixed. The
active-content scripted RELOAD fault was an invalid reproduction because
it unloaded the core from inside its input callback. Diagnostic handler
changes are excluded from this commit and the deployed production build.
2026-10-07 19:59:02 -04:00
Mihawk 04cd031dc0 Add DualSense touchpad stylus control to DS and 3DS cores
Adapt Proton contact tracking for relative hover and physical press/drag/release.
Keep touchpad Select in menus and other cores. Add resolution-scaled mouse
motion and clipped stylus cursors to DeSmuME and Azahar (Vulkan/software),
with reproducible core patches, build provenance and licence attribution.

Verified: format; 182 host/integration tests; all shipped native targets;
core ABI, manifest and licence checks; 103 evidence captures replayed.
Deployed build 6da657c4 to the base PS5; stored SHA-256 of eboot.bin and both
cores matches the stage. Physical controller/TV acceptance remains pending.

Reproduce:
PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE="$PWD/build/picker-template" tools/verify.sh
python3 -m unittest tests.test_input_ps5 tests.test_touchpad_stylus

Evidence: evidence/touchpad-stylus/capture.json and expectation.json.
Raw logs: klog/touchpad-stylus/ (ignored).
2026-10-07 19:10:53 -04:00
Mihawk 3de11ab36e Fix RetroArch WebUI QR action registration
Bind WebUI in the common action initializer instead of the custom keyboard
branch. Preserve patch markers so existing configured trees update too.
Production callback/dispatch regression fails before the fix and passes
after it; picker and ES-DE binaries remain byte-identical.

Resolve the integration fixture race exposed by this run: browser covers
now use valid PNG data and assertions require decoded images.

Validation: PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE="$PWD/build/picker-template" tools/verify.sh
Format, unit (177 tests), and build passed. After correcting the fixture,
tools/verify.sh format integration evidence passed (177 tests).
Evidence: evidence/webui-qr-actions/capture.json and expectation.json;
reproduce with python3 tools/evidence.py compare evidence/

Deployed build 0bdc8dd8: 6 changed files verified; exact stored-byte eboot
digest matches. Owner confirmed picker/ES-DE; XMB/RGUI live recheck pending.
2026-10-07 18:33:46 -04:00
Mihawk bf859b0d77 Add runtime WebUI QR access to every frontend
Picker Triangle toggles and Circle dismisses; XMB gets a WebUI tab left of
Settings; RGUI shows WebUI first; ES-DE replaces Scraper with WebUI.
Share runtime IPv4 discovery and an unchanged MIT Nayuki QR encoder.

Verify: PS5_FRONTENDS='es-de picker' PS5_VULKAN_TEMPLATE="$PWD/build/picker-template" tools/verify.sh
All five gates pass; 176 tests in each pass. Independent QR decoding,
address changes/offline recovery, picker input and menu routing tested.
Evidence: evidence/webui-qr/capture.json and expectation.json; replay with
python3 tools/evidence.py compare evidence/

Deployed build 700259e0: 9 changed files verified, 13614 retained. Exact
stored-byte digests match all three frontend binaries. On-console scans
and controller checks remain pending; no title launch performed.
2026-10-07 17:59:40 -04:00
Mihawk 6c75f742da evidence: record Games sorting console deployment
python3 tools/deploy-title.py verified 9 changed files and skipped 11689
unchanged files using the prior console deployment record. A separate
SELF-disabled FTP readback verifies the exact stored eboot SHA-256; live
HTTP page assets match the staged files. The old daemon is still running,
so metadata sorting on the restarted server is not yet claimed.

Artifact: evidence/webui-games-sorting/capture.json and expectation.json.
Verification: bash tools/check-manifest.sh; tools/verify.sh evidence
(100 captures, zero failures). Raw captures remain in ignored
klog/webui-games-sorting/deploy/.
2026-10-07 17:25:25 -04:00
Mihawk 7907ccb32a webui: add Games letter filters and sorting
Add All/#/A-Z buttons, remembered sort field and direction, and global
sorting before pagination. Expose stored release date, genre, developer,
publisher and rating in the library response; missing details stay last.

Verified: tools/verify.sh format; tools/verify.sh unit build integration
 evidence. All five gates pass: 169 tests per pass, PS5 title and daemon
 built, staged manifest checked, 100 evidence captures replayed.
Final format/evidence rerun also passes.

Reproduce feature checks:
python3 -m unittest -v tests.test_scraper.Scraper.test_library_sort_details tests.test_scraper.Scraper.test_games_page_in_a_browser
Artifacts: evidence/webui-games-sorting/capture.json and expectation.json
(host results and staged artifact hashes). Console deployment/live check
remain pending; no console verification is claimed.
2026-10-07 17:19:38 -04:00
Mihawk 11847f6beb Download Media: the job card shows each pass of the funnel
The owner saw a job "only download after all three passes". The job record says
otherwise: the NeoGeo games already had nearly every picture and their details; what
was missing were manuals and videos, which LaunchBox and libretro do not have. Their
passes checked all 204 games in ~10 s each and found nothing to give; ScreenScraper (one
game at a time on this account, level 1) has been downloading since, in real time (78
files, 70 MB in 3 min). But the card hid it: a game handed to the next source counts as
pending, so nothing moved during a pass.

- Job.passes (kept in the job file): per pass, the games its source checked, those it
  gave something, and how many kinds; /api/scraper/job gives them with the games that
  reached each source.
- The card: one step a source (done, running with "checked X of Y" and a bar, or
  waiting), and what it found.

Evidence: tests/test_scraper_screenscraper.py (the chain's passes: libretro 5 checked,
3 games, 6 kinds; ScreenScraper 5, 3, 6); a screenshot of the steps. Console: the job
record and the monitor's log above. tools/verify.sh: PASS (format unit build integration
evidence).
2026-10-07 16:42:47 -04:00
Mihawk b294a48d09 WebUI daemon: answer the title while the WebUI starts; the title asks again
On the console the daemon stopped minutes after every start, the page then refused to
connect, and the status said RetroArch was closed while the picker ran. Its log
(webui-daemon.txt): "serving the WebUI" 6 s (9 s the time before) after its start,
then "linked: picker", "unlinked: title", "unlinked: picker", and 20 s later "no program
of the title linked: the title is closed". The WebUI's start (reading the library, once
for each of 16 saved scraping jobs) ran on the link loop: the picker's hello waited past
its 5 s, the picker gave up for good, and the daemon, linked to nothing, stopped.

- The daemon starts the WebUI on a thread of its own (4 MiB stack, joined before a
  stop): hellos are answered meanwhile.
- The saved jobs are read against one scan of the library, not one each.
- src/webui_link.cpp: a hello not answered is asked again every second, never given up
  while the daemon is there.

Evidence: tests/test_webui_daemon.py test_hellos_are_answered_while_the_webui_starts (a
start made 7 s slow: both hellos answered in under 1.5 s, the WebUI up later with the
right frontend). tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 16:22:17 -04:00
Mihawk cdc2b0716c curl for the PS5: mbedTLS built thread-safe (the WebUI daemon crashed)
The daemon died at start on the console (SIGSEGV, fault address 0) and never came up:
the backtrace (klog, symbolized against build/webui-daemon/ps5-retroarch-webui.elf)
runs from a scraper worker's download through curl_easy_perform into
mbedtls_ssl_write_client_hello -> mbedtls_ctr_drbg_random_with_add ->
mbedtls_entropy_func -> mbedtls_sha512_update. curl 8.22 shares one CTR-DRBG (prediction
resistance on) among all its connections and relies on mbedTLS's own locks for it; our
mbedTLS 3.6.7 was built without them. With 32 workers (a034b6e) starting handshakes
together the shared generator was torn; the job resumed by itself at every start, so
every start crashed.

tools/build-curl.sh now sets MBEDTLS_THREADING_C and MBEDTLS_THREADING_PTHREAD
(scripts/config.py) before building mbedTLS. Why a flag in a shared script: curl's
mbedTLS backend requires it whenever handles run on several threads; the ES-DE build
that links this curl gains the same locks.

Evidence: libmbedcrypto.a's ctr_drbg.o now calls mbedtls_mutex_lock (pthread mutexes in
threading.o); the daemon's mbedtls_ctr_drbg_random calls the lock before and after its
work (objdump). tools/verify.sh: PASS (format unit build integration evidence). The
console run follows the deploy.
2026-10-07 16:04:38 -04:00
Mihawk e251eedb83 Scraper: a game held by a worker is never finished or taken by another
Two races of the funnel (a034b6e), found by repeated test runs:
- Between a source's result and the funnel moving the game to the next pass, the game
  looked settled; another worker could end the job then, and the game stayed pending.
- A worker clearing its hold after the funnel queued the game could clear the hold of
  the worker that had already taken it on.
Each worker marks the games it holds with its own number (Item.holder, not saved); a pass
or the job ends only when no game is held; a worker frees only its own hold; a held game
is never taken by another worker nor reset by Resume.

Evidence: the three scraper suites (tests/test_scraper*.py) passed five runs in a row
(failed 1 in 3 before). tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 15:47:05 -04:00
Mihawk 0c7dc390d4 WebUI: transparent media shown transparent
No checkerboard and no dark stage behind pictures: a logo, a 3D box or a disc shows the
page through its transparent parts, in the game overlay, the full-size view and the
Games tab's cards (all four views). Only a video keeps a black backdrop.

Evidence: screenshots with real transparent PNGs (3D box in the overlay, logo on a card)
checked by eye; tests/webui_scraper_browser.cjs unchanged and passing. tools/verify.sh:
PASS (format unit build integration evidence).
2026-10-07 15:47:05 -04:00
Mihawk a034b6e328 Download Media: sources as a funnel of passes, and 32 downloads at once
- The owner's design: the first source goes through every game at full speed, as if no
  other came after it; when its pass is over, the next source gets only the games still
  missing something, for what they miss; and so on. Before, each game went through every
  source in turn, and ScreenScraper's one-game account limit held the whole job to one
  game at a time.
- Job.pass (the running source) and Item.pass (the source a game waits for), kept in the
  job file; a pass ends when none of its games is pending or in flight. A choice in
  "Games that need you" runs its source again, then those below; Resume sends a failed
  game back to the top (what it has is kept).
- ScreenScraper's thread limit is a slot around its own work only; a game that gets its
  slot after a pause asks nothing.
- 32 workers on the PS5 (was 4); the PC helper downloads 16 at once (was 4). libretro's
  game list of a system is fetched once, not once a worker.
- Fixed a race the extra workers exposed: Resume turned games still held by a worker
  back to pending, so a second worker took them too (a video downloaded twice).
- The page: the job says which pass runs and how many games wait for the next source;
  the source hint describes the funnel.

Evidence: tests/test_scraper_screenscraper.py (libretro's pass ends before ScreenScraper
is asked about any game; with a one-game account libretro runs several at once and
ScreenScraper one; a quota pause mid-funnel resumes with nothing fetched twice);
tests/test_scraper.py (cancel, resume and restart with 32 workers). The three scraper
suites passed three runs in a row. tools/verify.sh: PASS (format unit build integration
evidence).
2026-10-07 15:13:06 -04:00
Mihawk 2152188814 Scraping: LaunchBox Games Database as a source (no account)
- LaunchBox has no API: its database is one download (Metadata.zip, ~110 MB). The
  console fetches it once a month, streams Metadata.xml and Mame.xml out of the zip
  (minizip, already in the daemon) and keeps a small index a platform in
  library/.launchbox (games, alternate names, images of the types used); the zip is
  deleted. Images come from its public image server: no credential in any address, so
  PC mode works too.
- Matching: a game chosen by the user, else its title or an alternate name (words as
  libretro's matching), else the closest names ("Name [id]"), the same title taken
  unasked; arcade romsets ("mslug3") by MAME's name. 80 library systems mapped to
  LaunchBox platforms (arcade systems to Arcade, Neo Geo MVS/AES...).
- Media: front, back and 3D box, gameplay and title screenshots, clear logo, disc or
  cartridge, fan art, in the region asked for; details: description, developer,
  publisher, release date, genres, players, rating (0-1).
- It joins the source chain; a failed database download pauses the job (one message,
  the other workers wait for the resume).

Evidence: tests/test_scraper_launchbox.py against a fake LaunchBox (titles, an alternate
name, a romset, regions, details, an ambiguous game and a search, the database fetched
once and no image twice, PC mode through the helper, a libretro -> LaunchBox chain, a
failed download paused then resumed). Against the real database on the host: index in
6 s (140 MB, 79 platforms); 143 of 150 FBNeo romsets of the base console matched.
tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 14:19:58 -04:00
Mihawk 9329d46910 Alerts: every installed core, and what each really needs
- The check covered only the 15 cores with option catalogs (webui/core-metadata/
  index.cfg); the 18 added since (3DO, Neo Geo CD, Atari, PC Engine, PC-FX, Amiga,
  Dreamcast...) were never looked at. It now lists cores/*_libretro.so and reads each
  core's .info (its display name, its library name for the per-core system folder).
- Rules where the .info files are wrong: PS1 and Saturn need one BIOS of any region,
  not all of them; 3DO and Neo Geo CD need one of their BIOS files (and Neo Geo CD its
  LO ROM) though the .info calls all optional; PC-FX needs its BIOS; PC Engine CD games,
  Amiga Kickstarts and the Dreamcast BIOS are needed only for some games.
- The page: "Needed" (counted in the badge) and "Only for some games" apart; each item
  says why, lists the files any one of which will do, and the folder they go in.

Evidence: tests/test_webui.py test_alerts_follow_installed_cores_and_custom_system_folder
(Saturn one-of, a custom system folder, a core without a catalog checked by its rule,
PUAE as some games, an unknown core by its .info); a preview of all 66 built cores with
no BIOS listed what each needs. tools/verify.sh: PASS (format unit build integration
evidence).
2026-10-07 13:58:15 -04:00
Mihawk b7058c44ca WebUI: say when the page is newer than the server running it
The daemon outlives a deploy or an update: the console served new page files from an
older server (Game details greyed out, no source chain: the server reported neither).
The server keeps the page build it started with (webui/version.json) and reports it
in /api/status; the page compares it with the version.json now on disk and, when they
differ, asks to close and reopen PS5 RetroArch. A source an older server describes
without "details" counts as having them unless it is libretro.

Evidence: tests/test_scraper.py test_the_server_reports_the_page_build_it_started_with;
tests/webui_scraper_browser.cjs (the notice with newer page files). tools/verify.sh:
PASS (format unit build integration evidence).
2026-10-07 13:47:27 -04:00
Mihawk 3d1c29651b Download Media: combine sources in an order, each asked only for what is missing
- Options.sources (sources=libretro,screenscraper): per game, the first source is
  asked for every kind and the details; each next one only for what is still missing,
  so a free source spends nothing of a limited one's quota. Each source keeps its own
  name for the game (Item.matches, kept in the job file). A game no source could tell
  offers the first ambiguous source's choices; once chosen, a later source can offer
  its own for what is left ("Some found; for the rest, ScreenScraper offers these").
  A quota pause mid-chain resumes without asking again for what was found.
- A chain downloads on the PS5 (one source at a time through a PC); the order is
  remembered with the settings; ScreenScraper's thread limit applies whenever it is in.
- The page: "Sources, in order": each source a row with its number, a switch (or Sign
  in), arrows and drag to reorder; the hint says who is asked first; media tiles say
  "Asked of libretro, then ScreenScraper"; the job and recap name the chain.

Evidence: tests/test_scraper_screenscraper.py: a chain where ScreenScraper is asked
only for videos (no picture), resolved through both sources' choices; a quota pause
with no cover fetched twice after the resume; PC refused for a chain; the order
remembered. tests/webui_screenscraper_browser.cjs: sign in joins the chain, reorder,
turn off, sign out. tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 13:29:21 -04:00
Mihawk cacd9d697e Games: large media previews with a playable video, and a choice of picture per card
- The game overlay: each kind is a wide row with a large stage (the whole picture,
  never cropped; logos on a checkerboard), a click opens it full size (Escape or a
  click closes); the video plays in place with a big play button and its controls;
  the manual opens as a PDF; missing kinds are short dashed rows to drop a file on.
- The Games tab: "Show" box art (front), 3D box, logo, or cartridge or disc on the
  cards, remembered in this browser; a game without that picture shows its drawing.

Evidence: tests/webui_scraper_browser.cjs (the rows, the view choice kept across a
reload, games without a 3D box drawn as such); screenshots with real PNGs and an MP4
checked by eye. tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 13:11:40 -04:00
Mihawk 1d9172fc10 Download Media: Game details as their own choice, beside the media
- Options.details (details=1): a job fetches the games' details (description,
  developer, publisher, release date, genre, players, rating) only when asked; media
  alone, details alone, or both. Details follow "Media you have": filled when missing,
  replaced only when replacing everything, and a hand-edited field never. Remembered
  with the settings; kept in the job file.
- libretro has pictures only: its tile says so, a details-only libretro job is refused
  with the reason; each source says whether it has details.
- The recap counts details like a kind (new / kept / missing).
- The page: a "Game details" tile under the media tiles; the summary names it.

Evidence: tests/test_scraper_screenscraper.py test_details_alone_or_media_alone (media
without details, details without media and no media fetched, kept on a second run, an
edited description kept when replacing, neither refused) and the recap totals;
tests/test_scraper.py test_libretro_has_no_details; the browser test's tile.
tools/verify.sh: PASS (format unit build integration evidence).
2026-10-07 13:01:28 -04:00
Mihawk 42d0dd16a9 Games: a card opens the game's media, each kind replaceable by the user's own file
- GET /api/library/game: a game's details and, for each of the ten kinds of media,
  whether it is stored (type, size, uploaded) and the types it takes.
- PUT /api/library/media: the user's file, streamed to a hidden file beside its final
  name and renamed in place when whole (token required; PNG/JPEG for pictures, MP4/WebM
  for video, PDF for manuals: what every frontend shows). It becomes the kind's only
  file and is marked in the .meta ("uploaded"): no scrape replaces it, even when
  replacing everything.
- The page: clicking a card opens an overlay with the game's details, then one row per
  kind (a preview, a description, its file); missing kinds greyed; Replace / Add, or a
  file dropped on the row, with progress. The card's tags name every kind properly.

Evidence: tests/test_scraper.py test_a_game_shows_its_media_and_takes_the_users_own
(refused types, unknown game and kind, a path outside the library, no token; the
user's cover replaces the scraped one and survives a replace-everything scrape);
tests/webui_scraper_browser.cjs (overlay rows, greyed missing kinds, a refused GIF,
an uploaded PNG shown at once, the checkbox still selecting). tools/verify.sh: PASS
(format unit build integration evidence).
2026-10-07 12:48:50 -04:00
Mihawk e357d7b829 Download Media: a recap of what each job did, and a plain choice for media you have
- Each game records the kinds this job stored (kept in the job file). GET
  /api/scraper/recap lists, per system and game, the kinds downloaded, already there
  and missing, with totals per kind.
- The finished job card shows it: per-kind tiles (new / kept / missing, a bar; a click
  narrows the list), filters (got new media, missing something, not found, unchanged),
  a search, and a collapsible list per system with each game's kinds as pills and why
  a game was not found.
- "Replace what's already there" becomes "Media you have": "Download only what's
  missing" (default) or "Replace everything", each explained, and named in the summary.

Evidence: tests/test_scraper_screenscraper.py (recap totals and per-game kinds, a second
job telling kept from new), tests/webui_scraper_browser.cjs (recap tiles, pills,
filters, search; the media-you-have hints). tools/verify.sh: PASS (format unit build
integration evidence).
2026-10-07 12:36:29 -04:00
Mihawk cfad33bc2e Scraping: ScreenScraper as a source, with a sign-in that stays on the console
- Sign-in: clicking ScreenScraper opens a login form. The name and password go to the
  console in a request body, are checked with ScreenScraper (ssuserInfos), then kept in
  config/private/screenscraper.cfg (0600, folder 0700, out of the 0777 repair, never
  served). No answer carries the password. A green check on the chip when signed in;
  Manage shows the quota, switches account or signs out.
- Engine: jeuInfos by CRC, file name and size, then jeuRecherche ("Name [id]"
  candidates; the same title taken unasked, others resolved from the page). Details in
  the language asked for (description, genre, developer, publisher, players, rating
  0-1, release date); media per kind and region; the account's maxthreads kept; 429
  waits; 401/403/423/426/430/431 pause the job with a message, resumable.
- The developer account: generated scrambled into build/screenscraper_dev.h from .env;
  releases carry it scrambled (owner's decision), PS5_SCREENSCRAPER_EXCLUDE=1 leaves it
  out. https only (media links upgraded, no https->http redirects). PC mode is refused
  for ScreenScraper: its media links carry the developer account, which never leaves
  the console. lint-shell fails on a plain copy in any tracked or shipped file.
- ES-DE: release dates become YYYYMMDDT000000.
- Systems table generated from ES-DE's ScreenScraper ids (MIT notice kept).

Evidence: evidence/scraper-screenscraper (6 host tests against a fake ScreenScraper API,
fake accounts, plus the leak check); reproduce with
  python3 -m unittest -v tests.test_scraper_screenscraper; bash tools/lint-shell.sh
tools/verify.sh: PASS (format unit build integration evidence).
Not verified: a live sign-in and job against api.screenscraper.fr (owner's run).
2026-10-07 12:02:10 -04:00
Mihawk c36ac76809 WebUI: drawn icons for regions, devices and media sources
Flags for USA, Europe, Japan and Korea and a globe for World, on the region choice
(now flag chips, like the other choices) and in place of a game card's region tag; a
console icon for PS5 and a PC icon for This PC → PS5; a badge for each source:
RetroArch's own invader for libretro thumbnails, and original drawings for
ScreenScraper, LaunchBox and EmuMovies (not their owners' logos, which are theirs to
license). Inline SVG, no image requests; the console icon is a generic outline.
Checked against the console at 1440 px (the Download Media tab, a scraped SNES card).
Command: tools/verify.sh: all five PASS (149 tests).
2026-10-07 11:26:58 -04:00
Mihawk 9b7ae64d32 Download Media: its own tab, compact and interactive; RetroArch shows the art
The scrape panel left the Games page for a Download Media tab: "Download from" PS5
or This PC → PS5 (the PC choice says it downloads on the PC, then transfers to the
PS5), source chips, media tiles with descriptions on hover or focus, a Games scope,
region and a replace switch, a live summary and Start bar, animated job stats and the
games that need the user folded under one summary. The tab's state is declared before
the page runs (opened straight on #media it failed: library read before its let).

- Jobs keep their download counters across restarts; hidden files a dead writer left
  in the library are swept at start (older than ten minutes).
- run-title --menu-capture=N pictures the whole frame, menu included, at frame N.

Evidence: evidence/retroarch-shared-media (base PS5 13.40): the XMB History tab
captured at frame 900 with Donkey Kong Country 2's scraped box art drawn, and no
/app0/thumbnails folder on the console: the art is the shared library's. The tab
checked at 1440 px and 390 px against the console.
Command: tools/run-title.sh --pad-script=... --menu-capture=900; tools/verify.sh: all
five PASS (149 tests).
2026-10-07 11:17:57 -04:00
Mihawk ce06e66cb3 Scraping: libretro media into one library every frontend reads
The WebUI's Games page scrapes box art, screenshots, title screens and logos from
libretro's thumbnail server into /app0/library/<system>/<kind>/<game>, one store for
every frontend: RetroArch's thumbnail lookup asks it first (patch 0114, which also
gives thumbnails_directory "default" its folder: it was empty and stopped every
lookup), EmulationStation has it as its media folder and fills its game lists from
its .meta files, the Games page shows it, a future frontend calls ps5_library_media.

- src/scraper.{h,cpp}: jobs saved and resumed without repeats, exact names, exact
  titles tags aside, ranked candidates for the user, fill-missing by default; PS5 mode
  on the daemon (runs with the page and the title closed) and PC mode through
  webui/ps5-media-helper.py (the console identifies; the bytes pass through the PC).
- The page: the method asked first and remembered on the console, sources and every
  media type described, version tags on each card. ScreenScraper, LaunchBox and
  EmuMovies are listed as coming (ScreenScraper's developer credentials requested).
- The daemon's HTTP is libcurl over mbedTLS 3.6 with the shipped CA bundle (Sony's SSL
  failed every handshake from the payload, 0x8095f00c); its updater uses it too.
- The shared library: one path a game read from outside the title (14 games were
  listed twice), a disc's listed .bin dropped beside its .cue.
- The daemon: workers on 1 MiB stacks (a 64 KiB frame overflowed one), stale hand-over
  on transfers in flight rather than idle keep-alive connections, pid in its log.

Evidence: evidence/scraper-libretro (base PS5 13.40): every system in 148 s (520
complete, 28 partly, 181 kept, 61 ambiguous, 234 unmatched); cached runs fetch
nothing; HTTPS through curl; PC mode with the helper killed: 90 images each sent once;
a job finished with the title closed; 1,024 games, 0 duplicates. Not seen live:
RetroArch and ES-DE drawing the media (owner to look), updates through the daemon.
Command: build/tmp/scraper/live.py; tools/verify.sh: all five PASS.
2026-10-07 10:45:24 -04:00
Mihawk 882c02799f verify.sh: every command of a gate counts
Gates run inside an if, where bash ignores set -e, so a gate reported only its last
command: a failing test in make test-integration passed the integration gate because
check-notices.py, after it, passed (seen 2026-10-07 on a flaky scraper test). Each
command but the last now returns on failure, in the format and integration gates.
Command: tools/verify.sh: all five PASS, 148 tests OK in unit and in integration.
2026-10-07 10:45:23 -04:00
Mihawk 90aaa75abe WebUI: a parallel transfer engine (batches, upload sessions, ranges, six lanes)
Measured first: one libmicrohttpd thread with 64 KiB a connection held uploads to
13 MB/s, and four connections to 15.3, on a link where FTP reached 25 / 30.5 MB/s.
PS5-Suite's design was reviewed (persistent connection pool, small files per
connection, large files as pwrite ranges, big socket buffers) and an engine built
from scratch on the WebUI's HTTP (src/webui_transfer.{h,cpp}, docs/WEBUI.md):

- a pool of four server threads, 512 KiB a connection, 4 MiB socket buffers, writes
  in 1 MiB blocks; non-transfer routes under one lock as on the single thread;
- PUT /api/upload/batch: many small files in one request (a framed stream), each
  renamed into place when whole, existing names skipped and named unless replace;
- upload sessions (/api/upload/session, /part, /commit): a large file's ranges in
  any order on any number of connections, committed once covered;
- Range downloads (206); every upload still atomic, never over an existing name;
- the page plans batches, sessions and single uploads onto six lanes.

Evidence: evidence/webui-transfer-engine (base PS5 13.40, PC on Wi-Fi 6). Uploads
26.0 MB/s on one connection and 29.9 MB/s for one file in six parts (FTP: 25 /
30.5); 400 x 32 KiB at 215 files/s (FTP 105). The page against the console: 605
files in 20.1 s, all equal by FTP readback, duplicates refused, a cancelled 200 MiB
file left nothing. ThreadSanitizer under a mixed parallel load: no report.
Command: build/tmp/transfer/bench.py; node tests/webui_transfer_browser.cjs;
tools/verify.sh: all five PASS.
2026-10-07 08:23:21 -04:00
Mihawk 189b415505 Remaps: saved to and loaded from config/remaps, per game and per core
Remaps never worked: the config's input_remapping_directory "default" was taken
as a folder named "default" (upstream resets it for its other folders, not this
one) and the port set no default remap folder, so Save Game/Core Remap File
failed with default/<core>/<game>.rmp and no remap ever loaded. The PS5 frontend
now names /app0/config/remaps (created 0777), and patch 0113 resolves "default"
to it. Game overrides and game core options already saved and loaded.

Evidence: evidence/remaps-per-game (base PS5 13.40, Snes9x, DKC2, Quick Menu by
pad script). Before: "Error saving remap file.: default/Snes9x/Snes9x.rmp". After:
game remap saved and "Game-specific remap found" on the next launch; core remap
saved (input_player1_btn_b = "8") and "Core-specific remap found"; game overrides
and game core options found on every launch. Test files deleted afterwards.
Command: tools/run-title.sh --game=snes9x:<DKC2> --pad-script=build/tmp/remap/*.txt;
tools/verify.sh: all five PASS.
2026-10-07 07:41:51 -04:00
Mihawk d2d85c535c WebUI: its own daemon, up across every frontend; downloads read in blocks
The WebUI only ran inside RetroArch: a frontend change (LoadExec) ended it with
every upload in flight. It is now a payload of its own (daemon/webui_daemon.cpp,
webui/ps5-retroarch-webui.elf) serving the title's folder on 6769. eboot.bin, the
picker and ES-DE each keep a link to it (src/webui_link.h, 127.0.0.1:6770) and the
first to find none starts it through the ELF loader (9021) or websrv (8080 /hbldr).
It stops 20 s after the title closes, never mid-transfer; an update's Install
closes the title and the daemon installs. With no loader, eboot.bin serves the
WebUI while RetroArch runs, as before. The page names the frontend shown.

Downloads were handed to MHD as a descriptor (sendfile), which stopped at 33,300
bytes on the console; they are read in 256 KiB blocks.

Evidence: evidence/webui-daemon (base PS5 13.40). 200 MiB upload across picker ->
RetroArch -> picker and 60 MiB across picker -> ES-DE: one token throughout, 201
Created, every 256 KiB block equal on the console; daemon stopped 20 s after the
title closed; a 1,917,648,896-byte download's sha256 equals the FTP copy.
Command: tools/run-title.sh --picker-test=600:retroarch --retroarch-frames=1800
--watch 110 with build/tmp/webui-handover.py; tools/verify.sh: all five PASS.
Host tests: tests/test_webui_link.py, tests/test_webui_daemon.py.
2026-10-07 07:41:51 -04:00
Mihawk 4812ffdee0 FTP: everything in the title folder is 0777, and the repair finishes
Users reported FTP permission refusals again. Since v0.6.7 the title usually
hands itself over at once (to the picker or ES-DE, through LoadExec, which ends
the process), so the start-up repair that opens /app0 to FTP was cut short; and
the picker and ES-DE, other executables, wrote with their own modes.
- Files are 0777 as folders are (were 0666): the link-time wrappers and the
  repair (src/permissions_ps5.cpp).
- ps5_permissions_settle waits for the repair (at most a minute) before every
  handover: to a frontend, closing the title, the relaunch test.
- ES-DE's library files and folders, the remembered frontend
  (src/ps5_frontend_choice.h) and the picker's test record are 0777; the picker
  opens RADV's shader cache to FTP after its first 30 frames and again when it
  hands over.

Verified on the base PS5: the trace reads "permissions: /app0 checked=17992
repaired=174 refused=0" then "handover waited 596.2 ms"; after a home-screen
launch held at the picker and closed there, an FTP walk of
/data/homebrew/PPSA99169 finds 17992 entries, 0 not 0777 (before: 175 files
at 0666) (evidence/permissions-0777). tools/verify.sh format, unit and
evidence PASS.
2026-10-07 00:00:46 -04:00
Mihawk cc5ded25ff ES-DE's videos play: FFmpeg with asetnsamples (issue 27)
ES-DE's VideoFFmpegComponent ends its audio filter graph with asetnsamples,
which tools/build-ffmpeg-esde.sh did not enable. The graph failed
("setupAudioFilters(): Couldn't add graph filter: Filter not found") and
every game video stayed a black frame, with no sound, while images showed.

Verified on the base PS5 with a synthetic H.264/AAC clip as a game's video
(and a cover, since Alekfull NX shows its basic variant, with no video, for a
game with neither cover nor miximage): before, a black frame and the error;
after, the clip plays in real time, its burned-in clock advancing with the
captures (5.3, 9.3, 15.3 s at 10, 14, 20 s), sound playing, no filter error.
es-de.bin carries the filter. The owner's ES-DE settings were restored and the
test media removed. evidence/issues-25-27 (both issues); tools/verify.sh
format, unit and evidence PASS (92 captures).
2026-10-06 23:40:55 -04:00
Mihawk 6ad0d62cff EmulationStation lists the games in the content folders (issue 25)
ES-DE's library came from RetroArch's playlists alone, so a game copied into
content/ appeared only after a Manual Scan in RetroArch. ps5_library_load_content
now also scans content roots after the playlists (a playlist's entry still
wins): /app0/content and the USB and extended drives ShadowMountPlus mounts.
- Each folder at a root's top that names a platform the title has a core for
  is that system's; the platform table gains the systems the title now runs
  (3DO, DOS, ScummVM, Neo Geo CD, NGPC, WonderSwan Color, SuperGrafx, PC-FX,
  Virtual Boy, Pokemon Mini, Atari 5200 and Jaguar, NAOMI, Atomiswave), most
  specific first ("NeoGeo CD" is not the arcade Neo Geo).
- In it, every file a core of the platform takes; .zip and .7z too for a
  cartridge system (RetroArch opens them), not for a disc system (an archived
  disc's tracks are not extracted). Left out: firmware (named in a core's
  info, a variant of one, or [BIOS]/Kickstart/boot ROM), and the tracks of a
  folder that has a .cue, .gdi or .m3u.
- A DOS game is a folder started by the program named like it (doom/DOOM.EXE);
  a ScummVM game is a folder started from a data file in it.
- A path too long for the buffers is skipped, never truncated.
es-de.bin passes the roots (frontends/es-de/ps5/library_ps5.cpp).

Verified: tests/ps5_library_test.c (folder names, firmware, tracks, DOS,
ScummVM, a drive, archives by kind, no duplicates) and esde_library_test PASS.
On the base PS5: ES-DE lists 36 systems and 1025 games; E.T. (Fixed), found
only by the scan, starts in RetroArch (Stella) and returns to ES-DE
(evidence/issues-25-27, next commit). tools/verify.sh format and unit PASS.
2026-10-06 23:40:55 -04:00
Mihawk 8c6e8911ae Four cores: 3DO, Commodore Amiga, Neo Geo CD and ScummVM
Each from my local fork with a PS5 platform commit, through
tools/core-fork.sh:
- Opera (PS5_Opera, 3DO): FreeDO's modified LGPL forbids commercial use,
  so it joins the non-commercial parts (text from its sources' headers,
  tooling/notices/texts/opera-freedo.txt).
- PUAE (PS5_PUAE, Amiga): GPL-2.0-or-later; it boots Kickstart 1.3 from
  system/kick34005.A500, AROS without one.
- NeoCD (PS5_NeoCD, Neo Geo CD): LGPL-3.0-or-later; BIOS in system/neocd/.
- ScummVM (PS5_ScummVM: scummvm/scummvm v2026.3.0, every engine, no
  OpenGL): GPL-3.0-or-later. Its Makefile empties LDFLAGS, so the title's
  link flags go in as PS5_LDFLAGS (without them the wrapper linked
  libSceNet, which the core loader refuses); AR carries its flags (ar cru);
  its pinned libretro deps are kept across clean checkouts.
build-title.sh stages the four; notices list them; README: systems, forks,
credits, licences (thirty-three cores).

Verified on the base PS5 (13.40), runs 224859-230600: BattleSport (from
its .iso: the archive's .cue names a file it does not hold), 3D Pool,
Metal Slug and Day of the Tentacle load, run and exit with 0
(evidence/four-cores-2026-10-06). Disc images inside archives do not load:
RetroArch extracts only the .cue; unpacked into folders on the console
they do. tools/verify.sh format, unit and evidence PASS (91 captures).
Before a release: publish the four forks.
2026-10-06 23:09:36 -04:00
Mihawk 68a7986dce locale shims: the C locale's rune table and ___runetype, in every build
FreeBSD's <ctype.h> inlines isalpha() and the rest as a look-up in
__getCurrentRuneLocale()'s table below _CACHED_RUNES and calls ___runetype()
above it. The console's libc exports the classification functions but
neither of these, so ScummVM's core left the title's link with both
undefined. The table is built once from the exported isalpha, isdigit,
tolower and the rest. It sits outside the PS5_RETROARCH_RADV guard: the
platform layer that replaces the other shims in a RADV build has neither.

Verified: the file compiles with and without PS5_RETROARCH_RADV and defines
both; the title links with ScummVM, whose Day of the Tentacle runs on the
base PS5 (evidence/four-cores-2026-10-06, next commit). tools/verify.sh
format and unit PASS.
2026-10-06 23:09:36 -04:00
Mihawk 7414eba5cf Thirteen cores: 32X, PC Engine, PC-FX, Virtual Boy, Neo Geo Pocket, WonderSwan, Pokemon Mini, Lynx, Jaguar, 2600, 5200, 7800, MS-DOS
Each from my fork beside this repository (all local, not yet on GitHub),
upstream plus a PS5 platform commit (no --no-undefined, no LTO, zstd without
trace hooks), built through tools/core-fork.sh:
- PicoDrive (PS5_PicoDrive): the 32X SH-2s on the interpreter (the
  recompiler's static read-write-execute buffer faults on the PS5); frames
  go out as XRGB8888. Non-commercial licence, like Snes9x and FBNeo.
- Beetle PCE (mednafen_pce, with SuperGrafx and CD), Beetle PC-FX, Beetle
  VB, Beetle NeoPop, Beetle Cygne: GPL-2.0-or-later.
- PokeMini (GPL-3.0-or-later), Handy (zlib; its text is in the source
  headers, tooling/notices/texts/handy-zlib.txt), Virtual Jaguar
  (GPL-3.0-or-later), Stella 7.0c, a5200 (built-in Altirra BIOS; its
  Makefile empties LDFLAGS, so the link flags go in as PS5_LDFLAGS),
  ProSystem: GPL-2.0-or-later unless noted.
- DOSBox Pure 1.0-preview7 (now on Codeberg): the dynamic x86 core's code
  cache is ps5_exec_allocate memory, and stdio replaces the VFS wrapper,
  whose funopen the console lacks.
build-title.sh stages all thirteen; the notices list each; README: the
systems, forks, credits and licences (twenty-nine cores). On the console the
owner's BIOS files were moved where the cores look: system/pcfx.rom,
system/lynxboot.img, system/bios.min.

Verified on the base PS5 (13.40), one game per core with
tests/pad/boot-and-start.txt (runs 212433-220552): every core loads, runs at
its system's rate (Virtual Boy 50.3, Pokemon Mini 72, the rest 60 frames a
second) and exits with 0; pictures inspected
(evidence/new-cores-2026-10-06). tools/verify.sh format, unit and evidence
PASS (90 captures, 0 failed); build and integration not rerun this step.
Before a release: publish the thirteen forks the notices and README name.
2026-10-06 22:11:05 -04:00
Mihawk 809fb31163 Port patch 0112: core frames are BGRA8888 textures, RGB565 widened on upload
PicoDrive's RGB565 frames (320 pixels, rows of exactly 640 bytes, so the
upload's one-memcpy path) reached the screen half-height, doubled and
miscoloured; Beetle PCE's padded RGB565 rows and every 32-bit frame were
right. In gfx/drivers/vulkan.c the core frame texture is now always
B8G8R8A8, and a 16-bit frame is widened row by row as it is copied in. A
16-bit core is not lent the frontend's framebuffer (runloop.c's
GET_CURRENT_SOFTWARE_FRAMEBUFFER), since that texture is no longer its
format; the guard is in the runloop because withdrawn 0077 replaces
vulkan_get_current_sw_framebuffer while the edits apply.

Verified on the base PS5: PokeMini, Handy, Beetle NeoPop, Beetle Cygne,
a5200 and ProSystem, all RGB565, show correct pictures
(evidence/new-cores-2026-10-06, next commit). tests/test_frontend.py pins
the block count at 249 (three added). tools/verify.sh format and unit PASS.
2026-10-06 22:10:54 -04:00
Mihawk c0a75c51fa Flycast: Dreamcast, NAOMI, NAOMI 2 and Atomiswave, the sixteenth core
tools/build-flycast.sh builds Flycast's libretro core from my fork
../PS5_Flycast (b95c532f: upstream flyinghead/flycast 59ed35a7e plus five
commits) through tools/core-fork.sh, like the other forked cores:
- the PS5 libretro build: no --no-undefined, 16 KiB pages;
- the SH-4, ARM7 and AICA DSP recompilers' code in the console's executable
  memory (ps5_exec_allocate, near the core's code);
- anisotropic filtering takes the level chosen, not its index (upstream
  bug: "16" gave 4x, the default "4" gave 2x);
- PS5 defaults: 2880x2160, per-pixel transparency, 16x filtering; the
  widescreen hack stays off;
- no locale switching and no serial pseudo-terminal: the console exports
  neither uselocale/newlocale nor grantpt/unlockpt/ptsname, which the
  title's link refused.
The port began as rpf16rj's (pull request 3), credited in the fork's
commits; its networking (DCNet DNS and socket changes) and the
ps5_dc_rumble hook, which the title does not export, are left out.
tooling/flycast/ps5-toolchain.cmake is the cores' link contract with
libzip's Annex K and Windows probes answered no. The notices list Flycast
as GPL-2.0-or-later: its LICENSE is the GPLv2 text, and its file headers
say "version 2 or any later version", none "only". README: the system, its
resolution, the fork and the credit.

Verified on the base PS5 (13.40): runs 210509, 210719, 211230. The core
loads (core loader, Vulkan OIT renderer on RADV), Sonic Adventure (USA, Rev
A) boots through the HLE BIOS, plays its film, title screen and attract
demo at 2880x2160 with sound, and START reaches "Select a memory card";
RetroArch exits with 0 (evidence/flycast-sonic-adventure, pictures
klog/pad-210719 and klog/pad-211230). About 60 frames a second in the demo,
30 in the film (its own rate), early stalls of 0.5-1.4 s.
Gates: tools/verify.sh format, unit and evidence PASS (89 captures, 0
failed); build and integration wait for MAME's rebuild. Before a release:
publish github.com/mihawk-99/PS5_Flycast, which the notices and README name.
2026-10-06 21:16:01 -04:00
Mihawk f88fc6f3a9 run-title: --game=CORE:PATH starts RetroArch in a core with its content
/app0/args.txt gets -L /app0/cores/<CORE>_libretro.so and the content path,
one argument a line, since content paths have spaces. With
--retroarch-frames it also asks for a picture of the last frame, collected as
klog/game-<stamp>.png when RetroArch takes it. That exit-time picture failed
for Flycast's hardware-rendered frames ("readback synchronously, but no image
is present"), so pictures of a game come from a pad script's SCREENSHOT
lines.

Used for the Flycast runs (evidence/flycast-sonic-adventure, next commit).
Gates: tools/verify.sh format and unit PASS.
2026-10-06 21:16:01 -04:00
Mihawk 3d5359c572 build-picker: PS5_VulkanTemplate b577e950, its current main
The picker was pinned to a6c32497, and tools/build-picker.sh requires the
template checkout to be at the pin, which it no longer is. b577e950 (on
GitHub) differs by rewritten commit ids, whose SDK and UI kit pins name the
same source trees (PS5_PayloadSDK 611893f = adc8dd7, PS5_VKHomebrewUI
2188642 = 4451c26, compared tree for tree), docs, and new-title.py's 1 GiB
flexible-memory request in a generated param.json, which the picker does not
use (it runs inside PPSA99169, whose param.json already asks for it). The
licence notices read the kit and SDK pins from the template's scripts, so
they follow.

Verified on the base PS5 (13.40), run-PPSA99169-204853: the picker generated
from b577e950, 300 frames, chose RetroArch, RetroArch quit after 600 frames,
back to the picker; picker test PASS (evidence/picker-template-pin; frame
klog/picker-204853/picker.png inspected). tools/verify.sh format and
evidence PASS (88 captures, 0 failed); build and integration wait for MAME's
rebuild.
2026-10-06 20:50:43 -04:00
Mihawk e06c1b87c8 Browser roots: the USB and extended drives ShadowMountPlus mounts
ShadowMountPlus 1.7beta4 mounts the console's /mnt (and /data) into every
PPSA title's sandbox while it runs. The top of the file browser and Load
Content is now INTERNAL (/app0) and each drive actually mounted there: USB 0-7
(/mnt/usbN) and EXTENDED 0-1 (/mnt/extN). A slot counts as a drive when its
root is another filesystem than /mnt. With none, EXTERNAL (/mnt) stays, as
before. /data is never offered (owner's decision). The startup trace records
what the sandbox exposes: roots, drive slots and /mnt's mount names, no file
names.

tools/build-title.sh gains PS5_REUSE_STAGED_CORES (development builds only,
refused for a release): stage a core as its last build left it, to deploy a
title change while MAME's hour-long rebuild runs.

Verified on the base PS5 (13.40), ShadowMountPlus 1.7beta4, one USB drive:
run-PPSA99169-204255; trace: "directory /mnt opened=1", "storage /mnt/usb0
(USB 0) available", no DATA root (evidence/usb-storage-roots). Before,
/mnt and /mnt/usb0 failed with errno 2. The owner confirmed the drive is
listed and a game on it plays.
Gates: tools/verify.sh format, unit and evidence PASS (87 captures, 0
failed). build and integration not run: MAME's full rebuild is in progress.
2026-10-06 20:44:15 -04:00
Mihawk 1cdf298db1 Pins: the new ids of my rewritten commits
On 2026-10-05 the history of my GitHub repositories was rewritten to replace
my old e-mail address with my GitHub noreply address. Each commit of mine got
a new id, with the same files, message and dates, and GitHub no longer serves
the old ids. This replaces every old id in this repository's pins, notes and
evidence with the new id of the same commit, at the same length; no other
character changes. Each pinned commit was checked on GitHub
(gh api repos/mihawk-99/<repo>/commits/<sha>): 22 of 22 found.
2026-10-05 14:00:05 -04:00
Mihawk edf4188122 param.json: ask the kernel for 1 GiB of flexible memory
sce_sys/param.json now carries kernel.flexibleMemorySize = 1073741824,
the most the kernel accepts. Without it the title has 448 MiB of flexible
memory; the extra 576 MiB comes out of direct memory. I want the room for
heavier cores and content, though no core measured today ran short.
The updater installs sce_sys/param.json, so the next release brings it.

Proven on the PS5 Pro (firmware 10.01) with the installed release, build
716a3c05, four runs (evidence/flexible-memory-1g). The 1 GiB runs used
this commit's param.json bytes, put back and read back after each run:
- klog FMEM size 0x1c000000 -> 0x40000000, DMEM size 0x300000000 ->
  0x2dc000000.
- Dolphin, a GameCube game: 359,424 -> 949,248 KiB of flexible memory
  free, flat all run; MARK frames 2266 / 2303 at 70 s.
- The PS2 core, a PS2 game: 402,432 -> 992,256 KiB free; MARK frames
  4606 / 4606 at 80 s, worst frame 20.6 / 19.4 ms.
- Every run stopped by its pad script, rarch_main returned 0.

Not yet run on the base PS5 (13.40), which was unreachable. tools/lint.sh's
param.json checks pass; the script still fails, before and after, on a
clang-tidy entry for tests/display_modes_ps5_test.cpp.
2026-10-05 13:46:35 -04:00
Mihawk 5eb7e0f2e6 README and v0.6.7-alpha.6 note: an IMPORTANT WARNING banner, in red
The owner asked for the warning in red, bigger, labelled "Important Warning"
and more emphatic. GitHub's alert boxes cannot carry that label or a colour of
their own, so the label is a banner image (assets/readme/important-warning.png,
white on #d1242f, 1840x208 for 2x screens, alt "Important warning") under the
title block, followed by the heading "PS5 RetroArch is a passion project, not a
piracy project." and the notice as bold points: piracy is not condoned; no
games, BIOS, firmware or keys, ever; legally obtained backups of games you own;
BIOS and firmware dumped from hardware you own; requests not welcome. The note
loads the banner from main and keeps each paragraph on one line, as the release
page turns every line break into a break.

Verified: rendered with gh api markdown (gfm) and checked at 1100 px and 420 px
in headless Chromium; owner approved the preview. tools/verify.sh format and
evidence -> PASS. No warning sentence removed (README: 6 piracy, 4 legally
obtained, 3 hardware you own).
2026-10-05 07:38:20 -04:00
Mihawk ea03d00ef3 README and v0.6.7-alpha.6 note: the anti-piracy warning first, as before
A README rewrite on 2 October (ef34e50) collapsed the legal notice, which
febe7cf had put throughout, into one paragraph, and removing the RPCS3 section
(8c4670b) took one more. Restored at the owner's request, with the warning at
the very top: a GitHub warning box under the title block, above the WebUI
image, saying PS5 RetroArch is a passion project, not a piracy project, then
the original text (no games, BIOS, firmware or keys shipped; legally obtained
backups of games you own and dumps from hardware you own only; requests not
welcome). Supported systems, Your files and the BIOS rows have their reminders
back. The release note opens with the same box, above the pre-screen image.

Verified: gh api markdown (gfm) renders the box as markdown-alert-warning;
tools/verify.sh format and evidence -> PASS.
2026-10-05 07:31:04 -04:00
Mihawk a9a2229889 Source archives: SHA256SUMS lists every archive, ICU's .tgz and .zip too
The checksums listed only *.tar*, so the two ICU source archives the frontends
need (icu4c-78.3-sources.tgz, icu4c-78.3-data.zip) were written without one.
They are now taken from every file the bundle writes, except the index files.

Verified: python3 -m unittest tests.test_source_bundle fails before the change
and passes after it; TMPDIR=$PWD/build/tmp bash tools/verify.sh -> verify: PASS
(format unit build integration evidence); python3 tools/source-bundle.py
dist/PPSA99169 dist/source-v0.6.7-alpha.6 -> 37 archives, 37 SHA256SUMS lines,
sha256sum -c all OK.
2026-10-05 00:16:33 -04:00
Mihawk 334e819efc Notices: no RPCS3 wording in parts a release ships
The SDK's LLVM entry pointed to an RPCS3 entry no release carries, and the
core-info entry named rpcs3_libretro.info and tooling/rpcs3, neither of which
is in the repository or the package any more.

Verified: TMPDIR=$PWD/build/tmp bash tools/verify.sh -> verify: PASS
(format unit build integration evidence).
2026-10-05 00:01:44 -04:00
Mihawk cd3593e640 Release v0.6.7-alpha.6: its note, and RPCS3 kept off the repository
releases/v0.6.7-alpha.6.md is the release note: the pre-screen first,
every improvement since v0.6.0-alpha.6, the owner's statement on
discontinuing the RPCS3 port, and the tested scope.

RPCS3 is discontinued and PS5_RPCS3 is deleted. Its build scripts and
toolchain (tools/build-rpcs3.sh, tools/build-llvm.sh, tooling/rpcs3/)
leave the repository and are ignored; they stay on the development
machine. config/LEGAL.txt, which every title carries, no longer tells
readers to build RPCS3 from the deleted repository.

Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 23:55:02 -04:00
Mihawk 8c4670bf89 README: PS5_RPCS3 is gone, and so is every RPCS3 part
The owner deleted the PS5_RPCS3 repository and has discontinued the
PS3 port (the v0.6.7-alpha.6 release note says why). The README no
longer names RPCS3 anywhere:
- the PlayStation 3 build-it-yourself section;
- the PS5_RPCS3 and PS5_LLVM fork links;
- the PS5_WITH_RPCS3 notes in the build overview and in the build-script
  list;
- the BIOS-alert exclusion;
- the supported-systems paragraph;
- the credits row;
- the licence paragraph and its table row.
Not pushed: the release waits for the owner's review.
2026-10-04 23:43:26 -04:00
Mihawk cc1827b20b README: the pre-screen under the WebUI, for v0.6.7-alpha.6
The WebUI overview stays the first picture, so a new user always learns
there is a WebUI. Just under it is the pre-screen, captured on the base
PS5 (picker test, run 233754, the title's current build): RetroArch
selected, Remember off. The version paragraph names v0.6.7-alpha.6, and
Get started says the title opens on the pre-screen, with Remember and
the WebUI's Start with.

assets/releases/v0.6.7-alpha.6 holds the release's screenshot catalog:
EmulationStation's carousel with the Favorites and Last Played
collections (ES-DE capture test, run 223054), with its catalog.json.
Not pushed: the release waits for the owner's review.
2026-10-04 23:40:25 -04:00
Mihawk 7e1bc4838d Evidence: a release-tagged build carries the frontends and passes the release checks
A local check, at bdf8c3c: no tag was pushed and nothing was published.
PS5_RELEASE_TAG=v0.0.0-local-review with PS5_FRONTENDS unset built
es-de/es-de.bin and picker/picker.bin and staged 46 parts with no
uncommitted source. check-notices --release found nothing missing, and
source-bundle --list (without --allow-dirty) archives ES-DE's commit,
the SDL fork, PS5_OpenGL, the eight library tarballs, the template, the
UI kit and the template's SDK pin.
Evidence: evidence/release-frontends/ (capture.json, expectation.json).
Command:
  env -u PS5_FRONTENDS PS5_RELEASE_TAG=v0.0.0-local-review tools/build-title.sh
  python3 tools/check-notices.py dist/PPSA99169 --release
  python3 tools/source-bundle.py dist/PPSA99169 OUT --list
2026-10-04 23:22:17 -04:00
Mihawk bdf8c3c605 Releases carry the frontends, with their notices and sources
A release build (PS5_RELEASE_TAG set) now builds the picker and ES-DE
unless PS5_FRONTENDS says otherwise (tools/build-title.sh). A
development build still carries them only when asked.

tooling/notices/components.json lists what the frontends ship:
- ES-DE 3.5.0 (MIT) with its own licenses folder;
- the Alekfull NX theme (CC BY-NC-SA 2.0, marked non-commercial; the
  picker's EmulationStation picture shows it);
- SDL2 (Zlib, the ps5-payload-dev fork);
- PS5_OpenGL (GPL-3.0-or-later, Mesa and its other parts in LICENSES/);
- FFmpeg (LGPL-2.1-or-later: no GPL parts configured), curl, Mbed TLS,
  FreeType, HarfBuzz, ICU, pugixml, libwebp and stb;
- the picker, with the notices the template staged for it.
The port, the payload SDK and the LLVM runtime list the frontends'
executables beside eboot.bin.

tools/stage-notices.py and tools/check-notices.py treat es-de/es-de.bin
and picker/picker.bin as executables: each must belong to a part, with
its digest recorded. tools/source-bundle.py archives a part's extra
sources (a library's tarball, a git tree at a pinned or fixed revision)
even when the part has no archive of its own. The bundle then holds
ES-DE's commit, the libraries' tarballs, the SDL fork, PS5_OpenGL, the
template, the UI kit and the template's SDK pin.

Checked: tests/test_notices.py PASS (a frontend executable must belong
to a part); a build with both frontends staged 46 parts, and
check-notices "covers every shipped executable"; source-bundle --list
names every frontend source. The release-tagged build and its
--release check follow this commit.
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 23:20:37 -04:00
Mihawk 2646f03cad EmulationStation has sound: SDL2 with the PS5 fork's audio driver
ES-DE played nothing. PS5_OpenGL's SDL2 integration built SDL without
its audio subsystem, so ES-DE opened no device and logged "Failed to
create sample conversion stream". PS5_OpenGL 4eab7eb turns SDL's audio
on. The pinned fork's PS5 driver opens sceAudioOut on the main port for
the system user at 48 kHz, as this title's own output does. ES-DE is
linked against that SDL build (build/sdl2-retroarch-20261004-224929, made
with integration/SDL2/build.py native against this project's SDK).

frontends/es-de/ps5/capture_ps5.cpp now records, with each picture, the
sound driver SDL chose and how many of its devices are playing.
tools/run-title.sh prints both.

Evidence: evidence/esde-sound/ (capture.json, expectation.json).
Base PS5 13.40, build 716a3c05, run 231157:
  ES-DE's log: "Audio driver: ps5", "Detected playback device: System
    audio output device"; the conversion-stream errors are gone
  pictures: at 4 s "sound ps5 (0 playing)", at 12 s, browsing with its
    navigation sounds, "sound ps5 (1 playing)"
Hearing it on the TV is not part of a scripted run.
Command:
  python3 ../PS5_OpenGL/integration/SDL2/build.py native \
    --sdl-source .deps/ps5-sdl --sdk-prefix ../PS5_OpenGL/build/sdk/ps5-opengl-gl46 \
    --payload-sdk .deps/native/ps5-payload-sdk \
    --compiler-wrapper build/tools/prospero-clang18 --out ../PS5_OpenGL/build/sdl2-retroarch-<stamp>
    (run from ../PS5_OpenGL, with the paths relative to it)
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin \
    --frontend-capture=4,12 --frontend-scroll --watch 35
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 23:20:21 -04:00
Mihawk bf939c43c8 Pin the payload SDK whose iswctype_l answers FreeBSD's masks
PS5_PayloadSDK f05c5de fixes ps5_iswctype_l at its source. It used to
hand libc++'s FreeBSD class mask to the console's Dinkumware iswctype,
which reads it as a table index; that faulted EmulationStation in
std::locale("C"). It now answers from the rune type, as FreeBSD's does.
This project pins it (tools/setup-native-dependencies.sh). The pin also
brings the SDK's two opt-in modules since f674976, fdv and elevation,
which this title does not call.

frontends/common/frontend_shims.c loses its own iswctype_l, and
tools/build-frontend.sh binds the name to the platform's like the other
locale variants. It is on the hand list, because libc++'s wide ctype
names it and no stub defines it.

Evidence: evidence/sdk-iswctype/ (capture.json, expectation.json).
Base PS5 13.40, build 716a3c05:
  ES-DE started on the platform's iswctype_l: "Audio driver: ps5",
    pictures PASS, GL error 0
  game mode on the new pin: Snes9x ran, RetroArch quit (status 0),
    back to ES-DE, no fatal line
PS5_PayloadSDK: make test PASS.
Command:
  bash tools/setup-native-dependencies.sh
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin \
    --frontend-capture=4,12 --frontend-scroll --watch 35
2026-10-04 23:17:30 -04:00
Mihawk 5ee2d48455 ES-DE: RetroArch's favourites, history and play in its collections
The shared game library (src/ps5_library.h) now carries what RetroArch
remembers of each game:
- whether it is a favourite (playlists/builtin/content_favorites.lpl);
- its place in the history (builtin/content_history.lpl);
- its runtime log (logs/<core's corename>/<content name>.lrtl): when it
  was last played, how often, and for how long.
A game in the history with no runtime log is given a time from its
place: the history file's time, a minute earlier for each place below
the top.

ES-DE's writer (frontends/es-de/ps5/library_ps5.cpp) merges these into
each game list entry:
- favorite: a game RetroArch added or removed since ES-DE's last start
  follows RetroArch (ES-DE/ps5-favorites.txt records RetroArch's set);
  any other game keeps ES-DE's own choice. A favourite set in either
  frontend therefore shows in ES-DE.
- lastplayed, playcount, playtime: the later or larger of ES-DE's and
  RetroArch's.
On the PS5, ES-DE's automatic collections default to Favorites and Last
Played (patches/0001). An install whose settings already said none gets
them once; a marker file keeps any later choice of the user's.

Evidence: evidence/esde-collections/ (capture.json, expectation.json).
Base PS5 13.40:
  RetroArch's History tab, the game's menu, Add to Favorites (pad
    script): RetroArch's favourites held Donkey Kong Country 2
  ES-DE's next start: 1 favourite, 15 games played; CollectionSystemsAuto
    favorites,recent; Donkey Kong Country 2 favourite, playcount 8,
    playtime 838 s (the runtime log's 0:13:58), its later lastplayed kept
  ES-DE's carousel: the SNES card reads "1 GAME (1 FAVORITE)", and the
    Favorites and Last Played collections are there
The test favourite's file was removed afterwards; the console had none.
Host tests: ps5_library and esde_library PASS.
Command:
  tools/run-title.sh --no-build --no-deploy --pad-script=tests/pad/history-favourite.txt --watch 25
  tools/run-title.sh --no-build --no-deploy --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4 --watch 30
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 22:34:28 -04:00
Mihawk 1e6b8cba4d Game mode: prove Close Content as a person closes a game
Game mode's return to the frontend had only been run through
--max-frames, which quits by the same path. tests/pad/game-close-content.txt
closes the game as a person does: L3+R3 (this console's menu combo) opens
the Quick Menu, DOWN twice reaches Close Content, CROSS closes it.
RetroArch then quits (quit_on_close_content = CLI), and the title goes
back to EmulationStation.

Two tools/run-title.sh fixes found on the way. Both came from one
--no-build run after the build gate had rebuilt dist/ without frontends:
- run-title stood a copy of eboot.bin in for /app0/es-de/es-de.bin, and
  its cleanup then deleted the file. It now refuses a frontend's image
  that dist/ does not have.
- The deployment record still vouched for the deleted file, so the next
  deploy skipped it. A file the cleanup removes is now dropped from the
  record.

Evidence: evidence/game-close-content/ (capture.json, expectation.json).
Base PS5 13.40, build 584d4d27, run 221401:
  the game running, then the Quick Menu (Resume selected), then Close
    Content selected, then closed
  game mode: RetroArch quit (status 0) after 11 s; back to the frontend
  frontend capture PASS: 3 pictures from 2 ES-DE processes
Command:
  tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin \
    --frontend-capture=4,20 --frontend-launch \
    --pad-script=tests/pad/game-close-content.txt --watch 80
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 22:19:29 -04:00
Mihawk c861d36600 Frontends: a remembered choice, a quit that closes, logs per session and mode
Three of the frontend follow-ups in docs/FRONTENDS.md:

- The frontend the title starts with is remembered in config/frontend.cfg
  ("ask", "retroarch" or "es-de"; src/ps5_frontend_choice.h, shared by
  eboot.bin, the picker and the WebUI). The picker's Square switches
  Remember, which writes the frontend started, or "ask". The WebUI's
  quick panel has "Start with", kept in that file, never in webui.cfg.
  A launch from the home screen then starts that frontend directly.
  Holding L1 while the title starts opens the picker anyway: the first
  user's pad is read for up to a second before anything opens it
  (src/frontend_hold_ps5.cpp).
- A frontend that quits now says so: ES-DE and the picker-started
  RetroArch restart eboot.bin with --ps5-mode=quit. That shows the picker,
  or closes the title when a frontend is remembered.
- Logs: a launch with no --ps5- argument starts a session, and keeps the
  previous session's trace as trace.1.txt. RetroArch logs a game-mode game
  to retroarch-game.log and its own sessions to retroarch.log, each with
  its previous copy (.1). ES-DE keeps its last stdout.txt, and starts
  es-de-ps5.log again past 512 KiB.
- An update installed as RetroArch quits no longer hands over. Its
  files are already in place, so the title closes rather than start the
  new picker or ES-DE from the old process.

tools/run-title.sh: --picker-test takes :remember or :forget; with
--home-launch the title starts as from the home screen (no test-run
marker); a game-mode run's log is checked in retroarch-game.log.

Evidence: evidence/frontend-launch/ (capture.json, expectation.json).
Base PS5 13.40, build 584d4d27:
  game mode: the game logged to retroarch-game.log, and the trace held
    only this session (the rest in trace.1.txt)
  picker test with Remember on: frontend.cfg "es-de"
  home launch: L1 read (not held, decided at once), started ES-DE; ES-DE
    quit, and the title closed by itself
  the console's WebUI: frontend_start es-de, set to ask (200); the next
    home launch opened the picker
Holding L1 on a real pad was not exercised by a script. An installed
update closing the title is covered by the host test only.
Host tests: frontend_mode_ps5 and test_webui PASS.
Command:
  tools/run-title.sh --picker-test=300:es-de:remember --frontend-capture=20 --watch 50
  tools/run-title.sh --no-build --no-deploy --home-launch --frontend-capture=8 --frontend-quit --watch 40
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 22:06:36 -04:00
Mihawk 4f72d850d4 Build the frontend picker from a template with a controller per user
PS5_VulkanTemplate f827f0a5 connects controllers the way this title's
input driver does since 4788d35 (ProsperoEden's method): player 0 is the
user who started the title, and every other signed-in user's controller
takes the next player at a scan about once a second. Its existing pad
calls keep meaning player 0, so the picker behaves as before;
tools/build-picker.sh now pins that commit.

Evidence: evidence/picker-multi-controller/ (capture.json,
expectation.json; user ids masked). Base PS5 13.40, run 213503, two
users signed in, the second controller switched off: the picker opened
player 0 for the launching user and player 1 for the second user, on
both of its launches. Picker test PASS (300 frames, a picture of the
last, then RetroArch). RetroArch opened ports 0 and 1 in the same run.
No fatal line in the kernel log.
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --picker-test=300:retroarch --retroarch-frames=600 --watch 60
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 21:39:24 -04:00
Mihawk 4788d35076 Up to four controllers, one per signed-in PS5 user
The console pairs each controller with a signed-in user, and scePadOpen
opens a user's controller, so RetroArch read only the first player's.
The joypad driver now connects controllers the way ProsperoEden does
(PS5_ProsperoEden b919847, headless/pad.cpp):

- Port 0 (player 1) is the user who started the title, and stays theirs.
- About once a second sceUserServiceGetLoginUserIdList is read again. A
  new user's controller takes the first free port (1 to 3); a port is
  let go, and announced gone, when its user signs out. A pad the process
  already holds is reused through scePadGetHandle.
- Each port is announced to RetroArch's autoconfiguration on its own and
  has its own buttons, axes, rumble and haptic stream. The pad script
  still plays port 0.

The console's config already maps players 1-4 to joypad indexes 0-3.
For test runs, tools/run-title.sh --pad-monitor arms
/app0/pad-monitor.txt, which traces each port's whole button word as it
changes, including the bit that says the system holds the controller.

Evidence: evidence/multi-controller/ (capture.json, expectation.json;
console user ids masked). On the base PS5 13.40 with two DualSense
controllers and two signed-in users (build 95a82988):
  run 211620: ports 0 and 1 opened, each with its haptic stream, and
    both reported connected
  the owner's own launch from the home screen: the launching user's
    controller became port 0 and the other port 1; both were opened again
    after RetroArch re-initialised input, and a racing game played with
    both controllers
The committed source differs from build 95a82988 only in the monitor's
trace format, which the host test covers; that build was not run on the
console.
Host test: tests/test_input_ps5.py PASS (the four-player scenario: join,
separate input and rumble, sign-out, rejoin, the first player kept, an
open failure, a full house, the monitor, re-announcement after a reset).
Command:
  tools/run-title.sh --no-build --no-deploy --pad-monitor --watch 120
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 21:26:17 -04:00
Mihawk c56a9aa77f Build the frontend picker from a template that refuses null imports
The picker imported mkstemp and readlink through stubs whose modules give
a title nothing, so each import was null at run time: the same fault as
strcasestr in 1b4ba51. Both came from RADV's archive (ac_gpu_info.c,
aco_print_asm.cpp) through PS5_Vulkan's link recipe, so the fix belongs
in PS5_Vulkan and PS5_VulkanTemplate rather than here:

- PS5_Vulkan f3cbf87: the recipe binds strcasestr, mkstemp, readlink,
  link and symlink to the platform layer when the SDK pin has them.
- PS5_VulkanTemplate 545df4c6: link-title.sh refuses a title that imports
  a name only libkernel_sys's or libScePosixForWebKit's stub defines.

tools/build-picker.sh now pins the template at 545df4c6. The picker built
from it passes that check and imports none of the five. eboot.bin imports
none of them either, with the recipe's bindings and this title's wraps
together.

Evidence: evidence/picker-null-imports/ (capture.json, expectation.json).
Base PS5 13.40, run 194705: picker test PASS (300 frames, a picture of
the last, then RetroArch). RetroArch quit after 600 frames and the title
went back to the picker. No fatal line in the kernel log.
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --picker-test=300:retroarch --retroarch-frames=600 --watch 60
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 19:51:14 -04:00
Mihawk 1b4ba51b74 Manual Scan: choosing Content Directory again no longer crashes
A tester's v0.5.5-alpha.5 crashed when Import Content -> Manual Scan ->
Content Directory was pressed a second time after a playlist had been
made. On that second visit RetroArch opens the browser on the parent of
the folder chosen before, with that folder pending, and finds the entry
to select with strcasestr. The title linked strcasestr against
libScePosixForWebKit's stub, and at run time the import was null: the
call jumped to address 0 inside menu_entries_list_search. The first
visit has no pending selection, so it never made the call.

Six imports had this fault: strcasestr and mkstemp from the WebKit
POSIX stub, and link, symlink, readlink and pathconf from libkernel_sys's.
src/platform_wraps.c now binds all six to the platform layer (pathconf
as the frontends answer it), and tools/build-title.sh adds the --wrap
flags. tools/build.sh refuses a title that imports anything only those
two stubs define: it finds all six in the old link and none in this one.
Every other strcasestr caller in the menu (list search, Explore
filtering, Load Content's last folder) had the same fault and is fixed
too.

Tooling: tools/run-title.sh --pad-script=FILE uploads a pad script for
one run and collects its pictures. A new MENU pad action prints the list
on top of the menu stack and its selected entry, because SCREENSHOT
leaves the XMB out when no content is running.

Evidence: evidence/manual-scan-reentry/ (capture.json, expectation.json).
Base PS5 13.40, tests/pad/manual-scan-reentry.txt:
  before (build 71f4bedc): signal 11, rip 0, return address in
    menu_entries_list_search after callq *0x2306c40 (GLOB_DAT strcasestr)
  after (build 10a05e61): the browser opened /app0/ with "assets"
    selected, quit status 0, no fatal signal
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --pad-script=tests/pad/manual-scan-reentry.txt --watch 40
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 19:36:09 -04:00
Mihawk f35915abca The game library: every frontend's systems and games come from RetroArch
A frontend keeps no game library of its own. Its systems and game lists
are RetroArch's playlists, read again each time it starts, so a game
added to or removed from a playlist in RetroArch appears or goes in
every frontend. src/ps5_library.h and src/ps5_library.c are that
contract, compiled into eboot.bin and into every frontend
(tools/build-frontend.sh), like game mode's:

- Games: the entries of /app0/playlists, minus RetroArch's history,
  favourites and media lists, each game once.
- A game's system: the first known platform among its entry's
  database, the playlist's name, its folder and its core's database;
  otherwise a system of its own. A mixed playlist of DETECT entries,
  like the base console's content.lpl, is split by folder.
- A system's core: the one its entries name most, else the core
  RetroArch's core info associates with the platform. Its folder is the
  deepest folder holding all its games, and its extensions are those of
  its games and its core.
- ps5_library_command gives game mode's command line; game mode now
  reads playlists through the same reader.

ES-DE (frontends/es-de/ps5/library_ps5.cpp) writes
custom_systems/es_systems.xml and each system's gamelist.xml from the
library at every start, using ES-DE's own names and themes. A game ES-DE
already knows keeps its play count, play time, favourite and scraped
details; a game no longer in any playlist is dropped. ES-DE then runs
with --gamelist-only. The fixed table (systems.tsv, systems.py) is gone.

Evidence: evidence/game-library/ (capture.json, expectation.json,
capture-test.jsonl); base PS5 13.40: 5 playlists gave 14 systems and 16
games, 0 failures. ES-DE loaded the 14 systems, started Donkey Kong
Country 2 through game mode and came back with the play counted
(playcount 2, playtime 21); no fatal signal.
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/es-de.bin --frontend-capture=4,20 \
    --frontend-launch --retroarch-frames=600 --watch 80
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 18:54:38 -04:00
Mihawk e98cca409c Measure Sony's local processes for this title's frontends
../PS5_Proton found that a title can have Sony's service start a fresh
executable as a child of its application (sceSystemServiceAddLocalProcess
with the libc-only preload mask, a socket at the child's descriptor 3,
sceSystemServiceKillLocalProcess). This probe asks what that is worth
here.

- frontends/child-probe: a frontend program (SDL2, PS5_OpenGL) that the
  title hands over to with LoadExec, as it does ES-DE. It holds the
  display while running a headless child, keeps a child alive across
  its own LoadExec, then releases the display and starts a graphics
  child. "nogpu" mode starts one without ever opening the display.
- frontends/child-probe-helper: the headless child, freestanding C with
  only libkernel, reporting its PID and the memory it sees.
- tooling/native: --preload-prx-flags writes the preload mask into the
  process parameters (0x50), as PS5_Proton's patch to the same tool
  does. Without it the output is unchanged.

Result on the base PS5 (13.40): a headless child starts in 0.12 s and
closes cleanly (kill 0, gone from the list). It reads the same flexible
memory figure as the parent (one budget), and it does not survive the
parent's LoadExec. A graphics child fails before its code runs
(gbase_gvmsw_open 6978, PRX_PROCESS_STARTUP_FAILURE), whether or not
the parent ever opened the display, and Sony then kills the whole
application.

Evidence: evidence/child-processes/ (capture.json, expectation.json,
probe-run-1.jsonl, probe-run-2-nogpu.jsonl). Command:
  PS5_FRONTENDS='es-de picker child-probe' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/child-probe.bin --watch 120
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 18:36:36 -04:00
Mihawk 26c003b61a Game mode: every frontend starts its games in RetroArch the same way
A frontend never runs a game itself. It asks the title to run the game in
RetroArch, in a process of its own, and to come back to it afterwards.
src/ps5_game.h and src/ps5_game.c are that contract, compiled into
eboot.bin and into every frontend executable (tools/build-frontend.sh),
so all frontends share one implementation:

- The frontend writes /app0/game-request.txt (content, core, its own
  executable, a state string) and restarts the title as eboot.bin
  --ps5-mode=game. Its launch command can simply be RetroArch's own,
  "/app0/eboot.bin -L <core> <content>"; ps5_game_parse_command reads
  it, shell escapes included.
- eboot.bin (src/frontend_mode_ps5.cpp) takes and checks the request.
  The core is the one RetroArch's playlists associate with the content
  when they name one of the title's cores, else the frontend's. A
  request that can't run is refused back to the frontend with the
  reason. RetroArch starts on the game (-L, no --menu,
  quit_on_close_content = CLI from config/game-mode.cfg). Once it has
  quit, /app0/game-result.txt gets the status and seconds, and the
  title restarts as the frontend.
- ES-DE: its launch (patch, FileData::launchGame) saves its game lists,
  releases the display and calls the contract. Its start (patch,
  ViewController::goToStart) counts the play, adds the play time and
  opens the system's game list on the game. systems.tsv gives each
  system a default core from the title's cores.
- The capture hook's "launch" action and tools/run-title.sh
  --frontend-launch start a game unattended.

Evidence: evidence/game-mode/ (capture.json, expectation.json,
capture-test.jsonl, gamelist-snes.xml); base PS5 13.40: es-de, eboot
(Donkey Kong Country 2 with snes9x, SRAM saved to
savefiles/Snes9x), es-de back on the game with playcount 1 and
playtime 11; no fatal signal.
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/es-de.bin --frontend-capture=4,20 \
    --frontend-launch --retroarch-frames=600 --watch 75
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 18:09:40 -04:00
Mihawk 8cb7e980bf Open the title on a frontend picker: RetroArch or EmulationStation
A launch from the home screen now shows a pre-screen in
PS5_VulkanTemplate's Gloss theme with two cards, each with a preview
captured on the console: RetroArch's XMB and ES-DE's Alekfull NX SNES
view. Choosing one restarts the title as that frontend (approach B,
LoadExec). Quitting either one comes back to the picker, and CIRCLE
in the picker closes the title.

- frontends/picker/picker.cpp: the picker, a program of the template's
  UI module. tools/build-picker.sh builds it the template's way (a
  generated one-program title at the pinned template commit, with its
  app root moved to /app0/picker) and stages picker/picker.bin, the
  kit's assets and the previews (assets/picker, NOTICE.md credits them).
- src/frontend_mode_ps5.cpp: eboot.bin decides first. --ps5-mode=
  retroarch runs RetroArch, es-de starts ES-DE, and no mode starts the
  picker, except for a test run. RetroArch the picker started goes
  back to the picker when it quits.
- tools/run-title.sh writes /app0/test-run.txt before every launch (as
  src/main.cpp documents), so scripted runs keep starting RetroArch;
  --picker-test=FRAMES:CHOICE, --retroarch-frames, --frontend-quit.

Evidence: evidence/frontend-picker/ (capture.json, expectation.json,
picker-test-es-de.jsonl, picker-test-retroarch.jsonl); base PS5 13.40:
eboot, picker, es-de, eboot, picker (ES-DE quit) and eboot, picker,
eboot (RetroArch, 600 frames), eboot, picker, with no fatal signal.
Command:
  PS5_FRONTENDS='es-de picker' tools/build-title.sh
  tools/run-title.sh --no-build --picker-test=300:es-de \
    --frontend-capture=15 --frontend-quit --watch 60
  tools/run-title.sh --no-build --picker-test=300:retroarch \
    --retroarch-frames=600 --watch 60
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 17:37:33 -04:00
Mihawk f0750abec2 Measure EmulationStation's frame times, and bring browsing to 60 fps
The owner saw ES-DE's frame rate as horribly low. Measured on the console
while it browses its systems (a Right key every 400 ms), frames were
36.6 ms apart (27 fps). A CPU profile put 75% of all time in
util_clflushopt_range: ../PS5_OpenGL flushed its whole 16 MiB scanout
pool before every unbatched scanout write, each window glClear (about
8 ms each). PS5_OpenGL c74ed38 flushes it once per pool; frames are now
17.1 ms apart (p95 17.6 ms).

- frontends/es-de/ps5/capture_ps5.cpp: each capture records the frame
  times since the last one (between swaps, in the swap, frames over
  25 ms and each over 100 ms with the presses so far), presses Right
  every 400 ms when asked ("scroll"), and samples the CPU every
  millisecond through an ITIMER_PROF signal ("profile").
- tools/run-title.sh --frontend-scroll, --frontend-profile;
  tools/esde-profile.py reports the samples by function.
- main_ps5.cpp: the driver's stdout to /app0/es-de/stdout.txt, and
  /app0/es-de/env.txt for its switches (PS5_GLTHREAD=1 measured: no
  gain once the flush was gone, so off).
- The ES-DE patch asks for no depth buffer and clears colour only: ES-DE
  never tests depth.

Evidence: evidence/esde-frame-rate/ (capture.json, expectation.json,
capture-test-before.jsonl, capture-test-after.jsonl); base PS5 13.40.
Command:
  PS5_FRONTENDS=es-de tools/build-title.sh
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/es-de.bin --frontend-capture=4,25,45 \
    --frontend-scroll --watch 60
Gates: tools/verify.sh PASS (format unit build integration evidence).
2026-10-04 17:09:50 -04:00
Mihawk eaba7a8114 Run EmulationStation (ES-DE 3.5.0) on the PS5 beside RetroArch
ES-DE is its own executable of the title, /app0/es-de/es-de.bin, started
through LoadExec. It draws through ../PS5_OpenGL and shows RetroArch's
content folders as its systems (frontends/es-de/systems.tsv), with the
Alekfull NX theme by default.

- tools/build-esde.sh: the dependencies (FreeType, HarfBuzz, ICU, pugixml,
  curl, FFmpeg and now libwebp, tools/build-libwebp.sh), the pinned stb
  headers, ES-DE at 50e4b600 with frontends/es-de/patches, CMake, and the
  pinned Alekfull NX theme.
- frontends/es-de: the port's main, a FreeImage stand-in over stb_image
  and libwebp, an armed framebuffer capture, and the staging of
  resources, themes and the systems file.
- tools/build-frontend.sh binds every import the SDK's platform layer
  replaces (readlink was a null import), wraps the thread functions as
  eboot.bin does, and fails on an import only libkernel_sys exports.
- frontends/common: iswctype_l with FreeBSD masks (the console's
  Dinkumware iswctype faulted), pathconf, lstat answered as stat (refused
  to a title), and a getcwd that can name a working directory.
- tools/run-title.sh --frontend-capture; tools/esde-settings.py.

Evidence: evidence/esde-first-light/ (capture.json, expectation.json,
capture-test.jsonl). On the base PS5 (13.40): ES-DE loads 24 systems and
the Alekfull NX theme in 1.4 s, with no fatal signal and no image it
could not load; the 30 s capture is the SNES system view at 1920x1080.
Command:
  PS5_FRONTENDS=es-de tools/build-title.sh
  tools/esde-settings.py Theme=alekfull-nx-es-de StartupSystem=snes
  tools/run-title.sh --no-build --relaunch-test=1 \
    --relaunch-image=es-de/es-de.bin --frontend-capture=15,30,50 --watch 70
Gates: tools/verify.sh PASS (format unit build integration evidence).
Known: no audio (the SDL2 build has none), games do not start yet.
2026-10-04 16:31:50 -04:00
Mihawk c0dbb264f0 Cross-build EmulationStation's libraries for the PS5
ES-DE 3.5.0 (frontends/es-de, next) needs FreeType, HarfBuzz, ICU, pugixml,
libcurl and FFmpeg with libavfilter beside SDL2 and OpenGL. Each is built from
a release archive pinned by digest (GitHub's published digests, or the same
bytes from two mirrors for FreeType) into .deps/native/<name>-ps5 as static
archives with headers, licences and a stamp, the way tools/build-libiconv.sh
does:

  build-freetype.sh    FreeType 2.13.3, no optional zlib/bzip2/PNG/Brotli/HarfBuzz
  build-harfbuzz.sh    HarfBuzz 14.5.1, the amalgamated harfbuzz.cc over FreeType
  build-icu.sh         ICU 78.3 libicuuc and a filtered static libicudata (en and
                       root, break rules without dictionaries: 2.4 MB, not ~30),
                       from a host build of the same release; U_STATIC_IMPLEMENTATION
                       because this compiler takes __declspec(dllexport)
  build-pugixml.sh     pugixml 1.16
  build-curl.sh        curl 8.22.0 over mbedTLS 3.6.7 (RetroArch's mbedTLS is 2.6.0),
                       HTTP and HTTPS only
  build-ffmpeg-esde.sh FFmpeg 8.1.1 (tools/build-ffmpeg.sh's release) with the
                       filters ES-DE's video player builds and the usual video and
                       audio formats; tools/build-ffmpeg.sh is unchanged

Verified: each script built on this host (freetype 1.4 MB, harfbuzz 2.5 MB,
libicuuc 4.3 MB + libicudata 2.4 MB, pugixml 444 KB, libcurl 1.6 MB + mbedTLS,
FFmpeg 17 MB of archives); bash tools/lint-shell.sh PASS. Nothing links them
yet: the ES-DE build that does is the next step, and its console run the proof.
2026-10-04 15:17:20 -04:00
Mihawk 124099591c Run an SDL2/OpenGL frontend executable beside eboot.bin
EmulationStation is to be its own executable in the title, drawing with
../PS5_OpenGL (mihawk-99's fork of blackbearreloaded/ps5-opengl), whose Mesa
cannot share an image with RADV's. tools/build-frontend.sh builds a program in
frontends/ the way PS5_OpenGL builds its SDL2 consumers: its runtime shims,
heap and linker script over this project's base layout and C runtime, SDL2
from its bridge (built against this project's payload SDK from
ps5-payload-dev/SDL 8c56053), its OpenGL SDK and AGC import stubs, and a
256 MiB libc heap (the native tool's link takes --libc-heap-size; eboot.bin
keeps the largest). The result is es-de/<program>.bin, built by
tools/build-title.sh before the manifest for each program PS5_FRONTENDS names
(none by default). frontends/common supplies __memset_chk, which this host's
clang 23 builtins (emutls) call.

tools/deploy-title.py verifies a signed executable other than eboot.bin by its
stored bytes, on a connection with ftpsrv's SELF transfer mode off; it used to
compare the decoded download and upload twice. tools/run-title.sh's
--relaunch-image uses a built image at that path rather than a copy.

frontends/sdl-probe opens an SDL window with an OpenGL 3.3 core context,
shows red, green and blue for two seconds each, reads the centre back, counts
controller events, records /app0/es-de/sdl-probe.json and restarts eboot.bin
with the argument it was given.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh -> PASS (format unit build
    integration evidence), default title unchanged
  PS5_FRONTENDS=sdl-probe tools/build-title.sh -> es-de/sdl-probe.bin
    (23,401,527 bytes) in the manifest, check-manifest PASS, build c1383963
  tools/run-title.sh --no-build --relaunch-test=1
    --relaunch-image=es-de/sdl-probe.bin --watch 45 on the base PS5: kernel
    log EXEC eboot.bin, es-de/sdl-probe.bin, eboot.bin; GL "4.6 (Core
    Profile) Mesa 26.2.0" on "PS5 AGC", 1920x1080, 121/120/120 frames in two
    seconds each with every centre pixel right, a joystick and a button
    press; relaunch PASS; the owner saw red, green, blue, then RetroArch
  deploy of the probe alone after the fix: one upload, verified as stored
Evidence: evidence/sdl-probe/ (python3 tools/evidence.py compare)
2026-10-04 15:05:48 -04:00
Mihawk 5e302e006a Prove LoadExec starts another executable of the title
EmulationStation draws with PS5_OpenGL, whose Mesa would collide with RADV's
in one image, so it is to be its own executable beside eboot.bin. Whether
LoadExec starts another image in /app0 is measured first: the relaunch test's
arm file takes an optional image ("<count> <run id> [/app0/...]", refused
outside /app0 or with '..'), and tools/run-title.sh --relaunch-image=PATH
uploads a copy of the build's eboot.bin there and removes it afterwards.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh -> PASS (format unit build
    integration evidence), build 256cb475; the host test covers the image
    path, its record and the refused paths
  tools/run-title.sh --no-build --relaunch-test=1
    --relaunch-image=probe/eboot-copy.bin --watch 45 on the base PS5: the
    kernel log shows EXEC /app0/probe/eboot-copy.bin after Kill for LoadExec
    => 0; generation 1 (pid 132) ran from the copy with argv[0] =
    "--ps5-relaunch=1" and 419430400 bytes flexible free; PASS, no fatal signal
Evidence: evidence/loadexec-second-image/ (python3 tools/evidence.py compare)
2026-10-04 14:52:16 -04:00
Mihawk f2efc8c03a Write the frame capture at the size RetroArch reads back
The port's capture (patch 0031, /app0/args.txt --ps5-capture=N) wrote its PPM
at the viewport's size, but vulkan_read_viewport's synchronous readback writes
the viewport no larger than the video size. With no core loaded the video size
follows the core-provided 4:3 aspect, so at 1080p it wrote 1440-pixel rows into
a 1920-pixel-wide image (four strips a row, the top quarter black), and 2880 at
2160. The PPM now takes min(viewport, video size), as the readback does.

Verified:
  rm -rf build/ra-conf; TMPDIR=build/tmp bash tools/verify.sh -> PASS (format
    unit build integration evidence), build 10a24fa1 (an existing ra-conf keeps
    a patch's old text while its marker is present, so the tree was recreated)
  On the base PS5: args.txt --ps5-capture=600 with an appended config for
    1920x1080 and a 16:9 aspect -> "viewport 1920x1080, read_viewport -> ok",
    a clean 1920x1080 XMB capture (no black rows); config/retroarch.cfg
    unchanged. The capture is the pre-screen's RetroArch card preview.
Evidence: evidence/capture-readback-size/ (python3 tools/evidence.py compare)
2026-10-04 14:37:52 -04:00
Mihawk d200efa4c6 Prove the driver's display sizes on the console with a display modes test
The Vulkan driver's display (RADV's VideoOut WSI, PS5_Mesa dc82d01, pinned by
PS5_Vulkan ab37acd) now offers 3840x2160, 2560x1440 and 1920x1080 instead of
4K alone. src/display_modes_ps5.cpp, armed by /app0/display-modes-test.txt
(tools/run-title.sh --display-modes-test[=frames]), makes a device on the
title's driver before RetroArch does and presents every mode in turn, then the
first again, each with a pattern a person can judge: two shades of one colour a
size split at the middle and a white border on every edge. Each mode's result
is a line of /app0/display-modes-test.jsonl; RetroArch then starts as usual.
RetroArch's default (the largest mode) is unchanged; video_fullscreen_x/y
selects a smaller one.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh -> PASS (format unit build integration
    evidence), building against PS5_Vulkan's archive at dc82d01 (build
    8128ea84...); the host test also ran armed against a host build of the
    WSI (PS5_WSI_MODES_ICD): every mode presented.
  tools/run-title.sh --no-build --display-modes-test=180 --watch 60 on the
    base PS5 (13.40, 1080p screen), build ce576f3d (same driver revision):
    sets 0-2 registered (buffers 0-14), 180 frames each in 2.96-3.00 s,
    3840x2160 again from set 0, PASS. The owner saw each size fill the TV,
    border on all four edges and split centred, and RetroArch steady after.
  A first run (PS5_Mesa 3321f8f) also offered 1280x720: VideoOut refused its
    set at buffers 15-19 (0x80290001) and RetroArch flickered after it, not
    alone; the size was left out of the merged driver.
Evidence: evidence/display-modes/ (python3 tools/evidence.py compare)
2026-10-04 14:22:07 -04:00
Mihawk d8cd370ce2 Prove the frontend handover's title restart on the console
The frontend picker hands over by restarting the title in a new process
(approach B). src/relaunch_ps5.cpp measures that restart: armed by
/app0/relaunch-test.txt (tools/run-title.sh --relaunch-test[=N]), each process
records its generation, argv, flexible memory, pid and clock in
/app0/relaunch-test.jsonl, restarts the title through
sceSystemServiceLoadExec("/app0/eboot.bin", {"--ps5-relaunch=N", NULL}), and
the last one continues into RetroArch. main() now takes the process arguments.

Found on the console: LoadExec's arguments become the new process's whole
argv, with no program name in front (argc=1, argv[0]="--ps5-relaunch=1"); a
home-screen launch gets argc=1, argv[0]="". The first run (build f935ae4e)
read from argv[1] and failed its argument check; the parser now reads from
argv[0] and the host test feeds argv in the console's shape.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh -> PASS (format unit build
    integration evidence); build identity 453864d6...
  tools/run-title.sh --no-build --relaunch-test=5 --watch 60 on the base
    PS5 (13.40): 5 restarts, pids 117-122, each with its arguments,
    419430400 bytes flexible free at every start, 0.56-0.61 s per restart;
    kernel log EXEC x6, "Kill for LoadExec => 0" x5, no fatal signal;
    RetroArch started after the chain with XMB assets loaded.
Evidence: evidence/loadexec-relaunch/ (python3 tools/evidence.py compare)
2026-10-04 13:34:42 -04:00
Mihawk 39289d4c6e Leave RPCS3 out of every title build unless PS5_WITH_RPCS3=1
PS3 is no longer a target, so no build stages rpcs3_libretro.so, its .info or
system/RPCS3, and no deployment puts them on a console. tools/build-rpcs3.sh
still builds the core on this machine; PS5_WITH_RPCS3=1 stages it for someone
building their own title, and a release refuses that before compiling anything.

Verified:
  TMPDIR=build/tmp bash tools/verify.sh
    -> PASS (format unit build integration evidence), 64 captures replayed
  find dist/PPSA99169 -iname '*rpcs3*' -> nothing; 15 cores staged;
    check-manifest PASS (11616 files); build identity eedff761...
  PS5_WITH_RPCS3=1 PS5_RELEASE_TAG=v-test bash tools/build-title.sh
    -> "a release never carries RPCS3", exit 2, before the frontend build
Gate log: build/verification/rpcs3-optin-gates-2.log
2026-10-04 13:13:06 -04:00
Mihawk 09ea2c2a66 Add verified console updates and BIOS alerts to the WebUI
Download and verify published packages on-console, stage changed managed files, preserve user data, and install after frontend shutdown. Set eboot execute permission explicitly so native installations relaunch.

Validation: tools/verify.sh (118 tests, native build and all gates); browser state checks; actual Alpha 6 download/install/relaunch and unchanged config. Evidence: evidence/webui-update/.
2026-10-02 19:06:46 -04:00
813 changed files with 69645 additions and 2548 deletions

No files matched your search

+8 -1
View File
@@ -14,7 +14,6 @@ Makefile text eol=lf
.clang-format text eol=lf
.clang-tidy text eol=lf
tooling/prospero-clang18 text eol=lf
*.ps1 text eol=crlf
*.png binary
*.dds binary
*.at9 binary
@@ -23,3 +22,11 @@ tooling/prospero-clang18 text eol=lf
# Preserve the upstream font license verbatim, including its original whitespace.
webui/assets/OFL.txt -text -whitespace
# Unified patches require a space on otherwise blank context lines.
platform/lapy/*.patch -whitespace
tooling/melondsds/*.patch -whitespace
evidence/lapy-autostart/*.patch -whitespace
# Unified patches retain blank context lines.
tooling/swanstation/*.patch -whitespace
+6
View File
@@ -55,3 +55,9 @@ config.h
# Published release notes are part of the versioned release.
!releases/*.md
# RPCS3 is discontinued (owner, 2026-10): its build scripts and toolchain stay on
# the development machine only.
tools/build-rpcs3.sh
tools/build-llvm.sh
tooling/rpcs3/
+69 -8
View File
@@ -1,6 +1,6 @@
{
"schemaVersion": 2,
"generatedAt": "2026-10-02T16:54:39.012163+00:00",
"generatedAt": "2026-10-08T14:03:22.221536+00:00",
"title": "Design System: PS5 RetroArch WebUI",
"extensions": {
"colorMeta": {
@@ -506,6 +506,61 @@
"name": "intermediate-max",
"value": "1300px"
}
],
"icons": {
"registry": "webui/icons.js",
"contract": "Role-specific colorful artwork; preserve authored colors and identities.",
"semantic": {
"family": "Fluent Color",
"rendering": "local SVG image assets with authored fills and gradients",
"navigationSize": "27px",
"source": "webui/assets/fluent/SOURCE.json",
"license": "webui/assets/fluent/LICENSE"
},
"regions": {
"family": "flag-icons",
"rendering": "accurate local regional SVGs",
"size": "26px \u00d7 20px",
"source": "webui/assets/flags/SOURCE.json",
"license": "webui/assets/flags/LICENSE"
},
"systems": {
"family": "Linear ES-DE hardware imagery",
"registry": "SYSTEM_ICONS",
"recognizedPlatformCount": 50,
"rendering": "local WebP images cropped above source logos",
"frame": "42px \u00d7 30px",
"imageSize": "42px \u00d7 42px",
"selectorFrame": "56px \u00d7 39px",
"selectorImageSize": "56px \u00d7 56px",
"fallback": "library artwork for unknown custom systems only",
"source": "webui/assets/systems/SOURCE.json",
"license": "webui/assets/systems/LICENSE"
},
"media": {
"style": "colorful recognizable media artwork, including authored geometric 3D box and disc",
"source": "MEDIA_SYMBOLS in webui/icons.js"
},
"identity": {
"brand": "actual RetroArch logo",
"providers": "original colored provider badges"
},
"utility": {
"family": "Lucide",
"roles": "simple controls including arrows, close and search",
"viewBox": "0 0 24 24",
"strokeWidth": 1.75,
"fill": "none",
"stroke": "currentColor",
"strokeLinecap": "round",
"strokeLinejoin": "round",
"source": "webui/assets/lucide/SOURCE.txt",
"license": "webui/assets/lucide/LICENSE"
},
"accessibility": "Decorative images have empty alt text and aria-hidden=true; decorative SVGs are hidden and not focusable. Labels or accessible control names carry meaning."
},
"implementationDrift": [
"Pre-existing game-dialog shadow, blurred backdrop and entrance animation, plus selection outlines implemented as shadows, differ from the flat contract. Retained without adopting new global tokens or repairing them in this update."
]
},
"components": [
@@ -546,8 +601,8 @@
"kind": "nav",
"refersTo": "nav-current",
"description": "The solid purple current-page tab inside the dark rail.",
"html": "<nav class=\"ds-nav\" aria-label=\"Example navigation\"><a href=\"#overview\" aria-current=\"page\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"m3 10 9-7 9 7v11h-6v-7H9v7H3Z\"/></svg><span>Overview</span></a></nav>",
"css": ".ds-nav{background:var(--rail,#37184a);padding:0.722222rem}.ds-nav a{font:1rem/1.45 Arial,Helvetica,sans-serif;display:flex;align-items:center;gap:1.55556rem;min-height:3.11111rem;padding:0.666667rem 1.22222rem;border-radius:0.666667rem;text-decoration:none;color:#fff;font-size:1.11111rem}.ds-nav a[aria-current=page]{background:#6c3b9d}.ds-nav a:hover{background:#62378d}.ds-nav a:focus-visible{outline:0.166667rem solid var(--focus,#a566ef);outline-offset:0.222222rem}.ds-nav svg{width:1.55556rem;height:1.55556rem;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}"
"html": "<nav class=\"ds-nav\" aria-label=\"Example navigation\"><a href=\"#overview\" aria-current=\"page\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M18.0674 27H30.0674V43H18.0674V27Z\" fill=\"url(#ds-home-paint0_linear_378174_9075)\"/>\n<path d=\"M26.4611 7.85517C25.0626 6.71494 23.0722 6.71494 21.6736 7.85517L8.49862 18.5966C7.59383 19.3342 7.06738 20.4493 7.06738 21.628V39.1134C7.06738 41.2599 8.7799 43 10.8924 43H19.0674V30.5C19.0674 29.1193 20.1867 28 21.5674 28H26.5674C27.9481 28 29.0674 29.1193 29.0674 30.5V43H37.2424C39.3549 43 41.0674 41.2599 41.0674 39.1134V21.628C41.0674 20.4492 40.5409 19.3342 39.6361 18.5966L26.4611 7.85517Z\" fill=\"url(#ds-home-paint1_linear_378174_9075)\"/>\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M24.0676 9.32899L8.06752 22.5437C7.20621 23.2427 5.92946 23.1255 5.21581 22.2818C4.50215 21.4381 4.62184 20.1874 5.48315 19.4884L22.2796 5.62735C23.299 4.79992 24.7723 4.78996 25.8033 5.60353L42.6084 19.1918C43.4795 19.8792 43.6168 21.1281 42.9151 21.9814C42.2134 22.8346 40.9384 22.9691 40.0674 22.2818L24.0676 9.32899Z\" fill=\"url(#ds-home-paint2_linear_378174_9075)\"/>\n<defs>\n<linearGradient id=\"ds-home-paint0_linear_378174_9075\" x1=\"24.0674\" y1=\"27\" x2=\"13.4808\" y2=\"44.6501\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#944600\"/>\n<stop offset=\"1\" stop-color=\"#CD8E02\"/>\n</linearGradient>\n<linearGradient id=\"ds-home-paint1_linear_378174_9075\" x1=\"10.3125\" y1=\"5.23952\" x2=\"45.1728\" y2=\"31.9998\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#FFD394\"/>\n<stop offset=\"1\" stop-color=\"#FFB357\"/>\n</linearGradient>\n<linearGradient id=\"ds-home-paint2_linear_378174_9075\" x1=\"17.8174\" y1=\"0.724922\" x2=\"25.3077\" y2=\"22.4516\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#FF921F\"/>\n<stop offset=\"1\" stop-color=\"#EB4824\"/>\n</linearGradient>\n</defs>\n</svg><span>Overview</span></a></nav>",
"css": ".ds-nav{background:var(--rail,#37184a);padding:0.722222rem}.ds-nav a{font:1rem/1.45 Arial,Helvetica,sans-serif;display:flex;align-items:center;gap:1.55556rem;min-height:3.11111rem;padding:0.666667rem 1.22222rem;border-radius:0.666667rem;text-decoration:none;color:#fff;font-size:1.11111rem}.ds-nav a[aria-current=page]{background:#6c3b9d}.ds-nav a:hover{background:#62378d}.ds-nav a:focus-visible{outline:0.166667rem solid var(--focus,#a566ef);outline-offset:0.222222rem}.ds-nav svg{width:27px;height:27px}"
},
{
"name": "Working panel",
@@ -562,8 +617,8 @@
"kind": "custom",
"refersTo": "dropzone",
"description": "Dashed file target; use alongside the primary browse action.",
"html": "<button class=\"ds-drop\" type=\"button\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M14 2H5v20h14V7Z M14 2v6h5 M8 12h8 M8 16h8\"/></svg><strong>Drop files here</strong><span>or click to browse</span></button>",
"css": ".ds-drop{font:1rem/1.45 Arial,Helvetica,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;min-height:7.88889rem;background:transparent;border:0.111111rem dashed #b3b5ce;border-radius:0.444444rem;padding:0.833333rem;color:var(--muted,#6c6f90);cursor:pointer}.ds-drop svg{width:2.11111rem;height:2.11111rem;margin-bottom:0.555556rem;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}.ds-drop strong{font-size:1.11111rem;color:var(--ink,#2d1447)}.ds-drop:hover,.ds-drop.ds-dragging{border-color:var(--accent,#7544b7);background:var(--ground,#ede7f6)}.ds-drop:focus-visible{outline:0.166667rem solid var(--focus,#a566ef);outline-offset:0.222222rem}.ds-drop:disabled{opacity:.55;cursor:not-allowed}@media(min-width:769px){.ds-drop{min-height:7rem}}"
"html": "<button class=\"ds-drop\" type=\"button\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"48\" height=\"48\" viewBox=\"0 0 48 48\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M11 43H37C38.6569 43 40 41.6569 40 40V18L30 14L26 4H11C9.34315 4 8 5.34315 8 7V40C8 41.6569 9.34315 43 11 43Z\" fill=\"url(#ds-document-text-paint0_linear_72095_9761)\"/>\n<path d=\"M11 43H37C38.6569 43 40 41.6569 40 40V18L30 14L26 4H11C9.34315 4 8 5.34315 8 7V40C8 41.6569 9.34315 43 11 43Z\" fill=\"url(#ds-document-text-paint1_radial_72095_9761)\" fill-opacity=\"0.5\"/>\n<path d=\"M26 15V4L40 18H29C27.3431 18 26 16.6569 26 15Z\" fill=\"url(#ds-document-text-paint2_linear_72095_9761)\"/>\n<path d=\"M16.25 23C15.5596 23 15 23.5596 15 24.25C15 24.9404 15.5596 25.5 16.25 25.5H31.75C32.4404 25.5 33 24.9404 33 24.25C33 23.5596 32.4404 23 31.75 23H16.25ZM15 30.25C15 29.5596 15.5596 29 16.25 29H31.75C32.4404 29 33 29.5596 33 30.25C33 30.9404 32.4404 31.5 31.75 31.5H16.25C15.5596 31.5 15 30.9404 15 30.25ZM16.25 35C15.5596 35 15 35.5596 15 36.25C15 36.9404 15.5596 37.5 16.25 37.5H31.75C32.4404 37.5 33 36.9404 33 36.25C33 35.5596 32.4404 35 31.75 35H16.25Z\" fill=\"url(#ds-document-text-paint3_linear_72095_9761)\" fill-opacity=\"0.9\"/>\n<defs>\n<linearGradient id=\"ds-document-text-paint0_linear_72095_9761\" x1=\"30.4\" y1=\"4\" x2=\"33.4843\" y2=\"36.9112\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#6CE0FF\"/>\n<stop offset=\"1\" stop-color=\"#4894FE\"/>\n</linearGradient>\n<radialGradient id=\"ds-document-text-paint1_radial_72095_9761\" cx=\"0\" cy=\"0\" r=\"1\" gradientUnits=\"userSpaceOnUse\" gradientTransform=\"translate(41.3333 5.21875) rotate(134.348) scale(24.7968 14.6539)\">\n<stop offset=\"0.362364\" stop-color=\"#4A43CB\"/>\n<stop offset=\"1\" stop-color=\"#4A43CB\" stop-opacity=\"0\"/>\n</radialGradient>\n<linearGradient id=\"ds-document-text-paint2_linear_72095_9761\" x1=\"32.9766\" y1=\"9.83333\" x2=\"29.4766\" y2=\"15.6667\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#9FF0F9\"/>\n<stop offset=\"1\" stop-color=\"#B3E0FF\"/>\n</linearGradient>\n<linearGradient id=\"ds-document-text-paint3_linear_72095_9761\" x1=\"33\" y1=\"43.3\" x2=\"17.47\" y2=\"15.8049\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#9DEAFF\"/>\n<stop offset=\"1\" stop-color=\"white\"/>\n</linearGradient>\n</defs>\n</svg><strong>Drop files here</strong><span>or click to browse</span></button>",
"css": ".ds-drop{font:1rem/1.45 Arial,Helvetica,sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;min-height:7.88889rem;background:transparent;border:0.111111rem dashed #b3b5ce;border-radius:0.444444rem;padding:0.833333rem;color:var(--muted,#6c6f90);cursor:pointer}.ds-drop svg{width:2.11111rem;height:2.11111rem;margin-bottom:0.555556rem}.ds-drop strong{font-size:1.11111rem;color:var(--ink,#2d1447)}.ds-drop:hover,.ds-drop.ds-dragging{border-color:var(--accent,#7544b7);background:var(--ground,#ede7f6)}.ds-drop:focus-visible{outline:0.166667rem solid var(--focus,#a566ef);outline-offset:0.222222rem}.ds-drop:disabled{opacity:.55;cursor:not-allowed}@media(min-width:769px){.ds-drop{min-height:7rem}}"
},
{
"name": "Settings value row",
@@ -607,14 +662,19 @@
},
{
"name": "The Flat Surface Rule",
"body": "Separate regions with tone, spacing and thin borders; the implemented interface has no shadows.",
"body": "Separate shell regions with tone, spacing and thin borders; do not turn the shading inside icon artwork into a new surface treatment.",
"section": "elevation"
},
{
"name": "The Recognizable Artwork Rule",
"body": "Preserve authored color and identity: Fluent Color for interface meaning, accurate flags for regions, hardware imagery for recognized systems, original brand/provider marks and simple outlines for utility controls.",
"section": "components"
}
],
"dos": [
"Do preserve the aubergine navigation, lavender ground and purple action hierarchy.",
"Do keep labels beside icons and status text beside status indicators.",
"Do use the local heading font, inline SVG icons and the discreet credited portrait.",
"Do use the local heading font, colorful semantic artwork, recognizable hardware and brand marks, and the discreet credited portrait.",
"Do retain visible keyboard focus and stable Cancel controls while progress changes.",
"Do stack working panels and keep release information visible on narrow screens.",
"Do keep settings scope, mode, search and save status explicitly labeled; show explanations in Guided and exact configuration keys in Advanced.",
@@ -622,7 +682,8 @@
],
"donts": [
"Don't present a failed release check or an unversioned development build as up to date.",
"Don't introduce external UI fonts, icon packages or framework dependencies into this offline interface.",
"Don't introduce external UI fonts, runtime icon packages or framework dependencies into this offline interface.",
"Don't flatten colorful semantic artwork, flags, hardware or brand identities into one monochrome icon family.",
"Don't expand the credit portrait into a hero image or replace useful content with fabricated metrics.",
"Don't remove the reduced-motion override when extending transfer feedback."
]
+317 -163
View File
@@ -2,105 +2,169 @@
# RetroArch for PS5
**Your collection on the console. Your controls in the browser.**
**Your games on the TV. Your library in your hands.**
Native Vulkan rendering · Fifteen release cores · A local WebUI
33 native cores · RetroArch & EmulationStation · A local WebUI
[Download a release](https://github.com/mihawk-99/PS5_RetroArch/releases) · [Get started](#get-started) · [Supported systems](#supported-systems) · [Build from source](#build-from-source)
[Download](https://github.com/mihawk-99/PS5_RetroArch/releases) · [Get started](#get-started) · [Supported systems](#supported-systems) · [What’s new](releases/v1.0.0-beta.1.md)
</div>
![RetroArch WebUI Overview with content uploads, library folders, recent transfers and quick settings](assets/readme/webui-overview.png)
![Important warning](assets/readme/important-warning.png)
<p align="center"><strong>WebUI Overview</strong> · Open <code>http://&lt;PS5-IP&gt;:6769</code> while RetroArch is running.<br><sub>Replace &lt;PS5-IP&gt; with your console’s local address. Use a browser on the same network.</sub></p>
## PS5 RetroArch is a passion project, not a piracy project.
A native RetroArch homebrew title for jailbroken PlayStation 5 consoles, made by
[Mihawk](https://github.com/mihawk-99). Play through XMB on your TV, then upload
content, browse your library and adjust settings from your phone or computer.
The purple WebUI starts with RetroArch and closes with it.
It exists so you can play the games you own on the console you own.
**v0.6.0-alpha.6** brings the WebUI, guided settings, offline video effects and
recent stability fixes together. This is an alpha release; compatibility varies
by core and workload. See the [release notes](https://github.com/mihawk-99/PS5_RetroArch/releases/tag/v0.6.0-alpha.6) for the tested scope.
- **Piracy is not condoned.**
- **No games, BIOS files, console firmware or decryption keys are included, and they never will be.**
- **Use only legally obtained backups of games you own**, made yourself from your own discs, cartridges or digital purchases.
- **Use only BIOS and firmware files dumped from hardware you own.**
- **Requests for, or links to, games, BIOS files, firmware or keys are not welcome** in this project's issues or discussions.
## Get started
---
1. **Install the complete title folder.** Extract a release and copy its
`PPSA99169` folder to the location used by your homebrew launcher. The tested
location is `/data/homebrew/PPSA99169/`. A source build produces the same
folder in `dist/`. A compatible jailbreak and launcher must already be set up.
2. **Open RetroArch.** It creates its writable folders and initial configuration.
Existing settings are preserved. XMB is the default menu; RGUI is also available.
3. **Open the WebUI.** Visit `http://<PS5-IP>:6769` on the same network.
Choose a destination and drop your content into **Upload content**, or use FTP.
4. **Load content on the console.** Select the matching core and your file.
Install any required BIOS files first, using the locations below.
<a id="galaxy-banner"></a>
<img src="assets/readme/galaxy-banner.svg" width="960" alt="RetroArch for PlayStation 5 — a violet starfield and magenta horizon grid">
<a href="assets/releases/v1.0.0-beta.1/webui-games-neogeo-b.jpg"><img src="assets/readme/galaxy-games.svg" width="960" alt="WebUI Games library with SNK Neo Geo selected and the B filter active, in a galaxy frame"></a>
<p align="center"><strong>Your collection, beautifully organized.</strong><br>Browse, add games, and manage artwork from your phone or computer.</p>
A native RetroArch homebrew title for jailbroken PlayStation 5 consoles, made by [Mihawk](https://github.com/mihawk-99). Play through RetroArch or EmulationStation, with Vulkan graphics and up to four controllers.
**Latest release: [v1.0.0-beta.1](https://github.com/mihawk-99/PS5_RetroArch/releases/tag/v1.0.0-beta.1).** Read [what’s new](releases/v1.0.0-beta.1.md) or [download the package](https://github.com/mihawk-99/PS5_RetroArch/releases/latest).
> [!WARNING]
> **Designed around ShadowMountPlus 1.7beta4 or later.** Use [ShadowMountPlus 1.7beta4](https://github.com/drakmor/ShadowMountPlus/releases/tag/1.7beta4) or newer for supported USB and extended-storage access.
<a id="choose-your-frontend"></a>
## <img src="assets/readme/starfield-section.svg" width="960" alt=""><br><img src="webui/assets/fluent/game-chat.svg" width="26" height="26" alt=""> Choose your frontend
**RetroArch or EmulationStation.** Pick your preferred way to browse and play.
<a href="assets/readme/pre-screen.png"><img src="assets/readme/galaxy-frontends.svg" width="960" alt="Frontend picker offering RetroArch and EmulationStation, in a galaxy frame"></a>
Press **Square** to remember your choice. Hold **L1** during startup to return to the picker, or change it in **WebUI Settings → Start with**.
<a id="highlights"></a>
## <img src="webui/assets/fluent/board.svg" width="26" height="26" alt=""> At a glance
| | Made for your collection |
| --- | --- |
| <img src="webui/assets/fluent/game-chat.svg" width="36" height="36" alt=""> | **33 cores.** From NES and Mega Drive to Dreamcast, GameCube, PSP, and more. |
| <img src="webui/assets/fluent/library.svg" width="36" height="36" alt=""> | **One shared library.** Artwork and game details across RetroArch, EmulationStation, and the WebUI. |
| <img src="webui/assets/fluent/arrow-square-down.svg" width="36" height="36" alt=""> | **Easy game setup.** Add a game, find its details, and download missing artwork. |
| <img src="webui/assets/fluent/edit.svg" width="36" height="36" alt=""> | **DualSense stylus.** Use the touchpad for Nintendo DS and 3DS touchscreen controls. |
**Explore:** [Get started](#get-started) · [WebUI](#webui) · [Systems](#supported-systems) · [Controls](#controls) · [Graphics](#graphics) · [Your files](#your-files) · [Known limits](#what-has-been-verified) · [Build](#build-from-source)
<a id="get-started"></a>
## <img src="webui/assets/fluent/arrow-square-down.svg" width="26" height="26" alt=""> Get started
1. **Install the complete title folder.** Extract a release and copy `PPSA99169` to your homebrew launcher’s location, usually `/data/homebrew/PPSA99169/`. A compatible jailbreak and launcher must already be set up.
2. **Choose your frontend.** Start RetroArch or EmulationStation from the picker. Press **Square** to remember your choice; hold **L1** during startup to return to the picker. XMB and RGUI are available in RetroArch.
3. **Open the WebUI.** Scan its QR code or visit `http://<PS5-IP>:6769` from a device on the same network.
4. **Add your games.** Use **Games → + Add Game** for one game backup, or **Content** for folders and multi-file games. Install any required BIOS files in the [system folders](#your-files), then load your game on the console.
No games, BIOS files, firmware or decryption keys are included. Use your own
legally obtained backups and system files dumped from hardware you own. Piracy
is not condoned; requests for these files are not welcome in issues or discussions.
## A browser companion, built in
### Scan to connect
| Menu | Open the WebUI QR code |
| --- | --- |
| **Picker** | Press **Triangle**; Triangle again or Circle closes it |
| **RetroArch XMB** | **WebUI** tab to the left of Settings |
| **RetroArch RGUI** | **WebUI** at the top of the main menu |
| **EmulationStation** | **WebUI** in the main menu |
<a id="webui"></a>
## <img src="assets/readme/starfield-section.svg" width="960" alt=""><br><img src="webui/assets/fluent/laptop.svg" width="26" height="26" alt=""> Your browser companion
Stay connected while switching between the picker, RetroArch, and EmulationStation. A supported ELF loader or homebrew launcher keeps the WebUI running across menus; without one, it is available while RetroArch runs.
| Page | What you can do |
| --- | --- |
| **Overview** | Check release status, read what’s new, upload content and reach your library and quick settings. Development builds identify themselves honestly. |
| **Content** | Browse folders, create subfolders, upload and download files. Uploads stream to storage, can be cancelled and never overwrite an existing filename. |
| **Transfers** | Follow upload progress and results for this browser session. Downloads use your browser’s download manager. |
| **Settings** | Edit global preferences or select a core profile. Guided categories explain each setting and offer supported choices; Advanced exposes technical keys and manual values. |
Guides cover all **15 release cores**, even before their first launch. Global
preferences cover Video, Audio, Input, Saving, System and Interface. Core
categories follow each emulator’s options. Runtime-specific choices, including
BIOS lists and arcade switches, appear when the core registers them.
**Save settings, then restart RetroArch to apply them.** Global preferences,
core options and per-core RetroArch overrides are separate. Existing game or
folder overrides can take precedence. Unsaved edits survive switching categories
or Guided/Advanced mode. WebUI light/dark appearance changes immediately.
The interface, fonts and artwork are served locally; only release checking needs
internet access. Updates are announced, not installed automatically. Uploads are
limited to the content folder and 64 GiB per file. Available storage is not
reported because the console API does not provide a measured value.
| <img src="webui/assets/fluent/home.svg" width="24" height="24" alt=""> **Overview** | Check updates, alerts, recent transfers, and quick settings. |
| <img src="webui/assets/fluent/library.svg" width="24" height="24" alt=""> **Games** | Browse cover art, search, filter by system or letter, and sort your collection. |
| <img src="webui/assets/fluent/arrow-square-down.svg" width="24" height="24" alt=""> **Download Media** | Download artwork and game details for a few games or your whole library. |
| <img src="webui/assets/fluent/document-folder.svg" width="24" height="24" alt=""> **Content** | Browse folders, create subfolders, and upload or download files. |
| <img src="webui/assets/fluent/arrow-sync.svg" width="24" height="24" alt=""> **Transfers** | Follow uploads and their results. Downloads appear in your browser’s download manager. |
| <img src="webui/assets/fluent/settings.svg" width="24" height="24" alt=""> **Settings** | Adjust global preferences, core options, controller mappings, and the startup frontend. |
> The WebUI is a local HTTP service without a login. Use a trusted network and
> do not forward port **6769** to the internet.
## Supported systems
<a id="supported-systems"></a>
Release builds contain the following native cores. “GPU” means the emulator
renders through Vulkan; “software” means RetroArch presents the core’s frames
through Vulkan. These are supported systems, not a promise that every title works.
## <img src="assets/readme/starfield-section.svg" width="960" alt=""><br><img src="webui/assets/fluent/game-chat.svg" width="26" height="26" alt=""> Supported systems
| Systems | Core | Rendering |
| --- | --- | --- |
| NES / Famicom | FCEUmm | Software |
| Game Boy / Game Boy Color / Game Boy Advance | mGBA | Software |
| SNES / Super Famicom | Snes9x | Software |
| Arcade, including Neo Geo and Sega System 16/32 | FinalBurn Neo | Software |
| Mega Drive / Genesis, Master System, Game Gear, SG-1000, Sega CD | Genesis Plus GX | Software |
| PlayStation Portable | PPSSPP | GPU |
| GameCube / Wii | Dolphin | GPU |
| PlayStation 2 | LRPS2 | GPU |
| PlayStation | Beetle PSX HW | GPU |
| Nintendo 64 | Mupen64Plus-Next | GPU |
| Sega Saturn | Beetle Saturn | Software |
| Commodore 64 | VICE x64sc | Software |
| Arcade | MAME | Software |
| Nintendo DS | DeSmuME | Software |
| Nintendo 3DS | Azahar | GPU |
**33 native cores**, grouped below by family. Compatibility and performance vary by game.
Use matching arcade sets: MAME currently targets **0.289**, and FBNeo requires
sets compatible with its pinned version. Azahar requires decrypted content.
Core binaries must be built for this native SDK and loader; desktop cores or
cores from another PS5 distribution are not interchangeable.
**Vulkan (GPU)** cores render on the GPU. **Software** cores render on the CPU, with Vulkan displaying the result.
RPCS3 is **source-only and excluded from every release**. Its separate build
instructions and distribution restrictions are below. It is also excluded from
the WebUI’s guided core catalogs.
| Family | Systems | Core | Renderer |
| --- | --- | --- | --- |
| **Nintendo** | <img src="webui/assets/systems/nes.webp" width="40" height="40" alt=""> **NES / Famicom** | FCEUmm | Software |
| | <img src="webui/assets/systems/gb.webp" width="40" height="40" alt=""> **Game Boy**<br><img src="webui/assets/systems/gbc.webp" width="40" height="40" alt=""> **Game Boy Color**<br><img src="webui/assets/systems/gba.webp" width="40" height="40" alt=""> **Game Boy Advance** | mGBA | Software |
| | <img src="webui/assets/systems/snes.webp" width="40" height="40" alt=""> **SNES / Super Famicom** | Snes9x | Software |
| | <img src="webui/assets/systems/n64.webp" width="40" height="40" alt=""> **Nintendo 64** | Mupen64Plus-Next | Vulkan (GPU) |
| | <img src="webui/assets/systems/gc.webp" width="40" height="40" alt=""> **GameCube**<br><img src="webui/assets/systems/wii.webp" width="40" height="40" alt=""> **Wii** | Dolphin | Vulkan (GPU) |
| | <img src="webui/assets/systems/nds.webp" width="40" height="40" alt=""> **Nintendo DS** | DeSmuME | Software |
| | <img src="webui/assets/systems/n3ds.webp" width="40" height="40" alt=""> **Nintendo 3DS** | Azahar | Vulkan (GPU) |
| | <img src="webui/assets/systems/virtualboy.webp" width="40" height="40" alt=""> **Virtual Boy** | Beetle VB | Software |
| | <img src="webui/assets/systems/pokemini.webp" width="40" height="40" alt=""> **Pokémon Mini** | PokeMini | Software |
| **Sony** | <img src="webui/assets/systems/psx.webp" width="40" height="40" alt=""> **PlayStation** | Beetle PSX HW | Vulkan (GPU) |
| | <img src="webui/assets/systems/ps2.webp" width="40" height="40" alt=""> **PlayStation 2** | PS5SX2 | Vulkan (GPU) |
| | <img src="webui/assets/systems/psp.webp" width="40" height="40" alt=""> **PlayStation Portable** | PPSSPP | Vulkan (GPU) |
| **Sega** | <img src="webui/assets/systems/genesis.webp" width="40" height="40" alt=""> **Mega Drive / Genesis**<br><img src="webui/assets/systems/mastersystem.webp" width="40" height="40" alt=""> **Master System**<br><img src="webui/assets/systems/gamegear.webp" width="40" height="40" alt=""> **Game Gear**<br><img src="webui/assets/systems/sg-1000.webp" width="40" height="40" alt=""> **SG-1000**<br><img src="webui/assets/systems/segacd.webp" width="40" height="40" alt=""> **Sega CD** | Genesis Plus GX | Software |
| | <img src="webui/assets/systems/sega32x.webp" width="40" height="40" alt=""> **Sega 32X** | PicoDrive | Software |
| | <img src="webui/assets/systems/saturn.webp" width="40" height="40" alt=""> **Sega Saturn** | Beetle Saturn | Software |
| | <img src="webui/assets/systems/dreamcast.webp" width="40" height="40" alt=""> **Dreamcast**<br><img src="webui/assets/systems/naomi.webp" width="40" height="40" alt=""> **NAOMI / NAOMI 2**<br><img src="webui/assets/systems/atomiswave.webp" width="40" height="40" alt=""> **Atomiswave** | Flycast | Vulkan (GPU) |
| **Arcade & SNK** | <img src="webui/assets/systems/fbneo.webp" width="40" height="40" alt=""> **Arcade**<br><img src="webui/assets/systems/neogeo.webp" width="40" height="40" alt=""> **Neo Geo** | FinalBurn Neo | Software |
| | <img src="webui/assets/systems/mame.webp" width="40" height="40" alt=""> **Arcade** | MAME | Software |
| | <img src="webui/assets/systems/ngpc.webp" width="40" height="40" alt=""> **Neo Geo Pocket / Color** | Beetle NeoPop | Software |
| | <img src="webui/assets/systems/neogeocd.webp" width="40" height="40" alt=""> **Neo Geo CD** | NeoCD | Software |
| **NEC** | <img src="webui/assets/systems/pcengine.webp" width="40" height="40" alt=""> **PC Engine / TurboGrafx-16 / CD**<br><img src="webui/assets/systems/supergrafx.webp" width="40" height="40" alt=""> **SuperGrafx** | Beetle PCE | Software |
| | <img src="webui/assets/systems/pcfx.webp" width="40" height="40" alt=""> **PC-FX** | Beetle PC-FX | Software |
| **Atari** | <img src="webui/assets/systems/atari2600.webp" width="40" height="40" alt=""> **Atari 2600** | Stella | Software |
| | <img src="webui/assets/systems/atari5200.webp" width="40" height="40" alt=""> **Atari 5200** | a5200 | Software |
| | <img src="webui/assets/systems/atari7800.webp" width="40" height="40" alt=""> **Atari 7800** | ProSystem | Software |
| | <img src="webui/assets/systems/atarilynx.webp" width="40" height="40" alt=""> **Atari Lynx** | Handy | Software |
| | <img src="webui/assets/systems/atarijaguar.webp" width="40" height="40" alt=""> **Atari Jaguar** | Virtual Jaguar | Software |
| **Computers & adventures** | <img src="webui/assets/systems/c64.webp" width="40" height="40" alt=""> **Commodore 64** | VICE x64sc | Software |
| | <img src="webui/assets/systems/amiga.webp" width="40" height="40" alt=""> **Commodore Amiga** | PUAE | Software |
| | <img src="webui/assets/systems/dos.webp" width="40" height="40" alt=""> **MS-DOS** | DOSBox Pure | Software |
| | <img src="webui/assets/systems/scummvm.webp" width="40" height="40" alt=""> **Point-and-click adventures** | ScummVM | Software |
| **More consoles** | <img src="webui/assets/systems/3do.webp" width="40" height="40" alt=""> **3DO** | Opera | Software |
| | <img src="webui/assets/systems/wonderswancolor.webp" width="40" height="40" alt=""> **WonderSwan / Color** | Beetle Cygne | Software |
## Balanced graphics for a 4K display
PicoDrive also supports Mega Drive, Mega-CD, Master System, and Pico. FinalBurn Neo includes Sega System 16/32; ScummVM supports many LucasArts, Sierra, and other adventures.
Use matching arcade sets: MAME targets **0.289**, and FBNeo needs sets compatible with its version. Azahar requires decrypted content. Only native cores built for this port are compatible; desktop cores and other PS5 distributions’ cores are not interchangeable. No games or BIOS files are bundled.
None of the games I tested with is provided.
**Use only legally obtained backups of games you own**, and BIOS files dumped
from your own hardware: piracy is not condoned.
<a id="controls"></a>
## <img src="webui/assets/fluent/edit.svg" width="26" height="26" alt=""> A stylus in your DualSense
In **DeSmuME** and **Azahar**, slide a finger on the touchpad to move the stylus. **Press** to touch the screen; hold the press while sliding to drag. Release to lift the stylus. A light tap does not click.
Up to **four controllers** are supported. Save controller mappings for each game or core.
<a id="graphics"></a>
## <img src="webui/assets/fluent/image.svg" width="26" height="26" alt=""> Graphics for your display
RetroArch presents at the display resolution. Internal rendering is chosen to
leave room for emulation and memory use; it need not reach native 4K. Saved
@@ -110,26 +174,25 @@ core, folder and game options take priority over these compiled defaults.
| --- | --- | --- |
| PPSSPP | 6× · 2880 × 1632 | 8× MSAA |
| Dolphin | 4× | No MSAA |
| LRPS2 | 4× · approximately 1440p | Core default |
| PS5SX2 | 4× · approximately 1440p (up to 8×; 6× is 4K) | Core default |
| Beetle PSX HW | 8× | MSAA off |
| Mupen64Plus-Next | 4× | Core default |
| Azahar | 6× · 2400 × 1440 top screen | Core default |
| DeSmuME | 4× · 1024 × 768 per screen | Core default |
| Flycast | 4.5× · 2880 × 2160 | Per-pixel transparency |
Other cores retain native rendering, scaled for the display. MAME uses a 4K
target for vector output; that path still needs console acceptance.
PPSSPP’s **6× / 8× MSAA** default passed the recorded save/load checks. At
10× / 8× MSAA, the tested workload exhausted device memory. Failed framebuffer
allocations are now cleaned up and retried once; an unrecoverable failure returns
to the menu instead of using a missing image. This improves recovery, not the
amount of memory available. Disabling MSAA leaves more headroom.
Higher resolutions and anti-aliasing use more memory. If a game struggles, lower its internal resolution or disable MSAA.
To adopt these defaults on an existing installation, change the listed options
in the WebUI or **Quick Menu → Core Options**. Reset Core Options resets every
option for that core, so use individual controls if you want to keep other choices.
## Shaders, filters and bezels
<a id="shaders-filters-and-bezels"></a>
## <img src="webui/assets/fluent/image.svg" width="26" height="26" alt=""> Shaders, filters and bezels
Release builds bundle offline Slang shaders, Mega Bezel, koko-aio and
standard overlays. Load GPU presets through **Quick Menu → Shaders**, choose a
@@ -137,13 +200,11 @@ CPU filter in **Settings → Video**, or choose artwork in **Settings → On-Scr
Display → On-Screen Overlay**. CPU filters apply to software-rendered cores;
hardware-rendered cores use GPU shaders.
The package retains the upstream collections and their notices. Exact versions,
path corrections and the five upstream file exclusions are recorded in
`video-assets.json`. Development overlay fixtures are excluded from both the
staged title and its ZIP. Representative presets and overlay layouts have been tested on PS5. Bundling
does not imply every preset is verified; see the release notes for limitations.
Presets and overlays are included for offline use. Compatibility varies by core and preset; original notices are retained.
## Your files
<a id="your-files"></a>
## <img src="assets/readme/starfield-section.svg" width="960" alt=""><br><img src="webui/assets/fluent/document-folder.svg" width="26" height="26" alt=""> Your files
`/app0` is the running title’s mount. Over FTP, use your installed title folder,
usually `/data/homebrew/PPSA99169/`.
@@ -151,23 +212,25 @@ usually `/data/homebrew/PPSA99169/`.
| Folder or file | Purpose |
| --- | --- |
| `content/` | Your content; available to WebUI uploads and downloads |
| `content/Saturn/` | Saturn disc images; created automatically |
| `content/<system>/` | A folder for each installed system, named as EmulationStation (ES-DE) names it: `psx`, `ps2`, `snes`, `saturn`; created automatically |
| `system/` | General BIOS and system data |
| `system/Saturn/` | Saturn BIOS files; created automatically |
| `system/pcsx2/bios/` | PlayStation 2 BIOS |
| `system/pcsx2/bios/` | PlayStation 2 BIOS (your own dump) |
| `system/fbneo/` | FinalBurn Neo system files |
| `cores/` and `info/` | Native cores and their metadata |
| `config/retroarch.cfg` | Live RetroArch configuration |
| `config/webui.cfg` | Saved global WebUI preferences |
| `config/<core>/` | Core options and RetroArch overrides |
| `config/<core>/` | Core options and RetroArch overrides, per core and per game |
| `config/remaps/<core>/` | Controller remaps, per core and per game |
| `library/<system>/` | Scraped box art, screenshots and details, shared by every frontend |
| `savefiles/` and `savestates/` | Save RAM and save states |
| `shaders/shaders_slang/` | GPU presets, including Mega Bezel and koko-aio |
| `filters/` | Built-in CPU filter configurations |
| `overlays/` | Standard overlay artwork and configurations |
| `radv-shader-cache/` | Reusable compiled graphics pipelines |
**Saturn setup:** place `mpr-17933.bin` (US/Europe) or `sega_101.bin` (Japan)
in `system/Saturn/`. Keep a disc’s `.cue` and all referenced tracks together,
**Saturn setup:** place your own BIOS dump, `mpr-17933.bin` (US/Europe) or
`sega_101.bin` (Japan), in `system/Saturn/`. Keep a disc’s `.cue` and all referenced tracks together,
then load the `.cue`. The default system path is routed to the Saturn folder;
an explicitly selected custom system directory remains unchanged.
@@ -175,29 +238,76 @@ Genesis Plus GX’s Sega CD BIOS files and Beetle PSX HW’s optional BIOS files
belong in the general system folder, with the filenames listed by their metadata.
BIOS uploads use FTP; the WebUI only exposes `content/`.
Everything you put in `content/` and `system/` must be **your own legally
obtained backups**: games you own, and BIOS or firmware dumped from hardware you
own. Piracy is not condoned.
Ordinary scripted updates preserve content and saved settings. Back up your
files before replacing or removing a title. The file browser has **INTERNAL**
for the title folder and **EXTERNAL** for `/mnt`; the current title sandbox hides
external mounts, so EXTERNAL may be empty.
files before replacing or removing a title. The file browser opens on **INTERNAL**,
the title folder. With [ShadowMountPlus](https://github.com/drakmor/ShadowMountPlus)
1.7beta4 or later running, it also lists each USB drive (**USB 0** to **USB 7**,
`/mnt/usb0` onward) and extended storage (**EXTENDED 0** and **1**, `/mnt/ext0` and
`/mnt/ext1`), only when a drive is mounted there. Without it, the sandbox hides them
and **EXTERNAL** (`/mnt`) may be empty.
## What has been verified
<a id="forwarders"></a>
Saturn now loads its BIOS from `system/Saturn/`. Console testing reached
controlled gameplay with clean rendering and a normal exit. The fixes cover
executable memory for its JITs and ownership of cropped video frames. This is
a short gameplay check, not a full compatibility or long-session guarantee.
## <img src="webui/assets/fluent/game-chat.svg" width="26" height="26" alt=""> Start a game from a home screen forwarder
Native startup and exit, Vulkan presentation, controller input, stereo audio,
configuration persistence and representative core loading have console evidence.
Save-state and memory-pressure results cover the recorded workloads, not every
core or firmware. Controller rumble uses synthesized DualSense haptics with an
ordinary-rumble fallback; physical feel still awaits owner confirmation.
### Make a tile in the WebUI
The title uses [PS5_Vulkan](https://github.com/mihawk-99/PS5_Vulkan)’s RADV port.
CPU and GPU allocations share the console’s direct-memory pool; each core does
not receive a separate 11.65 GiB allowance. A selectable CPU video backend remains
available. RetroAchievements and netplay remain disabled. Broader BIOS, disc
swapping, shader-preset and long-session coverage is still in progress.
The WebUI's **Game Forwarder** page makes home screen tiles that each open one game in
RetroArch. Choose a system, then one of its games from your library, the name the home screen
shows, the core (the game's own is chosen for you) and a picture: its box art, 3D box, logo,
cartridge or disc, screenshot, title screen or fan art, filling the square or fitted in it, with
the system's icon in the bottom right corner if you like. A picture the game has no file for
yet is greyed out; **Download Media** can find it. Choose **Create tile**, then close
RetroArch: the tile appears on the home screen. A new name or picture, and a tile you delete,
reach the home screen the same way, a few seconds after RetroArch closes.
A tile is a small title of its own, `PPSA99170` to `PPSA99269` in `/data/homebrew`, which
[ShadowMountPlus](https://github.com/drakmor/ShadowMountPlus) 1.7 or later installs. The page
needs it loaded with its API on (the default), and says so when it does not answer.
ShadowMountPlus installs and removes nothing while a game runs, and RetroArch counts as one:
that is why changes wait for RetroArch to close. Delete tiles from the page, which also removes
their folder. A tile opens a game in the content folder or on a USB or extended storage drive.
### Make a forwarder by hand
A forwarder is a small app with its own home screen tile that launches `PPSA99169` with
launch arguments. The title then starts that game in RetroArch with the core given, instead of
opening the picker or a frontend.
| Argument | Meaning |
| --- | --- |
| `--rom <file>` or `--rom=<file>` | The content to start: an absolute path (`/mnt/usb0/roms/snes/Game.zip`), or a path inside the title's `content/` folder (`/app0/content/`), such as `snes/Game.zip`. A folder is content for a core that opens folders, one whose info file lists `/` among its extensions: DOSBox Pure (`--rom /mnt/usb0/roms/dos/w95 --core dosbox_pure`), PUAE, VICE. A relative path may not contain `..`. |
| `--core <core>` or `--core=<core>` | The core to run it with, one of the title's: `snes9x`, `snes9x_libretro`, `snes9x_libretro.so` and `/app0/cores/snes9x_libretro.so` all name `/app0/cores/snes9x_libretro.so`. Without it, the core RetroArch's playlists associate with the content, or else the one core whose info file lists the file's extension (`.sfc` is Snes9x's; a Mega Drive `.md` or `.gen` is Genesis Plus GX's). An extension several cores take (`.cue`, `.chd`, `.iso`, `.zip`, `.bin`) needs `--core`. |
| `--exit-after-game` | When RetroArch quits (Close Content or Quit), the title closes and the console returns to the home screen. Without it, the title then opens as from the home screen. |
```text
--rom "snes/Game (USA).zip" --core snes9x
--rom /mnt/usb0/roms/psx/Game.chd --core mednafen_psx_hw --exit-after-game
```
The game runs as a frontend's game does in game mode (`src/ps5_game.h`): the content and core
are checked the same way, RetroArch starts with `-L <core> <content>` and its own configuration,
overrides and saves apply, and its log is `retroarch-game.log`. If the content is missing, the
core is not one of the title's, or no core is named and none can be chosen, the reason is
written to `trace.txt` (`forwarder: ... not run: ...`) and the title opens as from the home
screen. A launch that names a mode (`--ps5-mode=...`) wins over `--rom`. The title's own restarts
carry no arguments, so the forwarded game never starts twice. Arguments only reach a new
process: if the title is already running, a forwarder should close it first. Parsing is in
`src/frontend_mode_ps5.cpp`, checked by `tests/frontend_mode_ps5_test.cpp`.
<a id="what-has-been-verified"></a>
## <img src="webui/assets/fluent/checkmark-circle.svg" width="26" height="26" alt=""> Compatibility and known limits
Startup, controller input, audio, saved settings, and representative games have been tested on PS5. Compatibility and long-session testing are ongoing.
- **Azahar:** a crash when closing content, switching to Azahar, and starting a game remains under investigation.
- **RetroAchievements and NetPlay:** RetroArch starts its bundled Lapy service automatically through the console's local ELF loader, then reuses it on later launches. No Payload Manager autoload entry is needed. Files stay in their installation folder; existing `/app0` settings continue to work. Without the local loader or verified elevation, outgoing online features remain unavailable. See [service setup](platform/lapy/README.txt) and [online verification](evidence/online-features/README.txt).
- Shader presets, disc swapping, and rumble still need broader testing.
[Recorded evidence](evidence/) accompanies verified changes. For a bug report,
include the core, file format, settings and reproduction steps. Preserve
@@ -207,7 +317,12 @@ before sharing captures.
---
## Build from source
<a id="build-from-source"></a>
## <img src="webui/assets/fluent/settings.svg" width="26" height="26" alt=""> Build from source
<details>
<summary><strong>Build requirements and commands</strong></summary>
The current build uses Linux host tools and the project's cached public PS5 SDK.
Start with `bash tools/doctor.sh` for host-tool checks. You also need `curl`,
@@ -227,7 +342,7 @@ workspace/
```
The cores that needed changes for the console build from my forks of them
([PS5_LRPS2](https://github.com/mihawk-99/PS5_LRPS2),
([Libretro_PS5SX2](https://github.com/mihawk-99/Libretro_PS5SX2),
[PS5_BeetlePSX](https://github.com/mihawk-99/PS5_BeetlePSX),
[PS5_Mupen64Plus](https://github.com/mihawk-99/PS5_Mupen64Plus),
[PS5_BeetleSaturn](https://github.com/mihawk-99/PS5_BeetleSaturn),
@@ -235,9 +350,25 @@ The cores that needed changes for the console build from my forks of them
[PS5_MAME](https://github.com/mihawk-99/PS5_MAME),
[PS5_DeSmuME](https://github.com/mihawk-99/PS5_DeSmuME),
[PS5_Azahar](https://github.com/mihawk-99/PS5_Azahar) with
[PS5_Dynarmic](https://github.com/mihawk-99/PS5_Dynarmic), and, for builds you
make yourself, [PS5_RPCS3](https://github.com/mihawk-99/PS5_RPCS3) with
[PS5_LLVM](https://github.com/mihawk-99/PS5_LLVM)), each pinned by revision in
[PS5_Dynarmic](https://github.com/mihawk-99/PS5_Dynarmic),
[PS5_Flycast](https://github.com/mihawk-99/PS5_Flycast),
[PS5_PicoDrive](https://github.com/mihawk-99/PS5_PicoDrive),
[PS5_BeetlePCE](https://github.com/mihawk-99/PS5_BeetlePCE),
[PS5_BeetlePCFX](https://github.com/mihawk-99/PS5_BeetlePCFX),
[PS5_BeetleVB](https://github.com/mihawk-99/PS5_BeetleVB),
[PS5_BeetleNGP](https://github.com/mihawk-99/PS5_BeetleNGP),
[PS5_BeetleWSwan](https://github.com/mihawk-99/PS5_BeetleWSwan),
[PS5_PokeMini](https://github.com/mihawk-99/PS5_PokeMini),
[PS5_Handy](https://github.com/mihawk-99/PS5_Handy),
[PS5_VirtualJaguar](https://github.com/mihawk-99/PS5_VirtualJaguar),
[PS5_Stella](https://github.com/mihawk-99/PS5_Stella),
[PS5_A5200](https://github.com/mihawk-99/PS5_A5200),
[PS5_ProSystem](https://github.com/mihawk-99/PS5_ProSystem),
[PS5_DOSBoxPure](https://github.com/mihawk-99/PS5_DOSBoxPure),
[PS5_Opera](https://github.com/mihawk-99/PS5_Opera),
[PS5_PUAE](https://github.com/mihawk-99/PS5_PUAE),
[PS5_NeoCD](https://github.com/mihawk-99/PS5_NeoCD),
[PS5_ScummVM](https://github.com/mihawk-99/PS5_ScummVM)), each pinned by revision in
its build script. The script uses the sibling checkout when there is one, and
`github.com/mihawk-99/<fork>` otherwise.
@@ -263,10 +394,36 @@ bash tools/verify.sh
The five gates are **format → unit → build → integration → evidence**. The build
pins RetroArch 1.22.2, fetches core sources/metadata with checked hashes, builds the
frontend and all sixteen cores, and stages the native title in `dist/PPSA99169/` (a
release build, `PS5_RELEASE_TAG` set, leaves RPCS3 out).
frontend and thirty-three cores, and stages the native title in `dist/PPSA99169/`.
The initial dependency/source fetch requires network access.
To include the optional melonDS DS core, build with
`PS5_WITH_MELONDSDS=1 bash tools/build-title.sh`. It uses pinned upstream sources
and the software renderer with a native-memory JIT; DeSmuME remains available.
`bash tools/build-melondsds.sh` builds and ABI-checks the core by itself.
To include the optional SwanStation core, build with
`PS5_WITH_SWANSTATION=1 bash tools/build-title.sh`. It uses pinned upstream sources,
Vulkan and the x64 recompiler, with platform changes adapted from AngelXex's
SwanStationPS5; Beetle PSX HW remains the PlayStation default.
`bash tools/build-swanstation.sh` builds and ABI-checks the core by itself.
The `ps5` microphone driver is the default. A core hears the DualSense microphone
only while it turns its microphone on (melonDS DS while its microphone button is
held, its default), the DualSense mute button silences it, and **Settings → Audio →
Microphone** turns it off for every core. Capture stays in memory; the driver does
not record files or send audio over the network.
In melonDS DS, select `Microphone` as the input mode and `Always` as its button
mode to use the DualSense mute button without assigning another game button.
The core's screen-layout and microphone indicators can be hidden independently
under **On-Screen Display & Notifications**.
The optional melonDS DS port also accepts the DualSense touchpad as a relative
stylus in Joystick or Auto touch mode. Slide to position the cursor, press to
touch, and hold the press while sliding to drag. L2 remains available as Select;
the touchpad click is reserved for the stylus while this core is running.
For an already configured checkout:
```bash
@@ -274,11 +431,18 @@ bash tools/build-title.sh # Build/stage the frontend and all shipped cores
make genesis-plus-gx # Build and ABI-check one core only
# Other core targets: fceumm, mgba, snes9x, fbneo
bash tools/build-ppsspp.sh # The larger cores have scripts of their own:
# build-ppsspp.sh, build-dolphin.sh, build-lrps2.sh,
# build-ppsspp.sh, build-dolphin.sh, build-ps5sx2.sh,
# build-beetle-psx.sh, build-mupen64plus.sh,
# build-beetle-saturn.sh, build-vice.sh,
# build-mame.sh, build-desmume.sh, build-azahar.sh,
# build-rpcs3.sh (your own builds only)
# build-flycast.sh, build-picodrive.sh,
# build-beetle-pce.sh, build-beetle-pcfx.sh,
# build-beetle-vb.sh, build-beetle-ngp.sh,
# build-beetle-wswan.sh, build-pokemini.sh,
# build-handy.sh, build-virtualjaguar.sh,
# build-stella.sh, build-a5200.sh, build-prosystem.sh,
# build-dosbox-pure.sh, build-opera.sh, build-puae.sh,
# build-neocd.sh, build-scummvm.sh
```
When adding or updating a core, rebuild the title too: the frontend's native
@@ -286,36 +450,7 @@ import table and build identity depend on the shipped core binaries. Source
patches live in `patches/`; fetched and generated trees stay in ignored
`vendor/`, `.deps/`, `build/` and `dist/` directories.
### PlayStation 3 (RPCS3): build it yourself
No release of this title carries RPCS3, and none will while its licence stands
as it does: RPCS3 is **GPL-2.0-only**, and the title it runs in is
**GPL-3.0-or-later** (this port's runtime is linked into the core, and the core
runs against the title's GPL-3.0 platform code), so the two cannot be handed
out together as one program. What is public is the source: my fork
[PS5_RPCS3](https://github.com/mihawk-99/PS5_RPCS3) and this repository's build
scripts. **If you want RPCS3, you must compile it yourself**, for your own
console:
```bash
bash tools/build-title.sh # a development build: the frontend and every core, RPCS3 included
```
Leave `PS5_RELEASE_TAG` unset: a release build (`PS5_RELEASE_TAG=...`) leaves
RPCS3 out, and `tools/check-notices.py --release` refuses a title with any
RPCS3 file in it. Build the whole title, not the core alone: the title's native
import table is made from the cores it is built with, so a release title cannot
load an RPCS3 core built on its own. `tools/build-rpcs3.sh` builds only the
core (from PS5_RPCS3, with LLVM from my fork PS5_LLVM, at their pinned
revisions), for work on it.
Keep what you build for your own console: **do not share, upload or
redistribute the binaries you build.** RPCS3 needs your own copy of the PS3
system software (`PS3UPDAT.PUP`, from Sony's official PS3 system software
update page) in `system/RPCS3/`, where it installs on the first start, and
your own games: a disc you own, dumped yourself, or a PSN purchase with its
`.rap` licence file. Piracy is not condoned.
</details>
<details>
<summary><strong>Authors and acknowledgements</strong></summary>
@@ -341,6 +476,12 @@ lists and original notices.
| [RetroArch assets contributors](https://github.com/libretro/retroarch-assets) and the [M+ Fonts project](https://mplusfonts.github.io/) | Packaged XMB assets and font; original notices retained |
| [LLVM / Clang contributors](https://github.com/llvm/llvm-project), [zlib authors Jean-loup Gailly and Mark Adler](https://github.com/madler/zlib) | Compilation and compression tooling |
### Frontends and interface artwork
EmulationStation uses [ES-DE](https://es-de.org/) and the Alekfull NX theme. Touchpad handling builds on PS5_Proton; the picker uses PS5_VulkanTemplate’s UI foundation.
Color icons: **Microsoft Fluent Color**. System artwork: **Linear ES-DE**. Region flags: **flag-icons**. Utility icons: **Lucide**. Full attribution and licences are in [artwork credits](webui/assets/ICON-CREDITS.txt).
### Emulator cores
| Core | Authors / maintainers credited by upstream |
@@ -352,20 +493,35 @@ lists and original notices.
| [Genesis Plus GX](https://github.com/libretro/Genesis-Plus-GX) | Charles MacDonald, Eke-Eke and contributors |
| [PPSSPP](https://github.com/hrydgard/ppsspp) | Henrik Rydgård and contributors |
| [Dolphin](https://github.com/dolphin-emu/dolphin), [libretro/dolphin](https://github.com/libretro/dolphin) | Dolphin Emulator Project and contributors; libretro core maintainers |
| [PCSX2](https://github.com/PCSX2/pcsx2), [LRPS2](https://github.com/libretro/LRPS2) | PCSX2 Dev Team and contributors; libretro LRPS2 maintainers |
| [PCSX2](https://github.com/PCSX2/pcsx2), [PS5SX2](https://github.com/Swordpdf/PS5SX2) | PCSX2 Dev Team and contributors; Swordpdf (PS5SX2) |
| [Beetle PSX HW](https://github.com/libretro/beetle-psx-libretro), [Beetle Saturn](https://github.com/libretro/beetle-saturn-libretro) | The Mednafen authors and contributors; libretro Beetle maintainers |
| [Mupen64Plus-Next](https://github.com/libretro/mupen64plus-libretro-nx) | Mupen64Plus team and contributors; libretro core maintainers; ParaLLEl-RDP and ParaLLEl-RSP by Hans-Kristian Arntzen (Themaister) and contributors |
| [VICE](https://github.com/libretro/vice-libretro) | The VICE Team and contributors; libretro core maintainers |
| [MAME](https://github.com/libretro/mame) | MAMEdev and contributors |
| [DeSmuME](https://github.com/libretro/desmume) | DeSmuME team and contributors |
| [Azahar](https://github.com/azahar-emu/azahar), [Dynarmic](https://github.com/azahar-emu/dynarmic) | Azahar contributors, building on Citra; Dynarmic by merryhime and contributors |
| [RPCS3](https://github.com/RPCS3/rpcs3) | RPCS3 Team and contributors |
| [Flycast](https://github.com/flyinghead/flycast) | flyinghead and contributors, building on reicast and nullDC; PS5 port begun by rpf16rj |
| [PicoDrive](https://github.com/libretro/picodrive) | notaz, fDave and the PicoDrive contributors |
| [Beetle PCE](https://github.com/libretro/beetle-pce-libretro), [Beetle PC-FX](https://github.com/libretro/beetle-pcfx-libretro), [Beetle VB](https://github.com/libretro/beetle-vb-libretro), [Beetle NeoPop](https://github.com/libretro/beetle-ngp-libretro), [Beetle Cygne](https://github.com/libretro/beetle-wswan-libretro) | The Mednafen authors and contributors; libretro Beetle maintainers |
| [PokeMini](https://github.com/libretro/PokeMini) | JustBurn and contributors |
| [Handy](https://github.com/libretro/libretro-handy) | K. Wilkins and contributors |
| [Virtual Jaguar](https://github.com/libretro/virtualjaguar-libretro) | James Hammons and the Virtual Jaguar contributors |
| [Stella](https://github.com/stella-emu/stella) | Bradford W. Mott, Stephen Anthony and the Stella Team |
| [a5200](https://github.com/libretro/a5200) | The Atari800 team and the a5200 contributors |
| [ProSystem](https://github.com/libretro/prosystem-libretro) | Greg Stanton and contributors |
| [DOSBox Pure](https://codeberg.org/schelling/dosbox-pure) | Bernhard Schelling, building on DOSBox by the DOSBox Team |
| [Opera](https://github.com/libretro/opera-libretro) | The FreeDO authors, the 4DO and Opera contributors |
| [PUAE](https://github.com/libretro/libretro-uae) | The UAE, WinUAE and PUAE authors and contributors |
| [NeoCD](https://github.com/libretro/neocd_libretro) | Laurent Cayrol and contributors |
| [ScummVM](https://github.com/scummvm/scummvm) | The ScummVM Team and contributors |
| [libretro core-info](https://github.com/libretro/libretro-core-info) | Metadata maintainers and contributors |
</details>
## License and third-party terms
<a id="license-and-third-party-terms"></a>
## <img src="webui/assets/fluent/book-open.svg" width="26" height="26" alt=""> License and third-party terms
This repository's own code is **GPL-3.0-or-later** ([LICENSE](LICENSE)). Most source
files carry a copyright and SPDX notice; the ones that do not (for example
@@ -377,25 +533,23 @@ is Copyright (C) 2026 BlackBearReloaded, GPL-3.0-or-later. The title's
Every built title folder carries `LEGAL.txt` (the legal notice above) and
`licenses/`: the licence texts each part requires, and `components.json`, which
ties every executable file to the source revision it was built from
([tooling/notices/components.json](tooling/notices/components.json)). Each
release also carries the source archives of everything in it. Releases up to
v0.5.0-alpha.5 were published without `licenses/`.
**RPCS3 is licensed GPL-2.0-only, which is incompatible with this port's
GPL-3.0-or-later.** For that reason no release of this title contains RPCS3 or
any of its files, and you must compile it yourself from source if you want it
(see [PlayStation 3 (RPCS3): build it yourself](#playstation-3-rpcs3-build-it-yourself));
the binaries you build are for your own console only.
([tooling/notices/components.json](tooling/notices/components.json)). The package records public source links and exact revisions for every component.
The release tag contains the build scripts and patches; `tools/source-bundle.py`
can assemble the corresponding source archives from a prepared build checkout.
Releases up to v0.5.0-alpha.5 were published without `licenses/`.
The cores keep their own licences, and they differ:
| Licence | Cores |
| --- | --- |
| GPL-2.0-or-later | FCEUmm, PPSSPP, Dolphin, Beetle PSX HW, Beetle Saturn, Mupen64Plus-Next (with MIT and LGPL parts), VICE, DeSmuME, Azahar (Dynarmic is 0BSD), MAME (as a whole; many files BSD-3-Clause) |
| GPL-3.0-or-later | LRPS2 (PCSX2) |
| GPL-2.0-only | RPCS3: **not in any release; compile it yourself from source.** Its source is public and it builds with this repository, but it cannot be distributed together with this port's GPL-3.0 code |
| GPL-2.0-or-later | FCEUmm, PPSSPP, Dolphin, Beetle PSX HW, Beetle Saturn, Mupen64Plus-Next (with MIT and LGPL parts), VICE, DeSmuME, Azahar (Dynarmic is 0BSD), Flycast, Beetle PCE, Beetle PC-FX, Beetle VB, Beetle NeoPop, Beetle Cygne, Stella, a5200, ProSystem, DOSBox Pure, PUAE, MAME (as a whole; many files BSD-3-Clause) |
| GPL-3.0-or-later | PS5SX2 (PCSX2), PokeMini, Virtual Jaguar, ScummVM |
| LGPL-3.0-or-later | NeoCD |
| Zlib | Handy |
| MPL-2.0 | mGBA |
| Non-commercial licences | Snes9x, FinalBurn Neo, Genesis Plus GX: they may not be sold or used commercially, and FBNeo's forbids asking for donations for a project that uses its code |
| Non-commercial licences | Snes9x, FinalBurn Neo, Genesis Plus GX, PicoDrive, Opera (FreeDO's modified LGPL): they may not be sold or used commercially, and FBNeo's forbids asking for donations for a project that uses its code |
The Alekfull NX theme also carries non-commercial restrictions. Artwork credits and licence texts for the WebUI’s icons, flags, and system images are in [ICON-CREDITS.txt](webui/assets/ICON-CREDITS.txt).
Assets and fonts keep their licences too: the XMB theme is CC-BY-4.0 with the M+
font licence, PPSSPP's fonts are OFL-1.1, and Dolphin's `Sys` files carry theirs.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

+26
View File
@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="560" viewBox="0 0 1600 560" role="img" aria-labelledby="title desc">
<title id="title">RetroArch for PS5</title><desc id="desc">A violet starfield above a glowing magenta grid, inspired by the PS5 RetroArch homebrew background.</desc>
<defs>
<linearGradient id="sky" x2="0" y2="1"><stop stop-color="#060410"/><stop offset=".55" stop-color="#110b2d"/><stop offset="1" stop-color="#291050"/></linearGradient>
<radialGradient id="violet"><stop stop-color="#773bc0" stop-opacity=".62"/><stop offset="1" stop-color="#56208c" stop-opacity="0"/></radialGradient>
<radialGradient id="pink"><stop stop-color="#db5db4" stop-opacity=".30"/><stop offset="1" stop-color="#db5db4" stop-opacity="0"/></radialGradient>
<linearGradient id="floor" x2="0" y2="1"><stop stop-color="#130b32"/><stop offset="1" stop-color="#39104e"/></linearGradient>
<linearGradient id="fade" x2="0" y2="1"><stop offset=".56" stop-color="black"/><stop offset=".77" stop-color="white" stop-opacity=".13"/><stop offset="1" stop-color="white" stop-opacity=".65"/></linearGradient>
<mask id="ground"><rect width="1600" height="560" fill="url(#fade)"/></mask>
<linearGradient id="text" x2="1" y2=".5"><stop stop-color="#fff"/><stop offset="1" stop-color="#d8cbff"/></linearGradient>
</defs>
<rect width="1600" height="560" fill="url(#sky)"/>
<ellipse cx="1260" cy="255" rx="640" ry="350" fill="url(#violet)"/>
<ellipse cx="1080" cy="357" rx="620" ry="180" fill="url(#pink)"/>
<circle cx="380.0" cy="190.7" r="1.5" fill="#ecc3ff" opacity="0.62"/><circle cx="627.1" cy="422.3" r="0.6" fill="#ecc3ff" opacity="0.27"/><circle cx="1215.8" cy="41.3" r="0.8" fill="#faf6ff" opacity="0.73"/><circle cx="740.4" cy="113.3" r="0.8" fill="#ecc3ff" opacity="0.73"/><circle cx="406.9" cy="207.1" r="0.8" fill="#b9c5ff" opacity="0.20"/><circle cx="369.6" cy="291.1" r="1.0" fill="#b9c5ff" opacity="0.46"/><circle cx="576.2" cy="267.0" r="0.6" fill="#b9c5ff" opacity="0.42"/><circle cx="1026.4" cy="64.8" r="1.2" fill="#b9c5ff" opacity="0.80"/><circle cx="677.3" cy="94.1" r="1.5" fill="#b9c5ff" opacity="0.21"/><circle cx="710.2" cy="508.2" r="1.5" fill="#faf6ff" opacity="0.45"/><circle cx="1073.5" cy="461.9" r="1.2" fill="#faf6ff" opacity="0.47"/><circle cx="1568.4" cy="308.4" r="0.8" fill="#faf6ff" opacity="0.30"/><circle cx="334.8" cy="301.6" r="0.8" fill="#b9c5ff" opacity="0.80"/><circle cx="592.3" cy="300.6" r="0.6" fill="#faf6ff" opacity="0.76"/><circle cx="1317.2" cy="247.3" r="1.2" fill="#ecc3ff" opacity="0.41"/><circle cx="423.9" cy="347.9" r="0.8" fill="#faf6ff" opacity="0.60"/><circle cx="1420.2" cy="235.7" r="0.6" fill="#b9c5ff" opacity="0.65"/><circle cx="364.1" cy="29.1" r="1.2" fill="#b9c5ff" opacity="0.26"/><circle cx="373.9" cy="97.2" r="1.5" fill="#ecc3ff" opacity="0.37"/><circle cx="299.3" cy="273.4" r="1.0" fill="#ecc3ff" opacity="0.57"/><circle cx="1298.7" cy="109.6" r="0.8" fill="#ecc3ff" opacity="0.35"/><circle cx="159.8" cy="241.9" r="0.8" fill="#faf6ff" opacity="0.78"/><circle cx="286.9" cy="361.0" r="1.5" fill="#faf6ff" opacity="0.51"/><circle cx="518.3" cy="430.9" r="1.0" fill="#faf6ff" opacity="0.57"/><circle cx="1522.6" cy="116.2" r="1.5" fill="#b9c5ff" opacity="0.21"/><circle cx="346.1" cy="406.8" r="1.5" fill="#faf6ff" opacity="0.72"/><circle cx="1594.3" cy="505.2" r="1.5" fill="#b9c5ff" opacity="0.25"/><circle cx="813.3" cy="165.9" r="1.0" fill="#b9c5ff" opacity="0.52"/><circle cx="633.5" cy="193.4" r="0.8" fill="#b9c5ff" opacity="0.18"/><circle cx="1163.0" cy="475.7" r="1.2" fill="#ecc3ff" opacity="0.25"/><circle cx="1231.1" cy="311.7" r="0.8" fill="#ecc3ff" opacity="0.67"/><circle cx="596.2" cy="68.3" r="0.8" fill="#b9c5ff" opacity="0.34"/><circle cx="255.6" cy="13.0" r="1.5" fill="#ecc3ff" opacity="0.27"/><circle cx="920.2" cy="193.3" r="1.2" fill="#faf6ff" opacity="0.28"/><circle cx="955.9" cy="444.9" r="0.6" fill="#ecc3ff" opacity="0.26"/><circle cx="1073.1" cy="337.0" r="1.2" fill="#faf6ff" opacity="0.79"/><circle cx="812.4" cy="75.1" r="1.0" fill="#b9c5ff" opacity="0.73"/><circle cx="1128.6" cy="9.7" r="0.8" fill="#faf6ff" opacity="0.27"/><circle cx="1537.3" cy="158.0" r="1.2" fill="#faf6ff" opacity="0.72"/><circle cx="708.0" cy="96.1" r="1.5" fill="#faf6ff" opacity="0.25"/><circle cx="30.1" cy="29.0" r="1.2" fill="#ecc3ff" opacity="0.58"/><circle cx="397.0" cy="409.8" r="1.5" fill="#b9c5ff" opacity="0.60"/><circle cx="831.1" cy="117.5" r="0.8" fill="#faf6ff" opacity="0.62"/><circle cx="1099.5" cy="263.3" r="0.6" fill="#faf6ff" opacity="0.52"/><circle cx="1160.1" cy="187.3" r="1.2" fill="#b9c5ff" opacity="0.43"/><circle cx="1420.9" cy="442.2" r="1.0" fill="#b9c5ff" opacity="0.79"/><circle cx="1497.9" cy="334.3" r="0.6" fill="#faf6ff" opacity="0.53"/><circle cx="1194.1" cy="403.1" r="1.0" fill="#b9c5ff" opacity="0.59"/><circle cx="204.9" cy="42.3" r="1.0" fill="#faf6ff" opacity="0.49"/><circle cx="21.5" cy="112.7" r="1.5" fill="#ecc3ff" opacity="0.73"/><circle cx="775.1" cy="72.3" r="0.6" fill="#ecc3ff" opacity="0.54"/><circle cx="1026.1" cy="179.9" r="0.8" fill="#b9c5ff" opacity="0.25"/><circle cx="1431.4" cy="295.6" r="0.6" fill="#b9c5ff" opacity="0.64"/><circle cx="1290.2" cy="182.8" r="0.6" fill="#b9c5ff" opacity="0.37"/><circle cx="1086.8" cy="394.4" r="1.2" fill="#ecc3ff" opacity="0.27"/><circle cx="204.0" cy="210.9" r="0.8" fill="#ecc3ff" opacity="0.73"/><circle cx="258.8" cy="46.3" r="1.0" fill="#ecc3ff" opacity="0.31"/><circle cx="872.0" cy="186.0" r="1.5" fill="#ecc3ff" opacity="0.40"/><circle cx="1597.3" cy="178.8" r="0.6" fill="#ecc3ff" opacity="0.52"/><circle cx="406.7" cy="251.0" r="1.0" fill="#b9c5ff" opacity="0.20"/><circle cx="1275.6" cy="494.4" r="1.2" fill="#ecc3ff" opacity="0.78"/><circle cx="105.5" cy="148.6" r="1.2" fill="#faf6ff" opacity="0.34"/><circle cx="1377.4" cy="205.2" r="1.0" fill="#faf6ff" opacity="0.36"/><circle cx="1416.2" cy="495.8" r="0.6" fill="#b9c5ff" opacity="0.65"/><circle cx="437.3" cy="27.9" r="0.6" fill="#b9c5ff" opacity="0.57"/><circle cx="865.5" cy="77.9" r="0.6" fill="#faf6ff" opacity="0.70"/><circle cx="171.8" cy="232.8" r="1.0" fill="#b9c5ff" opacity="0.78"/><circle cx="967.8" cy="75.5" r="0.8" fill="#faf6ff" opacity="0.38"/><circle cx="1312.6" cy="78.7" r="1.2" fill="#b9c5ff" opacity="0.43"/><circle cx="1212.9" cy="244.1" r="1.5" fill="#ecc3ff" opacity="0.22"/><circle cx="935.5" cy="231.7" r="1.5" fill="#ecc3ff" opacity="0.49"/><circle cx="782.0" cy="41.5" r="0.8" fill="#ecc3ff" opacity="0.24"/><circle cx="1173.4" cy="Line truncated
<path d="M0 367H1600V560H0Z" fill="url(#floor)"/>
<g fill="none" stroke="#de54ec" stroke-width="1.25" mask="url(#ground)"><path d="M800 315 L-2000 560"/><path d="M800 315 L-1760 560"/><path d="M800 315 L-1520 560"/><path d="M800 315 L-1280 560"/><path d="M800 315 L-1040 560"/><path d="M800 315 L-800 560"/><path d="M800 315 L-560 560"/><path d="M800 315 L-320 560"/><path d="M800 315 L-80 560"/><path d="M800 315 L160 560"/><path d="M800 315 L400 560"/><path d="M800 315 L640 560"/><path d="M800 315 L880 560"/><path d="M800 315 L1120 560"/><path d="M800 315 L1360 560"/><path d="M800 315 L1600 560"/><path d="M800 315 L1840 560"/><path d="M800 315 L2080 560"/><path d="M800 315 L2320 560"/><path d="M800 315 L2560 560"/><path d="M800 315 L2800 560"/><path d="M800 315 L3040 560"/><path d="M800 315 L3280 560"/><path d="M800 315 L3520 560"/><path d="M800 315 L3760 560"/><path d="M0 377 H1600"/><path d="M0 386 H1600"/><path d="M0 399 H1600"/><path d="M0 417 H1600"/><path d="M0 444 H1600"/><path d="M0 486 H1600"/><path d="M0 549 H1600"/></g>
<path d="M0 373H1600" stroke="#a16be8" opacity=".2"/>
<svg x="96" y="74" width="56" height="34" xmlns="http://www.w3.org/2000/svg" viewBox="7 15 34 19"><path fill="#ffffff" d="M15.2214499,30.6589508l1.8361292-2.0981903-2.6117897-.00597c-1.4364796-.0032806-2.6114798-.0085793-2.6111097-.0117493.0002804-.00317.1577702-.6325893.3497696-1.3986893l.3491001-1.3929195h-5.60711l.9638901-3.8554993.9638901-3.8554993h2.0806599l-.5253801,2.0981903c-.2889605,1.1540108-.5256901,2.1008396-.5260696,2.1040707-.0002804.0032196.7925396.0058899,1.7620497.0058899h1.7627401l.0164003-.0646496c.0090303-.0355606.2452698-.9797401.5249901-2.0981903l.5085897-2.0335407,5.0112696-.0235004-1.0804691-1.32827c-.5942593-.7305498-1.1016502-1.3573599-1.1275406-1.3929195l-.0470695-.0646496h2.0714703l1.22439,1.3987999,1.22439,1.3987999h4.5306797l1.22439-1.3987999,1.22439-1.3987999h2.0714703l-.0470695.0646496c-.0258808.0355597-.5332794.6623697-1.1275406,1.3929195l-1.0804691,1.32827,5.0112686.0235004.5085907,2.0335407c.2797203,1.1184502.5159683,2.0626392.5249901,2.0981903l.0163994.0646496h1.7627411c.9695091,0,1.7624283-.0025196,1.7620506-.0058899-.0002785-.0032196-.2371101-.9500599-.5260696-2.1040707l-.5253792-2.0981903h2.0806618l.9638901,3.8554993.9638901,3.8554993h-5.6071091l.3490982,1.3929195c.1920013.7660999.3493996,1.3955193.3497696,1.3986893.0002785.00317-1.1746292.0084705-2.6111107.0117493l-2.6117802.00597,1.8361301,2.0981903,1.8361397,2.0981903-3.185791-.0028-1.7044106-2.1022892-1.7044201-2.1022892h-8.0401287l-1.7044201,2.1022892-1.7044106,2.1022892-3.1857901.0028,1.8361588-2.0981903ZM19.9799404,21.8959103v-1.7514305h-3.5263691l-.0060806,1.7279205c-.0033398.9503593-.0011196,1.7412205.0051899,1.7574596.0089397.0234108.37533.0282898,1.7692699.0235004l1.7579899-.0060291v-1.751421ZM31.5464401,20.1444798l-3.5146198-.0120602v3.5269699l3.5146198-.0120602v-3.5028496Z"/></svg>
<text x="173" y="100" fill="#ddd5f0" font-family="Arial, sans-serif" font-size="22" letter-spacing="5">PLAY YOUR WAY</text>
<text x="89" y="254" fill="url(#text)" font-family="Arial, sans-serif" font-weight="700" font-size="116" letter-spacing="-5">RetroArch</text>
<text x="97" y="326" fill="#d4c6eb" font-family="Arial, sans-serif" font-size="44" letter-spacing="1">for PlayStation 5</text>
<text x="98" y="453" fill="#ded4ee" font-family="Arial, sans-serif" font-size="24">Your games on the TV. Your library in your hands.</text>
<g fill="#eee7ff" opacity=".8"><path d="M1290 125v20m-10-10h20" stroke="#eee7ff"/><circle cx="1290" cy="135" r="2"/><circle cx="1430" cy="250" r="2"/></g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2028" height="1188" viewBox="0 0 2028 1188" role="img" aria-labelledby="title">
<title id="title">Original PS5 RetroArch screenshot in a galaxy frame</title>
<defs>
<linearGradient id="sky" x2="1" y2=".7"><stop stop-color="#080512"/><stop offset=".55" stop-color="#19102f"/><stop offset="1" stop-color="#342047"/></linearGradient>
<radialGradient id="glow"><stop stop-color="#944acc" stop-opacity=".38"/><stop offset="1" stop-color="#944acc" stop-opacity="0"/></radialGradient>
<linearGradient id="rim"><stop stop-color="#765bad"/><stop offset=".6" stop-color="#aa71c3"/><stop offset="1" stop-color="#d18ebe"/></linearGradient>
</defs>
<rect width="2028" height="1188" rx="18" fill="url(#sky)"/>
<ellipse cx="1622.4" cy="891.0" rx="1216.8" ry="1069.2" fill="url(#glow)"/>
<circle cx="592.6" cy="521.0" r="1.4" fill="#e3d6ff" opacity="0.33"/><circle cx="175.9" cy="849.4" r="0.8" fill="#e3d6ff" opacity="0.50"/><circle cx="443.3" cy="144.7" r="1.2" fill="#e3d6ff" opacity="0.21"/><circle cx="739.4" cy="281.9" r="1.4" fill="#e3d6ff" opacity="0.23"/><circle cx="189.9" cy="975.2" r="0.8" fill="#e3d6ff" opacity="0.50"/><circle cx="1001.6" cy="1173.7" r="0.6" fill="#e3d6ff" opacity="0.56"/><circle cx="158.1" cy="582.5" r="0.8" fill="#e3d6ff" opacity="0.46"/><circle cx="1233.6" cy="377.2" r="0.8" fill="#e3d6ff" opacity="0.22"/><circle cx="1492.8" cy="790.1" r="0.8" fill="#e3d6ff" opacity="0.61"/><circle cx="1604.9" cy="202.0" r="0.7" fill="#e3d6ff" opacity="0.20"/><circle cx="1578.2" cy="20.7" r="0.9" fill="#e3d6ff" opacity="0.21"/><circle cx="1056.7" cy="910.3" r="0.6" fill="#e3d6ff" opacity="0.56"/><circle cx="86.1" cy="513.1" r="1.2" fill="#e3d6ff" opacity="0.51"/><circle cx="885.6" cy="909.1" r="0.8" fill="#e3d6ff" opacity="0.60"/><circle cx="234.8" cy="912.0" r="0.8" fill="#e3d6ff" opacity="0.31"/><circle cx="1571.1" cy="758.9" r="0.9" fill="#e3d6ff" opacity="0.32"/><circle cx="274.2" cy="1132.7" r="0.7" fill="#e3d6ff" opacity="0.45"/><circle cx="539.7" cy="554.7" r="1.2" fill="#e3d6ff" opacity="0.30"/><circle cx="1711.2" cy="1079.3" r="1.0" fill="#e3d6ff" opacity="0.34"/><circle cx="82.2" cy="336.2" r="1.0" fill="#e3d6ff" opacity="0.22"/><circle cx="202.1" cy="528.9" r="0.9" fill="#e3d6ff" opacity="0.30"/><circle cx="750.5" cy="173.0" r="1.4" fill="#e3d6ff" opacity="0.29"/><circle cx="1911.9" cy="1176.5" r="0.9" fill="#e3d6ff" opacity="0.38"/><circle cx="1968.6" cy="364.7" r="1.2" fill="#e3d6ff" opacity="0.53"/><circle cx="1808.2" cy="165.6" r="0.7" fill="#e3d6ff" opacity="0.58"/><circle cx="1518.2" cy="758.3" r="0.8" fill="#e3d6ff" opacity="0.34"/><circle cx="1345.8" cy="55.0" r="0.7" fill="#e3d6ff" opacity="0.49"/><circle cx="359.1" cy="80.3" r="0.9" fill="#e3d6ff" opacity="0.20"/><circle cx="1854.8" cy="1004.8" r="1.1" fill="#e3d6ff" opacity="0.46"/><circle cx="1854.5" cy="832.9" r="0.7" fill="#e3d6ff" opacity="0.41"/><circle cx="384.5" cy="197.5" r="0.9" fill="#e3d6ff" opacity="0.35"/><circle cx="1976.1" cy="881.4" r="1.3" fill="#e3d6ff" opacity="0.41"/><circle cx="1535.0" cy="551.6" r="1.0" fill="#e3d6ff" opacity="0.52"/><circle cx="1192.0" cy="799.8" r="1.1" fill="#e3d6ff" opacity="0.47"/><circle cx="1880.4" cy="917.2" r="1.1" fill="#e3d6ff" opacity="0.35"/><circle cx="515.9" cy="846.1" r="0.6" fill="#e3d6ff" opacity="0.47"/><circle cx="1718.9" cy="1168.3" r="1.3" fill="#e3d6ff" opacity="0.27"/><circle cx="682.3" cy="364.2" r="1.3" fill="#e3d6ff" opacity="0.24"/><circle cx="576.3" cy="1110.9" r="1.2" fill="#e3d6ff" opacity="0.32"/><circle cx="1119.1" cy="1090.3" r="1.4" fill="#e3d6ff" opacity="0.28"/><circle cx="892.2" cy="527.1" r="0.8" fill="#e3d6ff" opacity="0.39"/><circle cx="532.2" cy="453.9" r="0.6" fill="#e3d6ff" opacity="0.38"/><circle cx="911.4" cy="823.6" r="0.9" fill="#e3d6ff" opacity="0.60"/><circle cx="1393.0" cy="90.0" r="0.7" fill="#e3d6ff" opacity="0.38"/><circle cx="1297.5" cy="712.5" r="1.0" fill="#e3d6ff" opacity="0.32"/><circle cx="1416.5" cy="762.1" r="1.2" fill="#e3d6ff" opacity="0.34"/><circle cx="1482.7" cy="743.2" r="0.9" fill="#e3d6ff" opacity="0.38"/><circle cx="634.4" cy="502.7" r="0.9" fill="#e3d6ff" opacity="0.22"/><circle cx="1922.1" cy="246.7" r="0.7" fill="#e3d6ff" opacity="0.34"/><circle cx="1294.7" cy="861.7" r="1.1" fill="#e3d6ff" opacity="0.29"/><circle cx="1596.3" cy="480.6" r="0.5" fill="#e3d6ff" opacity="0.60"/><circle cx="416.1" cy="135.3" r="1.0" fill="#e3d6ff" opacity="0.53"/><circle cx="218.6" cy="52.2" r="1.1" fill="#e3d6ff" opacity="0.38"/><circle cx="976.3" cy="79.8" r="0.5" fill="#e3d6ff" opacity="0.57"/><circle cx="308.2" cy="303.3" r="1.4" fill="#e3d6ff" opacity="0.22"/><circle cx="1244.3" cy="71.2" r="1.3" fill="#e3d6ff" opacity="0.61"/><circle cx="374.2" cy="933.1" r="0.6" fill="#e3d6ff" opacity="0.22"/><circle cx="1028.2" cy="916.1" r="1.0" fill="#e3d6ff" opacity="0.46"/><circle cx="1917.7" cy="643.3" r="1.3" fill="#e3d6ff" opacity="0.22"/><circle cx="1697.2" cy="715.9" r="1.0" fill="#e3d6ff" opacity="0.32"/><circle cx="465.8" cy="464.5" r="0.9" fill="#e3d6ff" opacity="0.23"/><circle cx="1670.1" cy="306.5" r="0.7" fill="#e3d6ff" opacity="0.30"/><circle cx="1764.7" cy="1145.1" r="1.2" fill="#e3d6ff" opacity="0.61"/><circle cx="1551.2" cy="895.5" r="0.9" fill="#e3d6ff" opacity="0.47"/><circle cx="329.9" cy="416.1" r="0.8" fill="#e3d6ff" opacity="0.57"/><circle cx="955.0" cy="273.8" r="1.1" fill="#e3d6ff" opacity="0.25"/><circle cx="1060.4" cy="851.1" r="1.0" fill="#e3d6ff" opacity="0.50"/><circle cx="315.8" cy="1000.0" r="0.7" fill="#e3d6ff" opacity="0.24"/><circle cx="1820.3" cy="321.2" r="0.6" fill="#e3d6ff" opacity="0.63"/><circle cx="1088.7" cy="15.1" r="0.8" fill="#e3d6ff" opacity="0.39"/><circle cx="911.7" cy="149.5" r="0.7" fill="#e3d6ff" opacity="0.41"/><circle cx="67.7" cy="101.9" r="0.8" fill="#e3d6ff" opacity="0Line truncated
<image x="54" y="54" width="1920" height="1080" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAB4AAAAQ4CAIAAABnsVYUAAEAAElEQVR42uydd9wcVfX/z9k86ZWakELvvYNIB2nSey+CCCgq+lXsHcWfIioWQKT33kFABKUTSmih1ySkkB7Sk/P7Y3dnbjnnzsw+m9A+7xcveLI7c+bOndl9wud+5nN44oz5zExEIlL/IUFzS7I2zErlPxOxviUVlWpskP2sblPfQN+m/oo0Nos3KDrd1GaJfQtGRcTNaQkmpzleY8aIxNlMLbgoSgEAAAAAAAAAAAAAUAlp0+uLqZR4UmTwVqmTFbNmYR3rrWBUruTobhD8u+oJNscvzCzNt92fo1LiKsDxURuyp6MP1+pbF6rPzJwdOLGhW6qxccWZdTdIyNNuEWZjfqWhPltjrh/CGky2o35hJHVFy6jPFEnGlijsfkIsmZ2TG0snSgEAAAAAAAAAAAAAQJ3QncugqltSZRdL45ISuyQUwuwfcy8psKiqUqGUG1a2WTy8rGBC4Syjx3JTKXY13qJS5rDYtymLSEddVk6qzxUESUd65sQdkh0t7U1Oa9DB68yt3Ndsu7ADHVmSYrpboUA0b/Xzye37AENiBgAAAAAAAAAAAABtJ2GsdCMBqITWzL5ls3wpjgpykQSXstsWyXaZ0KzaWN1XUnq0VBuVtZlqkU5YbIN5c9MvAg06E5CZWYTKC8VEVCuRvCFNtbo4o6O+Wf3HpFVbl25jf3F9dlS7cUra57KLMu4A4mUHz+VeJUVEHTOX+HwWPiNQfh2pjaUAAAAAAAAAAAAAAKDOuSHT0a/cplLpQ3BRyoJ1dCtlIeFQDmqmXLZcYlSc8jWLkNiG3bqAmfBxsyftKjpw9kcRYW6kZVA5oZgnfbigRAB0vqLA5Z3GBaEeKfdxsIZQeEzm1FHKhE1XrVxyYIpTu8wsJ7ObS2ZAF5ZKiOCwSAMAAAAAAAAAAAAA6kQER+BQliJzdLtSBCR5aCu0oUx+RSKqtzBAOG2ObqFy+QEk0p8L4jXS6c+lS9XK7SDcVMilrODKVu/BxnJBiXSStEU8Lds3NihjpG/JCSy2TFsYDdPGuAxOpuRU+kZgBHQAAAAAAAAAAAAAAOqs8TnthuTqpQLbJZdI/EiMipXwB13NK6kRWxbm7GDp/nOWelloWy4Z0OGmHIcnqIQ6JEKV2Q3JKOGAbuxXizOg1cNIM1GCS94cnGotWHIG09383JiOtP+8QGUuEX8dF3HV7fTyRSL+QkqMSJL2Z0l+rtJWa644KgAAAAAAAAAAAAAAqNU2ZmyoWGVMzeXDbLm02lb8Fheoi3lAguFV5XT6AnfWI1uqsRzbfRErFss0ZGYuOV7mpgNamljqtRMyLZ1ya3Poa1bzndVOjol3C23UVCWfOwiJVpcI0ge1YqCrhtoUPo9QuM7DVR5tgPcZAAAAAAAAAAAAAFDnUjgS6cxUWhCTZFSstKTScqFjt4RVuVIjwYTwWD+cFOUDSxUFOu30raZx27WkRAqHNGdTRGq+yMyJferCdpb0kUgDISeU2poRV9VN2aWpoFFj5qROq8B6QDgX3w3piI/0qKx1DzE+itnr5ReRpBOpzdbRIUMDAAAAAAAAAAAAAGrVAc2RxpXQuyxpmA1/dGGptA4mtos5kcFQ6EMtr+V6kQ9O+zpzVGkJO+mydYMoiK0TzH3JiVrNUlJXkFkTRsURhNnRRmtOZocU50yTcyRm1SKdDdxqhuiWLw6AJjNnI52/4eVNWynS/u7WTZZY30jbq9mI4LA+P2VScqTcR7EoeaZCKQAAAAAAAAAAAAAAaJFlQ1uOTG6pFFVUqBMe08IedYmBihNQYAX2ctIUW1LXztVLTg2ezdPn3JecBzWzUSoVj8GOSiwizFTXh/MmhJyIvDbCPuLAjSBOOiFnMxfdEI7yK0UeZCuEpUxKBpPZC7IuYVfuQckN77a5OpH8hEjyoYPy/UCl6PmFTj65AAAAAAAAAAAAAAAAdVpfYvu5/xZKSbIUt3ZeklRMJelQNpysUi46o4zzWjrZ2I1zCVc6W6s5mGbHQiLqcOXkzEQdXydHuuZCbdrdOLE9O10mE+JyOrvEE/KNOumUluwo4TacyvdIHJHsZYfE50oie7L16EHJZZ82lgIAAAAAAAAAAAAAgBalPC1VdpGWuiCmCkqx0Ti9C0dOZzUQo8LRi0zQ3iGK7N+pE2zqxZ3q/BeI6c0gDhHhiTPmN/TiosvT2CHLd9ZE6kZUtDMIo6VhQ70tWD0oimMus296m8IxFErb8bvutTczOqp/SNr1emu7AAAAAAAAAAAAAABALcnN4nuTRUuslY+oVKFQK1IQ81FKWU4Oq4z2m8iJTqQfpzfI5yRSfcXfVPUlq0p0PKu1rHFhGfU5LVZmKRyFXQ3T2d5xiLMdkl22e6NIiaAPMSOkS+Z8K1HiSZFXtNTmdKpG+WWfwk9a+VIAAAAAAAAAAAAAAFBp0VmMZAxV3UrnZrS3FKv5AUKFDlfmysKZJx6KGcFRxqGbVp+zTomWKTsRYkyB+ixSb+8XbJ2Jw5ECbKrPWQRHLU+aSM5xeQ+2G+lR6FqvZyUntsmiUhJSbsEMUnG3QHWb7OhW5TJ3hr6kYIjR0omHCJgqJLinDe/wQQMAAAAAAAAAAAAAajWCmWw3JBueS0toXqSlXCVXFXAlmYJARVp8kN2RiOyQZL6z1aWw8JVM23TjKNIN/9yAZav/X5yBoeqKWZEOIs0+HVxCJ7Mjm2srDzpuapiOx+Yq5uL4Fki0TsxuHe6cpOouJpTs1NiYoqQuzO37ACeaDZYs1fKoAAAAAAAAAAAAAACgEm0GyfAsVzIWc9RQTUqU4nRCLxkZCU2xzFJvEwkb7IuE6TNUxDouSKAutIqrqmZwFoEY6LQiNEXqTBPmIsty9m6NmYUyY7WYlyQPgC68H0SN2U70BpROhD64iwBKsDfnd0nivisZ5cGld2m8qxmoxU7b4ORHIqEOJ5zOJUtJSx94AAAAAAAAAAAAAACoRO6rLJpSUq6UFG1hRVikvc9lojlEUgbctLKsjioMAU7OReCkFk0lF8VQLAlBWZqUiwqRmpPoIWzbetlXuC37czBQ0fVy6kyERXCBmfOOkIoJOhGkIvkhmM02gwVCsxVG0xyb0qKQCgTl1jp1cvUYDTGWm2CCBgAAAAAAAAAAAADUCaczV1GTpX2lrFGx3Z8woQ2WFLgTImdCZXY1Uj3imRrKp6VbFkqEbgRH9RPgSH1uGpSzrn3pSs0/1FQftapwM+dNBRNStTSMx8TM9XRtSc57egrSs+O2+7NTtO13ubhFoXWNG69LMgRD9F6c6Y6cLQQ3s3EUqlJKSvQJBQAAAAAAAAAAAACAKgbJZnkAJW2U0olSiTwAKdMtkHR7a0I/TDiUCzOKE6pmFl+cOnScGaIZtNMiu2FqDpsQ5rHMDXHY7EMozWaG9T1rWQURSdtnXZd1sgch18eczaCumXJjgtIO9vTVbVwhJ+hZilzf4f3K3pWw7nflLeceYvvOY1buksIeg4lFh/LLO62VAgAAAAAAAAAAAACAOm2CJirrKWaqoGsVOi9byIBOVZPO2p9V5ddNJy5ZNqGPp6tx0oMruc7JhROWO5gTW4oEPQ1rmcYdh5tE1etNCFNJHXlzyFzRFnVixDH6WqsE2UUSM0MkHDBXuRWyQ6RWOdhUyRvJKckPXHqtIxHEbC3UWIIyJz3OUroUIYIDAAAAAAAAAAAAAFBnk6CrpTAb4rJUz8Tg6v3VMpNrC/EMlkKde16D0Gq1iR0VhwMrO/rdEVMSq+2zzrskeqqvGKWkofqKvgpQF4RFGlJ13adcq0eJ1HEbHTLH9mcp7m/o5ng0+yFa3uF0GLS7+MBJ4T/tIU+vJBQ2S8y91aIvU3AiwKW09EwllN8WbMuSfB5BID0DAAAAAAAAAAAAAFrkJmgu3SEw6FJWqVSe/1AuGKQMIk4YryobGu5jp++ebxjmMIKjtZgCpQUd25sVFsvE4WS+Mxf19GsU4qZUTcTMNRJxhGBOi7Zsacp+XkgWjZyQqqX01SUp7kbYGq7JOhEHrrqYJWnfpnIW/UprQVLiIy3lPpAI3wAAAAAAAAAAAAAAtHiDOAJpU+xmg1yUFpDO6Kj6emZvNYMvEuZo0WXAMtZboYKcaGtU1drHORbbRAKEL2hKIqW53oewSIzOjdI86cMF0oj64DInw1XkXjcWhKukW1Taxr1mbCeft9bqkLlgYFLCOOwWYWecEn20pOhTKm36aFml0ocAAAAAAAAAAAAAAIAquh7Tr3PpqA0pIcCmSwUbiNNmMKHb5lkZdr9Bs32d0WlQpNieXdLemg53ru7ZFaOUVM1oqJeqiQhFmrUY/mkvh6VoAuoua10DZS+ppNBEbHmTgxaCTGZKRrrVYWvu6awDoUTGY3bCZlLqdpUM6IRkzMbrVKKUlF47AgAAAAAAAAAAAACAqEDs4iqNzdJtA5nKRjxTuVJWfgD7gp4atVxJI/a0VttAzUlFt6QDWnc3iyefsp4iIlHYg54FkZmemz+E5cRQn5m5g5lFSMhrLci24MpVvM9sGcClVHRGYch3WkeulIAhUi3NI3dAi/KpEtJv2VLZI3ZQeqVd7FUZEuQ+AwAAAAAAAAAAAAD6yOI4KBK1ZLGU0i3SkttYPd2MSXwvc0F+RSK5oboZubxc2VDJSyjgwWgzwzGrPlktEiPTrF3zcfrS1LLIZmnEN9t25qzLodPK0JKexWl6GHqT2Wnj2DkHtHoJJW4WaUc2SzNg2lpMsIbnrn6wtpmI2aeSS6zJWE0CrYcI0kJz+VLle5ICAAAAAAAAAAAAAECaTsWlvcmqHiXtK5UeVaEMyE0ZOiUEsy1ssl42ezEhS7raZimTrhS7wkWSamRRLUkmdRjeaqllIi5HIRvhPk4Hwnorw1R1EWpo1rqLine truncated
</svg>

After

Width:  |  Height:  |  Size: 1.2 MiB

+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1334" height="2021" viewBox="0 0 1334 2021" role="img" aria-labelledby="title">
<title id="title">Original PS5 RetroArch screenshot in a galaxy frame</title>
<defs>
<linearGradient id="sky" x2="1" y2=".7"><stop stop-color="#080512"/><stop offset=".55" stop-color="#19102f"/><stop offset="1" stop-color="#342047"/></linearGradient>
<radialGradient id="glow"><stop stop-color="#944acc" stop-opacity=".38"/><stop offset="1" stop-color="#944acc" stop-opacity="0"/></radialGradient>
<linearGradient id="rim"><stop stop-color="#765bad"/><stop offset=".6" stop-color="#aa71c3"/><stop offset="1" stop-color="#d18ebe"/></linearGradient>
</defs>
<rect width="1334" height="2021" rx="18" fill="url(#sky)"/>
<ellipse cx="1067.2" cy="1515.75" rx="800.4" ry="1818.9" fill="url(#glow)"/>
<circle cx="389.8" cy="886.4" r="1.4" fill="#e3d6ff" opacity="0.33"/><circle cx="115.7" cy="1444.9" r="0.8" fill="#e3d6ff" opacity="0.50"/><circle cx="291.6" cy="246.1" r="1.2" fill="#e3d6ff" opacity="0.21"/><circle cx="486.4" cy="479.6" r="1.4" fill="#e3d6ff" opacity="0.23"/><circle cx="124.9" cy="1659.0" r="0.8" fill="#e3d6ff" opacity="0.50"/><circle cx="658.8" cy="1996.7" r="0.6" fill="#e3d6ff" opacity="0.56"/><circle cx="104.0" cy="991.0" r="0.8" fill="#e3d6ff" opacity="0.46"/><circle cx="811.5" cy="641.7" r="0.8" fill="#e3d6ff" opacity="0.22"/><circle cx="982.0" cy="1344.1" r="0.8" fill="#e3d6ff" opacity="0.61"/><circle cx="1055.7" cy="343.7" r="0.7" fill="#e3d6ff" opacity="0.20"/><circle cx="1038.2" cy="35.2" r="0.9" fill="#e3d6ff" opacity="0.21"/><circle cx="695.1" cy="1548.5" r="0.6" fill="#e3d6ff" opacity="0.56"/><circle cx="56.6" cy="872.9" r="1.2" fill="#e3d6ff" opacity="0.51"/><circle cx="582.6" cy="1546.5" r="0.8" fill="#e3d6ff" opacity="0.60"/><circle cx="154.4" cy="1551.4" r="0.8" fill="#e3d6ff" opacity="0.31"/><circle cx="1033.5" cy="1291.0" r="0.9" fill="#e3d6ff" opacity="0.32"/><circle cx="180.4" cy="1926.9" r="0.7" fill="#e3d6ff" opacity="0.45"/><circle cx="355.0" cy="943.6" r="1.2" fill="#e3d6ff" opacity="0.30"/><circle cx="1125.6" cy="1836.0" r="1.0" fill="#e3d6ff" opacity="0.34"/><circle cx="54.1" cy="572.0" r="1.0" fill="#e3d6ff" opacity="0.22"/><circle cx="133.0" cy="899.8" r="0.9" fill="#e3d6ff" opacity="0.30"/><circle cx="493.7" cy="294.3" r="1.4" fill="#e3d6ff" opacity="0.29"/><circle cx="1257.6" cy="2001.5" r="0.9" fill="#e3d6ff" opacity="0.38"/><circle cx="1294.9" cy="620.4" r="1.2" fill="#e3d6ff" opacity="0.53"/><circle cx="1189.4" cy="281.7" r="0.7" fill="#e3d6ff" opacity="0.58"/><circle cx="998.7" cy="1290.0" r="0.8" fill="#e3d6ff" opacity="0.34"/><circle cx="885.2" cy="93.6" r="0.7" fill="#e3d6ff" opacity="0.49"/><circle cx="236.2" cy="136.6" r="0.9" fill="#e3d6ff" opacity="0.20"/><circle cx="1220.1" cy="1709.4" r="1.1" fill="#e3d6ff" opacity="0.46"/><circle cx="1219.9" cy="1416.8" r="0.7" fill="#e3d6ff" opacity="0.41"/><circle cx="252.9" cy="336.0" r="0.9" fill="#e3d6ff" opacity="0.35"/><circle cx="1299.9" cy="1499.3" r="1.3" fill="#e3d6ff" opacity="0.41"/><circle cx="1009.7" cy="938.3" r="1.0" fill="#e3d6ff" opacity="0.52"/><circle cx="784.1" cy="1360.6" r="1.1" fill="#e3d6ff" opacity="0.47"/><circle cx="1236.9" cy="1560.3" r="1.1" fill="#e3d6ff" opacity="0.35"/><circle cx="339.4" cy="1439.4" r="0.6" fill="#e3d6ff" opacity="0.47"/><circle cx="1130.7" cy="1987.5" r="1.3" fill="#e3d6ff" opacity="0.27"/><circle cx="448.8" cy="619.6" r="1.3" fill="#e3d6ff" opacity="0.24"/><circle cx="379.1" cy="1889.8" r="1.2" fill="#e3d6ff" opacity="0.32"/><circle cx="736.1" cy="1854.8" r="1.4" fill="#e3d6ff" opacity="0.28"/><circle cx="586.9" cy="896.6" r="0.8" fill="#e3d6ff" opacity="0.39"/><circle cx="350.1" cy="772.2" r="0.6" fill="#e3d6ff" opacity="0.38"/><circle cx="599.5" cy="1401.1" r="0.9" fill="#e3d6ff" opacity="0.60"/><circle cx="916.3" cy="153.1" r="0.7" fill="#e3d6ff" opacity="0.38"/><circle cx="853.5" cy="1212.2" r="1.0" fill="#e3d6ff" opacity="0.32"/><circle cx="931.8" cy="1296.5" r="1.2" fill="#e3d6ff" opacity="0.34"/><circle cx="975.3" cy="1264.4" r="0.9" fill="#e3d6ff" opacity="0.38"/><circle cx="417.3" cy="855.3" r="0.9" fill="#e3d6ff" opacity="0.22"/><circle cx="1264.3" cy="419.8" r="0.7" fill="#e3d6ff" opacity="0.34"/><circle cx="851.7" cy="1465.9" r="1.1" fill="#e3d6ff" opacity="0.29"/><circle cx="1050.1" cy="817.5" r="0.5" fill="#e3d6ff" opacity="0.60"/><circle cx="273.7" cy="230.1" r="1.0" fill="#e3d6ff" opacity="0.53"/><circle cx="143.8" cy="88.9" r="1.1" fill="#e3d6ff" opacity="0.38"/><circle cx="642.2" cy="135.7" r="0.5" fill="#e3d6ff" opacity="0.57"/><circle cx="202.7" cy="515.9" r="1.4" fill="#e3d6ff" opacity="0.22"/><circle cx="818.5" cy="121.1" r="1.3" fill="#e3d6ff" opacity="0.61"/><circle cx="246.1" cy="1587.3" r="0.6" fill="#e3d6ff" opacity="0.22"/><circle cx="676.3" cy="1558.4" r="1.0" fill="#e3d6ff" opacity="0.46"/><circle cx="1261.4" cy="1094.4" r="1.3" fill="#e3d6ff" opacity="0.22"/><circle cx="1116.4" cy="1217.9" r="1.0" fill="#e3d6ff" opacity="0.32"/><circle cx="306.4" cy="790.2" r="0.9" fill="#e3d6ff" opacity="0.23"/><circle cx="1098.5" cy="521.4" r="0.7" fill="#e3d6ff" opacity="0.30"/><circle cx="1160.8" cy="1948.0" r="1.2" fill="#e3d6ff" opacity="0.61"/><circle cx="1020.4" cy="1523.5" r="0.9" fill="#e3d6ff" opacity="0.47"/><circle cx="217.0" cy="707.9" r="0.8" fill="#e3d6ff" opacity="0.57"/><circle cx="628.2" cy="465.8" r="1.1" fill="#e3d6ff" opacity="0.25"/><circle cx="697.5" cy="1447.8" r="1.0" fill="#e3d6ff" opacity="0.50"/><circle cx="207.7" cy="1701.2" r="0.7" fill="#e3d6ff" opacity="0.24"/><circle cx="1197.4" cy="546.4" r="0.6" fill="#e3d6ff" opacity="0.63"/><circle cx="716.1" cy="25.7" r="0.8" fill="#e3d6ff" opacity="0.39"/><circle cx="599.7" cy="254.4" r="0.7" fill="#e3d6ff" opacity="0.41"/><circle cx="44.6" cy="173.3" r="0.8" fill="#e3d6ff"Line truncated
<image x="35" y="35" width="1264" height="1951" href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAefBPADASIAAhEBAxEB/8QAHQABAAEFAQEBAAAAAAAAAAAAAAYDBAUHCAIBCf/EAGYQAAEDAwICBAYMCAoIAwYADwECAwQABRESIQYxBxNBURQiMmFxkQgzNFJTcoGSobHB0RUjQnOCk7LSFhc1NlRVYnSUsyQ3VnWiwuHwGEO0RGNkg9PxJTiVo8MohOImRUZlJ3by/8QAHAEBAAIDAQEBAAAAAAAAAAAAAAEEAgMFBgcI/8QASBEAAgECBAMECAUDAQUIAgIDAAECAxEEEiExBUFREzJhcRQigZGhsdHwBhVSweEjM/FCNFNigrIWJENUcpPS4geiY3OSNcL/2gAMAwEAAhEDEQA/ANEUpSuuXRSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFdD+xC93cT/AJuP9blc8V0P7EL3dxP+bj/W5Wqt3GYVO6dKUpSueVhSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlaK6VOkO6Iv0m02WSuHGiq6txxvZbixz35gA7bd1WMPh5YiWWJlCDk7I3rSuTv4YcSf17c/8Sv76fww4k/r25/4lf31f/KJ/qRt7B9TrGlcnfww4k/r25/4lf30/hhxJ/Xtz/wASv76flE/1Idg+p1jSuTv4YcSf17c/8Sv76fww4k/r25/4lf30/KJ/qQ7B9TrGlcnfww4k/r25/wCJX99P4YcSf17c/wDEr++n5RP9SHYPqdY0rk7+GHEn9e3P/Er++n8MOJP69uf+JX99Pyif6kOwfU6xpXJ38MOJP69uf+JX99P4YcSf17c/8Sv76flE/wBSHYPqdY0rk7+GHEn9e3P/ABK/vp/DDiT+vbn/AIlf30/KJ/qQ7B9TrGlcnfww4k/r25/4lf30/hhxJ/Xtz/xK/vp+UT/Uh2D6nWNK5O/hhxJ/Xtz/AMSv76fww4k/r25/4lf30/KJ/qQ7B9TrGlcnfww4k/r25/4lf31M+jTpFuzV/iW68ynJsKU4GQp45W2pRwkhXMjOMg1hU4VUhFyTTsQ6LSub+pSlcs0ilW7baXwVu+MMkJSeQAOOVevBmPgkeqpBWpVHwZj4JHqp4Mx8Ej1UBWpVHwZj4JHqp4Mx8Ej1UBWpVHwZj4JHqp4Mx8Ej1UBWpVHwZj4JHqp4Mx8Ej1UBWpVHwZj4JHqp4Mx8Ej1UBWpVHwZnsbCT3p2Prr7HUopUlZypCtOe/tH0GgKtKUqAKUpQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAfnJSlK6pcFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAUpSgFKUoBSlKAV0P7EL3dxP8Am4/1uVzxXQ/sQvd3E/5uP9blaq3cZhU7p0pSlK55WI9xpxKzw1bUvKQHZTxKWWicZI5k+YZHrA7c1raPxNxxd0PzLapwsNk7MsN6UnGdKdQJVgdm5q56b0vou1tcUT4MphSUDP5QV423oKah1huXVuNSJq3XY9pSZEdhA5uFacAkDYasEk92Bzr1PD8FT9EVZRUpPqr+FkjynEMZVljHRzOMV0dvG7Zsbo96Q3LpObtV7DYkubMyEjSFn3qhyBO+CMA8sZ57Mrlzh55+bxna3EY8IdntryE7AlwHOO4c66jrn8awlPDVV2atdXt0OtwnEVK1J9o72e4qGca8br4cvtntESyy7tPuaHVstx3EIOGwCryiByJPyVM61V0mOS7b0n8E3xqz3a4wYLMxL/4OiLfUgrbCU5CeW57T2GuTBJvU6yJVwdxtC4jeuEN2LKtV1t+DKgzkhDjaTuFgg4KT3j7RnKNcTWJ6FJmNXu2LiRsde+mW2UNZOBqVnCd++tOXuw8R8cL414hh2mbafCrUm1wYkxPUyJAS4lxZUn8nISUjJ31Vc8DcHi8TJ4n/AIcDL1mNseMy1MwWgFY8QBISpakEZCsEf2qzcI73JsjczlzgNSIcd2bFQ/MBMZtTqQp8AZOgZyrAOTjO1W8a/WqaZSLdcoMx+MkqdaYkIWpGPfAEkfLWlOD+HeKL+xcVXiHLgz7LYHLHblSEKbDr6gtJeQo8xpCBqHfX3oy4Zmt3KxuSm783KtVveYdbdtTMZhsqb0louAJU9k4UCNfLJO5o6aV9RY2lwjxtBvfA1v4muSo9njSwrxZMlISghak41nSDnTnlWaVfLSmCxNVdIIhvrDbT5kI6txZOAlKs4JPcK0Tw7YLnbOHujK5Xqx3CTbbSJomwRFUt1ha1r6twskaiBseWRsax/EtpmRuC7jLlWl6HbrpxWxIhWt5AbX1RyN0HZBX3HHLuqezTejFkb/h8UWGbBlzYl6tr0OIcSH0SUFDPxlZwPlr6eJrCG5Dhvdr6uNjr1eFt4azy1HPi589aUvVnnXeR0i3OycP3GDbZVpbhtR1w1MuSpCSMqS1jJwARnFZa3cERv4wOHFPcOgW8cM9XJKon4rr84ws4x1mCefjVGSPUWRtyZe7VCgNTplzgx4TuOrkOyEIbXncYUTg0kXu1Rm4zki5wWm5KSphTkhCQ6ANRKST4wA3OOzeudbTw1xDG4b6P5s9i7w48GPNYfS3bRKfirU8soJjrSThSdIzp2GOVSKPwa+hHRvEcttzlW9q4ypD7c2Kn/R0LGoBxKMoQkq3AOOeMDlR00uYsjaV+454dsvD34akXWI9AKw0hcd5DnWrJA0owcEjOT3AE9lXXEPELFs4Pn8QQ+quEaNEXLb6p0aHkhORhYyMHv3rSPE3Cs9rhvpCYhWKV4OL4xLiMtRThbeU61NJxuMc9PYK2xxsPwt0T3pNohSP9JtjojxRHUhw5QdKQ3jIPmxmocUrCxluG+JbffIMJbcqIic/EaluQkyErcZStIVuNjgZxnAq6Rf7OuBFmou1vVClOhiPIElBbecJICEKzhSsgjA3yDWm+GbVLm8UdHTcHh2faV2OA4LpJkRSylRWyEhIV/wCYSrUe8at8HOMDZol7HAfBvC6+G70iZZOImnZbxiK6oN9c4rUlX5Qw5zGwAyTuMz2a6ixti/8AH6rMy+65BiSQi7tWtKY9wS4oa/y3AE+Ioe8O/nrK8a8ZW3hi03F9UiHIuMSOqQm3mUlt1wAZ2G5Ax24NajvNgvDn8IurtNwV1vGUeU3pjLOtkc3Btuge+5VT4ps85lPSbbZfDFxuVxur/hVvlsxC6hTWBpAc7CjHk8zyANSoR0Jsjfdmm/hK0QZ3V9V4Uw29o1atOpIOM9uM1eViuE2nGOFrM082tt1uEylaFghSSEAEEHkaytaXuYFrAJIfySfxyxv6auqtbf5Mj88v66uqmW5LLVVwipujduU8BNcZVIS1g5LaVJSpWeWxWkfLV1UC4hu9ts3SpapF4uEOBHVZZTaXZTyWklXXxzpBUQM4BOPMax/GF2tF8vMGOmRbpMBUFx9mS5MUpl5Rc0FLKWyOsdBTzCspzgDJqVC9hY2R4Sz4WI3Wo8IKOsDefG05xnHdk1VrQtllOyo0O5tSOu4lXwk4mK4p3LrkxAUkpAz4zgHMc+099ZThdlvwZ6Rbb/DjxlWd8SnLap+S6FFKdLzgOcOoOogHCjlQ3wcZOnYmxuGVJZiR1vynUNMoGVLWcAek1VrRl9chyuHr1BT4HNS1EYkrnW6e46woJfSCXEEkNrwVHOpWRnJ2qf8ASEppPRs9+CZJRH/0VLD0d3OEF5sApUDvt21DhsRYmlWUe5xX7rMtzSyZcRtp11OkgBLmrTvyOdCvVWqeLIDEXi9y2SpFviWpm2tm3ouMp5CSsrcLqm1BWVOg6M5JVgjHM1ZXk3WLEvyVzVuSE2+xonS8ONFTPWuh5xQGFpBTqKuRAKu6pULk2N4UrTrTqbVYb47Hl2+VaJLkOOGrdKdRFjqW5pW4XjkJSUqTrCeQTvjVmrWDKjtP8RWxF9h26Gl6DJjlpTpirBKgsA6shpSkpSpQUE6tu3BjsxY3ZStJ3W4KkWmMwyIkSzovfVTX/D3XoDqfB9SMObFLRc0Ap8kLGCTk1K+DmIUaA0m5XKM/HVdibWmM68GkOdV7WhRP4xOzqgN07kfk7HCyuRY2DWLvd/t1kLCbg84lx/V1TTLLjziwndRCEJKsDIycYGRnnWF4/lfgNdp4kW4tES3PluaEk4Md0aCSO3Svq1Z7AFVGuHSeH79bLxxRIRE/CVtfccflOaUMvuPJdLOo7JIQUgDtDR7qhRurixsu3zY1xgsTILyH4r6A424g5CknkRWNtnFFnudwMKFL6x/xyjLS0od0HCi2sgJcwTvpJxUf4LcMfgyNblO+DXG4idJhMrBSrq1PLWg47NKXG/Rmo50fXVyM5wjAiXtc7MJTdzty22h+Dg013JSFtkOAIwsnVnPZmpybixsi73+32l9DU1UkLWnWOqiOujGcc0JIHLlV1ablEu9uYn253roj41NuaSnUM45EA9lRzjC7om8NW1u0Ssov0hmIzJaOCGnMqWtJ7+rSvB78GsuWrhBkQotqi29NmZbS2oKcWlxAG2EJCSCAAMZIrG2gMuTgZNWdmuUa82mHcoCyuJLaS80opKSUqGQcHlWnLBeGFcQ8J3Vl6NLine truncated
</svg>

After

Width:  |  Height:  |  Size: 533 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="72" viewBox="0 0 1600 72" role="img" aria-labelledby="title">
<title id="title">Subtle violet starfield</title>
<defs>
<linearGradient id="sky" x2="1" y2=".7"><stop stop-color="#080512"/><stop offset=".55" stop-color="#19102f"/><stop offset="1" stop-color="#342047"/></linearGradient>
<radialGradient id="glow"><stop stop-color="#944acc" stop-opacity=".38"/><stop offset="1" stop-color="#944acc" stop-opacity="0"/></radialGradient>
<linearGradient id="rim"><stop stop-color="#765bad"/><stop offset=".6" stop-color="#aa71c3"/><stop offset="1" stop-color="#d18ebe"/></linearGradient>
</defs>
<rect width="1600" height="72" rx="18" fill="url(#sky)"/>
<ellipse cx="1280.0" cy="54.0" rx="960.0" ry="64.8" fill="url(#glow)"/>
<circle cx="1067.4" cy="56.3" r="1.3" fill="#e3d6ff" opacity="0.26"/><circle cx="134.2" cy="47.9" r="1.0" fill="#e3d6ff" opacity="0.57"/><circle cx="1266.6" cy="16.6" r="0.9" fill="#e3d6ff" opacity="0.51"/><circle cx="1264.5" cy="18.6" r="0.8" fill="#e3d6ff" opacity="0.62"/><circle cx="860.1" cy="28.1" r="1.3" fill="#e3d6ff" opacity="0.60"/><circle cx="53.3" cy="38.1" r="0.9" fill="#e3d6ff" opacity="0.40"/><circle cx="305.4" cy="37.9" r="0.7" fill="#e3d6ff" opacity="0.49"/><circle cx="542.7" cy="40.1" r="0.8" fill="#e3d6ff" opacity="0.29"/><circle cx="659.3" cy="6.6" r="0.7" fill="#e3d6ff" opacity="0.42"/><circle cx="934.2" cy="25.6" r="1.4" fill="#e3d6ff" opacity="0.38"/><circle cx="150.1" cy="58.5" r="0.6" fill="#e3d6ff" opacity="0.38"/><circle cx="23.5" cy="30.9" r="1.1" fill="#e3d6ff" opacity="0.58"/><circle cx="1305.9" cy="2.1" r="1.0" fill="#e3d6ff" opacity="0.36"/><circle cx="861.5" cy="26.5" r="0.8" fill="#e3d6ff" opacity="0.51"/><circle cx="280.0" cy="39.0" r="0.5" fill="#e3d6ff" opacity="0.37"/><circle cx="532.9" cy="48.9" r="0.7" fill="#e3d6ff" opacity="0.33"/><circle cx="1579.4" cy="44.7" r="1.4" fill="#e3d6ff" opacity="0.25"/><circle cx="224.2" cy="41.6" r="0.7" fill="#e3d6ff" opacity="0.37"/><circle cx="120.4" cy="27.5" r="1.4" fill="#e3d6ff" opacity="0.34"/><circle cx="373.0" cy="62.8" r="0.7" fill="#e3d6ff" opacity="0.34"/><circle cx="1071.2" cy="40.1" r="0.9" fill="#e3d6ff" opacity="0.51"/><circle cx="215.0" cy="52.8" r="1.0" fill="#e3d6ff" opacity="0.28"/><circle cx="1375.2" cy="23.4" r="1.0" fill="#e3d6ff" opacity="0.64"/><circle cx="243.2" cy="68.5" r="1.3" fill="#e3d6ff" opacity="0.46"/><circle cx="243.7" cy="44.0" r="1.0" fill="#e3d6ff" opacity="0.61"/><circle cx="301.5" cy="44.0" r="0.7" fill="#e3d6ff" opacity="0.44"/><circle cx="2.7" cy="36.9" r="0.6" fill="#e3d6ff" opacity="0.34"/><circle cx="203.4" cy="17.4" r="0.8" fill="#e3d6ff" opacity="0.40"/><circle cx="781.1" cy="28.6" r="1.3" fill="#e3d6ff" opacity="0.52"/><circle cx="726.3" cy="39.0" r="0.6" fill="#e3d6ff" opacity="0.50"/><circle cx="1572.2" cy="69.9" r="1.3" fill="#e3d6ff" opacity="0.48"/><circle cx="895.7" cy="31.5" r="1.1" fill="#e3d6ff" opacity="0.29"/><circle cx="825.0" cy="43.0" r="1.1" fill="#e3d6ff" opacity="0.30"/><circle cx="896.7" cy="37.3" r="0.7" fill="#e3d6ff" opacity="0.60"/><circle cx="1078.0" cy="12.6" r="0.6" fill="#e3d6ff" opacity="0.26"/><circle cx="598.0" cy="43.3" r="1.1" fill="#e3d6ff" opacity="0.48"/><circle cx="540.9" cy="67.2" r="0.5" fill="#e3d6ff" opacity="0.57"/><circle cx="228.7" cy="52.8" r="1.0" fill="#e3d6ff" opacity="0.39"/><circle cx="634.6" cy="18.7" r="0.5" fill="#e3d6ff" opacity="0.63"/><circle cx="359.5" cy="59.9" r="0.9" fill="#e3d6ff" opacity="0.45"/><circle cx="1366.2" cy="59.5" r="0.7" fill="#e3d6ff" opacity="0.63"/><circle cx="1215.6" cy="8.4" r="0.6" fill="#e3d6ff" opacity="0.41"/><circle cx="1303.1" cy="27.9" r="0.8" fill="#e3d6ff" opacity="0.43"/><circle cx="306.6" cy="38.9" r="0.7" fill="#e3d6ff" opacity="0.46"/><circle cx="389.4" cy="62.3" r="0.9" fill="#e3d6ff" opacity="0.48"/><circle cx="365.1" cy="71.4" r="1.2" fill="#e3d6ff" opacity="0.43"/><circle cx="210.5" cy="49.3" r="0.8" fill="#e3d6ff" opacity="0.45"/><circle cx="738.1" cy="7.4" r="1.2" fill="#e3d6ff" opacity="0.38"/><circle cx="557.9" cy="67.5" r="1.2" fill="#e3d6ff" opacity="0.29"/><circle cx="1287.9" cy="9.1" r="0.7" fill="#e3d6ff" opacity="0.22"/><circle cx="1598.0" cy="65.2" r="0.9" fill="#e3d6ff" opacity="0.47"/><circle cx="340.4" cy="66.8" r="0.5" fill="#e3d6ff" opacity="0.47"/><circle cx="1580.0" cy="22.7" r="1.0" fill="#e3d6ff" opacity="0.62"/><circle cx="200.8" cy="53.8" r="1.2" fill="#e3d6ff" opacity="0.33"/><circle cx="1467.8" cy="17.5" r="1.3" fill="#e3d6ff" opacity="0.43"/><circle cx="180.4" cy="56.4" r="0.7" fill="#e3d6ff" opacity="0.61"/><circle cx="989.0" cy="63.3" r="1.1" fill="#e3d6ff" opacity="0.46"/><circle cx="755.8" cy="58.8" r="1.2" fill="#e3d6ff" opacity="0.57"/><circle cx="1001.9" cy="59.8" r="0.6" fill="#e3d6ff" opacity="0.38"/><circle cx="1594.8" cy="54.0" r="0.9" fill="#e3d6ff" opacity="0.62"/><circle cx="941.9" cy="15.9" r="0.7" fill="#e3d6ff" opacity="0.41"/><circle cx="76.6" cy="28.0" r="1.0" fill="#e3d6ff" opacity="0.38"/><circle cx="1548.0" cy="24.7" r="1.0" fill="#e3d6ff" opacity="0.40"/><circle cx="1131.2" cy="56.4" r="1.3" fill="#e3d6ff" opacity="0.44"/><circle cx="535.7" cy="24.0" r="0.5" fill="#e3d6ff" opacity="0.20"/><circle cx="85.0" cy="37.7" r="1.0" fill="#e3d6ff" opacity="0.23"/><circle cx="592.4" cy="67.4" r="0.7" fill="#e3d6ff" opacity="0.53"/><circle cx="1202.3" cy="4.0" r="0.8" fill="#e3d6ff" opacity="0.54"/><circle cx="672.1" cy="7.8" r="0.9" fill="#e3d6ff" opacity="0.59"/><circle cx="1392.7" cy="30.7" r="0.7" fill="#e3d6ff" opacity="0.30"/><circle cx="541.9" cy="15.4" r="0.5" fill="#e3d6ff" opacity="0.22"/><circle cx="402.6" cy="46.3" r="0.8" fill="#e3d6ff" opacity="0.30"/><circle cx="931.1" cy="12.8" r="0.9" fill="#e3d6ff" opacity="0.40"/><circle cx="513.9" cy="25.Line truncated
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,17 @@
{
"capture": "Unedited frames read back on the PS5 by the frontends' own capture tests (lossless PNG); the pre-screen picture is assets/readme/pre-screen.png",
"resolution": [
1920,
1080
],
"files": [
{
"file": "es-de-collections-ps5.png",
"sha256": "728144ae6b1208153c8fe77cf6bb27413e273798504b21f037820969ee63ac0d"
},
{
"file": "../../readme/pre-screen.png",
"sha256": "6e703f44d3195278e0e4966f8182da12891a751e44e9cf7545e7f6935e18a80d"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

@@ -0,0 +1,92 @@
{
"capture": "Unedited browser screenshots of the installed console WebUI. Add Game uses an unsaved LaunchBox metadata draft; no backup uploaded, no library edits saved, no download started. Full-page captures preserve scrollable page contents.",
"build": "8135742c",
"source_commit": "1e7edf5",
"files": [
{
"file": "webui-add-game-details.jpg",
"resolution": [
1265,
712
],
"sha256": "011e27a0713f3e50dc1590df32fbccde04f9a3347b67f7fad9d744a1d5b6eb66"
},
{
"file": "webui-add-game-manual-media.jpg",
"resolution": [
1265,
712
],
"sha256": "0dfa0a64e524c0086cde7a876950224441ef933fa7c67a5f666160127def29a7"
},
{
"file": "webui-add-game-media.jpg",
"resolution": [
1265,
712
],
"sha256": "2873f3e14454a50e352e7a37783b9a3178a54f64ae8afe91ca2f3e82fa2bfefc"
},
{
"file": "webui-download-media.jpg",
"resolution": [
807,
3218
],
"sha256": "ab9d2a5f1e432090acd4a28e4f5a5c81eb526da6ad8f9b485616b4ae3cf67a45",
"recap": "Nintendo - Game Boy Advance collapsed; game name and matched title hidden."
},
{
"file": "webui-download-sources.jpg",
"resolution": [
1265,
712
],
"sha256": "dc064904f1810e24989a4da693e0a00bc360897728b5b5cd0b798a7df6d517c8"
},
{
"file": "webui-game-details.jpg",
"resolution": [
801,
594
],
"sha256": "f9ecf12ae56c3b9a0496775f42cf5317bd514aac85a873f75731c9b644a0d7a2",
"game": "Puzzle Test Game"
},
{
"file": "webui-game-files.jpg",
"resolution": [
801,
594
],
"sha256": "78662bfb3473ff1ee2a735f3bfe0a55317816c9f16023d92a95a1d247c008d5b",
"game": "Puzzle Test Game"
},
{
"file": "webui-game-media.jpg",
"resolution": [
801,
594
],
"sha256": "7b312c4f136167b1ed419cd06f1020da370bdc56335b137c443ea2ef5e1541af",
"game": "Puzzle Test Game"
},
{
"file": "webui-game-savestates.jpg",
"resolution": [
801,
594
],
"sha256": "307439798aeb8ab8901a9665a5a6eac3c0f40569510601247815b90caf59e2bc",
"game": "Puzzle Test Game"
},
{
"file": "webui-games-neogeo-b.jpg",
"resolution": [
1264,
1951
],
"sha256": "63c582f50e5e38a0c3876059c63e011a6f8bd9d286bd6ca8e279b495329c2602"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

-9
View File
@@ -8,15 +8,6 @@ discs, cartridges or digital purchases, and BIOS or firmware files dumped from
hardware you own. Downloading or sharing games, BIOS files, firmware or keys
you do not own is piracy: do not do it, and do not ask for help with it.
PLAYSTATION 3 (RPCS3) IS NOT IN ANY RELEASE. RPCS3 is licensed
GPL-2.0-only; this port's code is GPL-3.0-or-later, and the two cannot be
distributed together as one program. RPCS3's source is public
(https://github.com/mihawk-99/PS5_RPCS3) and this project's build scripts
build it: if you want it, you must compile the title from source yourself,
for your own console (README.md, "Build from source"). Do not share the
binaries you build. RPCS3 also needs your own PS3 system software
(PS3UPDAT.PUP), your own games and their licence files, under the same rules.
Licences: every part of this title, its licence and its source are listed in
licenses/README.txt. Snes9x, FinalBurn Neo and Genesis Plus GX may not be sold
or used commercially.
+3
View File
@@ -35,3 +35,6 @@ audio_out_rate = "48000"
video_shader_dir = "/app0/shaders"
video_filter_dir = "/app0/filters"
overlay_directory = "/app0/overlays"
# RetroAchievements hardcore mode forbids loading save states: off unless the owner turns it on.
cheevos_hardcore_mode_enable = "false"
+487
View File
@@ -0,0 +1,487 @@
/* PS5 RetroArch - the WebUI daemon, /app0/webui/ps5-retroarch-webui.elf.
*
* Copyright (C) 2026 Mihawk
* SPDX-License-Identifier: GPL-3.0-or-later
*
* A payload, started through the console's ELF loader by the first program of the
* title that finds none running (src/webui_link.cpp): it serves the WebUI
* (src/webui_ps5.cpp) on 6769 from the title's folder for as long as the title is
* open, whichever frontend it shows, so a frontend change (a LoadExec, which ends the
* program it leaves) never cuts an upload or a page. The protocol is in
* src/webui_link.h.
*
* It stops when no program of the title has been linked for kIdleSeconds and no
* request is being answered: the title closed. An update the WebUI is asked to
* install makes every linked program close the title; once none is left, this
* installs it (ps5_update::install), the way eboot.bin did after RetroArch quit.
* What the Game Forwarder page's tiles left for the title's close (removals and new names
* or icons, src/ps5_forwarders.h) is done once no program of the title is linked: tried at
* once and every few seconds while ShadowMountPlus refuses, and at the stop.
*
* Its own record is <title>/webui-daemon.txt (the last run's is webui-daemon.1.txt).
* tests/test_webui_daemon.py builds it for the host, with its folder, ports and idle
* time its own.
*/
#include "../src/webui_ps5.h"
#include "../src/webui_update.h"
#include <arpa/inet.h>
#include <cerrno>
#include <csignal>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <atomic>
#include <string>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
#include "../src/webui_link.h"
#ifndef PS5_WEBUI_HOMEBREW
#define PS5_WEBUI_HOMEBREW "/data/homebrew"
#endif
#ifndef PS5_WEBUI_HTTP_PORT
#define PS5_WEBUI_HTTP_PORT 6769
#endif
#ifndef PS5_WEBUI_IDLE_SECONDS
#define PS5_WEBUI_IDLE_SECONDS 20.0
#endif
#ifdef __PROSPERO__
/* What the daemon creates in the title's folder is given 0777, as eboot.bin gives
* everything under /app0 (src/permissions_ps5.cpp), so FTP can change it: open,
* mkdir and fopen are wrapped at link time (tools/build-webui-daemon.sh). */
extern "C"
{
int __real_mkdir(const char *, mode_t);
int __real_open(const char *, int, ...);
std::FILE *__real_fopen(const char *, const char *);
int __wrap_mkdir(const char *path, mode_t mode)
{
const int result = __real_mkdir(path, mode | 0777);
if (result == 0)
chmod(path, 0777);
return result;
}
int __wrap_open(const char *path, int flags, ...)
{
if ((flags & O_CREAT) == 0)
return __real_open(path, flags);
va_list arguments;
va_start(arguments, flags);
const int mode = va_arg(arguments, int);
va_end(arguments);
const int fd = __real_open(path, flags, mode | 0777);
if (fd >= 0)
fchmod(fd, 0777);
return fd;
}
std::FILE *__wrap_fopen(const char *path, const char *mode)
{
std::FILE *const file = __real_fopen(path, mode);
if (file && mode && std::strpbrk(mode, "wa+"))
fchmod(fileno(file), 0777);
return file;
}
}
#endif
namespace
{
constexpr double kIdleSeconds = PS5_WEBUI_IDLE_SECONDS; /* no program linked: closed */
constexpr double kInstallSeconds = 5.0; /* no program linked, an install asked */
constexpr double kNoHelloSeconds = 30.0; /* started, and no program ever linked */
struct Link
{
int fd;
std::string pending, frontend;
bool hello = false;
};
std::string root, title, version, log_path;
std::vector<std::string> early_lines;
double now_s()
{
timespec now{};
clock_gettime(CLOCK_MONOTONIC, &now);
return double(now.tv_sec) + double(now.tv_nsec) / 1e9;
}
void say(const char *format, ...)
{
char line[512];
va_list arguments;
va_start(arguments, format);
std::vsnprintf(line, sizeof line, format, arguments);
va_end(arguments);
std::fprintf(stderr, "[webui-daemon] %s\n", line);
if (log_path.empty())
{
early_lines.emplace_back(line);
return;
}
if (std::FILE *file = std::fopen(log_path.c_str(), "a"))
{
std::time_t t = std::time(nullptr);
char stamp[32];
std::strftime(stamp, sizeof stamp, "%Y-%m-%d %H:%M:%S", std::gmtime(&t));
/* The process: a stale daemon still finishing writes to the same file. */
std::fprintf(file, "%s [%d] %s\n", stamp, int(getpid()), line);
std::fclose(file);
}
}
std::string read_small(const std::string &path)
{
std::string out;
if (std::FILE *file = std::fopen(path.c_str(), "rb"))
{
char buffer[4096];
size_t got;
while ((got = std::fread(buffer, 1, sizeof buffer, file)) > 0 && out.size() < 65536)
out.append(buffer, got);
std::fclose(file);
}
return out;
}
bool is_file(const std::string &path)
{
struct stat st{};
return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode);
}
bool valid_id(const std::string &id)
{
if (id.size() != 9)
return false;
for (char c : id)
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
return false;
return true;
}
/* The title's folder: what is mounted as its application folder
* (/data/homebrew/PPSA99169 -> /system_ex/app/PPSA99169, docs/FINDINGS.md), else
* the usual place; either only when it holds this title's eboot.bin and WebUI. */
std::string title_folder(const std::string &id)
{
std::vector<std::string> candidates;
#ifdef __PROSPERO__
struct statfs *mounts = nullptr;
const int count = getmntinfo(&mounts, MNT_NOWAIT);
const std::string app = "/system_ex/app/" + id;
for (int i = 0; i < count; ++i)
if (app == mounts[i].f_mntonname && mounts[i].f_mntfromname[0] == '/')
candidates.emplace_back(mounts[i].f_mntfromname);
#endif
candidates.push_back(PS5_WEBUI_HOMEBREW "/" + id);
for (const auto &folder : candidates)
if (is_file(folder + "/eboot.bin") && is_file(folder + "/webui/index.html") &&
read_small(folder + "/sce_sys/param.json").find(id) != std::string::npos)
return folder;
return "";
}
bool send_line(int fd, const char *line)
{
const size_t size = std::strlen(line);
return send(fd, line, size, 0) == ssize_t(size);
}
int listen_local(unsigned short port)
{
const int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
return -1;
int yes = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
sockaddr_in address{};
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, reinterpret_cast<sockaddr *>(&address), sizeof address) != 0 || listen(fd, 8) != 0)
{
close(fd);
return -1;
}
return fd;
}
void show_frontend(const std::vector<Link> &links)
{
const char *shown = "";
for (const auto &link : links)
if (link.hello)
shown = link.frontend.c_str();
ps5_webui_set_frontend(shown);
}
} // namespace
int main()
{
std::signal(SIGPIPE, SIG_IGN);
int listener = listen_local(PS5_WEBUI_LINK_PORT);
if (listener < 0)
{
std::fprintf(stderr, "[webui-daemon] another daemon has %d; this one stops\n",
PS5_WEBUI_LINK_PORT);
return 0;
}
ps5_webui_set_frontend("");
std::vector<Link> links;
bool serving = false, stale = false, install_sent = false, installed = false, closed = false;
bool tiles_done = false;
const double started = now_s();
double idle_since = started, installed_at = 0, last_serve_try = 0, last_tiles = 0;
unsigned serve_failures = 0;
/* The WebUI starts on a thread of its own (reading the library and the saved scraping
* jobs takes seconds): the title's hello is answered meanwhile. 0 idle, 1 starting,
* 2 serving, 3 failed. */
static std::atomic<int> start_state{0};
static std::string start_folder;
pthread_t starter{};
bool starter_running = false;
say("started; listening for the title on 127.0.0.1:%d", PS5_WEBUI_LINK_PORT);
for (;;)
{
std::vector<pollfd> waits;
if (listener >= 0)
waits.push_back({listener, POLLIN, 0});
for (const auto &link : links)
waits.push_back({link.fd, POLLIN, 0});
poll(waits.data(), waits.size(), 250);
if (listener >= 0 && (waits[0].revents & POLLIN))
{
const int fd = accept(listener, nullptr, nullptr);
if (fd >= 0 && links.size() < 16)
links.push_back({fd, "", "", false});
else if (fd >= 0)
close(fd);
}
for (auto &link : links)
{
pollfd wait{link.fd, POLLIN, 0};
if (poll(&wait, 1, 0) <= 0)
continue;
char buffer[512];
const ssize_t got = recv(link.fd, buffer, sizeof buffer, 0);
if (got <= 0 || link.pending.size() > 4096)
{
close(link.fd);
link.fd = -1;
continue;
}
link.pending.append(buffer, size_t(got));
size_t end;
while (link.fd >= 0 && (end = link.pending.find('\n')) != std::string::npos)
{
const std::string line = link.pending.substr(0, end);
link.pending.erase(0, end + 1);
char word[16] = {}, frontend[32] = {}, id[16] = {};
const int fields = std::sscanf(line.c_str(), "%15s %31s %15s", word, frontend, id);
if (fields == 3 && std::strcmp(word, "hello") == 0)
{
const char *reply = "ok\n";
if (stale)
reply = "stale\n";
else if (root.empty())
{
const std::string folder = valid_id(id) ? title_folder(id) : "";
if (folder.empty())
{
say("hello from %s of %s: no folder of that title found", frontend, id);
reply = "unknown\n";
}
else
{
root = folder;
title = id;
version = read_small(root + "/webui/version.json");
log_path = root + "/webui-daemon.txt";
rename(log_path.c_str(), (root + "/webui-daemon.1.txt").c_str());
for (const auto &early : early_lines)
say("%s", early.c_str());
early_lines.clear();
say("the title is %s, in %s", title.c_str(), root.c_str());
}
}
else if (title != id)
reply = "other\n";
else if (read_small(root + "/webui/version.json") != version)
{
say("hello from another build of the title: this daemon stops once "
"its requests end");
stale = true;
reply = "stale\n";
}
send_line(link.fd, reply);
if (std::strcmp(reply, "ok\n") == 0)
{
link.hello = true;
link.frontend = frontend;
say("linked: %s (%zu linked)", frontend, links.size());
if (install_sent)
send_line(link.fd, "install\n");
}
else
{
close(link.fd);
link.fd = -1;
}
}
else if (fields >= 2 && std::strcmp(word, "frontend") == 0 && link.hello)
{
link.frontend = frontend;
say("frontend: %s", frontend);
}
}
}
for (auto it = links.begin(); it != links.end();)
if (it->fd < 0)
{
if (it->hello)
say("unlinked: %s", it->frontend.c_str());
it = links.erase(it);
}
else
++it;
show_frontend(links);
const double now = now_s();
bool linked = false;
for (const auto &link : links)
linked |= link.hello;
if (linked)
idle_since = 0;
else if (idle_since == 0)
idle_since = now;
/* What a Game Forwarder tile waits for (a removal, a new name or picture, which
* ShadowMountPlus refuses while a game runs): tried once no program of the title is
* linked, and every few seconds after, not only at the stop kIdleSeconds later */
if (linked)
tiles_done = false; /* the page can leave more while the title runs */
else if (serving && !tiles_done && now - last_tiles >= 3.0)
{
last_tiles = now;
tiles_done = ps5_webui_finish_forwarders([](const char *line) { say("%s", line); });
}
if (start_state == 2 || start_state == 3)
{
if (starter_running)
pthread_join(starter, nullptr);
starter_running = false;
serving = start_state == 2;
start_state = 0;
if (serving)
say("serving the WebUI on %d from %s (%u s busy before)", PS5_WEBUI_HTTP_PORT,
root.c_str(), serve_failures);
else if (serve_failures++ == 0)
say("%d is busy (another daemon's last transfer?); trying every second",
PS5_WEBUI_HTTP_PORT);
}
if (!serving && start_state == 0 && !root.empty() && !stale && now - last_serve_try >= 1.0)
{
last_serve_try = now;
start_state = 1;
start_folder = root;
/* A payload's default thread stack is small; the library scan needs more. */
pthread_attr_t attributes;
pthread_attr_init(&attributes);
pthread_attr_setstacksize(&attributes, 4u << 20);
starter_running =
pthread_create(
&starter, &attributes,
[](void *) -> void *
{
#ifdef PS5_WEBUI_TEST_START_DELAY_MS /* the tests: a start as slow as a big library's */
usleep(PS5_WEBUI_TEST_START_DELAY_MS * 1000);
#endif
start_state =
ps5_webui_start(start_folder.c_str(), PS5_WEBUI_HTTP_PORT, false) ? 2
: 3;
return nullptr;
},
nullptr) == 0;
pthread_attr_destroy(&attributes);
if (!starter_running)
start_state = 3;
}
if (serving && !install_sent && ps5_update::exit_requested())
{
install_sent = true;
say("install requested: asking %zu linked program(s) to close the title", links.size());
for (const auto &link : links)
if (link.hello)
send_line(link.fd, "install\n");
}
if (install_sent && !installed && !linked && now - idle_since >= kInstallSeconds)
{
installed = true;
installed_at = now;
const bool ok = ps5_update::install();
say("install: %s", ok ? "done" : "failed (see .update-result)");
stale = true; /* this daemon is the old build's now */
}
// What must not be cut: transfers in flight, not an open page's idle keep-alive
// connections (counting those, a stale daemon kept 6769 from the new build).
const unsigned busy = ps5_webui_transfers();
const std::string update = ps5_update::status().state;
// A download, or a scraping job, goes on with the title closed and no page open.
const bool updating =
update == "downloading" || update == "verifying" || ps5_webui_scraping();
bool stop = false;
if (root.empty() && !linked && now - started >= kNoHelloSeconds)
{
say("no program of a title linked within %.0f s", kNoHelloSeconds);
stop = true;
}
else if (stale && !busy && !linked && (!installed || now - installed_at >= 15.0))
{
say("stopping for the new build's daemon (no transfer in flight%s)",
ps5_webui_scraping() ? "; the scraping job is saved and resumes there" : "");
stop = true;
}
else if (!root.empty() && !linked && !busy && !updating && !install_sent &&
now - idle_since >= kIdleSeconds)
{
say("no program of the title linked for %.0f s: the title is closed", kIdleSeconds);
stop = closed = true;
}
if (stale && listener >= 0 && !installed)
{
close(listener); /* the new build's daemon can start meanwhile */
listener = -1;
}
if (stop)
break;
}
if (starter_running)
pthread_join(starter, nullptr); /* a start under way ends before the stop */
for (const auto &link : links)
close(link.fd);
if (listener >= 0)
close(listener);
ps5_webui_stop();
/* ShadowMountPlus refuses to uninstall while a game runs, and RetroArch is one */
if (closed && !tiles_done)
ps5_webui_finish_forwarders([](const char *line) { say("%s", line); });
say("stopped");
return 0;
}
@@ -0,0 +1,72 @@
{
"step": "azahar-stylus-parity",
"recorded": "2026-10-08T00:25:07.482933+00:00",
"revision": "working tree on 63e11a3",
"command": "tools/verify.sh format; PS5_FRONTENDS=\"es-de picker\" PS5_VULKAN_TEMPLATE=\"$PWD/build/picker-template\" tools/verify.sh unit build integration evidence; python3 tools/deploy-title.py",
"lines": [
"format: PASS",
"182 unit and 182 integration tests: PASS",
"Azahar software raster matches DeSmuME at 1x, 5x and 18x: PASS",
"Vulkan shader compile/link, pen predicate parity and clipped nib coordinates: PASS",
"Old Azahar raster fails the DeSmuME pixel comparison as expected",
"Native Azahar build, ABI and cursor-patch provenance: PASS",
"Stored eboot.bin and both cores match staged SHA-256: PASS",
"Core-switch crash remains unresolved; no lifecycle changes made",
"Fresh PS5 Azahar launch: cursor captured, 399 frames by 7 seconds, clean exit"
],
"native_azahar": {
"sha256": "54cdcf809cf521eb544a60c26726a3074045c812c53942ce3cb9b4b383b2b0f6",
"bytes": 62186560,
"abi_passed": true,
"source_revision": "4aef900a1a903d0aa6ea86fdb1e18236fb0d5907",
"port_inputs_sha256": {
"tools/core-fork.sh": "392c8a7577eaa7a81e99f58a6e006cecab92610d959dc460aabc3ef1064ac1b0",
"tooling/native/ps5-core.ld": "5ffe0f0e99e23de5245afb46fa6dfda6dd860b53d942a5fe2f06eaf9e520df42",
"tooling/native/core_cxx_runtime.cpp": "6d7c11d8595f47601b3a281e616033ea5d28c6329872aa286d4d81c597509433",
"tools/build-azahar.sh": "2418b991f7f4a71c4c4c984ab7831dad085861c6a40e82c0e8ca791acee2f40c",
"tooling/azahar/ps5-toolchain.cmake": "930762cf6f56d1e8b1a927bd70ac5e782f77cdb9b9622617973ef92ce881c398",
"tooling/azahar/touchpad-stylus.patch": "fa8187c2919fda1152b611318aaeced29f9c4e8ccb25997f25f230ce624ccd6f"
}
},
"deployed_readback": {
"eboot.bin": {
"bytes": 38772204,
"sha256": "096ba6d77e9a6667f0d41c1473268200316cd086ee6b4d83523ada646e255287",
"matches_staged": true
},
"cores/desmume_libretro.so": {
"bytes": 5626312,
"sha256": "b1b4d20bf62a4115007646b76ae7f5e04ddb661b8f9cb5b79a00c940c3b801a4",
"matches_staged": true
},
"cores/azahar_libretro.so": {
"bytes": 62186560,
"sha256": "54cdcf809cf521eb544a60c26726a3074045c812c53942ce3cb9b4b383b2b0f6",
"matches_staged": true
}
},
"preview_sha256": "d7d3b09c0ad154398920b1f614a9d5c3cb4dec9dd71156197ea995226ca15a84",
"raw_directory": "klog/azahar-stylus-parity",
"console_run": {
"command": "python3 klog/azahar-stylus-parity/run-fresh.py",
"build_id": "cbe6d6f13b15aaaf5f75604e555562a32f5dce39ef0a434e84a661dee6a2ae83",
"raw_capture": "klog/run-PPSA99169-202311.log",
"frontend_log": "klog/retroarch-202311.log",
"events": [
"input: pad script MARK at 7.011 s: frames=399 worst_ms=80.4 over_40ms=7",
"input: pad script STOP at 9.01 s",
"rarch_main returned = 0"
],
"frames": [
{
"path": "klog/pad-202311/pad-shot-1.png",
"sha256": "54eaff3ddb6428207c2760e30c39f02b53c1244242c08080ae1eb43f3a6a8efb"
},
{
"path": "klog/pad-202311/pad-shot-2.png",
"sha256": "cc1920a1ad0e2a7337099e11b4ed18144faf232c292ee6443bdc6d29ef327361"
}
],
"visual_check": "Both screenshots show the white pen with dark outline/nib pointing down-left on the lower screen."
}
}
@@ -0,0 +1,17 @@
{
"step": "azahar-stylus-parity",
"note": "Matching DeSmuME pen in Azahar; crash investigation is separate.",
"must_contain": [
"format: PASS",
"182 unit and 182 integration tests: PASS",
"Azahar software raster matches DeSmuME at 1x, 5x and 18x: PASS",
"Vulkan shader compile/link, pen predicate parity and clipped nib coordinates: PASS",
"Old Azahar raster fails the DeSmuME pixel comparison as expected",
"Native Azahar build, ABI and cursor-patch provenance: PASS",
"Stored eboot.bin and both cores match staged SHA-256: PASS",
"Core-switch crash remains unresolved; no lifecycle changes made",
"Fresh PS5 Azahar launch: cursor captured, 399 frames by 7 seconds, clean exit"
],
"must_not_contain": [],
"human_check": "Check the stylus with physical touchpad movement, press and drag on the TV."
}
@@ -10,5 +10,5 @@
"must_not_contain": [
"verify: FAIL"
],
"human_check": "Pending: firmware 5.02 Chains of Olympus intro/gameplay, close/reload, and representative games in the other changed cores at these defaults."
"human_check": "Pending: firmware 5.02 PSP Test Game intro/gameplay, close/reload, and representative games in the other changed cores at these defaults."
}
@@ -0,0 +1,20 @@
{
"step": "capture-readback-size",
"recorded": "2026-10-04T18:37:43Z",
"console_run": "base PS5 13.40, 2026-10-04 14:39",
"revision": "d200efa + patch 0031 size fix (build 10a24fa1)",
"command": "args.txt: --ps5-capture=600 --ps5-capture-path=/app0/xmb-preview.ppm --appendconfig /app0/config/xmb-capture.cfg",
"raw_capture": "klog/capture-readback-summary.txt",
"raw_lines": 8,
"raw_bytes": 1043,
"lines": [
"==> PS5 RetroArch XMB preview capture: args.txt --ps5-capture=600 with --appendconfig (1920x1080, aspect 16:9, no config save)",
"before the fix (build ce576f3d): capture: frame 600 of 600, viewport 1920x1080, read_viewport -> ok; PPM 1920x1080 garbled",
"garbled PPM: written rows are 1440 pixels (rows 1440 apart differ by 0.92 on average; 1,555,200 = 1440x1080 pixels written); top quarter black",
"cause: vulkan_read_viewport writes min(viewport, video size) rows; the video size was 1440x1080 (aspect index 22, core-provided 4:3 with no core); patch 0031 wrote the PPM at the viewport size",
"fix (tools/apply-port-patches.py, 0031): the PPM takes min(vp, video) width and height, as read_viewport writes",
"after the fix (build 10a24fa1, build/ra-conf recreated): capture: frame 600 of 600, viewport 1920x1080, read_viewport -> ok (/app0/xmb-preview.ppm)",
"clean capture: 1920x1080, black rows 0, png sha256 45717b63754aa739b8ea526ac59b0069b9b890f1ce956175aa87629242639ec0",
"config/retroarch.cfg unchanged by both capture runs (sha256 785579484698f8e2...)"
]
}
@@ -0,0 +1,12 @@
{
"step": "capture-readback-size",
"note": "The port's frame capture writes the image at the size RetroArch's synchronous readback produces (the viewport no larger than the video size), so a capture is never garbled; the XMB preview for the pre-screen's RetroArch card is a clean 1920x1080 capture.",
"must_contain": [
"fix (tools/apply-port-patches.py, 0031): the PPM takes min(vp, video) width and height",
"after the fix (build 10a24fa1, build/ra-conf recreated): capture: frame 600 of 600, viewport 1920x1080, read_viewport -> ok",
"clean capture: 1920x1080, black rows 0",
"config/retroarch.cfg unchanged by both capture runs"
],
"must_not_contain": [],
"human_check": ""
}
+36
View File
@@ -0,0 +1,36 @@
{
"step": "chd-flac-hash",
"recorded": "2026-10-17T22:10:00Z",
"console_run": "",
"revision": "20dd7fe (this series), in build 838fd9a3",
"command": "tools/run-title.sh --no-build --no-deploy --game=mednafen_psx_hw:/app0/content/psx/chd-flac-test.chd --retroarch-arg=--appendconfig=/app0/tests/cheevos-offline.cfg --watch 30, on build 22b5d122 and on this one",
"raw_capture": "klog/chd-flac-hash/record.txt",
"raw_lines": 24,
"raw_bytes": 2056,
"lines": [
"Console: the owner's base PS5 on a 1440p display with 120 Hz and VRR, build identity 838fd9a3ca99e21ce4adba405a3f3867a0c4ce411ee89981e0a8111dcef02797 (this series at 20dd7fe with PS5_FRONTENDS=\"es-de picker\", PS5_WITH_SWANSTATION=1, PS5_WITH_MELONDSDS=1), 2026-10-17.",
"The disc: a CHD chdman 0.289 made from one of the owner's PlayStation backups (bin/cue), named chd-flac-test.chd for the run: codecs cdlz, cdzl and cdfl, 31,002 hunks, 2,338 of them CD FLAC.",
"Host, the CHD reader as build/ra-conf has it, every hunk read: without FLAC \"chd_open: unsupported format (27)\"; with FLAC (0133) \"chd_open: ok; codecs cdlz cdzl cdfl; 31002 hunks read, 0 failed\".",
"Console, Beetle PSX HW with that CHD, RetroAchievements on for an offline test user (cheevos_username set, no token or password, config_save_on_exit false):",
"before, build 22b5d122 (no FLAC):",
" before: [libretro INFO] chd_load '/app0/content/psx/chd-flac-test.chd' hunkbytes=19584",
" before: [INFO] [RCHEEVOS] Identifying game: /app0/content/psx/chd-flac-test.chd",
" before: [INFO] [RCHEEVOS] Found 9 potential consoles for chd file extension",
" before: [INFO] [RCHEEVOS] Could not open track",
" before: [INFO] [RCHEEVOS] Could not open track",
" before: [INFO] [RCHEEVOS] Could not open track",
" before: [INFO] [RCHEEVOS] Could not open track",
" before: [INFO] [RCHEEVOS] Could not open track",
" before: [INFO] [RCHEEVOS] Load failed (-25): hash generation failed",
"after, build 838fd9a3 (0133):",
" after: [INFO] [RCHEEVOS] Identifying game: /app0/content/psx/chd-flac-test.chd",
" after: [INFO] [RCHEEVOS] Found 9 potential consoles for chd file extension",
" after: [INFO] [RCHEEVOS] Trying console 12",
" after: [INFO] [RCHEEVOS] Found SYSTEM.CNF at sector 23",
" after: [INFO] [RCHEEVOS] Looking for boot executable: <boot executable>",
" after: [INFO] [RCHEEVOS] Found <boot executable> at sector 24",
" after: [INFO] [RCHEEVOS] Hashing <boot executable> title (11 bytes) and contents (563200 bytes)",
" after: [INFO] [RCHEEVOS] Generated hash <32 hex digits>",
" after: [INFO] [RCHEEVOS] Load failed (-28): Login required"
]
}
+15
View File
@@ -0,0 +1,15 @@
{
"step": "chd-flac-hash",
"note": "0133 on the console: the frontend's CHD reader opens a disc whose header lists CD FLAC, so RetroAchievements generates its PlayStation hash offline (and stops only at the sign-in); before, every candidate console answered \"Could not open track\" and the hash failed.",
"must_contain": [
"before: [INFO] [RCHEEVOS] Load failed (-25): hash generation failed",
"after: [INFO] [RCHEEVOS] Generated hash <32 hex digits>",
"after: [INFO] [RCHEEVOS] Found SYSTEM.CNF at sector 23",
"31002 hunks read, 0 failed"
],
"must_not_contain": [
"after: [INFO] [RCHEEVOS] Could not open track",
"after: [INFO] [RCHEEVOS] Load failed (-25): hash generation failed"
],
"human_check": ""
}
+23
View File
@@ -0,0 +1,23 @@
{
"step": "child-processes",
"recorded": "2026-10-04T22:33:23Z",
"console_run": "base PS5 13.40, runs 182727 and 183131",
"revision": "26c003b + child probe",
"command": "PS5_FRONTENDS='es-de picker child-probe' tools/build-title.sh; tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/child-probe.bin --watch 120 (and with es-de/child-probe-mode.txt = nogpu)",
"raw_capture": "klog/child-processes-summary.txt",
"raw_lines": 11,
"raw_bytes": 1388,
"lines": [
"==> PS5_FRONTENDS='es-de picker child-probe' tools/build-title.sh; tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/child-probe.bin --watch 120 (base PS5, 13.40)",
"start: probe pid 196, app 16408, local processes 1, flexible free 437256192",
"headless child (libc-only preload, display held by the parent): service id 335644465, reported in 0.12 s, native pid 197, socket type 5, listed 1, local processes 2",
"memory with the child alive: child flexible free 299892736, parent flexible free 299892736 (one budget)",
"headless child closed: kill status 0, present after 0, local processes 1",
"live child 335644481 (native pid 198) across the parent's LoadExec: listed after 0, local processes 1, flexible free 437256192",
"display opened and drawn by the second process: matched 120/120, then released",
"graphics child (full default preload) after the parent released the display: service id 335644513, reported 0",
"kernel log (run-PPSA99169-182727): EXEC child-probe.bin pid 200; gbase_gvmsw_open 6978 error; exception 0xa0020104 (PRX_PROCESS_STARTUP_FAILURE); App Crash PID=0xc8; Kill process 0xc8 and 0xc7 (the parent too)",
"graphics child from a parent that never opened the display: service id 369198993, reported 0",
"kernel log (run-PPSA99169-183131): EXEC child-probe.bin pid 205; gbase_gvmsw_open 6978 error; exception 0xa0020104 (PRX_PROCESS_STARTUP_FAILURE); App Crash PID=0xcd"
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"step": "child-processes",
"note": "Sony's local processes (sceSystemServiceAddLocalProcess, ../PS5_Proton's route) on the base PS5 13.40: a headless child starts from a LoadExec'd frontend in 0.12 s and closes cleanly, but shares the application's memory budget, dies with the parent's LoadExec, and cannot have the GPU: a graphics child fails at startup (gbase_gvmsw_open 6978) and Sony then kills the whole application.",
"must_contain": [
"headless child closed: kill status 0, present after 0",
"(one budget)",
"listed after 0",
"gbase_gvmsw_open 6978 error; exception 0xa0020104",
"graphics child from a parent that never opened the display"
],
"must_not_contain": [],
"human_check": ""
}
@@ -0,0 +1,12 @@
{"pid":196,"t":0.00,"step":"start","app":16408,"list_count":1,"flexible_free":437256192,"direct_total":12884901888,"direct_largest_free":12882804736}
{"pid":196,"t":2.02,"step":"display_held","opened":1,"width":1920,"height":1080,"matched":120}
{"pid":196,"t":2.15,"step":"headless_child","service_id":335644465,"reported":1,"seconds":0.12,"child_pid":197,"socket_type":5,"child_flexible_free":299892736,"child_direct_total":12884901888,"child_direct_largest_free":12578095104,"parent_flexible_free":299892736,"parent_direct_largest_free":12578095104,"listed":1,"list_count":2}
{"pid":196,"t":2.15,"step":"headless_child_answer","answered":1}
{"pid":196,"t":2.35,"step":"headless_child_closed","service_id":335644465,"kill_status":0,"present_after":0,"list_count":1}
{"pid":196,"t":2.55,"step":"live_child","service_id":335644481,"reported":1,"child_pid":198}
{"pid":196,"t":2.56,"step":"loadexec_with_live_child","service_id":335644481}
{"pid":199,"t":0.00,"step":"after_loadexec","service_id":335644481,"child_listed":0,"list_count":1,"flexible_free":437256192}
{"pid":199,"t":2.02,"step":"display_before_handover","opened":1,"width":1920,"height":1080,"frames":120,"matched":120,"seconds":2.02,"error":""}
{"pid":199,"t":2.06,"step":"display_released"}
{"pid":199,"t":2.25,"step":"graphics_child_spawned","service_id":335644513}
{"pid":199,"t":2.77,"step":"graphics_child","service_id":335644513,"reported":0,"seconds":0.71,"child_pid":0,"opened":0,"frames":0,"matched":0,"width":0,"height":0,"child_flexible_free":0}
@@ -0,0 +1,3 @@
{"pid":204,"t":0.00,"step":"nogpu_start","app":24600,"list_count":1}
{"pid":204,"t":0.13,"step":"nogpu_graphics_child_spawned","service_id":369198993}
{"pid":204,"t":0.62,"step":"nogpu_graphics_child","service_id":369198993,"reported":0,"seconds":0.61,"child_pid":0,"opened":0,"frames":0,"matched":0,"width":0,"height":0}
@@ -2,14 +2,14 @@
"step": "core-recheck-current-driver",
"recorded": "2026-09-24T01:05:36Z",
"console_run": "",
"revision": "driver ../PS5_Vulkan 49679c9 (R53); port e425dbe + retention, crash report, patch 0086",
"revision": "driver ../PS5_Vulkan 49679c9 (R53); port bfc2bf9 + retention, crash report, patch 0086",
"command": "args.txt per core: -L /app0/cores/<core>_libretro.so <content> --max-frames=1200 --max-frames-ss --max-frames-ss-path=/app0/savefiles/ss-<core>.png (screenshot fetched and removed; state snapshotted and restored)",
"raw_capture": "klog/r53-core-recheck-evidence.txt",
"raw_lines": 75,
"raw_bytes": 5917,
"lines": [
"== fceumm: 1943 - The Battle of Midway (USA).zip (screenshot checked; the trace of this run was not saved, the harness stopped at an FTP reply; FCEUmm traces are in display-retention runs A-C)",
"== snes9x: Super Mario World (USA).zip",
"== fceumm: NES Test Game (USA).zip (screenshot checked; the trace of this run was not saved, the harness stopped at an FTP reply; FCEUmm traces are in display-retention runs A-C)",
"== snes9x: SNES Test Game (USA).zip",
"[INFO] [Core] Geometry: 256x224, Aspect: 1.333, FPS: 60.10, Sample rate: 32040.00 Hz.",
"core loader: ready symbols=182 relocations=7952 mapped_bytes=6815744",
"core loader: ready symbols=182 relocations=7952 mapped_bytes=6815744",
@@ -19,7 +19,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== mgba-gba: Metroid Fusion (USA).7z",
"== mgba-gba: Space Hunter Advance (USA).7z",
"[INFO] [Core] Geometry: 240x160, Aspect: 1.500, FPS: 59.73, Sample rate: 65536.00 Hz.",
"core loader: ready symbols=95 relocations=19395 mapped_bytes=3293184",
"core loader: ready symbols=95 relocations=19395 mapped_bytes=3293184",
@@ -30,7 +30,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== mgba-gb: Tetris (World) (Rev 1).zip",
"== mgba-gb: Block Drop (World) (Rev 1).zip",
"core loader: ready symbols=95 relocations=19395 mapped_bytes=3293184",
"core loader: ready symbols=95 relocations=19395 mapped_bytes=3293184",
"[ps5vk] output: 119.88 Hz selected",
@@ -41,7 +41,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== fbneo7: sf2.zip",
"== fbneo7: arcade1.zip",
"core loader: ready symbols=192 relocations=961604 mapped_bytes=169852928 base=205168000",
"core loader: ready symbols=192 relocations=961604 mapped_bytes=169852928 base=205168000",
"[ps5vk] output: 119.88 Hz selected",
@@ -50,7 +50,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== gpgx-md: Sonic The Hedgehog (USA, Europe).zip",
"== gpgx-md: Mega Drive Test Game (USA, Europe).zip",
"[INFO] [Core] Geometry: 256x192, Aspect: 1.524, FPS: 59.92, Sample rate: 44100.00 Hz.",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
@@ -60,7 +60,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== gpgx-sms: Alex Kidd in Miracle World (USA, Europe).zip",
"== gpgx-sms: Master System Test Game (USA, Europe).zip",
"[INFO] [Core] Geometry: 256x192, Aspect: 1.524, FPS: 59.92, Sample rate: 44100.00 Hz.",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
@@ -70,7 +70,7 @@
"rarch_main returned = 0",
"[ps5vk] output: default mode restored",
"native quit: frontend status = 0",
"== gpgx-gg: Addams Family, The (World).zip",
"== gpgx-gg: Game Gear Test Game (World).zip",
"[INFO] [Core] Geometry: 160x144, Aspect: 1.333, FPS: 59.92, Sample rate: 44100.00 Hz.",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
"core loader: ready symbols=135 relocations=166628 mapped_bytes=51478528",
@@ -2,8 +2,8 @@
"step": "core-recheck-current-driver",
"note": "Every shipped core on the current driver at 119.88 Hz: FCEUmm (NES), Snes9x (SNES), mGBA (GBA from .7z, and GB), FBNeo (CPS1), Genesis Plus GX (Mega Drive, Master System, Game Gear). Each ran 1,200 frames, took its exit screenshot (colours checked by eye; screenshots are not committed), closed audio with errors=0 and discarded=0, returned 0 and restored the default output mode. FBNeo's exit screenshot crashed on a null call in scaler_ctx_scale before patch 0086 (the crash report and its symbolised return address are the last lines); with 0086 it saves.",
"must_contain": [
"== fbneo7: sf2.zip",
"== gpgx-gg: Addams Family, The (World).zip",
"== fbneo7: arcade1.zip",
"== gpgx-gg: Game Gear Test Game (World).zip",
"rarch_main returned = 0",
"[ps5vk] output: default mode restored"
],
+27
View File
@@ -0,0 +1,27 @@
{
"step": "display-modes",
"recorded": "2026-10-04T18:18:24Z",
"console_run": "base PS5 13.40, 1080p screen, 2026-10-04 14:14",
"revision": "d8cd370 + display modes test (build ce576f3d, RADV PS5_Mesa 7b59ef2)",
"command": "tools/run-title.sh --no-build --display-modes-test=180 --watch 60",
"raw_capture": "klog/display-modes-summary.txt",
"raw_lines": 15,
"raw_bytes": 1137,
"lines": [
"==> tools/run-title.sh --no-build --display-modes-test=180 --watch 60 (base PS5, firmware 13.40, 1080p screen)",
"build identity: ce576f3d98e85e8d9941db666e207141369132a8e996c9b8a03a731cb1568130",
"driver: RADV from PS5_Mesa 7b59ef2 (VideoOut WSI: a mode for each of 3840x2160, 2560x1440, 1920x1080)",
"display modes test: 3 modes: 3840x2160@59940 2560x1440@59940 1920x1080@59940",
"wsi/videoout: 3840x2160 framebuffers registered as set 0, buffers 0-4",
"wsi/videoout: 2560x1440 framebuffers registered as set 1, buffers 5-9",
"wsi/videoout: 1920x1080 framebuffers registered as set 2, buffers 10-14",
"3840x2160@59940: 180 frames in 2.956 s, 3 images, ok",
"2560x1440@59940: 180 frames in 2.990 s, 3 images, ok",
"1920x1080@59940: 180 frames in 2.997 s, 3 images, ok",
"3840x2160@59940: 180 frames in 2.994 s, 3 images, ok",
"record: {\"result\": \"PASS\"}",
"Owner: each size filled the TV, white border on all four edges, the split exactly in the middle",
"Owner: RetroArch after the test steady, no flicker",
"first run (build 09c22aee, PS5_Mesa 3321f8f): a fourth set, 1280x720 at buffers 15-19, refused 0x80290001; RetroArch flickered after it, not alone; 1280x720 left out"
]
}
@@ -0,0 +1,5 @@
{"width":3840,"height":2160,"refresh_millihertz":59940,"frames":180,"images":3,"seconds":2.956,"result":"ok"}
{"width":2560,"height":1440,"refresh_millihertz":59940,"frames":180,"images":3,"seconds":2.990,"result":"ok"}
{"width":1920,"height":1080,"refresh_millihertz":59940,"frames":180,"images":3,"seconds":2.997,"result":"ok"}
{"width":3840,"height":2160,"refresh_millihertz":59940,"frames":180,"images":3,"seconds":2.994,"result":"ok"}
{"result":"PASS"}
+18
View File
@@ -0,0 +1,18 @@
{
"step": "display-modes",
"note": "The Vulkan driver's display offers 3840x2160, 2560x1440 and 1920x1080; each registers as its own VideoOut buffer set, presents at the display's rate and fills the screen; a size used again reuses its set.",
"must_contain": [
"display modes test: 3 modes: 3840x2160@59940 2560x1440@59940 1920x1080@59940",
"wsi/videoout: 1920x1080 framebuffers registered as set 2, buffers 10-14",
"1920x1080@59940: 180 frames in 2.997 s, 3 images, ok",
"2560x1440@59940: 180 frames in 2.990 s, 3 images, ok",
"3840x2160@59940: 180 frames in 2.994 s, 3 images, ok",
"record: {\"result\": \"PASS\"}",
"Owner: each size filled the TV",
"Owner: RetroArch after the test steady, no flicker"
],
"must_not_contain": [
"FAIL"
],
"human_check": "The console's owner watched the run: each size filled the TV with the border on all four edges and the split centred, and RetroArch afterwards did not flicker."
}
+3 -3
View File
@@ -2,13 +2,13 @@
"step": "display-retention",
"recorded": "2026-09-24T01:05:36Z",
"console_run": "",
"revision": "driver ../PS5_Vulkan 49679c9 (R53: VideoOut retained across swapchains, handover report); port e425dbe + main.cpp ps5vk_display_retain, pad script",
"command": "args.txt: -L fceumm <1943 (USA).zip>; /app0/pad-script.txt: 10.0 L3+R3, 12.0 DOWN, 12.6 DOWN, 13.4 B, 18.0 RIGHT, 18.8 RIGHT, 19.8 B, 21.0 B, 28.0 L3+R3, 31.0 L3+R3; run B adds /app0/ps5vk-no-retain.txt; run C: 10.0 L3+R3, 12.0 A, 13.0 UP, 14.0 B",
"revision": "driver ../PS5_Vulkan 49679c9 (R53: VideoOut retained across swapchains, handover report); port bfc2bf9 + main.cpp ps5vk_display_retain, pad script",
"command": "args.txt: -L fceumm <NES Test Game (USA).zip>; /app0/pad-script.txt: 10.0 L3+R3, 12.0 DOWN, 12.6 DOWN, 13.4 B, 18.0 RIGHT, 18.8 RIGHT, 19.8 B, 21.0 B, 28.0 L3+R3, 31.0 L3+R3; run B adds /app0/ps5vk-no-retain.txt; run C: 10.0 L3+R3, 12.0 A, 13.0 UP, 14.0 B",
"raw_capture": "klog/r53-display-retention-evidence.txt",
"raw_lines": 61,
"raw_bytes": 4943,
"lines": [
"== run A: VideoOut retained (default), FCEUmm 1943, pad script: menu, close content, run from History, menu open/close",
"== run A: VideoOut retained (default), FCEUmm NES Test Game, pad script: menu, close content, run from History, menu open/close",
"[ps5vk] output: 119.88 Hz selected",
"input: pad script loaded, 10 presses",
"[ps5vk] swapchain handover: output opened; 0.0 ms from the previous present to this swapchain, first picture 1063.4 ms after it, 0 black presents before",
@@ -0,0 +1,48 @@
{
"step": "docs-showcase-refinement",
"command": "Source assertions and CUA preview inspection; bash tools/verify.sh format evidence",
"lines": [
"README warning and reminders unchanged",
"Four requested README sections removed",
"Frontend picker heading and screenshot visible outside disclosures",
"All 33 renderer entries match the original core table: Vulkan GPU or software",
"Both local previews refreshed; no broken images; README has no page overflow",
"Frontend showcase precedes At a glance"
],
"readme_sha256": "2902750b24350fa09ffe7837d7ec5e51aaa906ada5443d5575e636a583338a35",
"renderer_map": {
"FCEUmm": "Software",
"mGBA": "Software",
"Snes9x": "Software",
"Mupen64Plus-Next": "Vulkan (GPU)",
"Dolphin": "Vulkan (GPU)",
"DeSmuME": "Software",
"Azahar": "Vulkan (GPU)",
"Beetle VB": "Software",
"PokeMini": "Software",
"Beetle PSX HW": "Vulkan (GPU)",
"LRPS2": "Vulkan (GPU)",
"PPSSPP": "Vulkan (GPU)",
"Genesis Plus GX": "Software",
"PicoDrive": "Software",
"Beetle Saturn": "Software",
"Flycast": "Vulkan (GPU)",
"FinalBurn Neo": "Software",
"MAME": "Software",
"Beetle NeoPop": "Software",
"NeoCD": "Software",
"Beetle PCE": "Software",
"Beetle PC-FX": "Software",
"Stella": "Software",
"a5200": "Software",
"ProSystem": "Software",
"Handy": "Software",
"Virtual Jaguar": "Software",
"VICE x64sc": "Software",
"PUAE": "Software",
"DOSBox Pure": "Software",
"ScummVM": "Software",
"Opera": "Software",
"Beetle Cygne": "Software"
}
}
@@ -0,0 +1,15 @@
{
"step": "docs-showcase-refinement",
"must_contain": [
"README warning and reminders unchanged",
"Four requested README sections removed",
"Frontend picker heading and screenshot visible outside disclosures",
"All 33 renderer entries match the original core table: Vulkan GPU or software",
"Both local previews refreshed; no broken images; README has no page overflow",
"Frontend showcase precedes At a glance"
],
"must_not_contain": [
"FAIL"
],
"human_check": "README renderer table and expanded frontend showcase checked visually; frontend appears above highlights."
}
@@ -1,14 +1,14 @@
{
"step": "dolphin-wind-waker-boot",
"step": "dolphin-gamecube-boot",
"recorded": "2026-09-24T16:14:34Z",
"console_run": "dol12",
"revision": "port (this commit), driver ../PS5_Vulkan 9e63105 (R58), libretro/dolphin c6630001e0 + patches/dolphin/ps5-port.patch",
"command": "args.txt: -L /app0/cores/dolphin_libretro.so '/app0/content/Nintendo - GameCube/<Wind Waker>.rvz' --max-frames=1800 --max-frames-ss (screenshot fetched and removed; state snapshotted and restored)",
"command": "args.txt: -L /app0/cores/dolphin_libretro.so '/app0/content/Nintendo - GameCube/<GameCube Test Game>.rvz' --max-frames=1800 --max-frames-ss (screenshot fetched and removed; state snapshotted and restored)",
"raw_capture": "klog/dol12-evidence.txt",
"raw_lines": 18,
"raw_bytes": 1049,
"lines": [
"== dolphin: Legend of Zelda, The - The Wind Waker (USA).rvz, 1800 frames, JIT64 + fastmem, Vulkan",
"== dolphin: GameCube Test Game (USA).rvz, 1800 frames, JIT64 + fastmem, Vulkan",
"core loader: ready symbols=29383 relocations=64546 mapped_bytes=36683776",
"core loader: ready symbols=29383 relocations=64546 mapped_bytes=36683776",
"[ps5vk] output: 119.88 Hz selected",
@@ -0,0 +1,16 @@
{
"step": "dolphin-gamecube-boot",
"note": "Dolphin boots GameCube Test Game to its title screen on the console: JIT64 with fastmem, Vulkan through PS5_Vulkan, 1800 frames at 119.88 Hz output, zero driver refusals, zero failed Vulkan calls, audio closed with errors=0 and discarded=0, clean exit. The title screen is NOT yet correct: the logo, sky, clouds and text render, but the mountain in the background is missing (reported on the console; open). Screenshots are not committed.",
"must_contain": [
"Fastmem enabled = Yes",
"CPU Core: JIT64",
"refusals: 0",
"failed calls in retroarch.log: 0",
"rarch_main returned = 0"
],
"must_not_contain": [
"crash:",
"errors=1"
],
"human_check": ""
}
+50 -50
View File
@@ -2,62 +2,62 @@
"step": "dolphin-p10-state-stress",
"recorded": "2026-09-25T13:35:59Z",
"console_run": "",
"revision": "title 718b41d7: driver d419098 (R78 and the zero-mapping fix), RetroArch c1d9f15 and the state-copy test aid",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Wind Waker from a copy of my entry state in /app0/p10-states; 21 saves and 21 loads, the last twelve 0.6 s apart; driver profile armed",
"revision": "title 718b41d7: driver d419098 (R78 and the zero-mapping fix), RetroArch d89c5a6 and the state-copy test aid",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game from a copy of my entry state in /app0/p10-states; 21 saves and 21 loads, the last twelve 0.6 s apart; driver profile armed",
"raw_capture": "klog/p10-states-ww-states-excerpt.txt",
"raw_lines": 68,
"raw_bytes": 6080,
"lines": [
"build identity: 718b41d7aaf5ad5a727d8aa124f60b54600880792d79b50fc27cce9c80d19c7f",
"test state copy bytes = 22579764",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93447736 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93447736 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382128 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/Legend of Zelda, The - The Wind Waker (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93447736 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93447736 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382128 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Saving state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"[State] Loading state \"/app0/p10-states/GameCube Test Game (USA).state\", 93382088 bytes.",
"audio ps5: window played=399115 silence=80885",
"audio ps5: window played=480000 silence=0",
"audio ps5: window played=480000 silence=0",
+2 -2
View File
@@ -2,8 +2,8 @@
"step": "dolphin-p11-soak30-re4",
"recorded": "2026-09-25T21:55:12Z",
"console_run": "",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 11f5a81",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Resident Evil 4 from my entry state, 30 minutes with no input, a screenshot every five minutes; driver profile armed",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 35c905b",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game 3 from my entry state, 30 minutes with no input, a screenshot every five minutes; driver profile armed",
"raw_capture": "klog/p11-soak30-re4-excerpt.txt",
"raw_lines": 99,
"raw_bytes": 12128,
+2 -2
View File
@@ -2,8 +2,8 @@
"step": "dolphin-p11-soak30-ww",
"recorded": "2026-09-25T21:55:12Z",
"console_run": "",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 11f5a81",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Wind Waker from my entry state, 30 minutes with no input, a screenshot every five minutes; driver profile armed",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 35c905b",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game from my entry state, 30 minutes with no input, a screenshot every five minutes; driver profile armed",
"raw_capture": "klog/p11-soak30-ww-excerpt.txt",
"raw_lines": 95,
"raw_bytes": 11981,
+2 -2
View File
@@ -2,8 +2,8 @@
"step": "dolphin-p11-soak60-ww",
"recorded": "2026-09-25T21:55:12Z",
"console_run": "",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 11f5a81",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Wind Waker from my entry state, 60 minutes with no input, a screenshot every ten minutes; driver profile armed",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 35c905b",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game from my entry state, 60 minutes with no input, a screenshot every ten minutes; driver profile armed",
"raw_capture": "klog/p11-soak60-ww-excerpt.txt",
"raw_lines": 93,
"raw_bytes": 11712,
@@ -3,7 +3,7 @@
"recorded": "2026-09-25",
"console_run": "not read this run",
"revision": "title 5beca7cc: driver R74 (up to five images) and RetroArch patch 0091",
"command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; driver profile armed",
"command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; GameCube Test Game from my entry state; driver profile armed",
"raw_capture": "klog/ww-p2-r74-trace.txt",
"raw_lines": 106,
"raw_bytes": 16201,
@@ -3,7 +3,7 @@
"recorded": "2026-09-25",
"console_run": "not read this run",
"revision": "title f2585b8f: driver R73, three swapchain images at 119.88 Hz with a swap interval of 2",
"command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; driver profile armed",
"command": "tooling/dolphin-profiles/p2-ubershaders-1x.txt as /app0/dolphin-options.txt; GameCube Test Game from my entry state; driver profile armed",
"raw_capture": "klog/p2-ubershaders-1x-ww-trace.txt",
"raw_lines": 112,
"raw_bytes": 16467,
@@ -1,6 +1,6 @@
{
"step": "dolphin-p2-swapchain-before",
"note": "Before: Wind Waker under synchronous ubershaders ran at 89-99% (a window as short as 427837 of 480000 audio frames) with the GPU busy 0.7 ms a present. RetroArch presented every frame twice into three images, and the second present waited for a vblank on the thread that runs the frame-stepped core.",
"note": "Before: GameCube Test Game under synchronous ubershaders ran at 89-99% (a window as short as 427837 of 480000 audio frames) with the GPU busy 0.7 ms a present. RetroArch presented every frame twice into three images, and the second present waited for a vblank on the thread that runs the frame-stepped core.",
"must_contain": [
"Got 3 swapchain images",
"[ps5vk] output: 119.88 Hz selected",
@@ -3,8 +3,8 @@
"recorded": "2026-09-25T11:56:13Z",
"console_run": "",
"revision": "title 9c623566: driver releasing only mappings with bytes; RetroArch patch 0092",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Melee from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"raw_capture": "klog/p9-fixed-ssbm-reload-excerpt.txt",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game 2 from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"raw_capture": "klog/p9-fixed-gc2-reload-excerpt.txt",
"raw_lines": 55,
"raw_bytes": 2100,
"lines": [
@@ -3,8 +3,8 @@
"recorded": "2026-09-25T11:56:13Z",
"console_run": "",
"revision": "title 26c9daa0: driver R78 with the direct_live profile; RetroArch without patch 0092",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Melee from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"raw_capture": "klog/p9-long-ssbm-reload-excerpt.txt",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game 2 from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"raw_capture": "klog/p9-long-gc2-reload-excerpt.txt",
"raw_lines": 55,
"raw_bytes": 2100,
"lines": [
+2 -2
View File
@@ -2,8 +2,8 @@
"step": "dolphin-p9-reload-flat",
"recorded": "2026-09-25T19:25:07Z",
"console_run": "",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 11f5a81",
"command": "tooling/dolphin-profiles/p1-accurate.txt; Melee from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"revision": "title 9a84b116: driver 2f1e447, RetroArch patch 0093 on 35c905b",
"command": "tooling/dolphin-profiles/p1-accurate.txt; GameCube Test Game 2 from my entry state; the pad script's four Quick Menu closes and RELOADs; driver profile armed",
"raw_capture": "klog/p9-0093-reload-excerpt.txt",
"raw_lines": 26,
"raw_bytes": 2900,
+1 -1
View File
@@ -3,7 +3,7 @@
"recorded": "2026-09-25",
"console_run": "not read this run",
"revision": "title 5253301f: Dolphin core before the duplicate-field fix, driver R73",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; no pad input for 150 s; driver profile armed",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; GameCube Test Game from my entry state; no pad input for 150 s; driver profile armed",
"raw_capture": "klog/wwsmp73b-trace.txt",
"raw_lines": 283,
"raw_bytes": 38497,
@@ -1,6 +1,6 @@
{
"step": "dolphin-pacing-before",
"note": "Before: with bSkipPresentingDuplicateXFBs (the default) a 30 FPS game's repeated fields hand RetroArch no frame, so every other retro_run returns without presenting and is paced by the audio writes alone. At 119.88 Hz (swap interval 2) Wind Waker's present periods cross 40 ms 14-17 times in 130 s, at full speed.",
"note": "Before: with bSkipPresentingDuplicateXFBs (the default) a 30 FPS game's repeated fields hand RetroArch no frame, so every other retro_run returns without presenting and is paced by the audio writes alone. At 119.88 Hz (swap interval 2) GameCube Test Game's present periods cross 40 ms 14-17 times in 130 s, at full speed.",
"must_contain": [
"[ps5vk] output: 119.88 Hz selected",
"hitch period_ms=4",
@@ -3,7 +3,7 @@
"recorded": "2026-09-25",
"console_run": "not read this run",
"revision": "title f2585b8f: duplicate (NULL) video_cb from Presenter::ViSwap, end-of-run fallback",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; no pad input for 150 s; driver profile armed",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; GameCube Test Game from my entry state; no pad input for 150 s; driver profile armed",
"raw_capture": "klog/ww-dupecb2-trace.txt",
"raw_lines": 206,
"raw_bytes": 22446,
@@ -3,7 +3,7 @@
"recorded": "2026-09-25",
"console_run": "not read this run",
"revision": "title 7b05e5b2: duplicate (NULL) video_cb at the end of retro_run only",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; Wind Waker from my entry state; no pad input for 150 s; driver profile armed",
"command": "tooling/dolphin-profiles/p1-accurate.txt as /app0/dolphin-options.txt; GameCube Test Game from my entry state; no pad input for 150 s; driver profile armed",
"raw_capture": "klog/ww-dupecb-trace.txt",
"raw_lines": 208,
"raw_bytes": 22493,
+1 -1
View File
@@ -16,7 +16,7 @@
"driver 34ca3ad (R64): 0.43-0.52, corner triangles and a panel spill in the screenshots",
"Dolphin drawing lists instead of restart strips: 0.12-0.20",
"driver A/B writing restart before every indexed draw: 0.12-0.20",
"== save state slot 0 (Outset Island), --entryslot=0, --max-frames=3600, driver 75f33eb",
"== save state slot 0 (the first island), --entryslot=0, --max-frames=3600, driver 75f33eb",
"[INFO] [State] Loading entry state from <state> succeeded.",
"rarch_main returned = 0",
"audio ps5: closed accepted=5692056 played=5692056 discarded=0 silence=122216 calls=22712 errors=0 peak=3072 capacity=3072 drain=00000100 close=00000000"
@@ -1,6 +1,6 @@
{
"step": "dolphin-restart-strips",
"note": "Wind Waker's 3D scene drew a huge corrupt surface (R64: the driver turned primitive restart off right after each restart draw, and the write landed mid-draw) and, after that fix, thin triangles to the screen corner and a minimap panel spill (R65: every submission a copy splits off starts with restart off). Both fixed in ../PS5_Vulkan; the FIFO log now matches desktop Dolphin to resampling (mean 1.13-1.16) and the save state on Outset Island draws cleanly.",
"note": "GameCube Test Game's 3D scene drew a huge corrupt surface (R64: the driver turned primitive restart off right after each restart draw, and the write landed mid-draw) and, after that fix, thin triangles to the screen corner and a minimap panel spill (R65: every submission a copy splits off starts with restart off). Both fixed in ../PS5_Vulkan; the FIFO log now matches desktop Dolphin to resampling (mean 1.13-1.16) and the save state on the first island draws cleanly.",
"must_contain": [
"after R65 (dolfull2, driver 75f33eb): 1.13 1.15 1.16",
"Loading entry state from <state> succeeded.",
@@ -0,0 +1,29 @@
{
"step": "dolphin-texture-budget-before",
"recorded": "2026-10-19T01:58:46Z",
"console_run": "213014",
"revision": "aa7f0b2",
"command": "dolphin_load_custom_textures = \"enabled\" in config/dolphin-emu/dolphin-emu.opt, the owner's pack in savefiles/dolphin-emu/User/Load/Textures/; tools/run-title.sh --no-build --no-deploy \"--game=dolphin:/app0/content/gc/<gc game>.gcz\" --pad-script=tests/pad/dolphin-texture-pack.txt --watch 95",
"raw_capture": "klog/evidence-5-before-focus.txt",
"raw_lines": 17,
"raw_bytes": 1293,
"lines": [
" armed pad script tests/pad/dolphin-texture-pack.txt",
"==> [run] ok launched PPSA99169 result=0x00004018 via lncUtilApp/foreground as user <user> (the console reports the title running)",
" build identity: a2c7800a344aa5ddb08c175f9fa8543edadf271d79895da3bf15fbf5995c2093",
" core loader: ready symbols=29319 relocations=64467 mapped_bytes=36749312 base=400840000",
" core loader: ready symbols=29319 relocations=64467 mapped_bytes=36749312 base=400840000",
" video driver: video_vulkan init entered",
" input: pad script loaded, 0 presses",
" input: pad script SCREENSHOT at 40.02 s: /app0/pad-shot-1.png 1",
" input: pad script SCREENSHOT at 60.01 s: /app0/pad-shot-2.png 1",
" input: pad script STOP at 70.01 s",
" pad script: 2 picture(s) saved to klog/pad-213014",
"RetroArch log is stale or logging failed: current build identity absent",
"[INFO] [PS5] build identity: a2c7800a344aa5ddb08c175f9fa8543edadf271d79895da3bf15fbf5995c2093",
"[libretro INFO] 31:12:988 Core/ConfigManager.cpp:279 N[CORE]: Active title: <gc game> (<game id>)",
"[INFO] [Core] Geometry: 2560x2112, Aspect: 1.333, FPS: 59.94, Sample rate: 32029.00 Hz.",
"[INFO] [Video] Set video size to: fullscreen.",
"[libretro ERROR] 31:14:071 VideoCommon/Assets/CustomAssetCache.cpp:26 E[Video]: Not enough system memory for custom resources."
]
}
@@ -0,0 +1,10 @@
{
"step": "dolphin-texture-budget-before",
"note": "Issue 5 before the fix, on build a2c7800a: Dolphin's custom asset cache gets no memory at all on the PS5, as reported.",
"must_contain": [
"build identity: a2c7800a",
"CustomAssetCache.cpp:26 E[Video]: Not enough system memory for custom resources."
],
"must_not_contain": [],
"human_check": ""
}
@@ -0,0 +1,28 @@
{
"step": "dolphin-texture-budget",
"recorded": "2026-10-19T01:58:46Z",
"console_run": "215319",
"revision": "3c23512",
"command": "dolphin_load_custom_textures = \"enabled\" in config/dolphin-emu/dolphin-emu.opt, the owner's pack in savefiles/dolphin-emu/User/Load/Textures/; tools/run-title.sh --no-build --no-deploy \"--game=dolphin:/app0/content/gc/<gc game>.gcz\" --pad-script=tests/pad/dolphin-texture-pack.txt --watch 95",
"raw_capture": "klog/evidence-5-after-focus.txt",
"raw_lines": 16,
"raw_bytes": 1145,
"lines": [
" armed pad script tests/pad/dolphin-texture-pack.txt",
"==> [run] ok launched PPSA99169 result=0x0000a018 via lncUtilApp/foreground as user <user> (the console reports the title running)",
" build identity: 191ff52ccdb6eb3b157895e1ca3804f477e180f206dfaf1b9c2452493cd072d4",
" core loader: ready symbols=29319 relocations=64467 mapped_bytes=36732928 base=400840000",
" core loader: ready symbols=29319 relocations=64467 mapped_bytes=36732928 base=400840000",
" video driver: video_vulkan init entered",
" input: pad script loaded, 0 presses",
" input: pad script SCREENSHOT at 40.02 s: /app0/pad-shot-1.png 1",
" input: pad script SCREENSHOT at 60.01 s: /app0/pad-shot-2.png 1",
" input: pad script STOP at 70.01 s",
" pad script: 2 picture(s) saved to klog/pad-215319",
"==> [run] VERDICT: it exited on its own before 95s",
"[INFO] [PS5] build identity: 191ff52ccdb6eb3b157895e1ca3804f477e180f206dfaf1b9c2452493cd072d4",
"[libretro INFO] 54:16:480 Core/ConfigManager.cpp:279 N[CORE]: Active title: <gc game> (<game id>)",
"[INFO] [Core] Geometry: 2560x2112, Aspect: 1.333, FPS: 59.94, Sample rate: 32029.00 Hz.",
"[INFO] [Video] Set video size to: fullscreen."
]
}
@@ -0,0 +1,15 @@
{
"step": "dolphin-texture-budget",
"note": "Issue 5 after the fix, on build 191ff52c: with Load Custom Textures on and the owner's pack in place, the cache starts with a 512 MiB budget (no error), unloads nothing, and the game runs to the script's STOP.",
"must_contain": [
"build identity: 191ff52c",
"Active title: <gc game> (<game id>)",
"pad script STOP at 70.01 s"
],
"must_not_contain": [
"Not enough system memory",
"Unloading asset",
"crash: signal"
],
"human_check": "Start the game with the pack and Load Custom Textures on: the pack's textures show."
}
@@ -1,16 +0,0 @@
{
"step": "dolphin-wind-waker-boot",
"note": "Dolphin boots Wind Waker to its title screen on the console: JIT64 with fastmem, Vulkan through PS5_Vulkan, 1800 frames at 119.88 Hz output, zero driver refusals, zero failed Vulkan calls, audio closed with errors=0 and discarded=0, clean exit. The title screen is NOT yet correct: the logo, sky, clouds and text render, but the mountain in the background is missing (reported on the console; open). Screenshots are not committed.",
"must_contain": [
"Fastmem enabled = Yes",
"CPU Core: JIT64",
"refusals: 0",
"failed calls in retroarch.log: 0",
"rarch_main returned = 0"
],
"must_not_contain": [
"crash:",
"errors=1"
],
"human_check": ""
}
@@ -1,7 +1,7 @@
{
"step": "driver-1.0-native-byte-order",
"recorded": "2026-09-20",
"revision": "ca02af3 (title source) / 8b311e3 (the driver the link carries)",
"revision": "2b1d129 (title source) / 8b311e3 (the driver the link carries)",
"command": "PS5_VULKAN_DIR=$PWD/build/frozen-driver PS5_VULKAN_ICD=$PWD/build/frozen-driver/build/driver/ps5/libvulkan.so.1 bash tools/verify.sh",
"raw_capture": "build/verify-native-byte-order.log",
"raw_lines": 7718,
+1 -1
View File
@@ -27,5 +27,5 @@
},
"cleanup": "Title exited normally, test files removed or restored, frontend configuration and PPSSPP profile byte-identical, control helper stopped.",
"limitations": "Native service accepted nonzero synthesized PCM. Physical feel was not observed. Float format and failure recovery verified with host mocks, not forced on hardware. Games/cores must request libretro rumble; this does not add authored game haptics or adaptive triggers.",
"derived_from": "https://github.com/mihawk-99/PS5_RetroArch/pull/6 at cc0cc0a4755ad4a69321b22ba041eae9e2322b82"
"derived_from": "https://github.com/mihawk-99/PS5_RetroArch/pull/6 at 56a369270dd07cf61687d7914a87feed095e87b1"
}
+23
View File
@@ -0,0 +1,23 @@
{
"step": "esde-collections",
"recorded": "2026-10-05T02:32:04Z",
"console_run": "base PS5 13.40, runs 222652 (favourite), 222946 and 223054 (ES-DE)",
"revision": "1e6b8cb + RetroArch's favourites and play in ES-DE",
"command": "tools/run-title.sh --no-build --no-deploy --pad-script=tests/pad/history-favourite.txt --watch 25; tools/run-title.sh --no-build --no-deploy --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4 --watch 30",
"raw_capture": "klog/collections-evidence/summary.txt",
"raw_lines": 11,
"raw_bytes": 1283,
"lines": [
"==> base PS5 13.40, build of this commit (PS5_FRONTENDS='es-de picker')",
"RetroArch, tests/pad/history-favourite.txt (History tab, the newest game's menu, Add to Favorites):",
" input: pad script MENU at 9.51 s: depth 2, list 'deferred_rpl_entry_actions' '', entry 3 of 7 'Add to Favorites'",
" RetroArch's favourites (playlists/builtin/content_favorites.lpl): Jungle Run 2",
"ES-DE's next start (--relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4 --watch 30):",
" start: library from RetroArch's playlists: 14 systems, 16 games (16 with ES-DE's details kept), 15 cores, 1 favourites, 15 played, 0 failures",
" es_settings.xml: CollectionSystemsAuto = favorites,recent (enabled once; it was empty)",
" gamelist snes, Jungle Run 2: favorite true, lastplayed 20261004T221855 (ES-DE's, later than the runtime log's 22:18:53), playcount 8 (the runtime log's), playtime 838 (the runtime log's 0:13:58)",
" the SNES card: 1 GAME (1 FAVORITE)",
"ES-DE scrolled (--frontend-capture=4,6,8,10 --frontend-scroll): the carousel holds the Favorites (heart) and Last Played (clock) collections",
"host tests: ps5_library (favourites, history, runtime logs, a time from the history place) PASS; esde_library (favourites from both frontends, play records, the collections once) PASS"
]
}
@@ -0,0 +1,12 @@
{
"step": "esde-collections",
"note": "RetroArch's favourites, history and runtime logs reach ES-DE's game lists through the shared library: a favourite added in RetroArch's menu is ES-DE's favourite, RetroArch's play counts, times and last played show there, and ES-DE shows its Favorites and Last Played collections.",
"must_contain": [
"1 favourites, 15 played, 0 failures",
"CollectionSystemsAuto = favorites,recent",
"favorite true, lastplayed 20261004T221855",
"playcount 8 (the runtime log's), playtime 838"
],
"must_not_contain": [],
"human_check": "klog/frontend-20261004-223054 pictures: the carousel shows a heart (Favorites) and a clock (Last Played) card after PSP; klog/frontend-20261004-222946 shows '1 GAME (1 FAVORITE)' under SNES."
}
@@ -0,0 +1,3 @@
{"run":"frontend-20261004-162717","seconds":15,"frame":800,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-162717-15.ppm","gl_error":0,"written":true}
{"run":"frontend-20261004-162717","seconds":30,"frame":1677,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-162717-30.ppm","gl_error":0,"written":true}
{"run":"frontend-20261004-162717","seconds":50,"frame":2854,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-162717-50.ppm","gl_error":0,"written":true}
+32
View File
@@ -0,0 +1,32 @@
{
"step": "esde-first-light",
"recorded": "2026-10-04T20:29:35Z",
"console_run": "base PS5 13.40, run frontend-20261004-162717",
"revision": "c0dbb26 + ES-DE port (uncommitted at capture)",
"command": "PS5_FRONTENDS=es-de tools/build-title.sh; tools/esde-settings.py Theme=alekfull-nx-es-de StartupSystem=snes; tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=15,30,50 --watch 70",
"raw_capture": "klog/esde-first-light-summary.txt",
"raw_lines": 20,
"raw_bytes": 1436,
"lines": [
"==> PS5_FRONTENDS=es-de tools/build-title.sh; tools/esde-settings.py Theme=alekfull-nx-es-de StartupSystem=snes; tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=15,30,50 --watch 70 (base PS5, 13.40)",
"es-de/es-de.bin: 48,040,117 bytes, sha256 167667843b4a64e6; deployed and verified as stored",
"kernel log: EXEC /app0/eboot.bin",
"kernel log: EXEC /app0/es-de/es-de.bin",
"kernel log: fatal signals 0, heap refusals 0",
"es_log: ES-DE 3.5.0 (r52)",
"es_log: GL renderer: PS5 AGC",
"es_log: GL version: 4.6 (Core Profile) Mesa 26.2.0",
"es_log: Display resolution: 1920x1080",
"es_log: Parsed configuration for 24 systems, loaded 24 systems (collections not included)",
"es_log: Total game count: 46",
"es_log: Added controller with default configuration: \"ffashiofile\" (GUID: 0300d0424c050000e60c000011810000, serial number: n/a, instance ID: 0, device index: 0)",
"es_log: Finished loading theme \"alekfull-nx-es-de\"",
"es_log: Application startup time: 1438 ms",
"es_log: Couldn't initialize SDL audio: SDL not built with audio support",
"es_log: 134 lines, 0 isSymlink refusals, 0 images not loaded",
"capture 15 s: frame 800, 1920x1080, GL error 0, 377792 colours",
"capture 30 s: frame 1677, 1920x1080, GL error 0, 377792 colours",
"capture 50 s: frame 2854, 1920x1080, GL error 0, 377792 colours",
"frontend capture PASS: 3 pictures (the 30 s one is the Alekfull NX SNES system view: klog/previews/esde-alekfull-snes-1920x1080.png)"
]
}
@@ -0,0 +1,16 @@
{
"step": "esde-first-light",
"note": "EmulationStation (ES-DE 3.5.0) runs on the PS5 as /app0/es-de/es-de.bin: it loads RetroArch's 24 content folders as systems, draws the Alekfull NX theme's SNES system view at 1920x1080 through PS5_OpenGL, with no fatal signal, no refused heap allocation and no image it could not load.",
"must_contain": [
"kernel log: EXEC /app0/es-de/es-de.bin",
"kernel log: fatal signals 0, heap refusals 0",
"es_log: Parsed configuration for 24 systems, loaded 24 systems (collections not included)",
"es_log: Finished loading theme \"alekfull-nx-es-de\"",
"es_log: GL renderer: PS5 AGC",
"es_log: 134 lines, 0 isSymlink refusals, 0 images not loaded",
"capture 30 s: frame 1677, 1920x1080, GL error 0, 377792 colours",
"frontend capture PASS"
],
"must_not_contain": [],
"human_check": "The 30 s capture (klog/previews/esde-alekfull-snes-1920x1080.png) shows Alekfull NX's SNES system view: the Super Nintendo logo, Space Hunter artwork and the system carousel."
}
+32
View File
@@ -0,0 +1,32 @@
{
"step": "esde-folder-names",
"recorded": "2026-10-17T23:18:12Z",
"console_run": "",
"revision": "the commits this record lands with (content folders as ES-DE names them; a cartridge system's archives; ES-DE's first start)",
"command": "tools/run-title.sh --no-build --no-deploy --home-launch --watch 30; tools/run-title.sh --no-build --no-deploy --picker-test=300:es-de --frontend-capture=25,40 --watch 60; FTP reads of content/, playlists/, config/ and es-de/ES-DE/custom_systems/es_systems.xml",
"raw_capture": "klog/esde-folder-names/record.txt",
"raw_lines": 20,
"raw_bytes": 2224,
"lines": [
"Console: the owner's base PS5 (1440p, 120 Hz, VRR), a fresh install (no config/frontend.cfg), 325 of the owner's game backups in 34 ES-DE folders, 2026-10-17.",
"Seeded as an earlier build left it: content/Sega - Dreamcast (a placeholder; no dreamcast folder), content/Nintendo - Game Boy (a placeholder; gb held 10 backups), content/Sony - PlayStation (a placeholder; psx held 10), playlists/Nintendo - Game Boy.lpl naming the placeholder, config/mGBA/Nintendo - Game Boy.opt.",
"Home-screen launch, build identity 02db7d47f5efd5e42dc17f121cd19125fa99a09ed80aeaae5786494d68b8f54e:",
" startup: system folders renamed as ES-DE names them = 3",
" startup: system folders made in content = 12",
" frontend: mode '', test run 0, picker test 0, picker 1, es-de 1, remembered ask, L1 0 -> picker",
" EXEC /app0/picker/picker.bin; [Frontend Picker] starts",
"After: no Sega - Dreamcast, Nintendo - Game Boy or Sony - PlayStation folder; dreamcast 1 file, gb 11, psx 11.",
" playlist: \"scan_content_dir\": \"/app0/content/gb\", \"path\": \"/app0/content/gb/placeholder-a.gb\"",
" config/mGBA/gb.opt",
"ES-DE's first start on that build (its folder not made yet):",
" start: library from RetroArch's playlists: 28 systems, 255 games (0 with ES-DE's details kept), 33 cores, 0 favourites, 0 played, 29 failures",
" start: systems written -1",
"ES-DE's second start (ES-DE had made its folder by then): 28 systems, 255 games, 0 failures; systems written 28.",
"ES-DE's first start after the fixes (build identity 22b5d1229342dc01e94a5b023dd9534b17836c1b70f038246a92ed5f811a9c61, ES-DE's folder moved aside):",
" start: library from RetroArch's playlists: 34 systems, 317 games (0 with ES-DE's details kept), 33 cores, 0 favourites, 0 played, 0 failures",
" start: systems written 34",
"es_systems.xml: 34 systems, every path /app0/content/<name>: amiga atari2600 atari5200 atari7800 atarijaguar atarilynx c64 dreamcast fds gamegear gb gba gbc gc genesis mastersystem n3ds n64 nds nes ngp ngpc pcengine pokemini psp psx sega32x sg-1000 snes supergrafx virtualboy wii wonderswan wonderswancolor",
"Host: python3 -m unittest tests.test_ps5_library tests.test_esde_library: OK; each new case fails without its fix.",
"Test files removed from the console afterwards; the owner's backups kept."
]
}
@@ -0,0 +1,16 @@
{
"step": "esde-folder-names",
"note": "Content folders take ES-DE's system names; folders earlier builds named as RetroArch does are renamed or merged with their playlists and per-folder settings; a fresh install starts in the picker; ES-DE's first start writes its library (it wrote nothing before: its own folder did not exist yet) and finds archived games of systems whose cores also take discs.",
"must_contain": [
"startup: system folders renamed as ES-DE names them = 3",
"picker 1, es-de 1, remembered ask, L1 0 -> picker",
"\"path\": \"/app0/content/gb/placeholder-a.gb\"",
"config/mGBA/gb.opt",
"29 failures",
"34 systems, 317 games (0 with ES-DE's details kept), 33 cores, 0 favourites, 0 played, 0 failures",
"start: systems written 34",
"every path /app0/content/<name>"
],
"must_not_contain": [],
"human_check": ""
}
@@ -0,0 +1,3 @@
{"run":"frontend-20261004-170557","seconds":4,"frame":173,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-170557-4.ppm","gl_error":0,"written":true,"interval_frames":172,"interval_ms":{"mean":23.27,"p50":17.01,"p95":37.18,"max":674.18,"over_25":15},"swap_ms":{"mean":0.03,"p95":0.04,"max":0.85},"presses":0,"slow":["0.90:674@0","1.38:193@0"]}
{"run":"frontend-20261004-170557","seconds":25,"frame":1372,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-170557-25.ppm","gl_error":0,"written":true,"interval_frames":1199,"interval_ms":{"mean":17.52,"p50":16.99,"p95":17.62,"max":373.30,"over_25":30},"swap_ms":{"mean":0.03,"p95":0.03,"max":0.06},"presses":50,"slow":["4.38:373@0"]}
{"run":"frontend-20261004-170557","seconds":45,"frame":2543,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-170557-45.ppm","gl_error":0,"written":true,"interval_frames":1171,"interval_ms":{"mean":17.08,"p50":16.99,"p95":17.62,"max":350.18,"over_25":8},"swap_ms":{"mean":0.03,"p95":0.03,"max":0.06},"presses":49,"slow":["25.36:350@50"]}
@@ -0,0 +1,4 @@
{"run":"frontend-20261004-163532","seconds":4,"frame":170,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-163532-4.ppm","gl_error":0,"written":true,"interval_frames":169,"interval_ms":{"mean":23.68,"p50":16.67,"p95":40.46,"max":678.56,"over_25":15},"swap_ms":{"mean":0.03,"p95":0.04,"max":0.53},"presses":0}
{"run":"frontend-20261004-163532","seconds":12,"frame":394,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-163532-12.ppm","gl_error":0,"written":true,"interval_frames":224,"interval_ms":{"mean":35.72,"p50":37.62,"p95":57.87,"max":372.80,"over_25":174},"swap_ms":{"mean":0.03,"p95":0.04,"max":0.06},"presses":17}
{"run":"frontend-20261004-163532","seconds":24,"frame":718,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-163532-24.ppm","gl_error":0,"written":true,"interval_frames":324,"interval_ms":{"mean":37.14,"p50":37.75,"p95":38.03,"max":365.03,"over_25":298},"swap_ms":{"mean":0.03,"p95":0.04,"max":0.06},"presses":28}
{"run":"frontend-20261004-163532","seconds":40,"frame":1154,"width":1920,"height":1080,"file":"/app0/es-de/capture-frontend-20261004-163532-40.ppm","gl_error":0,"written":true,"interval_frames":436,"interval_ms":{"mean":36.64,"p50":37.72,"p95":38.01,"max":344.55,"over_25":402},"swap_ms":{"mean":0.03,"p95":0.03,"max":0.06},"presses":37}
+22
View File
@@ -0,0 +1,22 @@
{
"step": "esde-frame-rate",
"recorded": "2026-10-04T21:07:48Z",
"console_run": "base PS5 13.40, runs frontend-20261004-163532 (before), frontend-20261004-170557 (after); profiles frontend-20261004-164802, frontend-20261004-165545",
"revision": "eaba7a8 + frame-time capture and profiler; PS5_OpenGL e6990b5",
"command": "PS5_FRONTENDS=es-de tools/build-title.sh; tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4,25,45 --frontend-scroll --watch 60",
"raw_capture": "klog/esde-frame-rate-summary.txt",
"raw_lines": 10,
"raw_bytes": 1323,
"lines": [
"==> tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=... --frontend-scroll [--frontend-profile] --watch 60 (base PS5, 13.40; Right pressed every 400 ms from 5 s)",
"before (PS5_OpenGL 54f01c7): 224 frames to 12 s, 17 presses: between swaps mean 35.72 ms, p95 57.87 ms, 174 over 25 ms",
"before (PS5_OpenGL 54f01c7): 324 frames to 24 s, 28 presses: between swaps mean 37.14 ms, p95 38.03 ms, 298 over 25 ms",
"before (PS5_OpenGL 54f01c7): 436 frames to 40 s, 37 presses: between swaps mean 36.64 ms, p95 38.01 ms, 402 over 25 ms",
"after (PS5_OpenGL e6990b5): 1199 frames to 25 s, 50 presses: between swaps mean 17.52 ms, p95 17.62 ms, 30 over 25 ms",
"after (PS5_OpenGL e6990b5): 1171 frames to 45 s, 49 presses: between swaps mean 17.08 ms, p95 17.62 ms, 8 over 25 ms",
"profile before (frontend-20261004-164802): 24567 samples, 75.0% self in util_clflushopt_range, 18288 of them under ps5_agc_gate2_run's scanout pool flush (ps5_agc_native_runtime.c:4223)",
"profile after (frontend-20261004-165545): 10267 samples, util_clflushopt_range 1.8% self",
"driver: PS5_OpenGL e6990b5 'platform: flush the scanout pool once, not on every unbatched write to it'; make test PASS",
"frames over 100 ms after the change: the startup ones and one at each capture (the capture's own readback and 6 MB write)"
]
}
+12
View File
@@ -0,0 +1,12 @@
{
"step": "esde-frame-rate",
"note": "ES-DE ran at 27 fps while browsing because ../PS5_OpenGL flushed its whole 16 MiB scanout pool from the CPU cache before every unbatched scanout write (each window glClear, about 8 ms each); flushed once per pool (PS5_OpenGL e6990b5), ES-DE browses at 58-60 fps.",
"must_contain": [
"before (PS5_OpenGL 54f01c7): 436 frames to 40 s, 37 presses: between swaps mean 36.64 ms",
"after (PS5_OpenGL e6990b5): 1171 frames to 45 s, 49 presses: between swaps mean 17.08 ms, p95 17.62 ms, 8 over 25 ms",
"75.0% self in util_clflushopt_range",
"util_clflushopt_range 1.8% self"
],
"must_not_contain": [],
"human_check": "The owner reported the frame rate as horribly low before the change."
}
+20
View File
@@ -0,0 +1,20 @@
{
"step": "esde-sound",
"recorded": "2026-10-05T03:20:21Z",
"console_run": "base PS5 13.40, run 231157",
"revision": "bf939c4 + PS5_OpenGL ec06d8c",
"command": "tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4,12 --frontend-scroll --watch 35",
"raw_capture": "klog/esde-sound/summary.txt",
"raw_lines": 8,
"raw_bytes": 708,
"lines": [
"==> tools/run-title.sh --no-build --relaunch-test=1 --relaunch-image=es-de/es-de.bin --frontend-capture=4,12 --frontend-scroll --watch 35 (base PS5 13.40, build 716a3c05, run 231157)",
"SDL2: PS5_OpenGL ec06d8c integration/SDL2 (SDL_AUDIO on: the fork's PS5 driver), build/sdl2-retroarch-20261004-224929; SDL_config.h: SDL_AUDIO_DRIVER_PS5 1",
"es_log: Setting up AudioManager...",
"es_log: Audio driver: ps5",
"es_log: Detected playback device: System audio output device",
"es_log: no 'Failed to create sample conversion stream' and no 'Unable to open audio device'",
"process 277, frame 174 at 4 s: 1920x1080, GL error 0, sound ps5 (0 playing)",
"process 277, frame 610 at 12 s: 1920x1080, GL error 0, sound ps5 (1 playing)"
]
}
+11
View File
@@ -0,0 +1,11 @@
{
"step": "esde-sound",
"note": "EmulationStation has sound: SDL2 is built with the PS5 fork's audio driver (sceAudioOut, main port, 48 kHz), ES-DE opens it and its device plays while it browses.",
"must_contain": [
"es_log: Audio driver: ps5",
"es_log: Detected playback device: System audio output device",
"sound ps5 (1 playing)"
],
"must_not_contain": [],
"human_check": "Hearing it: ES-DE's navigation sounds and video previews on the TV; not part of a scripted run."
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"step": "fbneo-native",
"recorded": "2026-09-19",
"revision": "46e6358 + native FBNeo port",
"revision": "9155dd5 + native FBNeo port",
"command": "bash tools/verify.sh; bash tools/run-title.sh --no-build --core-test=fbneo --watch 240 (owner manually loads arcade archive and tests menu/next-game transitions)",
"raw_capture": "klog/fbneo-catalogue-run.log",
"raw_lines": 10848,
+1 -1
View File
@@ -1,7 +1,7 @@
{
"step": "fceumm-build",
"recorded": "2026-09-19",
"revision": "4ac3bce + FCEUmm build step",
"revision": "dae9006 + FCEUmm build step",
"command": "make fceumm; tools/verify.sh; tools/run-title.sh --no-build --watch 120 (owner loads core/game manually)",
"raw_capture": "klog/fceumm-metadata-run.log",
"raw_lines": 7865,
+73
View File
@@ -0,0 +1,73 @@
{
"step": "flexible-memory-1g",
"recorded": "2026-10-05T16:15:59Z",
"console_run": "PS5 Pro, firmware 10.01, 2026-10-05: four runs of the installed release, PPSA99169",
"revision": "release build 716a3c05 on the console; sce_sys/param.json of 5eb7e0f plus the kernel block",
"command": "python3 klog/flexible-memory-1g-20261005/ab.py gc|ps2 base|1g (a scratch driver: idle check, test files, param.json swap and restore, launch, klog, trace)",
"raw_capture": "klog/flexible-memory-1g-20261005/summary.txt",
"raw_lines": 61,
"raw_bytes": 5288,
"lines": [
"==> klog/flexible-memory-1g-20261005/ab.py <game> base|1g: the installed release (build 716a3c05), launched by the control payload with args.txt -L <core> <content>, ps5vk-profile.txt and a pad script (MARK every 10 s from 20 s, STOP at 92 s); 1g puts kernel.flexibleMemorySize = 1073741824 into /app0/sce_sys/param.json for the run, then restores the original and reads it back",
"gc-base: a GameCube game (dolphin_libretro), param.json as committed (no kernel key)",
"gc-base: klog FMEM size 0x1c000000 DMEM size 0x300000000; ended by itself at 101 s; param.json restored True",
"gc-base: memory: event=startup result=0 request=0 known=1 pool=12884901888 free=12845056000 largest=12845056000 ranges=1 flexible=419430400 cpu_heap=33554432 gpu=0 gpu_known=1",
"gc-base: memory: flexible_free=359424 KiB",
"gc-base: memory: platform exec=2/235MiB shm=1/64MiB views=23 ranges=1/14336MiB committed=0MiB",
"gc-base: input: pad script MARK at 20.003 s: frames=768 worst_ms=1404.9 over_40ms=14",
"gc-base: input: pad script MARK at 30.001 s: frames=1067 worst_ms=53.0 over_40ms=16",
"gc-base: input: pad script MARK at 40.009 s: frames=1367 worst_ms=35.1 over_40ms=16",
"gc-base: input: pad script MARK at 50.019 s: frames=1667 worst_ms=36.3 over_40ms=16",
"gc-base: input: pad script MARK at 60.010 s: frames=1967 worst_ms=50.0 over_40ms=17",
"gc-base: input: pad script MARK at 70.003 s: frames=2266 worst_ms=50.0 over_40ms=18",
"gc-base: input: pad script MARK at 80.013 s: frames=2566 worst_ms=43.7 over_40ms=19",
"gc-base: input: pad script STOP at 92.01 s",
"gc-base: rarch_main returned = 0",
"gc-base: native quit: frontend status = 0",
"gc-1g: a GameCube game (dolphin_libretro), param.json with kernel.flexibleMemorySize 1073741824",
"gc-1g: klog FMEM size 0x40000000 DMEM size 0x2dc000000; ended by itself at 101 s; param.json restored True",
"gc-1g: memory: event=startup result=0 request=0 known=1 pool=12280922112 free=12241076224 largest=12241076224 ranges=1 flexible=1023410176 cpu_heap=33554432 gpu=0 gpu_known=1",
"gc-1g: memory: flexible_free=949248 KiB",
"gc-1g: memory: platform exec=2/235MiB shm=1/64MiB views=23 ranges=1/14336MiB committed=0MiB",
"gc-1g: input: pad script MARK at 20.003 s: frames=805 worst_ms=1349.4 over_40ms=6",
"gc-1g: input: pad script MARK at 30.011 s: frames=1105 worst_ms=49.9 over_40ms=7",
"gc-1g: input: pad script MARK at 40.019 s: frames=1405 worst_ms=35.2 over_40ms=7",
"gc-1g: input: pad script MARK at 50.010 s: frames=1705 worst_ms=36.3 over_40ms=7",
"gc-1g: input: pad script MARK at 60.004 s: frames=2004 worst_ms=49.8 over_40ms=8",
"gc-1g: input: pad script MARK at 70.000 s: frames=2303 worst_ms=50.1 over_40ms=9",
"gc-1g: input: pad script MARK at 80.007 s: frames=2603 worst_ms=43.6 over_40ms=10",
"gc-1g: input: pad script STOP at 92.00 s",
"gc-1g: rarch_main returned = 0",
"gc-1g: native quit: frontend status = 0",
"ps2-base: a PS2 game (pcsx2_libretro), param.json as committed (no kernel key)",
"ps2-base: klog FMEM size 0x1c000000 DMEM size 0x300000000; ended by itself at 101 s; param.json restored True",
"ps2-base: memory: event=startup result=0 request=0 known=1 pool=12884901888 free=12845056000 largest=12845056000 ranges=1 flexible=419430400 cpu_heap=33554432 gpu=0 gpu_known=1",
"ps2-base: memory: flexible_free=402432 KiB",
"ps2-base: memory: platform exec=2/365MiB shm=2/324MiB views=12008 ranges=2/4112MiB committed=0MiB",
"ps2-base: input: pad script MARK at 20.006 s: frames=1009 worst_ms=21.0 over_40ms=0",
"ps2-base: input: pad script MARK at 30.016 s: frames=1609 worst_ms=19.7 over_40ms=0",
"ps2-base: input: pad script MARK at 40.009 s: frames=2208 worst_ms=20.0 over_40ms=0",
"ps2-base: input: pad script MARK at 50.002 s: frames=2807 worst_ms=19.7 over_40ms=0",
"ps2-base: input: pad script MARK at 60.012 s: frames=3407 worst_ms=19.9 over_40ms=0",
"ps2-base: input: pad script MARK at 70.006 s: frames=4006 worst_ms=20.4 over_40ms=0",
"ps2-base: input: pad script MARK at 80.016 s: frames=4606 worst_ms=20.6 over_40ms=0",
"ps2-base: input: pad script STOP at 92.01 s",
"ps2-base: rarch_main returned = 0",
"ps2-base: native quit: frontend status = 0",
"ps2-1g: a PS2 game (pcsx2_libretro), param.json with kernel.flexibleMemorySize 1073741824",
"ps2-1g: klog FMEM size 0x40000000 DMEM size 0x2dc000000; ended by itself at 98 s; param.json restored True",
"ps2-1g: memory: event=startup result=0 request=0 known=1 pool=12280922112 free=12241076224 largest=12241076224 ranges=1 flexible=1023410176 cpu_heap=33554432 gpu=0 gpu_known=1",
"ps2-1g: memory: flexible_free=992256 KiB",
"ps2-1g: memory: platform exec=2/365MiB shm=2/324MiB views=12008 ranges=2/4112MiB committed=0MiB",
"ps2-1g: input: pad script MARK at 20.015 s: frames=1010 worst_ms=21.0 over_40ms=0",
"ps2-1g: input: pad script MARK at 30.008 s: frames=1609 worst_ms=20.8 over_40ms=0",
"ps2-1g: input: pad script MARK at 40.002 s: frames=2208 worst_ms=20.1 over_40ms=0",
"ps2-1g: input: pad script MARK at 50.012 s: frames=2808 worst_ms=19.8 over_40ms=0",
"ps2-1g: input: pad script MARK at 60.005 s: frames=3407 worst_ms=20.1 over_40ms=0",
"ps2-1g: input: pad script MARK at 70.015 s: frames=4007 worst_ms=20.1 over_40ms=0",
"ps2-1g: input: pad script MARK at 80.008 s: frames=4606 worst_ms=19.4 over_40ms=0",
"ps2-1g: input: pad script STOP at 92.00 s",
"ps2-1g: rarch_main returned = 0",
"ps2-1g: native quit: frontend status = 0"
]
}
@@ -0,0 +1,22 @@
{
"step": "flexible-memory-1g",
"note": "kernel.flexibleMemorySize = 1 GiB in param.json: the kernel gives the title 1 GiB of flexible memory (FMEM 0x40000000) and 576 MiB less direct memory (DMEM 0x2dc000000); Dolphin and the PS2 core keep their frame counts and end cleanly, with 2.6x and 2.5x the flexible memory free.",
"must_contain": [
"gc-1g: klog FMEM size 0x40000000 DMEM size 0x2dc000000",
"ps2-1g: klog FMEM size 0x40000000 DMEM size 0x2dc000000",
"gc-base: klog FMEM size 0x1c000000 DMEM size 0x300000000",
"gc-1g: memory: flexible_free=949248 KiB",
"gc-base: memory: flexible_free=359424 KiB",
"ps2-1g: memory: flexible_free=992256 KiB",
"ps2-base: memory: flexible_free=402432 KiB",
"gc-1g: input: pad script MARK at 80.007 s: frames=2603",
"ps2-1g: input: pad script MARK at 80.008 s: frames=4606 worst_ms=19.4 over_40ms=0",
"ps2-1g: rarch_main returned = 0",
"gc-1g: rarch_main returned = 0",
"param.json restored True"
],
"must_not_contain": [
"param.json restored False"
],
"human_check": "The 1g runs report FMEM 0x40000000 and about 2.6x the flexible memory free of the base runs, with the same MARK frame counts and a clean rarch_main return."
}
@@ -0,0 +1,26 @@
{
"step": "flycast-dreamcast-boot",
"recorded": "2026-10-07T01:15:03Z",
"console_run": "base PS5 13.40, runs 210509, 210719 and 211230 (pad script tests/pad/flycast-dreamcast-boot.txt)",
"revision": "PS5_Flycast b95c532f; working tree on 3d5359c (this commit)",
"command": "PS5_FRONTENDS='es-de picker' PS5_REUSE_STAGED_CORES=mame tools/build-title.sh; tools/run-title.sh --no-build --no-deploy --game=flycast:/app0/content/Dreamcast/<Dreamcast Test Game .cue> --pad-script=tests/pad/flycast-dreamcast-boot.txt --watch 100",
"raw_capture": "klog/flycast-dreamcast/run-211230.txt",
"raw_lines": 14,
"raw_bytes": 1023,
"lines": [
"[INFO] [Core] Loading dynamic libretro core from: \"/app0/cores/flycast_libretro.so\".",
"[libretro INFO] [BOOT] retro_load_game: /app0/content/Dreamcast/Dreamcast Test Game (USA) (Rev A)/Dreamcast Test Game (USA) (Rev A).cue",
"[libretro INFO] [BOOT] Did not load BIOS, using reios",
"[libretro INFO] [RENDERER] GPU Supports Vulkan API: 1.4.354",
"[libretro INFO] [RENDERER] OITVulkanRenderer::Init",
" input: pad script MARK at 10.014 s: frames=325 worst_ms=1318.7 over_40ms=10",
" input: pad script MARK at 20.019 s: frames=626 worst_ms=66.5 over_40ms=11",
" input: pad script MARK at 30.030 s: frames=927 worst_ms=38.2 over_40ms=11",
" input: pad script MARK at 50.009 s: frames=1942 worst_ms=458.7 over_40ms=19",
" rarch_main returned = 0",
" pad script: 4 picture(s) saved to klog/pad-211230",
"==> [run] VERDICT: it exited on its own before 100s",
"pictures klog/pad-211230: 1 intro film, 2 black (film skipped), 3 title screen, 4 Select a memory card after START",
"pictures klog/pad-210719: title screen and attract demo with the hero at 2880x2160"
]
}
@@ -0,0 +1,13 @@
{
"step": "flycast-dreamcast-boot",
"note": "Flycast from PS5_Flycast loads in the title, renders through the frontend's Vulkan device with per-pixel transparency at 2880x2160, boots Dreamcast Test Game through its HLE BIOS (no dc_boot.bin on the console), plays its intro, title screen and attract demo with sound, and takes the pad: START skipped the film, left the title screen and reached Select a memory card. Frames: 30 a second in the intro film (the film's rate), about 60 in the attract demo (run 210719, 598 frames over 50-60 s); stalls of 0.5-1.4 s early on. RetroArch exited normally.",
"must_contain": [
"Loading dynamic libretro core from: \"/app0/cores/flycast_libretro.so\"",
"Did not load BIOS, using reios",
"OITVulkanRenderer::Init",
"rarch_main returned = 0",
"Select a memory card after START"
],
"must_not_contain": [],
"human_check": "Agent inspected klog/pad-210719 (title screen; attract demo with the hero in 3D) and klog/pad-211230 (intro film; title screen; Select a memory card after START). The owner's own play is not yet recorded."
}
+28
View File
@@ -0,0 +1,28 @@
{
"step": "forwarder-launch",
"recorded": "2026-10-17T22:10:00Z",
"console_run": "",
"revision": "20dd7fe (this series), in build 838fd9a3",
"command": "tools/run-title.sh --no-build --no-deploy --relaunch-test=1 --relaunch-arg=--rom --relaunch-arg=\"nes/<a backup>\" --relaunch-arg=--core --relaunch-arg=fceumm --relaunch-arg=--exit-after-game --retroarch-arg=--appendconfig=/app0/tests/no-save.cfg --retroarch-frames=600 --watch 45; then tools/run-title.sh --no-build --no-deploy --home-launch --watch 30",
"raw_capture": "klog/forwarder-launch/record.txt",
"raw_lines": 16,
"raw_bytes": 1680,
"lines": [
"Console: the owner's base PS5 on a 1440p display with 120 Hz and VRR, build identity 838fd9a3ca99e21ce4adba405a3f3867a0c4ce411ee89981e0a8111dcef02797 (this series at 20dd7fe with PS5_FRONTENDS=\"es-de picker\", PS5_WITH_SWANSTATION=1, PS5_WITH_MELONDSDS=1), 2026-10-17.",
"The control payload launches with no arguments, so the relaunch test restarts the title with a forwarder's: --rom nes/<a backup> --core fceumm --exit-after-game; RetroArch quits after 600 frames:",
" armed relaunch test: 1 restarts, run relaunch-20261017-220459",
" each restart carries: ['--rom', 'nes/<a backup in content/nes>', '--core', 'fceumm', '--exit-after-game']",
" relaunch test: generation 0 of 1 (arguments name -1), restart",
" relaunch test: generation 1 of 1 (arguments name 1), continue",
" forwarder: /app0/content/nes/<a backup in content/nes> with /app0/cores/fceumm_libretro.so, closing after it",
" forwarder: RetroArch quit (status 0) after 11 s; the title closes",
" relaunch test PASS: 1 restarts, every one with its arguments",
" retroarch-game.log: [INFO] [PS5] build identity: 838fd9a3ca99e21ce4adba405a3f3867a0c4ce411ee89981e0a8111dcef02797",
" retroarch-game.log: [INFO] [Core] Loading dynamic libretro core from: \"/app0/cores/fceumm_libretro.so\".",
" retroarch-game.log: [INFO] [Config] Appending config: \"/app0/config/game-mode.cfg\".",
" retroarch-game.log: [INFO] [Config] Appending config: \"/app0/tests/no-save.cfg\".",
" retroarch-game.log: [INFO] [Content] Loading content file: \"/app0/content/nes/<a backup in content/nes>#<its .nes>\".",
" After a second such run, a launch from the home screen at once:",
" frontend: mode '', test run 0, picker test 0, picker 1, es-de 1, remembered ask, L1 0 -> picker"
]
}
@@ -0,0 +1,13 @@
{
"step": "forwarder-launch",
"note": "#34 on the console: a launch whose arguments are a forwarder's runs that game in RetroArch through game mode with the core named, and with --exit-after-game the title closes when RetroArch quits; the next launch from the home screen opens the picker as usual.",
"must_contain": [
"forwarder: /app0/content/nes/<a backup in content/nes> with /app0/cores/fceumm_libretro.so, closing after it",
"forwarder: RetroArch quit (status 0) after 11 s; the title closes",
"relaunch test PASS: 1 restarts, every one with its arguments",
"'--rom', 'nes/<a backup in content/nes>'",
"-> picker"
],
"must_not_contain": [],
"human_check": ""
}
@@ -0,0 +1,22 @@
{
"step": "four-cores-2026-10-06",
"recorded": "2026-10-07T03:08:38Z",
"console_run": "base PS5 13.40, runs 224859-230600 (pad script tests/pad/boot-and-start.txt)",
"revision": "working tree on 7414eba (this commit)",
"command": "PS5_FRONTENDS='es-de picker' tools/build-title.sh; tools/run-title.sh --no-build --game=<core>:<content> --pad-script=tests/pad/boot-and-start.txt --watch 120",
"raw_capture": "klog/four-cores/runs-20261006.txt",
"raw_lines": 10,
"raw_bytes": 775,
"lines": [
"224859 core=cores/opera_libretro.so format= content=1fail",
"225106 core=cores/puae_libretro.so format=XRGB8888 content=0fail",
"225313 core=cores/neocd_libretro.so format=RGB565 content=1fail",
"225520 core=cores/scummvm_libretro.so format=RGB565 content=0fail",
"230131 core=cores/neocd_libretro.so format=RGB565 content=0fail",
"230600 core=cores/opera_libretro.so format=RGB565 content=0fail",
"four-run-puae: MARK at 25.010 s: frames=1265 worst_ms=23.1 over_40ms=0 | rarch_main returned = 0",
"four-run-scummvm: MARK at 25.014 s: frames=1497 worst_ms=33.5 over_40ms=0 | rarch_main returned = 0",
"disc-run-neocd: MARK at 25.009 s: frames=1430 worst_ms=293.7 over_40ms=3 | rarch_main returned = 0",
"disc-run-opera: MARK at 25.004 s: frames=1500 worst_ms=27.0 over_40ms=0 | rarch_main returned = 0"
]
}
@@ -0,0 +1,13 @@
{
"step": "four-cores-2026-10-06",
"note": "Opera (3DO, 3DO Test Game), PUAE (Amiga, Amiga Test Game on Kickstart 1.3), NeoCD (Run and Gun, CDZ BIOS) and ScummVM (ScummVM Test Game) load, run and exit with 0. Disc images in archives fail (224859, 225313): RetroArch extracts only the .cue, not its tracks; unpacked into folders they load. 3DO Test Game's own .cue names an .iso that is not in the archive, so it was started from the .iso. Pictures: 3DO Test Game title and menu, Amiga Test Game's boot disk, Run and Gun's menu and Mission 1, ScummVM Test Game's intro and ScummVM's menu.",
"must_contain": [
"230600 core=cores/opera_libretro.so format=RGB565 content=0fail",
"225106 core=cores/puae_libretro.so format=XRGB8888 content=0fail",
"230131 core=cores/neocd_libretro.so format=RGB565 content=0fail",
"225520 core=cores/scummvm_libretro.so format=RGB565 content=0fail",
"four-run-puae: MARK at 25.010 s: frames=1265"
],
"must_not_contain": [],
"human_check": "Agent inspected klog/pad-225106, pad-225520, pad-230131 and pad-230600."
}
@@ -0,0 +1,20 @@
{
"step": "framegen-flip-rate-60hz",
"recorded": "2026-10-17T22:20:32Z",
"console_run": "",
"revision": "5c4256b (patch 0131); the hotfix eboot.bin built from it, identity 1f81446a",
"command": "the owner's launch of the installed hotfix; FTP read of /data/homebrew/PPSA99169/trace.txt and retroarch.log",
"raw_capture": "klog/framegen-flip-rate-60hz/record.txt",
"raw_lines": 8,
"raw_bytes": 851,
"lines": [
"Console: the owner's base PS5 on an output without 120 Hz, the hotfix eboot.bin (build identity 1f81446ad708fb08f7dfbbb5a4239afd69b49e256b77b2b630ec5a95dd27dce3), RetroArch's menu, 2026-10-17.",
"build identity: 1f81446ad708fb08f7dfbbb5a4239afd69b49e256b77b2b630ec5a95dd27dce3",
"wsi/videoout: 119.88 Hz not offered on the console's present output (0); presenting at 59.94 Hz",
"[INFO] [PS5] Display runs at 59.940 Hz; video_refresh_rate follows it (was 119.880).",
"[INFO] [PS5] Flips timed 16.67 to 16.69 ms apart, median 16.68 ms.",
"[INFO] [PS5] The output shows 59.943 flips a second in its 59.940 Hz mode.",
"[INFO] [FrameGen] Target Framerate 60: display 59.940 Hz, game 119.880 fps -> 0.50 pictures per game frame, each shown 1x",
"[INFO] [FrameGen] pace: 61.3 presents/s, 0.0 generated/s, 0.0 new frames/s, core 61.3 calls/s, governor level 0 (0.00 ms a picture)"
]
}
Loaded 100 of 813 files, more files were not shown because too many files have changed in this diff. Show more