emulators.rpcs3.Rpcs3
RPCS3 (PS3) launcher: exit-only savestates via PINE, plus archive boot support.
RPCS3 (PS3) launcher: exit-only savestates via PINE, plus archive boot support.
Declared imports take PS3 save folders (save data under
home/00000001/savedata, game data under game/) and one savestate,
which rides the archive and is filed under the boot target's title id.
See import_spec.
Attributes
attributename= 'rpcs3'attributedisplay_name= 'RPCS3'attributesave_root= DEV_HDD0attributestate_subtrees= ('savestates',)States land under the symlinked savestates dir; see _ensure_sstate_link.
attributeclears_stale_saves= TrueOn: clear_working_slot empties every cellSaveData and cellGameData dir.
Savestates are the one part of the tree cleared per title rather than
whole, since a title the session never boots can hold states no dump has
carried off yet; the ones a session cannot scope by title id are handled
by _clear_leftover_states once PINE names the title.
attributelog_path= RPCS3_LOG_PATHattributeterm_timeout= float(os.environ.get('RPCS3_STOP_WAIT', '2'))attributerom_extensionstuple[str, ...]Bootable formats, minus the archives needing the disabled extraction cache.
An archive only reaches a boot target by way of CACHE_DIR, so with
the cache off it is not bootable and must not be advertised as
accepted. .pkg is unaffected: it installs through _install_pkgs
into GAME_DIR regardless of the cache flag.
attributesave_subtreestuple[str, ...]CellSaveData saves, save states, plus the cellGameData save dirs under game/.
game/ mixes save data with installed PKG titles and RPCS3's lock folders, so the dump enumerates only the dirs without a bootable EBOOT.BIN. A restore inverts the problem: the archive holds nothing but previously dumped save dirs, and those dirs don't exist on disk yet, so the whole game/ prefix is declared to let them through.
attributerestore_subtreestuple[str, ...]The restore set, whatever _restoring says.
Preflight reads this before clear_working_slot has flipped
_restoring, and a restore needs the whole game/ prefix: the dirs
it brings back do not exist on disk yet.
attributelink_rootstuple[Path, ...]The savestate tree dev_hdd0/savestates links to.
Read at call time so a test that patches SSTATE_ROOT is honoured.
Functions
func__init__(self) -> NoneInitialize per-session launch tracking state.
paramselfReturns
Nonefuncclear_working_slot(self, excluded=()) -> NoneCreate the savestates symlink and drop the last session's saves and states.
Save data goes first and goes whole (_clear_stale_save_data): a save
dir is not scoped to the incoming title the way a savestate dir is, and
the exit dump ships a session save dir whole, so a previous player's
untouched files left sitting in one would leave in this player's
archive.
RPCS3 has no fixed slot to clear, but activate() calls this before it ever reads save_subtrees to restore an archive, which makes it the one place that is safe to create the savestates symlink: it runs on every activate, but never during a bare construction (registry sweeps build every emulator against real, unredirected paths).
It also drops the incoming title's own savestates dir and flips _restoring on early. Clearing first means a leftover local state from a previous session never outranks (by mtime) whatever this session's archive restores. Only that one title goes: every other title's dir holds states that may not have been archived yet (a crashed session, a title mid-restore for a different game), and none of them can outrank this session's states anyway because every lookup is already scoped to SSTATE_ROOT/<title id>. A boot target whose title id the path alone cannot supply (a bare .iso, an archive) therefore clears nothing rather than guessing. Flipping _restoring here, not just in prepare_restore(), matters because api.py reads save_subtrees for the restore extract before it ever calls prepare_restore().
paramselfparamexcludedtuple[str, ...]= ()Subtrees carried by the whole-card routes. RPCS3 names no memory card subtree, so this is always empty.
Returns
Nonefuncimport_spec(self) -> imports.ImportSpecDeclare what RPCS3 takes: PS3 save folders, and one savestate.
Save data and game data are both folders that must hold PARAM.SFO.
RPCS3 groups the units itself in validate_import_plan, because a
unit sits two or four components deep depending on the tree. The
state rides the archive and resumes through save.resume_slot.
paramselfReturns
webstation_broker.imports.ImportSpecThe spec.
funcplace_import(self, member, spec, ctx) -> Union[imports.Placement, imports.ImportRefusal]Place one declared member: a save folder's file, or the state.
paramselfparammemberimports.ImportMemberThe member, already past the kind gate.
paramspecimports.ImportSpecThis emulator's spec.
paramctximports.ImportCtxThe launch context.
Returns
typing.UnionThe placement, or a refusal.
funcvalidate_import_plan(self, plan, ctx) -> list[imports.ImportRefusal]Refuse a save folder that has no PARAM.SFO at its root.
paramselfparamplanlist[imports.Placement]The placements that passed every per-member check.
paramctximports.ImportCtxThe launch context.
Returns
listOne incomplete_unit per member of each folder that lacks one.
funcidentity_source(self) -> Optional[imports.IdentitySource]Read the session's title id off the boot target, and take RomM's when it has none.
paramselfReturns
typing.OptionalThe source.
funcprepare_restore(self) -> NoneStop any running instance and mark the session as archive-restoring.
paramselfReturns
Nonefuncresolve_rom_file(self, path) -> Optional[Path]Resolve a RomM path to a single bootable PKG, disc, or ISO.
A file is taken as is. A directory is searched for the game's
installer/disc layout via _ROM_SEARCH_GLOBS. The chosen target is
also recorded in _pending_rom for clear_working_slot, which runs
next and has no other way to name the title it is clearing for.
paramselfparampathPathRomM's resolved rom path, file or directory.
Returns
typing.OptionalThe path to boot, or None if nothing bootable was found, or path
func_xdotool(self, *args) -> Optional[str]Run xdotool, returning its stdout, or None if it failed.
paramselfparamargsstr= ()Returns
typing.Optional[str]func_game_window(self) -> Optional[str]The render window belonging to this session's rpcs3, or None.
The class is shared by every rpcs3 on the display, so a zombie left by
a session that failed to die answers the search too, and the first
match is as likely to be that one as this one: a save hotkey sent
there saves nothing and never returns a state. xdotool search --pid
cannot make the distinction either, because AppRun runs the emulator
as a child and the window's pid is the child's, not the handle's, so
the launch process group is what names it.
paramselfReturns
typing.OptionalThe matching window id. A window whose pid cannot be read at all
func_send_key(self, key) -> boolFocus the render window and send key through XTEST.
Activating first is what makes this survive the player clicking back into the page: XTEST delivers to whatever holds focus, so a key sent at an unfocused RPCS3 goes to the desktop instead.
paramselfparamkeystrReturns
boolfunclaunch(self, rom_path, resume_slot) -> NoneStop any running instance and boot rpcs3 for the given ROM.
Installs PKGs and extracts archives as needed, then resolves a savestate to boot into when a resume slot is requested and one exists for the title.
Raises
RuntimeError: If the PKG install or the archive extraction fails, or the boot target it produced resolves outside the ROM library and the emulator's own data dirs.
paramselfparamrom_pathPathRomM's resolved rom path (file or directory).
paramresume_slotOptional[int]Slot to resume from, or None for a fresh boot.
Returns
Nonefunc_boot_watchdog(self, seq) -> NoneConfirm the launched game reaches a running state via PINE.
Also opportunistically resolves the title id via MsgID for boots that had no serial from the file path alone (a bare .iso).
A process still alive when the deadline passes without ever reporting running is the boot-error-dialog case: RPCS3 does not exit on its own, so nothing else in the broker would ever notice.
paramselfparamseqintReturns
Nonefuncsave_and_exit(self, slot) -> dictSend the save-state hotkey, then stop the emulator.
paramselfparamslotOptional[int]Slot to save to, or None to exit without saving.
Returns
dictA dict with state_saved, state_slot, and state_file (path,
funcstate_path(self) -> Optional[Path]Return the state the last saving exit confirmed, or None.
Only a confirmed exit state is served, never the newest file in the title's savestates dir: until the exit's write settles, the newest file there is an older one the archive brought in.
paramselfReturns
typing.OptionalThe state file's path, or None when no saving exit has confirmed one
funcstop(self) -> NoneKill the process, invalidating any in-flight boot watchdog first.
paramselfReturns
Nonefunc_session_save_dirs(self) -> list[Path]This title's save dirs.
A dir qualifies by name (prefixed with the session serial) or by containing a file written while the session ran.
paramselfReturns
listThe save dirs belonging to this session. Empty when nothing was