Crusader_Decomp/.github/instructions/ghidra.instructions.md
MaddoScientisto 3d4c4933ec Refactor Ghidra instructions, add new binary files, and enhance decompilation notes
- 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.
2026-03-21 21:43:33 +01:00

85 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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.
- 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.
- Keep `crusader_segment_coverage_ledger.csv` updated after each verified batch whenever a segment can be promoted or reclassified.
- Keep the progress section in `plan-mid.md` updated after each verified batch so the next pass can resume from the exact stopping point.
- Keep `ghidra_mcp_wishlist.md` updated whenever the workflow hits a missing MCP capability and has to fall back to PyGhidra or another local-only path.
- Each wishlist entry should be short and concrete: what MCP lacked, what command/script/tool had to replace it, and what a useful MCP endpoint or behavior would look like.
- Record raw-import addresses alongside original segment-relative offsets when porting names.
- **Always use `rename_function_by_address`** — `rename_function` (by name) fails with "must have required property 'old_name'" and is broken. Use `"function_address": "000c:XXXX"` format.
# PyGhidra Fallback
- Use the local PyGhidra toolkit in `tools/pyghidra_crusader` when MCP is missing an operation such as function creation, deletion, or batched scripted edits.
- When PyGhidra is needed because MCP lacks a required operation, append a note to `ghidra_mcp_wishlist.md` in the same batch if the gap is not already documented.
- The workspace-local Python environment for this toolkit is `.venv-pyghidra311`, created from `C:\Users\Maddo\.pyenv\pyenv-win\versions\3.11.6\python.exe` and installed from the bundled Ghidra 11.3.2 offline packages.
- Default install dir for the toolkit is `I:\Apps\ghidra_11.3.2_PUBLIC`.
- Invoke the toolkit with `\.venv-pyghidra311\Scripts\python.exe -m tools.pyghidra_crusader ...` from the repo root.
- Keep PyGhidra batches small too: prefer one focused repair plan or 1-5 direct edits at a time.
- Write operations require the Ghidra project to open successfully. If `Crusader.lock` is present because the GUI owns the project, close Ghidra first or operate on a project copy.
- If the workflow needs the user to change Ghidra state, use the ask-questions tool with a yes/no confirmation prompt instead of plain text. Ask the user to close Ghidra before PyGhidra write commands, and ask the user to open the Ghidra project before MCP server commands. The prompt should briefly describe exactly what to do and instruct the user to answer `Yes` only after the action is complete.
# 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)