mirror of
https://github.com/mihawk-99/PS5_RetroArch.git
synced 2026-10-18 23:09:29 -05:00
A home screen tile is a title of its own (PPSA99170 to PPSA99269) that starts one
game in RetroArch. Titles cannot launch titles (sceSystemServiceLaunchApp returns
0x8094000F from one, sceLncUtilLaunchApp is a null import), so the tile hands a
payload to the console's ELF loader on 127.0.0.1:9021 and exits:
- forwarder/tile.c: the tile's eboot.bin. Reads its title id from
/app0/sce_sys/param.json, stamps it into /app0/launch.elf's slot, sends it to
the loader, logs /app0/tile.log, and leaves through LoadExec("exit") (a return
from main ends in a SIGSYS error notice).
- forwarder/launch.c: the payload. Reads /data/homebrew/<tile>/forward.txt (one
argument a line: --rom, --core, --exit-after-game, src/frontend_mode_ps5.cpp),
waits for the tile to leave, and starts PPSA99169 with those arguments through
sceLncUtilLaunchApp (sceSystemServiceLaunchApp as the fallback).
- tools/build-forwarder.sh builds both; tools/build-title.sh puts them in
dist/PPSA99169/forwarder/ and counts them in the title's identity; the notices
cover both files.
Evidence:
- host: python3 -m unittest tests.test_forwarder (the tile stamps and sends the
payload byte for byte, refuses a lowercase id, a missing or a doubled slot; the
payload passes the five arguments, falls back to SystemService, and an
unstamped payload starts nothing) - OK.
- console (base PS5, 13.40, ShadowMountPlus 1.7-beta5fix1), the test tile
PPSA99170 registered through ShadowMountPlus's manual list:
tile.log "tile PPSA99170: launch payload 110832 bytes, 110832 sent to
127.0.0.1:9021 (errno 0)"; launch.log five arguments, sceLncUtilLaunchApp
00002018; RetroArch's trace "forwarder: /app0/content/nes/<game> with
/app0/cores/fceumm_libretro.so, closing after it"; the tile exited cleanly
(no error notice), back to the home screen after the game.
- gates: format, unit (206 tests), build (tile 11,123 B, payload 110,832 B),
integration (206 tests; a first run timed out starting two scraper test
servers under load, both passed alone and on the rerun), evidence - PASS.
55 lines
3.4 KiB
Bash
Executable File
55 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# PS5 RetroArch - build the Game Forwarder's two programs (forwarder/): the app every home
|
|
# screen tile runs (eboot.bin, linked as tools/build.sh links the title's, with nothing of
|
|
# RetroArch in it) and the launch payload a tile sends to the console's ELF loader
|
|
# (launch.elf, built with the payload SDK as the WebUI's daemon is). The WebUI copies both
|
|
# into each tile it makes. Run after the title's build, whose native tool this uses.
|
|
# Prints the folder holding them.
|
|
set -euo pipefail
|
|
root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
|
sdk="$root/.deps/native/ps5-payload-sdk"
|
|
native="$root/tooling/native"
|
|
tool="$root/build/host/ps5-native-tool"
|
|
out="$root/build/forwarder"
|
|
[[ -x $sdk/bin/prospero-clang ]] || { echo "error: no SDK at $sdk" >&2; exit 2; }
|
|
[[ -x $tool ]] || { echo "error: no $tool; build the title first (tools/build.sh)" >&2; exit 2; }
|
|
target=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["titleId"])' "$root/sce_sys/param.json")
|
|
[[ $target =~ ^[A-Z]{4}[0-9]{5}$ ]] || { echo "error: no title id in sce_sys/param.json" >&2; exit 2; }
|
|
rm -rf -- "$out"
|
|
mkdir -p "$out/obj"
|
|
|
|
# The launch payload: what it starts is this title.
|
|
PS5_PAYLOAD_SDK="$sdk" PS5_CLANG=${PS5_CLANG:-clang} "$sdk/bin/prospero-clang" -std=c11 -O2 -Wall -Wextra -Werror \
|
|
-DFORWARDER_TARGET="\"$target\"" -o "$out/launch.elf" "$root/forwarder/launch.c" \
|
|
-lSceSystemService -lSceUserService
|
|
grep -q "PS5RA-FORWARDER-TILE-ID-SLOT" "$out/launch.elf" ||
|
|
{ echo "error: launch.elf has no slot for a tile's title id" >&2; exit 1; }
|
|
|
|
# The tile's app.
|
|
cc() { PS5_PAYLOAD_SDK="$sdk" sh "$root/tooling/prospero-clang18" "$@"; }
|
|
cc -std=c11 -O2 -Wall -Wextra -Werror -ffunction-sections -fdata-sections \
|
|
-c "$root/forwarder/tile.c" -o "$out/obj/tile.o"
|
|
for part in app_crt app_cpp_runtime; do
|
|
cc -std=c++20 -O2 -Wall -Wextra -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections \
|
|
-c "$native/$part.cpp" -o "$out/obj/$part.o"
|
|
done
|
|
"$sdk/bin/prospero-lld" -T "$native/ps5-pie.ld" -L "$native" --eh-frame-hdr --error-limit=0 \
|
|
--version-script "$native/app-symbols.map" --exclude-libs=ALL -e _start -o "$out/tile.elf" \
|
|
"$out/obj/app_crt.o" "$out/obj/app_cpp_runtime.o" "$out/obj/tile.o" --as-needed "$sdk"/target/lib/*.so
|
|
# As tools/build.sh: an import only libkernel_sys or libScePosixForWebKit defines is null at
|
|
# run time (the spike's tile called sceLncUtilLaunchApp, null for a title, and faulted at 0).
|
|
mapfile -t null_imports < <(comm -23 \
|
|
<("$sdk/bin/llvm-nm" -D --undefined-only "$out/tile.elf" | awk '$1 == "U" { sub(/@.*/, "", $2); print $2 }' |
|
|
sort -u) \
|
|
<(for stub in "$sdk"/target/lib/*.so; do
|
|
case $(basename "$stub") in libkernel_sys.so | libScePosixForWebKit.so) continue ;; esac
|
|
"$sdk/bin/llvm-nm" -D --defined-only "$stub" 2>/dev/null | awk '{ print $NF }'
|
|
done | sort -u))
|
|
((${#null_imports[@]} == 0)) || { echo "error: the tile imports what no module exports: ${null_imports[*]}" >&2; exit 1; }
|
|
"$tool" link --in "$out/tile.elf" --out "$out/tile.module.elf" --stub-dir "$sdk/target/lib" \
|
|
--module-sdk 0x02000009 --companion-sdk 0x08050001 --file-name eboot.elf >&2
|
|
"$tool" self --sign --in "$out/tile.module.elf" --out "$out/eboot.bin" --magic 0x1D3D154F >&2
|
|
printf '==> [forwarder] tile app %s bytes, launch payload %s bytes (starts %s)\n' \
|
|
"$(stat -c %s "$out/eboot.bin")" "$(stat -c %s "$out/launch.elf")" "$target" >&2
|
|
printf '%s\n' "$out"
|