emulators.xenia
Xenia Edge (Xbox 360) launcher: ROM resolution and launch/kill lifecycle.
Xenia Edge (Xbox 360) launcher: ROM resolution and launch/kill lifecycle.
Xenia has no save states and no external control API (no IPC, no socket, no stdin protocol; the only listener is a gdbstub behind --debug, which drives the JIT, not the session). Persistence is the game's own save data, which the guest commits to plain host files under the content tree:
<storage_root>/content/<XUID>/<TITLE_ID>/00000001/<save name>/...
Guest writes are write-through: every guest NtWriteFile is an immediate host pwrite() with no user-space buffering, so save files are complete on disk the moment the game writes them and survive a hard kill. Xenia installs no SIGTERM/SIGINT handlers (only SIGILL/SIGSEGV/SIGBUS for the JIT), so SIGTERM kills it with default disposition. That skips xenia's own exit path, but that path only saves config and flushes the log, nothing save-related, so the base SIGTERM->SIGKILL stop is the shutdown story. The exit must always be broker-driven anyway: a game exiting to dashboard leaves xenia sitting on a modal dialog instead of exiting, and a guest main-thread exit leaves the process idling with the window open.
The profile tree (content/<XUID>/FFFE07D1/...) ships inside the content subtree: Xbox 360 save paths embed the profile XUID, so profile and saves must travel together for a restore into a fresh container to line up.
A profile has to exist before the broker launches anything. Xenia Edge checks its account list as soon as the emulator is initialised and, finding none, raises a native "No Profiles Found" dialog that is not gated on --headless and that nobody in the stream can dismiss; without a profile a game cannot save either. Creating one on the desktop signs it in and persists the XUID into xenia-edge.config.toml under the storage root (logged_profile_slot_0_xuid), so every later launch against the same --storage_root signs in silently. docs/standalone_emulators.md carries the user-facing version of this.
--headless does not mean windowless: the game still renders in the normal
window selkies captures. It auto-answers guest system dialogs (storage
select, sign-in, message boxes) that nobody in the stream could dismiss.
One title per process: loading a second game requires a process restart.
Bootable forms: an XISO (.iso), a bare executable (.xex), an extracted dump (folder with default.xex at its root), or an XBLA / Games on Demand title folder, whose STFS package the resolver digs out of the content-type layout the console uses (see _find_container).
attributelog= logging.getLogger(__name__)attributeROM_ROOT= Path(os.environ.get('ROM_ROOT', '/romm'))attributeXENIA_BIN= os.environ.get('XENIA_BIN', '/opt/xenia/AppRun')attributeDATA_DIR= Path(os.environ.get('XENIA_DATA_DIR', '/config/xenia'))attributeXENIA_LOG_PATH= Path(os.environ.get('XENIA_LOG_PATH', '/config/xenia.log'))attributeROM_EXTENSIONS= ('.iso', '.xex')