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)
This commit is contained in:
66
reverse/resolve_decomp.py
Normal file
66
reverse/resolve_decomp.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user