# CRUSADER.EXE Hole-Filling Priorities From CRUSADER-RAW.EXE ## Purpose This note tracks the highest-value places where the NE-loaded `CRUSADER.EXE` project is still materially unclear, but the older `CRUSADER-RAW.EXE` project already holds enough verified structure to close the gap. The goal is not to duplicate the raw notes. The goal is to rank the NE-side holes that are most likely to collapse quickly once the raw-side evidence is ported carefully. Evidence used here: - live Ghidra MCP checks in the open `CRUSADER.EXE` session - `plan-mid.md` - `crusader_segment_coverage_ledger.csv` - `docs/raw-porting-progress.md` - `docs/raw-0007-rendering.md` - `docs/raw-0008-000c.md` - `docs/raw-000a-000d.md` - `docs/raw-000e.md` - `docs/crusader-disasm-reference.md` ## Priority 1: VM / USECODE Selector And Owner-Loaded Runtime Lane ### Why it is still unclear in `CRUSADER.EXE` - The NE project already carries the main structural names, but the actual upstream selector into the VM sequencer is still unresolved. - The dark wrappers around slot `0x0a` / `0x0b` are named structurally, not behaviorally. - The owner-resource helper is partially understood, but the exact class-family selection and configured owner-file naming are still open. - Coverage ledger state is still only `Partial` for segments `133`, `134`, and `135`. ### What the raw project already knows - `000d:463a` is now a verified generic masked-create hub: `entity_vm_context_try_create_masked_for_entity`. - `000d:45c5` is a three-way category mapper: `entity_vm_slot_index_from_entity`. - `000d:4c99` and `000d:7000` classify the runtime bootstrap and owner-resource creation path. - `000d:ebe3` is a verified ordered opcode sequencer: `entity_vm_opcode_sequence_run`. - `0005:2c35` is already narrowed to `entity_vm_context_try_create_mask_0400_slot0a_with_offset`, but its outward caller role is still dark. - The raw-side docs also already carry the exact `NPCTRIG` / `EVENT` body-window fits, referent-registry model, and the current selector blocker. - `docs/crusader-disasm-reference.md` already identifies the best auxiliary corpus for this lane: `usecode_opcodes.txt`, `map-item-dump.txt`, `shapedata_more_complete.txt`, and the `unkcoffs/` dumps. ### Porting work that should close the gap - Port the raw-side selector, context, and owner-loader comments into the NE project at the same segment:offset anchors. - Keep the slot-`0x0a` / slot-`0x0b` wrappers conservative until real caller roles are recovered. - Use the external opcode list and map/shape corpus only as hint generators while reconciling the live NE caller path. - Prioritize recovery of the first caller that turns the category spans from `000d:45c5` into a concrete class-family choice. ### Live anchors in the open NE project - `000d:45c5` -> `entity_vm_slot_index_from_entity` - `000d:463a` -> `entity_vm_context_try_create_masked_for_entity` - `000d:4c99` -> `entity_vm_runtime_create` - `000d:ebe3` -> `entity_vm_opcode_sequence_run` - `0005:2c35` -> `entity_vm_context_try_create_mask_0400_slot0a_with_offset` ### Latest verified NE pass - `0005:295f` is now the only recovered non-hub consumer of `entity_vm_slot_index_from_entity` in the open NE session. It recomputes the slot index, tests owner-row bit `0x0040` directly, exposes that result to the caller, and only then optionally calls `entity_vm_context_try_create_masked_for_entity` with slot `0x06` and mask `0x0040`. - The direct NE callers currently recovered for `0005:295f` are still only `0006:43c3`, `0006:c5f0`, and `0007:3584`, but they are now all structurally classified in the live NE session instead of only enumerated as open caller families. - The first anchored caller family is now structurally classified in the live NE session. Repaired wrapper `0006:4379` is a seg031 dispatch-entry subtype gate over objects built by `0006:42d9`: event type `0x236`, source type `8`, subtype/tag at `+0x3c`, dword payload/source far pointer at `+0x32`, and aux words at `+0x36/+0x38`. - In that wrapper, subtype `0x20c` at `0006:43c3` routes into `0005:295f`, while nearby sibling subtype `0x20b` at `0006:43e5` routes into `0005:2918` (`slot 0x05` / `mask 0x0020`) using the same `+0x36/+0x38` aux pair. The split is therefore local to one dispatch-entry family, not a direct compiled `NPCTRIG` / `EVENT` class-family selector by itself. - The `0006:43c3` lane now shows where the owner-row bit-`0x0040` probe is consumed locally: inside a subtype-`0x20c` dispatch-entry object rather than at a generic descriptor-choice site. That improves caller provenance for `0005:295f`, but it still does not prove which owner-loaded class family seeded the later VM data. - The second direct caller family is now closed too. Old `0006:c5f0` lands at live call site `1128:0ff0` inside `Item_ReceiveHit`, where the non-NPC item path calls `Item_GetDamaged` with hitter sentinel `0x4000`, packed damage `(damagetype << 8) | damage_lo`, and a local flag-out byte that records the returned owner-row bit-`0x0040` capability before the local destruction / impact follow-up. - The third direct caller family is now closed too. Old `0007:3584` lands at live call site `1138:1384` inside `SuperSprite_HitAndFinish`, where the non-NPC collision lane probes `Item_GetDamaged` with hitter sentinel `0x4000` and packed damage `(firetype << 8) | damage`; only when that flag-out byte stays clear and the target is not fixed does the lane fall through into the local `Item_ReceiveHit` knockback path. - The next earlier compiled-side producer is now closed for one real gameplay family. `AreaSearch_CollideMove` at `10e0:123a` queues paired `0x236` storage processes in both its first-collision and linked-list collision lanes: `0x20b` is always created as the local `hit` notifier from moving item to collided item, and the reciprocal `0x20c` process is always created as the `got-hit` notifier from collided item back to the moving item. - The local queue-helper cluster around that producer is now named in the live NE database too: `10f0:046d` = `storage_process_ref_list_create`, `10f0:0502` = `storage_process_ref_list_append`, and `10f0:06b5` = `storage_process_ref_list_destroy`. Their recovered contract is a counted far-pointer array drained later by `StorageDataProcess_RunAndTerminateProcs`, not a darker allocator or owner-resource helper. - The producer surface above `AreaSearch_CollideMove` is now wider but still collision-local. Direct callers currently verified in the live session are `Item_LegalMoveToPoint`, `Item_LegalMoveToPointWithCollisionInfo` (`10a0:1841`), `GravityProcess_Run`, `AnimPrimitive_CheckToStartNewAnimation`, `AnimPrimitiveProcess_Run`, `SuperSprite_AdvanceFrame`, and `GravityProcess_FastAreaCleanup` (`1038:11fd`). No non-collision caller currently reaches `StorageDataProcess_Create` or `StorageDataProcess_RunAndTerminateProcs` directly. - Two more local movement helpers are now named structurally in the live NE database: `10a0:1841` = `Item_LegalMoveToPointWithCollisionInfo`, a legal-move wrapper that preserves blocked/collision outputs around the same `AreaSearch_CollideMove` commit path, and `1138:0ee8` = `SuperSprite_SweepTestAdvance`, the supersprite-side sweep probe that stores the first collision before `SuperSprite_AdvanceFrame` commits the move. - The surrounding movement and cleanup helper layer is now less anonymous too. `10e0:11c5` = `AreaSearch_SweepShapeBetweenPoints`, `10e0:15b4` = `AreaSearch_SweepItemToPointWithStepUp`, and `10e0:162f` = `AreaSearch_SweepShapeBetweenPointsWithStepUp` now cover the step-aware sweep path beneath the legal-move wrappers, while `10f0:03ff` = `StorageDataProcess_Release` and `10f0:0542` = `storage_process_ref_list_terminate_item_matches` close the local queue-release side. Adjacent seg090 helper `10a0:196f` is now `ItemCache_PushAndPopToDirectionalOffset`. - `0005:2c35` remains outward-dark in the current NE session: instruction search still shows no recovered code or data xrefs, and its proven local role is still only `sign-extended additive word -> slot 0x0a / mask 0x0400 -> generic masked hub`. - The live `CRUSADER.EXE` integration batch is now extended for this lane. Comment-backed anchors were already present at `1420:0dc5` (`Item_GetUsecodeClassId`), `1420:0e3a` (`Usecode_ItemCallEvent`), `10a0:2718` (`Item_Hit`), `10a0:275f` (`Item_GetDamaged`), `10f0:02d9` (`StorageDataProcess_Create`), and `10f0:0379` (`StorageDataProcess_Run`), with branch comments at `10f0:03c3` and `10f0:03e5` preserving the verified `0x20c` / `0x20b` split; this pass adds live comments at `10e0:123a`, `10f0:046d`, `10f0:0502`, and `10f0:06b5` and promotes the queue helpers to stable names. - Result of this pass: all currently recovered direct `0005:295f` caller families are now closed, and the current `0x236` storage-process producer surface is now mapped far enough to say something negative too: the queue remains collision-local in the current database and is reached through movement, gravity, animation, and supersprite area-search paths rather than through a broader owner-family selector. The next defensible NE step is therefore either an earlier policy/dispatch layer deciding when those movement lanes call `AreaSearch_CollideMove`, or the first real non-collision producer if one exists elsewhere. ## Priority 2: Rendering / Camera / Tile-Visibility / Watch-Controller Lane ### Why it is still unclear in `CRUSADER.EXE` - The raw rendering/camera work is one of the strongest finished structural maps, but the NE ledger still shows only `Partial` coverage for the watch-controller lane (`seg049`) and `None` for several related render-heavy segments. - The exact controller-versus-watched-entity ownership label around `0x2bd8` is still open. - The NE side still does not have the same whole-lane readability that the raw `0007` rendering notes already provide. ### What the raw project already knows - The raw notes already define the draw-list node format, tile visibility pipeline, screen/global state, scroll/camera globals, and the watch-controller family. - `0007:ba00`, `0007:ba45`, and `0007:baea` already have conservative structural names and caller contracts in the raw project. - The coordinate-transform lane is also already documented in a way that can be reused safely for the NE segments that call into it. ### Porting work that should close the gap - Port the controller/object comments around `0x2bd8`, `0x2be0`, and the scroll globals before attempting any more aggressive renames. - Carry the draw-list, tile-grid, and viewport/global names into the NE lane where the same DS globals and call shapes match. - Use `map-item-dump.txt` and `shapedata_more_complete.txt` to explain object-heavy camera/trigger placements only after the binary side matches the raw lane. ### Evidence anchors - Ledger: `seg049` = `Partial`, `seg053` = `None`, multiple surrounding render-heavy segments remain `None` - Raw-side anchor doc: `docs/raw-0007-rendering.md` ## Priority 3: Startup / Display Transition And Active-Hold Lane ### Why it is still unclear in `CRUSADER.EXE` - The NE startup/display lane is structurally strong, but one of the key bodies is still overlap-damaged. - `000c:db68` currently resolves to `cursor_nav_update_and_dispatch`, but boundary analysis still shows an obviously oversized body spanning `000c:5b68 - 000c:ff1c`. - The renderer-pair role at `0x8c5c/0x8c60` and the exact higher-level label for some preset paths remain open. ### What the raw project already knows - The raw project already separates the seg126/127/136/137/138 transition helpers into a usable startup/display model. - The `0x6828 + 0x40` borrowed hold byte is already separated from the seg108-local `0x4f38` bit-`0x40` lane. - The fade-controller behavior and the shared break/hold state around `0x31a2` are already documented tightly enough to guide NE cleanup. ### Porting work that should close the gap - Use the raw-side boundary and caller notes to split `000c:db68` back into the real local helper bodies before promoting any more labels. - Port the hold-token provenance and the seg108-versus-seg136 separation comments into the NE database. - Keep the remaining renderer-pair naming conservative until the overlap is repaired. ### Live anchors in the open NE project - `000c:db68` -> `cursor_nav_update_and_dispatch` - Boundary analysis for `000c:db40..000c:dc40` still reports `cursor_nav_update_and_dispatch @ 000c:db68 body 000c:5b68 - 000c:ff1c` - Ledger: `seg126`, `seg127`, `seg136`, `seg137`, `seg138` are all only `Partial` ## Priority 4: `0x4588` Callback / Allocator / Palette Cleanup Lane ### Why it is still unclear in `CRUSADER.EXE` - Many helpers in this lane are already named in the NE project, but the actual subsystem identity of the `0x4588` object is still unresolved. - The same blocker is repeated across the ledger for segments `80`, `82`, `91`, `95`, and `138`, which means the NE project has structure but still lacks the final behavioral classification. ### What the raw project already knows - The raw project already has a stable lifecycle map for `runtime_callback_object_init_once`, `runtime_callback_object_teardown_once`, `allocator_phase_finalize_pass`, and `entity_cleanup_resources_and_dispatch`. - The raw notes already constrain the video-state snapshot pair, one-time guards, vtable call slots, emitted payload pairs, and the relationship to the watch-controller release path. ### Porting work that should close the gap - Port field-level comments around `0x4588`, `0x4590`, `0x4594`, `0x4595`, `0x45a6`, and the emitted payload pairs before attempting any subsystem rename. - Reconcile the raw callback emissions with the NE caller windows in segments `91`, `95`, and `138`. - Only promote a behavior-level subsystem name once those emitted payload pairs and caller families converge in the NE project too. ### Live anchors in the open NE project - `000a:4913` -> `runtime_callback_object_init_once` - `0009:b1c3` -> `allocator_phase_finalize_pass` - `000d:9afd` -> `entity_cleanup_resources_and_dispatch` ## Priority 5: Parser / RIFF / Animation Video-Subframe Lane ### Why it is still unclear in `CRUSADER.EXE` - The main parser and animation entrypoints are already named in the NE project, but the surrounding segment coverage is still weak. - The ledger still shows `None` for the raw-`000e`-mapped segments `99` through `103`. - `000e:ffb0` is still only a positional function object and remains overlap-damaged. ### What the raw project already knows - The raw notes already define the parser cluster, RIFF chunk matching, animation ring-buffer model, and the audio/video split. - The raw-side caller proof already ties `000e:ffb0` to the `00db` / `00dc` video-frame lane paired with `anim_load_audio_frame`. - The raw notes also already separate the text-oriented parser usage from the binary owner-loaded USECODE lane. ### Porting work that should close the gap - Port the parser and animation comments into the NE segment windows that still show no formal coverage. - Keep `000e:ffb0` conservative, but import the raw-side comment that it is the unresolved video-side subframe loader for the `00db` / `00dc` chunk lane. - Normalize the segment-to-topic mapping for the `000e` range in the NE coverage ledger once those comments are in place. ### Live anchors in the open NE project - `000e:2104` -> `animation_start` - `000e:3639` -> `record_table_parse_buffer` - `000e:ffb0` -> `FUN_000e_ffb0` - Boundary analysis for `000e:ff90..000f:00f0` still reports `FUN_000e_ffb0 @ 000e:ffb0 body 000e:ffb0 - 000f:00e0` ## Priority 6: Gameplay / Input / Projectile Expansion Into Adjacent NE Lanes ### Why it is still unclear in `CRUSADER.EXE` - Segment `1` itself is already deep, but the adjacent event/dispatch and targeting lanes are not equally complete. - The ledger still shows only `Partial` coverage for `seg021` and `seg043` even though the raw project already holds stronger gameplay-side anchors. - The old disasm corpus has shape and event-code evidence that can explain object-heavy gameplay lanes, but that evidence has not yet been turned into a disciplined NE crosswalk. ### What the raw project already knows - Verified raw imports already exist for `shot_entity_alloc`, `projectile_update_tick`, and the rest of the main projectile chain. - `seg021` already has a verified direct raw import anchor via `entity_count_by_type_a`. - The external corpus adds key/event notes such as `CTRL-Q = 0x410` and map-backed shape examples like `STEAM2` and `SnapEgg`. - The raw side also already narrowed the seg043 start-of-segment hole enough to reject the earlier bad mapping. ### Porting work that should close the gap - Extend from the already-verified gameplay names into adjacent NE dispatch/targeting callers only when the segment:offset and caller role match directly. - Build one conservative shape-id / map-placement crosswalk for trigger-heavy or object-heavy gameplay lanes before promoting new names. - Use the old key/event notes as lead generation only, not as direct rename authority. ### Live anchors in the open NE project - `0007:28ce` -> `shot_entity_alloc` - `0007:371d` -> `projectile_update_tick` - `0007:5a00` has no current symbol - Boundary analysis for `0007:59e0..0007:5b40` still reports `0007:5a00` as a candidate entry and keeps the earlier rejected mapping explicitly closed ## Secondary Structural Gaps To Keep On The Tracker These are real NE holes, but they are weaker `old -> new` closure candidates than the priorities above because the raw-side model is still not strong enough to drive safe naming by itself. ### `000b:2e00` - Still a high-traffic gap in the NE project. - Live symbol lookup returns no symbol at `000b:2e00`. - Boundary analysis for `000b:2de0..000b:3050` shows a missing top-level entry at `000b:2e00` plus several later artificial subfunctions. - Keep it on the list, but do not treat it as the first raw-to-NE porting target. ## Immediate Actions Justified Now - Create and maintain this tracker document. - Use it as the ranked starting point for the first focused `CRUSADER.EXE` porting batch. - Avoid immediate new Ghidra renames in the listed holes unless a pass first repairs the overlapping function objects or recovers the missing callers. - Conservative comments are justified before renames in the VM, transition, and animation lanes because the remaining uncertainty is mostly about caller provenance and boundaries, not about the already-known local contracts. ## Progress Estimate Impact This note still does not justify a headline percentage change by itself. The `plan-mid.md` progress estimates should stay unchanged until one of these prioritized NE passes produces verified new symbol, boundary, or ledger promotions beyond this local caller-family classification.