feat: describe platform launchers in the schema

A launcher record says what a platform's own launch code looks for and hands to an emulator, read from that code at the pinned revision.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 20:14:30 +02:00
1 parent 7151ea212c
commit 2556a03ed3
2 files changed
+147

No files matched your search

+103
View File
@@ -100,6 +100,13 @@
"$ref": "#/$defs/system"
}
},
"launchers": {
"type": "array",
"description": "What the platform's own launch code looks for and hands to each emulator or core, read by its scraper at the revision `source` pins",
"items": {
"$ref": "#/$defs/launcher"
}
},
"overrides": {
"type": "object",
"additionalProperties": false,
@@ -115,6 +122,102 @@
}
},
"$defs": {
"launcher": {
"type": "object",
"additionalProperties": false,
"required": [
"emulator",
"build",
"kind",
"names",
"source_ref"
],
"properties": {
"emulator": {
"type": "string",
"description": "Emulator or core as the platform names it (comma-separated when one code path serves several)"
},
"build": {
"type": "string",
"enum": [
"standalone",
"libretro"
]
},
"kind": {
"type": "string",
"enum": [
"names_searched",
"fixed_name",
"core_option",
"passes_path",
"links",
"copies",
"scans_dir"
],
"description": "names_searched: tries the names in order, first found wins. fixed_name: writes or opens one path per name. core_option: sets a core option whose value names the file. passes_path: hands over a directory. links, copies: links or copies a file to where the emulator reads it. scans_dir: the emulator picks from a directory"
},
"slot": {
"type": "string",
"description": "The key the launcher files this list under, such as a region or a machine"
},
"names": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names relative to search_dir, in the order the code tries or writes them. A {X} placeholder is expanded from `expands`"
},
"expands": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
},
"description": "Values a placeholder in `names` takes, in the order the code tries them for each name"
},
"case_insensitive": {
"type": "boolean",
"description": "Whether the launcher matches names regardless of case"
},
"search_dir": {
"type": "string",
"description": "Directory the launcher looks in, as the code resolves it"
},
"passes_to": {
"type": "string",
"description": "Where the found file is handed to the emulator: config key, command-line flag or core option"
},
"fails_when_missing": {
"oneOf": [
{
"type": "boolean"
},
{
"type": "string"
}
],
"description": "False when the launcher starts without the file, else the message it raises"
},
"fails_scope": {
"type": "string",
"enum": [
"emulator"
],
"description": "emulator: the failure is raised only when every record of this emulator with the same source_ref found nothing"
},
"depends_on_option": {
"type": "string",
"description": "Options or conditions in the launcher code that decide whether this record applies"
},
"source_ref": {
"type": "string",
"description": "path:lines in the platform's repository at the pinned revision"
}
}
},
"system": {
"type": "object",
"additionalProperties": false,
+44
View File
@@ -196,6 +196,50 @@ systems:
when the upstream destinations already carry their own root, as RetroDECK does
with `bios/` and `roms/`.
### Launchers
Between the file a user drops in the BIOS folder and the emulator, a
platform's own launch code often decides which name is read and where it is
handed over. Batocera's DuckStation generator tries 22 names per region, first
found wins, and writes the hit into `settings.ini`. That behaviour belongs to
the platform, not to the emulator, so the scraper records it from the
platform's code under `launchers:`, never as an alias in an emulator profile.
```yaml
launchers:
- emulator: duckstation # as the platform names it
build: standalone # standalone or libretro
kind: names_searched
slot: NTSCU # the key the code files this list under
names: [scph101.bin, scph1001.bin, scph5501.bin, scph7001.bin, scph7501.bin]
case_insensitive: true
search_dir: /userdata/bios
passes_to: "[BIOS] PathNTSCU"
fails_when_missing: No PSX1 BIOS found
fails_scope: emulator # raised only when every slot found nothing
source_ref: "package/.../duckstationGenerator.py:115-136, 501-523"
```
| Field | Meaning |
|-------|---------|
| `emulator` | Emulator or core as the platform names it |
| `build` | `standalone` or `libretro` |
| `kind` | `names_searched` (tried in order, first found wins), `fixed_name` (one path per name), `core_option` (an option value names the file), `passes_path`, `links`, `copies`, `scans_dir` |
| `slot` | The key the code files the list under: a region, a machine |
| `names` | In the order the code tries or writes them, relative to `search_dir` |
| `expands` | Values a `{X}` placeholder in `names` takes, in the order tried (Hatari tries each TOS in ten languages) |
| `case_insensitive` | Whether the launcher matches names regardless of case |
| `search_dir` | Directory the launcher reads, as the code resolves it |
| `passes_to` | Config key, command-line flag or core option that receives the file |
| `fails_when_missing` | `false`, or the message the launcher raises |
| `fails_scope` | `emulator` when the failure needs every record of the emulator to come up empty |
| `depends_on_option` | Options or conditions that decide whether the record applies |
| `source_ref` | `path:lines` at the revision `source` pins |
Each record is read with `ast` from the generator. An extractor that no longer
recognises the shape of the code raises: an empty list would read as a
launcher that looks for nothing.
### Inheritance
If the new platform inherits from an existing one (e.g. Lakka inherits RetroArch),