Every platform pad needed three hand-maintained files (es_input.cfg, the RetroArch autoconfig and gamecontrollerdb.txt) and nothing checked they agreed; the Soysauce pad shipped without the SDL entry, so ports saw no controller. Add projects/ArchR/inputs: one JSON definition per pad plus gen-input-configs, which verifies the three artifacts against the defs (wired as a fail-fast gate on the docker-% Makefile targets), regenerates them on demand, imports legacy entries, and computes SDL GUIDs the same way the runtime does, including the name-embedded form SDL uses when vendor*product is zero. The 17 platform pads were imported from the current artifacts and the migration already caught two real inconsistencies. The GO-Super gamecontrollerdb line carried a numeric-form GUID, but the pad reports vendor 0 so SDL derives the name-embedded GUID at runtime (the form the device-captured es_input block always had); the line is now fixed to 1900bb3e474f2d537570657220476100 and its RetroArch cfg canonicalized. And retrogame_joypad legitimately ships different mappings per bus (guide:b10 only on 0x19), now modeled explicitly as a per-GUID map. Verification is semantic for es_input blocks and RetroArch cfgs (the legacy files mix tabs and spaces) and byte-exact for gamecontrollerdb lines, with GUIDs always recomputed from the identity fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Built-in gamepad definitions (single source of truth)
Every built-in platform pad must exist in THREE shipped artifacts:
packages/ui/emulationstation/config/common/es_input.cfg(EmulationStation)packages/emulators/libretro/retroarch/retroarch-joypads/gamepads/<name>.cfg(RetroArch)packages/apps/gamecontrollerdb/config/gamecontrollerdb.txt(SDL games and ports)
Historically these were maintained by hand and nothing checked they agreed:
the Soysauce pad shipped without the SDL entry, so ports saw no controller.
The JSON files in defs/ are now the source of truth and gen-input-configs
keeps the three artifacts honest.
Daily use
make docker-* refuses to build if the artifacts drifted from defs/:
./gen-input-configs verify # what CI/build runs
./gen-input-configs write # regenerate artifacts from defs
Only external controllers (USB/Bluetooth pads inherited from upstream databases) stay unmanaged; the managed set is exactly the defs directory.
Adding a new pad (example: a future R36T Max)
-
On the device, read the identity:
cat /proc/bus/input/devices # Name=, Vendor=, Product=, Version= -
Compute the GUID the runtime will use (bus 0x19 for platform drivers):
./gen-input-configs guid "r36tmax_Gamepad" --vendor 1 --product 0x1199 --version 0x0100Vendor or product zero switches SDL to the name-embedded GUID form automatically, exactly like the runtime does (that mismatch was the Soysauce bug).
-
Copy the closest def (
defs/r36s_Gamepad.json), adjust name, identity, buses, the SDL mapping string, the ES inputs and the RetroArch keys. -
Materialize and check:
./gen-input-configs write "r36tmax_Gamepad" ./gen-input-configs verify -
Commit the def together with the regenerated artifacts.
Fidelity rules (deliberate)
- gamecontrollerdb line: byte-exact; the GUID is always recomputed from the
identity fields (use
guid_overrideonly for entries that predate the tool and cannot be reproduced; none of the platform pads needs it). - RetroArch cfg and es_input block: semantically exact (key/value sets);
comments, blank lines and ordering are not preserved by
write. sdl_mappingmay be a per-GUID dict when buses legitimately differ (retrogame_joypad ships guide:b10 only on the 0x19 bus line).