Skip to content

EngineHost Overview

M T edited this page Oct 4, 2026 · 1 revision

EngineHost Overview

native/EngineHost is the native host runtime that the statically translated Halo PC 1.10 executable runs inside. The translator (see Static Translation Pipeline) turns every x86 function of halo.exe into a C function that operates on an EngineCPU register file and a flat 4 GiB guest address space; EngineHost supplies everything that the original executable expected Windows to provide: the PE loader, the guest address space and heaps, every imported Win32/DirectX API as a native "shim", threads and synchronization, and the Metal/AudioQueue/GameController back ends behind Direct3D 9, DirectSound and DirectInput. The same sources are linked two ways: into the desktop halo-host command-line binary (macOS, via the Makefile) and into the visionOS HaloVision.app (via native/EngineVision/project.yml).

This page is the map of the directory: what every file is for, how the files are assembled into translation units, how the host is built and linked, and how a run proceeds from process start to the translated game loop.

Source files

The tables below list every file in native/EngineHost. "TU" marks a file compiled as its own translation unit; everything else is a header or an .inc fragment textually included into a TU (see Inclusion structure).

Core runtime (this page and its siblings)

File Lines Kind Role
main.c 40 TU (desktop only) halo-host entry point: parses <game dir> [--quiet] [--frames N], starts the 1.5 GiB-stack game thread, services the AppKit run loop.
host.c 672 TU PE image loader, guest address space, guest heap and page allocators, handle table, import binding and dispatch of magic import addresses, guest thread contexts (TEB/TLS), crash diagnostics, host_run. See Guest Memory and Heap.
host.h 123 header Shared host API: guest memory accessors (GPTR, G32, S32...), shim calling macros (ARG, RET_STDCALL, RET_CDECL, SHIM), allocator/handle/path prototypes, HOST_ENV.
host_path.inc 41 inc → host.c host_path(): Windows path to case-correct host path under the game root. See Win32 Compatibility Layer.
host_snapshot.h 36 inc → host.c Test-only resident-memory capture at a chosen guest PC (HALO_CAPTURE_PC/HALO_CAPTURE_DIR) for instruction differential tests.
shims_kernel32.c 636 TU KERNEL32 shims: process/module, memory, TLS, time, Sleep accounting, cache-read waits, locale, files, async I/O and APCs, resources, console, sync and threads. See Win32 Compatibility Layer.
shims_misc.c 446 TU USER32 (windows, message queue, dialogs), GDI32, ADVAPI32 (registry emulation, CryptoAPI hashes, security stubs), OLE32/OLEAUT32, WINMM, VERSION, SHELL32, WININET, WinSock, DirectSound entry points, EULA/Keystone/NVCPL/Bink/Vorbis stand-ins.
threading.c / threading.h 491 / 52 TU / header pthread-backed events, mutexes, guest threads (QoS, suspend/resume, cancellation), waits with telemetry, critical sections. See Threading and Synchronization.
resources.c 98 TU Reads PE resources from halo.exe/strings.dll on disk: LoadStringA string tables, FindResource*, and DLGTEMPLATEEX parsing that chooses which dialog button to "press".
ddraw.c 52 TU Minimal IDirectDraw7 so the game's display probing succeeds (mode list, refresh rate, device identifier).
overrides.c 265 TU engine_dispatch_override: the hook point that lets native code replace or observe specific translated engine functions. See Engine Overrides and Hooks.
campaign_unlock.h 11 header → overrides.c In-memory campaign unlock (HALO_UNLOCK_CAMPAIGN=1).
hsc_trace.inc 172 inc → overrides.c Read-only HSC (Halo script) thread trace at the script scheduler (HALO_HSC_TRACE).
a10_control.inc 46 inc → overrides.c HALO_A10_TRACE main-loop/BSP/material diagnostics at engine call boundaries.
a10_gamepad.inc 110 inc → overrides.c HALO_A10_GAMEPAD=1 session-only default gamepad bindings written into a fresh profile's empty binding slots. See Input and Controllers.
audio_ownership_trace.inc 87 inc → overrides.c HALO_AUDIO_OWNERSHIP_TRACE=1 observations of the engine's sound-tag ownership around three sound functions. See Audio System.
gather_diagnostics.h 18 header HostGatherDiagnostics counters for the native BSP gather (HALO_NATIVE_GATHER), read by the app's report.
halo_settings.c / halo_settings.h 154 / 88 TU / header Live, lock-free presentation settings seeded from environment variables and changed by the Swift settings panel. See Runtime Settings.
pointer.c / pointer.h 98 / 35 TU / header Gaze/head pointer: steers the engine's own menu cursor toward the gaze target and turns pinches into clicks. See Engine Overrides and Hooks.
pointer_step.inc 28 inc → pointer.c and its test Pure servo step through the inverse of the engine's measured mouse acceleration curve.
core_telemetry.h 178 header Measurement-only Present-to-Present frame arithmetic, including the game-time tick counter read. See Diagnostics and Telemetry.

Graphics (owned by the graphics and panorama pages)

File Lines Kind Role
d3d9.c 1003 TU Direct3D 9 COM objects in guest memory with native method shims; Present, device reset, resources. See Direct3D9 Bridge.
d3d9_render.inc 1086 inc → d3d9.c Draw/state bridge to Metal. See Direct3D9 Bridge.
d3d9_capture.inc 226 inc → d3d9_render.inc Opt-in render capture diagnostics. See Diagnostics and Telemetry.
d3d9_process_vertices.inc 53 inc → d3d9_render.inc ProcessVertices differential switch (HALO_PV_DIFFERENTIAL). See Geometry Fast Paths.
fp_vertex_capture.inc 104 inc → d3d9_capture.inc One-frame first-person vertex capture.
model_vertex_capture.inc 235 inc → d3d9_capture.inc One-frame actor model capture.
model_capture_hooks.inc 42 inc → overrides.c Engine-boundary side of the model capture (HALO_MODEL_CAPTURE).
model_capture_boundary.h, model_capture_scope.h 12 / 25 header Model capture records and scope stack.
metalrenderer.m / metalrenderer.h 2935 / 366 TU (ObjC) / header Offscreen Metal rasterizer. See Metal Renderer.
metalshader.c / metalshader.h 205 / 58 TU / header D3D9 shader bytecode to Metal via MojoShader. See Shader Translation.
metalwin.m / metalwin.h 330 / 56 TU (ObjC, desktop only) / header Cocoa window + CAMetalLayer that presents the framebuffer on macOS. The visionOS app supplies its own implementation of the metalwin.h API.
texture_decode.c / texture_decode.h 240 / 80 TU / header D3D surface formats to BGRA8. See Textures and Texture Packs.
texture_mips.h, texture_content_hash.h 32 / 18 header Box-filter mips; XXH3 texture cache keys.
texture_mod_pack.h, texture_mod_runtime.inc 79 / 54 header / inc → d3d9_render.inc Replacement texture pack (HALO_TEXTURE_PACK). See Textures and Texture Packs.
shader_mod_pack.h, shader_mod_runtime.inc 52 / 33 header / inc → d3d9.c Replacement shader pack (HALO_SHADER_PACK). See Shader Translation.
stateblock.h 70 header D3D9 state block recording. See Direct3D9 Bridge.
rhw_declaration.h 32 header Recognizes Halo's POSITIONT declarations as RHW layouts.
process_vertices.h, process_vertices_skin.h, process_vertices_differential.h, vertex_shader_cpu.h 134 / 51 / 60 / 94 header CPU vertex shader execution for ProcessVertices, skinning fast path. See Geometry Fast Paths.
visible_surfaces.h 237 header → overrides.c Native visible-triangle marking (00553920). See Geometry Fast Paths.
native_leaves.h, native_gather.h, native_gather_tree.h 454 / 205 / 218 header → overrides.c Bit-exact native replacements of hot compute-only engine functions and BSP gather loops. See Geometry Fast Paths.
radial_fog.h 229 header Bearing-invariant fog (HALO_RADIAL_FOG=1). See Radial Fog.

Panorama and pacing

File Lines Kind Role
panorama.h 137 header Panorama public API and status values. See Panorama System.
panorama_hooks.inc 690 inc → overrides.c Engine-side multi-view rendering hooks. See Panorama System.
panorama_render.inc 430 inc → d3d9_render.inc D3D-side panorama layer production.
panorama_budget.h, panorama_lod.h 397 / 232 header Bearing budget and LOD. See Panorama Budget and LOD.
panorama_overlay_scope.h 26 header Camera-relative overlay entry addresses.
frame_pacer.h, frame_pacing_hooks.inc 350 / 165 header / inc → overrides.c Optional 90 Hz grid publication pacing. See Frame Pacing.

Audio, input and haptics

File Lines Kind Role
directsound.c / directsound.h 882 / 120 TU / header DirectSound 8 COM emulation and AudioQueue output. See Audio System.
directsound_engine_state.inc 176 inc → directsound.c Read-only engine sound state for the device report.
directsound_mixer.c / directsound_mixer.h 341 / 86 TU / header Float stereo mixer, 3D voices. See Audio System.
vorbis_shim.c / vorbis_shim.h 137 / 8 TU / header The four VORBISFILE.dll calls on top of stb_vorbis.
audio_source_identity.h 9 header XXH-based audio source identity.
dinput8.c 721 TU DirectInput 8 keyboard/mouse/joystick. See Input and Controllers.
gamecontroller.m / gamecontroller.h / gamecontroller_guard.h 477 / 122 / 83 TU (ObjC) / header Apple GameController snapshot bridge and Bluetooth link guard. See Input and Controllers.
haptics.c / haptics.h 145 / 36 TU / header Haptics synthesized from mixed audio. See Haptics.

Other directories

Path Role
tests/ 111 entries: C/ObjC unit and differential tests (test_*.c, test_*.m), the Python hook-coverage check test_dispatch_interest.py, and x87 stack-model test support files. Most tests #include the production .c file directly so the fixture cannot drift from the shipped code. See Testing and Source Checks.
third_party/xxhash/ Vendored xxhash.h (with LICENSE and UPSTREAM.json), used for texture content hashing and audio source identity.

Inclusion structure

Several large subsystems are split into .inc fragments that are textually included into a single translation unit, so that they can share static state without exported symbols. The complete map, verified with grep '#include ".*\.inc"':

flowchart LR
    host_c["host.c"] --> host_path["host_path.inc"]
    host_c --> host_snapshot["host_snapshot.h"]
    pointer_c["pointer.c"] --> pointer_step["pointer_step.inc"]
    directsound_c["directsound.c"] --> ds_state["directsound_engine_state.inc"]
    d3d9_c["d3d9.c"] --> d3d9_render["d3d9_render.inc"]
    d3d9_c --> shader_mod["shader_mod_runtime.inc"]
    d3d9_render --> d3d9_capture["d3d9_capture.inc"]
    d3d9_render --> d3d9_pv["d3d9_process_vertices.inc"]
    d3d9_render --> pano_render["panorama_render.inc"]
    d3d9_render --> tex_mod["texture_mod_runtime.inc"]
    d3d9_capture --> fp_cap["fp_vertex_capture.inc"]
    d3d9_capture --> model_cap["model_vertex_capture.inc"]
    overrides_c["overrides.c"] --> a10c["a10_control.inc"]
    overrides_c --> a10g["a10_gamepad.inc"]
    overrides_c --> hsc["hsc_trace.inc"]
    overrides_c --> pano_hooks["panorama_hooks.inc"]
    overrides_c --> pacing["frame_pacing_hooks.inc"]
    overrides_c --> model_hooks["model_capture_hooks.inc"]
    overrides_c --> audio_own["audio_ownership_trace.inc"]
    overrides_c --> leaves["native_leaves.h / native_gather.h / native_gather_tree.h / visible_surfaces.h"]
Loading
Including file Line Included fragment
host.c 55 host_snapshot.h (defines a static function, so it behaves as a fragment)
host.c 426 host_path.inc
pointer.c 33 pointer_step.inc (also included by tests/test_pointer_step.c)
directsound.c 631 directsound_engine_state.inc
d3d9.c 308-309 d3d9_render.inc, shader_mod_runtime.inc
d3d9_render.inc 118-119, 194, 319 d3d9_capture.inc, d3d9_process_vertices.inc, panorama_render.inc, texture_mod_runtime.inc
d3d9_capture.inc 224, 226 fp_vertex_capture.inc, model_vertex_capture.inc
overrides.c 107 visible_surfaces.h
overrides.c 167-177 a10_control.inc, a10_gamepad.inc, hsc_trace.inc, panorama_hooks.inc, frame_pacing_hooks.inc, model_capture_hooks.inc, audio_ownership_trace.inc, native_leaves.h, native_gather.h, native_gather_tree.h, gather_diagnostics.h

Header chains worth knowing: native_gather_tree.h includes native_gather.h, which includes native_leaves.h; both panorama_hooks.inc and frame_pacing_hooks.inc include frame_pacer.h.

The include order inside overrides.c matters: frame_pacing_hooks.inc is documented as "included after panorama_hooks.inc", and all of the hook fragments rely on the Override typedef declared just before them (overrides.c:166).

The two link targets

flowchart TB
    subgraph gen["native/build/engine-reuse/whole-exe (generated)"]
        chunks["chunk_000..031.c (include sub_XXXXXXXX.c)"]
        bundle["engine_bundle.c (engine_dispatch, engine_reuse_entry)"]
        imports["engine_imports.c (sections, IAT, entry point, TLS dir)"]
    end
    subgraph host["native/EngineHost"]
        core["host.c threading.c shims_*.c overrides.c resources.c ddraw.c halo_settings.c pointer.c"]
        gfx["d3d9.c metalrenderer.m metalshader.c texture_decode.c"]
        av["directsound*.c vorbis_shim.c dinput8.c gamecontroller.m haptics.c"]
        desk["main.c metalwin.m"]
    end
    mojo["third_party/mojoshader (4 files)"]
    app["native/EngineVision Sources (Swift + EngineVisionRuntime.m)"]
    gen --> hh["halo-host (Makefile)"]
    core --> hh
    gfx --> hh
    av --> hh
    desk --> hh
    mojo --> hh
    gen --> va["HaloVision.app (project.yml / Xcode)"]
    core --> va
    gfx --> va
    av --> va
    mojo --> va
    app --> va
Loading

Desktop: halo-host (Makefile)

Makefile builds a macOS arm64 command-line runner.

Variable / target Value / effect
VISION repository root ($(abspath ../..))
GEN $(VISION)/native/build/engine-reuse/whole-exe (generated translation output)
OBJ $(GEN)/host-obj (all objects, host and generated)
CFLAGS -O2 -w -std=c11 -DENGINE_FLAT_MEMORY=1 -DHALO_ARM64_FENV_FAST=1 -I. -I$(VISION)/native/EngineReuse -I$(GEN) (Makefile:6)
CHUNKS every $(GEN)/chunk_*.c; each chunk object additionally depends on the sub_*.c files it includes, extracted with sed at parse time (Makefile:12)
HOST_SRCS host.c threading.c haptics.c halo_settings.c pointer.c shims_kernel32.c shims_misc.c d3d9.c texture_decode.c metalshader.c dinput8.c ddraw.c directsound.c directsound_mixer.c vorbis_shim.c resources.c overrides.c main.c (Makefile:13)
HOST_OBJC metalwin.m metalrenderer.m gamecontroller.m, compiled with -ObjC -fobjc-arc
MOJO_DEFINES / MOJO_OBJS MojoShader built with every profile except Metal disabled (SUPPORT_PROFILE_*=0) and MOJOSHADER_NO_VERSION_INCLUDE=1
all (default) halo-host
$(GEN)/engine_bundle.c runs tools/generate_engine_reuse.py @decompilation/c9acf0c46954/function-addresses.txt @decompilation/c9acf0c46954/extra-function-entries.txt --label whole-exe --max-functions 10000 --trap-unsupported --discover --chunks 32 (Makefile:25-26)
$(GEN)/engine_imports.c runs tools/export_engine_imports.py (PE sections, import table, entry point, TLS directory) (Makefile:27-28)
halo-host links chunks, engine_bundle.o, engine_imports.o, host objects and MojoShader with -lm -framework Cocoa -framework Metal -framework QuartzCore -framework GameController -framework CoreHaptics -framework AudioToolbox (Makefile:56-57)
clean removes $(OBJ) and halo-host

The extra dependency lines at Makefile:21-23, 48-52 and 63-67 exist because of the .inc structure: overrides.o must be rebuilt when any hook fragment changes, d3d9.o when any render fragment changes, host.o when host_snapshot.h changes, and every object that uses threading.h when it changes. Generated objects also depend on engine_hooks.h, engine_cpu.h, engine_arm64_fenv_prototype.h and engine_flags.h (Makefile:30), because changing the hook list changes how every translated call site is compiled (see Engine Overrides and Hooks).

Note that the Makefile's CFLAGS do not include -frounding-math -ffp-contract=off, which the visionOS target and several source-check tests pass; see x87 Floating Point for why those flags matter to the translated code.

visionOS: HaloVision.app (project.yml)

native/EngineVision/project.yml lists the EngineHost sources individually: host.c, shims_kernel32.c, shims_misc.c, directsound.c, directsound_mixer.c, haptics.c, halo_settings.c, pointer.c, vorbis_shim.c, d3d9.c, texture_decode.c, metalshader.c, dinput8.c, ddraw.c, resources.c, overrides.c, gamecontroller.m, threading.c, metalrenderer.m, the four MojoShader files, and the generated chunk_*.c, engine_bundle.c and engine_imports.c. It compiles them with OTHER_CFLAGS including -frounding-math -ffp-contract=off -DENGINE_FLAT_MEMORY=1 -DHALO_ARM64_FENV_FAST=1 and the MojoShader profile switches, at -O2 in Release and -O0 otherwise.

Two desktop files are deliberately absent, and the app provides replacements:

Desktop symbol Desktop source visionOS replacement
main() and host_command_line main.c EngineVisionRuntime.m defines host_command_line as a 512-byte buffer initialised to "C:\Halo\halo.exe" -window -novideo (EngineVisionRuntime.m:29-30) and starts the engine from enginevision_start
metalwin_init, metalwin_present, metalwin_should_close, metalwin_poll metalwin.m Implemented in EngineVisionRuntime.m (plus metalwin_present_gpu, metalwin_present_dropped); metalwin_should_close() returns host_quit_requested != 0 (EngineVisionRuntime.m:597)

See visionOS App and Build System for the app side.

Startup sequence

Desktop

sequenceDiagram
    participant M as main thread (main.c)
    participant G as game thread (1.5 GiB stack)
    participant H as host_run (host.c)
    participant E as translated engine
    M->>M: build command line + HALO_CMDLINE_EXTRA, parse --quiet / --frames
    M->>G: pthread_create(run_thread)
    loop until game_done
        M->>M: CFRunLoopRunInMode(default, 0.05 s)
    end
    G->>H: host_run(root/halo.exe, root)
    H->>H: mmap 4 GiB guest space, PROT_NONE first 64 KiB
    H->>H: sigaltstack + SIGSEGV/SIGBUS handler, SIGALRM heartbeat (15 s)
    H->>H: load_image (headers + sections)
    H->>H: setup_imports (IAT := magic proc addresses)
    H->>H: init EngineCPU, main TEB/PEB/TLS, push return token
    H->>E: setjmp, engine_reuse_entry(cpu, PE entry point)
    E->>E: CRT startup, WinMain, game loop (imports via shims)
    E-->>H: return / host_exit longjmp / engine_fail longjmp
    H->>H: dump call ring + CPU, DirectSound shutdown, audio stats
    H-->>G: exit status
    G->>M: game_done = 1, CFRunLoopStop
    M->>M: keep window ~20 s if shown, return status
Loading

Step by step:

  1. main (main.c:12-40). Requires argv[1] (the game directory containing halo.exe and maps/); prints usage: halo-host <game dir> [--quiet] [--frames N] and exits 2 otherwise. The guest command line is host_command_line ("C:\Halo\halo.exe" -window -novideo) with HALO_CMDLINE_EXTRA appended after a space when set. --quiet sets host_trace_imports = 0 (silences host_trace); --frames N sets host_frame_limit, which makes Present call host_exit(0) after N frames (d3d9.c:743).
  2. Game thread. main creates a thread with a 1.5 GiB native stack (pthread_attr_setstacksize(..., 1536 MiB)). Every translated guest call is also a nested native C call, so guest call depth becomes native stack depth; the source does not record how the 1.5 GiB figure was chosen, but the app refuses to start the engine with anything smaller. The main thread runs CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.05, true) in a loop so that the Metal window, which d3d9.c creates from the game thread via metalwin_init, can be serviced by AppKit. If thread creation fails, host_run is called directly on the main thread.
  3. host_run (host.c:625-665):
    • line-buffers stderr; reads HALO_TRACE_LO/HALO_TRACE_HI (hex) into engine_trace_lo/hi to enable per-instruction PC tracing over that range (engine_pc_trace, host.c:56);
    • sets host_game_root;
    • reserves the guest space with mmap(NULL, 0x100000000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON) and makes [0, 0x10000) PROT_NONE to trap guest null dereferences;
    • installs a 256 KiB alternate signal stack and a SIGSEGV/SIGBUS handler that logs the faulting guest PC, flags a likely guest stack overflow when esp < 0x100000, dumps the esi object/vtable, prints a guest backtrace and _exit(4)s (host.c:605-616);
    • starts a SIGALRM heartbeat every 15 s that logs PC, instruction count, heap high-water and the current import (host.c:601-604);
    • load_image copies the first 0x1000 bytes (PE headers) to the image base and each section's raw bytes (clamped to its virtual size) to its virtual address (host.c:513-525);
    • setup_imports binds every IAT slot (see Import resolution);
    • initialises the main EngineCPU: fs_base = 0x7FFDE000, failure callback cpu_failure, x87 init, esp = 0x00100000 + 0x00100000 - 0x100, TEB/PEB/TLS via setup_thread_teb(..., thread id 1), pushes the return token MAGIC_PROC|0, eflags = 0x202;
    • setjmp(host_escape) then engine_reuse_entry(cpu, engine_pe_entry_point), which is engine_dispatch(cpu, entry) in the generated bundle. The PE entry point is the original executable's C runtime startup, which calls the CRT-facing KERNEL32 shims (GetVersionExA, GetCommandLineA, GetStartupInfoA, GetEnvironmentStrings*, heap and TLS functions) and then the game's WinMain.
  4. Termination paths. host_run returns from setjmp in one of four ways (host.c:649-664):
setjmp value Cause Return status
0 the entry function returned with pc == MAGIC_PROC ("entry returned normally") 0
1 engine_fail → cpu_failure longjmp (unimplemented import, unsupported instruction, bad guest access, RaiseException, ...) 1
2 host_exit(code) on the main context (ExitProcess, TerminateProcess(current), frame limit, quit request, fatal allocation failure) code
3 something dispatched to `MAGIC_PROC 0` itself ("entry point returned")

Except for case 2 it dumps the last 48 indirectly dispatched function addresses (engine_record ring, host.c:49-53), then always logs the CPU registers, shuts DirectSound down and logs [audio-result] frames=... nonzero=... peak=.... 5. Back in main, if a window was shown (host_window_shown, set by d3d9.c when metalwin_init succeeded) the last frame stays on screen for up to 400 × 50 ms or until the window is closed.

visionOS app

enginevision_start(game_root) (EngineVisionRuntime.m:669) runs on the app side:

  1. On visionOS, it composes -vidmode W,H,60 from HALO_VIDMODE, else from the HaloRenderResolution user default (accepted if it parses as WxH within 640x480 to 4096x3072), else 2048,1536,60, and stores it in HALO_CMDLINE_EXTRA (without overwriting an existing value) (EngineVisionRuntime.m:681-695).
  2. Chooses the zero-copy panorama GPU hand-off (HALO_PANORAMA_GPU; on by default on visionOS, off by default on the Mac).
  3. Appends HALO_CMDLINE_EXTRA to host_command_line.
  4. Sets host_trace_imports = 0, clears host_quit_requested, and creates the engine worker thread with a 1.5 GiB stack at QOS_CLASS_USER_INTERACTIVE (EngineVisionRuntime.m:729-741). If visionOS refuses the stack size it fails loudly rather than starting with a smaller stack.

engine_worker (EngineVisionRuntime.m:608-667) then installs the device defaults with setenv(..., 0) (never overriding an existing variable; the same list is recorded in mods/runtime-settings.json), applies HALO_DRAW_FASTPATH, points HALO_TEXTURE_PACK/HALO_SHADER_PACK at the bundled packs, decides host_core_telemetry from HALO_CORE_TELEMETRY, and calls the same host_run(runtime_exe, runtime_root) as the desktop. The return status is turned into the app's status text. A stop request from the app sets host_quit_requested; the next Present sees metalwin_should_close() and calls host_exit(0).

Import resolution

flowchart TB
    A["export_engine_imports.py: engine_imports[] = {IAT VA, dll, name}"] --> B["setup_imports(): for each import"]
    B --> C["host_proc_address(dll, name)"]
    C --> D{"(dll,name) already in procs[]?"}
    D -- yes --> E["MAGIC_PROC | index"]
    D -- no --> F["append to procs[] (max 8192), fn = find_shim(dll, name)"]
    F --> E
    E --> G["S32(IAT slot, magic)"]
    H["guest: call [IAT] / COM vtable call / GetProcAddress result"] --> I["engine_dispatch(cpu, 0xFF00xxxx)"]
    I --> J{"address >= 0xFE000000"}
    J -- yes --> K["engine_dispatch_external()"]
    K --> L{"index 0?"}
    L -- yes --> M["longjmp: entry point returned"]
    L -- no --> N{"procs[i].fn set?"}
    N -- no --> O["log UNIMPLEMENTED import, engine_fail"]
    N -- yes --> P["host_current_import = name, then fn(cpu)"]
    P --> Q["shim reads ARG(i), RET_STDCALL pops args, eax = result, pc = return address"]
Loading

find_shim searches five tables in order: host_shims_kernel32, host_shims_misc, then the tables built at first use by host_shims_d3d9_build, host_shims_dinput8_build and host_shims_ddraw_build (host.c:442-449). DLL names compare case-insensitively with any .dll suffix ignored (dll_equal). COM interfaces are exposed the same way: their methods are entries named Interface::Method (for example IDirectDraw7::GetDisplayMode or IDirectSoundBuffer8::Lock), and the guest-resident vtables are filled with the magic addresses host_proc_address returns, so every COM call crosses the same checked boundary as a normal import. Details of shim calling conventions, dynamic loading (LoadLibraryA/GetProcAddress) and the full API inventory are on Win32 Compatibility Layer.

Within the generated engine_dispatch, the order of checks is: magic addresses (>= 0xFE000000) → engine_dispatch_override (hooks, see Engine Overrides and Hooks) → record in the crash call ring → binary search over the translated function entries → engine_dispatch_external once more → engine_fail("unresolved original engine boundary") (tools/generate_engine_reuse.py:220-231).

Runtime threads at a glance

Thread Created by Runs
Desktop main thread process AppKit run loop (main.c)
Engine thread ("game thread", "presenting thread") main.c or enginevision_start host_run, the translated WinMain loop, every D3D9 call and Present
Guest threads CreateThread shim → host_thread_create Halo's cache-file reader (00443940) and saved-game writer (00538980), each on its own native pthread with its own guest stack and TEB
Audio, controller, app UI, compositor threads host frameworks Never run guest code; communicate with the engine through locks/atomics (halo_settings, pointer queue, mixer)

See Threading and Synchronization for the full thread model.

Diagnostics built into host.c

Facility Where Notes
host_log host.c:72-74 Always on; [host] prefix on stderr.
host_trace host.c:75-78 Only when host_trace_imports is nonzero (desktop default 1; --quiet and the app set 0).
Import call trace host.c:504-505 Each import's first 8 calls and every power-of-two call count, with the first four stack arguments.
Guest backtrace host.c:33-43 Scans 128 stack dwords for values in 0x00401000..0x00639596 and names them from <game root>/halo-vision-symbols.txt (lines ADDR name, up to 16384) when present.
Call ring host.c:49-53 Thread-local ring of the last 256 dispatched addresses; only indirect calls are recorded because direct calls bypass engine_record.
PC trace host.c:56, host.c:627 HALO_TRACE_LO/HALO_TRACE_HI hex range.
Test capture host_snapshot.h With PC tracing on, HALO_CAPTURE_PC (hex) and HALO_CAPTURE_DIR write memory.bin (resident pages above 64 KiB, up to 512 MiB) and state.json (registers, x87 state, region list, "testOnly":true) once.
FPCR guard host.c:406-422 host_fp_environment_check() is called once per Present; if the engine thread's FPCR has a non-default rounding mode, FZ, DN, FZ16 or FIZ/AH/NEP bit, it is restored and logged once. Translated x87 code relies on the default FPCR when the guest rounds to nearest.

Related pages

Clone this wiki locally