emulators.duckstation.Duckstation
PlayStation 1 sessions on duckstation-qt.
PlayStation 1 sessions on duckstation-qt.
The broker launches duckstation-qt -batch -fullscreen -- <disc> after
forcing its settings.ini (no setup wizard, no power-off confirm, save a
state on exit, no state backups, no update check). There is no runtime
control channel, so the lifecycle is entirely command line and shutdown
driven. A resume passes the newest <serial>_resume.sav with
-statefile, resolved by the broker because DuckStation's own -resume
aborts on a missing file. A save is the graceful shutdown itself: stop()
sends SIGTERM, DuckStation writes the resume state on the way out, and
the write is confirmed by diffing the savestates directory across the
stop. term_timeout is raised well above the base default so the SIGKILL
escalation does not discard that write.
Because the resume state is the only state a shutdown produces, there is
no mid-session save or load, and supports_states stays at the base
default; the requested slot is echoed back purely for API symmetry. Save
data (memcards) and states (savestates) both ride the save archive.
DuckStation writes the resume state whether or not one was asked for, so
an exit without a slot simply leaves it unreported, and the emulator
resumes from it locally as usual.
Attributes
attributename= 'duckstation'RomM platform key, duckstation.
attributedisplay_name= 'DuckStation'Human-readable name shown in the UI.
attributesave_root= DATA_DIRDuckStation's data root, which the save subtrees hang off.
attributesave_subtrees= ('memcards', 'savestates')memcards and savestates, the directories the save archive carries.
attributerom_extensions= ROM_EXTENSIONSBootable disc formats, best first.
attributelog_path= DUCKSTATION_LOG_PATHThe DuckStation log the broker exposes.
attributeterm_timeout= float(os.environ.get('DUCKSTATION_STOP_WAIT', '30'))Seconds SIGTERM gets before SIGKILL (env DUCKSTATION_STOP_WAIT, default 30).
Functions
funcclear_working_slot(self) -> NoneDrop every resume state left in SSTATE_DIR before a restore.
All titles share one flat directory, and _newest_resume_state() picks whichever file is newest with no serial filter. A leftover from an earlier session is otherwise indistinguishable from the state a restore is about to write, so clearing everything here is what keeps a stale file from being served as the new game's own.
paramselfReturns
Nonefuncresolve_rom_file(self, path) -> Optional[Path]Resolve a RomM path to the disc image to boot.
A file is taken as is. A directory is searched one level deep for the
best candidate by _pick_rom_file.
paramselfparampathPathThe ROM file or folder RomM handed over.
Returns
typing.OptionalThe image to pass to duckstation-qt, or None when there is nothing bootable.
funclaunch(self, rom_path, resume_slot) -> NoneStop any running instance, patch settings.ini, and start duckstation-qt.
The binary comes from env DUCKSTATION_BIN (default
/opt/duckstation/AppRun). With resume_slot set, the newest resume
state is passed with -statefile; a resume with no state on disk is
logged and boots clean.
paramselfparamrom_pathPathThe disc image or playlist to boot.
paramresume_slotOptional[int]Any slot to resume from (the number itself is not used), or None to boot clean.
Returns
Nonefuncsave_and_exit(self, slot) -> dict[str, Any]Stop the emulator and report the resume state its shutdown wrote.
The save is the graceful shutdown: the savestates directory is snapshotted, the process is stopped, and a resume state that appeared or changed across the stop is reported as the saved state.
paramselfparamslotOptional[int]The slot RomM asked for, echoed back unchanged; None reports no state even though DuckStation still writes one.
Returns
dictA dict with state_saved (bool), state_slot (slot as given) and state_file