port/third_party/sfvr is a copy of ~/code/sfvr's include/ and src/ (VERSION
holds its commit, ad2f545), never edited here. The VR guest build compiles
its six .c files for the ILP32 guest with the project's ABI flags (and
-Wall -Wextra, which they pass clean); nothing uses them yet.
The build is registered on the Frame as a Lepton devkit title, HaloCEVR,
which deploy.sh keeps up to date, so the game starts from the Steam
library without the Mac. SteamVR offers an app only the rate its display
setting is on, so vr.refresh_rate takes the highest offered at or below
it; the docs say where to set 90 Hz.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The main menu's scene stays on the flat screen behind its menus,
rather than going stereo with the menus head-locked.
- The HUD layer was invisible: the game's HUD never writes alpha, which
it keeps as scratch. Its copy into the layer now makes each pixel as
opaque as it is bright (premultiplied, OpenXR's default).
- The right stick's up and down reach the game again while the head
aims (the head still sets the facing each frame), so the first level's
look test, which watches the stick, can pass.
- vr.timing logs where a frame's time goes ([vr-frame]); vr.timing_gpu
waits for the GPU before each frame to time it apart.
- deploy.sh: macOS's rsync has no --info.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It is staged in build/android/jniLibs-vr, which only the vr flavour
packages, instead of beside the flat build's libraries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lepton's app storage goes with a container reset; the headset's
Documents folder (Android's /sdcard/Documents) stays. The VR build
prefers game data in Documents/HaloCE, with its settings and saves
beside it.
tools/steam_frame/extract_maps.py copies maps out of a disc image on the
Mac (as port/linux/src/xiso.c reads it); deploy.sh copies them to the
headset over SSH, edits config.toml settings, installs and starts the
app over Lepton's adb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
configure.py --vr builds com.halo.decomp.vr: the host gains host_xr.c,
which owns the Khronos loader (1.1.63, fetched from Maven Central and
checked by SHA-256), the instance, a GLES session on the game's EGL
context, two eye swapchains and a quad, and controller actions for the
Steam Frame, Touch and Index profiles. The guest drives it through seven
imports (host_imports_vr.list) with fixed-layout structures in
halo_android_abi.h, checked on both ABIs.
vr.probe_seconds shows dim colours in each eye before the game starts.
On the Steam Frame (Lepton) it reaches SYNCHRONIZED and holds 72 frames
a second; the Frame controller profile binds.
SDL3's Java side assumed a clipboard service, which Lepton lacks; a
patch (port/android/patches) applied at configure time checks for it.
The update provider's authority follows the application id, so the
flat and VR apps install side by side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NDK's host prebuilts live under darwin-x86_64 on a Mac (universal
binaries), and the case-insensitive file system made StdDef.h include
itself instead of musl's stddef.h; include_next skips this directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recover transparent geometry submit (+1,285 meaningful code, zero exact losses); reconcile COMMON ownership and Q10 85+EXT1 credit, actions/main data, and separately disclosed denominator corrections. Preserve canonical-only repairs and all held research. C09 is ordinary disclosed cleanup, not a claim of proven scaffolding intent. Clean rebuild, 1,663 tests, parks, audits, provider links and independent EXT1 check pass. Local reconciliation only.
PB from dccd2e95, including coverage/substitution hardening. Current canonical independent 910-section and20-provider review passed; existing report unchanged without new entry. Full build/stable/parks/admission clean; 1318 tests,5 skipped,100 subtests. Scorer remains pinned3.3.1; no actions credit.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Owner-approved PA+PC from c32e53c2. Independent current-object review and negative controls passed. Full build, stable sweep zero gains/losses, parks/admission unchanged; 1199 tests pass. Extent credit and verifier are separate commits.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
tools/system_link_bots.py joins a native-build host with many lightweight
machines, each with one player, speaking the system link protocol directly:
join, settings (reassembling the fragmented record), player, map precached,
loaded, then a UDP input message each tick that acknowledges the host's
updates, as a real client keeps a lockstep game running. Each machine binds
its own loopback address (127.0.0.2 and up), since the host tells machines
apart by address.
python tools/system_link_bots.py --machines 127 --start
fills a 128-machine session on the host at 127.0.0.1 and starts it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrate verified source and data progress; exclude unproven COMMON ownership, held UB/asm forms, and address-named data credit. Stable sweep: 23 exact functions gained, zero lost.
The old ranking was wrong in a way that buried the cheapest work. alndiff's
region count systematically OVERSTATES the closest functions, because it counts
pure addressing noise as divergence: relocation-target spelling, the relocation
SITE address (which shifts whenever anything upstream changes length), branch
immediates, and trailing alignment NOPs. relocdiff scores all of those `=`.
Stripped, the picture changes completely:
_actor_path_refresh 12 reported -> 0 REAL
_actor_look_idle_find_prop 16 reported -> 1 REAL
_actor_perception_friend_prop_is_attacking 3 reported -> 1 REAL
_actor_situation_update 23 reported -> 2 REAL
residual_triage.py puts frame, reference multiset, real code end and REAL
regions in one row per residual and ranks cheapest-first. It takes a directory,
so bare it covers all of source/ai (51 residuals, 96,032 bytes); the committed
table is Lane A's eleven units (30 residuals, 75,392 bytes).
The top of the table is a cluster of eight functions at three or fewer real
regions, about 8,200 bytes. Two had never been probed and are decoded in the
write-up:
_actor_path_refresh (1,440 B, 0 real regions) - the whole residual is THREE
branches at 4 bytes each. January encodes them short to the NEAR copy of the
return epilogue; we encode near to the FAR copy 0x267 bytes back. Both copies
exist in both builds, only the binding differs. Source site at
actor_moving.c:2064 and a suggested lever recorded in scratch.
_actor_look_idle_find_prop (608 B, 1 real region) - January emits
`cmp ebx, dword ptr [_ai_debug+0x38]`, we emit a load then a compare. The
source is already the natural form at both of its sites and the split appears
where the scheduler fills an fcos latency gap, so it is instruction selection
under x87 scheduling. Also 3 bytes short overall, so a branch-width difference
is hiding behind the normalisation.
Also decoded and recorded: _actor_situation_update (2 real regions, one
five-instruction block January places out of line and we place inline) and
_actor_perception_friend_prop_is_attacking (1 real region, an AL/CL
two-colouring; three guard spellings measured, all dead).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found the tool's own blind spot by using it. It compared relocation SITE
addresses (`rel+0x...`) and branch immediates as if they were content, so any
upstream length change made every downstream row look like a divergence.
_actor_path_refresh read 9 REAL regions when its instruction text is in fact
identical throughout.
Now stripped: the row address, the relocation-target spelling, the relocation
site address, and branch/call immediates. Trailing alignment filler is counted
separately instead of appearing as a twelve-NOP "deletion". Output splits into
REAL / addressing-only / padding-only.
_actor_path_refresh 12 reported -> 0 REAL
_actor_perception_friend_prop_is_attacking 3 reported -> 1 REAL
And the caveat is now in the docstring rather than left to be rediscovered:
normalising a branch immediate also hides SHORT vs NEAR encoding, which is a
real byte difference. `0 REAL` means every instruction's TEXT matches, NOT that
the function is exact. _actor_path_refresh is the worked example - content
identical, our encodings about 12 bytes longer, January's 12 trailing NOPs
absorbing it into the same section size. `0 REAL` plus a padding-only region
means the gap is encoding length: go compare where real code ends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alndiff prints a region whenever its two normalized instruction texts differ,
and that includes a pure PRESENTATION difference in how the two objects spell a
relocation target - `target=symbol:__real@0` against
`target=defined-noncode:.rdata:__real@0`. Same address, same type, same target;
relocdiff --allow-structural scores that row `=`. It is not a divergence.
Over the Lane A AI portfolio, 86 of 860 reported regions (10%) are that noise -
but it is not spread evenly. It is concentrated on exactly the functions that
are CLOSEST, where the ratio is worst:
_actor_perception_friend_prop_is_attacking 3 raw -> 1 real
_code_00039990 11 raw -> 2 real
_ai_communication_get_player_rating 12 raw -> 2 real
_actor_emotion_update 12 raw -> 3 real
_ai_communication_actor_talk_weight 9 raw -> 3 real
_actor_perception_update 30 raw -> 14 real
A function reported at "12 regions" that is really at 2 is one a triage pass
will wrongly deprioritise, so this systematically hides the cheapest work.
Read-only, recompiles nothing, runs in no build step. --show prints the
surviving regions so you can go straight to the real divergence.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>