Skip to content

About

Let an AI agent drive a jailbroken PS5 by itself: a virtual DualSense and fast screen capture that close the loop for unattended homebrew port development.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

PS5 DevLoop

stars licence

Let an AI agent, or a script, drive a jailbroken PS5 by itself: press buttons, see the screen, repeat.

Porting homebrew to the PS5 has had a gap in the middle of every loop. A machine can build a title, deploy it, launch it and read its log, but it cannot press Cross on the menu, and it cannot see that the screen is black. So a person sits with a controller, and every long task stops to wait for them.

DevLoop closes that gap. It is one small payload that stays resident on the console, and one Python client:

  • Input: a system-wide virtual DualSense. The home screen and every title see a real controller.
  • Sight: the running title's own frame in about a second, or a screenshot of the whole screen, system UI included, in about three. Both arrive as a small PNG an agent can read, and neither leaves anything on the console.

With those two, an agent can launch a port, walk its menus, notice what is wrong, change the code, and try again, with nobody at the pad.

tools/ps5loop.py load                  # once per console boot
tools/ps5loop.py open                  # connect the virtual controller
tools/ps5loop.py seq l1 wait:1000 cross wait:14000    # Games tab, launch
tools/ps5loop.py grab                  # the title's frame -> captures/shot-<time>.png
tools/ps5loop.py seq down down cross:300 wait:1000
tools/ps5loop.py shot                  # the whole screen, for the home screen and dialogs
tools/ps5loop.py close

That sequence, on a real console: switch to the Games tab, launch a Quake III port, go through its menus, quit through "Exit game?", and land back on the home screen, every step checked from a picture.

For homebrew development on a console you own. It is tested on firmware 13.60 only; the calls it relies on are undocumented and may differ elsewhere. payload/main.c reports what every step returned so that another firmware can be brought up.

For agents

AGENTS.md is the operating guide: the loop, which picture to take when, the rules that keep the console safe, and how to give a new title the grab hook (a few lines). CLAUDE.md includes it, so Claude Code reads it on entering the folder. Point any other agent at it first.

What you need

  • A jailbroken PS5 on your network, with an ELF loader (port 9021) and a klog server (port 3232) running, as the usual payload chains provide.
  • Python 3 on the host, and for pictures JxrDecApp (jxrlib) and magick (ImageMagick): brew install jxrlib imagemagick, or your distribution's packages.
  • To build: the PS5 payload SDK. tools/build.sh finds it through PS5_PAYLOAD_SDK.
  • The console's address in PS5_HOST, or in .env (copy .env.example).

DevLoop presses buttons and looks. Launching and closing titles from a script, and reading klog, are jobs for the tools you already have; mine are PS5_Vulkan's ps5vkctl and run tools. The pad can launch anything from the home screen without them.

Commands

Command What it does
load [ELF] send the payload to the console's ELF loader
open create the virtual pad on the foreground user and make it the one titles read
press BUTTONS [--ms N] press and release: cross, l1+r1
hold BUTTONS [--lx N ...] hold buttons, sticks (0-255) and triggers until the next hold or release
seq STEPS... presses and waits in order: down cross:300 wait:1000
grab [FILE] the running title's frame (--title, from titles.json)
shot [FILE] the whole screen, through the console's screenshot
close remove the pad

tools/ps5loop.py --help lists the rest; payload/main.c documents the wire protocol (one text command per connection, port 9112).

How it works

  • Input is the system's virtual pad device (scePadVirtualDevice*, what Remote Play uses). The payload holds a state and inserts it every 8 ms; presses are timed on the console, not across the network.
  • A title's frame (grab) comes from the title. The payload creates a request file in the title's folder; the title removes it and writes a picture of its next frame; the payload sends that and deletes it. About 1 s end to end at 3840x2160. A title needs a hook of a few lines (AGENTS.md, "Giving a title grab"), and an entry in titles.json.
  • The whole screen (shot) is the console's own screenshot: the payload holds the virtual Create button for 1.2 s, waits for the new file under /user/av_contents/photo, sends it, and deletes it through sceContentDeleteByPath (libSceContentDelete), as the gallery's own Delete does: the file, its thumbnail and its row in the media database all go. The client decodes it (JPEG XR when HDR is on) and scales it to 1280 wide.

What the console showed (firmware 13.60)

  • AddDevice for the real user answers 0x803b0001 and creates nothing. For user 1 it answers 0x803b0006 and the device appears in klog (DEVICE_ADDED ... subType:22); its id is the handle InsertData takes.
  • sceMbusBindDeviceWithUserId, called in the payload's own process, binds it (returns 0, DEVICE_OWNER_CHANGED follows). No injection into the shell is needed.
  • The console still raises "Who's using this controller?". Circle from the virtual pad closes it; open does that 1.5 s after opening.
  • A title opens its user's first controller (scePadOpen(user, 0, 0)). With a real DualSense connected the virtual pad is the second and the title never reads it. sceMbusDisconnectDevice on the real one leaves the virtual pad first, and a title already running picks it up at once. open finds the real pad in klog's [CIM] table and disconnects it; PS on the real pad reconnects it.
  • Confirmed by picture, on the home screen and in a title's menus: Cross, Circle, the d-pad, L1, Create. Not yet exercised: sticks, triggers, PS, Options, the touchpad.
  • sceScreenShotCapture (libSceScreenShot), called from the payload, answers "busy" until it is cancelled, with either type: no way round the Create hold was found.

Limits

  • Pictures are stills, not video. Nothing known reads the composited display from a payload.
  • shot takes about 3 s: Create held for less than 1.2 s opens the Create menu instead.
  • While the pad drives a title, the real controller is disconnected (open --beside keeps it, for the home screen only).
  • The pad's device id is read from the klog server while it opens: another klog client connecting at that moment can make open miss it.
  • The payload listens on the local network with no authentication. Run it on a network you trust.

Credits

The virtual pad's call sequence and its 120-byte sample follow what SplashDown, Control4Free, OmniPad-PS5 and Ghostpad established on hardware. The payload's command server follows ps5vkctl from PS5_Vulkan by Mihawk. Built with the PS5 payload SDK.

Not affiliated with Sony Interactive Entertainment.

License

GPL-3.0-or-later. See LICENSE.

About

Let an AI agent drive a jailbroken PS5 by itself: a virtual DualSense and fast screen capture that close the loop for unattended homebrew port development.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages