# Crusader Decompilation Mid-Project Plan ## Purpose This file is the workspace-facing mid-project tracker for the Crusader decompilation effort. It is intended to answer four questions clearly: 1. How far along is the project? 2. What is already solid? 3. What still blocks broader decompilation? 4. What should be implemented next? The estimates below are intentionally conservative. They measure verified behavioral understanding, not just renamed symbols. ## Progress Snapshot ## Working Progress ### Last Confirmed State - Priority 0 has started: `crusader_segment_coverage_ledger.csv` exists and contains a first-pass 145-row ledger. - The currently seeded ledger rows are conservative and strongest around seg001, seg004, seg021, seg043, seg080, seg082/083/085, seg091, seg094, and seg095. - Priority 1 has started on the cache/backend cluster: the seg082 allocator mechanics are now materially recovered (`allocator_head_try_alloc_block`, `allocator_head_free_block`, `allocator_free_block_by_ptr`, `allocator_try_alloc_from_head_table`, `allocator_phase_finalize_pass`), and the `0x4588` path now has named lifecycle helpers (`runtime_callback_object_init_once`, `runtime_callback_object_teardown_once`). - The `0x4588` blocker is tighter than before: `000a:b988` boundary repair now includes both callback sync callsites (`000a:b9e5` / `000a:ba66`) inside one real function body, `000d:9d5e` / `000d:a3b7` are confirmed inside `entity_cleanup_resources_and_dispatch`, and adjacent helpers are now clarified as `allocator_head_finalize_sweep` (`0009:a961`), `video_bios_state_snapshot` (`000a:4a1f`), and `video_mode_set_and_record_state` (`000a:4972`). Concrete subsystem identity is still unresolved. - A larger MCP rename batch completed for cleanup callees: `palette_buffer_alloc_and_init_256` (`0009:7853`), `file_handle_alloc_init_and_open` (`0009:1c3a`), `file_handle_open_with_mode` (`0009:1d6a`), `surface_release_internal` (`0009:8d7b`), `surface_release_and_maybe_free` (`0009:8e0a`), and `sprite_redraw_global_if_active` (`000d:9231`). This reduces `entity_cleanup_resources_and_dispatch` ambiguity on file/surface/palette teardown paths. - The previously missing `000d:7e00` function object is now recovered and named `entity_dispatch_entry_init_runtime_state`, with paired destructor `entity_dispatch_entry_release_runtime_state` at `000d:8078`. Adjacent missing helpers `0003:a880` and `0003:b8e2` were also recovered, with `0003:b8e2` promoted to `far_buffer_alloc_with_mode_flags`. - Additional helper stabilization now covers seg061/064/076: `vga_palette_read` (`0009:6ec7`) is confirmed alongside existing palette write/free paths, `timer_entity_enable_wrapper` (`0008:d3ba`) is named, and seg064 one-shot gate helpers around `0x3b72/0x3b73` are documented with conservative comments while keeping speculative naming deferred. - Constructor-lane semantics tightened further: `entity_set_update_period_and_reschedule` (`0008:d27e`) and `palette_buffer_alloc_copy_from_source` (`0009:7905`) are now named, and both `0x4588` callback emit callsites (`000d:9d5e`, `000d:a3b7`) now have explicit payload-pair annotations in disassembly. - The seg082 allocator table structure is now pinned down as the allocator head table at `0x8724` and active head count at `0x879c`, and the old structural helpers at `0009:b06b` / `0009:b1c3` are now promoted to `allocator_try_alloc_from_head_table` and `allocator_phase_finalize_pass`. - New caller-side seg138 evidence now exists at `FUN_000d_938c` (`000d:938c-000d:9583`): it builds one scratch-palette dispatch entry (`kind 0x3c`) and one current-palette dispatch entry (`kind 0x14`) through `entity_dispatch_entry_init_runtime_state`, waits for each entry's active flag to clear, then redraws the global sprite path and dispatches through the input object's vtable slot `+0x08`. This narrows the open lane to presentation/dispatch semantics without yet justifying a concrete subsystem rename. - seg137 is now promoted from `Foothold` to `Partial`: direct MCP recovery stabilized a coherent palette/dispatch-entry helper family with safe renames for all-black, all-white, arbitrary-RGB, grayscale, black-state, and solid-color state builders around the same `entity_dispatch_entry_init_runtime_state` lane. The remaining gap is the higher-level event/script meaning of those helpers, not the local mechanics. - seg005 and seg136 now have new high-value footholds: `FUN_0004_60c0` is recovered as a startup/display orchestration handoff that drives the seg137 palette helper family, validates an object through vtable `+0x0c`, creates the default active dispatch entry, programs mouse state, and then hands off into `0004:1e00`; nearby seg136 helpers are now stabilized as `active_dispatch_entry_mark_enabled`, `active_dispatch_entry_mark_disabled`, and `active_dispatch_entry_create_default`. - The downstream seg005 handoff body is now also classified further: `FUN_0004_1e00` (`0004:1e00-0004:2420`) is a non-return startup/display transition driver with confirmed use of `vga_palette_set_all_black`, `animation_ctor_variant_b`, `sprite_node_get_or_traverse`, seg064 gate helpers, the `0x2bd8` vtable lane, and the `0x4aa/0x7e22` resource/object lane. The remaining work is naming the exact state label, not repairing the structure. - seg126 is now promoted from `Foothold` to `Partial`: `FUN_000c_7412`, `transition_preentry_setup_resources`, `transition_preentry_release_resources`, `transition_preentry_run_until_complete_or_abort`, `transition_preentry_step_script`, `thunk_callf_0000_ffff_000c_827d`, `thunk_callf_0000_ffff_000c_82f9`, and `FUN_000c_834a` now show a coherent pre-entry, guarded-entry, script/fade step, and post-transition control shell around the same `FUN_0004_1e00` startup/display state. - seg127 is now promoted from `Foothold` to `Partial`: `palette_fade_begin_full_up`, `palette_fade_begin_full_down`, `transition_palette_fade_begin`, `transition_palette_fade_tick`, `transition_palette_fade_out_step`, and `transition_palette_fade_in_step` form a concrete local palette-fade controller with verified full-range wrappers and caller-side state gating immediately beside the same seg126/seg005 transition lane. - seg049 is no longer blank: `watch_entity_controller_create_global`, `watch_entity_controller_create`, and `watch_entity_controller_dispatch_if_present` now show that `0x2bd8` is a real type-stamped watch/camera controller object lane rather than only a raw watched-entity pointer, and that same controller is exercised from `FUN_0004_1e00`. - seg108 is no longer blank: `sprite_object_clear_flag40_if_present` and `sprite_object_set_flag40_if_present` now anchor the `0x4f38` global sprite/object lane as a real state-bit-controlled object path used beside the same `0x4588` callback sync and startup/display transition flow. - Direct MCP follow-up on seg126 and seg127 now recovered the missing helper bodies after boundary repair: `transition_preentry_setup_resources` (`000c:c63a`), `transition_preentry_release_resources` (`000c:c890`), `transition_preentry_run_until_complete_or_abort` (`000c:c9f4`), `transition_preentry_step_script` (`000c:ca1d`), and the neighboring `transition_palette_fade_tick` / `transition_palette_fade_begin` / `transition_palette_fade_out_step` / `transition_palette_fade_in_step` chain are now named against verified behavior. The latest semantic pass also tightened the two main open globals: `0x8c5c` / `0x8c60` are now best understood as a paired temporary text-renderer lane, while `0x31a2` behaves like an external input/event break gate maintained by queue/interrupt-side code. The remaining structural cleanup is the separate oversized overlap rooted at `000c:db68`, not the seg126 helper family. - Bonus cheat-lane cleanup is now visible in Ghidra too: `cheat_code_check` has recovered local names (`input_event_record`, `input_event_offset`, `new_cheat_enabled`, `cheat_status_display_root`) and a decompiler comment stating that it matches the five-byte event-code sequence `50 80 3e fd 27 00` before toggling the cheat-state bytes and taking one of two local notification paths. ### Current Focus 1. Finish Priority 0 refinement by promoting more exact segment rows where notes already support a verified foothold. 2. Continue the Priority 1 pass by tracing the higher-level startup/display callers, branch outcomes, pre-entry object lanes, palette-fade ownership, watch/camera controller ownership, and active sprite/object ownership that stitch the seg137 palette helper family into the wider `0x4588` / dispatch-entry object-role lane. ### Next Resume Point 1. Keep classifying the seg126 pre-entry text-renderer lane around `transition_preentry_setup_resources`, `transition_preentry_step_script`, and `transition_preentry_release_resources`, especially by: - comparing more preset `0x10` / `0x11` text-renderer callsites, - tracing who owns the rendered buffer loaded into `0x6301:0x6303`, - mapping the control bytes `0x21` / `0x23` / `0x24` / `0x26` / `0x2a` / `0x40` / `0x5e` to concrete display behavior, - and deciding whether the paired `0x8c5c` / `0x8c60` lane is a title/body pair, normal/highlight pair, or another fixed UI pairing. 2. Finish the `0x31a2` gate pass as one batch: - classify the read sites at `0004:c24d`, `000c:ca11`, `000c:e4d8`, `000c:e546`, `000c:e5c6`, `000d:9304`, `000d:b6b1`, and `000d:c0ee`, - relate them back to interrupt-side updates at `0008:a283` / `0008:a314`, - and decide whether `0x31a2` is best described as user-acknowledge, queued-input depth, or a broader event-break gate. 3. Tighten the `DS:0x6341` to `0x6828` relationship: - compare the seg126 `animation_ctor_variant_a` call with the other raw callsites at `0005:3c4f`, `0005:3c74`, `000c:6176`, and `000c:619c`, - map who owns `g_active_dispatch_entry_farptr[+0x40]`, - and classify whether seg126 is constructing a transition-local animation payload for the shared active dispatch entry or only toggling an owner-side state bit after setup. 4. Identify which higher-level transition states own the seg127 fade-controller inputs at `0x630a-0x6316` and how that fade state is chosen from the seg005/seg126 startup path. 5. Repair the still-oversized overlap rooted at `000c:db68` only if it blocks follow-on analysis or decompiler visibility in the same transition lane. 6. Clarify the relationship between the seg049 watch/camera controller at `0x2bd8`, the seg108 sprite/object lane at `0x4f38`, and the object validated through `FUN_0004_60c0` vtable slot `+0x0c`. 7. Continue caller-role classification inside `entity_cleanup_resources_and_dispatch` (contains both `000d:9d5e` and `000d:a3b7`) and map how it relates to `FUN_000d_938c`, `FUN_0004_60c0`, `FUN_000c_7412`, `transition_preentry_release_resources`, and the seg136/seg137 active-dispatch helper family. 8. Keep the cheat/input side lane warm when it offers cheap wins: - identify the upstream producer for the five-byte cheat event-code sequence `50 80 3e fd 27`, - resolve the exact success-side presentation path behind `DS:0x287b` versus `DS:0x2892`, - finish naming the verified cheat-only actions now that plain `F10` is confirmed in `seg001_input_keyboard_handler`, - map the remaining caller-side hotkey bytes in `FUN_0007_04dc` (`0x37`, `0x4a`, `0x4e`, `0x52`, `0x53`, `0x0f`, `0x24`, `'9'`, `'R'`) to final user-facing controls, - verify whether the reported `H` / hack-mover description belongs to this build or to a higher translation layer, - and tie the cheat toggle flags `0x844` / `0x6045` into the wider input/event-dispatch system, especially the cheat-gated overlay events `0x141`, `0x142`, `0x143`, `0x241`, `0x410`, and `0x441`. 9. Revisit `allocator_phase_finalize_pass` only where it intersects the same callback object semantics, rather than broad allocator mechanics that are already sufficiently constrained. 10. Continue `ASYLUM.24` only after the `0x4588` / dispatch-entry lane and `0004:1e00` transition path have no further cheap wins. ### Headline Estimate - Overall useful decompilation progress: about 35% - Reasonable uncertainty band: about 30% to 40% This is the best single-number estimate for the full game right now. ### Supporting Metrics | Metric | Estimate | Meaning | |---|---:|---| | Top 100 far-call target coverage | about 80% | Roughly 80 of the top 100 most-called far-call targets have been named or materially classified | | Whole-program behavioral coverage | about 35% | Verified subsystem and function understanding across the executable | | Segment spread with meaningful analysis | about 19% to 25% | Segments with more than a trivial foothold or isolated note | | Tooling maturity for continued work | about 75% | Core repair, lookup, and fallback automation needed for continued progress | ### Why These Numbers Differ - The hot-target metric is much higher because the project has already focused on the most shared and most-called helpers. - The whole-program metric is lower because most of the 145 NE segments still have not had systematic coverage passes. - The segment-spread metric is lower still because only a subset of segments have coherent subsystem-level treatment. ## What Is Already In Place ### Workflow and Tooling - Raw full-EXE Ghidra target is established and in active use. - Verified raw-import mapping exists for seg001 and seg021. - NE relocation parsing has been implemented. - Internal literal far-call fixups have been applied to the raw import. - PyGhidra fallback tooling exists for create/delete function work and batch scripted edits. - Conservative boundary-repair workflow already exists and has been used successfully. - Notes are detailed enough to support a formal executable-wide tracker. ### Objective Milestones Already Reached - 145 NE segments identified from the internal NE header. - 8851 internal literal CALLF sites patched to real targets in the raw import. - 2841 non-CALLF far-pointer relocations identified and deferred. - 119 import callsites annotated. - Top 100 far-call target list processed through five tiers, with about 80 named or materially classified. ## Strongly Advanced Areas ### Core Gameplay and Entity Work - seg001 gameplay, cursor, entity lifecycle, projectile, combat, and AI footholds are strong. - A verified seg001 raw-port path is working and already used for multiple projectile helpers. - Entity table, class-table, and several global gameplay fields are partially mapped. ### Timer, Event, and State Systems - seg021 timer and event-dispatch work has meaningful coverage. - 000c state-dispatch, cursor-nav, UI-listbox, palette-fade, and mini-VM clusters have footholds. ### Rendering and Camera - 0007 rendering, draw-list, tile-visibility, and camera work has strong structural coverage. - `world_to_screen_coords` and adjacent geometric helpers are understood well enough to support further caller analysis. ### Dispatch and Pair-Sync Helpers - 0008 dispatch-entry helper families have multiple verified rename batches. - Pair-sync and target-state helper clusters are no longer isolated unknowns. ### Cache, Tracked Handles, and Bucket Logic - 000a cache manager layer is structurally mapped. - 000a tracked-handle table is structurally mapped. - 000d tracked bucket / proximity / visibility bucket logic has several meaningful behavioral names. - The client/cache distinction is much clearer than before. ### Parser and Animation Framework - 000e parser cluster has a stable set of verified names. - 000e animation framework has a real foothold: chunk lookup, audio load, tick, frame advance, and constructor variants are partly mapped. ### Local Repair Successes - seg043 overlap repair succeeded and recovered multiple valid function objects. - seg091 boundary recovery succeeded and exposed RNG helpers plus local init/context helpers. - Recent seg004 reset-path recovery and cache-reset follow-up added a new high-value analysis cluster. ## What Still Blocks Broader Coverage ### High-Value Classification Gaps - The object rooted at `0x4588` is still not classified well enough to safely rename the callback object itself beyond the current allocator-side glue names. - `ASYLUM.24` is only known as an import site, not yet a confidently identified routine. - Some structural names in the cache/backend/finalize cluster are waiting on object-role confirmation. ### Boundary and Decompiler Gaps - Some high-caller targets still require conservative boundary repair or follow-up validation. - Certain functions still decompile poorly because of overlaps, thunk-heavy paths, or unresolved downstream targets. - `000e:ffb0` remains a notable animation/video-side blocker because of overlapping instructions. ### Coverage Management Gap - A first-pass normalized segment-by-segment coverage ledger now exists for all 145 NE segments. - The remaining gap is refinement rather than absence: most segments still need manual promotion from `None` to `Foothold` / `Partial` / `Deep` as coverage expands. ### Deferred Data Work - Non-CALLF far-pointer relocations still exist and will matter for deeper object/table recovery. - They are no longer the main blocker, but they remain a real second-pass problem. ## Current Best Assessment Of Remaining Work The project has solved most of the architectural uncertainty needed to keep going efficiently. The remaining effort is mainly a scaling problem: - expand coverage across many more segments, - remove the last high-value boundary blockers, - convert structural names into subsystem names when evidence is strong enough, - and normalize progress tracking so the whole program can be managed deliberately. In practical terms, this looks like a true mid-project state rather than an early exploratory state or a late polish state. ## Implementation Priorities ### Priority 0: Coverage Ledger First pass completed: an executable-wide coverage ledger now exists for all 145 NE segments in `crusader_segment_coverage_ledger.csv`. Next work under Priority 0: 1. Promote additional segments from `None` where notes already support a verified foothold. 2. Normalize raw-address subsystem islands (notably the `000e:` parser/animation cluster) back onto exact NE segment rows. 3. Keep the ledger updated together with `crusader_decompilation_notes.md` after each verified batch. Minimum columns: | Column | Meaning | |---|---| | Segment | NE segment number | | Type | Code or data | | File offset | From the NE segment table | | Length | Segment length | | Coverage status | None, foothold, partial, deep | | Known subsystem | Best current classification | | Key named functions | Short summary only | | Blockers | Boundary, import, thunk, overlap, unknown object, etc. | | Notes source | Notes section or evidence anchor | This is the most important missing artifact because it will make the percentage estimates maintainable. ### Priority 1: Finish The New Cache/Backend Cluster Work the newest verified reset-path cluster to closure: 1. Trace more callers of `0009:b06b`. 2. Trace more callers of `FUN_0009_a961`. 3. Classify the object rooted at `0x4588`. 4. Revisit `allocator_phase_finalize_pass` once the object role is clearer. This is currently the best next analysis target because it closes a live cluster that already has fresh verified work around it. ### Priority 2: Resolve `ASYLUM.24` Identify what imported routine `ASYLUM.24` actually is. Goal: - tighten the description of `runtime_cache_reset_sequence`, - determine whether the import belongs to cache/resource/backend/media initialization, - and improve naming confidence around the reset path. ### Priority 3: Continue Small-Batch Boundary Repair Use the existing conservative repair approach for remaining high-value blockers. Good candidates include: - unresolved high-caller function objects, - ranges that still steal bytes from adjacent real bodies, - and overlaps that block decompilation of already-active subsystems. ### Priority 4: Finish Partial Subsystem Islands Before Expanding Broadly Recommended order: 1. seg043 plus connected seg004 reset and dispatch paths 2. 000e animation/video overlap at `000e:ffb0` 3. 000c UI-listbox, mini-VM, and cursor-nav families 4. Remaining structural 0007 and 0008 helper cohorts The goal is to reduce the number of half-understood islands before starting broad segment sweeps. ### Priority 5: Broaden Coverage Across The Remaining Executable Once the ledger exists and the current hot cluster is closed, broaden analysis segment by segment. Preferred method: 1. Group segments by adjacency and call relationships. 2. Identify entry points and hot callees first. 3. Classify globals and tables next. 4. Promote helper names only when supported by strong evidence. ## Recommended Tracking Model Use these status values for segment coverage: | Status | Meaning | |---|---| | None | No meaningful verified analysis yet | | Foothold | One or two verified entry points or helper names, but no subsystem picture | | Partial | Several verified names plus some globals/tables or object fields | | Deep | Coherent subsystem-level understanding with multiple verified related functions | Use these status values for subsystem maturity: | Status | Meaning | |---|---| | Unknown | Not enough evidence to classify | | Structural | Behavior is partly mapped but still generic | | Behavioral | Confident subsystem role is known | | Stable | Multiple connected functions and data objects support the classification | ## Suggested Immediate Work Queue ### Queue A: Highest Leverage 1. Expand the first-pass segment coverage ledger beyond the currently seeded segments. 2. Trace `allocator_try_alloc_from_head_table`, `allocator_head_finalize_sweep`, and `allocator_phase_finalize_pass`. 3. Identify `ASYLUM.24`. ### Queue B: Repair And Stabilize 1. Review remaining high-caller gap functions. 2. Repair any still-blocking overlaps in small batches. 3. Re-decompile repaired ranges and keep only evidence-backed names. ### Queue C: Broaden Carefully 1. Expand into adjacent segments connected to already-understood clusters. 2. Avoid speculative naming. 3. Update the notes and the coverage ledger together after each verified batch. ## Concrete Progress Interpretation If a single number is needed, use 25%. If a more honest dashboard is acceptable, use all three: - 80% of top-100 hot targets processed - 25% overall behavioral decompilation progress - 10% to 15% segment spread with meaningful analysis That combination best reflects the actual state of the project. ## Source Anchors Primary sources for this file: - `crusader_segment_coverage_ledger.csv` - `crusader_decompilation_notes.md` - `crusader_ne_segments.csv` - `tier4_output.txt` - `tier5_output.txt` - repo memory progress summary ## Next Update Rule Update this file when one of the following happens: - the overall estimate changes materially, - a new subsystem reaches behavioral or stable status, - a major blocker such as `0x4588`, `allocator_phase_finalize_pass`, or `ASYLUM.24` is resolved, - or the segment coverage ledger is created and becomes the new primary progress source.