webstation-broker
DeveloperPython referenceemulators

emulators.duckstation

DuckStation launcher (RomM platform `psx`): disc resolution, ini patching, and shutdown save states.

DuckStation launcher (RomM platform psx): disc resolution, ini patching, and shutdown save states.

DuckStation has no runtime control channel; the whole lifecycle rides its CLI and settings.

Resume: -statefile <sav> loads a state as part of boot. The broker resolves the state file itself because -resume and -state abort with an error dialog when the file is missing.

Save: SIGTERM triggers a graceful shutdown which, with Main/SaveStateOnExit=true, writes <serial>_resume.sav into savestates/ before the process exits. The write is confirmed by diffing the directory across stop(). The resume state is the only state a shutdown produces, so it doubles as the broker's save state; the slot number is carried only for API symmetry.

Ownership: savestates/ is flat and shared by every title, and the broker never reads a disc's serial, so it records its own <state>.rom marker beside each state it confirms. That marker, not the filename, is what ties a state to the disc that produced it on the next resume. Markers ride the save archive with the states they name.

A shutdown the broker had to force-kill may have torn the state mid-write. Such a state is set aside under UNTRUSTED_SUFFIX rather than destroyed: it can be the only copy of the player's progress, and only the resume path needs to be kept away from it.

attributelog
= logging.getLogger(__name__)
attributeROM_ROOT
= Path(os.environ.get('ROM_ROOT', '/romm'))

Root of the RomM library mount (env ROM_ROOT, default /romm).

A resolved disc image must sit under it; candidates resolving outside are discarded.

attributeDATA_DIR
= _data_root()

DuckStation's data root, holding settings.ini, the memory cards and the savestates.

Not configurable, and deliberately: nothing on DuckStation's command line names it, so an override would move only the tree the broker patches and reads its resume states out of and leave DuckStation writing its own. launch exports the root this resolved to instead.

attributeINI_PATH
= DATA_DIR / 'settings.ini'

The settings.ini the broker patches before every launch.

attributeSSTATE_DIR
= DATA_DIR / 'savestates'

Directory DuckStation writes <serial>_resume.sav into on shutdown.

attributeMEMCARD_DIR
= DATA_DIR / 'memcards'

Directory DuckStation keeps its memory cards in, as the pinned [MemoryCards] Directory names it.

attributePINNED_CARD
= 'shared_card_1.mcd'

The one card the pinned settings mount in slot 1, under MEMCARD_DIR.

DuckStation's default names card 1 after the game's title, which the broker never reads, so an imported card would have no name to land under before the disc boots. A Shared card has one fixed name.

attributeDUCKSTATION_LOG_PATH
= Path(os.environ.get('DUCKSTATION_LOG_PATH', '/config/duckstation.log'))

Log file the broker tails for this emulator (env DUCKSTATION_LOG_PATH).

Defaults to /config/duckstation.log.

attributeROM_EXTENSIONS
= ('.m3u', '.chd', '.cue', '.pbp', '.ccd', '.mds', '.iso', '.img', '.ecm', '.bin', '.exe', '.psexe')

Disc formats duckstation-qt can boot, best first.

A folder holding several candidates picks by this order so an .m3u playlist or .chd beats the raw .bin beside it.

attributeOWNER_SUFFIX
= '.rom'

Suffix of the marker file recording which disc a resume state belongs to.

The marker sits beside the state it names, holds the booted disc's resolved path, and rides the save archive with it. It does not match *_resume.sav, so it is never mistaken for a state.

attributeUNTRUSTED_SUFFIX
= '.untrusted'

Suffix a resume state is renamed with when a force-killed exit may have torn it.

The file is only suspected of being incomplete, never known to be, so it is set aside under this name rather than deleted. It stops matching *_resume.sav, so no resume can pick it up, and clear_working_slot keeps it where it sweeps everything else. It rides the save archive as ordinary save data (see save_file_kind), which is what makes it recoverable at all: SSTATE_DIR does not outlive the container.

attributeMEMCARD_BYTES
= 131072

The size of a raw PlayStation memory card, which is what DuckStation mounts: 16 blocks of 8 KiB.