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.
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).
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.
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.
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.
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.
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.
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.
/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.
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.
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.
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.
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.
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).
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.
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.
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).
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).
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.
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.
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
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).
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).
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
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).
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).
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).
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).
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).
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).
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).
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).
../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).
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).
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).
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).
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.
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.
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)
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)
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)
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)
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)
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
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/.
Lead with a real console Overview screenshot and port6769 URL legend.
Organize setup, guided global/core settings, systems, balanced graphics and
file locations before developer details. Preserve licensing and attribution;
separate source-build features from release availability and remove game names.
Validation: desktop/mobile GitHub-style previews, no page overflow or missing
image; actual Overview inspected; Impeccable finish review and final delta ship.
Screenshot provenance embedded. Evidence: evidence/readme-overview, replay with
python3 tools/evidence.py compare evidence/. All project gates passed this round.
Create content/Saturn and system/Saturn at startup. Route the default core
system directory and menu BIOS preflight consistently, retaining custom paths.
Patch Saturn's x86 code pools to use the SDK executable allocator and release.
Keep cropped frames in core-owned storage to avoid resized lent texture reuse.
Include the patch in core stamps, provenance and shipped notices.
Two deliberate frontend patch blocks increase the expected count 232 to 234.
Verification: bash tools/verify.sh, all five gates PASS, 96 tests.
Evidence: evidence/saturn-runtime; replay with python3 tools/evidence.py compare evidence/.
Console: 100s controlled gameplay with clean captures; final frontend repeats
100s boot/demo and exits0; explicit BIOS preflight passes. Settings restored.
Expose 846 options from 15 pinned PS5 core catalogs before first use,
with categories, descriptions, exact choices, saved-value preservation,
and Advanced mode. Include installed cores without saved profiles and
preserve first-use PPSSPP/Dolphin edits through their profile migration.
Bind dynamic runtime options to the catalog and core binary revision.
Validation: all five tools/verify.sh gates pass (96 tests), all 15 core
browser checks pass, and the deployed native menu-only run exposes all
846 catalog keys with matching JSON, build identity and normal return.
Restore original settings and stop the temporary helper after testing.
Evidence: evidence/webui-settings-guide/{capture,expectation}.json
Reproduce: python3 tools/evidence.py compare evidence/
Raw native record: klog/webui-guide/all-cores-20261002-130144/report.json
Keep the purple dashboard at a stable desktop scale and expose searchable global configuration, saved core options, and core overrides. Apply pending core edits once on startup and preserve later native changes.
Validation: all five project gates pass (93 tests), browser regression suite passes at desktop and mobile sizes, and three native PS5 runs verify persistence, isolation, stale-save rejection, and normal shutdown. Evidence: evidence/webui-settings/. Reproduce project gates with bash tools/verify.sh.