- Updated Ghidra instructions to emphasize keeping analysis batches small. - Added new binary files: `db.104.gbf`, `db.105.gbf`, and `db.27.gbf`. - Expanded decompilation notes for `cheat_code_check`, detailing its internal workings and verified cheat actions. - Revised segment coverage ledger to reflect new findings and promote segments from `Foothold` to `Partial`. - Enhanced `plan-mid.md` with updated estimates and focus areas for ongoing analysis.
23 KiB
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:
- How far along is the project?
- What is already solid?
- What still blocks broader decompilation?
- 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.csvexists 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 the0x4588path now has named lifecycle helpers (runtime_callback_object_init_once,runtime_callback_object_teardown_once). - The
0x4588blocker is tighter than before:000a:b988boundary repair now includes both callback sync callsites (000a:b9e5/000a:ba66) inside one real function body,000d:9d5e/000d:a3b7are confirmed insideentity_cleanup_resources_and_dispatch, and adjacent helpers are now clarified asallocator_head_finalize_sweep(0009:a961),video_bios_state_snapshot(000a:4a1f), andvideo_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), andsprite_redraw_global_if_active(000d:9231). This reducesentity_cleanup_resources_and_dispatchambiguity on file/surface/palette teardown paths. - The previously missing
000d:7e00function object is now recovered and namedentity_dispatch_entry_init_runtime_state, with paired destructorentity_dispatch_entry_release_runtime_stateat000d:8078. Adjacent missing helpers0003:a880and0003:b8e2were also recovered, with0003:b8e2promoted tofar_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 around0x3b72/0x3b73are documented with conservative comments while keeping speculative naming deferred. - Constructor-lane semantics tightened further:
entity_set_update_period_and_reschedule(0008:d27e) andpalette_buffer_alloc_copy_from_source(0009:7905) are now named, and both0x4588callback 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
0x8724and active head count at0x879c, and the old structural helpers at0009:b06b/0009:b1c3are now promoted toallocator_try_alloc_from_head_tableandallocator_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) throughentity_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
FootholdtoPartial: 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 sameentity_dispatch_entry_init_runtime_statelane. 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_60c0is 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 into0004:1e00; nearby seg136 helpers are now stabilized asactive_dispatch_entry_mark_enabled,active_dispatch_entry_mark_disabled, andactive_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 ofvga_palette_set_all_black,animation_ctor_variant_b,sprite_node_get_or_traverse, seg064 gate helpers, the0x2bd8vtable lane, and the0x4aa/0x7e22resource/object lane. The remaining work is naming the exact state label, not repairing the structure. - seg126 is now promoted from
FootholdtoPartial: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, andFUN_000c_834anow show a coherent pre-entry, guarded-entry, script/fade step, and post-transition control shell around the sameFUN_0004_1e00startup/display state. - seg127 is now promoted from
FootholdtoPartial:palette_fade_begin_full_up,palette_fade_begin_full_down,transition_palette_fade_begin,transition_palette_fade_tick,transition_palette_fade_out_step, andtransition_palette_fade_in_stepform 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, andwatch_entity_controller_dispatch_if_presentnow show that0x2bd8is a real type-stamped watch/camera controller object lane rather than only a raw watched-entity pointer, and that same controller is exercised fromFUN_0004_1e00. - seg108 is no longer blank:
sprite_object_clear_flag40_if_presentandsprite_object_set_flag40_if_presentnow anchor the0x4f38global sprite/object lane as a real state-bit-controlled object path used beside the same0x4588callback 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 neighboringtransition_palette_fade_tick/transition_palette_fade_begin/transition_palette_fade_out_step/transition_palette_fade_in_stepchain are now named against verified behavior. The latest semantic pass also tightened the two main open globals:0x8c5c/0x8c60are now best understood as a paired temporary text-renderer lane, while0x31a2behaves like an external input/event break gate maintained by queue/interrupt-side code. The remaining structural cleanup is the separate oversized overlap rooted at000c:db68, not the seg126 helper family. - Bonus cheat-lane cleanup is now visible in Ghidra too:
cheat_code_checkhas 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 sequence50 80 3e fd 27 00before toggling the cheat-state bytes and taking one of two local notification paths.
Current Focus
- Finish Priority 0 refinement by promoting more exact segment rows where notes already support a verified foothold.
- 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
- Keep classifying the seg126 pre-entry text-renderer lane around
transition_preentry_setup_resources,transition_preentry_step_script, andtransition_preentry_release_resources, especially by:- comparing more preset
0x10/0x11text-renderer callsites, - tracing who owns the rendered buffer loaded into
0x6301:0x6303, - mapping the control bytes
0x21/0x23/0x24/0x26/0x2a/0x40/0x5eto concrete display behavior, - and deciding whether the paired
0x8c5c/0x8c60lane is a title/body pair, normal/highlight pair, or another fixed UI pairing.
- comparing more preset
- Finish the
0x31a2gate pass as one batch:- classify the read sites at
0004:c24d,000c:ca11,000c:e4d8,000c:e546,000c:e5c6,000d:9304,000d:b6b1, and000d:c0ee, - relate them back to interrupt-side updates at
0008:a283/0008:a314, - and decide whether
0x31a2is best described as user-acknowledge, queued-input depth, or a broader event-break gate.
- classify the read sites at
- Tighten the
DS:0x6341to0x6828relationship:- compare the seg126
animation_ctor_variant_acall with the other raw callsites at0005:3c4f,0005:3c74,000c:6176, and000c: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.
- compare the seg126
- Identify which higher-level transition states own the seg127 fade-controller inputs at
0x630a-0x6316and how that fade state is chosen from the seg005/seg126 startup path. - Repair the still-oversized overlap rooted at
000c:db68only if it blocks follow-on analysis or decompiler visibility in the same transition lane. - Clarify the relationship between the seg049 watch/camera controller at
0x2bd8, the seg108 sprite/object lane at0x4f38, and the object validated throughFUN_0004_60c0vtable slot+0x0c. - Continue caller-role classification inside
entity_cleanup_resources_and_dispatch(contains both000d:9d5eand000d:a3b7) and map how it relates toFUN_000d_938c,FUN_0004_60c0,FUN_000c_7412,transition_preentry_release_resources, and the seg136/seg137 active-dispatch helper family. - 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:0x287bversusDS:0x2892, - finish naming the verified cheat-only actions now that plain
F10is confirmed inseg001_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/0x6045into the wider input/event-dispatch system, especially the cheat-gated overlay events0x141,0x142,0x143,0x241,0x410, and0x441.
- identify the upstream producer for the five-byte cheat event-code sequence
- Revisit
allocator_phase_finalize_passonly where it intersects the same callback object semantics, rather than broad allocator mechanics that are already sufficiently constrained. - Continue
ASYLUM.24only after the0x4588/ dispatch-entry lane and0004:1e00transition 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_coordsand 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
0x4588is still not classified well enough to safely rename the callback object itself beyond the current allocator-side glue names. ASYLUM.24is 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:ffb0remains 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
NonetoFoothold/Partial/Deepas 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:
- Promote additional segments from
Nonewhere notes already support a verified foothold. - Normalize raw-address subsystem islands (notably the
000e:parser/animation cluster) back onto exact NE segment rows. - Keep the ledger updated together with
crusader_decompilation_notes.mdafter 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:
- Trace more callers of
0009:b06b. - Trace more callers of
FUN_0009_a961. - Classify the object rooted at
0x4588. - Revisit
allocator_phase_finalize_passonce 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:
- seg043 plus connected seg004 reset and dispatch paths
- 000e animation/video overlap at
000e:ffb0 - 000c UI-listbox, mini-VM, and cursor-nav families
- 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:
- Group segments by adjacency and call relationships.
- Identify entry points and hot callees first.
- Classify globals and tables next.
- 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
- Expand the first-pass segment coverage ledger beyond the currently seeded segments.
- Trace
allocator_try_alloc_from_head_table,allocator_head_finalize_sweep, andallocator_phase_finalize_pass. - Identify
ASYLUM.24.
Queue B: Repair And Stabilize
- Review remaining high-caller gap functions.
- Repair any still-blocking overlaps in small batches.
- Re-decompile repaired ranges and keep only evidence-backed names.
Queue C: Broaden Carefully
- Expand into adjacent segments connected to already-understood clusters.
- Avoid speculative naming.
- 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.csvcrusader_decompilation_notes.mdcrusader_ne_segments.csvtier4_output.txttier5_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, orASYLUM.24is resolved, - or the segment coverage ledger is created and becomes the new primary progress source.