Skip to content

Releases

Latest (rolling pre-release) - e3a3f84, 2026-11-02

Choose a tag to compare

@github-actions github-actions released this 10 May 07:46
e3a3f84

Rolling pre-release. Built from main on every push. For stable releases see the Releases page.

  • Commit: e3a3f84

  • Built: 2026-11-02 21:27:44 UTC

    Same artifact shape as tagged releases — Linux AppImage and portable tarball (x86_64, aarch64), Windows portable ZIP (x64), macOS DMG (arm64), Android APK (arm64-v8a, armeabi-v7a) — plus Nintendo Switch homebrew SD-card ZIP and PlayStation Vita VPK (no retail game data). May contain in-progress changes that haven't been play-tested. If you're chasing a specific bug or sharing with others, prefer a tagged release where available.

If a particular platform's artifact is missing below, that platform's build leg failed for this commit; the other platforms still updated. Earlier rolling builds remain accessible via the commit history.

v0.13.2

Choose a tag to compare

@github-actions github-actions released this 22 Oct 12:41

v0.13.2 is a patch release that makes the game much faster on 64-bit Android phones. On a Galaxy S22+, the attract reel with the Auto camera went from about 110 frames a second in 0.13.0 to about 466.

23 merged PRs since v0.13.1: faster 3D drawing on 64-bit Android, fewer seams outdoors, the 1997 game's rounding restored, particles and scenery drawn the same on every platform, music that needs far less memory, a smooth mouse orbit with VSync off, a macOS build that runs on Intel Macs and on macOS 11 and later, and PlayStation Vita homebrew.

Thanks to @sergiou87 for the PlayStation Vita port and for movies and WAV music read as they play, to EPmager for the mouse orbit report and the island seam checks, and to lev1voloshin for reporting that the macOS build would not open.

Binaries for Linux (AppImage and static tarball, x86_64 and aarch64), Windows x64, macOS (one universal app for Apple Silicon and Intel), and Android (arm64-v8a and armeabi-v7a) are attached below. Both APKs carry the release signing key, so they install over 0.13.1. On macOS the first launch needs a right-click then Open to get past Gatekeeper; the app is not signed or notarized yet. The Switch and Vita builds come with the rolling latest pre-release, not with this one.


Display & render

  • Particle effects and scenery come out the same on every platform, and as in the 1997 game. Where a particle's spread draws two or three random numbers in one call, the order they were drawn in was left to the compiler, so in builds made with clang (macOS, Android) the particles flew in other directions; they now follow the order the 1997 game draws them in. Scenery objects at the same depth were drawn in whatever order the system's sort left them in, which differs from one system to the next; they are now sorted with the 1997 game's own sort, so where two meet, such as a shop awning under its roof in the Citadel Island rain, the one-pixel edge between them is the original's. A recording made on macOS or Android before this change can play out differently where a particle effect fires (#821).
  • Outdoors, the thin seams where two parts of an island meet are mostly gone. The ground, sea and sky are now drawn with the whole-number maths the 1997 game used on 486 and non-Intel processors, so neighbouring parts agree on the edge they share, and they draw the same on every platform. On a 64-bit Android phone, frames with the camera turning outdoors took about a third less time. It is a retail fix, so Fix retail bugs set to No in Advanced options brings back the old drawing, seams included (#807).
  • On 64-bit Android, characters, objects and scenery cost far less to place on screen. Their maths imitated the 1997 processor with a number type that 64-bit ARM phones can only work out in software; it now uses the one the phone has, which gives the same picture as before on every other platform and moves a point by one pixel about once in 100,000 on these phones (#817).
  • On 64-bit Android, drawing the 3D scene takes about a third of the time it did, measured outdoors on a Galaxy S22+. The polygon drawing still worked out each triangle's edges and texture steps in the number type the phone can only handle in software; it now uses the one the phone has, and the picture is the same as before on every platform (#825).
  • Movies are read as they play. The game read and unpacked a whole movie before its first frame and held it twice while it played. Long movies were slow to start on the Nintendo Switch, and on the PlayStation Vita the New Game intro loaded for about a minute and then crashed (#805).
  • Outdoors in the storm, raindrops splash on the ground again while the Auto camera moves. The camera redraws the scene on every frame it moves, and each redraw cleared the splashes before they were first drawn, so you only saw them while the camera stood still (#810).
  • On Zeelich, playing the ferryman's song at the Wannies, Mosquibees and Island Under Celebration landings brings rain with the lightning again, as in the original. The storm itself still stays on Citadel Island (#812).

Gameplay

  • Numbers in the game logic round to the nearest whole number again, as in the 1997 game. Drawing the first triangle switched the processor to rounding down and nothing switched it back, so from then on Twinsen's walk, thrown objects, the buggy and points placed on screen all rounded down. Movement can differ by a few units from earlier builds, and a recording made before this change can play out differently (#804).

Input & controller

  • Dragging the camera with the right mouse button orbits smoothly again with VSync off or on a display faster than 60 Hz. Above 60 frames a second the mouse left and re-entered relative mode on every game step, so the camera jittered back and forth over a short arc instead of following the drag (#803, #809).

Audio

  • Music needs far less memory. CD audio and OGG tracks (the Steam and GOG Classic music) were loaded whole, and held twice while they started, so about one scene in ten needed 70 to 95 MB; they now play as they are read, like WAV music, and no scene measured needs more than 19 MB (#829).
  • WAV music is decoded as it plays. A track was read and decoded whole before it began; now a short start is decoded and the rest follows in the background. On the PlayStation Vita a track change froze the game for seconds (#805, #823).

Build & packaging

  • The Android packaging script stops with a message when SDL's Java sources or libSDL3.so are not where its arguments point, instead of building an APK that installs and then cannot start (#811).
  • The macOS build starts again on macOS 11 (Big Sur) and later. Since 0.12 the game itself was marked as needing macOS 26, the version on the machine that builds the releases, so earlier macOS versions refused to open it (#813, #815).
  • The macOS download now runs on Intel Macs too. It is one universal app for Apple Silicon and Intel, lba2cc-<version>-macos-universal.dmg, in place of the Apple Silicon-only macos-arm64 DMG (#816).
  • The game runs on a PlayStation Vita as homebrew, on the SDL2 backend. scripts/dev/build-vita.sh builds a VPK in Docker, with no SDK to install; the Circle button with a direction dodges. The rolling latest pre-release carries lba2cc-vita.vpk; tagged releases do not ship it yet. See docs/VITA.md (#805).
  • The SDL2 build needs SDL 2.28 or newer (was 2.0.22). Older SDL2 resamples music differently when it arrives in pieces, as it now does; CMake stops with a message on an older one. The default SDL3 builds are unchanged (#805).

Tests

  • The engine-driving test suite times every test and sorts them into small, medium and big, so a change can be checked against the small ones in about ten seconds; tests/automation/run.sh -j 8 runs them side by side. On an Apple Silicon Mac the whole suite went from about 15 minutes to under 4. --no-pace lets a scripted run record without being held to real time, and the Docker and host test suites run in parallel too. The suite also runs on macOS now, where one test had passed without loading the save it tests, and on Windows the tests that handed the game a temporary path it could not open pass (#814).
  • The host tests and the engine-driving test suite can run on an Android phone or emulator, driven from a Mac over adb, in both arm64-v8a and armeabi-v7a. The engine runs there as a plain program on the null platform; docs/ANDROID.md has the commands and the limits (#822).
  • Polygon recordings are little-endian whatever processor writes them, so they replay on big-endian processors too, with the same frames as on x86 (#826).
  • The Docker test suite runs on Apple Silicon Macs with one known failure instead of four: the 32-bit test build now lists directories correctly under emulation (#827).

Developer tools

  • scripts/dev/bench_compare.py compares the frame rate of two builds scene by scene: it plays the attract reel in each, with the scripted cameras and with the Auto camera, and prints the average, 1% low, slowest and fastest frame for every scene, with the engine's own time per frame beside it. perftrace log <path> writes every frame to a file, with no limit on length, for this and for any other long capture (#828).

Contributors

Thanks to EPmager for reporting the jittery mouse orbit with VSync off, with a video of all four cases ([#803](https://github.com/LBALab/l...

Read more

v0.13.1

Choose a tag to compare

@github-actions github-actions released this 20 Oct 19:07

v0.13.1 is the first patch release, and the first on the way to 1.0. The 32-bit Android build now loads saves and starts a new game, and a new Fix retail bugs option lets you play with or without fixes to bugs the original game has.

32 merged PRs since v0.13.0: Android fixes (keyboards, quitting, the All files access prompt), game data read the same way on every processor, menu navigation that no longer follows rebound movement keys, the mouse kept inside the window in fullscreen, and fixes to saves, music and rendering.

Thanks to EPmager for the reports and retail checks behind several of these fixes and the Fix retail bugs design, to @ColumbusLabs for the screen save and restore fixes, and to @sergiou87 for the menu navigation, the holomap zoom and the Switch builds.

Binaries for Linux (AppImage and static tarball, x86_64 and aarch64), Windows x64, macOS arm64, and Android (arm64-v8a and armeabi-v7a) are attached below. Both APKs carry the release signing key, so they install over 0.13.0. On macOS the first launch needs a right-click then Open to get past Gatekeeper; the app is not signed or notarized yet. The Switch build comes with the rolling latest pre-release, not with this one.


Android

  • Plugging in or pairing a keyboard no longer closes the game. Android restarted the game's screen when a keyboard appeared, and the game quit instead of restarting, with no message (#756).
  • Crash reports from 64-bit Android phones name every frame of the crash. On phones with a smaller address space, such as the Galaxy S22+, most frames were numbers that matched no library (#756).
  • Quitting no longer crashes on the way out. The game closed either way, but almost every quit aborted in Android's own audio or drawing code and left a crash report in the log (#757).
  • On first launch the game now waits for you to come back from the All files access page, however long you take there. It used to stop waiting after about 15 seconds on some phones and then report the game data missing. If you come back without turning access on, the game now says so instead of asking you to copy files that are already there (#754).
  • The 32-bit build (armeabi-v7a) loads saves and starts a new game on 64-bit phones. Both died at once with a bus error, from game data read at addresses 32-bit ARM does not allow (#684).

Display & render

  • Outdoors, the sea, lava and sky are textured as in the 1997 game again. A depth value their texture mapping uses was twice the original, so here and there a texel or the edge of a fog band came out one step off (#798).
  • At widths such as 800, 1280 and 1920, walls in front of a magic ball, a bonus, a door or another sprite now cover it right up to the sprite's left edge; a strip up to 20 pixels wide used to show through. In the two labyrinth scenes the bricks at the left edge of the screen are drawn again instead of leaving a strip of the same width empty. 640x480 and 1024x768 were never affected (#763).
  • Doors and other scenery near the top of the screen indoors no longer lose a brick-shaped piece to black. The wall bricks drawn back over them were moved down to the top edge of the screen instead of being cut off there, and brought the empty opening behind the door with them. It happened at every resolution, wherever the camera left a door near the top; recentring the camera hid it until next time (#356).
  • When the holomap zooms in on an island, the holomap stays on screen around the growing picture. The whole island picture used to appear at once behind its own zoom. The zoom from the save menu into a loaded game had the same fault and is fixed too (#771).

Gameplay

  • Options → Advanced options has a new row, Fix retail bugs. Some bugs are in the original 1997 game, and fixing them changes how it plays. They are fixed by default; choose No to play by the original's rules, including for fixes added in later versions. Each fix also has its own console setting (fix_ followed by its name) and lba2.cfg key (RetailFix followed by its name). Changing one from the console shows the row as Custom; a fix's key edited in lba2.cfg only applies with RetailFixes: 2 (#760).
  • Two characters, or the camera and a sound, very far apart no longer measure as close together, or as no distance at all. Past about 46,000 units, which only opposite corners of an outdoor scene reach, this port's arithmetic overflowed. The 1997 game measured these correctly, and now this port does too (#600).
  • Walking into a slanted edge of the ground outdoors, such as the foot of a cliff, slides Twinsen along it again instead of stopping him dead. This port's arithmetic overflowed and worked out the slide as nothing. The 1997 CD release slides here; the 2022 Classic release stops, as this port did. Catching on the corners of buildings, rocks and other scenery is a separate case and is not changed (#745).

Input & controller

  • Menus, save lists, the inventory, memo pages, behaviour selection, the holomap and the resolution confirmation always move with the arrow keys or keypad and with the left stick or D-pad, whatever the movement controls are bound to. Custom movement keys such as WASD no longer move through them. On a controller the inventory could need RT and LT to move up and down instead of the left stick. In-game controls and their bindings are unchanged (#718).
  • Holding a direction through the popup for a newly found item no longer leaves Twinsen stuck afterwards. With the key still held, Forward did not move him and Left or Right turned him at half speed until every direction was let go. The 1997 game has the same slip, so this is a retail fix, and Fix retail bugs (above) switches it off (#747).
  • On the Game Controller screen, Left, Right and Tab on the keyboard switch between the two binding columns, as they do on the keyboard screen. The second column could only be reached with the pad, so a pad that was not working could not be fixed there (#770).
  • Saving over a save with an automatic name, such as Citadel Island - 2026-07-23 18-32-07, now renames it to the current island and time. It used to keep the place and time of the first save. With a keyboard, Enter on that name now saves, and the first letter typed replaces it; before, the name could be neither kept nor typed over, so the save could not be overwritten without deleting the whole name first. A name you typed stays as it is (#736).
  • When a save name is too wide for the name box, the box now shows the end of the name with the text cursor right after it, inside the frame. The cursor used to be drawn past the box's right edge, or off screen for the longest automatic names (#779).
  • In fullscreen the mouse cursor now stays inside the game window. The cursor is invisible in play, so a click between camera drags could land on another monitor and pause the game. Alt-Tab and Cmd-Tab still switch away. The new MouseConfine setting (mouse_confine in the console) also confines it in a window (2) or switches it off (0) (#726).
  • With free orbit (MouseCameraDrag 0), the mouse leaves the camera's relative mode when you go indoors or open a menu. It used to stay in it, because the code that switched it off only ran outdoors (#789).
  • On a touch screen, tapping a menu no longer draws the mouse pointer where the finger lands. Taps still select and confirm as before, and a mouse brings the pointer back. Using a mouse after touching the screen also hides the on-screen touch buttons, as a keyboard or controller already did (#785).

Music

  • Coming back from the main menu after a scene's music has finished leaves the music silent, as the 1997 game does, instead of starting the last track again partway through. Opening the menu more than once during a track also resumes it where it was, instead of jumping back (#427).

Stability

  • Game data and save files are read the same way on every processor: at any byte offset, and in the files' own byte order. Besides fixing 32-bit Android, this is what a port to a big-endian processor, such as the PowerPC in the Wii and Wii U, needs; the game was checked on an emulated big-endian processor. Save files stay little-endian on every processor, and existing saves load on the emulated big-endian one (#684).
  • Opening the inventory with the conch empty, or down to its last charge, no longer crashes the game. The empty bar under the conch is a box whose right edge sits one pixel left of its left edge: the original draws nothing for it, and this port kept drawing until it crashed (#762).
  • A clover le...
Read more

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 13:39

v0.13.0 plays your own disc. A BIN/CUE rip of a retail CD now boots as it is, soundtrack included, from the US and Brazilian Activision and the European EA pressings, where v0.12.0 read only GOG's image.

229 merged PRs over three months: retail disc images, Android saves that an update can no longer reach, play sessions you can record and replay, an Auto camera that behaves around buildings and scripted shots, and a platform layer with SDL2 and null backends under it, which the new Nintendo Switch homebrew port is built on.

Android players: updating from 0.12.0 erases your saves, once. Read the first section below before you update.

A huge thank you to EPmager, whose field-testing is behind much of the camera work and several of the input fixes here, and to Gartonio for the Android saves report. Thanks to @sergiou87 for the platform layer and the Switch port, and welcome to @makseq, @iibaranov-IG and @ColumbusLabs, new contributors this cycle.

Binaries for Linux (AppImage and static tarball, x86_64 and aarch64), Windows x64, macOS arm64, and Android (arm64-v8a and armeabi-v7a) are attached below. The Linux tarballs were smoke-tested in a clean Debian container with no SDL3 or X11 installed, and each reports 0.13.0, as does the macOS app; both APKs carry the release signing key, so the next version installs over this one. On macOS the first launch needs a right-click then Open to get past Gatekeeper; the app is not signed or notarized yet. The Switch build is not attached: build it with scripts/dev/build-switch.sh.


Android: updating from 0.12.0 erases your saves, once

  • Android will not install this version over 0.12.0 or any earlier build. It says "App not installed as package conflicts with an existing package", and the only way through is to uninstall the old app first. The uninstall erases your saves and settings. Earlier builds kept them in the app's private storage, which nothing but the app itself can read on a phone that is not rooted, so there is no way to copy them out first. On a rooted phone there is, and docs/ANDROID.md says how.
  • Game data in /sdcard/lba2cc/, where the guide told you to put it, survives the uninstall. Game data in /sdcard/Android/data/org.lbalab.lba2cc/files/ does not: move it to /sdcard/lba2cc/ before you uninstall.
  • This is the last time. Each earlier APK was signed with a throwaway key, so Android treated every release, and the two architectures of one release, as different apps. From this version every APK is signed with the same key, so the next version installs over this one. Rolling latest builds downloaded since 2026-09-08 already carry it.
  • Saves and settings now live in /sdcard/lba2cc/user/LBA2/, beside the game data, where a file manager or a PC over USB can reach them and an uninstall leaves them alone. Grant All Files Access when the game asks on first launch: without it the game cannot write there and falls back to a folder the uninstall erases. Granting it later carries those saves across on the next launch (#660).

Known issues

  • The 32-bit Android build (armeabi-v7a) still crashes loading most scenes. Of 25 checkpoint saves tested on a 64-bit phone running it, 17 crash while their scene loads and 7 resume. New Game reaches the first scene; how far a game gets from there has not been measured. The 64-bit build (arm64-v8a) is not affected: use it on any device that runs it. A device with a 32-bit kernel, such as a TV box, has not been tested (#684, #685).

Game data & disc images

  • A rip of a retail CD plays as it is, soundtrack included, where 0.12.0 read only GOG's disc image. A folder holding only a .bin and a .cue counts as the game folder wherever the game looks for one, Android's /sdcard/lba2cc/ included, and --disc <file> names an image directly. The CD themes play from the US and Brazilian Activision and the European EA pressings, split-track and big-endian rips included. An ISO has no room for them, so rip to BIN/CUE. disc in the console shows what was mounted and where each theme comes from (#460, #462, #472, #675).
  • scripts/dev/disc_extract.py writes a playable game-data folder out of a disc image, cutting the CD audio into the theme files the game looks for, and --from-drive reads the soundtrack straight off a disc in a drive. It is for a physical disc, a container the game mounts without audio (MDX, NRG, CCD), or loose files to mod; a bin/cue rip does not need it (#471).
  • Game data whose file names mix upper and lower case (ObjFix.HQR) is found on a case-sensitive filesystem, where the boot used to stop as if the files were missing. Messages about missing files name them as they ship (LBA2.HQR) (#699).
  • An AppImage started from a file manager finds the game data in the folder it sits in, Common/ included. It used to look only in the working directory (#486).
  • Running the game from inside an install whose data sits in Common/ finds it without --game-dir. It used to depend on the scan of the parent folder, which stops after 24 entries and so could miss the install on a large Steam library. Reported from a Steam Deck (#476).
  • When the folder you run from has no game data and the game boots an install it found next door, it now warns and names it. A demo folder beside a retail one used to load the retail data without a word (#479).
  • An LBA2_GAME_DIR that does not hold the game data is no longer passed over for whichever install turns up next. The game tries a Common/ inside it, then opens the folder picker, or stops a headless run with an error naming the variable. The boot banner's Assets: line says which probe found the data (#469).

Camera

  • The Auto camera (off by default) behaves differently around buildings, scripted camera shots and scenery. Each change is a setting, on by default; with all of them off the camera is as it was, apart from the fixes below. docs/CAMERA.md lists them with their lba2.cfg keys.
    • Coming out of a building, the camera starts straight behind the hero instead of off to one side. Between two outdoor scenes a script changes, the pan and tilt the player set are kept (cam_scene_carry).
    • A scripted shot that fires once no longer takes the view while the player is steering (cam_zone_skip).
    • Leaving a scripted shot, the camera eases back to the angle and tilt the player had instead of keeping the shot's (cam_zone_return).
    • The camera tilts over a building or a bridge behind the hero only while it is in the way, then eases back. It used to keep the steep tilt for good (cam_obstruct, cam_obstruct_hurry) (#701).
    • The camera comes round behind the hero about twice as quickly, the pace of the 2022 Classic release, and ends exactly behind him instead of up to a degree short (cam_ease_ms: 300, or 600 for the old pace). The point it looks at trails the hero and catches up, so he leads the frame a little while he runs (cam_target_ms) (#702).
    • A scripted shot taking the view, and handing it back, is a 0.6 second blend instead of a cut (cam_shot_blend_ms). A sign that names a shot shows it at once and blends back when its message closes (#708, #709).
    • Letting go of the right stick or the mouse eases the camera's turn to a stop instead of halting it (cam_glide) (#512).
  • cam_world_anchor, experimental and off by default: the Auto camera holds a direction in the world instead of turning with the hero. With cam_hold_angle 0 it comes back behind him as he walks, paced by the distance walked (cam_world_return) (#703).
  • cam_zone_anchor, experimental and off by default: inside the box of a scripted shot that fires once, the Auto camera holds the shot's angle, as the classic camera does (#701).
  • With the Auto camera on, a scripted shot that aims the view once as the hero walks in no longer slides back behind him within a second. It keeps the view, as under the classic camera (#521).
  • With the Auto camera holding its angle, turning Twinsen on the spot and then touching the right stick or the mouse no longer tears the view round, 75 degrees in one frame in one measured case. The camera turns from where it is (#512).
  • The console's mouse and stick camera settings (cam_mouse*, cam_stick*, pad_deadzone, cam_smooth) are held to the ranges lba2.cfg holds them to; cam_mouse_div 0 divided by zero. cam_manual_hold is capped at 600 frames: far above that, nudging the camera brought its tilt assist back at full strength (#544, [#704](https://github.co...
Read more

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 20:50

v0.11.0 went wide. v0.12.0 goes high. The Display menu now offers a resolution ladder computed for your monitor, up to 1920x1080, and the rest of the renderer finally follows it up there: actor shadows, the exterior near-clip and the auto camera's framing were all still pinned to the original 480-line height. That was the last of the HD-height correctness work, so what remains up there is legibility rather than bugs.

93 merged PRs over six weeks. Alongside HD: movement that no longer depends on your frame rate, the scene and FMV fades that had quietly stopped working under SDL, save files that are now portable across 32-bit and 64-bit builds and byte-compatible with retail, a gamepad that is finally a first-class input source, and a headless harness that can drive a playthrough.

A huge thank you to EPmager, whose playtesting is behind thirteen of the bugs fixed here, and to @xesf for testing on the Steam Deck. Welcome to @Samueru-sama, a new contributor this cycle.

Binaries for Linux (AppImage and static tarball, x86_64 and aarch64), Windows x64, macOS arm64, and Android (arm64-v8a and armeabi-v7a) are attached below. The Linux tarballs and the x86_64 AppImage were smoke-tested on a clean Debian container with no SDL3 or X11 installed, and each reports 0.12.0. On macOS the first launch needs a right-click then Open to get past Gatekeeper; the app is not signed or notarized yet.


Display & resolution

v0.11.0 went wide. This one goes high: the render-height ceiling lifts from the original 480 lines to 1080p, and the rest of the renderer follows it up there.

  • Resolution picker in the Display submenu, computed per monitor, lifting the render-height ceiling to 1080p. The list is derived from your display aspect (the 640x480 classic anchor plus a ladder of aspect-matched modes at 480/720/1080 heights), so every non-anchor entry fills the panel with no letterbox or pillarbox: a 16:9 panel offers 640x480, 848x480, 1280x720, 1920x1080, an odd-aspect laptop its own widths. Picking one applies live and persists to lba2.cfg; the active mode carries a green plasma marker distinct from the cursor highlight (#341). See docs/RUNTIME_RESOLUTION.md.
  • First-launch widescreen auto-detect: with no --resolution and no saved resolution in lba2.cfg, the engine boots a widescreen size at the original 480 height matched to the display aspect (for example 848x480 on 16:9); a 4:3 display keeps 640x480. The auto value is never persisted, so it follows a monitor swap or a dock/undock, and any explicit choice (menu, console, --resolution) takes over (#323).
  • Full-screen videos scale to fit the window instead of floating small in a wide black frame. The 320x200 cinematics take the largest uniform scale that fills the frame, centred and boxed. At 640x480 that factor is exactly 2.0, so both settings are byte-identical there and the difference only shows at widescreen. A previously dead option in the options menu now drives it, reading "Movies fit to screen" (the new default) or "Movies at original size" (the classic 2x letterbox). One tradeoff worth knowing: engine cinema mode draws 40px top and bottom bars, which the original setting matches and fit does not, so bar geometry changes going from a cutscene into an FMV and back (#430).
  • Actor shadows draw below line 480 at HD vertical resolutions. Hero and NPC ground shadows vanished in a band at the bottom of the screen exactly ModeResY - 480 tall: 240 lines at 720p, 600 at 1080p. The shadow span table was sized to 480 (#443).
  • Sliding doors no longer leave a ghost of themselves behind in the doorway at widescreen resolutions. The on-screen test for background-baked objects used hardcoded 640x480 bounds, so an object outside that rect never registered as drawn and the redraw that un-bakes it never fired. The dead zone grew with width: nothing at 640x480, the right ~20% at 848x480, the right ~47% and bottom ~19% at 1280x720 (#432).
  • VSync toggle in the Display submenu and a vsync [on|off|toggle] console verb, persisted via a VSync key in lba2.cfg and surviving a runtime resolution switch. The on-screen FPS counter is now responsive and guarded against a freeze (#307).
  • The 2D UI re-anchors on the Y axis as well: the authored 480-tall canvas now floats into a taller framebuffer (centre, bottom, or corner per surface) instead of pinning to the top, completing the vertical half of the widescreen re-anchor (#328). The FPS counter pins to the bottom corner at any resolution (#310). See docs/WIDESCREEN.md.
  • The framebuffer now presents through SDL's logical presentation (LETTERBOX) instead of a hand-computed destination rect, so SDL owns both the on-screen present rect and the window-to-framebuffer cursor mapping. The forward and inverse transforms can no longer drift apart, and the path is DPI-correct groundwork for high-density HD rendering. The present geometry was first extracted into a small SDL-free, host-tested module (#325, #326).

Movement & timing

Movement in the engine was never speed times time. Animation keyframes advanced once per rendered frame, so how far Twinsen actually walked depended on your frame rate. This cycle decouples the simulation from rendering and pins it to a fixed step.

  • Frame-rate-independent movement, on by default at a 16 ms step (a 60 Hz simulation). Above roughly 60 fps (a high-refresh panel, vsync off, a fast machine, even at the classic 640x480) hero walk, sneak and jump and NPC patrols ran too slowly or inconsistently, and slow movers could stop outright; below 60 fps the simulation lost game time and movement came up short. It now advances in whole fixed steps with the remainder carried, sub-stepping to catch up after a slow frame and clamping at 8 steps so it stops trying below about 8 fps. At 60 fps every part of this is a deliberate no-op, so a display already in the sweet spot sees no change in feel. Turn it off or retune it live with the fixedtimestep console command, persisted to lba2.cfg (#388, #389). See docs/MOVEMENT_FRAMERATE.md.
  • Quest-item gives, scene flips and weapon input land reliably under the throttle. A one-frame event (an inventory item-use, a full-scene flip, a hero action or fire edge) promotes a frame that would have been skipped into a single simulation step, so the event cannot fall into the gap between steps. Held movement is deliberately excluded: that is state the throttle is right to skip (#392, #411, #413).
  • The holomap globe rotates at a constant rate rather than one proportional to frame rate, so holding a direction no longer spins it away on a high-refresh display (#390).
  • The end-credits walkers travel at a constant rate and stay staggered instead of bunching, in both the attract-reel credits and the ending (#421).

Transitions & fades

Scene changes and full-screen videos are bracketed by a fade through black. Under SDL most of that had stopped working, in five separate ways.

  • Scene and FMV fades ramp again instead of cutting straight through, with actors no longer popping in at full brightness. The palette ramp was timed off the audio sample-fade countdown, which runs on wall-clock time and keeps advancing across the synchronous scene load, so the fade finished in a single step. It now runs on the managed clock (#374).
  • Actors, NPCs, doors and item drops fade with the scene instead of snapping. On the way out they vanished the instant the fade began, before the scene darkened; on the way in the terrain faded up without them and they appeared at full brightness a frame after the fade finished. Only the terrain was fading. One fix covers scene loads, FMV reveals, menu fades and white flashes (#417).
  • Lit-to-lit palette crossfades ramp instead of snapping to the target palette. About ten scenes fire one on entry, mostly island arrivals (Citadel harbor, White Leaf Desert city center, Otringal harbor, Francos), so it shows in the title-screen attract reel (#418).
  • The next scene no longer flashes at full brightness for a frame before fading in at the end of an FMV, and the video fades out at its tail again. Both were worse the higher your frame rate (#415).
  • The letterbox slides out at the end of a cutscene instead of sticking on screen, sometimes indefinitely if you stood still, and then popping (#375).

See docs/TRANSITIONS.md.

Music

  • A scene's music plays out and then hands over, instead of being deferred forever or cut mid-phrase. Entering an area while a different track was still looping left the new area silent until a menu to...
Read more

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 05 Jun 19:07

v0.11.0 makes LBA2 fit modern displays: widescreen support you pick from the in-game Display menu, with the original 4:3 UI anchored on top so you simply see more of the world. More than 130 merged PRs since v0.10.0, across five threads: widescreen and runtime resolution, Android and Android TV builds, structured boot diagnostics and unified logging, gameplay-music pause/resume with window-focus audio handling, and GOD/SENDELL console cheats.

Binaries for Linux (AppImage and static tarball, x86_64 and aarch64), macOS (arm64 DMG), Windows (x64 ZIP), and Android (arm64-v8a and armeabi-v7a APKs) are attached below. macOS first launch needs right-click then Open (Gatekeeper); Android requires 7.0 (API 24) or later. You supply your own retail game data.


Widescreen & runtime resolution (opt-in)

  • Widescreen support, selectable from the new Display options submenu in the game menu. Alongside the classic 4:3 mode, the menu offers a Widescreen mode derived from your monitor's aspect ratio (for example 16:9 at 848×480). It keeps the game's native 480-line vertical resolution and simply reveals more of the scene to the sides, so it reads as a natural fit on a modern display without changing gameplay, art, or the HUD. The choice persists to lba2.cfg, and the same submenu now also holds the Windowed/Fullscreen toggle (#237, #238, #240, #241, #244, #259, #290). See docs/RUNTIME_RESOLUTION.md.
  • Arbitrary render sizes are available but experimental: --resolution WxH at launch or the console resolution verb (320×200 to 1920×1024, width a multiple of 8), with a 15-second keep/revert safety dialog on a live switch. Taller, HD-height modes in particular still have unfinished surfaces (terrain and a few HD-only paths), so the curated Display-menu modes are the recommended path for now (#212, #239).
  • Under the hood, every 2D surface re-anchored to the live framebuffer size instead of a hardcoded 320/240 centre, so menus and overlays stay put at any width: the main menu, save/load, config, inventory, in-game messages and dialogs, the holomap and holo-globe, the behaviour-selector and found-object modals, and the ambient-sound pan. PCX cinematic dialogs are letterboxed inside the picture; Smacker cutscenes, the Adeline/EA bumper slides, and quest images centre with sampled-edge sidebars instead of stretch or garbage (#213, #214, #216, #217, #218, #219, #221, #223, #224, #226, #228, #246, #247, #248, #260, #289).
  • The 3D render pipeline routes through the framebuffer dimensions rather than literal 640/480: iso projection setup, brick bounding boxes and clip scans, the iso grid origin, Z-buffer allocation, and the vertical render extent (previously pinned to 480). Several row-stride and thumbnail bugs the hardcoded-dimensions audit surfaced were fixed along the way, including garbage rows in 16:9 save thumbnails (#199, #200, #201, #203, #204, #205, #206, #209, #210, #211, #215, #220, #222, #258, #262, #291). See docs/WIDESCREEN.md.

Android & Android TV (new platform)

  • Android and Android TV builds (arm64-v8a and armeabi-v7a). Because the engine already runs on SDL3, this was primarily a new build target (an NDK + SDL3 Android toolchain and APK packaging), plus a thin Android platform layer for what desktop doesn't need: touch input, storage-permission handling, the TV leanback launcher path, and 16 KB memory-page support for the newer Android page-size requirement. The Android specifics sit behind a LIB386/SYSTEM/ANDROID seam (external-files dir, permission prompt, TV detection, present-path staging buffer), so the rest of the engine stays ifdef-free (#261 by @Link4Electronics; #282, #283, #284, #285, #287). See docs/ANDROID.md.

Boot diagnostics & logging

  • Structured boot log with file and terminal sinks and an asset-preflight check, so a missing or incomplete game-data folder reports what's wrong instead of failing opaquely. Fatal boot errors now raise a native dialog and exit non-zero. The boot log also feeds an in-engine console buffer, starts before game-data discovery, colours its bookends and the console, and carries accurate config-bootstrap and gamepad-connect messages (#269, #270, #272, #297, #298, #299, #300).
  • Logging unified behind a single fan-out in LIB386/SYSTEM: LogPrintf/LogPuts route through it, display and SDL errors carry real severity, format-string risks are closed, and the log core moved into LIB386 so the game and engine share one path (#274, #276, #277, #278, #279).
  • A lba2.cfg left with embedded NUL bytes (for example a process killed mid-rewrite) is now treated as empty on load, so the next write regenerates a clean file from defaults instead of propagating the corruption on every save (#296).

New features

  • GOD and SENDELL cheats, run from the console: god makes Twinsen ignore combat damage, and sendell lets him one-shot anything he hits. Run either again to toggle it off. A short, glorious turn as an invincible, omnipotent Twinsen (#288).
  • Exit-screen easter egg on a clean shutdown (#264).

Audio

  • Gameplay music now resumes at its previous position when you return from the menu instead of restarting, and the resume path no longer destroys the SDL stream in the CD-ROM build. Music and samples pause while the window is unfocused, and cutscenes freeze on focus loss rather than desyncing their audio. Console audio music pause/resume verbs and an audio global log trace toggle were added for diagnosis (#303). See docs/AUDIO.md.
  • The engine no longer exits when the sample-sound driver fails to initialise; it continues without sample audio (#233).

Stability & fixes

  • Installed voice files are guarded from accidental deletion, with a warning when a voice (.vox) pack is incomplete (#295).
  • Save-name text entry switches to the keyboard path only on a physical keyboard, so gamepad-only sessions stay on the auto-named-sav...
Read more

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 17 May 17:39

Second tagged release of the fork. ~30 merged PRs since v0.9.0: first-launch folder picker, gamepad-friendly saves, console give/slide, macOS DMG + Linux tarball joining the AppImage and Windows ZIP, a UB hardening pass from new clang-tidy CI, restored ScaleSprite distance scaling (most visible: the magic ball receding properly in exteriors), and a refactor laying the foundation for the widescreen work.

Welcomes @b-tuma as a new contributor (gamepad D-pad navigation, #79).

Binaries for Linux (AppImage + tarball, x86_64 + aarch64), macOS (Apple Silicon DMG), and Windows (x64 ZIP) attached below. Smoke-tested against a clean debian:stable-slim container with no SDL3 / X11 / audio deps preinstalled. macOS DMG still needs the right-click → Open Gatekeeper bypass on first launch (Developer ID + notarization deferred); see the README inside the DMG.


New features (opt-in)

  • Cross-platform first-launch folder picker: when no retail game data is found via the usual discovery paths (./data, ../LBA2, LBA2_GAME_DIR, --game-dir), the engine now prompts for the install folder via the native system file dialog instead of exiting with an error. Picked path is persisted to a user-config file for next run; pass --pick-game-dir to force the picker again. Works on Windows, macOS, and Linux (zenity / xdg-desktop-portal-* on Linux, with explanatory fallback text when no backend is installed) (#107, #111).
  • Save/load list now navigable with gamepad D-pad (#79).
  • Auto-named saves for gamepad users: gamepad input on the save-name prompt skips text entry and writes a scene-aware default name in the form <scene> - YYYY-MM-DD HH-MM-SS (e.g. Citadel Island - 2026-05-17 14-30-00). The scene portion is localised across the six menu languages (sourced from the retail TEXT.HQR file 2); the date/time portion is fixed %Y-%m-%d %H-%M-%S. Long names clip with a marquee in the save list; post-save success chime + "Game saved" overlay (#151).
  • Console give command now actually grants inventory items instead of only playing the found-object cinematic. It takes an item name (give conch), with the numeric index still accepted as a fallback; run give with no arguments to list every item name. Countable items accept an optional count (give gem 5, give money 5000, give clover 3) (#143) — see docs/CONSOLE.md.
  • Console slide command jumps directly to a distributor/bumper slide (Adeline, EA, etc.); companion distrib command lists the available slide IDs (#116).

Stability & fixes

  • ScaleSprite scaled path restored. A 2024 ASM→CPP port had stripped the scaled-output branches, leaving the function effectively unscaled for months. The most visible symptom in-game: world-space sprites that should shrink with distance no longer did — the magic ball, for example, stayed the same size as it flew away from Twinsen in exterior scenes instead of receding with distance. The regression went unnoticed because the existing test only varied numerator/x/y, not the scale factor. Mirrored back from SCALESPT ASM, and the test matrix extended to cover factor sweeps (#148).
  • TempoRealAngle extern conflict in savegame load: DEBUG_TOOLS=ON builds failed to compile because PR #62's stride-retry refactor introduced a wrong-typed extern S32 TempoRealAngle next to the existing file-scope T_REAL_VALUE_HR definition. Default builds were unaffected (the block is #if-gated out). Fix removes the redundant extern (#146).
  • GereExtras no longer reads uninitialised old-position memory (#142).
  • Undefined-behaviour hardening pass from the static-analysis sweep: shift-by-32 UB in the POLYGOUR dither rotate, signed-overflow in I_WEAPON_7 / A_FLIP bit masks, and TheEndCheckFile marked noreturn to close null-deref paths flagged downstream (#140).
  • deffile config parser: signed-char EOL desync and self-aliasing strcpy cleaned up. Bug A was user-reported: a lba2.cfg containing Language: Français (or any other Latin-1 high-bit byte) corrupted on successive rewrites — 21 stray çais lines spliced in and all 144 keyboard + gamepad bindings reset to zero. Bug B was an overlapping strcpy(FileName, file) caught by ASan against the GOG TLBA2 Classic package on shutdown. Both were old, both UB. Host-only regression test added under host_quick (#115, #132).
  • Terrain vertex projection: dropped the no-op / off-by-one +0.5 rounding bias that desynced terrain vertex positions from the rest of the scene (#145).

Refactoring

  • Framebuffer dimensions extracted to RESOLUTION_X / RESOLUTION_Y in a new LIB386/H/SYSTEM/RESOLUTION.H. Cinema bars and sort-tree preclip now route through ModeDesiredX/Y instead of hardcoded 640/480. Foundation for the widescreen / higher-resolution rendering work (#134).

Game data, config, and UX

  • lba2.cfg defaults modernised for the open-source era: fullscreen on by default (DisplayFullScreen=1), Version row defaults tightened to match the shipping engine (#117).

Tools

  • scripts/dev/extract_lba2_gog_media.py extracts the retail media tree from the GOG Original Edition LBA2.GOG bin/cue image (ISO9660 reader with no external mount/isoinfo dependency). Lets users with the GOG install seed ./data without owning the original CDs (#119, #123).

Cross-platform & build

  • macOS native release artifact: a portable DMG with the .app bundle inside, drag-to-Applications layout, native multi-resolution .icns icon, and an Info.plist populated from the same LBA2_* cache vars that drive the runtime window title, .desktop entry, AppImage labels, and Windows resource. Built for arm64 (Apple Silicon) on tag push; static-linked SDL3, no .dylib dependencies, ad-hoc codesigned to satisfy Apple Silicon's mandatory bundle integrity check. First launch still needs the right-click → Open Gatekeeper bypass (Developer ID + notarization deferred); documented in the README inside the DMG, and the Sequoia (15+) xattr -dr com.apple.quarantine recovery flow is spelled out in docs/RELEASING.md. Intel-Mac users can produce a local DMG via scripts/dev/build-macos-release.sh --preset macos_x86_64 while hosted Intel runners are unreliable (#102, #103, #105, #106, #149).
  • Linux binary tarball release artifact alongside the AppImage: same static-linked single-binary layout, drop-in for distros where AppImage FUSE is a hassle. libxtst-dev added to the tarball build deps after the first CI run surfaced the missing X11 link (#124, #125).
  • Post-release smoke-test script (scripts/dev/verify-release.sh) downloads each published Linux artifact, runs the discovery self-tests, and reports per-asset pass/fail. Pointers added to the releasing recipe and announcement steps (#126, #127).
  • Rolling latest pre-release: every push to main triggers a CI build of all three platforms (Linux AppImages, Windows ZIP, macOS DMG) and force-updates a latest-tagged GitHub Release. Marked pre-release; the most recent stable tag keeps GitHub's "Latest" promotion. Lets community testers grab bleeding-edge main builds without cloning + building (#108) — see docs/RELEASING.md "Rolling latest pre-release".
  • Release workflow hardening: low-friction release-target additions, CI path filters so doc-only pushes skip the heavy matrix, pinned setup-sdl SHA + per-arch cache discriminator after a stale-cache drift incident (#129, #130).

CI & developer workflow

  • clang-tidy static-analysis pass wired into CI, with a tuned check-list that silences bulk-noise categories (Watcom-era idioms, C-style casts in ported code) while keeping the high-signal UB / memory-safety / portability checks. The first analysis pass produced the UB-hardening fixes listed above (#139, #141).
  • Docs-only gate for the required build/test checks so README/CHANGELOG edits don't block on the full matrix ([#136](https://github.c...
Read more

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 09 May 19:18

What's Changed

The list below is hand-curated from merged PRs and tells the story of the
fork's evolution.

Highlights since the fork

The biggest architectural shift is portability. The playable game now builds as pure C/C++ against SDL3 and libsmacker, with no ASM toolchain on the build path. The original 16/32-bit x86 assembly is still in the tree as the source of truth and powers the ASM↔C++ equivalence tests, but it no longer runs in the playable binary. Native builds for Linux x86_64, macOS arm64/x86_64, and Windows (MSYS2 UCRT64). Community members are already running it on handheld devices in the wild.

Stability & 64-bit hardening

  • Magic-school grand-wizard crash on 64-bit hosts — fixed (#78, #84). U32+pointer-wraparound trap in CopyMask, pinned by a host-only regression test.
  • Renderer-side U32+pointer-wrap class named, documented, and swept across ~80 files in SVGA, pol_work, 3D, 3DEXT, GRILLE, INTEXT — one bug fixed (above), six "safe by convention" latent traps recorded (#85, #86, #87, #88, #89, #90) - Endgame credits segfault on 64-bit hosts — fixed (#65, #66)
  • Legacy 32-bit .lba saves load safely on 64-bit builds, with a corpus harness to prevent regressions (#62, #63)
  • 32-bit on-disk ABI rule and credits HQR layout codified in docs (#67)
  • Linux exterior draw-order bug — SinTab/CosTab arrays merged into a single contiguous block to match the original ASM layout (#55)
  • Compiler warnings cleaned up across GCC, Clang, and MinGW (#52)
  • Bezier C++ memory corruption — fixed (#8)

New features (opt-in)

All new features default-off or default-to-original-behavior.

  • Quake-style debug console: built-in, always available, configurable toggle key, host-tested (#23, #24, #60) — see docs/CONSOLE.md
  • SDL3 gamepad support (#38, #58)
  • Optional exterior auto-camera centering (FollowCamera) (#37, #50) — see docs/CAMERA.md
  • Menu mouse QoL: hover, selection, marquee for long text, keyboard takes priority over mouse hover (#41, #53, #56)
  • Original Adeline debug tools re-enabled behind DEBUG_TOOLS=ON (#17, #22) — see docs/DEBUG.md

Game data, config, and UX

  • Auto-discovery of retail game data (./data, ../LBA2, LBA2_GAME_DIR, --game-dir); embedded default config; clearer startup errors
    (#54) — see docs/GAME_DATA.md
  • Menus reorganized to match LBA2 Original from GOG (#42)
  • Menu language selection (#42)
  • Menu music and jingle handling — fixed (#43)
  • Case-sensitive music paths on Linux — fixed (#12)

Audio/video backends

  • SDL audio backend with Miles parity gap tracking (#27, #28, #9)
  • SDL renderer presentation path with LTO and inline rotates (#11, #51)
  • Early SDL bring-up for renderer/audio in the fork's first cross-platform phase, plus follow-up SDL3 AIL-linking fixes (#6, #9, #11, e9ba7d8)
  • Smacker FMV via libsmacker; video audio routed through the AIL backend (#10, #29, #35)

Ported (ASM → C++)

  • Object display, rendering helpers, and supporting math via the ASM-validation framework, with byte-for-byte equivalence tests (#31, #32)
  • Early renderer/SVGA/object-display ASM→CPP ports (polygon fillers, blitters/sprites, and object rendering), which established the base for later validation-driven porting work (#6)
  • See docs/ASM_TO_CPP_REFERENCE.md for the full port-status table.

Cross-platform & build

  • First Linux release artifact: AppImage built in CI from an any-linux base, packaged as a single portable binary (#74)
  • First Windows release artifact: a portable ZIP (lba2cc-<version>-windows-x64.zip) built in CI under MSYS2 UCRT64 with SDL3 and the GCC C++ runtime static-linked. Drop the single lba2cc.exe into your existing LBA2 install folder — no DLLs, no installer, no registry entries. Follows the same release-workflow conventions as the AppImage flow (matrix build → idempotent gh-release attach), see docs/RELEASING.md.
  • Product metadata (executable name, display name, description, desktop ID) centralized into CMake cache variables so the runtime window title, .desktop entry, and AppImage script all consume one source (#83). Window title now shows the version (e.g. LBA2 Classic Community 0.9.0-dev) — previously just LBA2.
  • Default binary renamed lba2 → lba2cc to avoid colliding with the retail executables (lba2.exe, tlba2.exe, tlba2c.exe). Path is now build/SOURCES/lba2cc[.exe]. Override-able via -DLBA2_EXECUTABLE_NAME=... for anyone shipping a custom branded build. - Windows .exe now ships with an embedded application icon and populated File Properties → Details (Product Name, File Description, Product Version, Original Filename). Strings flow from the sameLBA2_* cache vars that feed the runtime window title, .desktop entry, and AppImage labels. Pre-release versions (anything carrying -dev/-dirty/-rc suffix) are flagged with VS_FF_PRERELEASE in VERSIONINFO.
  • macOS CI build (arm64); macOS x86_64 preset (#52)
  • Portable Windows build via MSYS2 UCRT64 (#14) — see docs/WINDOWS.md
  • CMake presets for Linux, macOS arm64/x86_64, Windows UCRT64, and cross-compile Linux→Windows (#21)
  • SDL3 throughout (audio, video, renderer)
  • Default Windows preset switched to Release
  • clang-format with checked-in config; ASM and LIB386/libsmacker/ excluded (#36)
  • Host-only discovery tests run on Linux/macOS/Windows in CI without Docker or retail files (#54)

Tests

  • ASM↔CPP equivalence test framework with byte-for-byte assertions and randomized stress rounds (#31) — see docs/TESTING.md
  • Polyrec replay with --bisect for finding the first divergent draw call between ASM and CPP renderers
  • Tightened assertions across POLYFLAT, POLYGOUR, POLYDISC, POLYTEXT, POLYGTEX, AFF_OBJ, FPU precision, and the resampler (#44)

Documentation

Read more