vehicleSubSystems: full reverse-engineering + reconstruction spec (blocked)
Reverse-engineered the vehicleSubSystems config primitive end to end (Ghidra headless, all ~28 functions). It is NOT a widget: it is the engineering-screen (MFD) subsystem-panel system. Its Make (FUN_004cbaf0) is a per-subsystem factory that builds a SubsystemCluster-family status panel onto one of 12 auxiliary MFD positions, dispatching on subsystem classID (HeatSink/Myomer/Energy/Ballistic clusters). The whole cluster family (base + 5 subclasses) + 4 btl4gau2 sub-gauges (CoolingLoop/PowerSource/ScalarBarGauge/ConfigMapGauge) are declared in btl4gau2.hpp but not reconstructed. BLOCKER: the Make reads base subsystem fields subsystem[0x1dc] (aux-screen position), [0x1e4]/[0x1e0]/[0x224] that our MechSubsystem reconstruction (ends 0x114) does not have or populate -- so the panels render nothing until the core subsystem layout is extended + populated from the resource parse, which touches the working combat/heat subsystem code (regression risk). Checkpointed at full spec pending go/no-go on the large core-touching implementation. - docs/VEHICLE_SUBSYSTEMS.md: complete reconstruction spec (dispatch table, geometry table, class family map, sub-gauge inventory, engine-primitive reuse, the blocker, the Phase-1/Phase-2 plan). - reference/ghidra_scripts/DecompVSS.java: headless address-list decompiler (reusable for any function the assert-anchored exporter skipped). - CLAUDE.md: record the finding in the gauge-widget notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
099871ef4c
commit
a5fa9f1c79
@@ -0,0 +1,70 @@
|
||||
// Comprehensive decompile of the vehicleSubSystems Make dispatcher, the whole
|
||||
// SubsystemCluster family, and the sub-gauge ctors they build. Also dumps the
|
||||
// per-group geometry table and the relevant vtables. Writes to scratchpad.
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import java.io.*;
|
||||
|
||||
public class DecompVSS extends GhidraScript {
|
||||
public void run() throws Exception {
|
||||
String outPath = "C:\\Users\\epilectrik\\AppData\\Local\\Temp\\claude\\C--git-bt411\\09bf24bb-f92d-423a-b815-1eb662736f58\\scratchpad\\vss_sub.txt";
|
||||
PrintWriter out = new PrintWriter(new FileWriter(outPath));
|
||||
FunctionManager fm = currentProgram.getFunctionManager();
|
||||
Memory mem = currentProgram.getMemory();
|
||||
DecompInterface dec = new DecompInterface();
|
||||
dec.openProgram(currentProgram);
|
||||
|
||||
long[] targets = {
|
||||
// btl4gau2 sub-gauge ctors that must be reconstructed
|
||||
0x4c70a4L, 0x4c7134L, 0x4c3134L, // CoolingLoop/AnimatedSubsystemLamp + dtor + connection
|
||||
0x4c7160L, 0x4c71f0L, 0x4c31ecL, // PowerSource/AnimatedSourceLamp + dtor + connection
|
||||
0x4c721cL, 0x4c72acL, 0x4c733cL, // ScalarBarGauge/GeneratorVoltage + ctor2 + dtor
|
||||
0x4c6d80L, 0x4c6e54L, 0x4c6ee0L, // ConfigMapGauge + dtor + SetColor
|
||||
0x4c6f34L, // ConfigMapGauge Execute (guess)
|
||||
// SubsystemCluster helpers
|
||||
0x4c89c4L, 0x4c8820L, 0x4c8990L, 0x4c8a28L, 0x4c2ec4L,
|
||||
// Weapon/Ballistic extra vtable slots
|
||||
0x4c92e4L, 0x4c9b24L, 0x4c9b50L, 0x4c8990L,
|
||||
// connections
|
||||
0x4749deL, 0x474855L, 0x4c3288L,
|
||||
};
|
||||
for (long t : targets) {
|
||||
Address a = toAddr(t);
|
||||
Function f = fm.getFunctionContaining(a);
|
||||
if (f == null) {
|
||||
try { disassemble(a); f = createFunction(a, "FUN_" + Long.toHexString(t)); }
|
||||
catch (Exception e) { out.println("// could not create fn at " + a + ": " + e); }
|
||||
}
|
||||
out.println("\n//====================================================================");
|
||||
out.println("// FUN_" + Long.toHexString(t) + (f!=null ? " ("+f.getName()+")" : ""));
|
||||
out.println("//====================================================================");
|
||||
if (f == null) { out.println("// NO FUNCTION"); continue; }
|
||||
DecompileResults r = dec.decompileFunction(f, 60, monitor);
|
||||
if (r != null && r.decompileCompleted()) out.println(r.getDecompiledFunction().getC());
|
||||
else out.println("// decompile failed: " + (r!=null?r.getErrorMessage():"null"));
|
||||
}
|
||||
|
||||
// vtables for the cluster family
|
||||
long[] vts = { 0x51a020L /*SubsystemCluster*/, 0x519fd4L /*HeatSink*/, 0x519f88L /*Myomer*/,
|
||||
0x519f38L /*Weapon*/, 0x519ee8L /*Energy*/, 0x519e98L /*Ballistic*/ };
|
||||
out.println("\n//==== cluster vtables ====");
|
||||
for (long vt : vts) {
|
||||
StringBuilder sb = new StringBuilder(String.format("vt 0x%x: ", vt));
|
||||
for (int i=0;i<20;i++){ long v = mem.getInt(toAddr(vt+i*4))&0xffffffffL; sb.append(String.format("[%d]=0x%x ",i,v)); }
|
||||
out.println(sb.toString());
|
||||
}
|
||||
// geometry table
|
||||
out.println("\n//==== geometry table @0x51bf34 (12 groups x 7 dwords) ====");
|
||||
for (int g=0; g<12; g++) {
|
||||
StringBuilder sb = new StringBuilder("group "+(g+1)+": ");
|
||||
for (int k=0;k<7;k++){ long v = mem.getInt(toAddr(0x51bf34L+(g*7L+k)*4))&0xffffffffL; sb.append(String.format("0x%x ",v)); }
|
||||
out.println(sb.toString());
|
||||
}
|
||||
out.close();
|
||||
println("DecompVSS full done -> " + outPath);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user