Skip to content

RPCS3 (PS3 emulation) in ES-DE

How the PlayStation 3 emulator RPCS3 is set up on av under the guest account, launched from ES-DE inside the Sunshine/Moonlight + gamescope gaming session. This sits entirely in the gaming stack (docs/gaming/), separate from the homelab; nothing here runs as root and nothing touches the homelab services.

The gaming session is under guest (UID 1001). Admin access is via ssh guest@localhost (skill-lava's key is in guest's authorized_keys). guest's shell is bash.

Where things live

Thing Path
RPCS3 AppImage ~guest/Applications/rpcs3-*.AppImage
Global config ~guest/.config/rpcs3/config.yml
Per-game configs ~guest/.config/rpcs3/custom_configs/config_<SERIAL>.yml
Controller config ~guest/.config/rpcs3/input_configs/global/Default.yml
Firmware (dev_flash) ~guest/.config/rpcs3/dev_flash/ (v4.93)
PPU/SPU cache + log ~guest/.cache/rpcs3/ (RPCS3.log, cache/<SERIAL>/)
Patches ~guest/.config/rpcs3/patches/
ES-DE data ~guest/ES-DE/ (custom_systems/, gamelists/, downloaded_media/)
ROMs /opt/games/roms-flat/<system>/ (~guest/ROMs → this; group gamers)
PS3 ROMs /opt/games/roms-flat/ps3/

Current build: RPCS3 v0.0.41 (rpcs3-v0.0.41-19564-700ca262), firmware 4.93.

Install

RPCS3 is a standalone emulator — not a RetroArch core (the libretro RPCS3 core is defunct). ES-DE launches the standalone AppImage.

  1. AppImage — download the latest Linux build and drop it in ~/Applications (ES-DE's find-rule matches ~/Applications/rpcs3*.AppImage). No root needed; the AppImage self-mounts (FUSE works on this host).
    cd ~/Applications
    curl -fL -O "$(curl -fsSL https://api.github.com/repos/RPCS3/rpcs3-binaries-linux/releases/latest \
        | grep -o 'https://[^"]*linux64.AppImage')"
    chmod +x rpcs3-*.AppImage
    
  2. PS3 firmware — official Sony PUP, installed headlessly (no GUI):
    # firmware from https://www.playstation.com/en-us/support/hardware/ps3/system-software/
    #   (dus01.ps3.update.playstation.net/.../PS3UPDAT.PUP)
    cd ~/Applications
    ./rpcs3-*.AppImage --headless --installfw ~/Downloads/PS3UPDAT.PUP
    # verify:
    head -1 ~/.config/rpcs3/dev_flash/vsh/etc/version.txt   # -> release:04.9300:
    

    --installfw alone opens a GUI confirmation that blocks headless; --headless bypasses it. The QCommandLineParser: option not expecting values warning is benign.

The AMD Radeon 8060S (RADV, Strix Halo) is the Vulkan device — RPCS3 defaults to Vulkan, which is correct here.

ES-DE integration

A custom ps3 system overrides the bundled one so any .iso (or .ps3 directory) in ROMs/ps3 launches straight into standalone RPCS3 — the same "drop a file in the folder and it just works" behaviour as Dolphin/gc.

~guest/ES-DE/custom_systems/es_systems.xml (the ps3 block):

<system>
  <name>ps3</name>
  <fullname>Sony PlayStation 3</fullname>
  <path>%ROMPATH%/ps3</path>
  <extension>.desktop .iso .ISO .ps3 .PS3 .ps3dir .PS3DIR</extension>
  <command label="RPCS3 ISO (Standalone)">%EMULATOR_RPCS3% --no-gui %ROM%</command>
  <command label="RPCS3 Directory (Standalone)">%EMULATOR_RPCS3% --no-gui %ROM%</command>
  <command label="RPCS3 Shortcut (Standalone)">%ENABLESHORTCUTS% %EMULATOR_OS-SHELL% %ROM%</command>
  <command label="RPCS3 Game Serial (Standalone)">%EMULATOR_RPCS3% --no-gui %RPCS3_GAMEID%:%INJECT%=%BASENAME%.ps3</command>
  <platform>ps3</platform>
  <theme>ps3</theme>
</system>

The default (first) command is the ISO launcher; the Directory command is identical, so folder ("JB folder") games renamed with a .ps3 extension launch too.

Adding a PS3 game

1. Decrypted ISO (the common case — Vimm's .dec.iso)

7z e -y "/home/guest/Downloads/<game>.7z" "*.iso" -o/opt/games/roms-flat/ps3/
# (drop the ".dec" from the name if present)

2. Encrypted ISO (needs the disc key)

If a plain .iso fails with Invalid or unsupported file format: EBOOT.BIN, it's disc-encrypted (EBOOT magic isn't SCE\0/7fELF). RPCS3 will decrypt it on the fly if you drop the 32-hex disc key next to the ISO as a .dkey with the same basename:

cp "<game>.key" "/opt/games/roms-flat/ps3/<game>.dkey"   # 32 hex chars, e.g. redump .dkey
# RPCS3 log then shows: "ISO: init: Found matching key file: ...dkey"

3. Boot-test to get the serial (headless, no display needed)

cd ~/Applications
: > ~/.cache/rpcs3/RPCS3.log
xvfb-run -a ./rpcs3-*.AppImage --headless "/opt/games/roms-flat/ps3/<game>.iso" &
sleep 8; grep -aE 'SYS: (Title|Serial|Version)' ~/.cache/rpcs3/RPCS3.log
# a serial dir appearing under ~/.cache/rpcs3/cache/<SERIAL>/ == it really booted
pkill -f 'rpcs3-.*AppImage'; pkill Xvfb

4. Scrape metadata + art (Skyscraper → ScreenScraper → ES-DE)

Input must be passed with -i (gather mode's default input is the RetroPie path, which doesn't exist here):

Skyscraper -p ps3 -s screenscraper -i ~/ROMs/ps3 --flags unattend
Skyscraper -p ps3 -f esde        -i ~/ROMs/ps3 --flags unattend,nobrackets
Restart ES-DE afterwards so it picks up the new gamelist/media.

Per-game configs & tuning

Per-game configs live at ~/.config/rpcs3/custom_configs/config_<SERIAL>.yml (a full copy of config.yml with overrides). RPCS3 loads them automatically for that serial — not config/custom_configs/ (a common wrong guess).

Global defaults applied to all games: Resolution Scale 150%, Anisotropic 16×, Keep pads connected: true (survives transient controller drops).

Working titles

Game Serial Status Key settings
Resistance: Fall of Man BCUS98107 Flawless stock (global defaults only)
Metal Gear Solid 4 BLUS30109 Playable see below
L.A. Noire not via RPCS3 use Steam (see below)

MGS4 (BLUS30109) — the settings that made it playable

Wiki-recommended plus what we found by testing: - SPU XFloat Accuracy: Accuratethe fix for the early-gameplay freeze (same symptom RPCS3 issue #16920; the wiki's crash-fix patch does not help and doesn't match our decrypted dump's EBOOT hash anyway). - Frame limit: PS3 Native, Write Color Buffers: true, Accurate RSX reservation access: true, ZCULL Relaxed (Relaxed ZCULL Sync: true + Accurate ZCULL stats: false). - Libraries Control: ["libvdec.sprx:lle"] — required for FMV cutscenes. - Memory-safety (see swap section): LLVM Precompilation: false, Max LLVM Compile Threads: 4.

Controller

The gaming session is driven over Moonlight, so the physical controller arrives as Sunshine's virtual Xbox pad. RPCS3 binds Player 1 to it by exact name in input_configs/global/Default.yml:

Player 1 Input:
  Handler: SDL
  Device: "Xbox One S Controller 1"   # = SDL's GameControllerDB name for the Sunshine pad, + " 1"

RPCS3 auto-fills the standard Xbox button map when the Config: block is omitted.

  • This one config covers every Moonlight session regardless of the physical controller — Sunshine always presents the same virtual pad.
  • Controllers plugged directly into av report their own names, so each distinct model needs a one-time selection (RPCS3 binds by exact device name). To find a device's exact name: launch with a placeholder SDL config and read SDL: Found game pad N: name='...' from the log — RPCS3's device string is "<that name> 1".

Memory / swap (important on this host)

Strix Halo carves most of the 128 GB unified memory as GPU VRAM, so the OS sees only ~31 GB system RAM, and the distro's only swap is zram (compressed, in that RAM — no real overflow). A heavy game (MGS4) exhausted RAM+zram and got OOM-killed.

Fix: a 64 GB disk-backed swapfile as overflow (priority below zram). Created by tools/setup-gaming-swap.sh (Btrfs-aware: dedicated /swap subvolume, NOCOW, persisted in fstab with nofail). Run once:

sudo bash ~/homelab/tools/setup-gaming-swap.sh
This stops the OOM kills (game spills to disk instead of dying). The per-game LLVM Precompilation: false + capped compile threads also cut peak RAM.

Steam games in ES-DE (and why L.A. Noire lives there)

Steam games appear in ES-DE's steam system as tiny launcher scripts in ROMs/steam/:

# ROMs/steam/<Game>.sh
#!/bin/bash
systemctl --user start --no-block gamescope-steam-game@<APPID>.service
Example added this way: L.A. Noire (Steam app 110800).

L.A. Noire is intentionally not on RPCS3. Both PS3 editions are rated "Ingame" (not "Playable") in RPCS3's compat DB — they deadlock in the MotionScan video decoder (libsmvd2 throws sys_mutex … CELL_ESRCH: the decoder's threads reference a mutex RPCS3 has already torn down → the game waits forever on an event that never fires). We climbed the whole ladder — RPCS3 Scheduler beat an earlier title-screen deadlock, SPU interpreter + libvdec/ libsmvd2/libavcdec LLE got it into the first case — but the decoder wall is an upstream emulator bug (open issues #9087, #14513), not a config we can fix. The native Steam version sidesteps it entirely.

Check any game's status before sinking time into it:

curl -fsSL "https://rpcs3.net/compatibility?g=<SERIAL>&api=v1"   # look for "status": "Playable" vs "Ingame"

Operations & troubleshooting

  • RPCS3 launches in-session only. --no-gui can't create its Vulkan window when started over SSH — it blocks at window creation. ES-DE launches it as a child of the desktop session, so it renders fine. (Boot logic can be verified headlessly with --headless + Xvfb; rendering can't.)
  • Kill a hung RPCS3 (the process re-execs as AppRun.wrapped, so match by comm):
    for p in $(ps -u guest -o pid=,comm= | awk '/rpcs3|AppRun/{print $1}'); do kill -9 "$p"; done
    
  • ES-DE is service-managed — killing the process just makes it relaunch. To actually stop/restart it (e.g. to re-scan the library):
    export XDG_RUNTIME_DIR=/run/user/1001 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus
    systemctl --user {stop,start,restart} gamescope-esde.service
    
  • Log: ~/.cache/rpcs3/RPCS3.log (note: .cache, not .config). RPCS3 buffers it and doesn't flush the tail on SIGKILL, so mid-run state is best read from side-effects (a cache/<SERIAL>/ dir = it booted) rather than the log tail.
  • Gaming is sacred: don't claim Sunshine's ports (47984–48010, 1716) or touch display/session/audio config from homelab work. See CONTEXT.md.

Open follow-ups

  • Two 8BitDo Pro 3 controllers used directly on av still need their exact device names captured (put them in X-input mode, then read SDL: Found game pad).
  • Redundant game .7z archives in ~guest/Downloads/ (~68 GB) can be deleted to reclaim space; the extracted ISOs are what's used.