From 36a366ba3f98e0fdf61c0302fef7894d6213065c Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sat, 13 Jun 2026 15:52:37 -0600 Subject: [PATCH] chore(phase-1): install RE stack + headless import (PsyQ 4.0.0, MCP live) - WSL RE stack installed: JDK 21.0.11, Ghidra 12.1 PUBLIC, GhidrAssistMCP v2.8.0, ghidra_psx_ldr 2026.06.04 (extensions extracted into Ghidra/Extensions/ at version=12.1; exact-12.1 pin held, no version-lock) - headless import of extracted/SLUS_007.26: PSX loader auto-selected (PSX:LE:32:default), PsyQ Version = 4.0.0 recorded, 1726 functions, saved to ghidra/bfm.{gpr,rep} (gitignored) - GhidrAssistMCP headless server verified on 127.0.0.1:8080 (41 tools); get_code(0x80018730) returns the LZSS streaming decompressor (milestone substance, also confirmed via headless decompiler) - tools/ghidra_scripts: DumpProgramInfo.java, DecompileAt.java (helpers) - docs/SETUP.md: confirm PsyQ 4.0.0; 41 tools + async get_code/SSE notes; headless -loader rejected -> use auto-detect; extension-install path; ledger #1 moot, #12 resolved --- docs/SETUP.md | 13 ++++--- phase-ends/CURRENT_PHASE.md | 40 +++++++++++++------- tools/ghidra_scripts/DecompileAt.java | 45 +++++++++++++++++++++++ tools/ghidra_scripts/DumpProgramInfo.java | 29 +++++++++++++++ 4 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 tools/ghidra_scripts/DecompileAt.java create mode 100644 tools/ghidra_scripts/DumpProgramInfo.java diff --git a/docs/SETUP.md b/docs/SETUP.md index 031c9e7d3..84cbbae7e 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -121,7 +121,7 @@ cd ~ && unzip ghidra_12.1_PUBLIC_20260513.zip 1. Extract `SLUS_007.26` from the disc image (LBA 24, 0x65000 bytes, track 1 is MODE2/2352) and import into Ghidra. The loader auto-selects **"PSX Executables Loader"**, language `PSX:LE:32:default`, and builds the full PS1 memory map: RAM around the image at 0x80000000, scratchpad 0x1F800000, all IO/DMA/timer/CD/GPU/SPU register blocks, **and a synthetic GTEMAC segment at 0x20000000 automatically** — do NOT run the `CreateGteMacSegment` script (that is only for migrating legacy non-PSX projects). 2. Run auto-analysis with the **"PsyQ Signatures"** analyzer enabled (auto-enabled for PSX-language programs). Analyzer options: "Only first match", "Minimal signature entropy" (default 3.0 — can skip tiny low-entropy library stubs), "PsyQ Version if not found" (manual override). -3. Read the detected PsyQ version: **Edit → Options for Program → Program Information → "PsyQ Version"**. Expected: **4.0** (our own EXE scan found 12 genuine `Ps` stamps: 9× 4.0, one 4.0.1x on libnum 16, one 4.2 on libnum 0, one 4.2.1x on libnum 12 — i.e. PsyQ 4.0 libs + 4.2 library updates; see §5.1). If detection errors with `'psyq/xx' cannot be found`, append `.0` to the version field. Record the detected value in the phase log. +3. Read the detected PsyQ version: **Edit → Options for Program → Program Information → "PsyQ Version"**. Expected: **4.0** (our own EXE scan found 12 genuine `Ps` stamps: 9× 4.0, one 4.0.1x on libnum 16, one 4.2 on libnum 0, one 4.2.1x on libnum 12 — i.e. PsyQ 4.0 libs + 4.2 library updates; see §5.1). If detection errors with `'psyq/xx' cannot be found`, append `.0` to the version field. Record the detected value in the phase log. **CONFIRMED 2026-06-13 (Phase 1):** DetectPsyQ at headless import recorded `PsyQ Version = 4.0.0` on the extracted US EXE (resolves ledger #12). Import also reported: loader `PSX Executables Loader`, language `PSX:LE:32:default`, ImageBase `80000000`, address range `1f800000–801fffff`, 1726 functions, ~177 s analysis. *(Note: a raw track-1 scan during research reported slightly different per-libnum details — raw 2352-byte-sector scans produce false positives; the extracted-EXE scan is the ground truth, and DetectPsyQ at import is the final word.)* 4. **One-time manual `.gdt` attach (GUI only — no MCP tool opens archives):** in the CodeBrowser Data Type Manager, attach the bundled PsyQ type archive for the detected version — `psyq400.gdt` (`psyq420.gdt` also exists). This gives PsyQ struct/typedef types for retyping work. 5. **Early MCP type-resolution test (run before any bulk typing):** via MCP, run the `types` tool with `action=set` applying a PsyQ type (e.g. apply a known PsyQ struct at some address) and confirm it resolves. **UNVERIFIED** whether the `types`/`struct` tools can reference types living in the attached archive or only types already copied into the program's own data type manager — this 5-minute test decides the typing workflow. Record the answer here when known. @@ -143,7 +143,7 @@ The committed repo-root `.mcp.json` (already present): Single `ghidra` entry only — do NOT copy psxrecomp's duplicated `ghidra` + `ghidra_psx` pair (same URL twice = every tool duplicated in context). -Verify in Claude Code with `/mcp`: **~38 `mcp__ghidra__*` tools** should appear. These are the **v2.8.0 names** — e.g. `get_binary_info`, `get_code` (format: disassembly|decompiler), `disassemble_at`, `analyze_function`, `xrefs`, `get_functions` (paginated), `struct` (actions: create/modify/merge/set_field/name_gap/auto_create/rename_field/field_xrefs), `types` (list/get_info/set/delete), `variables` (list/rename/set_type/set_prototype), `rename_symbol`, `batch_rename`, `create_data_var`, `create_function`, `search_bytes`, `patch_bytes`, `assemble_code`, `export_program`. **NOT the pre-2.4.0 names** (`get_function_info`, `list_data`) that appear in psxrecomp's PLAN.md and older write-ups — those were renamed in v2.4.0 (upstream commit `aa3ffc7d`, 2026-03-14). +Verify in Claude Code with `/mcp`: **~38–41 `mcp__ghidra__*` tools** should appear. *(CONFIRMED 2026-06-13: GhidrAssistMCP v2.8.0 reports **41 tools** headless. Operational notes from Phase 1: tool responses come back as SSE frames (`event: message` / `data: {json}`) on the streamable `/mcp` endpoint; heavy tools like `get_code` run **asynchronously** — the `tools/call` returns a `task_id` and you must poll `get_task_status` for the result. `get_code` takes `{function: "0x80018730", format: "decompiler|disassembly|pcode"}`.)* These are the **v2.8.0 names** — e.g. `get_binary_info`, `get_code` (format: disassembly|decompiler), `disassemble_at`, `analyze_function`, `xrefs`, `get_functions` (paginated), `struct` (actions: create/modify/merge/set_field/name_gap/auto_create/rename_field/field_xrefs), `types` (list/get_info/set/delete), `variables` (list/rename/set_type/set_prototype), `rename_symbol`, `batch_rename`, `create_data_var`, `create_function`, `search_bytes`, `patch_bytes`, `assemble_code`, `export_program`. **NOT the pre-2.4.0 names** (`get_function_info`, `list_data`) that appear in psxrecomp's PLAN.md and older write-ups — those were renamed in v2.4.0 (upstream commit `aa3ffc7d`, 2026-03-14). Operational cautions: - Tools operate on the program currently open in CodeBrowser and fail (sometimes silently) if none is open. @@ -161,7 +161,10 @@ For unattended batch passes, GhidrAssistMCP runs headless (supported since v2.3. ``` - **`wait=true` is mandatory** — without it the analyzeHeadless process exits right after the prescript instead of serving MCP clients. -- On subsequent runs use **`-process SLUS_007.26`** (not `-import`) to reuse the existing project. +- On subsequent runs use **`-process SLUS_007.26`** (not `-import`) to reuse the existing project. Pair with **`-noanalysis`** so it doesn't re-run analysis on every server start. +- **CONFIRMED 2026-06-13:** this headless flow works end-to-end (server "started on port 8080 … with 41 tools"; `get_binary_info` and `get_code` operate on the `-process` program). **The headless server holds the project `.rep` lock while serving** — stop it (cancel the analyzeHeadless process) before opening the same project in the GUI. +- **Extension install (headless-compatible):** extract each extension zip into `/Ghidra/Extensions/` (e.g. `unzip ext.zip -d ~/ghidra_12.1_PUBLIC/Ghidra/Extensions/`). Both GUI and `analyzeHeadless` then load the extracted module dirs (no GUI "Install Extensions" step needed). Verified for GhidrAssistMCP + ghidra_psx_ldr on Ghidra 12.1. +- **Loader selection in headless:** `-loader "PSX Executables Loader"` is **rejected** (`InvalidInputException: Invalid loader name specified`) even though that is the loader's display name. **Omit `-loader` and let auto-detection pick** — for a real `PS-X EXE` it correctly selects "PSX Executables Loader" over Raw Binary (log line: `Using Loader: PSX Executables Loader`). --- @@ -448,7 +451,7 @@ A Track-1 match proves the dump is the canonical redump dump, which transitively | # | Item | Status | |---|---|---| -| 1 | GhidrAssistMCP/psx_ldr 12.1 zips on Ghidra 12.1.2 | **UNVERIFIED** — pin 12.1 (§2.2) | +| 1 | GhidrAssistMCP/psx_ldr 12.1 zips on Ghidra 12.1.2 | **MOOT for us** — pinned Ghidra **12.1** exactly; both extensions load fine at `version=12.1` (confirmed 2026-06-13). The 12.1.2 question stays untested by design. | | 2 | MCP `types`/`struct` resolution of attached-archive (.gdt) types | **UNVERIFIED** — early test, §2.5 step 5 | | 3 | GhidrAssistMCP struct-tool ergonomics under matching-decomp load | **UNPROVEN** — psxrecomp never exercised heavy struct creation | | 4 | PCSX-Redux web-server port config field (8080 collision) | **TBD** on install (§3) | @@ -459,6 +462,6 @@ A Track-1 match proves the dump is the canonical redump dump, which transitively | 9 | ASPSX tier for game code: 2.56 vs 2.67 | **OPEN** — Phase 6 empirical (§5.2 tell) | | 10 | Cross-OS networking | **N/A under all-in-WSL** — MCP is local loopback (§4.2); no mirrored mode, firewall rule, or host-IP discovery | | 11 | Canonical git remote URL (off-box push/pull backup) | **TBD** (§4.3) | -| 12 | Per-libnum stamp detail (raw-track scan reported 16 hits vs 12 genuine in extracted EXE — extracted-EXE scan is ground truth, see §5.1) | re-confirm via DetectPsyQ at import (§2.5 step 3) | +| 12 | Per-libnum stamp detail (raw-track scan reported 16 hits vs 12 genuine in extracted EXE — extracted-EXE scan is ground truth, see §5.1) | **RESOLVED 2026-06-13** — DetectPsyQ at headless import recorded `PsyQ Version = 4.0.0` (§2.5 step 3) | | 13 | Overlay load addresses (resident 0x800CDF58 / location 0x80128508, EXE ptr table ~0x62620) | **JP-only — re-derive for US** (owned by docs/memory-map.md) | | 14 | Greenfield claim: decomp.me scratch search is script-blocked (Cloudflare) | **TBD** — one-time manual browser check for BFM scratches | diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 847c2a006..6f949c994 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -48,25 +48,39 @@ To formalize in `PhaseEnd_Phase1.md` under "Rules Added" (P10): the H1 relaxatio - [x] User `musashi` created (uid 1000): passwordless sudo via `/etc/sudoers.d/90-musashi` (visudo-validated), set as default login user via `/etc/wsl.conf` `[user] default=musashi` - [x] Git identity set global: `user.name=Drew T`, `user.email=50529377+Druthulu@users.noreply.github.com`, `init.defaultBranch=main`; `gh` 2.45.0 + `unzip` 6.0 installed - [x] Claude Code 2.1.177 installed (native installer → `~/.local/bin/claude`; `~/.local/bin` appended to `~/.bashrc` PATH; `claude --version` resolves in a fresh interactive shell) -- [ ] **USER — interactive: run `claude` in a WSL terminal → sign into Anthropic** (OAuth code/paste flow). Git needs no WSL auth — push/pull is GitHub Desktop on Windows (see correction #6). +- [x] **USER — sign into Anthropic in WSL** — DONE: this session IS Claude Code running signed-in inside the WSL clone. Git needs no WSL auth — push/pull is GitHub Desktop on Windows (correction #6). **B1. Establish the clone + sync:** - [x] Seeded `~/bfm-decomp` (ext4, `/dev/sdd`) from the **local Windows repo** (`git clone /mnt/z/Storage/git/BFM-decomp ~/bfm-decomp`; HEAD up to date with this log). `origin` re-pointed to `https://github.com/Druthulu/BFM-decomp.git`; `core.filemode=false` set. **First push is via GitHub Desktop on Windows** (Add Local Repository → `\\wsl.localhost\Ubuntu-24.04\home\musashi\bfm-decomp` → Push), NOT `git push` from WSL. -- [ ] Make the dump available in `disks/` (one-shot copy onto ext4 — the dump is not in the repo) -- [ ] Relaunch Claude Code inside WSL (in `~/bfm-decomp`); this Windows session ends -- [ ] Install RE stack in WSL (JDK 21, Ghidra 12.1, ghidra_psx_ldr, GhidrAssistMCP); start MCP server +- [ ] Make the dump available in `disks/` (one-shot copy onto ext4) — **NOT needed for the milestone**: the EXE is already extracted + committed + sha1-verified (`143dbb89…`). Only needed to re-run `--verify-disc` (Track-1 vs redump) inside WSL. +- [x] Relaunch Claude Code inside WSL — DONE (this session runs in `~/bfm-decomp` on ext4 `/dev/sdd`) +- [x] Install RE stack in WSL — **DONE 2026-06-13** (this session): JDK 21.0.11; Ghidra 12.1 PUBLIC at `~/ghidra_12.1_PUBLIC` (zip integrity-verified, 6693 files); both extensions extracted into `~/ghidra_12.1_PUBLIC/Ghidra/Extensions/` at `version=12.1` (GhidrAssistMCP v2.8.0, ghidra_psx_ldr 2026.06.04); headless GhidrAssistMCP server starts on 127.0.0.1:8080 with **41 tools**. ### C. First import + milestone (in WSL) -- [ ] Run extractor → `extracted/SLUS_007.26`; `--verify-disc` PASS (Track-1 == redump) -- [ ] Import into Ghidra (WSLg): PSX loader, auto-analyze + PsyQ signatures, record PsyQ version, attach `psyq400.gdt`, MCP type-resolution test -- [ ] MCP round-trip: `/mcp` ~38 tools; `get_binary_info`; decompile `0x80018730` -- [ ] **MILESTONE:** MCP decompile of `0x80018730` ≈ LZSS decompressor; PsyQ version recorded → user confirm → `PhaseEnd_Phase1.md` +- [x] EXE present at `extracted/SLUS_007.26` (sha1 `143dbb89f34491258bbc27810d0a12ec8b43a8dd`, 413,696 bytes, "PS-X EXE" magic). `--verify-disc` re-run in WSL is **pending the dump copy** (it PASSED on Windows: Track-1 == redump `b44f0f0a…`). +- [x] Imported into Ghidra (**headless** `analyzeHeadless`, not WSLg GUI): PSX loader auto-selected (`PSX:LE:32:default`), auto-analysis + PsyQ Signatures ran (177 s), **PsyQ Version = 4.0.0 recorded** (DetectPsyQ), 1726 functions, program saved to `ghidra/bfm.{gpr,rep}`. **PENDING (GUI-only):** `.gdt` attach (`psyq400.gdt`) + MCP type-resolution test (ledger #2) — no headless/MCP path opens a `.gdt` archive. +- [x] MCP round-trip **verified via raw JSON-RPC over the live server** (curl): `tools/list` = 41 tools; `get_binary_info` → SLUS_007.26 / 1726 funcs; `get_code(0x80018730, decompiler)` → LZSS code. **PENDING:** the *literal* in-Claude-Code-session round-trip needs a `/mcp` reconnect (server came up after this session started, so `mcp__ghidra__*` aren't live in-session yet). +- [~] **MILESTONE — substance proven, gate pending user.** `0x80018730` decompiles to the documented LZSS streaming decompressor (scratchpad ring @ `0x1F800000`, 0x3ff window mask, resumable state machine via `DAT_800c7d24`, `pos==0` terminator), confirmed via BOTH Ghidra headless decompiler AND the MCP `get_code` tool; PsyQ 4.0.0 recorded. Awaiting: user confirmation (P8 gate), and a decision on the `/mcp` reconnect + GUI `.gdt` step (see Next task). ## Next task -**Section B — migrate to WSL (bootstrap + clone done; sign-in + relaunch next).** The WSL2 environment is up and `~/bfm-decomp` is cloned on ext4 with `origin`→GitHub (B0/B1). Remaining: the user signs into Anthropic by running `claude` in a WSL terminal (git needs no WSL auth — GitHub Desktop on Windows pushes, correction #6); push the current commits once via GitHub Desktop; copy the dump into `disks/`; relaunch Claude Code inside `~/bfm-decomp`; then install the RE stack per docs/SETUP.md §2. Resume from this log inside the WSL clone. +**Close Phase 1 (milestone gate).** The RE stack is installed, the EXE is imported+analyzed (PsyQ 4.0.0, 1726 funcs), and the LZSS decompile at `0x80018730` is proven via both the headless decompiler and the live MCP `get_code` tool. Two touches remain before the gate, and they hinge on a user choice (the headless MCP server currently holds the `bfm.rep` project lock, so the GUI can't open it until that server is stopped): + +- **(literal milestone) `/mcp` reconnect** — run `/mcp` in this session so the `ghidra` server connects (it's listening now); then a real `mcp__ghidra__*` decompile of `0x80018730` closes the milestone wording exactly. *(Keep the headless server running for this.)* +- **(checklist item, GUI-only) `.gdt` attach + MCP type-resolution test (ledger #2)** — needs the Ghidra GUI under WSLg: stop the headless server, launch `ghidraRun`, open `bfm`, attach `psyq400.gdt`, confirm the GhidrAssistMCP panel on 8080, then `/mcp` reconnect and run the type-resolution test. This GUI session also becomes the documented steady-state (SETUP.md §7). + +Recommendation queued for the user (see chat): do the one GUI session — it delivers the literal in-session MCP round-trip AND the `.gdt`/type test in one go, fully clearing the Phase 1 checklist. Alternatively, defer `.gdt` (record a deviation) and close on the headless-proven milestone now. ## Blockers / needs-user -- **Anthropic sign-in (only the user can do this):** run `claude` in a WSL terminal → sign in (OAuth code/paste flow). Git needs no WSL auth. -- **GitHub Desktop (Windows):** Add Local Repository → `\\wsl.localhost\Ubuntu-24.04\home\musashi\bfm-decomp` → Push to sync. If it warns "unsafe / dubious ownership," accept its one-click fix (Git-for-Windows `safe.directory`). -- Confirm the GitHub repo `Druthulu/BFM-decomp` is set to **Private** before any push. -- RE-stack install + EXE import (Section C) require driving Ghidra's WSLg GUI; do them from the in-WSL Claude session. +- **Milestone gate (P8):** user confirms the milestone (LZSS decompile + PsyQ version) before `PhaseEnd_Phase1.md` is written. +- **`/mcp` reconnect** (trivial, in this session) for the literal Claude-Code MCP round-trip — server is live on 127.0.0.1:8080. +- **`.gdt` attach + type-resolution test** — GUI-only; requires stopping the headless MCP server (it locks `bfm.rep`) and driving `ghidraRun` under WSLg. +- **GitHub Desktop (Windows):** Add Local Repository → `\\wsl.localhost\Ubuntu-24.04\home\musashi\bfm-decomp` → Push. If it warns "unsafe / dubious ownership," accept its one-click fix. +- Confirm the GitHub repo `Druthulu/BFM-decomp` is **Private** before any push. +- (optional) Copy the disc dump into `disks/` to re-run `--verify-disc` in WSL — not required for the milestone. + +## WSL session log — 2026-06-13 (RE stack live + headless import + milestone substance) +- **Installs:** JDK 21.0.11 (apt). Ghidra 12.1 PUBLIC (`ghidra_12.1_PUBLIC_20260513.zip`, 567 MB, central-dir verified) → `~/ghidra_12.1_PUBLIC`. Extensions extracted into `Ghidra/Extensions/`: GhidrAssistMCP v2.8.0 (`…20260530…`), ghidra_psx_ldr 2026.06.04 (`…20260604…`), both `version=12.1` (no version-lock issue — exact 12.1 pin held). +- **Import:** `analyzeHeadless ~/bfm-decomp/ghidra bfm -import extracted/SLUS_007.26` (auto-detect loader). Loader = **PSX Executables Loader**, lang `PSX:LE:32:default`, ImageBase `80000000`, Min/Max addr `1f800000`/`801fffff`, **1726 functions**, analysis 177 s, **PsyQ Version = 4.0.0** (resolves ledger #12 / confirms §2.5 step 3). Saved to `ghidra/bfm.{gpr,rep}` (gitignored). +- **MCP:** headless server (`-process SLUS_007.26 -preScript GAMCPStartServerScript.java host=127.0.0.1 port=8080 wait=true`) → "started on port 8080 … with **41 tools**". JSON-RPC over `/mcp` verified: `initialize`/`tools/list`/`get_binary_info` ok; `get_code(0x80018730, decompiler)` returns the LZSS decompressor. **Note:** `get_code` (and likely other heavy tools) run **async** — return a `task_id`; poll `get_task_status`. Wire format is SSE (`event: message` / `data: {json}`). +- **SETUP.md corrections recorded** (see commit): headless `-loader "PSX Executables Loader"` is rejected (`InvalidInputException`) — use loader auto-detect; tool count is 41 (not ~38); async task pattern for `get_code`; extensions install by extracting into `/Ghidra/Extensions/`. +- **Helper scripts added:** `tools/ghidra_scripts/DumpProgramInfo.java`, `DecompileAt.java`. diff --git a/tools/ghidra_scripts/DecompileAt.java b/tools/ghidra_scripts/DecompileAt.java new file mode 100644 index 000000000..f6d69abf9 --- /dev/null +++ b/tools/ghidra_scripts/DecompileAt.java @@ -0,0 +1,45 @@ +// DecompileAt.java — headless postScript: decompile the function at a given +// address (default 0x80018730, the LZSS streaming decompressor) and print the C. +// Used to verify Phase 1's milestone substance (recognizable LZSS code) directly +// from Ghidra's decompiler. Usage: -postScript DecompileAt.java [hexaddr] +import ghidra.app.script.GhidraScript; +import ghidra.app.decompiler.DecompInterface; +import ghidra.app.decompiler.DecompileResults; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Instruction; + +public class DecompileAt extends GhidraScript { + @Override + public void run() throws Exception { + String addrStr = "80018730"; + String[] a = getScriptArgs(); + if (a.length > 0) addrStr = a[0]; + Address addr = toAddr(addrStr); + println("=== DECOMPILE @ 0x" + addrStr + " ==="); + Function f = getFunctionContaining(addr); + if (f == null) f = getFunctionAt(addr); + if (f == null) { + println("NO FUNCTION at/containing 0x" + addrStr + ". Raw instructions follow:"); + Instruction ins = getInstructionAt(addr); + for (int i = 0; i < 24 && ins != null; i++) { + println(String.format(" %s %s", ins.getAddress(), ins.toString())); + ins = ins.getNext(); + } + println("=== END DECOMPILE ==="); + return; + } + println("Function: " + f.getName() + " @ " + f.getEntryPoint() + + " bytes=" + f.getBody().getNumAddresses()); + DecompInterface di = new DecompInterface(); + di.openProgram(currentProgram); + DecompileResults res = di.decompileFunction(f, 90, monitor); + if (res != null && res.decompileCompleted()) { + println(res.getDecompiledFunction().getC()); + } else { + println("DECOMPILE FAILED: " + (res != null ? res.getErrorMessage() : "null result")); + } + di.dispose(); + println("=== END DECOMPILE ==="); + } +} diff --git a/tools/ghidra_scripts/DumpProgramInfo.java b/tools/ghidra_scripts/DumpProgramInfo.java new file mode 100644 index 000000000..020bc794d --- /dev/null +++ b/tools/ghidra_scripts/DumpProgramInfo.java @@ -0,0 +1,29 @@ +// DumpProgramInfo.java — headless postScript that prints the authoritative +// program metadata after import+analysis: language/compiler spec, image base, +// function count, and every Program-Information property (incl. the +// ghidra_psx_ldr "PsyQ Version" that Phase 1's milestone requires recorded). +// Plain Java GhidraScript (compiled at runtime by Ghidra; no Jython needed). +import ghidra.app.script.GhidraScript; +import ghidra.framework.options.Options; +import ghidra.program.model.listing.Program; + +public class DumpProgramInfo extends GhidraScript { + @Override + public void run() throws Exception { + Program p = currentProgram; + println("=== BFM PROGRAM INFO ==="); + println("Name: " + p.getName()); + println("LanguageID: " + p.getLanguageID()); + println("CompilerSpecID: " + p.getCompilerSpec().getCompilerSpecID()); + println("ImageBase: " + p.getImageBase()); + println("ExecutableFormat: " + p.getExecutableFormat()); + println("FunctionCount: " + p.getFunctionManager().getFunctionCount()); + println("DefinedDataCount: " + p.getListing().getDefinedData(true).hasNext()); + Options opts = p.getOptions(Program.PROGRAM_INFO); + println("--- Program Information properties ---"); + for (String n : opts.getOptionNames()) { + println("PROP " + n + " = " + opts.getValueAsString(n)); + } + println("=== END BFM PROGRAM INFO ==="); + } +}