emulators.cemu.Cemu
Wii U via Cemu, driven by command line flags and config file patching.
Wii U via Cemu, driven by command line flags and config file patching.
Cemu has no control API, so the broker pins settings.xml before every
launch (no update check, no Discord presence, no boot sound, no menubar),
seeds the player-0 GamePad profile once, and boots the game fullscreen
with -f -m -g. Cemu installs no signal handler, so the stop is a hard
SIGTERM kill; that is safe because the game's own save data is already
on disk the moment the game writes it.
There are no save states: persistence is the title save tree under
usr/save in the virtual MLC, which is what the archive carries. At exit
every file in the title save dirs written during the session gets its
mtime refreshed so the delta dump ships those saves whole while other
titles' saves stay filtered out. A resume slot is logged and ignored.
Attributes
attributename= 'cemu'Provider key, cemu.
attributerom_cacheable= TrueOn; with the ROM cache enabled, launch boots a local copy of the ROM.
attributedisplay_name= 'Cemu'Human-readable name.
attributeclears_stale_saves= TrueOn; activate empties the title save tree.
attributesave_root= MLC_DIRThe MLC directory the save subtrees hang off.
attributesave_subtrees= ('usr/save',)usr/save, the title save tree.
attributerom_extensions= ROM_EXTENSIONSBootable formats, best first.
attributelog_path= CEMU_LOG_PATHThe emulator log file.
attributeterm_timeout= float(os.environ.get('CEMU_STOP_WAIT', '5'))SIGTERM grace before SIGKILL (env CEMU_STOP_WAIT, default 5).
attribute_session_start= float('inf')Unix time launch started Cemu at; infinity until it does.
Infinity rather than zero so an instance that never launched matches
no file at all. Zero is newer than nothing, so every title in the
container would read as written this session and save_and_exit
would restamp and ship all of them.
Functions
func__init__(self) -> NoneInitialise the process handle and the session baseline.
paramselfReturns
Nonefuncclear_working_slot(self, excluded=()) -> NoneEmpty the title save tree before the archive restore.
Cemu keys a save by title id and by an account id the container shares across players, so the previous session's saves sit exactly where this one's belong. The restore only writes the members the incoming archive carries, and the exit restamp ships a title whole once anything under it is written, so a title the last player saved and this one's archive does not name would be readable here and would leave again in this player's dump.
The account store under usr/save/system stays: it is what the save
paths are keyed by, not one player's data, and Cemu only writes it on
a first boot, so clearing it every activate would drop the session onto
account setup instead of the game.
paramselfparamexcludedtuple[str, ...]= ()Subtrees carried by the whole-card routes. Cemu has no memory card, so this is always empty.
Returns
Nonefuncprepare_restore(self) -> NoneStop a running Cemu so the archive can be extracted under it.
paramselfReturns
Nonefuncimport_spec(self) -> imports.ImportSpecDeclare what Cemu takes: title save trees, and nothing else.
Cemu has no states and no cards, so the save kind is the only one.
paramselfReturns
webstation_broker.imports.ImportSpecThe spec.
funcplace_import(self, member, spec, ctx) -> Union[imports.Placement, imports.ImportRefusal]Place one declared save file under the session's title.
More than one donor account in the archive is refused, for every file that names one: they would all land on Cemu's single account.
paramselfparammemberimports.ImportMemberThe member, already past the kind gate.
paramspecimports.ImportSpecThis emulator's spec.
paramctximports.ImportCtxThe launch context.
Returns
typing.UnionThe placement, or a refusal.
funcidentity_source(self) -> Optional[imports.IdentitySource]Take the session's game from RomM's title id, in either Wii U spelling.
RomM writes a Wii U title id as the low half on its own (101C9400)
or as the whole sixteen-digit id (00050000101C9400), the same two
spellings a member's path may carry, and both name the one save
folder. Cemu boots titles by file, not by a path that names an id, so
there is no rom reader.
paramselfReturns
typing.OptionalA hex8 source with no reader, reading RomM's id as a wiiu_title.
funcresolve_rom_file(self, path) -> Optional[Path]The file Cemu should boot for path.
paramselfparampathPathA ROM file, or a folder searched up to three levels deep.
Returns
typing.OptionalThe file itself, the best-ranked bootable file in the folder, or None.
funclaunch(self, rom_path, resume_slot) -> NonePatch settings, seed the pad profile and boot the game fullscreen.
Raises
RuntimeError: When settings.xml cannot be patched, which would leave Cemu parked on its Getting Started modal.
paramselfparamrom_pathPathThe file to boot.
paramresume_slotOptional[int]Ignored with a log line; Cemu has no save states.
Returns
Nonefunc_modified_title_saves(self) -> list[Path]Title save dirs holding a file written while the session ran.
A listing that fails is logged and skipped rather than raised: the MLC can vanish under the walk, and the exit path calling this still has a process to stop and a report to hand back.
Only <8 hex>/<8 hex> pairs count. usr/save also holds the system
tree, and both halves feed the restamp walk, so a name that is not a
title id half is not a title save dir.
paramselfReturns
listThe usr/save/<titleHigh>/<titleLow> directories touched since
funcsave_and_exit(self, slot) -> dict[str, Any]Stop Cemu and mark this session's title saves for the dump.
paramselfparamslotintIgnored; there are no save states.
Returns
dictstate_saved, state_slot and state_file, all None.