Skip to content

Open Questions and Discrepancies

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

Open Questions and Discrepancies

This wiki was written by reading the whole source tree at commit 9f915af. During that review, a few places turned up where the code, its comments, its tests or the user documentation disagree. Others are behaviors worth a second look. They are collected here so contributors can confirm or fix them.

None of these items was established by running the game or the tests. They come from reading the source. Each entry says what the code does and what remains unverified.

Possible correctness issues

x87 FPREM condition-code bits

engine_cpu.h:380-386 maps the low three quotient bits to the status word as follows:

Quotient bit Code sets Intel SDM says
Q0 C1 (0x0200) C1
Q1 C0 (0x0100) C3
Q2 C3 (0x4000) C0

C0 and C3 look swapped. This only matters to code that branches on those bits after FPREM or FPREM1, typically argument reduction that selects an octant. No test covers it. See x87 Floating Point.

Locked read-modify-write instructions are lowered without atomicity

The mnemonic is normalized with m.split()[-1] (extra.py:71). As a result, lock xadd and lock cmpxchg reach the ordinary lowering in extra.py:98-106, which is a plain read, compute and write. lock-prefixed flag instructions such as lock add are rejected instead (flags.py:106-113). It has not been checked whether Halo runs these instructions on data shared between its threads. That would be the main loop, the cache reader and the save writer. See XWA Decoder and Lifter.

Point and line primitives are rejected

primitive_vertices (d3d9_render.inc:948-951) returns 0 for D3D primitive types 1–3 (point list, line list, line strip). Every D3D draw entry point therefore rejects them, even though the Metal renderer supports lines and points. Whether Halo issues such draws in normal play is not established. See Direct3D9 Bridge.

Fog colour offset in the legacy fixed-fragment layout

The graphics review inferred from struct layouts that the shader would read the fog colour at byte 48, while the CPU writes it at byte 32 of a 48-byte buffer. This can only matter when all of these hold:

  • a programmable vertex shader is bound;
  • no pixel shader is bound;
  • fog is on;
  • radial fog is off.

It was not tested. See Metal Renderer.

Documentation that disagrees with the code

Where Says Code does
docs/CONTROLS.md "L2 / left trigger: Throw grenade" dinput8.c:354 maps L2 (and R3) to zoom; L1 throws a grenade. The in-app help text agrees with the code: "R2: fire · L2: zoom · L1: grenade · R1: melee" (HaloEngineVisionApp.swift:196). See Input and Controllers.
docs/VALIDATION.md (1.0.0 section) "the included Ubuntu CI workflow" .github/workflows/source-checks.yml runs on macos-15. The 1.0.2 changelog records the move to macOS.
Comments citing CORE_TELEMETRY.md (EngineDiagnostics.swift:482, EngineVisionRuntime.m:638, shims_kernel32.c:267) Refers to a design note No such file exists in the public tree.

Stale comments

Location Comment Current behavior
d3d9.c:2 "programmable shaders remain pending" Programmable shaders are translated through MojoShader. See Shader Translation.
EngineVisionRuntime.m:135 "Three slots of four BGRA8 textures" Each slot holds HALO_PANORAMA_LAYERS (10) textures. EnginePanoramaTexture.swift similarly says "four-layer snapshots".
panorama_hooks.inc header "Render three directions" 10 layers: 6 ring bearings, 2 caps, HUD and right-eye centre. See Panorama System.
panorama_budget.h Tiers wait "ninety frames" The code uses seconds. See Panorama Budget and LOD.
pointer.c:35 "HALO_GAZE_POINTER=0 seeds it off" It is off unless the value is exactly 1.
haptics.c A 2000-frame gap is "~45 ms at 44.1 kHz" Output runs at 48 kHz, which makes it about 42 ms.
engine_arm64_fenv_prototype.h:1 "Not included by engine_cpu.h; default OFF" engine_cpu.h includes it whenever HALO_ARM64_FENV_FAST is set, which every game build does.
engine_hooks.h A dispatch table of "8336 entries" The two checked-in lists hold 8,122 unique addresses. The rest presumably come from --discover; the exact count is in each generated ENGINE_FN_COUNT.

Inconsistent fallbacks

halo_settings.c seeds settings from the environment with one default, but its setters and accessors fall back to a different value when a stored value is out of range:

Setting Environment default Setter and accessor fallback
Panorama target FPS 30 (HALO_PANORAMA_TARGET_FPS) 27 (line 112, line 135)
Panorama vertical FOV 105° (HALO_PANORAMA_VFOV) 150° (2.618 rad, line 106, line 128)

See Runtime Settings.

Tests that look stale or are not run

tools/run_source_checks.py runs a fixed list. Several test sources are outside that list, and some of them no longer match the code they test:

Test Observation
native/EngineHost/tests/test_desktop_video_dialog.c Asserts a 1920×1080 desktop. shims_misc.c now reports 4096×3072 (HOST_DESKTOP_WIDTH/HEIGHT). It also needs a game directory.
native/EngineHost/tests/test_destalpha_blend.m Asserts MR_BLEND_COUNT == 9 (the header has 10) and calls configure_program_color with 4 arguments (it now takes 7).
native/EngineHost/tests/test_d3d9_destalpha_blend.c Expects DESTALPHA/ZERO to be rejected. It is now mapped to MR_BLEND_CUSTOM.
native/EngineHost/tests/test_d3d9_texture_cache.c Expects a new cache key after a byte change with no generation bump. The current code deliberately reuses keys within a generation.
native/EngineVision/Tests/PanoramaTextureValidation.swift, PanoramaViewportValidation.swift Target APIs that no longer exist (panorama(sector:), a 4-layer fixture).
tools/engine_reuse/test_decode.py, test_flags.py, test_fpu.py, tools/test_texmod_import.py, tools/test_hd_texture_merge.py Not invoked by run_source_checks.py or CI. The engine_reuse tests use package-relative imports and need the pinned Python dependencies.
Metal tests returning 77 when no Metal device is present The runner treats any non-zero exit as a failure, so 77 fails rather than skipping.

Testing and Source Checks has the complete catalogue, marking which tests the runner executes.

Build route differences

The Xcode route (XcodeGen project.yml) and the direct route (build_engine_vision.py --direct) produce slightly different apps:

  • HaloBuildID is written into the Info.plist only by the direct route. Xcode builds therefore report buildID: "unknown" in diagnostics.
  • UIFileSharingEnabled and LSSupportsOpeningDocumentsInPlace come from Info.plist on the Xcode route only.
  • The hand-tracking usage string differs between the two routes.
  • HALO_STAGED_ENGINE is passed as a compile definition by run_source_checks.py:123, but no source reads it.
  • native/EngineReuse/engine_runtime.c and module.modulemap are not compiled into either app. Only native/Tests/EngineRuntimeContract.c uses engine_runtime.c.

See Build System.

Unmodeled Windows behavior

Area Observation
SEH Structured exception unwinding is not modeled, and the RtlUnwind shim fails. Non-local returns of the longjmp style work through the L_ENTRY resume mechanism. See EngineReuse Runtime.
Missing shims There are no shims for ResetEvent, PulseEvent, CreateSemaphore*, WaitForMultipleObjects or SuspendThread. A call would fail loudly with "UNIMPLEMENTED import". This suggests the supported executable does not import them.
Handle lifetime Guest thread stacks and TEBs, and event, mutex, thread and mapping objects, are not freed on CloseHandle. See Threading and Synchronization.
C:\Halo\... paths host_path maps absolute paths under the game root without special-casing the C:\Halo prefix used in the synthetic command line.

Related pages

Clone this wiki locally