-
Notifications
You must be signed in to change notification settings - Fork 0
Developer Guide
Updated for v1.1.91. Release tags build and verify Linux, macOS, Windows, Android, and iOS artifacts in GitHub Actions.
cmake -S . -B build -DCMAKE_C_COMPILER=cc -G Ninja
ninja -C build- Pure C (C17 standard). No C++ code.
-
Compiler:
cc(system clang on macOS). Do not use gcc. - Build system: CMake 3.20+ with Ninja generator.
- Dependencies: SDL3, SDL3_ttf, zlib.
- Platforms: macOS (arm64), Linux (x86_64), Windows (x64), Android (arm64), iOS (arm64).
Always test both Debug and Release configurations. Test targets compile with assertions enabled in Release builds.
ctest --test-dir build -j4 --output-on-failure- 49 tests covering decoders, game logic, rendering, combat, save/load.
- Some viewport tests require original game data files and will fail or timeout without them.
- Run a subset with
-R <pattern>:
ctest --test-dir build -R "liberation" -j4 --output-on-failuresrc/
engine/ - Game engine: start_menu, combat, save_load, inventory, shop,
puzzle, droid_ui, terminal, map_gen, spawn, arcd_decoder,
liberation_plotgen
game/ - Liberation runtime: city_nav, dialogue, shop, building_interact,
combat, npc_dialogue, save
render/ - Rendering: hud, holamap, captive_compositor, renderer, viewport,
liberation_viewport_3d
audio/ - Audio: music, midi_player, sfx, adlib_sfx, opl2_emu
data/ - Data loading: data_vfs, sha256, gfx_loader, texture_atlas,
pl5/anm/rnc/ctv decoders, amiga_ofs/hunk/planar,
liberation_data/anim/vgm/x3g/img/fnt, i18n, iso9660
custom/ - Custom features: audio_reverb, automap, cross_save,
custom_features, debug_hud, lighting, minimap, replay,
upscale_xbrz
include/ - All public headers
tests/ - 49 test files
docs/ - Documentation and wiki
po/ - Translation files (19 languages)
data/ - Bundled fonts
assets/ - Icons (SVG, ICO, ICNS)
tools/ - hash_find, hash_extract, and other utilities
The start menu presents an 8-item navigation grid (2 columns x 4 rows):
| Column 0 | Column 1 |
|---|---|
| Captive (new game) | Liberation (new game) |
| Continue Captive | Continue Liberation |
| Settings | About |
| Controls | Quit |
- Continue items are only shown when saves exist for that game.
- Data status indicators: each game card shows a SHA-256 verification indicator (checkmark when all required data is present, cross when missing).
- Data Scanner (press D): scans the VFS for all required content hashes and reports results per game.
- Settings panel: 24 configurable options with scrolling.
- About screen: credits, version, technology.
- Controls screen: full keyboard reference.
GameState struct (~1.1 MB). Must be allocated as static or heap storage, never on the stack (Windows enforces a 1 MB stack limit).
Content-addressed asset loading via SHA-256. Supports directories, ZIP archives, nested ZIPs, ADF disk images, and ISO9660 tracks. Resources are identified by content hash, never by filename.
-
vfs_init()/vfs_free()— lifecycle -
vfs_find_sha256()— returns caller-owned buffer matching a digest -
vfs_file_exists()— check if a file is accessible -
vfs_read_file()— read a named file (for non-hash-identified resources)
8-item grid with settings, about, controls, and scanner overlays. Renders game card backgrounds, data status indicators, and navigation highlighting.
- OPL2 emulator for AdLib SFX (YM3812 FM synthesis).
- MIDI playback with OPL2 instrument bank.
- 8SVX sample loader and 8-channel mixer.
-
DataVFSowns archive indexing, not returned match buffers. -
vfs_find_sha256()returns caller-owned bytes. -
MusicSystemowns the current MIDI bytes until track replacement or stop. -
LiberationDataowns the raw disc buffer and closes it as one unit. -
GameState,CreatureListandPuzzleListare saved together for Captive.
The Captive save header has magic, version, campaign identity, party state, objective counters, gold and dynamic-list counts. The loader regenerates the deterministic base, overlays saved cells, validates every record and assigns only after complete success. Corrupt input leaves the active session intact.
Never extend a save record without incrementing its version and adding tests for old-version rejection and new-version round trip.
Never use original media filenames as identity. Introduce a resource with:
- A SHA-256 digest in source
- A content-hash lookup via VFS
- Checked lengths/offsets before decode
- A deterministic test or verifier
- No copied game payload in the repository
For Amiga OFS media, use amiga_ofs_inventory during discovery. For Liberation CD32 resources, use liberation_inventory. Both emit only digest, byte count and container class.
Do not use a generated renderer hash as proof of parity. Capture an original frame and an OpenCaptive frame at the same native resolution, then compare:
./build/visual_compare original.ppm opencaptive.ppm diff.ppmReports exact-pixel coverage and mean absolute RGB error. Keep captures outside the repository and record their SHA-256 digests in the test report.
- Source files:
src/{subsystem}/{feature}.c - Headers:
include/{feature}.h - Tests:
tests/test_{feature}.c
- Bump version in
CMakeLists.txtandinclude/opencaptive.h. - Commit and tag with
vX.Y.Z. - Push with
--tags— GitHub Actions builds all platforms and creates a release. - CI matrix: Ubuntu 24.04 (deb, rpm, AppImage, tar.gz), macOS 14 (DMG), Windows 2022 (Inno Setup installer), Android (APK via Gradle/NDK), iOS (IPA via xcodebuild).
- Preserve unrelated dirty work in the checkout.
- Add a focused test that fails before the change.
- Build Debug and Release.
- Run the complete CTest suite.
- Test hash-verified startup when changing loading or runtime code.
- Document implementation status honestly — do not label a placeholder as original-game parity.