Repository navigation
06 systems and launch
System model, the game-launch pipeline, launch strategies, emulator config handlers, AI parameter resolution. Related: 05 — Configuration · 07 — Core Services · 18 — Emulator Parameters
A "system" = ISystemManager (see 05 — Configuration): name, one or more ROM folders, image folder, file formats to search/launch, extract-before-launch flag, group-by-folder flag, and 1–5 emulator configurations.
- Loaded from
system.xmlbySystemManagerService(viaSystemConfigurationServicein the app). - Validated by
DisplaySystemInformation(bad paths shown red + error dialog) andEditSystemWindow.SaveSystem(name/character/path validation, relative-path rewriting to%BASEFOLDER%, GroupByFolder restricted to MAME/DOSBox).
SimpleLauncher\Services\GameLauncher\GameLauncherService.cs
flowchart TD
A[User clicks game] --> B[Build LaunchContext]
B --> C[ValidateContextAsync<br/>existence, long paths, OneDrive, GroupByFolder]
C --> D{Emulator config handler matches?}
D -- yes --> E[HandleConfigurationAsync<br/>writes emulator config / shows window]
E -- false --> Z[Launch vetoed]
E -- true --> F
D -- no --> F[Pick ILaunchStrategy by priority]
F --> G[Execute strategy<br/>launch / extract / mount / convert]
G --> H[Wait for exit]
H --> I[Exit-code check<br/>+ optional AI parameter fix]
I --> J[Record play time >5s, stats, history, GamePlayed event]
Key points (with citations):
- Entry point
HandleButtonClickAsync(:92); guards: no system (:117), no emulators (:126), emulator lookup by name (:136). -
ValidateContextAsync(:145, impl:212): file/dir existence incl. long paths (:225-231), Unicode-normalization fallback (:237-247), OneDrive-specific guidance (:254-270), GroupByFolder restricted to MAME/DOSBox (:309-326). -
Config-handler interception (
:154-158): first matchingIEmulatorConfigHandlerruns; returningfalsevetoes the launch. -
Strategy selection (
:170):_launchStrategies.FirstOrDefault(s => s.IsMatch(context))ordered byPriority(:67). - Finally (
:184-192): resume gamepad, record play time (>5 s), update stats/history, raiseGamePlayed.
- Per-emulator quirks (
:875-906); archive extraction for Azahar/Citra/DuckStation/Ootake/Sameboy/Ymir/ExtractFileBeforeLaunch(:935-965); RetroArch-Land Xemu-dvd_pathparameter validation (:913-933). - Path resolution: emulator exe (
:1026-1038), emulator folder (:1041-1053), containing system folder (:1056), ROM name (:1060),ResolveParameterString(:1063-1070). -
Auto-append ROM unless
%ROM%/%NAME%/game placeholder present; MAME/Raine get the bare machine name (:1080-1104). -
ProcessStartInfowith redirected UTF-8 stdout/stderr (:1121-1132), waits for exit (:1180).
- Ignored: exit 0,
MemoryAccessViolation(-1073741819),DepViolation(-1073740791) (:1329), RetroArch "File open/read error" (:1335-1339). - RetroArch mkdir/permission (
:1342-1360) and parameter issues (:1363-1378). - MAME: ROM-set/unknown system/load-image/INI corruption (
:1381-1466, incl.MameConfigurationService.RestoreMameIniFromSampleat:1455). - Generic failure →
CouldNotLaunchGameMessageBox+AskAiToFixParameters(:1472-1478). -
DoNotCheckErrorsOnSpecificEmulators(:1522-1550): skips Kega Fusion, Project64, Emulicious, Speccy, ProSystem, fMSX (configEmulatorsToSkipErrorChecking).
CheckApplicationControlPolicyService (Core, :16-35): Win32 error classification —
| NativeErrorCode | Meaning | UI |
|---|---|---|
4551 (ERROR_BLOCKED_BY_POLICY) |
Blocked by policy (locale-independent, AppLocker/WDAC) | ApplicationControlPolicyBlockedMessageBoxAsync |
| 5 + AppLocker/WDAC message (EN/ES/FR) | Blocked by policy | ApplicationControlPolicyBlockedMessageBoxAsync |
| 740 | Requires elevation | ElevationRequiredMessageBoxAsync |
| 1223 | User canceled UAC | silent |
Used in batch/shortcut/exe/emulator launch paths and in the Core external-tool launcher.
Ordered by Priority; first IsMatch wins:
| Strategy | Priority | Applies to | Behavior |
|---|---|---|---|
DefaultLaunchStrategy |
999 | everything else |
.BAT → batch, .LNK/.URL → shortcut, .EXE → executable, else regular emulator launch |
ZipMountStrategy |
30 |
.zip/.7z/.rar + RPCS3 / ScummVM / XBLA |
Windows: mount archive; load EBOOT.BIN, ScummVM auto-detect, or XBLA nested exe. Linux/macOS: extract to a temp directory and launch from there (same search logic) |
DosBoxLaunchStrategy |
25 | DOSBox-family emulator + directory/archive/ISO/CHD | ISO/CHD mount, archive extract, .conf/.bat/.exe/.com detection or DosBoxFileSelectionWindow, temp conf, -conf append. Windows mounts ISO/CHD as a drive; Linux/macOS imgmounts ISO files directly and converts CHD with CHDSharp first |
ChdToCueStrategy |
25 |
.chd + 4DO / Raine |
ConvertChdToCueBinAsync → launch .cue → delete temp |
XisoMountStrategy |
20 | Cxbx + .iso / .xiso / .cso / .zar (display name or executable path) |
mount through SimpleXisoDrive → launch the mounted default.xbe (Cxbx-Reloaded cannot read a raw XISO/CISO/ZArchive) |
XemuMountStrategy |
20 | Xemu + .cso/.zar
|
SimpleXisoDrive mounts the image with the virtual image.iso option (MountImageIsoAsync, --image-iso) through Dokan (Windows) / FUSE 3 (Linux), then xemu is launched with -dvd_path <mount>\image.iso; unmounted when xemu exits |
CommanderGeniusLaunchStrategy |
20 | Commander Genius + archive | resolve CG data path (Windows: Documents\Commander Genius; Linux/macOS: ~/.CommanderGenius - LB-11), extract to games\<zipname>, dir="games/<zipname>"
|
PbpToCueStrategy |
15 |
.pbp + Mednafen |
ConvertPbpToCueBinAsync → launch .cue → clean temp |
ChdMountStrategy |
10 |
.chd (not RetroArch/DOSBox) + 19-emulator list |
Xbox-ISO CHDs (xemu/Cxbx-Reloaded) are mounted through SimpleXisoDrive first on Windows and Linux (virtual image.iso / mounted default.xbe) and fall back to CHDMounter when the Dokan/FUSE driver is unavailable; other emulators mount via CHDMounter (Windows Dokan/WinFsp, Linux FUSE 3), find the launch file per emulator (EBOOT.BIN/default.xex/image.iso/default.xbe/.bin/.cue), regular launch; Linux falls back to the managed CHDSharp conversion when the mount is unavailable |
SimpleLauncher.Core\Services\ExtractFiles\ExtractionService.cs
-
ExtractToTempAndGetLaunchFileAsync(:41),ExtractToFolderAsync(:70); only 7z/zip/rar (IsSupportedArchivePath), unsupported formats are an expected input condition logged at Information + user box (bug #67537); file-lock retry 10×1 s (:100-123). -
.extraction_in_progressmarker written before extract, removed on success. On failure only the marker plus the files written by that run are deleted — destination folders are never wiped (CORE-01), and tracked files are removed before the marker. -
Disk-space check: estimated size × 1.2 vs
DriveInfo.AvailableFreeSpace→DiskSpaceErrorMessageBoxAsync+ IOException (:165-196). -
Path-traversal guard: every entry must resolve under the destination root (
:199-219,:364-382); the comparison is Ordinal on case-sensitive filesystems, andDIR\FILE.BINentry names are normalized to the platform separator before combining (LB-16); random temp names (:351-354). -
7-Zip fallback: SharpCompress failure on
.7z→tools\SevenZip\7za.exe/_arm64.exeon Windows,tools/SevenZip/7zz/7zz_arm64on Linux/macOS (GetSevenZipExecutableName,:490-497), argsx -o"dest" -y "archive", 30-minute timeout with kill; on Unix the execute bits are set on 7zz before launch (LB-15). - Temp root:
%TEMP%\SimpleLauncher(:20);ValidateAndFindGameFileAsync(:540) searchesFileFormatsToLaunchthen any file.
| Service | Backend | Details |
|---|---|---|
MountChdFiles / MountChdDrive
|
tools\CHDMounter\{win,linux}-{x64,arm64}\CHDMounter(.exe) |
Windows: Dokan check (DokanValidation.IsDokanInstalled()), args /a "<chd>" /s:<consoleAlias>, auto-picked drive, poll 240×500 ms (120 s max); Linux: FUSE check (FuseValidation.IsFuseAvailable()), explicit mount point under the temp folder, args /s:<alias> "<chd>" "<mountPoint>", readiness poll 60×500 ms (30 s max), silent Information-level fallback to the managed conversion; console alias per system/emulator; unmount = SIGTERM (clean FUSE unmount) → SIGKILL fallback → fusermount3 -u/umount → mount-point removal; DisposeAsync verifies the release |
MountIsoFiles |
PowerShell Mount-DiskImage
|
drive letter from Get-Volume output (:193-258); 30 s PS timeout (:225); polls 10 s for mount (:74); finds EBOOT.BIN (:95); dismount in finally (:317-342); execution-policy detection (:412+) |
MountXisoFiles / MountXisoDrive
|
tools\SimpleXisoDrive\SimpleXisoDrive.exe (+_arm64) |
Platform check first (Linux/macOS → Information "not supported" — LB-14); Dokan validation (:80-87); drive letter Z→D selection (:33-57); args "<iso>" "Z:"; polls for default.xbe 240×500 ms (:172-209); kill + 20 s wait on dispose (:67, :91). MountImageIsoAsync adds --image-iso and polls for image.iso instead (Xemu + .cso/.zar, launcher tool 1.4.0+) |
MountZipFiles |
tools\SimpleZipDrive\SimpleZipDrive.exe (+_arm64) on Windows; temp extraction on Linux/macOS |
Path-traversal validation always runs (platform-neutral simulated root, backslash entries normalized); Windows mounts a drive and polls 1 min; Unix extracts to %TEMP%/SimpleLauncher/ZipLaunch/<guid> (separator-normalized, containment-checked), finds EBOOT.BIN / nested 000D0000 file / ScummVM folder, then deletes the temp directory when the emulator exits |
DokanValidation |
P/Invoke dokan2.dll
|
DokanVersion() > 0 (:12-36) |
SimpleLauncher.Core\Services\Converters\DiscConverter.cs — all with 5-minute timeout + kill, temp files in %TEMP%\SimpleLauncher:
| Method | Tool | Args |
|---|---|---|
ConvertChdToIsoAsync |
CHDSharp (NuGet) first, tools\CHDStudio\win-x64\chdman.exe / win-arm64\chdman_arm64.exe fallback on Windows |
managed DVD extraction (ExtractToDirectory); chdman extractdvd -i -o
|
ConvertChdToCueBinAsync |
CHDSharp (NuGet) first, chdman fallback on Windows | managed CD extraction to CUE/BIN (cooked: true); chdman extractcd -i -o
|
ConvertPbpToCueBinAsync (:211) |
PBPSharp (NuGet package) |
PbpFile.Open → disc 1 → ExtractToBinCue
|
ConvertToIsoAsync (:317) |
tools\RVZStudio\win-x64\DolphinTool.exe / win-arm64\DolphinTool_arm64.exe
|
convert --format=iso --input --output |
Interface: IEmulatorConfigHandler.IsMatch(name, path) + Task<bool> HandleConfigurationAsync(context) (IEmulatorConfigHandler.cs:16-23); false vetoes launch (GameLauncherService.cs:157).
Windows-only in Avalonia: the handlers write the Windows config locations of each emulator (Documents/%APPDATA% paths, Windows file layouts), so EmulatorConfigHandlerRegistration registers them only on Windows — matching the hidden "Inject emulator config" menu — and Linux/macOS launches run no config handlers (LB-08).
Pattern: if ShowSettingsBeforeLaunch → modal Inject*ConfigWindow (user "Run" vs "Cancel"); else XxxConfigurationService.InjectSettings(...).
| Handler (app) | Core service | Config file written |
|---|---|---|
| AresConfigHandler | AresConfigurationService | settings.bml |
AzaharConfigHandler (catch AzaharPermissionException → msg, still launch) |
AzaharConfigurationService | qt-config.ini |
| BlastemConfigHandler | BlastemConfigurationService | default.cfg |
| CemuConfigHandler | CemuConfigurationService | settings.xml |
| DaphneConfigHandler (no file — appends CLI args) | DaphneConfigurationService | none (BuildArguments) |
| DolphinConfigHandler | DolphinConfigurationService |
Dolphin.ini (portable User\Config\ else %APPDATA%\Dolphin Emulator\Config) |
| DuckStationConfigHandler | DuckStationConfigurationService | settings.ini |
| FlycastConfigHandler | FlycastConfigurationService | emu.cfg |
| MameConfigHandler (catch IO/Unauthorized → msg, still launch) | MameConfigurationService | mame.ini |
| MednafenConfigHandler | MednafenConfigurationService | mednafen.cfg |
| MesenConfigHandler | MesenConfigurationService | settings.json |
Pcsx2ConfigHandler (catch Pcsx2PermissionException → msg, still launch) |
PCSX2ConfigurationService |
PCSX2.ini (portable inis\, emu dir, or Documents\PCSX2\inis) |
| RaineConfigHandler | RaineConfigurationService | config\raine32_sdl.cfg |
| RedreamConfigHandler | RedreamConfigurationService | redream.cfg |
| RetroArchConfigHandler | RetroArchConfigurationService | retroarch.cfg |
| Rpcs3ConfigHandler | RPCS3ConfigurationService |
config.yml (YAML) |
| SegaModel2ConfigHandler | SegaModel2ConfigurationService | EMULATOR.INI |
| StellaConfigHandler | StellaConfigurationService |
stella.sqlite3 (SQLite upserts; sample DB copied if missing) |
| SupermodelConfigHandler | SupermodelConfigurationService | Config\Supermodel.ini |
| XeniaConfigHandler (swallows all exceptions) | XeniaConfigurationService |
xenia-canary.config.toml + xenia.config.toml (or Documents\Xenia\) |
| YumirConfigHandler | YumirConfigurationService | Ymir.toml |
All services restore missing config files from samples\{Emulator}\{file} and produce the per-field settings consumed by the 21 Inject*ConfigViewModels (settings persisted in settings.xml under EmulatorSettings\{Emulator}).
-
ParameterResolverService(Core,Services\ParameterResolver\ParameterResolverService.cs): POST to relativeapi/ParameterResolver/resolveon theParameterResolverClient(baseUrls:ParameterResolverApi, 60 s), headerX-Api-Key(fromAppConstants.GetApiKey()), camelCase JSON. Request:ParameterResolverRequest(system name/folder, formats, flags); response:ParameterResolverResult(SuggestedParameter,Explanation). Non-2xx → null + error log; malformed JSON → null (logged); network errors propagate. -
AskAiToFixParameters(app,Services\GameLauncher\AskAiToFixParameters.cs): after a failed launch, prompts the user, shows a loading overlay, calls the resolver, shows a suggestion dialog, and on accept saves the newEmulatorParametersand reloads the system.
- Home
- 01 — Overview
- 02 — Projects & Solution
- 03 — Quickstart
- 04 — Architecture
- 05 — Configuration
- 06 — Systems & Launch
- 07 — Core Services
- 08 — UI Layer
- 09 — RetroAchievements
- 10 — Game Scanning
- 11 — Bundled Tools
- 12 — Data Formats
- 13 — Logging & Debug
- 14 — Testing
- 15 — Development
- 16 — Updater
- 17 — Release Notes
- 18 — Emulator Parameters
- GUI Test Harness
- Manual Tests
- Parameters