Game data (retail files)
June 28, 2026 · View on GitHub
The engine needs the original Little Big Adventure 2 data files. They are not included in this repository (licensing). You must own a legitimate copy (e.g. GOG, Steam).
What to point the engine at
Use the directory that contains lba2.hqr (and the other .hqr files, music/, video/, vox/, etc.). On Steam, classic data may live under a Classic-related install path; remastered or other editions may use a different layout — this fork targets classic data only.
Validation: discovery and overrides only succeed when the chosen path is a directory that contains lba2.hqr (that is the required marker; see IsValidResourceDir / FILE_VALID_RES_DIR in SOURCES/DIRECTORIES.CPP). The filename is matched with the same case-folding logic as other asset files.
Single asset root: Once that directory is chosen, GetResPath, GetJinglePath, GetMoviePath, and related APIs resolve files relative to that same directoriesResDir (see InitDirectories / GetResPath in SOURCES/DIRECTORIES.CPP). The engine does not look for music/, video/, vox/, or other .hqr files on separate paths; finding lba2.hqr establishes the one root for classic data.
Sibling scan: If the fixed paths above fail, the engine looks at siblings of the parent of the current working directory (for example the folder that contains both your clone and a retail install). For each immediate subdirectory, it tries that folder and CommonClassic, common, Common, and Classic under it. Only bounded scans (entry and directory-count limits); see TryParentSiblingScan in SOURCES/RES_DISCOVERY.CPP.
Overrides (highest priority first)
| Mechanism | Example |
|---|---|
| Command line | ./lba2cc --game-dir /path/to/game or --data-dir (alias) |
| Environment | export LBA2_GAME_DIR=/path/to/game |
| Persisted choice | last_game_dir.txt in the user-prefs folder, written automatically the first time you pick a folder via the launch dialog (see First-launch folder picker below) |
| Discovery | See SOURCES/RES_DISCOVERY.CPP: SDL binary directory, current working directory, parents of cwd, ./data, ../LBA2, ../game, sibling scan of parent-of-cwd |
If none of the above resolves a valid directory, the engine falls back to the First-launch folder picker. On a headless system (no display, CI runner, etc.), the picker can't open and the process exits with a log listing candidates tried and hints.
First-launch folder picker
When all four override mechanisms above fail and the engine is running in a windowed environment, it shows the platform-native folder dialog (NSOpenPanel on macOS, IFileDialog on Windows, GTK / xdg-desktop-portal on Linux). The user picks the folder containing lba2.hqr; the engine validates it via IsValidResourceDir, persists the choice to last_game_dir.txt, and continues startup.
Android is the exception: there is no folder picker (SDL's file dialog returns content:// document URIs, not filesystem paths the engine can probe). When discovery fails there, the engine shows a message telling the user to copy their data to /sdcard/lba2cc/ and relaunch, instead of opening a dialog. See docs/ANDROID.md for the full Android data layout.
| Scenario | Behavior |
|---|---|
User picks a valid folder (contains lba2.hqr) | Engine launches; last_game_dir.txt updated; subsequent launches skip the dialog. |
| User picks an invalid folder | Engine shows a "Game data not found" message and re-opens the dialog. |
| User cancels the dialog | Engine exits cleanly (same exit code as the headless no-data case). |
| Persisted path no longer valid (game install moved/deleted) | Engine silently falls through to auto-discovery, then the picker. The next valid pick rewrites last_game_dir.txt. |
| Headless environment (no display, CI, dummy video driver) | Picker can't open; engine exits with the existing "no game data" error and the candidate path list. |
| Linux/WSL with no dialog backend installed | The display works (so the engine shows the "Game data folder not found" message), but SDL_ShowOpenFolderDialog reports "File dialog driver unsupported" because neither zenity nor xdg-desktop-portal is available. Engine shows a follow-up message-box with recovery hints and exits. See Picker backends per environment below for the right install command, or use --game-dir / LBA2_GAME_DIR to skip the picker. |
The persisted path lives at <SDL_GetPrefPath("Twinsen", "LBA2")>/last_game_dir.txt — typically:
| Platform | Path |
|---|---|
| Linux | ~/.local/share/Twinsen/LBA2/last_game_dir.txt (honors XDG_DATA_HOME) |
| macOS | ~/Library/Application Support/Twinsen/LBA2/last_game_dir.txt |
| Windows | %APPDATA%\Twinsen\LBA2\last_game_dir.txt |
It's a single-line text file (the absolute path to the chosen game-data folder). Safe to delete by hand to force the picker to re-appear on next launch — the engine treats a missing file as "never picked," same as a fresh install.
Forcing the picker without deleting the file
Pass --pick-game-dir on the command line. The engine skips the persisted-LastGameDir probe and the auto-discovery chain, going straight to the picker for this run only. Use cases:
- Test the first-launch UX repeatedly without
rm-ing the persisted file each time. - Switch to a different LBA2 install without editing
last_game_dir.txtby hand. - Anyone whose persisted or auto-discovered path resolves to the wrong place and wants a UI re-do.
A successful pick rewrites last_game_dir.txt with the new path (acts as a "switch install" UX). A cancelled pick changes nothing — your previous setting stays intact.
--game-dir and LBA2_GAME_DIR still win against --pick-game-dir if both are set on the same launch (explicit path beats "show the picker"). Useful for lba2cc --game-dir /path/to/install overriding a misbehaving persisted setting one-shot, or --pick-game-dir for the "let me browse" UX.
Picker backends per environment
SDL3 implements the Linux folder picker via one of two backends — zenity (GTK-based, simple) or xdg-desktop-portal (Freedesktop portal protocol, integrates with the desktop environment). SDL3's hint documentation says it tries "all available dialog backends in a reasonable order" without committing to one — install whichever fits your environment.
| Environment | Recommended install | Notes |
|---|---|---|
| WSL Ubuntu / Debian | sudo apt install zenity | One package, works immediately. Portal would need extra D-Bus session bus setup that is fragile inside WSL. |
| Arch + KDE Plasma | pacman -S xdg-desktop-portal xdg-desktop-portal-kde | Native Plasma-styled file dialog. |
| Arch + GNOME | pacman -S xdg-desktop-portal xdg-desktop-portal-gnome (often pre-installed) | Native GNOME dialog. xdg-desktop-portal-gtk is an equivalent fallback. |
| Arch + Hyprland | pacman -S xdg-desktop-portal-hyprland xdg-desktop-portal-gtk | XDPH itself does not provide a file picker; the Hyprland wiki recommends installing -gtk alongside it. Don't add -kde or -gnome — mixing portal implementations on Hyprland causes D-Bus timeouts and breaks every app that uses portals. |
| sway / minimal Wayland | xdg-desktop-portal-gtk (or zenity if you have XWayland) | GTK portal is the cleaner Wayland answer. |
| i3 / minimal X11 | zenity | Simplest universal fallback. |
If you want to force a specific backend even when both are installed, set SDL_FILE_DIALOG_DRIVER=zenity (or =portal) in the environment before launching. Useful for debugging which backend's misbehaving on a particular setup.
Explicit path (recommended for anything non-local): Use --game-dir or LBA2_GAME_DIR when the install is not next to your working tree (another drive, ~/Games/…, CI, etc.). That is the stable, portable option.
What “adjacent” means in discovery: Heuristics only look at predictable relative locations (cwd, parents of cwd, ../LBA2, ./data, SDL binary dir, then siblings of the parent of cwd with a small set of subfolder names). There is no full-disk or deep recursive search. “Adjacent” here means often the same parent folder as your clone when you run from the repo root — not “any path on the machine.”
Safety: Every candidate is accepted only if IsValidResourceDir succeeds (lba2.hqr present). There is no execution of paths from untrusted config beyond what you pass on the command line or in the environment.
Developer layout
Common choices:
- Symlink or copy retail files into
data/at the repo root (that directory is gitignored in this repo so retail files are not committed by mistake). - One directory up:
../LBA2/(or../game/) withlba2.hqrinside.
From any directory in the clone you can use:
./scripts/dev/build-and-run.sh
or make run (see Makefile). Set LBA2_BUILD_DIR if you do not use build/.
Windows without Bash: configure CMake as in WINDOWS.md, then run lba2cc.exe with --game-dir or set LBA2_GAME_DIR in the environment.
GOG DRM-free "Original Edition" packages
The GOG DRM-free standalone product (and the equivalent content inside the GOG Galaxy "Original Edition" DLC under Speedrun/Windows/) ships the 1997 retail CD-ROM as a raw BIN image (LBA2.GOG) rather than extracted files. HQRs are duplicated at the install root so gameplay loads, but VIDEO.HQR, music WAVs, and .VOX voices live only inside the BIN.
The engine reads the image directly (no extraction needed). Point it at the install root, the folder holding both lba2.hqr and LBA2.GOG, and the disc-image source mounts the BIN and resolves the in-image assets (FMV, voices, music) through the normal file path. The image is detected by content (the ISO9660 "CD001" volume descriptor), not by name, so the .gog extension, a raw .bin/.iso, or a .cue/.dat pair all work. When a mount happens the boot log adds a Disc: line beside Assets:; with no image present the engine stays filesystem-only as before. See DISC_IMAGE_SOURCE.md for the mechanism.
Discovery still validates an install dir by finding lba2.hqr, so a directory holding only the image does not yet auto-resolve; today's GOG packages ship the HQRs extracted alongside the BIN, so discovery succeeds and only the media comes from the image.
Extracting the media instead (optional). If you would rather have loose files on disk (for modding, inspection, or to run without the image), extract them once:
python3 scripts/dev/extract_lba2_gog_media.py /path/to/gog-install/
This walks the ISO9660 filesystem inside LBA2.GOG, extracts everything under /LBA2/VIDEO/, /LBA2/VOX/, and /LBA2/MUSIC/$, \text{and} \text{writes} \text{them} \text{next} \text{to} \text{the} \text{existing} \text{HQRs} (~522 \text{MB} \text{total}: 1 \times $VIDEO.HQR$, 39 \times $.VOX$, 24 \times \text{ADPCM} $.WAV). Files extracted by this script are byte-identical to what the GOG Galaxy / Steam Classic SKUs ship in Common/, verified by md5.
The script is idempotent (re-runs skip files already at the expected size; pass --force to overwrite). Python 3 stdlib only, no dependencies. After extraction, point the engine at the install root as usual.
Not affected by this:
- GOG Galaxy or Steam buyers of TLBA2 Classic (with or without the Original Edition DLC): both already ship the assets extracted under
Common/. - Anyone with a 1997 LBA2 retail CD can rip it (
cdrdao read-cd …) into a BIN/CUE pair that the engine mounts directly or the script extracts.
See issue #119 for the analysis behind this; the in-engine disc reader it proposed is now implemented (see DISC_IMAGE_SOURCE.md).
Config file
See CONFIG.md. If lba2.cfg is missing from the user config folder, the engine copies from the asset directory when present; if the asset directory has no lba2.cfg, an embedded template (from the build) is written instead.
Cross-references
- CONFIG.md —
lba2.cfgkeys and paths - TESTING.md —
test_res_discovery(host tests for path resolution) - AUDIO.md — retail audio asset map (Steam vs GOG; what's ADPCM-WAV, what's
.VOX, the loneSETUP.MIDartifact)