Native Prototype Shim Notes
November 6, 2025 · View on GitHub
Overview
The native prototypes for the Quake Live client and gameplay frame loops now
live under src-re/prototypes/. They are thin C99 translations of the annotated
reverse-engineering notes and mirror the original VM entry point signatures
(CL_Frame and G_RunFrame). Both functions rely on bindable context
structures instead of the engine’s globals so that unit tests or harnesses can
inject deterministic state when exercising the routines.
Logging shim
A reusable shim (src-re/prototypes/common/native_shim.c) writes structured
trace lines to logs/re/native-shim.log. The helper truncates the log on first
use, ensures the directory exists, and exposes qlr_native_shim_logf/_syscall
for routine-level and syscall-level instrumentation. Prototype bindings call the
logger around every outward hook invocation, allowing side-by-side comparison
with the QVM trace output without patching the engine binaries. Syscall
contracts are also mirrored to logs/syscall_contract.log, which shares the
same origin/module/argument schema as the engine-side
SyscallContract_LogEvent helper. Run python tools/tests/validate_syscall_contract.py to compare the shim output against the
baseline in tests/expectations/syscall_contract.expect when auditing changes.
Delta analysis
- Client frame sequencing: The prototype preserves the cddialog/menu bootstrap, AVI demo timing clamp, and autopause guard observed in the retail binary while swapping global references for the context struct. Hook calls are wrapped with logging so their sequencing remains visible when ported to native code.
- Gameplay frame loop: The translation maintains the negative
msecclamp, think scheduling semantics, entity iteration, and warmup/intermission timers. Optional hook delegates (physics, event dispatch, client end-frame) let the harness substitute bespoke behavior while the default path mimics the annotated VM flow.
Compatibility notes
- Call
QLR_ClientFrame_BindContext/QLR_Game_BindFrameContextbefore invoking the exported frame functions. PassingNULLmirrors the VM’s early-out behavior and emits a diagnostic log line. - The shim only records when hooks are present; leave function pointers unset to disable segments of the loop during incremental porting.
- Use
qlr_native_shim_reset_log()at the start of a capture window to clear the log andqlr_native_shim_close()when tearing down the harness to flush buffered output.