From 5b6ffc0664620a1cb57bb3671bd0d6c5ddcd2fe0 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Sat, 13 Jun 2026 16:46:03 -0600 Subject: [PATCH] chore(phase-1): headless .gdt attach + MCP type-resolution test (ledger #2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tools/ghidra_scripts/ImportPsyqGdt.java: opens psyq400.gdt and resolve()s all 2599 PsyQ 4.0 types into the program DTM (205 -> 2609), registering psyq400 as a SourceArchive; saved to ghidra/bfm.rep - verified over MCP: types get DRAWENV -> full 92-byte struct (/LIBGPU.H), SVECTOR (/LIBGTE.H) -> the .gdt "attach" is doable headlessly, no GUI - docs/SETUP.md: ledger #2 RESOLVED; §2.5 step 5 answered (import types into the program; MCP type tools then resolve them) --- docs/SETUP.md | 4 +- phase-ends/CURRENT_PHASE.md | 3 +- tools/ghidra_scripts/ImportPsyqGdt.java | 59 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 tools/ghidra_scripts/ImportPsyqGdt.java diff --git a/docs/SETUP.md b/docs/SETUP.md index 84cbbae7e..91d04f123 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -124,7 +124,7 @@ cd ~ && unzip ghidra_12.1_PUBLIC_20260513.zip 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. +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. **RESOLVED 2026-06-13 (Phase 1) — answer: import the types into the program.** The `.gdt` attach is doable **headlessly** (no GUI needed) via `tools/ghidra_scripts/ImportPsyqGdt.java`, which opens `psyq400.gdt` as a `FileDataTypeManager` and `resolve()`s all its types into the program's DataTypeManager — this both copies the types in **and** registers `psyq400` as a SourceArchive. After that, the MCP `types` tool fully resolves them: `types action=list filter=DRAWENV` and `types action=get name=DRAWENV` return the complete struct (`DRAWENV` [/LIBGPU.H], 92 bytes, all fields incl. `RECT`/`DR_ENV`), and `SVECTOR` [/LIBGTE.H]. psyq400.gdt carries **2599 types** (program DTM went 205 → 2609). **Takeaway for the typing workflow:** don't rely on a bare archive *attachment* — import (resolve) the PsyQ types into the program once, and all MCP type/struct tools see them. The bundled archives live at `/Ghidra/Extensions/ghidra_psx_ldr/data/psyq*.gdt` (psyq400.gdt for our SDK). ### §2.6 `.mcp.json` wiring + verification @@ -452,7 +452,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 | **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 | +| 2 | MCP `types`/`struct` resolution of attached-archive (.gdt) types | **RESOLVED 2026-06-13** — import (`resolve()`) the .gdt types into the program headlessly (`tools/ghidra_scripts/ImportPsyqGdt.java`); MCP `types` then resolves them fully (§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) | | 5 | WSL distro is Ubuntu 24.04 (the single all-in-WSL host) | confirm via `/etc/os-release` (§4.1); username no longer load-bearing (no `wsl.exe --cd` targets) | diff --git a/phase-ends/CURRENT_PHASE.md b/phase-ends/CURRENT_PHASE.md index 6f949c994..13e9c1941 100644 --- a/phase-ends/CURRENT_PHASE.md +++ b/phase-ends/CURRENT_PHASE.md @@ -58,7 +58,8 @@ To formalize in `PhaseEnd_Phase1.md` under "Rules Added" (P10): the H1 relaxatio ### C. First import + milestone (in WSL) - [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] 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}`. +- [x] **`.gdt` attach + MCP type-resolution test — DONE HEADLESSLY (ledger #2 RESOLVED).** `tools/ghidra_scripts/ImportPsyqGdt.java` resolved all **2599 types** from `psyq400.gdt` into the program (DTM 205 → 2609) and registered `psyq400` as a SourceArchive; saved. MCP `types` then resolves them: `get DRAWENV` → full 92-byte struct (`/LIBGPU.H`), `SVECTOR` (`/LIBGTE.H`). Answer for the typing workflow: import (resolve) PsyQ types into the program; bare attachment isn't needed. - [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). diff --git a/tools/ghidra_scripts/ImportPsyqGdt.java b/tools/ghidra_scripts/ImportPsyqGdt.java new file mode 100644 index 000000000..2a8ed83bd --- /dev/null +++ b/tools/ghidra_scripts/ImportPsyqGdt.java @@ -0,0 +1,59 @@ +// ImportPsyqGdt.java — headless equivalent of the GUI ".gdt attach". +// Opens a PsyQ type archive (default psyq400.gdt) and resolves ALL its data +// types into the program's DataTypeManager. resolve() both copies the types in +// AND registers the .gdt as their SourceArchive, so the program shows psyq400 +// as an attached source archive and the MCP types/struct tools can reference +// the PsyQ structs/typedefs. Run via: -process SLUS_007.26 -noanalysis +// -postScript ImportPsyqGdt.java [/abs/path/to/psyqXXX.gdt] +import ghidra.app.script.GhidraScript; +import ghidra.program.model.data.DataType; +import ghidra.program.model.data.DataTypeConflictHandler; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.FileDataTypeManager; +import ghidra.program.model.data.SourceArchive; +import java.io.File; +import java.util.Iterator; + +public class ImportPsyqGdt extends GhidraScript { + @Override + public void run() throws Exception { + String gdt = "/home/musashi/ghidra_12.1_PUBLIC/Ghidra/Extensions/ghidra_psx_ldr/data/psyq400.gdt"; + String[] a = getScriptArgs(); + if (a.length > 0) gdt = a[0]; + File f = new File(gdt); + println("=== IMPORT PSYQ GDT ==="); + println("GDT: " + f.getAbsolutePath() + " exists=" + f.exists()); + if (!f.exists()) { println("ERROR: gdt not found"); return; } + + FileDataTypeManager fdtm = FileDataTypeManager.openFileArchive(f, false); + DataTypeManager dtm = currentProgram.getDataTypeManager(); + int before = dtm.getDataTypeCount(true); + println("Program DTM types before: " + before); + println("Archive DTM types: " + fdtm.getDataTypeCount(true)); + + int n = 0; + java.util.List sample = new java.util.ArrayList<>(); + int tx = currentProgram.startTransaction("import " + f.getName()); + try { + Iterator it = fdtm.getAllDataTypes(); + while (it.hasNext()) { + DataType dt = it.next(); + dtm.resolve(dt, DataTypeConflictHandler.DEFAULT_HANDLER); + if (n < 15) sample.add(dt.getName()); + n++; + } + } finally { + currentProgram.endTransaction(tx, true); + } + fdtm.close(); + + println("Resolved " + n + " types from " + f.getName()); + println("Program DTM types after: " + dtm.getDataTypeCount(true)); + println("Sample type names: " + sample); + println("Source archives now associated with the program:"); + for (SourceArchive sa : dtm.getSourceArchives()) { + println(" - " + sa.getName()); + } + println("=== END IMPORT PSYQ GDT ==="); + } +}