LBA2 Classic Community
June 3, 2026 · View on GitHub
How to build, package, and run LBA2 Classic Community on Android
(arm64-v8a and armeabi-v7a). The build and packaging scripts live under
scripts/dev/ and scripts/packaging/.
Quick start
Minimum Android version: 7.0 (API 24). The APK targets API 24 (required by SDL3) and is tested on API 24–36.
You need:
- Android NDK r28 or later (recommended: r28.2.13676358) — required for 16 KB memory-page support on arm64 (see 16 KB page support).
- SDL3 built for your target ABI(s).
- Ninja build system.
- Retail game data (HQR files) — you must provide these.
1. Build SDL3 for Android
# arm64-v8a: clones SDL (release-3.2.16) to out/android/SDL3, installs to
# out/android/sdl3-install, and applies the 16 KB-page linker flags.
bash scripts/dev/build-sdl3-android.sh
The source tree it leaves under out/android/SDL3 is also the
--sdl3-java-src the bundler needs for the SDLActivity Java sources. For
armeabi-v7a, configure SDL3 by hand with -DANDROID_ABI=armeabi-v7a
(the script targets arm64-v8a).
2. Build LBA2
# From the repo root, point SDL3_ANDROID_DIR at the install from step 1
export ANDROID_NDK=$HOME/Android/Sdk/ndk/28.2.13676358
export SDL3_ANDROID_DIR=$PWD/out/android/sdl3-install
bash scripts/dev/build-android.sh # arm64-v8a (default)
bash scripts/dev/build-android.sh --abi armeabi-v7a # 32-bit
3. Install on device
The build script produces out/build/android_arm64/SOURCES/liblba2cc.so
(or armeabi-v7a for 32-bit builds). To package into an APK, use the
bundler script:
bash scripts/packaging/bundle-android.sh \
--lib out/build/android_arm64/SOURCES/liblba2cc.so \
--version "$(cat out/build/android_arm64/VERSION.txt)" \
--arch arm64-v8a \
--build-dir out/build/android_arm64 \
--sdk-root $ANDROID_HOME \
--sdl3-java-src out/android/SDL3 \
--sdl3-lib $SDL3_ANDROID_DIR/lib/libSDL3.so \
--cxx-shared-lib $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so \
--output-dir dist
# Install
adb install -r dist/lba2cc-*-android-arm64-v8a.apk
--sdl3-lib is required — without it libSDL3.so is omitted from the APK
and the app fails to load. --cxx-shared-lib points the bundler at the
NDK's libc++_shared.so; CI passes both explicitly.
16 KB page support
Android 15+ runs 64-bit apps on 16 KB memory pages on some devices. A
native library whose LOAD segments are 4 KB-aligned fails to load there
(... program alignment (4096) cannot be smaller than system page size (16384)). The arm64-v8a build is 16 KB-safe:
- NDK r28 ships
libc++_shared.so16 KB-aligned and defaults the linkermax-page-sizeto 16384. r26 cannot be fixed by flags alone — its prebuilt STL is 4 KB-aligned. -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384, set in theandroid_commonpreset and the build scripts, keepliblba2cc.soandlibSDL3.soaligned even on r27.- The APK stores the
.souncompressed and 16 KB-zipaligned withextractNativeLibs="false", so the loader maps them straight from the APK. scripts/dev/check-16k-align.sh <apk>verifies all of the above; CI runs it on every arm64 build.
Backward-compatible: max-page-size sets a maximum, so a 4 KB-page
kernel loads the libraries normally. armeabi-v7a is unaffected — 16 KB
pages are a 64-bit concern; 32-bit ARM (older devices, all current
Android TV) is always 4 KB.
Game data on Android
Place your retail LBA2 .HQR files on the device with ADB. Recommended:
/sdcard/lba2cc/ — the game probes it ahead of the other external roots and
it survives an uninstall/reinstall:
adb push lba2.hqr /sdcard/lba2cc/
adb push ress.hqr /sdcard/lba2cc/
# ...and the rest of your retail HQR set
/sdcard/lba2cc/ is general external storage, so on Android 11+ (API 30+)
the app needs All Files Access (MANAGE_EXTERNAL_STORAGE). The game checks
for it on launch and opens the system Settings page if it is missing — grant it
and relaunch. You can also pre-grant it:
adb shell appops set org.lbalab.lba2cc MANAGE_EXTERNAL_STORAGE allow
The app-specific dir /sdcard/Android/data/org.lbalab.lba2cc/files/ needs
no permission, but on stricter API 30+ / TV images files adb pushed there can
be invisible to the app's own view, so prefer /sdcard/lba2cc/.
ResolveGameDataDir (SOURCES/RES_DISCOVERY.CPP) scans, in order:
--game-dir/--data-dirCLI flagLBA2_GAME_DIRenvironment variable- Persisted last-used directory (from a previous picker session)
- SDL base path and its
data//game/subdirs - Current running directory
- App-specific external-files dir (Android; no permission) —
/sdcard/Android/data/<pkg>/files/ /sdcard/lba2cc/and/storage/emulated/0/lba2cc/, then/sdcard/and/storage/emulated/0/- Parent-directory walk
- Folder picker fallback (if compiled with debug tools)
Key mapping (touch overlay)
The on-screen virtual gamepad uses the following layout:
+--(ESC)---------(HIDE)---------(MEN)(CAM)-+
| |
| |
| |
| |
| (U) (MD) (ACT) (USE) |
| (L) (D) (R) (THR) (INV) (W) |
+-------------------------------------------+
- ESC — Escape (quit/back)
- HIDE — Show or hide the overlay (persistent pill when hidden)
- MEN — Menu (F10 / in-game menu)
- CAM — Camera toggle (Backspace)
- D-pad — U/D/L/R → Arrow keys (movement)
- MD — Mode (Ctrl / behaviour mode)
- ACT — Action (Space / jump/interact)
- USE — Use/confirm (Enter)
- THR — Throw (Alt)
- INV — Inventory (Shift)
- W — Action (always-on action key, "W" for walk/run)
The layout is defined in SOURCES/TOUCH_INPUT.CPP and can be customised
by editing the kButtons[] table (normalised 0..1 coordinates).
Limitations
- Software renderer: The game uses a software 8-bit→ARGB pipeline. Performance on modern ARM64 devices is acceptable at 640x480. On older 32-bit ARM (armeabi-v7a) devices, expect lower framerates.
- CD audio: CD-ROM music tracks are not available. Use the digital
sample backend (
SOUND_BACKEND=sdl). - Text input: Console commands still require a hardware keyboard. Save-game naming no longer does — without a physical keyboard (touch, gamepad, or Android TV) the save menu auto-generates a name from the date and the current island instead of prompting.
- Save/resume: Android lifecycle pause/resume is handled by SDL3 (window focus events). Surface re-creation is managed by the existing SDL3 infrastructure.
- Game data: You must provide your own retail LBA2 data files.
- Android TV: Touch overlay is automatically disabled when a TV device
is detected (
android.software.leanbackfeature). Use a gamepad or remote control instead.