Files
BFM-decomp/tools/ghidra_scripts/ImportOverlay.java
T
Drew T bd5e21b366 feat(phase-3): file-loader & overlay-map RE + PCSX-Redux runtime oracle
- RE'd the full file-loader/overlay subsystem in Ghidra via MCP: CdReadRequest ->
  CdReadStateMachine (hand-rolled CdControl SeekL/ReadN reader, NOT PsyQ CdRead);
  LIST.CD RAM cache cdFileLocTable + ResourceGetCdLoc/resourceIdMap lookup; LZSS
  staging buffer 0x80079A70; loadDestPtrTable; ~50 symbols named (T1-T5,T8)
- T6b MILESTONE: resident blob @0x800CEDF8 + location overlay @0x80128158 proven
  byte-identical (sha1-equal contiguous prefixes) vs live PCSX-Redux RAM; blobs
  imported into Ghidra (ImportOverlay.java); overlay-map table -> memory-map.md §4.3
- PCSX-Redux runtime oracle: Windows-native build, WSL reads RAM via the web API
  at 172.17.208.1:8081; ledger #4 resolved
- live-verified player/time state: gold(/10) HP BP day hour tiredness boss-HP;
  save-header template (Q#5) + debug-menu dispatch (Q#9: DEBUG.BIN path is dead)
- overlay map: 13 area-states across SC01/02/03, 2 duplicate pairs, location x
  chapter keying, free-roam vs menu-hub granularity
- resolves Open Q#1/#3/#4/#5/#6/#8/#9/#12; corrects SLUS-90029 (FF8 demo, not BFM)
- 2 US prototypes acquired + CRC-verified (Aug-31 1369DE07, Sep-8 5C24728E)
- rules R9 (verify persistence), R10 (>=3-datapoint live verification),
  R11 (runtime oracle Windows-native, bridged to WSL)
- bumps project version 1.2.0 -> 1.3.0
2026-06-14 02:04:28 -06:00

63 lines
2.9 KiB
Java

// ImportOverlay.java — headless postScript: load the T6b-proven resident blob + one location
// overlay into the program at their byte-verified vaddrs, so the overlay/resident code becomes
// disassemblable in Ghidra. The MCP tools cannot create memory blocks / bulk-load file bytes, so
// this is done headless (analyzeHeadless opens the project directly and saves on completion).
//
// ~/ghidra_12.1_PUBLIC/support/analyzeHeadless ~/bfm-decomp/ghidra bfm \
// -process SLUS_007.26 -noanalysis \
// -scriptPath ~/bfm-decomp/tools/ghidra_scripts -postScript ImportOverlay.java
//
// Idempotent: re-runs write into the blocks created on the first run.
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import java.io.ByteArrayInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ImportOverlay extends GhidraScript {
public void run() throws Exception {
String home = System.getProperty("user.home");
// {block name, vaddr, extracted file}
String[][] targets = {
{"residentBlob", "0x800CEDF8", home + "/bfm-decomp/extracted/MAIN.CD.dir/FILE_010.dir/1.1"},
{"ovl_tutForest", "0x80128158", home + "/bfm-decomp/extracted/SC01.CD.dir/FILE_077.dir/0.4.dec"},
};
Memory mem = currentProgram.getMemory();
println("BFMOVL == existing memory blocks ==");
for (MemoryBlock b : mem.getBlocks())
println("BFMOVL " + b.getName() + " " + b.getStart() + "-" + b.getEnd()
+ " init=" + b.isInitialized());
for (String[] t : targets) {
String name = t[0];
Address addr = toAddr(t[1]);
byte[] bytes = Files.readAllBytes(Paths.get(t[2]));
MemoryBlock existing = mem.getBlock(addr);
if (existing == null) {
mem.createInitializedBlock(name, addr, new ByteArrayInputStream(bytes),
bytes.length, monitor, false);
println("BFMOVL CREATED " + name + " @ " + addr + " len=" + bytes.length);
} else {
println("BFMOVL into existing block '" + existing.getName()
+ "' init=" + existing.isInitialized());
if (!existing.isInitialized())
mem.convertToInitialized(existing, (byte) 0);
mem.setBytes(addr, bytes);
println("BFMOVL WROTE " + bytes.length + " bytes @ " + addr);
}
}
// sanity: read back first 8 bytes at each vaddr
for (String[] t : targets) {
Address a = toAddr(t[1]);
byte[] c = new byte[8];
mem.getBytes(a, c);
StringBuilder sb = new StringBuilder();
for (byte x : c) sb.append(String.format("%02x", x));
println("BFMOVL VERIFY " + a + " first8=" + sb);
}
println("BFMOVL done.");
}
}