Repository navigation
EngineReuse Runtime
native/EngineReuse is the small C runtime that every translated function compiles against. It defines the guest CPU (EngineCPU), guest memory access, the stack, exact EFLAGS arithmetic, the x87 unit, the per-instruction step hook, the failure path, and the hook list that decides which translated calls must go through the dispatcher. It sits between the generated code (see Static Translation Pipeline) and the host (see EngineHost Overview): generated code calls only these inline helpers plus engine_dispatch, and the host supplies memory, imports and overrides through a handful of callbacks. The directory also contains a separate, sandboxed invocation API (engine_runtime.c) used by the runtime contract test.
| File | Role |
|---|---|
| native/EngineReuse/engine_cpu.h |
EngineCPU, engine_fail, engine_step, memory and stack helpers, divide/multiply, CPUID/RDTSC, FS base, the complete x87 model |
| native/EngineReuse/engine_flags.h | EFLAGS bit constants and width-correct flag helpers (add/sub/logic/inc/dec/neg/shifts/imul/shld/shrd/rotates) |
| native/EngineReuse/engine_registers.h |
eax..edi, eflags, sub-register and segment-selector macros used by generated code |
| native/EngineReuse/engine_hooks.h |
ENGINE_HOOK_ADDRESSES, ENGINE_TRACE_HOOK_ADDRESSES, engine_hooked, ENGINE_DIRECT
|
| native/EngineReuse/engine_arm64_fenv_prototype.h | ARM64 FPCR/FPSR inline assembly for non-nearest x87 rounding (see x87 Floating Point) |
| native/EngineReuse/engine_runtime.h, engine_runtime.c | Sandboxed, transactional halo_engine_* API over explicit memory regions |
| native/EngineReuse/module.modulemap | Clang module HaloEngineRuntime exporting engine_runtime.h (not referenced by any build file in this tree) |
| native/Tests/EngineRuntimeContract.c | Contract test for engine_runtime.c
|
| native/EngineHost/tests/test_engine_direct_calls.c |
ENGINE_DIRECT behavior |
| native/EngineHost/tests/test_dispatch_interest.py | Hook list completeness check |
| Host side (for context): host.c, overrides.c |
engine_dispatch_external, engine_dispatch_override, engine_record, engine_pc_trace, engine_flat_base
|
| Field | Type | Meaning |
|---|---|---|
gpr[8] |
uint32_t |
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI (x86 encoding order) |
flags |
uint32_t |
EFLAGS. Status bits are exact; DF (0x400) drives string ops; other bits are carried |
pc |
uint32_t |
guest address of the current instruction, or the return address just popped by ret
|
fp_reg[8] |
double |
x87 physical register file; ST(i) is fp_reg[(fp_top + i) & 7]
|
fp_valid |
uint8_t |
logical tag bitmap: bit i set means ST(i) is not empty |
fp_top |
uint8_t |
x87 TOP |
fp_control, fp_status
|
uint16_t |
x87 control word and status word (TOP is merged in on read) |
address |
EngineAddress |
read translation callback (non-flat builds) |
write_address |
EngineAddress |
write translation callback; when NULL, address is used for writes too |
failure |
EngineFailure |
called by engine_fail; must not return |
context |
void * |
owner data for the callbacks |
instruction_limit, instruction_count
|
uint64_t |
budget, only enforced with ENGINE_STEP_FULL=1
|
fs_base |
uint32_t |
guest address of this thread's TEB; 0 means ENGINE_DEFAULT_FS_BASE (0x7FFDE000) |
There is one EngineCPU per guest thread. The host creates the main thread's in host_run and one per CreateThread in host_initialize_guest_thread (fresh stack, fresh TEB, flags = 0x202, engine_fp_init); see Threading and Synchronization. All threads share the one flat guest address space.
Generated code never names cpu->gpr[...] directly; engine_registers.h maps eax..edi to cpu->gpr[0..7] and eflags to cpu->flags, and defines LO8, HI8, LO16, SET_LO8, SET_HI8, SET_LO16, BSWAP32 (__builtin_bswap32) and constant segment selectors (_seg_cs = 0x1B, _seg_ds/_seg_es/_seg_ss = 0x23, _seg_fs = 0x3B, _seg_gs = 0) "so that rare or spuriously-decoded segment ops ... compile". The header must be included after engine_cpu.h and after any helper declarations, because short names such as esp would otherwise rewrite C identifiers.
engine_cpu.h:60-87. Generated code uses engine_read_u8/16/32/64(cpu, addr) and engine_write_u8/16/32/64(cpu, addr, value), plus engine_read_f32/f64/f80 and engine_write_f32/f64/f80 for floating values. Every access goes through memcpy, so unaligned guest accesses are well-defined C.
Two address translation modes are selected at compile time:
| Mode |
engine_address / engine_write_address
|
Used by |
|---|---|---|
ENGINE_FLAT_MEMORY defined |
engine_flat_base + a; no checks at all |
the game (macOS host and visionOS app), most host tests |
| not defined | rejects a + n > 2^32 ("address wrap"/"write address wrap"), calls cpu->address or cpu->write_address, fails with "unmapped guest address" or "unmapped or readonly guest write" on NULL |
engine_runtime.c, EngineRuntimeContract.c, test_fpu.py
|
In flat mode engine_flat_base is defined by the host: host_run maps 0x100000000 bytes read/write anonymous memory and makes the first 0x10000 bytes PROT_NONE to catch null dereferences. A guest address is therefore a plain offset, and an invalid access becomes a host SIGSEGV/SIGBUS, which the host's signal handler reports with the guest pc and esp. Guest memory layout (stack, heap, page ranges, TEB/PEB) is described on Guest Memory and Heap.
ENGINE_FS_BASE is (cpu->fs_base ? cpu->fs_base : 0x7FFDE000u) (line 300). The lifter rewrites every FS- or GS-relative operand to (ENGINE_FS_BASE + offset), so mov eax, fs:[0x18] reads the TEB self pointer that the host wrote into guest memory. 0x7FFDE000 is the same address the host uses as MAIN_TEB_BASE.
engine_push/engine_pop adjust gpr[4] (ESP) by 2 or 4 and store/load guest memory; any other size fails with "invalid push width"/"invalid pop width". Calls push real return addresses, so the guest stack has exactly the layout the original code expects (stack-walking code, [esp+N] argument access, ret N cleanup, SEH frames, setjmp buffers).
| Helper | Lines | Behavior |
|---|---|---|
engine_divide(c, divisor, bits, sign) |
99-120 | 8-bit: AX / src into AL (quotient) and AH (remainder); 16/32-bit: DX:AX or EDX:EAX. Fails on zero divisor ("integer division by zero"), INT64_MIN / -1, and quotient overflow ("integer quotient overflow"), where hardware raises #DE. Flags left unchanged ("undefined ... retained deterministically") |
engine_mul1(c, src, bits, sign) |
302-322 | one-operand MUL/IMUL into AX, DX:AX or EDX:EAX; sets CF and OF together when the upper half is significant |
engine_bsf, engine_bsr
|
323-324 |
__builtin_ctz / 31 - __builtin_clz; the lifter handles the zero-source case |
engine_cpuid(c) |
325-333 | leaf 0: max leaf 1, vendor GenuineIntel; leaf 1: signature 0x686 (family 6), EDX = FPU, TSC, CX8, CMOV, FXSR (no MMX, SSE, SSE2, 3DNow!); other leaves return zeros. The point is to make the game and its CRT pick x87 code paths, which the translator models exactly |
engine_rdtsc(c) |
334-337 |
clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) * 3 into EDX:EAX ("~3 GHz tick") |
engine_flags.h holds the arithmetic for every flag-producing instruction the lifter supports. Each helper takes uint32_t *flags (always &eflags, i.e. &cpu->flags), the operands and the width (8, 16 or 32), and returns the width-masked result.
There is no lazy-flags scheme: each producer computes all of its defined flags at the instruction that produces them, and each consumer (jcc, setcc, cmovcc, fcmov, adc/sbb, rcl/rcr) reads the bits from cpu->flags. That is what makes flags survive calls, block boundaries and unrelated register writes (the flags.py docstring contrasts this with the upstream operand-history heuristic).
| Constant | Bit |
|---|---|
ENGINE_EFLAGS_CF |
0x0001 |
ENGINE_EFLAGS_RESERVED1 |
0x0002 |
ENGINE_EFLAGS_PF |
0x0004 |
ENGINE_EFLAGS_AF |
0x0010 |
ENGINE_EFLAGS_ZF |
0x0040 |
ENGINE_EFLAGS_SF |
0x0080 |
ENGINE_EFLAGS_OF |
0x0800 |
ENGINE_EFLAGS_STATUS_MASK |
CF, PF, AF, ZF, SF, OF |
| Helper | Defined flags written | Notes |
|---|---|---|
engine_flags_add(f, a, b, carry_in, w) |
all six | used for ADD, ADC (carry from CF), XADD |
engine_flags_sub(f, a, b, borrow_in, w) |
all six | SUB, SBB, CMP, NEG, SCAS, CMPS, CMPXCHG |
engine_flags_logic(f, result, w) |
PF, ZF, SF; CF=OF=0 | AF normalized to 0 |
engine_flags_inc / _dec
|
all but CF (CF preserved) | |
engine_flags_neg |
as SUB from 0 | |
engine_flags_shl / _shr / _sar
|
PF, ZF, SF, CF, OF (count 1) | count masked to 5 bits; count 0 leaves flags untouched |
engine_flags_imul(f, a, b, w) |
CF=OF=overflow | two/three-operand IMUL |
engine_flags_shld / _shrd
|
CF, OF, SF, ZF, PF | count masked, then reduced modulo width when larger |
engine_flags_rol / _ror
|
CF, OF | count reduced modulo width |
engine_flags_rcl / _rcr
|
CF, OF | rotate through CF, count modulo width+1 |
Architecturally undefined outputs are made deterministic (header comment lines 14-17, mirrored in UNDEFINED_FLAG_NOTES):
| Case | Rule |
|---|---|
| AND/OR/XOR/TEST | AF cleared |
| nonzero SHL/SHR/SAR | AF cleared |
| shift count >= width | CF preserved |
| shift count > 1 | OF preserved |
| two/three-operand IMUL | PF, AF, ZF, SF preserved |
| DIV/IDIV | flags untouched (the flags mixin documents the lowering as rejected; EngineLifter lowers DIV itself through engine_divide) |
All helpers widen to uint64_t or use unsigned arithmetic before shifting, so there is no signed overflow or oversized shift; test_flags.py checks this with UBSan (receipt FLAGS-004).
Every translated instruction begins with engine_step(cpu, pc) (lines 43-59):
static inline void engine_step(EngineCPU *cpu, uint32_t pc) {
cpu->pc = pc;
#if ENGINE_STEP_FULL
if (cpu->instruction_limit && cpu->instruction_count >= cpu->instruction_limit)
engine_fail(cpu, "original instruction budget exhausted");
cpu->instruction_count++;
if (pc >= engine_trace_lo && pc < engine_trace_hi) engine_pc_trace(cpu);
#endif
}The comment explains the split: "The lean default only records the pc (faults, shims and dispatch need it). ENGINE_STEP_FULL=1 restores the instruction budget, the instruction counter and the HALO_TRACE_LO/HI pc trace, which the bounded-invocation tests rely on; the game builds do not use them and the checks cost several host operations on every translated instruction." The game builds do not define ENGINE_STEP_FULL, so:
-
HALO_TRACE_LO/HALO_TRACE_HIare read by host.c:627 but only take effect if the translated chunks were compiled with-DENGINE_STEP_FULL=1. -
instruction_countstays 0 in release builds (the host's heartbeat log prints it).
engine_fail is _Noreturn: it calls cpu->failure(cpu, reason) and then abort()s, because "A failure callback must leave execution; never resume a bad instruction." The host's callbacks longjmp to the frame that started the guest thread and log the reason, cpu->pc, a register dump and a backtrace. Reasons emitted by the runtime and the generated code:
| Reason | Source |
|---|---|
unsupported original instruction |
an instruction trapped by --trap-unsupported was reached |
dispatch to non-leader address |
the L_ENTRY switch of the function chosen by engine_dispatch has no such leader |
unresolved original engine boundary |
no generated function at or below the address, and no host handler |
unresolved import boundary |
address >= 0xFE000000 not handled by the host |
int3 breakpoint reached, hlt executed, ud2 executed
|
ExtraMixin |
integer division by zero, integer division overflow, integer quotient overflow, invalid divide width
|
engine_divide |
invalid push width, invalid pop width
|
stack helpers |
address wrap, unmapped guest address, write address wrap, unmapped or readonly guest write
|
non-flat memory |
original instruction budget exhausted |
ENGINE_STEP_FULL builds |
empty x87 stack register, x87 stack overflow, invalid x87 stack register, invalid x87 arithmetic, invalid x87 unary operation, invalid x87 transcendental, invalid FILD size, invalid FIST size
|
x87 helpers (x87 Floating Point) |
host reasons (unimplemented import, callback return mismatch, ...) |
EngineHost Overview |
engine_dispatch(cpu, address) is generated into engine_bundle.c (code on Static Translation Pipeline). Its order of decisions:
flowchart TD
D["engine_dispatch(cpu, address)"] --> M{"address >= 0xFE000000 ?"}
M -- yes --> EXT["engine_dispatch_external (host)"]
EXT -- handled --> R["return"]
EXT -- no --> F1["engine_fail: unresolved import boundary"]
M -- no --> OV{"engine_dispatch_override (host)<br/>handled?"}
OV -- yes --> R
OV -- no --> PC["cpu->pc = address<br/>engine_record(address)"]
PC --> BS["binary search: greatest entry <= address"]
BS -- found --> FN["engine_fns[idx](cpu)<br/>-> L_ENTRY switch on cpu->pc"]
FN --> R
BS -- none --> EXT2["engine_dispatch_external (host)"]
EXT2 -- handled --> R
EXT2 -- no --> F2["engine_fail: unresolved original engine boundary"]
The three host callbacks are weak in the bundle and strong in the host:
| Callback | Host definition | Purpose |
|---|---|---|
int engine_dispatch_external(EngineCPU*, uint32_t) |
host.c:495-510 | Owns 0xFF000000 | index procedure handles that setup_imports wrote into the IAT and that GetProcAddress returns. Index 0 is the host return sentinel ("entry point returned"); others call the registered shim, or fail with unimplemented import. Returns 0 for anything else |
int engine_dispatch_override(EngineCPU*, uint32_t) |
overrides.c:238-265 | Native replacements and observation hooks (panorama, model capture, audio ownership, native leaves, CRT overrides). A 65,536-bit membership filter (dispatch_interest) built at startup from the hook lists and override table rejects most addresses with one bit test |
void engine_record(uint32_t) |
host.c:50 | Per-thread 256-entry ring of dispatched addresses; the last 48 are printed when the guest stops |
engine_reuse_entry(cpu, entry) is the bundle's exported entry; the host calls it once with the PE entry point. Guest threads and host-to-guest callbacks call engine_dispatch directly (host.c:577-600, host.c:484-494). The override catalogue is documented on Engine Overrides and Hooks.
The generator rewrites calls and tail calls whose target is a constant, translated entry into (engine_hooks.h:65-69):
#define ENGINE_DIRECT(cpu, address, fn) do { \
if (engine_hooked(address)) engine_dispatch((cpu), (address)); \
else { (cpu)->pc = (address); fn(cpu); } \
} while (0)engine_hooked is a switch over a compile-time constant, so at each call site the test folds away and the call is either a plain C call or a dispatch. The header's comment gives the reason: dispatch "looks every call up: the override filter, the crash call record, a binary search over 8336 entries and an indirect branch. On b30 that was about a tenth of the engine thread, and 25,835 of the 32,373 translated call sites name a fixed function entry."
The consequence is a rule for contributors: any address the host intercepts in engine_dispatch_override must be listed in ENGINE_HOOK_ADDRESSES, and the translated chunks recompiled, or direct calls will bypass the hook. The Makefile makes every chunk object depend on engine_hooks.h for that reason. The current list (lines 22-37):
| Addresses | Purpose (header comment) |
|---|---|
004C6E80 |
campaign unlock and input/control tick |
00442550, 00544090, 00544120, 0048A1A0
|
audio / HSC |
004D6FC0, 00533850, 00533730
|
model capture |
00492430, 0052B050, 00518F40, 004924B0, 005154A0, 005537C0, 0050CC40, 00449780, 00494730, 004984C0, 0050BEA0, 0050BFB0, 0050BA80
|
panorama |
0050F740 |
object pixel size, one per object per panorama frame |
0050BDC0 |
panorama: the game's interface record |
00626BA4, 00631930, 00631260
|
overrides: _mbstowcs, _mbtowc, _wctomb
|
00634D8E, 00634D50, 00634F61, 00634DCB
|
FP intrinsic dispatchers |
00449590 |
int32 sort, native for the render pass's callback |
00553920 |
visible-triangle marking, native (visible_surfaces.h) |
004CC0D0, 00554260, 005541B0, 00553380, 00552C20
|
native leaves |
00552DE0 |
BSP material walk |
005540C0, 00553C40, 00553F10
|
exact light/shadow gather loops |
ENGINE_TRACE_HOOK_ADDRESSES (004C8800, 00477EA0, 005527F0, 005528F0, 00511F30) are boundaries that only the HALO_A10_TRACE diagnostics observe. They are treated as hooked only when the chunks are compiled with -DENGINE_TRACE_HOOKS=1; in release builds those calls go direct and the trace only sees the indirect ones. Direct calls also skip engine_record, so the crash dump's call ring contains only dispatched calls, but "the native backtrace still shows the whole chain".
The calling protocol uses cpu->pc as the handshake between caller and callee:
- The caller pushes the real return address (
engine_push(cpu, ret_va, 4)). - It transfers control:
ENGINE_DIRECT(setscpu->pc = entry, calls the C function) orengine_dispatch(for imports, registers, memory operands and hooked entries). - The callee's C function starts at
L_ENTRY(or the entry fast path), runs, and itsretdoescpu->pc = engine_pop(cpu, 4); esp += n; return;. - Back in the caller,
if (cpu->pc != ret_va): if the new pc lies inside the caller's own address range,goto L_ENTRYre-enters the caller's leader switch at that pc; otherwise the caller itselfreturns, propagating the unusual pc up the C stack until a function that contains it, or the host, is reached.
Step 4 is how translated code survives guest code that does not return normally: CRT longjmp, code that adjusts the stack and returns to a different site, and imports or host shims that set cpu->pc themselves. Because the decoder makes every call return site a block leader, the containing function can always resume there. (Full SEH unwinding is not modeled; the host's RtlUnwind shim fails explicitly.)
sequenceDiagram
participant A as sub_A caller
participant M as ENGINE_DIRECT
participant B as sub_B callee
A->>A: engine_push(cpu, RET, 4)
A->>M: ENGINE_DIRECT(cpu, B_VA, sub_B)
alt B_VA not in ENGINE_HOOK_ADDRESSES
M->>B: set pc to B_VA and call sub_B(cpu)
else hooked entry
M->>M: engine_dispatch(cpu, B_VA), an override may run instead
end
B->>B: entry fast path, pc equals B_VA so goto L_B
B->>B: body
B->>B: ret pops RET into pc
B-->>A: C return
A->>A: pc equals RET, continue
Note over A: otherwise goto L_ENTRY if pc is inside sub_A, else return
sequenceDiagram
participant A as sub_A caller
participant D as engine_dispatch
participant H as host external or override
participant T as engine_fns[idx]
A->>A: target read from IAT slot, memory or register
A->>A: engine_push(cpu, RET, 4)
A->>D: engine_dispatch(cpu, target)
alt target at or above 0xFE000000, an IAT magic handle
D->>H: engine_dispatch_external runs the shim
H->>H: RET_STDCALL pops RET and args, sets eax, sets pc to RET
else hook or override address
D->>H: engine_dispatch_override handles it
else translated code
D->>D: set pc to target, engine_record(target)
D->>T: binary search, call the function containing target
T->>T: L_ENTRY switch on pc, run block, ret sets pc
end
D-->>A: C return
A->>A: check pc against RET
An indirect jmp lowers to a switch over the current function's own leaders, so jump tables whose targets were harvested become direct gotos. Anything else is treated as a tail call: engine_dispatch(cpu, target); return;. Since cpu->pc then holds whatever the tail callee's ret popped, the C caller's return check works unchanged.
host_call_guest is the inverse direction (window procedures, APCs, thread starts): push arguments, push the sentinel 0xFF000000 as the return address, engine_dispatch(cpu, fn), then require cpu->pc == 0xFF000000 ("callback return mismatch" otherwise), pop cdecl arguments if needed, and restore ESP if the callback left it unbalanced.
engine_runtime.h defines a self-contained way to run translated code against explicit memory, with transactional rollback. The game does not use it; in this tree only EngineRuntimeContract.c links it. It is compiled without ENGINE_FLAT_MEMORY, so all accesses go through its region table.
| Type / function | Description |
|---|---|
HaloEngineRegion {address, byte_count, bytes, writable} |
a guest memory region supplied by the caller |
HaloEngineState |
registers[8], flags, pc, fp_values[8] in logical stack order (ST(0) first, whatever fp_top is), fp_control, fp_status, fp_valid, fp_top
|
HaloEngineStatus |
OK, INVALID, MEMORY, BUSY, EXECUTION
|
HaloEngineResult {status, pc, reason[160]} |
|
halo_engine_create(regions, count, result) |
validates every descriptor first (non-NULL bytes, non-zero length, writable 0 or 1, no 32-bit overflow, at most 65,536 regions, no overlap after sorting), then copies each region into owned memory. "No files, snapshots, emulator, network, host executable loading, or hidden runtime initialization" |
halo_engine_read / halo_engine_write
|
bounded access to one region; writes to read-only regions and cross-region spans are INVALID; BUSY while executing |
halo_engine_invoke(rt, entry, state) |
halo_engine_invoke_bounded with a 10,000,000-instruction limit |
halo_engine_invoke_bounded(rt, entry, state, limit) |
rejects a zero limit, a busy runtime, fp_top > 7, or a control word with any exception unmasked ((fp_control & 0x3F) != 0x3F); snapshots every writable region; loads the CPU (converting logical ST values to physical slots); calls engine_reuse_entry; on success publishes the CPU back in logical order; on engine_fail (via longjmp) restores every writable region and leaves state untouched |
Address lookup is a binary search over the sorted regions; an access must lie entirely within one region, and writes require writable. Calls on one runtime must be serialized by the owner (the busy flag only detects reentry).
| Macro | Default | Effect | Set by |
|---|---|---|---|
ENGINE_FLAT_MEMORY |
undefined | flat engine_flat_base + a addressing |
Makefile, visionOS builds, most tests |
ENGINE_STEP_FULL |
0 |
instruction budget, counter and pc trace in engine_step
|
runtime contract test (-DENGINE_STEP_FULL=1) |
ENGINE_TRACE_HOOKS |
0 |
treat trace-only addresses as hooked | manual builds for HALO_A10_TRACE work |
HALO_ARM64_FENV_FAST |
0 |
inline FPCR/FPSR assembly for non-nearest x87 rounding on arm64 | Makefile, visionOS builds (1) |
| Variable | Default | Effect | Read at |
|---|---|---|---|
HALO_TRACE_LO, HALO_TRACE_HI
|
unset (trace range empty: lo = 0xFFFFFFFF, hi = 0) |
hex guest pc range for engine_pc_trace logging; both must be set; effective only in ENGINE_STEP_FULL=1 builds |
host.c:627 |
The runtime headers themselves read no environment variables.
Built by run_source_checks.py:120-126 with -DENGINE_STEP_FULL=1 and linked with engine_runtime.c. It supplies its own engine_reuse_entry with six synthetic entries and checks:
| Check | Assertion |
|---|---|
| ownership | changing the caller's buffer after create does not change guest memory |
| public access | read-only write rejected; unmapped and cross-region accesses rejected |
| success | registers, flags and pc published; a guest-side read of ST(1) and two pushes appear as fp_valid == 3, fp_top == 6, fp_values[0] == 3.25, fp_values[1] == 1.5; guest write visible |
| failure rollback | after engine_fail, the HaloEngineState is byte-identical to before and both writable regions are restored |
| memory faults | unmapped read gives "unmapped guest address"; read-only write gives a reason containing "readonly"
|
| budget | a limit of 2 with three engine_steps fails with "instruction budget" and rolls back; a limit of 0 is INVALID
|
| repeatability | a second successful call after failures sees the earlier committed memory and stacks four x87 values |
| x87 validation |
fp_control = 0 is rejected unchanged |
| logical x87 ABI | for every TOP 0..7 and every valid mask 0..127, one push yields TOP-1, (valid << 1) | 1, and the logical values shifted by one, compared bit-for-bit including empty slots |
| create validation | zero length, writable == 2, address overflow, overlap and NULL descriptors rejected |
Defines a stub engine_dispatch and checks that ENGINE_DIRECT calls an unhooked entry directly with cpu->pc set to the entry, sends hooked entries (0050BEA0 panorama renderer, 00449590 sort, 00553920 visible surfaces, 00552DE0 BSP walk) through engine_dispatch, sends the trace-only 00511F30 direct unless ENGINE_TRACE_HOOKS, and that every listed hook address is hooked while address + 1 is not. Run by run_source_checks.py.
A static source check, run first in run_source_checks.py. It parses engine_hooks.h, overrides.c and the hook include files (a10_control.inc, a10_gamepad.inc, hsc_trace.inc, audio_ownership_trace.inc, model_capture_hooks.inc, panorama_hooks.inc, panorama_overlay_scope.h, native_leaves.h, frame_pacing_hooks.inc) and asserts:
- every address compared as
address == 0x...(or a#defined name) in those files and inengine_dispatch_override, plus004C6E80, is in one of the two hook lists; - behavioral hooks are not in the trace-only list (which goes direct in release);
- every entry of the
overrides[]table (except the0xFFFFFFFFplaceholder) is inENGINE_HOOK_ADDRESSES; -
overrides.cstill builds its interest filter from both lists and the override table.
Documents master-chef at commit 9f915af (v1.0.3). Unofficial project, not affiliated with Microsoft, Bungie, Gearbox or Apple. Original code is MIT licensed; game content is not included.
Overview
- Architecture Overview
- Repository Layout
- Glossary
- Environment Variables
- Contributing Guide
- Open Questions
Translation
- Static Translation Pipeline
- XWA Decoder and Lifter
- Function Address Lists
- EngineReuse Runtime
- x87 Floating Point
Host runtime
- EngineHost Overview
- Win32 Compatibility Layer
- Threading and Synchronization
- Guest Memory and Heap
- Engine Overrides and Hooks
- Runtime Settings
Graphics
- Direct3D9 Bridge
- Metal Renderer
- Shader Translation
- Textures and Texture Packs
- Geometry Fast Paths
- Radial Fog
Panorama and presentation
- Panorama System
- Panorama Budget and LOD
- Frame Pacing
- visionOS App
- Immersive Presenter
- Layer Alignment
Audio and input
Tooling and process