Ported from sacredK with module-relative addresses, hooks by exported name and a SHA-256 check of the six game modules (Community Patch 2.65.1). Borderless windowed at monitor size replaces exclusive fullscreen, whose focus loss during loading made the game quit and crash on exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.4 KiB
Java
84 lines
3.4 KiB
Java
// Decompile functions of one program of the persistent Ghidra project.
|
|
//
|
|
// Headless usage (see tools/decompile.ps1):
|
|
// analyzeHeadless <projectDir> sacred2 -process <module> -noanalysis
|
|
// -scriptPath tools/ghidra -postScript Decompile.java <outFile> <addr|symbol> [...]
|
|
//
|
|
// An argument that is not a number is looked up as a symbol name (e.g. a
|
|
// decorated export). Addresses without a function (e.g. methods only
|
|
// reachable via a vtable) get one created first; run without -readOnly to
|
|
// keep those in the project.
|
|
// @category sacred2K
|
|
|
|
import java.io.File;
|
|
import java.io.PrintWriter;
|
|
|
|
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
|
import ghidra.app.decompiler.DecompInterface;
|
|
import ghidra.app.decompiler.DecompileOptions;
|
|
import ghidra.app.decompiler.DecompileResults;
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.program.model.listing.Function;
|
|
import ghidra.program.model.symbol.SymbolIterator;
|
|
|
|
public class Decompile extends GhidraScript {
|
|
|
|
// A hex address (0x-prefixed or 8 digits), or the address of a symbol with
|
|
// that name.
|
|
private Address resolve(String text) {
|
|
if (text.matches("0x[0-9a-fA-F]+|[0-9a-fA-F]{8}")) {
|
|
return toAddr(text);
|
|
}
|
|
SymbolIterator symbols = currentProgram.getSymbolTable().getSymbols(text);
|
|
return symbols.hasNext() ? symbols.next().getAddress() : null;
|
|
}
|
|
|
|
@Override
|
|
protected void run() throws Exception {
|
|
String[] args = getScriptArgs();
|
|
if (args.length < 2) {
|
|
printerr("usage: Decompile.java <outFile> <address> [address...]");
|
|
return;
|
|
}
|
|
|
|
DecompInterface decompiler = new DecompInterface();
|
|
decompiler.setOptions(new DecompileOptions());
|
|
decompiler.openProgram(currentProgram);
|
|
|
|
try (PrintWriter out = new PrintWriter(new File(args[0]), "UTF-8")) {
|
|
for (int i = 1; i < args.length; i++) {
|
|
Address address = resolve(args[i]);
|
|
if (address == null) {
|
|
out.println("// ==== " + args[i] + " ====");
|
|
out.println("// no such address or symbol");
|
|
continue;
|
|
}
|
|
Function function = getFunctionAt(address);
|
|
if (function == null) {
|
|
function = getFunctionContaining(address);
|
|
}
|
|
if (function == null) {
|
|
new DisassembleCommand(address, null, true).applyTo(currentProgram, monitor);
|
|
function = createFunction(address, null);
|
|
}
|
|
out.println("// ==== " + args[i] + " ====");
|
|
if (function == null) {
|
|
out.println("// could not create a function here");
|
|
continue;
|
|
}
|
|
out.println("// " + function.getName(true) + " @ " + function.getEntryPoint()
|
|
+ " (" + function.getCallingConventionName() + ")");
|
|
DecompileResults result = decompiler.decompileFunction(function, 120, monitor);
|
|
if (result.decompileCompleted()) {
|
|
out.println(result.getDecompiledFunction().getC());
|
|
} else {
|
|
out.println("// decompile failed: " + result.getErrorMessage());
|
|
}
|
|
}
|
|
} finally {
|
|
decompiler.dispose();
|
|
}
|
|
}
|
|
}
|