Commit Graph
100 Commits
Author SHA1 Message Date
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