RetroStack: a modular Docker platform providing scalable, multi‑emulator support for retro gaming. Run emulators standalone or as composable services. Features include multi-arch images (amd64/arm64), profile-based emulator selection, persistent config/saves, gamepad auto-detection, and optional integration with EmulationStation-DE via FIFO control pipes.
Sponsored and maintained by Blackout Secure.
Tip
RetroStack can run standalone — no frontend required. For an optional frontend, see docker-emulationstation-de (also by Blackout Secure).
This project packages upstream emulators (RetroArch, PPSSPP, Dolphin) into ready-to-run container images for cabinets, desktops, HTPCs, and handheld Linux systems. Each image starts its own internal Xorg server and launches the emulator GUI by default (standalone mode) or listens for launch commands via FIFO control pipes when set to daemon mode — ideal for integration with frontends like EmulationStation-DE. No host X server is required.
Quick links:
- Docker Hub listing: blackoutsecure/retrostack
- GitHub repository: blackoutsecure/docker-retrostack
- ES-DE frontend container: docker-emulationstation-de
- Balena block metadata: balena.yml
- RetroStack
Note
Not sure which emulator to pick? Use RetroArch — it covers the widest range of systems (NES, SNES, GB/GBA, Genesis, PS1, and hundreds more) via libretro cores. It's the default and recommended choice for most users.
Standalone — run a game directly (container exits when done):
docker run --rm \
-e DISPLAY=:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v /path/to/roms:/roms:ro \
--device=/dev/dri:/dev/dri \
--device=/dev/input:/dev/input \
--device=/dev/snd:/dev/snd \
blackoutsecure/retrostack:retroarch \
--core gambatte /roms/gb/game.gbTry instantly — no ROMs needed (uses the bundled demo ROM):
docker compose --profile retroarch up -dThe image ships with a free, open-source demo ROM (Libbet and the Magic Floor) that is automatically seeded into /roms on first boot when the volume is empty and writable. If you supply your own ROMs, seeding is skipped entirely and /roms can be mounted read-only.
Service mode — start emulator containers using profiles:
# Start RetroArch emulator container
docker compose --profile retroarch up -d
# Start all emulator containers
docker compose --profile all up -dFor compose examples, device passthrough, Balena deployment, and local build options, see Usage below.
Docker Hub (Recommended):
- All images are published to Docker Hub
- Simple pull command:
docker pull blackoutsecure/retrostack:retroarch - Multi-arch support: amd64, arm64
- No registry prefix needed when pulling from Docker Hub
# Pull RetroArch (default)
docker pull blackoutsecure/retrostack:latest
docker pull blackoutsecure/retrostack:retroarch
# Pull PPSSPP
docker pull blackoutsecure/retrostack:ppsspp
# Pull Dolphin
docker pull blackoutsecure/retrostack:dolphin-emuRetroStack packages three upstream emulator projects into containerised runtimes. Each runs as an independent service — pick only the emulators you need. RetroArch is the recommended default — it handles the widest range of systems via libretro cores. Use PPSSPP or Dolphin only if you need dedicated PSP or GameCube/Wii support beyond what RetroArch provides.
| Tag | Emulator | Install Method | Upstream | License |
|---|---|---|---|---|
latest |
RetroArch + cores | PPA (ppa:libretro/stable) |
libretro/RetroArch | GPL-3.0 |
retroarch |
RetroArch + cores | PPA (ppa:libretro/stable) |
libretro/RetroArch | GPL-3.0 |
ppsspp |
PPSSPP (PSP) | Source build | hrydgard/ppsspp | GPL-2.0 |
dolphin-emu |
Dolphin (GC/Wii) | Source build | dolphin-emu/dolphin | GPL-2.0 |
All images use ghcr.io/linuxserver/baseimage-ubuntu:noble as the runtime base
(configurable via BASE_IMAGE* build args). Versions are tracked automatically
by upstream monitor workflows and injected at build time via --build-arg.
Upstream project details:
- RetroArch: retroarch.com · ppa:libretro/stable
- PPSSPP: ppsspp.org · hrydgard/ppsspp
- Dolphin: dolphin-emu.org · dolphin-emu/dolphin
This image is published as a multi-arch manifest. Pulling blackoutsecure/retrostack:latest retrieves the correct image for your host architecture.
The architectures supported by this image are:
| Architecture | Available Tags |
|---|---|
| x86-64 | latest, retroarch, ppsspp, dolphin-emu |
| arm64 | latest, retroarch, ppsspp, dolphin-emu |
Tag scheme:
| Variant | Rolling | Platform-Pinned | Emulator-Pinned | Commit-Pinned |
|---|---|---|---|---|
| RetroArch | latest, retroarch |
1.0.0, 1.0.0-retroarch |
retroarch-v1.22.2 |
retroarch-sha-<commit> |
| PPSSPP | ppsspp |
1.0.0-ppsspp |
ppsspp-v1.20.3 |
ppsspp-sha-<commit> |
| Dolphin | dolphin-emu |
1.0.0-dolphin-emu |
dolphin-emu-2509 |
dolphin-emu-sha-<commit> |
Docker Compose (recommended, click here for more info)
Run a single emulator:
Standalone mode (default — emulator launches its own GUI, seeds bundled demo ROM on first boot):
---
services:
retroarch:
image: blackoutsecure/retrostack:retroarch
container_name: retrostack-retroarch
environment:
DISPLAY: ':0'
PULSE_SERVER: 'unix:/run/pulse/native'
volumes:
- retrostack-config:/config
- retrostack-roms:/roms # writable so demo ROM can be seeded
- retrostack-bios:/bios:ro
- pulse-socket:/run/pulse:ro
devices:
- /dev/dri:/dev/dri
- /dev/input:/dev/input
- /dev/snd:/dev/snd
privileged: true
tmpfs:
- /var/tmp
- /run:exec
shm_size: 1gb
restart: unless-stopped
volumes:
retrostack-config:
retrostack-roms:
retrostack-bios:
pulse-socket:Tip: If you provide your own ROMs, you can mount
/romsread-only (retrostack-roms:/roms:ro). Demo ROM seeding is only attempted when the volume is empty, so:rowith existing content works without errors.
Daemon / integration mode (ES-DE or another frontend controls the emulator — ROMs are read-only):
---
services:
retroarch:
image: blackoutsecure/retrostack:retroarch
container_name: retrostack-retroarch
environment:
DISPLAY: ':0'
PULSE_SERVER: 'unix:/run/pulse/native'
RETROSTACK_FRONTEND_MODE: 'daemon'
volumes:
- retrostack-config:/config
- retrostack-roms:/roms:ro # read-only — frontend owns the ROMs
- retrostack-bios:/bios:ro
- retrostack-emulator-control:/run/retrostack-emulators
- pulse-socket:/run/pulse:ro
devices:
- /dev/dri:/dev/dri
- /dev/input:/dev/input
- /dev/snd:/dev/snd
privileged: true
tmpfs:
- /var/tmp
- /run:exec
shm_size: 1gb
restart: unless-stopped
volumes:
retrostack-config:
retrostack-roms:
retrostack-bios:
retrostack-emulator-control:
pulse-socket:Using profiles from the included docker-compose.yml:
# Start RetroArch only
docker compose --profile retroarch up -d
# Start all emulators
docker compose --profile all up -dDocker CLI (click here for more info)
Standalone game launch (container exits when done):
# Game Boy game with RetroArch + gambatte core
docker run --rm \
-e DISPLAY=:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v /path/to/roms:/roms:ro \
--device=/dev/dri:/dev/dri \
--device=/dev/input:/dev/input \
--device=/dev/snd:/dev/snd \
blackoutsecure/retrostack:retroarch \
--core gambatte /roms/gb/game.gb
# PSP game with PPSSPP
docker run --rm \
-e DISPLAY=:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v /path/to/roms:/roms:ro \
--device=/dev/dri:/dev/dri \
blackoutsecure/retrostack:ppsspp \
/roms/psp/game.iso
# GameCube game with Dolphin
docker run --rm \
-e DISPLAY=:0 \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v /path/to/roms:/roms:ro \
--device=/dev/dri:/dev/dri \
blackoutsecure/retrostack:dolphin-emu \
/roms/gc/game.isoDaemon mode (container waits for a launch command, then exits after the game ends):
docker run -d \
--name=retrostack-retroarch \
--restart unless-stopped \
-e DISPLAY=:0 \
-e PULSE_SERVER=unix:/run/pulse/native \
-e RETROSTACK_IDLE_TIMEOUT=600 \
-e RETROSTACK_FRONTEND_MODE=daemon \
-v retrostack-emulator-control:/run/retrostack-emulators \
-v retrostack-shared:/run/retrostack-shared:ro \
-v retrostack-config:/config \
-v retrostack-roms:/roms:ro \
-v retrostack-bios:/bios:ro \
-v x11-unix:/tmp/.X11-unix:ro \
-v pulse-socket:/run/pulse:ro \
--device=/dev/dri:/dev/dri \
--device=/dev/input:/dev/input \
--device=/dev/snd:/dev/snd \
--shm-size=1gb \
blackoutsecure/retrostack:retroarchThis image can be deployed to Balena-powered devices using the included docker-compose.yml file (Balena labels are included and harmlessly ignored by standard Docker).
- Block metadata: balena.yml
- Compose file: docker-compose.yml
balena push <your-app-slug>See Balena documentation for details.
When used with docker-emulationstation-de, both containers share a control volume and the same X11 display:
┌──────────────────────────────┐ ┌──────────────────────────┐
│ RetroStack │ │ emulationstation-de │
│ (this repo) │ │ (separate repo) │
│ │ │ │
│ Emulator binary stays here │ control pipe │ User selects game │
│ Listens on FIFO for launch │◀────────────────│ retrostack-emulator- │
│ commands, runs emulator on │ /run/retro*/ │ launch writes to FIFO │
│ shared X11 display │────────────────▶│ reads exit code back │
│ │ exit status │ │
└──────────────────────────────┘ └──────────────────────────┘
│ │
├── /dev/dri (GPU) ├── /dev/dri (GPU)
├── /dev/input (controllers) ├── /dev/input
├── /dev/snd (audio) ├── /dev/snd
└── X11 socket └── X11 socket
Both containers share a volume at /run/retrostack-emulators/. Each emulator creates:
| File | Direction | Purpose |
|---|---|---|
<name>.cmd |
ES-DE → Emulator | FIFO — write emulator args (one line, shell-quoted) |
<name>.status |
Emulator → ES-DE | FIFO — read exit code after game finishes |
- Startup: Emulator container creates FIFO pipes at
/run/retrostack-emulators/<name>.cmdand.status, then waits for a launch command (or times out afterRETROSTACK_IDLE_TIMEOUTseconds) - Discovery: ES-DE installs
retrostack-emulator-launchand symlinks each emulator name to it (e.g.retroarch → retrostack-emulator-launch) - Game launch: When the user selects a game, ES-DE calls the symlink.
retrostack-emulator-launchwrites the args to the.cmdpipe, the emulator container reads it and runs the game on the shared display - Return: When the game exits, the emulator container writes the exit code to the
.statuspipe.retrostack-emulator-launchreads it and returns, giving control back to ES-DE. The emulator container then stops.
volumes:
emulationstation-config:
emulationstation-roms:
emulationstation-bios:
retrostack-emulator-control:
retrostack-shared:
x11-unix:
pulse-socket:
services:
emulationstation:
image: blackoutsecure/emulationstation-de:latest
container_name: emulationstation
environment:
TZ: 'Etc/UTC'
DISPLAY_NUM: '0'
XDG_RUNTIME_DIR: '/run/esde'
ESDE_USE_INTERNAL_X: '1'
UDEV: '1'
volumes:
- emulationstation-config:/config
- emulationstation-roms:/roms:ro
- emulationstation-bios:/bios:ro
- retrostack-emulator-control:/run/retrostack-emulators
- x11-unix:/tmp/.X11-unix:ro
- pulse-socket:/run/pulse:ro
devices:
- /dev/dri:/dev/dri
- /dev/input:/dev/input
- /dev/snd:/dev/snd
privileged: true
shm_size: 1gb
restart: unless-stopped
retroarch:
image: blackoutsecure/retrostack:retroarch
container_name: retrostack-retroarch
environment:
DISPLAY: ':0'
PULSE_SERVER: 'unix:/run/pulse/native'
RETROSTACK_FRONTEND_MODE: 'daemon'
volumes:
- retrostack-emulator-control:/run/retrostack-emulators
- retrostack-shared:/run/retrostack-shared:ro
- emulationstation-roms:/roms:ro
- emulationstation-bios:/bios:ro
- x11-unix:/tmp/.X11-unix:ro
- pulse-socket:/run/pulse:ro
devices:
- /dev/dri:/dev/dri
- /dev/input:/dev/input
- /dev/snd:/dev/snd
shm_size: 1gb
restart: unless-stoppedInstall retrostack-emulator-launch in the ES-DE container and create symlinks for each emulator:
# Copy the launch script from the RetroStack image
docker cp retrostack-retroarch:/usr/local/bin/retrostack-emulator-launch /usr/local/bin/
# Create symlinks — ES-DE calls these by name
ln -sf /usr/local/bin/retrostack-emulator-launch /usr/local/bin/retroarch
ln -sf /usr/local/bin/retrostack-emulator-launch /usr/local/bin/PPSSPPSDL
ln -sf /usr/local/bin/retrostack-emulator-launch /usr/local/bin/dolphin-emu[retrostack:retroarch] Daemon mode — version: RetroArch 1.22.2 (Git ...)
[retrostack:retroarch] Available cores: 6
[retrostack:retroarch] Control pipe: /run/retrostack-emulators/retroarch.cmd
[retrostack:retroarch] Idle timeout: 600s
[retrostack:retroarch] Ready — waiting for launch commands from frontend (e.g. ES-DE)
After a game finishes:
[retrostack:retroarch] Launch: --core gambatte /roms/gb/game.gb
[retrostack:retroarch] Exited: 0
[retrostack:retroarch] Emulator process ended — stopping container.
If no launch command is received within the idle timeout:
[retrostack:retroarch] Idle timeout (600s) reached — no launch commands received. Exiting.
| Parameter | Description | Required |
|---|---|---|
-e EMULATOR_NAME |
Emulator identifier (set in image — retroarch, ppsspp, dolphin-emu) |
Set per target |
-e EMULATOR_BINARY |
Path to emulator binary | Set per target |
-e EMULATOR_CORE |
Default libretro core for RetroArch | Optional |
-e DISPLAY=:0 |
X11 display | Optional |
-e PULSE_SERVER |
PulseAudio server path | Optional |
-e XDG_RUNTIME_DIR |
Runtime directory for display/session (default: /run/retrostack) |
Optional |
-e RETROSTACK_EMULATORS_CONTROL |
Control pipe directory (client-side) | Optional |
-e RETROSTACK_IDLE_TIMEOUT |
Seconds to wait for a launch command before the container exits (default: 600, set to 0 to disable) |
Optional |
-e RETROSTACK_FRONTEND_MODE |
standalone (default) launches the emulator's own GUI; daemon listens on FIFO for ES-DE integration |
Optional |
-e RETROSTACK_USE_INTERNAL_X |
Start an internal Xorg server in standalone mode (default: 1). Set to 0 to use an external X socket |
Optional |
| Mount | Description | Required |
|---|---|---|
retrostack-config:/config |
Persistent emulator settings, saves, and states | Recommended |
retrostack-roms:/roms |
ROM library — writable by default for demo ROM seeding on first boot; can use :ro if you supply your own ROMs or in daemon/integration mode |
Recommended |
retrostack-bios:/bios:ro |
BIOS files for emulators that need them | Optional |
pulse-socket:/run/pulse:ro |
PulseAudio socket | Optional |
/tmp/.X11-unix:/tmp/.X11-unix:ro |
X11 socket (only when RETROSTACK_USE_INTERNAL_X=0) |
Conditional |
retrostack-emulator-control:/run/retrostack-emulators |
FIFO control pipe volume (daemon mode / ES-DE only) | Daemon only |
retrostack-shared:/run/retrostack-shared:ro |
Shared runtime — gamepad mappings, Xauthority (ES-DE only) | Daemon only |
| Device | Description | Required |
|---|---|---|
--device=/dev/dri:/dev/dri |
GPU passthrough for Intel/AMD rendering | Optional |
--device=/dev/input:/dev/input |
Gamepad and input passthrough | Optional |
--device=/dev/snd:/dev/snd |
Audio device passthrough | Optional |
| Setting | Value | Purpose |
|---|---|---|
read_only |
false |
Keep root filesystem writable for LSIO init ownership setup |
tmpfs /var/tmp /run |
writable | Writable runtime scratch paths |
shm_size |
1gb |
Shared memory for SDL and rendering stability |
The container stores persistent emulator data under /config/<emulator-name>/.
- Required: No, but recommended if you want settings and saves to survive restarts
- Purpose: Stores emulator configuration, save games, save states, and logs
- Example: Named volume
retrostack-config:/config
- Required: Recommended
- Purpose: ROM library for the emulator to browse and play
- Default (writable): On first boot, if the volume is empty, the bundled demo ROM (Libbet and the Magic Floor) is automatically seeded so you can start playing immediately
- Read-only (
:ro): Safe to use when you supply your own ROMs — seeding is skipped when content already exists. Also recommended for daemon/integration mode where the frontend (e.g. ES-DE) owns the ROM library
- Required: Optional
- Purpose: Supply BIOS files used by emulator backends that need them
- Example: Named volume
retrostack-bios:/bios:ro
- Keep
/configpersistent so emulator saves and settings survive container recreation - Leave
/romswritable for the default out-of-box experience — the bundled demo ROM is seeded on first boot when the volume is empty - Mount
/romsread-only (:ro) if you supply your own ROMs or in daemon/integration mode — seeding is safely skipped when content already exists - Mount
/biosread-only unless you have a specific reason to allow writes - Use the same ROM and BIOS volume mounts as your ES-DE container when using ES-DE integration
- Add a
FROM ... AS <name>stage to theDockerfile(with a builder stage if compiling from source). - Set
ENV EMULATOR_NAME=<name>,ENV EMULATOR_BINARY=/path/to/binary, andENV DISPLAY=:0. - Add a service in
docker-compose.ymlwith the control volume and GPU/input/display mounts. - Add a matrix entry in
publish.yml(docker + manifest jobs) andupstream-monitor.yml. - On the ES-DE side, symlink
retrostack-emulator-launchas the emulator name.
Project layout:
| Path | Purpose |
|---|---|
root/usr/local/lib/retrostack-lib.sh |
Shared functions and constants (sourced by all scripts) |
root/usr/local/bin/retrostack-emulator-run |
Container entrypoint (daemon + standalone modes) |
root/usr/local/bin/retrostack-emulator-launch |
Client-side FIFO launcher (installed in ES-DE container) |
root/usr/local/bin/retrostack-provision |
Export emulator binary + libs to shared volume |
root/etc/s6-overlay/s6-rc.d/ |
s6-overlay service definitions |
VERSION |
RetroStack platform version (semver) |
.github/upstream/*.json |
Tracked upstream emulator versions |
# Build all emulators
docker compose --profile all build
# Build a single emulator
docker build --target retroarch -t blackoutsecure/retrostack:retroarch .
docker build --target ppsspp -t blackoutsecure/retrostack:ppsspp .
docker build --target dolphin-emu -t blackoutsecure/retrostack:dolphin-emu .
# Override a tracked version
docker build --build-arg PPSSPP_VERSION=v1.20.3 --target ppsspp .
docker build --build-arg RETROARCH_VERSION=1.22.2 --target retroarch .- Verify the emulator container is running:
docker ps | grep retrostack- - Check container logs:
docker logs retrostack-retroarch - Ensure
/dev/driis passed through for GPU access - Verify
/tmp/.X11-unixis mounted for X11 display
- Ensure the
retrostack-emulator-controlvolume is shared between ES-DE and the emulator container - Check that the emulator container started successfully and created the FIFO pipes
- Look for
[retrostack] ERROR: pipe not foundin ES-DE logs - Start the emulator container:
docker compose --profile retroarch up -d
- Ensure
/dev/sndis passed through orPULSE_SERVERis set - PulseAudio socket must be accessible at
/run/pulse/native
- Ensure the container has access to
/dev/input - Use
privileged: truefor full device access in kiosk/cabinet setups
Each emulator image bundles the SDL_GameControllerDB community database (~3000 known gamepads). When running with ES-DE, the shared gamepad DB from the ES-DE container takes priority.
Mapping priority (highest first):
| Priority | Source | Description |
|---|---|---|
| 1 | SDL_GAMECONTROLLERCONFIG env var |
User manual overrides |
| 2 | Shared DB from ES-DE (/run/retrostack-shared/gamecontrollerdb.txt) |
ES-DE sidecar mappings |
| 3 | Bundled community gamecontrollerdb.txt |
~3000 known gamepads |
| 4 | SDL2 built-in DB | Major brand controllers (Xbox, PlayStation, Switch) |
If your gamepad isn't recognized:
- Find your gamepad's SDL2 GUID — run
sdl2-jstest --listinside the container - Generate a correct mapping at SDL_GameControllerDB or General Arcade Gamepad Tool
- Set the mapping in your compose environment:
environment:
SDL_GAMECONTROLLERCONFIG: "03000000790000001100000000000000,DragonRise Generic USB Joystick,a:b2,b:b1,..."A GitHub Actions workflow monitors all three emulator upstreams every 6 hours:
| Emulator | Monitors | Rebuilds |
|---|---|---|
| RetroArch | libretro/RetroArch releases + PPA version |
retroarch target |
| PPSSPP | hrydgard/ppsspp releases |
ppsspp target |
| Dolphin | dolphin-emu/dolphin releases/tags |
dolphin-emu target |
Tracked versions are stored in .github/upstream/*.json and read by the publish/release workflows.
RetroStack uses a dual-version scheme: a platform version for the packaging/scripts and independent emulator versions tracked from upstream.
The RetroStack platform version (packaging, scripts, s6 services, CI) is tracked in the VERSION file at the repo root and follows Semantic Versioning:
- Major: Breaking changes to the control pipe protocol, volume layout, or environment interface
- Minor: New emulator targets, new features, non-breaking config changes
- Patch: Bug fixes, dependency updates, documentation
Current version: 1.0.0
Each emulator tracks its own upstream release independently. Versions are stored in .github/upstream/*.json and resolved at build time:
| Emulator | Version Source | Tracked File |
|---|---|---|
| RetroArch | libretro/RetroArch releases + PPA | .github/upstream/retroarch-release.json |
| PPSSPP | hrydgard/ppsspp releases | .github/upstream/ppsspp-release.json |
| Dolphin | dolphin-emu/dolphin releases | .github/upstream/dolphin-release.json |
| Tag Pattern | Example | Description |
|---|---|---|
:latest |
:latest |
Rolling latest (RetroArch) |
:<target> |
:retroarch |
Rolling latest for emulator |
:<target>-<emu-version> |
:retroarch-v1.22.2 |
Pinned to emulator version |
:<rs-version>-<target> |
:1.0.0-retroarch |
Pinned to RetroStack platform version |
:<rs-version> |
:1.0.0 |
Platform-pinned (RetroArch default) |
:<target>-sha-<commit> |
:retroarch-sha-abc123 |
Commit-pinned |
Each image includes OCI and RetroStack-specific labels:
| Label | Value |
|---|---|
org.opencontainers.image.version |
RetroStack platform version |
org.opencontainers.image.vendor |
Blackout Secure |
io.retrostack.version |
RetroStack platform version |
io.retrostack.emulator |
Emulator name (retroarch, ppsspp, dolphin-emu) |
io.retrostack.emulator.version |
Upstream emulator version |
- Stable Docker and Balena block publishing: .github/workflows/publish.yml
- GitHub release publishing: .github/workflows/release.yml
- Upstream emulator release monitoring (every 6 hours): .github/workflows/upstream-monitor.yml
- Version resolution: .github/actions/resolve-versions/action.yml
- GitHub repository: blackoutsecure/docker-retrostack
- Docker Hub image: blackoutsecure/retrostack
- ES-DE frontend container: blackoutsecure/docker-emulationstation-de
- docker-emulationstation-de — ES-DE frontend container
- retroarch.com · ppa:libretro/stable
- ppsspp.org · dolphin-emu.org
- ES-DE: https://es-de.org
- ES-DE source: https://gitlab.com/es-de/emulationstation-de
- ES-DE user guide: https://gitlab.com/es-de/emulationstation-de/-/blob/master/USERGUIDE.md
- LSIO Ubuntu base image: https://docs.linuxserver.io/images/docker-baseimage-ubuntu/
- SDL_GameControllerDB (community gamepad mappings): https://github.com/gabomdq/SDL_GameControllerDB
