Files
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

89 lines
4.3 KiB
Java

// Headless script: attribute functions to their original .cpp via embedded assert source-paths
// (d:\tesla_bt\bt\<file>.cpp), then decompile the BattleTech game-logic functions and write them
// grouped per source file into decomp/recovered/. Engine (munga\) is summarized, not exported.
import ghidra.app.script.GhidraScript;
import ghidra.app.decompiler.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.StringDataInstance;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import java.io.*;
import java.util.*;
public class ExportBTSource extends GhidraScript {
public void run() throws Exception {
File outDir = new File("C:\\git\\nick-games\\decomp\\recovered");
outDir.mkdirs();
// 1) map function -> set of source files it references (via assert path strings)
Map<Function, Set<String>> funcFiles = new HashMap<>();
Map<String, Set<Function>> fileFuncs = new TreeMap<>();
ReferenceManager rm = currentProgram.getReferenceManager();
FunctionManager fm = currentProgram.getFunctionManager();
Listing listing = currentProgram.getListing();
int strCount = 0;
DataIterator di = listing.getDefinedData(true);
while (di.hasNext() && !monitor.isCancelled()) {
Data d = di.next();
if (d == null || !d.hasStringValue()) continue;
Object v = d.getValue();
if (v == null) continue;
String s = v.toString().toLowerCase().replace('/', '\\');
if (!s.contains("\\tesla_bt\\")) continue;
if (!(s.endsWith(".cpp") || s.endsWith(".hpp"))) continue;
strCount++;
String file = s.substring(s.lastIndexOf('\\') + 1);
// classify subdir
String dir = s.contains("\\bt_l4\\") ? "bt_l4" : s.contains("\\bt\\") ? "bt"
: s.contains("\\munga_l4\\") ? "munga_l4" : "munga";
String tag = dir + "/" + file;
for (Reference ref : rm.getReferencesTo(d.getAddress())) {
Function f = fm.getFunctionContaining(ref.getFromAddress());
if (f == null) continue;
funcFiles.computeIfAbsent(f, k -> new HashSet<>()).add(tag);
fileFuncs.computeIfAbsent(tag, k -> new LinkedHashSet<>()).add(f);
}
}
println("assert source-path strings: " + strCount);
println("source files attributed: " + fileFuncs.size());
// 2) decompile + export functions for BT game modules (bt/ and bt_l4/)
DecompInterface dec = new DecompInterface();
dec.openProgram(currentProgram);
PrintWriter index = new PrintWriter(new FileWriter(new File(outDir, "_index.txt")));
for (Map.Entry<String, Set<Function>> e : fileFuncs.entrySet()) {
String tag = e.getKey();
int n = e.getValue().size();
index.println(tag + " -> " + n + " functions");
boolean isGame = tag.startsWith("bt/") || tag.startsWith("bt_l4/");
if (!isGame) continue; // engine/HAL we already have as source — skip export
String safe = tag.replace('/', '_');
PrintWriter out = new PrintWriter(new FileWriter(new File(outDir, safe + ".c")));
out.println("// Recovered decompilation of functions attributed to " + tag);
out.println("// (from BTL4OPT.EXE via embedded assert paths). Pseudocode — reconstruct against the header.\n");
int ok = 0;
for (Function f : e.getValue()) {
if (monitor.isCancelled()) break;
DecompileResults r = dec.decompileFunction(f, 60, monitor);
if (r != null && r.decompileCompleted()) {
DecompiledFunction df = r.getDecompiledFunction();
if (df != null) {
out.println("/* ---- @ " + f.getEntryPoint() + " ---- */");
out.println(df.getC());
out.println();
ok++;
}
}
}
out.close();
println("exported " + tag + ": " + ok + "/" + n + " functions");
}
index.close();
dec.dispose();
println("DONE. Output in " + outDir.getAbsolutePath());
}
}