pack-dist copies OpenAL32.dll out of the build machine's SysWOW64. That is the same shape as the d3dx9_43 bug found this week - a runtime dependency the dev box quietly satisfies - except here it does not fail outright, it just means the audio implementation a customer gets is decided by whatever was installed on the machine that happened to pack the release. And what that machine has is Creative's 2009 stack. OpenAL32.dll v6.14 is only a router; the real implementation is wrap_oal.dll v2.2.0.5 beside it, with oalinst.exe bundled as a 791KB fallback installer. The router exists so several vendors' OpenAL implementations could coexist on one machine, which stopped being a thing about fifteen years ago. Bundling OpenAL Soft instead is the fix: vendored like steam_api.dll is, not lifted from the system. wrap_oal.dll and oalinst.exe go, dist gets 1.2MB smaller, and the same implementation runs on Windows and Linux - which matters more than the size, because it means an audio bug reproduces on both instead of being someone's Windows. It also removes the only __declspec in the tree, which lives in Creative's al.h. Only al.h, alc.h and efx.h are ever included, eleven sites across ten files. efx-creative.h, EFX-Util.h and xram.h are vendored and referenced by nothing, so they simply go. Filed as a near-term item rather than a port phase, and written up in both docs, because it stands on its own: OpenAL is not what makes the game hard to move. The API is cross-platform and all ninety-one call sites compile unchanged against libopenal - it is the reason audio barely appears in the port plan at all. Dropping OpenAL itself would mean reimplementing EFX reverb and rewriting ninety-one working call sites to remove a dependency that is not in the way. The note says so, so nobody talks themselves into it later. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
314 lines
21 KiB
Markdown
314 lines
21 KiB
Markdown
# RP 4.12 on Linux — compatibility assessment
|
||
|
||
Requirements exploration for making RP412 playable on Linux (desktop and Steam
|
||
Deck). A formatted copy of the original assessment lives at
|
||
<https://claude.ai/code/artifact/47e4cf5f-a9a3-416e-bd5e-25c810983551>.
|
||
|
||
**Status: it runs.** The assessment below was written 2026-08-12 as prediction;
|
||
on 2026-08-14 a field test on real hardware found and cleared the one blocker,
|
||
and the game played a full mission on Linux. §0 is what the test found and what
|
||
was done about it — read it first, it corrects several predictions in §2. The
|
||
remaining work is the Deck-specific and DXVK items in §0's open list.
|
||
|
||
**Verdict: target Proton as the official Linux path; treat a native port as a
|
||
separately-scoped future project.** The existing 32-bit Win32/D3D9 build is
|
||
squarely on Proton's best-supported path, every Proton-blocking issue found is
|
||
fixable inside the Windows build, and Valve's Deck Verified program accepts
|
||
Windows-only titles. A native port is a genuine second platform layer
|
||
(~77k lines of `MUNGA_L4` plus ~6,300 lines of raw Win32 UI in `RP_L4`) and it
|
||
forces the 32/64-bit question, which reaches into the wire protocol and the
|
||
`.RES` format. Nothing on the Proton path is throwaway: each fix is an
|
||
improvement to the shipping Windows build.
|
||
|
||
Provenance: code findings below are measured against this repo (four
|
||
exploration sweeps over `MUNGA`, `MUNGA_L4`, `RP`, `RP_L4`, `DivLoader` and
|
||
the build files, 2026-08-12, main @ 1e0c412). External claims — Proton/DXVK
|
||
behavior, Steam Runtime direction, Deck Verified criteria — are from Valve
|
||
partner docs, the DXVK source, the steam-runtime repository, and Wine release
|
||
notes as of August 2026, with unconfirmed items called out as needing live
|
||
tests.
|
||
|
||
## 0. Field test, 2026-08-14 — one stubbed function, and a Windows bug behind it
|
||
|
||
Test environment: Linux Mint 22.3, RTX 5070 Ti (driver 595.84), Wine 10.0
|
||
Staging from GE-Proton10-21, 32-bit prefix, **wined3d** renderer, windowed
|
||
1920×1080, symbols from the shipped `rpl4opt.pdb`. Build 4.12.233.
|
||
|
||
**The blocker was `D3DXConcatenateMeshes`.** Wine has never implemented it.
|
||
The game calls it exactly once per session, from `ConsolidateStaticObjects`
|
||
([MUNGA_L4/L4VIDEO.cpp](../MUNGA_L4/L4VIDEO.cpp)) via
|
||
[MUNGA/APP.cpp](../MUNGA/APP.cpp)'s first-mission-load latch, so every player
|
||
hits it on their first race. Wine raised `EXCEPTION_WINE_STUB` (0x80000100)
|
||
naming the function in plain text; our own crash filter caught it and wrote
|
||
`rpl4crash.dmp`, so it presented as a mystery crash. Of Wine's 329 `d3dx9_43`
|
||
exports, 63 are stubs; the game imports 20 of them; exactly one is stubbed.
|
||
There is no second landmine on the current code path.
|
||
|
||
**The fix was already a Windows bug.** `d3dx9_43.dll` is a *hard* import of
|
||
`rpl4opt.exe` — confirmed in the import table, and only `steam_api.dll` is
|
||
delay-loaded — and it is **not part of Windows**. It ships solely with the
|
||
June 2010 DirectX End-User Runtime. A customer on a clean Windows 10/11 who
|
||
has never installed that redistributable cannot start the game at all: the
|
||
loader fails at 0xC0000135 before `WinMain`, with no window and no log line.
|
||
Every machine this was built or tested on had the SDK or some older game
|
||
installed, which is why it went unnoticed. `pack-dist.ps1` now ships the DLL
|
||
beside the exe, extracted from the SDK's redistributable cab. Wine prefers an
|
||
application-directory DLL over its own builtin, so the same one file closes
|
||
the Windows failure and the Linux blocker together — verified working on a
|
||
stock prefix with no overrides and nothing installed.
|
||
|
||
> **Licensing:** the DirectX SDK terms nominate DXSETUP as the delivery
|
||
> method for this redistributable. Shipping the loose DLL is near-universal
|
||
> practice and is what is verified working on both platforms, but confirm the
|
||
> terms before a public release.
|
||
|
||
**Address space, not memory.** The 32-bit process mapped **VmSize 3193 MB**
|
||
under Wine, and on an older Wine that exhausted the address space outright —
|
||
the game died before video init with nothing useful logged. It is not using
|
||
that much memory: measured on Windows, a standalone mission peaks at **166 MB
|
||
committed / 179 MB working set against 795 MB of address space**, already a
|
||
~4.8× ratio. Address space is inflated by reservations, file mappings, and
|
||
above all the graphics driver mapping VRAM apertures and its own bookkeeping
|
||
into the process; under wined3d's OpenGL path with an NVIDIA driver that grows
|
||
several-fold again. For a 32-bit process this is a hard 2 GB ceiling entirely
|
||
separate from RAM, so it is possible to run out of *addresses* while using
|
||
170 MB. `/LARGEADDRESSAWARE` is now set: it raises the ceiling to 4 GB on any
|
||
64-bit host, frees no memory and changes no behaviour. Worth having on Windows
|
||
too — 795 MB of 2 GB is already 40% before 1080p, eight pods, and the 100 MB
|
||
buffer `RP412RECORDSIZE` takes when recording is armed.
|
||
|
||
**Diagnostics added, so the next gap names itself.** The crash filter now
|
||
decodes 0x80000100 and writes the offending `module.function` to `rpl4.log`
|
||
before the minidump, and startup probes `wine_get_version` in `ntdll` and logs
|
||
the platform next to the version banner. Neither line can ever appear on
|
||
Windows. Together they would have reduced this entire investigation to reading
|
||
one line of a tester's log.
|
||
|
||
**Do NOT install the VC++ redistributable into a Wine prefix.** It *causes* a
|
||
crash — a null dereference inside `std::mutex`, reached from
|
||
`KeyLight_SetLogger` via `PadRIO::PadRIO`. Wine's builtin C++ runtime works
|
||
correctly. This is a support-desk trap worth knowing, because installing the
|
||
redistributable is a common first instinct. (On real Windows the VC++ runtime
|
||
is genuinely required; Steam installs it as a depot prerequisite.)
|
||
|
||
### Predictions in §2 that the test corrected
|
||
|
||
- **The `windowsapp.lib` static-import worry was wrong.** §2 called it a
|
||
load-time failure risk under Wine. The game boots, and the VC++ trace above
|
||
runs *through* `KeyLight_SetLogger` — that code executes. Wine satisfies the
|
||
WinRT API-set imports.
|
||
- **`d3dx9` was the right area, the wrong function.** §2 named
|
||
`D3DXLoadMeshFromXA` and `D3DXCreateTextureFromFile` as the heaviest leans on
|
||
Wine's reimplementation. Both are fully implemented. The lesson is method:
|
||
rank by *what Wine stubs*, not by what the game uses most — intersecting the
|
||
import table against Wine's stub list yields the answer directly.
|
||
- **Address-space exhaustion was not predicted at all**, and static analysis
|
||
would not have found it.
|
||
|
||
### Still open after this test
|
||
|
||
- **DXVK is untested.** The test ran wined3d; Proton defaults to DXVK for
|
||
D3D9. The §2 headline risk — `Present` into a child `STATIC` via
|
||
`hDestWindowOverride` with GDI panes clipped over it — is specifically a
|
||
DXVK question, and whether the run exercised cockpit split mode
|
||
(`L4MFDSPLIT=1`) at all is not recorded. **This is the next test**, and the
|
||
rig now exists to do it cheaply.
|
||
- **Steam networking under Proton** — SDR/FakeIP through the lsteamclient
|
||
bridge. The test's networking was almost certainly plain TCP.
|
||
- **Everything Deck-specific**: gamescope with the plasma window and the
|
||
exploded MFD view, the gamepad-complete front end, the 9 px font floor.
|
||
- **Optional load-time polish, both platforms**: 303 `d3dx_load_pixels_from_pixels`
|
||
calls (texture format conversion at load — shipping textures in device format
|
||
skips it) and 150 `OptimizeInplace` calls (the `.X` loader; baking meshes to
|
||
an in-house format at build time would retire the `.X` dependency).
|
||
- **Swap Creative's OpenAL for OpenAL Soft** — the same shape of bug as the
|
||
`d3dx9_43.dll` one above: `pack-dist.ps1` copies `OpenAL32.dll` out of the
|
||
build machine's `SysWOW64`, and what ships is Creative's 2009 router +
|
||
`wrap_oal.dll` + a 791 KB installer. Bundling OpenAL Soft instead means the
|
||
same implementation runs on Windows and Linux. Scoped in
|
||
[RP412-UNIFIED-BUILD.md](RP412-UNIFIED-BUILD.md) — near-term, independent of
|
||
the port.
|
||
- **The native port** is scoped separately in
|
||
[RP412-UNIFIED-BUILD.md](RP412-UNIFIED-BUILD.md) (proposal, not scheduled).
|
||
Its survey supersedes several figures in §1 and §3 below — notably that
|
||
`/Zp1` affects exactly one struct, that the `<windows.h>` leak into engine
|
||
core is `NETWORK.h` rather than `MATRIX.h`, and that ISO conformance rather
|
||
than include hygiene is what actually blocks a GCC compile.
|
||
- **The durable fix for the blocker**: replace `D3DXConcatenateMeshes` with a
|
||
hand-written mesh merge, ~60–80 lines using only helpers Wine implements
|
||
(`D3DXCreateMesh`, `D3DXGetDeclVertexSize`, `D3DXVec3TransformCoordArray`,
|
||
`D3DXMatrixInverse`, …). Worth doing eventually because `D3DXSimplifyMesh`
|
||
and `D3DXSplitMesh` are stubs too, so any future mesh work hits the same
|
||
wall. Verify by comparing vertex/face counts and attribute ranges against
|
||
the D3DX output on Windows.
|
||
|
||
## 1. How the Windows dependency actually distributes
|
||
|
||
The architecture is far more portable than the mechanics. The engine is
|
||
single-threaded by construction, has exactly one timing seam (`SystemClock`,
|
||
implemented in [MUNGA_L4/L4TIME.cpp](../MUNGA_L4/L4TIME.cpp)), one file-I/O
|
||
seam ([MUNGA/FILESTUB.cpp](../MUNGA/FILESTUB.cpp), six syscalls), a formal
|
||
network transport interface with all real socket calls in one ~350-line class
|
||
([MUNGA_L4/L4NETTRANSPORT.cpp](../MUNGA_L4/L4NETTRANSPORT.cpp)), and audio
|
||
already speaks OpenAL. But the mechanical surface is total: **1,800 of 1,844
|
||
quoted `#include` lines cite a filename in a case that doesn't exist on disk,
|
||
and 334 use backslash separators** — on a case-sensitive filesystem, nothing
|
||
compiles until a repo-wide normalization pass is done.
|
||
|
||
| Tree | Lines | Portability |
|
||
|------|-------|-------------|
|
||
| `RP\` | 16,441 | **Clean.** Zero Win32 API, zero Win32 types, zero threads. Only 16 `stricmp` and 4 `getenv`. |
|
||
| `MUNGA\` | 116,695 | **~90% clean** (267 of 297 files carry no Win32/MSVC token). The leaks are few and named: `PostQuitMessage` as the abort path (32 sites in 9 files; the portable prototype still sits commented out at `MUNGA/STYLE.H:31`), [MUNGA/MATRIX.h](../MUNGA/MATRIX.h) line 4 including `<D3DX9.h>` (which transitively feeds `windows.h` to most of the engine), `HWND`/`SOCKADDR_IN` carried as opaque values in a handful of headers, and `SPOOLER`'s `CreateDirectoryA`/`CopyFileA`. |
|
||
| `RP_L4\` | 22,588 | **Mixed.** 20 of 34 files clean. The rest is `WinMain`/`WndProc` ([RP_L4/RPL4.CPP](../RP_L4/RPL4.CPP)) plus ~6,300 lines of hand-written Win32+GDI UI (front end, lobby, console board) behind no platform interface. |
|
||
| `MUNGA_L4\` | 76,575 | **The platform layer.** 41 of 100 files touch D3D9 / DirectInput / XInput / Winsock / serial / WinRT / Steam. This is what a native port rewrites. |
|
||
| `DivLoader\` | 1,714 | **No callers.** Entirely non-portable (D3D9 in its public API, an implementation-defined bitfield read off disk) — and nothing in this tree calls it. Delete, don't port. |
|
||
|
||
Inventories that came back smaller than expected:
|
||
|
||
- **Input**: the only XInput entry point used anywhere is `XInputGetState`
|
||
(3 call sites). The live DirectInput reader
|
||
([MUNGA_L4/L4JOY.cpp](../MUNGA_L4/L4JOY.cpp)) models 8 axes / 32 buttons /
|
||
4 hats — a direct match for SDL's joystick model.
|
||
- **Audio**: OpenAL (~91 calls) + libsndfile (3 calls), **zero**
|
||
winmm/mmsystem anywhere. EFX is already optional and self-degrading. One
|
||
engine-core leak: `MUNGA/AUDIO.cpp` calls three `al*` functions directly.
|
||
- **Networking**: TCP only, no UDP, no `select()`/`poll()` — a pure per-frame
|
||
nonblocking poll loop branching on exactly three error codes
|
||
(`WSAECONNREFUSED`/`WSAECONNRESET`/`WSAEWOULDBLOCK`).
|
||
- **Steam**: four interfaces total (NetworkingSockets, NetworkingUtils,
|
||
Matchmaking, Friends+User), all gated behind the
|
||
`SteamNetTransport_ClientLibraryPresent()` probe. The vendored SDK already
|
||
ships `linux32/linux64 libsteam_api.so`. Steam Input is not used.
|
||
- **Renderer**: 100% fixed-function D3D9 — no shaders, no D3D9Ex. All ~193
|
||
device calls sit in five `MUNGA_L4` files. D3DX usage is mesh loading
|
||
(`D3DXLoadMeshFromXA`), texture loading (PNG), and matrix helpers.
|
||
|
||
The leaky seam is **windowing**: of ~430 user32/gdi32 call sites, about
|
||
two-thirds live *outside* the platform layer, and the per-frame message pump
|
||
sits in engine core (`MUNGA/APPMGR.cpp:126`). The cockpit composition is
|
||
genuinely exotic Win32: up to 11 top-level windows, D3D `Present` into a child
|
||
`STATIC` control via `hDestWindowOverride`, GDI-painted panes clipped over the
|
||
3D viewscreen, and hit-testing that relies on the stock STATIC proc returning
|
||
`HTTRANSPARENT`.
|
||
|
||
## 2. Path A — Proton as the official Linux target (recommended)
|
||
|
||
External findings (August 2026): 32-bit D3D9 through DXVK is Proton's
|
||
most-traveled path and is deliberately preserved — Steam Runtime 4 dropped
|
||
most i386 libraries *except* the ones Proton needs. Wine 10 (in current
|
||
Proton) merged Vulkan child-window rendering, removing the one historical
|
||
blocker that would have hit the viewscreen pane directly. Valve's stated
|
||
position is neutral between native and Proton, Deck Verified is achievable
|
||
with a Windows-only build, and the prevailing small-studio practice for
|
||
legacy Win32 titles is to ship Proton officially. Wine resolves filenames
|
||
case-insensitively, so the repo's asset-case inconsistencies cost nothing on
|
||
this path.
|
||
|
||
One dated fact that matters: a 32-bit-specific Proton bug corrupted
|
||
`ISteamNetworkingSockets::ReceiveMessagesOnConnection` data — exactly RP412's
|
||
receive path — and was fixed only in **Proton 10.0-4b / 11.0-1b (July 2026)**.
|
||
The multiplayer test matrix should pin that as the minimum version.
|
||
|
||
### Fix in the Windows build (confirmed in code)
|
||
|
||
> Written 2026-08-12 as prediction. The first row is **disproven** by the
|
||
> field test and is kept only so the reasoning is on the record; rows 2–5
|
||
> stand and remain Deck work. See §0.
|
||
|
||
| Item | Evidence | Candidate fix |
|
||
|------|----------|---------------|
|
||
| ~~**WinRT lamp mirror is a static import.**~~ **WRONG — see §0.** The reasoning was that `windowsapp.lib` is not delay-loaded and Wine has no `windows.devices.lights`, so the image would fail to load before the in-code guards could run. In practice Wine satisfies the WinRT API-set imports and the game boots; `KeyLight` code demonstrably executes. | [MUNGA_L4/L4KEYLIGHT.cpp](../MUNGA_L4/L4KEYLIGHT.cpp) line 33 | None needed. |
|
||
| **Second top-level window fights gamescope.** The "Plasma Display" window plus the exploded `L4MFDSPLIT=2` mode (6 top-level panes) hit gamescope's known multi-window weakness on Deck. | [MUNGA_L4/L4PLASMASCREEN.cpp](../MUNGA_L4/L4PLASMASCREEN.cpp) line 271, [MUNGA_L4/L4MFDVIEW.cpp](../MUNGA_L4/L4MFDVIEW.cpp) line 297 | On Deck/gamescope, default the plasma glass into the cockpit window (the `SetParent` path already exists at `L4PLASMASCREEN.cpp:91`) and treat exploded view as desktop-only. |
|
||
| **Implicit hit-test transparency.** Clicks fall through the viewscreen only because the stock STATIC proc returns `HTTRANSPARENT`; no explicit `WM_NCHITTEST` handler exists. If Wine's STATIC differs, all mouse input over the 3D view dies silently. | [MUNGA_L4/L4VB16.cpp](../MUNGA_L4/L4VB16.cpp) line 4659 | Handle `WM_NCHITTEST` explicitly in the viewscreen subclass. Removes the dependency on an undocumented control behavior everywhere. |
|
||
| **Deck legibility floor.** The 1920×1080 canvas lands on Deck at 1280×720 — a 1.5× shrink. Valve's gate is 9 px minimum font height, so anything under ~14 px on the canvas fails. | Fixed canvas: [MUNGA_L4/L4APP.cpp](../MUNGA_L4/L4APP.cpp) line 269 | Audit the smallest cockpit and front-end type; bump or provide a Deck-scale preset. |
|
||
| **Gamepad-complete front end.** The menu/lobby is mouse-driven GDI; callsign entry is a Win32 `EDIT` control. Deck Verified requires the full flow on pad alone, with the Steam on-screen keyboard for text. | [RP_L4/RPL4FE.cpp](../RP_L4/RPL4FE.cpp) line 2467 | Add pad navigation to the front end and invoke `ShowFloatingGamepadTextInput` for the callsign field. The largest Path-A work item. |
|
||
|
||
### Verify under Proton (status as of the 2026-08-14 field test)
|
||
|
||
- **Child-window Present**: `Present(…, hDestWindowOverride)` into a child
|
||
STATIC under DXVK — supported per the DXVK source (per-HWND presenter map)
|
||
and Wine 10's child-window Vulkan work, but this exact composition (GDI
|
||
siblings clipped over the presented pane) is the least-exercised path in any
|
||
D3D9 stack. **STILL OPEN — the field test ran wined3d, not DXVK.** Next test.
|
||
- **Steam networking end-to-end**: repeat the three-machine SDR/FakeIP race
|
||
([STEAM-3-MACHINE-TEST.md](STEAM-3-MACHINE-TEST.md)) with one or more peers
|
||
on Proton ≥ 10.0-4b. No Proton-specific FakeIP defects are on record, but
|
||
absence of reports is not confirmation. **STILL OPEN.**
|
||
- ~~**Wine's d3dx9**: `D3DXLoadMeshFromXA` and `D3DXCreateTextureFromFile`~~ —
|
||
**DONE, and both are fine.** The stubbed import was `D3DXConcatenateMeshes`;
|
||
see §0.
|
||
- **Input odds and ends**: the `IG_` RawInput device-path heuristic for XInput
|
||
de-duplication (`L4JOY.cpp:72-146`) assumes Windows-shaped HID paths;
|
||
`GetAsyncKeyState` + foreground-window focus gating under gamescope's focus
|
||
model; the legacy DirectInput paths' `DISCL_EXCLUSIVE` claims.
|
||
- **The joyconfig wizard** (`AllocConsole` + `conio`, `L4JOY.cpp:811-1392`)
|
||
very likely misbehaves under Proton — it is optional, and pad/keyboard
|
||
defaults don't need it; document rather than block on it.
|
||
- **Multi-adapter gauge windows** (up to 4 D3D devices, one per adapter,
|
||
`L4VB16.cpp:207`) are thin ice under DXVK — but that's the arcade
|
||
multi-monitor config, not the consumer default. Confirm the default path
|
||
never creates them.
|
||
|
||
## 3. Path B — the native port, honestly scoped
|
||
|
||
Feasible — the clean core makes it a real option, not a rewrite of the game —
|
||
but it is a phased engineering project, not a compatibility fix:
|
||
|
||
0. **Mechanical compile pass — everything blocks on this.** Normalize include
|
||
case and separators repo-wide (267 of 289 distinct include names don't
|
||
match disk case; renaming files inside a case-insensitive checkout takes
|
||
care), shim the MSVC CRT tail (100 `stricmp`, the Annex K `_s` family, the
|
||
`_time64` family, `itoa`/`strlwr`, 9 `std::ostrstream` sites), fix
|
||
`Fail_To_Debugger`'s non-const `char*` signature (GCC/Clang reject it
|
||
outright, so the `DEBUG_LEVEL>0` build doesn't compile at all), and address
|
||
the `SKIPPY_CAST` strict-aliasing punning (`MUNGA/STYLE.H:108`).
|
||
1. **Drain the core leaks.** Replace `PostQuitMessage`-as-abort with the
|
||
portable hook whose prototype still sits commented out in `STYLE.H:31`
|
||
(32 sites); strip `<D3DX9.h>` out of `MATRIX.h` and give it a neutral
|
||
interop type; typedef-shim the `HWND`/`HINSTANCE`/`SOCKADDR_IN` values
|
||
carried in core headers; split `RIOBase` out of
|
||
[MUNGA_L4/L4RIO.h](../MUNGA_L4/L4RIO.h) so the controls stack stops
|
||
dragging `<windows.h>` through the serial-packet header; port `FILESTUB`
|
||
(six syscalls) and `L4TIME` (one file); add a path-separator/case layer to
|
||
`FILEUTIL` and the 19 hardcoded `"dir\\"` literals.
|
||
2. **The 32/64-bit fork — the strategic decision.** The wire protocol and
|
||
`.RES` format are `/Zp1`-packed host-order struct dumps carrying `size_t`
|
||
and `unsigned long` fields (`MUNGA/RECEIVER.h:172`,
|
||
`MUNGA/RESOURCE.h:208-210`). A **32-bit native build** keeps every format
|
||
byte-identical but targets Steam's legacy *scout* runtime — swimming
|
||
against the current. A **64-bit build** (what Steam Runtime 4 wants) means
|
||
retyping the serialized fields to fixed widths, discovering and
|
||
pack-annotating the serialized struct set (46 raw stream overloads, no
|
||
manifest exists), and versioning the wire so Linux and Windows builds can
|
||
still race each other. Dovetails with the un-`/Zp1` cleanup already on the
|
||
roadmap; if the native port ever happens, 64-bit with fixed-width formats
|
||
is the right side of the fork.
|
||
3. **The new platform layer.** SDL for window/events/input (XInput's one
|
||
function → `SDL_GameController`; `L4JOY`'s 8-axis/32-button model maps
|
||
directly to `SDL_Joystick`, and SDL makes the RawInput de-dup unnecessary);
|
||
OpenGL for the fixed-function renderer (~193 device calls in five files,
|
||
no shaders to translate — the real work is replacing D3DX mesh/texture
|
||
loading); the CPU-side gauge canvases become streaming textures instead of
|
||
GDI blits; OpenAL-soft is a near drop-in; sockets go BSD (mechanically
|
||
small: 3 errno branches, ~45 `SOCKADDR_IN` occurrences); Steam loads via
|
||
`dlopen` through the flat API (~30 symbols) since ELF has no `/DELAYLOAD`.
|
||
4. **Rebuild the desktop UI.** The front end, lobby, and console board —
|
||
~6,300 lines of GDI drawing and Win32 controls behind no interface — get
|
||
redrawn through the engine or SDL, and the ~580-line console-mode joyconfig
|
||
wizard gets a portable rewrite. This is also where the four separate
|
||
message pumps collapse into one event loop.
|
||
5. **Delete, don't port.** The legacy DirectInput paths (`L4DINPUT`,
|
||
`ThrustMaster`), DOS-era `L4KEYBD`/`L4MOUSE` stubs, the AWE32 audio layer
|
||
and `sos/`, `JOYSTICK.asm`, DivLoader (no call sites), and Windows Dynamic
|
||
Lighting (OS-exclusive by nature). The serial RIO path is optional hardware
|
||
support — stub it, or port it to termios only if pod preservation on Linux
|
||
ever matters.
|
||
|
||
## 4. Sequencing
|
||
|
||
The Path-A fixes (lamp-mirror delay-load, explicit hit-test, Deck window
|
||
policy, font audit, pad-driven front end) all improve the Windows build on
|
||
their own, and none are wasted if Path B ever runs. The reverse is not true.
|
||
When this resumes: an afternoon smoke test of the current build under Proton
|
||
on a Linux box — child-window Present first — before committing to anything.
|