Files
BFM-decomp/tools/ghidra_scripts/ApplySymbols.java
T
Drew T a7394f44dc feat(phase-31): S78 #12 — the 800c3 "wall" band is LIBPAD 4.2.1 + LIBAPI 4.2: 46 names applied; integrate wired by subseg range; renames via ApplySymbols
- provenance: the psx loader's per-version PsyQ signature sets place PADENTRY/PADCMD/PADPORTD/
  PADSEQD (4.2), WAITRC2 (4.3), COUNTER/C114/FIRST/PAD/PATCH/CHCLRPAD (libapi 4.2) byte-exact in
  0x8005CE48-0x8005FC68 / 800c2 -> 12 of main's 29 stubs incl. all four §332 walls are Sony's
  DualShock library in reorder mode. 46 names -> symbols.us.txt (count 1081), band TUs, verbatim
  manifest, wave_exclude; firstfile/firstfile2 (4.2 naming); CdGetToc @0x800430B8 (was the Phase-21
  xdedup mislabel DecDCToutCallback). SETUP §5.1 corrected; psyq-worklist S78; cookbook §487;
  decision-log + accelerators S78; CHECKSUMS +Psy-Q_46.zip +PSYQ_SDevTC_v4.5.zip.
- psyq_integrate: --yaml maps stub<->objects by SUBSEG RANGE with an exact-tiling check and PRINTS
  the located-but-unwired residue (libgte: 13 objs / 1,264 ins) — main's LINKED build had been RED
  at HEAD since the S77 psyq_identify fix (22 libgte blocks merged to 3; gate worktrees take the
  stub fallback so it never showed); a library object's exported symbol whose recovered address the
  curated file names differently is --redefine-sym'd (R15; A66 firstfile->firstfile2).
- Ghidra: 47 MCP renames did NOT persist through the sentinel stop (R9 caught it) -> NEW
  tools/ghidra_scripts/ApplySymbols.java + tools/ghidra_apply_symbols.sh mirror the curated file
  headless with a real save: 73 renamed, R9-verified x4. SETUP inventory rows (R21).
- lint_symbol_refs: scans verbatim __asm__ bodies (`.ent\tfunc_X` is invisible to \b and to the
  string-masked scan); negative-controlled (red on the pre-fix TUs, green on the passing tree).
- R22: clean extract-all 212/212 + check-all green on the final config; main rebuilt byte-identical
  143dbb89 after the last src-only fix -> 213/213; tools-health OK.
2026-09-04 15:57:06 -06:00

84 lines
4.7 KiB
Java

// ApplySymbols.java — MIRROR the curated symbol file INTO Ghidra (rule R15 / G6), headless.
//
// WHY (P31 S78): 47 renames made through the headless MCP server (batch_rename / rename_symbol)
// did NOT survive the sentinel stop — "Save succeeded", the DB grew, the names were gone (R9 caught
// it). The curated text file `config/symbols.us.txt` is the source of truth anyway (R15), so the
// mirror is now a deterministic headless postScript: read `name = 0xADDR; // func|data`, and for every
// address inside program memory set the function's name (or create/primary a label) to the curated
// name. analyzeHeadless commits + saves on exit (the DefineFunctions.java precedent). Idempotent.
//
// Name collisions: a curated name currently held by ANOTHER address is moved first — to that
// address's own curated name when it has one (firstfile/firstfile2 4.0-vs-4.2), else to
// `<name>__at_<addr>` so nothing is silently dropped. Prints BFMAPPLY counts; verify with
// tools/ghidra_mcp_verify.sh <addr> <name> (R9).
//
// Args: <symbols file> [<symbols file> ...] (paths relative to the CWD analyzeHeadless ran from)
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.DuplicateNameException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.*;
public class ApplySymbols extends GhidraScript {
private final Map<Long, String> want = new TreeMap<>();
private int renamedFn = 0, labels = 0, unchanged = 0, skipped = 0, failed = 0, moved = 0;
@Override
protected void run() throws Exception {
String[] args = getScriptArgs();
if (args == null || args.length < 1) { println("BFMAPPLY ERROR: no symbols file given"); return; }
Pattern p = Pattern.compile("^\\s*([A-Za-z_]\\w*)\\s*=\\s*0x([0-9A-Fa-f]+)\\s*;");
for (String path : args) {
for (String ln : Files.readAllLines(Paths.get(path))) {
Matcher m = p.matcher(ln);
if (m.find()) want.put(Long.parseLong(m.group(2), 16), m.group(1));
}
}
for (Map.Entry<Long, String> e : want.entrySet()) apply(e.getKey(), e.getValue(), 0);
println("BFMAPPLY renamed_funcs=" + renamedFn + " labels=" + labels + " unchanged=" + unchanged
+ " moved_holders=" + moved + " skipped_outside_memory=" + skipped + " failed=" + failed
+ " wanted=" + want.size());
}
private Address addrOf(long off) {
return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(off);
}
private void apply(long off, String name, int depth) {
Address a = addrOf(off);
if (!currentProgram.getMemory().contains(a)) { skipped++; return; }
SymbolTable st = currentProgram.getSymbolTable();
Function f = currentProgram.getFunctionManager().getFunctionAt(a);
Symbol cur = f != null ? f.getSymbol() : st.getPrimarySymbol(a);
if (cur != null && name.equals(cur.getName())) { unchanged++; return; }
try {
if (f != null) { f.setName(name, SourceType.USER_DEFINED); renamedFn++; }
else { st.createLabel(a, name, SourceType.USER_DEFINED).setPrimary(); labels++; }
} catch (DuplicateNameException dup) {
if (depth > 3) { failed++; println(" fail @0x" + Long.toHexString(off) + " " + name + ": " + dup.getMessage()); return; }
// the name is held elsewhere: move the holder out of the way, then retry
for (Symbol h : st.getSymbols(name)) {
long hoff = h.getAddress().getOffset();
if (hoff == off) continue;
String hwant = want.get(hoff);
String to = (hwant != null && !hwant.equals(name)) ? hwant : name + "__at_" + Long.toHexString(hoff);
try {
Function hf = currentProgram.getFunctionManager().getFunctionAt(h.getAddress());
if (hf != null) hf.setName(to, SourceType.USER_DEFINED); else h.setName(to, SourceType.USER_DEFINED);
moved++;
println(" moved holder of '" + name + "' @0x" + Long.toHexString(hoff) + " -> " + to);
} catch (Exception ex) { failed++; println(" fail moving holder @0x" + Long.toHexString(hoff) + ": " + ex.getMessage()); return; }
}
apply(off, name, depth + 1);
} catch (Exception ex) {
failed++; println(" fail @0x" + Long.toHexString(off) + " " + name + ": " + ex.getMessage());
}
}
}