Crusader_Decomp/.github/instructions/ghidra.instructions.md
2026-03-20 00:24:27 +01:00

3.9 KiB
Raw Blame History

applyTo
**

Crusader Ghidra Workflow

  • Active target is the raw full-EXE Ghidra program CRUSADER-RAW.EXE unless explicitly stated otherwise.
  • Use Ghidra MCP tools for analysis, decompilation, renaming, comments, and xref work.
  • Keep analysis batches small: prefer 1-5 functions, labels, or comments at a time.
  • Avoid speculative renames. Prefer names that are supported by one of these:
    • verified raw mapping from standalone segment work
    • direct string evidence
    • clear call/field behavior in decompiler or disassembly
    • xref relationships to already-named functions
  • When porting names from standalone segment extracts into CRUSADER-RAW.EXE, use only verified base mappings.

Verified Raw Mapping Rules

  • seg001 raw base = 0x6E570
  • seg021 raw base = 0x87170
  • Porting formula: raw_full_exe_flat = verified_segment_base + standalone_segment_relative_offset
  • seg001 and seg021 both contain a keyboard handler; keep the seg001 name as seg001_input_keyboard_handler to avoid collision.

Working Method

  • Prefer a single decompile call first.
  • If the decompiler collapses to thunk-heavy output, use one disassembly lookup to confirm the wrapper or parameter setup.
  • When decompile_function output is too large (>~50KB), the result is written to a temp JSON file that read_file returns as empty {}. Use disassemble_function instead — it returns inline assembly directly and is fully navigable for large functions.
  • Add a short decompiler comment when a rename is mapped from verified notes so the provenance stays visible in Ghidra.
  • Keep crusader_decompilation_notes.md updated after each verified batch.
  • Record raw-import addresses alongside original segment-relative offsets when porting names.
  • Always use rename_function_by_addressrename_function (by name) fails with "must have required property 'old_name'" and is broken. Use "function_address": "000c:XXXX" format.

Current Verified Raw-Import Ports

  • 0006:e5d0 = cursor_update_hover from seg001 0x0060
  • 0008:7377 = entity_count_by_type_a from seg021 0x0207
  • 0007:28ce = shot_entity_alloc from seg001 0x435e
  • 0007:2a19 = shot_entity_free from seg001 0x44a9
  • 0007:2bc9 = projectile_init_vector from seg001 0x4659
  • 0007:3001 = entity_fire_weapon from seg001 0x4a91
  • 0007:3088 = fire_weapon_from_cursor from seg001 0x4b18
  • 0007:30e8 = projectile_check_hit from seg001 0x4b78
  • 0007:319e = projectile_step_update from seg001 0x4c2e
  • 0007:3298 = projectile_trace_ray from seg001 0x4d28
  • 0007:371d = projectile_update_tick from seg001 0x51ad
  • 0007:4009 = projectile_apply_hit from seg001 0x5a99

Named 000e: Functions (direct analysis — not segment-ported)

Parser Cluster (000e:34xx38xx)

  • 000e:345e = record_table_init
  • 000e:34cc = record_table_destroy
  • 000e:35c6 = record_table_release_buffer
  • 000e:35ef = record_table_next_slot
  • 000e:3639 = record_table_parse_buffer
  • 000e:3798 = record_parser_read_line
  • 000e:38f8 = record_parser_find_marker

RIFF/Animation Cluster (000e:03xx2xxx)

  • 000e:2a28 = riff_find_chunk_by_type (RIFF LIST/RIFF walker; FourCC match at chunk+8)
  • 000e:2104 = animation_start (finds "movi" chunk, inits timing ring buffer, kicks advance)
  • 000e:12f4 = animation_advance_frame (fixed-point 0x1000 timer stepper, ring buffer update)
  • 000e:103f = animation_tick (guard wrapper — checks +0xd4 != -1, calls advance_frame)
  • 000e:06f7 = anim_load_audio_frame (checks "01wb" chunk tag 0x62773130, copies audio into ring buffer)

Constructor/Assert Helpers (000e:22xx29xx)

  • 000e:223d = assert_alive_sentinel (expects +0xd4 == -1; traps on mismatch)
  • 000e:2777 = animation_ctor_variant_a (alloc + init flags + chained init/assert/finalize)
  • 000e:2860 = animation_ctor_variant_b (variant A with extra +0x109 init)
  • 000e:2969 = animation_ctor_variant_c (default static flag profile +0x4c=0xd)