Decompilation notes and limits
Tools
- Ghidra 12.2 (public, 2026-10-16 build), headless, with default auto-analysis and the built-in C decompiler. Ran on the device under Temurin JDK 25 (Ghidra 12.2 needs Java 25).
- The
.exeunpack used Pythondnfileto read .NET manifest resources. No code ran.
What the output is
Per binary, under decomp/native/<binary>/:
global/functions_NNN.c— every function, in address order, 250 per file. Each starts with// <addr> <name> (<size> bytes).strings_by_function.txt— the index to use. Every function that references a string, and the strings it uses. Grep a feature → get the address → open that function.functions.csv— address, name, size, file, and whether it decompiled.types.h,imports.txt,exports.txt,strings.txt.
Limits you need to know (these are process issues, not bugs in the mod)
-
Both binaries are stripped. No symbols, no PDB. Every function is
FUN_<addr>and there are no C++ class/method names. Call graphs read asFUN_x(FUN_y(...)). To get names, xrefs and renaming, import the binary into the Ghidra GUI. -
This is pseudo-C, not source. The decompiler reconstructs control flow and types; it invents variable names, can get calling conventions or struct layouts wrong, and
undefined4/uRam…casts are guesses. Treat it as a readable disassembly, not ground truth. -
One DLL function did not decompile:
FUN_66175b90(31,444 bytes — the largest in the DLL). The decompiler timed out on it even at 150 s. It's markedfalseinfunctions.csvand appears as a/* decompile failed: timeout */stub. Open it in the GUI if you need it. (1,266 of 1,267 DLL functions succeeded; the server was 623/623.) -
The CRT dominates. Most functions are the statically-linked MinGW C runtime and libstdc++ (
std::,random_device,__cxa_…, overflow/length_error machinery). The mod's own code is the subset that references FlandersMP/SHAR strings — that's whatstrings_by_function.txtis for. -
Strings are loaded by address, so grepping for a literal inside
global/*.coften misses it (you'll see a bare hex constant instead). Grepstrings_by_function.txtorstrings.txtto map address↔string, then jump to the function. -
Switch tables showed up as fake namespaces (
switchD_…). I removed those stub files; the real jump logic is inline in the owning function. -
No dynamic analysis. Everything is inferred from static code. Anything about runtime behaviour (what actually gets sent on the wire, whether a bound is hit in practice) is an inference and is tagged accordingly in
issues.md.