Renderer Display and Widescreen - LordBlacksun/Starlancer-OSS GitHub Wiki
Renderer, Display & Widescreen
The render path is Surrender (SR) β srddraw.dll β DirectDraw7 / Direct3D7, with WinVFX
providing the 2D overlay (HUD, menus, sprites). All facts below were read in the decompilation
([verified]) unless marked [open].
The device/context struct
A single global pointer holds the live renderer/device context; almost all render and HUD code dereferences it:
DAT_00588730β the SR device/context struct (assigned= FUN_004C3830()during device init). [verified]DAT_005E6B50β the actual struct storage (~0x580C bytes). [verified]
Screen-geometry fields (offsets relative to DAT_00588730): [verified]
| Offset | Meaning | Default |
|---|---|---|
+0x1666 |
render width | 0x280 = 640 |
+0x166A |
render height | 0x1E0 = 480 |
+0x160A / +0x160E |
surface bit-depth / bytes-per-pixel | 0x10 / 2 (16-bit) |
+0x166E / +0x1686 |
projection X-scale / Y-scale (worldβscreen) | set by render-init |
+0x167A / +0x1692 |
projection X-centre / Y-centre | set by render-init |
+0x169E / +0x16A2 |
near / far clip | 100.0f / 100000.0f |
+0x15FA |
gamma | from INI |
+0x40,+0x54,+0x78,+0x84,+0x88 |
device vtable callbacks installed by SR_driver_init inside srddraw.dll (+0x40 = device render-init/buffer+viewport setup β consumes the projection, does not write it; then gamma, flip, surface-lock, render-loop) |
Startup & device bring-up
| Addr | Role |
|---|---|
0x004D1210 |
OEP / MSVC mainCRTStartup. [verified] |
0x004A8B10 |
WinMain: "StarlancerRunning" single-instance mutex; SR_MEM init; calls the DX probe; requires DirectX β₯ 7 (if (ver < 0x700) β fatal). Parses cmdline (-credits, -greyscale, -m<n>). Reads INI [Device] β width DAT_00595D84 (def 640), height DAT_00595D94 (def 480), Windowed DAT_005D54E4, Gamma DAT_005D6080, Tdetail/Gdetail/Lmaps/Transitions. [verified] |
0x004778C0 |
DirectX capability probe (not the real device-create): dynamically LoadLibrarys DDRAW.DLL/DINPUT.DLL, QIs DirectDraw2/4/7, probes DirectDrawCreateEx, returns an escalating version code. Confirms DDraw is dynamically loaded, not in the IAT. [verified] |
0x004A8880 |
Display-mode enumeration β reads dmodes.bin (header 0x102, a device count, then device records of 0x144C bytes into DAT_00595DA0), and WritePrivateProfileString("Device","renderdevice%d",β¦). Ends by resetting width/height to 640Γ480. [verified] |
0x004ACBE0 |
Real per-mode device init β DAT_00588730 = FUN_004C3830(); writes clip planes and hard-codes +0x1666 = 0x280 (640) and +0x166A = 0x1E0 (480); copies the selected mode record; loads the colour-cube; sets the viewport via FUN_004C3A60(0,0,1,1,0.6,0.8); invokes the set-projection vtable (*(β¦+0x40))(). This is what nails the back buffer to 640Γ480. [verified] |
0x004A8600 |
Apply-mode wrapper; on repeated failure falls back to a fixed 640Γ480 windowed mode. Persists detail/gamma to [Device]. [verified] |
0x0042E9B0 |
In-game video-options menu β steps through the enumerated mode list and reads width/height straight from the mode table. The engine only ever offers resolutions present in dmodes.bin (no free-form entry). [verified] |
Mode-table layout (base DAT_00595DA0; device stride 0x513 dwords; per-mode stride 0x12
dwords = 0x48 bytes): mode count (&DAT_00595FE8)[d*0x513]; per mode m: width
(&DAT_00595FEC)[d*0x513 + m*0x12], height (&β¦FF0)[β¦], bpp (&β¦FF4)[β¦]. [verified]
Surrender (SR) core
| Addr | Role |
|---|---|
0x004C3830 |
SR_init β allocates the device struct + ~10 scratch buffers (tagged surrenderlib). [verified] |
0x004C3A60 |
projection scale/centre writer β from width _DAT_005E81B6 (+0x1666) and height _DAT_005E81BA (+0x166A) and 6 viewport args it writes scale_x=(wβK)Β·sX β +0x166E, scale_y=(hβK)Β·sY β +0x1686, centres wΒ·0.5/hΒ·0.5 β +0x167A/+0x1692, and the 5 clip planes. Args pass sX=0.6,sY=0.8 (= 480/640 β 4:3). This is the Hor+ FOV patch point, not the +0x40 callback. [verified] |
0x004C9A40 |
texture cache (file magic 0x66, 1000 Γ 0xF0 descriptors); registers pixel formats ARGB8888 / ARGB4444 / RGB565. [verified] |
0x004CB9D0 / 0x004CBBD0 |
colour-cube (CCB) load / save β 256-entry palette + LUT for the palettized blit path. [verified] |
0x004BFF40 |
DirectDraw DDERR_* β string decoder (diagnostics). [verified] |
WinVFX 2D overlay
0x004A26D0 (init_vfx): loads winvfx8.dll (8-bit) or winvfx16.dll (16-bit) per the bit-depth
field, resolves ~28 VFX_* entry points, and builds the full-screen overlay pane sized directly
from the dimension fields: VFX_window_construct(*(β¦+0x1666), *(β¦+0x166A)) then
VFX_pane_construct(β¦, 0, 0, width-1, height-1). [verified] β so the 2D layer already tracks the
configured resolution.
HUD coupling
0x00483150 (hud.cpp) allocates fixed 0x19000-byte flip/work buffers and derives the HUD centre
from the live resolution: DAT_00566628 = width >> 1, DAT_0056662C = height >> 1. [verified]
0x004934F0 loads cockpit/radar art; 0x00494040 is the in-flight frame loop (lock-ring/target
cleanup). HUD element positions resolve against the +0x1666/+0x166A + projection centre fields.
Widescreen (Hor+) β SOLVED
No true-widescreen fix existed anywhere for Starlancer β this was the project's prime original
target, and it is now implemented as a static EXE patch (tools/ws_patch.py in the repo).
The mechanism (corrected RE)
The earlier [open] hypothesis β that the projection fields are written behind the
DAT_00588730 + 0x40 callback β was wrong. The projection scale/centre is written directly by
FUN_004C3A60 (called at the end of 0x004ACBE0/0x004AD0A0/0x004AD2E0). The +0x40 callback is
SR_driver_init inside the external srddraw.dll and merely consumes the projection (it sizes
buffers / sets the D3D7 viewport). FUN_004C3A60 computes, from the live width/height:
scale_x (+0x166E) = (width β K) Β· sX sX = 0.6 (0x3F19999A)
scale_y (+0x1686) = (height β K) Β· sY sY = 0.8 (0x3F4CCCCD) K = 0.1
centre_x(+0x167A) = width Β· 0.5 centre_y(+0x1692) = height Β· 0.5
sX/sY = 0.6/0.8 = 480/640 β a baked 4:3 encoding that gives equal X/Y scale (square pixels)
only at 4:3. On a wider buffer, stock scale_x β scale_y β the image is horizontally stretched.
The fix (tools/ws_patch.py)
Two .text-slack code-caves (size unchanged, 61 bytes total; verified by capstone disassembly; the
exe is never executed):
- Hor+ FOV β cave at the entry of
FUN_004C3A60forcessX := sY Β· height / width, soscale_x == scale_y(square pixels) and the X clip-planes (derived from the same value) widen to match. Vertical FOV is preserved; horizontal FOV widens with the aspect β textbook Hor+. At any 4:3 resolution it recomputes to exactly0.6β byte-identical to stock (regression-safe). - Force resolution β cave at
0x004ACBE0bakes the chosen size into the flight globalsDAT_005D6B2C(width) /DAT_005D6C88(height), overriding INI/dmodes.bin. The front-end device stays 640Γ480, so menus/briefing pillarbox (centred, not stretched).
python tools/ws_patch.py --fov-table # FOV per aspect
python tools/ws_patch.py --width 1920 --height 1080 IN.exe OUT.exe
python tools/ws_patch.py --verify OUT.exe # and --revert to restore
Representative result (vertical FOV fixed β 64Β°): 16:10 β ~90Β° H, 16:9 β ~96Β° H, 21:9 β ~112Β° H.
Why the HUD survives
The core flight HUD already derives from the live resolution β 0x00483150 sets the HUD centre to
width>>1,height>>1, WinVFX builds its overlay from +0x1666/+0x166A, and element positions
resolve against the projection centres β so radar, reticle and gauges auto-centre on widescreen.
Deferred to v2
Native-widescreen menus/briefing (they pillarbox today; ~50β100 hard-coded 640Γ480 sites) and
repositioning the few flight-HUD widgets that still use a 320Γ240 grid (0x00494040 region).
Caveats
Widths > ~1280 may hit the back/Z-buffer ceiling inside srddraw.dll (outside static view) β pair
with dgVoodoo2/DDrawCompat and verify in-game. (In-game testing is the community's; the project
builds and verifies statically.)
Licence β this page is licensed CC BY 4.0: use it in any project, whatever that project's own licence, with attribution. Code and tools in Starlancer-OSS remain GPL-3.0-only, and the Game Reference pages are CC BY-SA 3.0, as they derive from the Starlancer Fandom Wiki. StarLancer, its code and its assets belong to their owners; no licence here covers them.