SDK (Software Dev. Kit)
Build and deploy community External Applications for compatible AtGames arcade devices using C++, SDL2, and Docker.
Overview
The AtGames External Application SDK lets community developers build games and utilities that run directly on compatible AtGames arcade devices.
External Applications are standard Linux executables compiled for the device's
aarch64 processor. The SDK handles cross-compilation inside Docker so you
never need to install a legacy Linux toolchain on your own machine.
The SDK ships two fully playable sample games, Brick Breaker and Skyfall, plus the Arcade Test diagnostic application. Together they demonstrate controls, audio, local leaderboards, HTTPS access, and multi-screen support.
Available to everyone
External Applications are now available to everyone and no longer require
enrollment in the beta program. Update your device to the latest standard
firmware to use the current features. System backglass leaderboards and
developer logging are available on Legends 4KP firmware 6.1.30, 6.0.34B,
and 6.1.30D, and HDP firmware 1.0.30.
Requirements
| Requirement | Notes |
|---|---|
| Docker | The entire build runs inside a container — required |
| C++ (SDL2) | The included helpers and samples use C++ and SDL2 |
| A USB drive | Deployment means copying a folder to USB and plugging it in |
On Windows, use the included run.bat script or run .sh scripts via Git Bash
or WSL.
How It Works
The build system cross-compiles your C++ source code inside a Docker container
running Ubuntu 16.04. This ensures the output binary targets aarch64 Linux and
stays compatible with the firmware's glibc 2.26 userspace. Binaries that depend
on newer symbols will not run correctly on the device.
When the build finishes, the ready-to-deploy files appear in dist/external/.
Copy the dist/ contents to the root of a USB drive and plug it in. The device
scans the USB automatically and shows your app under External Applications.
USB Layout
Each application lives in its own folder under external/ at the USB root:
external/
└── my-game/
├── my-game.elf
├── my-game.png
├── my-game.xml
├── data/
│ └── leaderboard.json
├── logs/
│ └── output.txt
└── res/
└── audio/| File | Description |
|---|---|
my-game.elf | Compiled binary |
my-game.png | Icon shown in the arcade menu |
my-game.xml | Metadata (name, developer, description…) |
data/leaderboard.json | Local scores created by the leaderboard helper |
logs/output.txt | Optional firmware-managed stdout and stderr log |
res/audio/ | Optional audio assets |
The .elf, .png, and .xml files must all share the same base name and sit
directly in the app folder. The SDK build writes this layout automatically. You
only need to copy dist/ to the USB root.
App Metadata (XML)
The XML file provides the name and optional information shown by the firmware:
<root>
<name>My Game</name>
<developer>Studio Name</developer>
<category>Arcade</category>
<year>2026</year>
<copyright>Copyright 2026 Studio Name</copyright>
<description>A short description shown on the arcade display.</description>
<logging>true</logging>
</root>Only <name> is required. When at least one optional element is present, the
firmware shows an About button next to Launch with the full metadata.
The optional <logging> element controls firmware-managed application logs.
Logging is disabled when the element is omitted. Use true or 1 to enable
it; use false, 0, or omit the element to disable it. The firmware derives
the destination from the application folder, so the XML cannot select an
arbitrary log path.
Developer Logs
When logging is enabled (configured in the XML), the firmware appends both standard output and standard error to:
/media/usb0/external/<app-id>/logs/output.txtThe same absolute path is passed to the application through
ATGAMES_LOG_PATH. This variable is only present when logging is enabled.
Applications do not need to open the file themselves. Any diagnostic output
sent to stdout or stderr is captured automatically by the firmware. When
output.txt has reached 5 MiB at the next launch, it is moved to
output.txt.previous and a new log is started. Only one previous log is
retained.
Quick Start
Build the included Brick Breaker sample:
./run.sh brick-breaker-appOn Windows:
run.bat brick-breaker-appThe first run builds the Docker image automatically. Subsequent builds reuse it.
The output lands in dist/external/brick-breaker-app/. Copy dist/ to a USB
drive and the app appears on the device under External Applications.
Runtime Environment
The firmware passes these environment variables to External Applications at launch:
| Variable | Description |
|---|---|
ATGAMES_USERNAME | Account display name, or Legends ID as fallback. Empty for guests. |
ATGAMES_UNIQUE_ID | Stable per-device identifier derived from hardware. |
ATGAMES_DEVICE_TYPE_ID | Device model code, e.g. HA9920. |
ATGAMES_DEVICE_TYPE_NAME | Product family name, e.g. Legends Ultimate. |
ATGAMES_FIRMWARE_VERSION | Installed firmware version string. |
ATGAMES_LOG_PATH | Absolute log file path. Only present when <logging> is enabled. |
SDK Modules
Controls
sdk/controls/Controls.h provides a device-independent input interface.
Instead of handling raw joystick button numbers, your game receives high-level
actions that work across supported controls:
#include "Controls.h"
AtGames::Controls controls;
controls.open();
SDL_Event event;
while (SDL_PollEvent(&event)) {
const AtGames::ControlEvent action = controls.event(event);
switch (action) {
case AtGames::ControlEvent::DpadLeft:
// Navigate to the previous menu item.
moveMenuLeft();
break;
case AtGames::ControlEvent::A:
// Confirm the currently selected item.
confirmSelection();
break;
case AtGames::ControlEvent::Back:
// Return to the previous screen or menu.
returnToPreviousScreen();
break;
default:
break;
}
}
const float horizontal = controls.horizontal();Call controls.open() after initializing SDL. Use controls.event() for
button actions and controls.horizontal() for continuous left and right
movement. The helper supports keyboard input during desktop development and
cabinet controls on Legends hardware.
Display Topology and Secondary Screens
sdk/displays/ detects optional screens such as the Backglass, DMD, and
so an application can display its own content on them.
#include "displays/DisplayTopology.h"
const AtGames::DisplayTopology topology = AtGames::detectDisplayTopology();
if (topology.backglass.available) {
// The device has a backglass available for custom content.
}Always check available before using a secondary screen.
HTTPS Requests
sdk/http/HttpRequest.h provides a small synchronous HTTPS GET helper using the
firmware's curl binary and CA certificate bundle:
#include "http/HttpRequest.h"
const std::string response = AtGames::httpGet("https://example.com/data.json");
if (response.empty()) {
// Handle network, certificate, timeout, and HTTP errors.
}The call blocks until it completes or reaches its timeout, so avoid calling it from a time-sensitive render loop.
Leaderboard
sdk/leaderboard/ provides local score storage saved directly to the USB drive,
with an optional full-screen SDL2 overlay. The device firmware can also display
the same local scores on the system backglass.
// The ID must match the app folder name on the USB drive
AtGames::Leaderboard leaderboard("my-game");
// Submit a score at the end of a run
leaderboard.submitScore(score);
// Read the top 10 scores
auto top = leaderboard.topScores(10);Scores persist as JSON at external/my-game/data/leaderboard.json on the USB
drive. If the USB is not writable, scores stay in memory for the current session
and the game keeps running normally.
System Backglass Leaderboard
On a device with a backglass, the system reads the local
data/leaderboard.json file when the application is selected in the External
Applications menu. After the application exits, the menu refreshes and reads
the latest saved scores automatically. The application does not need to render
to the backglass or make a network request.
The SDK leaderboard helper already writes the expected format:
{
"scores": [
{
"name": "AAA",
"accountName": "Player One",
"score": 125000,
"createdAt": 1785542400
}
]
}| Field | Required | Description |
|---|---|---|
name | Yes | Primary player name or initials. |
score | Yes | Integer score used for ranking. |
accountName | No | Secondary account name shown below name. |
createdAt | No | Unix timestamp in seconds. Earlier entries win tied scores. |
version | No | Root-level format version. When present, it must be 1. |
The system accepts files up to 64 KiB, parses up to 100 entries, sorts them by score from highest to lowest, and displays the top 10.
The leaderboard is fully developer-managed and local to the USB drive. It is not uploaded to or synchronized with AtGames services.
Optional In-Game Overlay
The SDK overlay is separate from the system backglass. It renders a full-screen leaderboard inside the application and is embedded directly in the binary:
AtGames::LeaderboardOverlay overlay(leaderboard);
overlay.open();
overlay.render(renderer, logicalWidth, logicalHeight);
overlay.close();Compatibility Check
Every build automatically verifies the output binary for glibc symbol requirements. The maximum supported version is glibc 2.26 — binaries that depend on newer symbols will crash on the device.
You can also run the check manually:
sdk/check-compat.sh dist/external/my-game/my-game.elfSDK Structure
SDK/
├── run.sh / run.bat
├── apps/
│ ├── brick-breaker-app/
│ ├── skyfall-app/
│ └── arcade-test-app/
├── dist/external/
├── sdk/
│ ├── AtGamesHelpers.h
│ ├── controls/
│ ├── displays/
│ ├── http/
│ ├── leaderboard/
│ └── check-compat.sh| Path | Description |
|---|---|
run.sh / run.bat | Entry point for all builds |
apps/brick-breaker-app/ | Sample: Brick Breaker (portrait SDL2) |
apps/skyfall-app/ | Sample: Skyfall (portrait SDL2) |
apps/arcade-test-app/ | Diagnostic application for controls, displays, HTTPS, and permissions |
dist/external/ | Build output — copy to USB root |
sdk/AtGamesHelpers.h | Helpers for firmware-provided environment values |
sdk/controls/ | Shared arcade input helper |
sdk/displays/ | Display topology and GPU-accelerated secondary-screen helpers |
sdk/http/ | Synchronous HTTPS request helper |
sdk/leaderboard/ | Shared local leaderboard + overlay |
sdk/check-compat.sh | glibc symbol compatibility check |