Files
RP412/BUILD.md
T
CydandClaude Opus 5 4f34684b16 environ.ini is written on first run, not shipped
Packing one into every zip meant a tester who unzipped a new build over
their folder got their configuration replaced. bindings.txt has never had
that problem, because the exe carries the template and writes the file
only when it is absent. environ.ini now works the same way, so a new
build can land on an existing folder and every setting survives.

The 245-line template moves out of pack-dist.ps1 and into RPL4ENVIRON.cpp
as the exe's own literal, which also means the exe alone can produce a
working install. It was lifted mechanically rather than retyped, and the
file it writes is line-for-line identical to the one we have been
shipping - only the line endings changed, from a mongrel 243 LF plus one
stray CRLF that PowerShell's Set-Content left on the end, to the uniform
LF the game already writes bindings.txt with.

It cannot simply become optional. Without environ.ini, L4GAUGE is unset -
which disables the gauge renderer and takes every MFD with it - and
L4MFDSPLIT is unset, which is the packed-window arcade layout rather than
the glass cockpit. The shipped values ARE the desktop game; the built-in
getenv fallbacks are the 1995 pod. So the game writes the file rather
than tolerating its absence.

The cost of a file that is never overwritten is that a tester carrying
one across many builds stops being offered new options. Nothing breaks -
an option added later defaults to "behave as before" - but it goes
unnoticed, and "the podium does not work" is a confusing bug report when
the real answer is that their environ.ini predates RP412PODIUM. So the
load names every template key the player's file has never mentioned, and
says they are at built-in defaults and that deleting the file brings the
documented one back. A stale seven-line file lists all 40.

The file is read, never rewritten. The mention test is deliberately
generous - a key counts as known if it appears in any form, commented or
not - because the failure it guards against is worse than a missed
notice: environ.ini is applied line by line, so a second copy of a key
appearing later in the file would silently override the player's own.

The version line also moves to the top of WinMain. It used to print after
the environment was loaded, so the first thing in rpl4.log was a message
about environ.ini rather than which build wrote it.

Verified: the written file matches the old shipped one line for line; an
edited file with a hand-added comment survives another run untouched; a
seven-line file from an older build boots and names all 40 options it has
never heard of; and a full mission on a self-written file brings up the
glass cockpit at 125% with the virtual RIO active and nothing alarming in
the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:46:18 -05:00

237 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Building Red Planet 4.12 (Win32)
This is the Win32 source for the pod-racing game **Red Planet**, built on the
in-house **MUNGA** engine and its **L4** (Win32 / DirectX 9) platform layer.
As of RP 4.12 the build targets **Visual Studio 2022 (v143)**; the legacy
VS 2005/2008 projects are retained for reference (see §5).
> ✅ **Verified build (2026-07-12):** all 4 projects build clean (`Release|Win32`)
> with **VS2022 Build Tools 17.14** (MSVC 14.44, v143) + **Windows 11 SDK
> (10.0.26100)** + **DirectX SDK (June 2010)**. Outputs: `Release\rpl4opt.exe`
> (the game), `Release\RPL4TOOL.exe`, `lib\Munga_l4.lib`, `lib\DivLoader.lib`.
> Runtime-verified against the VC9 baseline built from the same tree: identical
> log output and behavior at every checkpoint, including RIO init against vRIO
> and mission load (see §4 for the pre-existing crash both builds share).
---
## 1. Requirements
| Component | Version | Notes |
|-----------|---------|-------|
| **VS2022 Build Tools** (or any VS2022 with C++ workload) | 17.x, MSVC v143 | Installed at `C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools`. Install via `winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"`. |
| **Windows 10/11 SDK** | any recent (26100 verified) | Comes with the VCTools workload. |
| **DirectX SDK (June 2010)** | — | Still needed for **d3dx9** and **dxerr** only; everything else now comes from the Windows SDK. The projects reference it via `$(DXSDK_DIR)`, which the SDK installer sets machine-wide. The DXSDK include/lib paths are appended **after** the Windows SDK paths (see the `IncludePath`/`LibraryPath` properties in the projects) so modern headers win — do not move them to `AdditionalIncludeDirectories`. |
### Bundled third-party libraries (already in the repo)
Committed under [lib/](lib/), no install needed: `OpenAL32.lib`, `libsndfile-1.lib`
(import libs). At **run time** the game needs `OpenAL32.dll` (system-wide install —
`assets/RP411/oalinst.exe` — or beside the exe) and `libsndfile-1.dll` (beside the
exe; a copy ships in `assets/RP411/`).
## 2. Building
```powershell
& "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\MSBuild.exe" `
WinTesla.sln /p:Configuration=Release /p:Platform=Win32 /m
```
`Debug|Win32` also builds. `x64` is not configured — this is a 32-bit build.
The solution is [WinTesla.sln](WinTesla.sln) with four v143 projects:
| Project | Type | Output |
|---------|------|--------|
| [MUNGA_L4/Munga_L4.vcxproj](MUNGA_L4/Munga_L4.vcxproj) | Static lib | `lib\Munga_l4.lib` |
| [RP_L4/RP_L4.vcxproj](RP_L4/RP_L4.vcxproj) | App (Windows) | `Release\rpl4opt.exe`**the game** |
| [RP_L4/RPL4TOOL.vcxproj](RP_L4/RPL4TOOL.vcxproj) | App (Console) | `Release\RPL4TOOL.exe` |
| [DivLoader/DivLoader.vcxproj](DivLoader/DivLoader.vcxproj) | Static lib | `lib\DivLoader.lib` (Release) |
Build order is resolved by `ProjectReference` (RP_L4 and RPL4TOOL both reference
Munga_L4).
**Versioning:** the patch number *is* the repository's commit count, so a
build always names the commit it came from and there is no question about
which changes a given binary contains.
[stamp-version.ps1](stamp-version.ps1) runs as RP_L4's pre-build step and
writes the generated, uncommitted `RP_L4\rpl4build.h`:
```
#define RP412_VERSION "4.12.96"
#define RP412_VERSION_LONG "4.12.96 (a1b2c3d)"
```
The game logs the long form on its first line. A trailing `+` on the hash
means the tree had uncommitted changes to tracked files when it was built —
useful when a test machine reports something a clean build cannot reproduce.
Only the `4.12` product line is set by hand, at the top of the script.
The header is deliberately not committed: the commit that recorded a
hardcoded number would itself change the count, so the file would be stale
the moment it landed. It is rewritten only when the stamp actually changes,
so ordinary rebuilds do not recompile `RPL4.CPP` for nothing. Building
outside a git checkout stamps `4.12.x (no repository)` rather than inventing
a number that would sort against real ones.
**Packaging:** [pack-dist.ps1](pack-dist.ps1) assembles a runnable game into
`dist\` (exe + PDB, game data, OpenAL/libsndfile runtimes, launch scripts,
HANDBOOK.html, README). It deliberately does **not** write `environ.ini`
the exe carries that template and writes it on first run
([RP_L4/RPL4ENVIRON.cpp](RP_L4/RPL4ENVIRON.cpp)), so a tester can drop a new
build over an old folder without losing their settings. Pass `-Zip` to also produce
`RedPlanet-<version>.zip` for handing to someone else. It reads the version
from `rpl4build.h` rather than asking git again, so the package and the
binary inside it cannot disagree, and it warns if the build it is packing
came from a modified tree.
## 3. VS2022 migration notes (what changed and why)
Settings preserved from the VC9 projects: **`/Zp1` struct packing** in Munga_L4
and RP_L4 (the engine's on-disk/on-wire binary formats depend on it — RPL4TOOL
and DivLoader never had it), Unicode, subsystems, x86, `/DYNAMICBASE:NO`,
optimization levels, and `/FORCE:MULTIPLE` (see below). Deliberate changes:
- **CRT unified to `/MD` (Release) / `/MDd` (Debug).** The VC9 build mixed
`/MT` (Munga_L4, RPL4TOOL) with `/MD` (RP_L4) and forced the link.
- **Import libs are no longer merged into `Munga_L4.lib`** by the librarian;
the exes link OpenAL32/d3d9/d3dx9/dinput8/dxguid/ws2_32 directly.
- `WINDOWS_IGNORE_PACKING_MISMATCH` is defined: the modern Windows SDK
`static_assert`s against `/Zp1`; the VC9 build always compiled Windows
headers at `/Zp1`, so this preserves those exact semantics. Un-`/Zp1`-ing
the engine (pragma-pack only the serialized structs) is future work.
- `_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS` is defined: the code still uses
`stdext::hash_map`; porting to `std::unordered_map` is future work.
- `legacy_stdio_definitions.lib` is linked: the June-2010 `dxerr.lib`
references pre-UCRT stdio symbols.
- **Source fixes** (behavior-preserving): standard copy-ctor/assignment
overloads added to `Time` (rvalues can't bind to `volatile&` in standard
C++); `operator==(SOCKADDR_IN&,...)` in `NETWORK.h` made `inline`; the
L4DINPUT callbacks renamed `DIEnum*` (they collided with L4CTRL's under
LTCG); `std::ios.in``std::ios::in` in `CAMMGR.cpp`.
- `/FORCE:MULTIPLE` is still required: `gOpNames`, `gReplacementData`
(hash_maps defined in a header) and `GlobalEggFileName` are genuinely
defined in multiple TUs (LNK4006 warnings, same as the VC9 build). Moving
them to a single TU is future cleanup.
## 4. Runtime notes
**Fixed (2026-07-12):** standalone mission load used to crash with an access
violation in `d3d_OBJECT::LoadTexture` ([MUNGA_L4/L4D3D.cpp](MUNGA_L4/L4D3D.cpp)):
`D3DXCreateTextureFromFileA` failures were never checked and the NULL texture
was cached and `AddRef()`ed. It crashed identically on VC9 — a latent defect
in the 2007 DPL→D3D9 port, exposed by the pod-skin textures (`VIDEO\player18`)
that a bare working copy doesn't contain (they normally come from the
replacement-material/presets path, `WTPresets``VIDEO\<name>.png`). Missing
textures now log `L4D3D.cpp couldn't load texture …` and render untextured;
the game boots to a running window with `-windowed -res 640 480 -egg TEST.EGG`
from a working copy like `assets/RP411/`.
For runtime debugging the v143 build produces full PDBs — run
`cdb -g -G -lines -y Release rpl4opt.exe ...` from the working directory
(cdb ships in this machine's Windows Kits).
### Running without the cockpit (Workstream A prototype)
Two new environment options remove the hardware dependency entirely:
- **`L4CONTROLS=PAD;KEYBOARD`** — selects **PadRIO**
([MUNGA_L4/L4PADRIO.cpp](MUNGA_L4/L4PADRIO.cpp)), an in-process RIO that
speaks the full RIO control surface (buttons, axes, lamps) from an XInput
controller + the PC keyboard. The stock `VTVRIOMapper` path runs unchanged.
Hot-plugging works; with no controller it falls back to keyboard only.
- **`L4PLASMA=SCREEN`** — renders the 128×32 plasma display as its own
desktop window ("Plasma Display", plasma orange, `L4PLASMASCALE` sets the
pixel size, default 4). It opens directly below the main view;
`L4PLASMAPOS=x,y` overrides. No COM port. Closing the window hides it.
- **`L4MFDSPLIT=1`** — assembles the **whole pod interior in one window**.
The pod hardware drove five monochrome MFDs from the color channels of
two video outputs (window 3 = upper MFDs in R/G/B, window 4 = lower
MFDs in R/G) and mounted the map display portrait. In split mode the
main game window becomes the cockpit shell and every display is a
chrome-less child pane, arranged as in the pod:
```
[ MFD UL ] [ MFD UC ] [ MFD UR ]
[ plasma (reduced) ][ viewscreen (centered) ]
[ MFD LL ] [ Map ] [ MFD LR ]
```
The cockpit is a fixed **1920×1080 internal canvas** (scaled down
uniformly on smaller work areas): the viewscreen fills the whole
canvas (launch with `-res 1920 1080` for native 1:1 3D), with compact
320×240 MFD glasses in the four corners, the score glass top-center,
and the portrait map bottom-center. The panes deliberately **overlap
the viewscreen and render over it** — in the pod the MFD bezels
partially occluded the viewport, and the same trick gives a
full-screen 3D view with the cockpit floating over its edges. MFDs render green-screen and the map full-color/rotated,
CPU-side from the shared gauge canvas (the packed D3D windows stay
hidden); the 3D scene presents into the viewscreen pane via
`Present`'s `hDestWindowOverride` (`gMainPresentWindow`), clipped
around the panes (viewscreen pinned to the bottom of the z-order).
Mouse clicks over the viewscreen fall through to the game window
(STATIC pane hit-test transparency). The plasma glass is currently
hidden in cockpit mode. This replaces the external BitBlt-mirror
launcher wrapper.
Each split window also carries its display's **physical button bank**
(geometry per vRIO's `CockpitLayout`): 4 red buttons above and below
each MFD glass (RIO addresses descending from the cluster anchor —
upper left 0x2F, upper center 0x27, upper right 0x37, lower left 0x0F,
lower right 0x07), and 6 amber buttons down each side of the map
(Secondary 0x100x15 left, Screen 0x180x1D right; the columns' other
addresses are Tesla relays, not buttons). They **light from the lamp
states the game commands** (dim/bright, flash modes animate) and press
the corresponding RIO unit with the mouse — active when `PadRIO` is the
control device, dark and inert with real serial hardware.
Bindings (vRIO's default profile, condensed):
| Input | Pod control |
|-------|-------------|
| Left stick / WASD | joystick X/Y |
| LT / RT, Q / E | left / right pedal |
| Right stick Y, PgUp / PgDn | throttle (rate; position holds) |
| A / Space | joystick trigger |
| B / R | reverse thrust (ButtonThrottle1) |
| DPad / arrows | joystick hat (look) |
| X, Y, LB, RB | pinky / thumb-low / thumb-low / thumb-high |
| Start / F1, Back / F2 | config buttons (AuxUpperRight 1 / 2) |
`L4PADFLIP=XY` (or `X` / `Y`) inverts the stick axes if the feel is wrong.
Example desktop `environ.ini`:
```ini
L4CONTROLS=PAD;KEYBOARD
DPLARG=1
L4DPLCFG=RPDPL.INI
L4GAUGE=640x480x16
L4PLASMA=SCREEN
L4MFDSPLIT=1
TARGETFPS=60
```
Lamp commands land in `PadRIO::lampState[]` — the hook for the planned
on-screen cockpit panel (vRIO buttons arranged around the displays).
Also note: `Fail()` compiles to `abort()` in release (`MUNGA/DEBUGOFF.h`) —
e.g. running with `L4CONTROLS=KEYBOARD` alone aborts at VTV creation because
no keyboard-only pod mapper exists (the old CRT showed a blocking R6010
dialog; the UCRT fail-fasts). Use `L4CONTROLS=RIO;KEYBOARD` with vRIO serving
the RIO side, exactly like the arcade config.
## 5. Legacy VS2005/2008 build
The original `.vcproj` files and solution are kept as
[WinTesla_vc9.sln](WinTesla_vc9.sln) and still build with VC++ 2008 Express
SP1 + DXSDK June 2010:
```bat
@echo off
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
set "DXSDK_DIR=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\"
vcbuild /nologo /rebuild WinTesla_vc9.sln "Release|Win32"
```
See [docs/BUILD-NOTES.md](docs/BUILD-NOTES.md) for the original repository
cleanup history and the findings behind the legacy build.