# -*- coding: utf-8 -*- """ q_pack_inv_6 -- DIMENSION 1 pass 6: CLASS -> DESERIALIZER map for the whole image. HYPOTHESIS Pass 5 found a top-level parser FUN_18017fc20 whose ONLY root key is packList(0x20d). That is the pack-inventory response we have never modelled. Running class_deser over every RS4:Fut* literal in the image names it, and at the same time produces the complete class->deser table (useful far beyond this task). CONTROLS * FutSquadSaveServerResponse -> 0x180171a60, FutSquadListServerResponse -> 0x180172140, FutCreateMatchServerResponse -> 0x180120380 must all resolve. * class_deser is KNOWN to false-negative, so an unresolved class is reported as UNRESOLVED, never as "has no deserializer". """ import re import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres" ATOM_TABLE = 0x1802D2760 def atom_name(i): try: p = qword(ATOM_TABLE + i * 8) if 0x180001000 <= p <= 0x1802EFC08: return rd_str(p, 60) except Exception: pass return "?" try: names = set() for h in find_all(b"RS4:", blocks=(".rdata", ".data")): s = rd_str(h, 120)[4:] if s.startswith("Fut"): names.add(s) elif s.startswith(":Fut"): names.add(s[1:]) print("class-name literals found: %d" % len(names)) fwd = {} for nm in sorted(names): got = sorted(set(x[0] for x in class_deser(nm))) fwd[nm] = got print(" %-46s -> %s" % (nm, [hex(g) for g in got] or "UNRESOLVED")) print("\n########## CONTROLS ##########") for nm, w in (("FutSquadSaveServerResponse", 0x180171A60), ("FutSquadListServerResponse", 0x180172140), ("FutCreateMatchServerResponse", 0x180120380)): print(" %-32s %s" % (nm, "PASS" if w in fwd.get(nm, []) else "FAIL")) print("\n########## REVERSE LOOKUP for the parsers this dimension found ##########") for tgt in (0x18017FC20, 0x180122C50, 0x180161B00, 0x180124EE0, 0x18014CC60, 0x1801234E0, 0x180162880, 0x1801758C0, 0x180174630, 0x180146970): owners = [nm for nm, v in fwd.items() if tgt in v] print(" %#x -> %s" % (tgt, owners or "UNRESOLVED")) print("\n########## packList ELEMENT deser FUN_18017f830 ##########") s = dec(0x18017F830) print("len=%d" % len(s)) print(s) open(OUT + "/d1_packlist_elem_18017f830.txt", "w").write(s) ats = set() for m in re.finditer(r"(?:==|!=)\s*(0x[0-9a-fA-F]+|\d+)", s): v = int(m.group(1), 0) if 1 <= v <= 0x38C and v not in (6, 10, 0xB, 0xD, 0x38C): ats.add(v) for m in re.finditer(r"case\s+(0x[0-9a-fA-F]+|\d+)\s*:", s): v = int(m.group(1), 0) if 1 <= v <= 0x38C and v not in (6, 10, 0xB, 0xD, 0x38C): ats.add(v) print("\npackList element keys: " + ", ".join("%#x=%s" % (a, atom_name(a)) for a in sorted(ats))) print("\n########## who calls FUN_18017fc20 (the packList parser) ##########") for (fr, ty, fn, en) in xrefs_to(0x18017FC20): print(" %#x %-10s %s @%#x" % (fr, ty, fn, en)) print("\n########## model container getter vtbl[0x940] users ##########") import struct as _s for modrm in (0x90, 0x91, 0x92, 0x93, 0x96, 0x97): pat = bytes([0xFF, modrm]) + _s.pack("