The resolver asked one server over UDP and took the first answer, even
a blocker's 0.0.0.0. It now tries UDP on four public resolvers, then
TCP, then DNS over HTTPS (Cloudflare, Google), then the addresses the
last good lookup saved. Sinkhole answers count as blocked, curl gets
every address of a host, and a host whose addresses refuse or drop the
connection is looked up again. Sony hosts stay pinned to Sony's root;
only the DoH requests use public roots.
The network check reports where each answer came from and what failed
before it.
The UI follows the browser language, which on the PS5 is the system
language, and Settings has a selector to override it per device (kept in
localStorage). All UI text moved into web/i18n.js (306 keys) with English,
German, French, Spanish, Italian, Portuguese (Brazil), Japanese, Simplified
Chinese, Korean and Russian; missing keys fall back to English.
Log entries keep their key and values, so the Logs view shows them in the
chosen language while the diagnostic report stays English for issues.
Sony's version.xml only lists the newest patch. When that one needs a
newer firmware, PatchDL now asks Prospero Patches for the version history
and offers the newest patch that still runs on the console. Prospero only
hands out download links after a human check, so a guide with a QR code
moves the user to a phone, where they copy the SC link. PatchDL derives
Sony's manifest from that link and downloads like an official update.
The game list now groups by what the user can do: Updates, Up to date,
Not supported. Update all read a nonexistent state.games and did nothing;
it now uses state.titles.
Write param.json + icon0.png into /user/app/<TITLE_ID>/sce_sys/, then
call sceAppInstUtilAppInstallTitleDir to register the directory as an
app. No package, no code signing — the installer reads the metadata
files directly.
The tile uses TITLE_ID PTDL00001 and deeplinkUri
http://127.0.0.1:12880/, so tapping it opens PatchDL's own UI in the
on-console browser. Only useful while the ELF is running.
Asset pipeline: param.json + icon0.png live under assets/ and get
.incbin'd straight into .rodata. Makefile lists them as TILE_ASSETS so
a touch on either forces a relink.
Stat-guard: file_matches() diffs each asset against the on-disk copy
first; when nothing changed the install API isn't called at all. Keeps
repeated payload starts from re-registering the app. Repeated
/api/install_tile calls return "already installed and up to date".
Backend lazy-load: AppInstUtil isn't mapped until something pokes it,
so the helper polls patchdl_install_backend_check() for up to ~15 s
before resolving sceAppInstUtilAppInstallTitleDir.
Wiring: /api/install_tile POST triggers the install on demand;
patchdl_websrv_start() runs it at startup when home_shortcut is on;
and flipping the toggle from off to on in /api/config also fires it.
All three paths share the stat-guarded helper so they're safe to
repeat.
Standalone PS5 payload with an embedded web UI on :12880, no etaHEN.
- Scans installed titles and classifies the source (genuine install,
ShadowMountPlus mount, preinstall, unknown) from the on-disk layout.
- Reads name, installed version and the Sony version.xml URL from the PS5
app database (vendored SQLite).
- Resolves version.xml past nanoDNS via a raw DNS query, TLS pinned to the
SCEI DNAS root.
- Filters patches to the newest one compatible with the current firmware.
- Downloads the patch and installs it through AppInstUtil (sysmodule loaded
at runtime), gated to genuine installs with a package title-id match guard.
- Action-based UI filters and an on-screen startup notification with the URL.