Files
SandTools/reverse/resolve_decomp.py
DownloadPizza fc6b270fa8 master-server replay + trampler RE: protocol, hashes, footprints, map renderer
- master_scrape.py: live master-server (ger.hologryph.com) ClientMessage replay over the
  two-socket /login + /connect handshake (PlayFab ticket auth). Pulled compartment defs,
  shop prices, research tree, storage, characters, expedition -> extracted/master_*.json
- PlayFab confirmed auth-only for this title (Economy disabled); docs corrected
- trampler_hashes.py: blueprint hash algo MD5(UTF8(compact-JSON)); CompartmentsHash(#1) and
  ConnectionsHash(#3) verified & generatable from scratch
- walkerdto_to_blueprint.py: WalkerDto(expedition) -> WalkerBlueprintDto, enum int<->name,
  verified by storage->WS->storage round-trip
- render_trampler.py: per-floor map from CompartmentsDatabase cell footprints (rotation solved
  via overlap check) + doors/hatches from Connections + turret arcs + cargo C1-C8 in game order
- docs/MASTER_SERVER.md, docs/TRAMPLER.md; ghidra address-offset bug fixed (no -0x1000)
2026-06-16 00:35:17 +02:00

67 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Annotate ghidra/decomp.c: resolve absolute VAs (0x180........) to IL2CPP symbol names
(methods.tsv) and string literals (strings.tsv). Ghidra image base = 0x180000000.
methods.tsv : <rva-decimal>\t<symbol> (rva = scriptAddress - 0x1000)
strings.tsv : <scriptAddress-decimal>\t<str> (raw ScriptString.Address)
Usage:
venv/bin/python reverse/resolve_decomp.py # writes ghidra/decomp.annotated.c
venv/bin/python reverse/resolve_decomp.py <substr> # print only funcs whose name matches substr
"""
import re, sys, os
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
G = os.path.join(ROOT, "ghidra")
BASE = 0x180000000
def load_methods():
m = {}
for l in open(os.path.join(G, "methods.tsv")):
if "\t" in l:
rva, name = l.rstrip("\n").split("\t", 1)
m[int(rva)] = name
return m
def load_strings():
s = {}
p = os.path.join(G, "strings.tsv")
if os.path.exists(p):
for l in open(p):
if "\t" in l:
a, v = l.rstrip("\n").split("\t", 1)
s[int(a)] = v
return s
def main():
methods = load_methods()
strings = load_strings()
src = open(os.path.join(G, "decomp.c"), encoding="utf-8", errors="replace").read()
hexre = re.compile(r"0x(1[0-9a-fA-F]{8,9})")
def repl(m):
va = int(m.group(1), 16)
rva = va - BASE
if rva in methods:
return "%s/*%s*/" % (m.group(0), methods[rva])
# string literal: try raw rva, rva+0x1000 (scriptAddress), and the table conventions
for cand in (rva, rva + 0x1000, va, va - BASE + 0x1000):
if cand in strings:
return '%s/*"%s"*/' % (m.group(0), strings[cand][:60])
return m.group(0)
out = hexre.sub(repl, src)
outp = os.path.join(G, "decomp.annotated.c")
open(outp, "w", encoding="utf-8").write(out)
nres = out.count("/*")
print("wrote %s (%d annotations)" % (outp, nres))
if len(sys.argv) > 1:
sub = sys.argv[1]
blocks = re.split(r"(?=// ==== )", out)
for b in blocks:
if sub.lower() in b[:200].lower():
print(b)
if __name__ == "__main__":
main()