Crusader_Decomp/docs/ne-hole-filling-priorities.md

16 KiB

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.
  • 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; new live comments now also anchor the remaining direct caller sites at 1128:0ff0 and 1138:1384.
  • Result of this pass: all currently recovered direct 0005:295f caller families are now closed, but the compiled-side selector evidence still bottoms out at subtype-gated dispatch or generic gameplay damage consumers plus owner-row capability bits, not a concrete NPCTRIG / EVENT class-family choice. The next defensible NE step is therefore an earlier producer that assigns subtype 0x20b/0x20c into field +0x3c or otherwise chooses the owner-loaded class family before these generic damage consumers run.

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.