Files
OpenRA3/README.md
T
EnderTheCoder b6d619254d v0.3.0: display abstraction, map-browser menu, SAGE terrain tiling/blends
- ra3.client::display: shared interactive loops; SDL and Vulkan backends
  implement only the primitives (init/present/poll_event/window_size/
  key_down/present_terrain). ra3.display picks the backend.
- Menu: maps by localized name (gamestrings.csf), red/gold theme, hover
  highlight, mouse + keyboard, wheel scroll, fullscreen and FPS/vsync
  options, loading progress bar.
- Terrain: continuous tile sampling via a texture array (REPEAT, uv =
  cell/(2*cellSize)) removes per-cell grid seams; SAGE blend ramp for
  material transitions; FPS label + top-right minimap overlays.
- Skip the skirmish sim for map views; reuse the Vulkan texture; no idle
  terrain redraw.
2026-09-20 02:20:39 +08:00

12 KiB

OpenRA3

A from-scratch, portable re-implementation of Command & Conquer: Red Alert 3 in C++26 using C++ modules and import std;, built with Clang for both Linux and Windows.

Red Alert 3 runs SAGE 2.0. EA never released that engine's C++ source, but it did open-source the closely related SAGE 1.0 engine as electronicarts/CnC_Generals_Zero_Hour (GPLv3). OpenRA3 uses that tree as the architectural reference and reconstructs the RA3-specific behaviour by decompiling the retail ra3_1.12.game binary with Ghidra.

OpenRA3 ships no game assets or binaries. You must own Red Alert 3. The engine reads your own local installation at runtime; nothing from it is ever copied into this repository. Command & Conquer and Red Alert are trademarks of Electronic Arts; this project is unaffiliated with and unsupported by EA.

Status

OpenRA3 is at v0.3.0. The engine compiles and runs headless, and a minimal skirmish is playable: it reads a real multiplayer map out of your install, recovers the player start waypoints, and simulates two sides building a base, extracting ore and fighting until one side is wiped out. The balance is the retail Red Alert 3 balance, pinned in ra3.data from EA's open RA3 XML: damage types, ArmorTemplate percentages, weapon target masks, build costs and the ore economy. Presentation is a Vulkan backend (ra3.vulkan) with a null fallback; the software renderer still produces headless images. Terrain tiles cross-fade the way the retail Terrain.fx does (a per-cell blend ramp plus a gutter-padded atlas), so material boundaries are smooth instead of a grid of hard lines. An in-window menu lists the maps by their localized name and exposes every render/skirmish option for tweaking before launch. The whole tree builds for Linux (clang + libc++) and cross-compiles to Windows (openra3.exe + SDL3.dll) with llvm-mingw — both using C++26 modules and import std;.

$ openra3 skirmish --game-dir "/game" --map map_mp_2_feasel4
OpenRA3 skirmish  map=map_mp_2_feasel4  source=archive:map_mp_2_feasel4  seed=1
start positions:
  P0 (1338, 1940)
  P1 (1291, 1404)
result: decided  winner=0  frames=9293 (309.8 s)
  P0 Commander  Allied  money=   700 units=15 kills=21 losses=15
  P1 AI         Soviet  money=   376 units= 0 kills=15 losses=21

Offline only

OpenRA3 implements offline skirmish and, eventually, LAN/single-player. There is deliberately no online mode: no EA account, no online service, no matchmaking, no GameSpy/Steam integration, and no networking in the simulation. The match loop is deterministic and self-contained so it can be replayed and tested without any external service.

Assets

The runtime reads a portable assets/ folder next to the executable — there is no --game-dir at run time. The build extracts it from your install:

  • openra3_assets (build target) runs cmake/extract_assets.cmake, which
    • extracts every map (double-unwrapped CkMp) and terrain TGA into assets/ via the engine's own openra3 extract;
    • with -DOPENRA3_EXTRACT_ALL=ON (default) also dumps every .big entry plus models, textures (.png), sound effects/voice and movie audio via the ra3-headless/ra3tools scripts.
  • CMake cache vars: RA3_GAME_DIR (default C:/Red Alert 3), RA3TOOLS_DIR (the ra3tools scripts), PYTHON_EXECUTABLE.
  • The step is skipped when the exe cannot run on the build host (a Windows cross-build in a Linux container); the exe then extracts maps/terrain on first launch, and the full dump can be run explicitly: cmake --build <build> --target openra3_assets on a native host, or openra3 extract --game-dir DIR --out DIR.

No game data is committed or distributed. reference/, assets/ and the ra3tools checkout are git-ignored. Without an install the engine falls back to a built-in test map so the project still builds and runs in CI.

Layout

Path Purpose
src/core/ra3.core.cppm fundamental types, math, strings, random, message stream
src/logic/ra3.logic.cppm objects, players, teams, spatial partition, game loop
src/client/ra3.client.cppm display abstraction + shared interactive loops + client facade
src/display/ra3.display.cppm picks the backend (Vulkan, then SDL) for the app
src/game/ra3.game.cppm RA3 sides, player templates, skirmish defaults
src/data/ra3.data.cppm retail RA3 balance: damage types, armour, weapons, units, economy
src/fs/ra3.fs.cppm BIG4 archives, RefPack codec, local install locator
src/map/ra3.map.cppm map catalog, EAR/RefPack unwrap, start waypoints, gamestrings.csf display names
src/skirmish/ra3.skirmish.cppm base building, economy, AI, combat, win condition
src/terrain/ra3.terrain.cppm CkMp terrain: HeightMapData, BlendTileData (tiles + blends), Terrain.big tiles
src/render/ra3.render.cppm ARGB framebuffer, TGA decode, BMP encode, map compositing, bitmap-font text
src/ui/ra3.ui.*.cppm SDL3 window viewer and menu (null backend when SDL3 is absent)
src/vulkan/ra3.vulkan.*.cppm Vulkan presentation backend and menu (null fallback without a loader)
src/ra3.cppm umbrella module re-exporting the SDK
apps/openra3/main.cpp menu / maps / skirmish / render CLI
tests/ra3_tests.cpp smoke tests (run via ctest)
tools/ reference fetch + Ghidra-driven reconstruction helpers
docs/ architecture, reverse-engineering notes, roadmap
Dockerfile Linux build image (dev toolchain + deploy runtime)
Dockerfile.win isolated Windows cross-build image (llvm-mingw + SDL3 MinGW)
cmake/toolchains/ llvm-mingw-x86_64.cmake cross toolchain
scripts/ build-linux.sh / build-windows.sh one-shot builders
.gitlab-ci.yml build both targets → test → package pipeline

Why Clang + import std;

The engine never #includes the standard library: every module does import std;. CMake's support for that (CXX_MODULE_STD) works today with Clang + libc++. Linux uses the distro clang (LLVM 21); Windows cross-compiles with llvm-mingw (clang 23 + libc++ + the libc++ std module). The two toolchains live in separate images so their compilers and standard libraries never interfere.

Build

The host needs no toolchain: each target builds inside its own image.

# Linux  -> build/linux/bin/openra3
scripts/build-linux.sh

# Windows -> build/windows/bin/openra3.exe (+ SDL3.dll)
scripts/build-windows.sh

Or drive Docker directly:

docker build --target dev -t openra3-linux:local .
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local \
    cmake -S . -B build/linux -G Ninja -DCMAKE_BUILD_TYPE=Release
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local cmake --build build/linux -j
docker run --rm -v "$PWD:/work" -w /work openra3-linux:local ctest --test-dir build/linux --output-on-failure

docker build -f Dockerfile.win --target dev -t openra3-windows:local .
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local \
    cmake -S . -B build/windows -G Ninja \
        -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/llvm-mingw-x86_64.cmake \
        -DCMAKE_BUILD_TYPE=Release
docker run --rm -v "$PWD:/work" -w /work openra3-windows:local cmake --build build/windows -j

Behind a slow or blocked mirror, pass --build-arg APT_MIRROR=<url>.

The two images are deliberately separate so the Linux (clang + libc++) and Windows (llvm-mingw + MinGW SDL3) toolchains never interfere. The Windows demo is build/windows/bin/openra3.exe next to SDL3.dll:

# show a real map in a Vulkan window
openra3.exe render --game-dir "C:\Red Alert 3" --vulkan

Vulkan is provided by vendored volk + headers (third_party/), resolved at runtime, so neither image needs a Vulkan SDK.

Running a skirmish

# list the maps in your install
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
    /work/build/linux/bin/openra3 maps --game-dir /game

# play a headless skirmish on a real map
docker run --rm -v "/path/to/Red Alert 3:/game:ro" openra3-linux:local \
    /work/build/linux/bin/openra3 skirmish --game-dir /game --map map_mp_2_feasel4 --seed 7

The Windows build is a native openra3.exe — copy it next to SDL3.dll and run it from cmd/PowerShell, e.g. openra3.exe skirmish --game-dir "C:\Red Alert 3".

--frames N caps the simulation length (default 15 minutes of game time at 30 Hz). The result is deterministic for a given map and seed.

Menu

Run openra3 with no arguments (or openra3 menu) to open a window listing the maps by their localized display name (read from the install's gamestrings.csf, e.g. map_mp_2_feasel4 → "Battlebase Beta") with their id below. The Options column exposes every render/skirmish parameter — mode (3D terrain / top-down / skirmish), window size, camera pitch/yaw/height, FOV, zoom, terrain scale, terrain pitch, world size, seed, frame cap, the overview thumbnail toggle and a BMP output path. Up/Down selects, Left/Right changes a value (or moves the text caret on the BMP field), Tab switches between the map list and the options, Enter starts and Esc quits. Starting a 3D or top-down view opens the viewer; closing it returns to the menu. When no window backend is available the same flow falls back to a console picker, and openra3 menu-preview renders one menu frame to a BMP for inspection.

Rendering the map

render draws the real terrain: it parses the map's HeightMapData (elevation grid) and BlendTileData (per-cell tile index plus the per-cell Blends/ThreeWayBlends and their BlendDescriptions), loads the tile textures from Data\Terrain.big (RefPack + TGA), and rasterises the map with an elevation shade. Each cell cross-fades into its blend neighbour with the same linear ramp the retail Terrain.fx uses, so material transitions are smooth; on the GPU path the tile atlas is padded with a replicated gutter so filtering never bleeds between tiles. Match state is overlaid (start markers in yellow, player 0 in blue, player 1 in red). --thumbnail uses the old <map>_art.tga overview instead.

Offscreen image (works anywhere, no display needed):

docker run --rm -v "/path/to/Red Alert 3:/game:ro" -v "$PWD/out:/out" openra3-linux:local \
    /work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4 --out /out/map.bmp

On Windows the same command runs natively: openra3.exe render --game-dir "C:\Red Alert 3" --map map_mp_2_feasel4 opens an SDL3 window.

Interactive window (Vulkan with --vulkan, otherwise SDL3). The camera follows the retail tactical view: it opens centred on the first player's start, the wheel zooms, pushing the cursor against a screen edge scrolls, dragging with the left button pans, and Esc quits. --zoom Z sets the initial zoom (1 fits the whole map). In a container you need an X server on the host — on Windows run VcXsrv and launch it with "Disable access control", then:

docker run --rm -v "/path/to/Red Alert 3:/game:ro" -e DISPLAY=host.docker.internal:0.0 \
    openra3-linux:local /work/build/linux/bin/openra3 render --game-dir /game --map map_mp_2_feasel4

If no display is available the viewer falls back to writing openra3_view.bmp. The unit overlay uses an approximate world scale (--world-size, default 5120); exact calibration from the map's heightmap is on the roadmap.

Reverse engineering

The reconstruction is driven by Ghidra against the retail binary. See docs/REVERSE_ENGINEERING.md for the workflow and the recovered symbol map, and tools/ for the helpers.

License

Project code: GPLv3, matching the CnC_Generals_Zero_Hour reference (see LICENSE).