# -*- coding: utf-8 -*- """ q_pack_inv_1 -- DIMENSION 1 (pack inventory) pass 1. HYPOTHESIS (a) FutCreateUserServerResponse deser 0x18014cc60 dispatches atom 0x2e5 (starterPack) and 0x5d (bonusPacks) to dedicated sub-deserializers whose addresses appear in its decompile. (b) userInfo deser 0x18013ec10 dispatches atom 0x35e (unopenedPacks) to a sub-deserializer. (c) The image contains response classes beyond those in ENDPOINT_MAP.md; a census of b"RS4:Fut" enumerates them all. (d) Route/format fragments "ut/%s/", "purchased", "unassigned", "purchasegroup" appear as literals whose xrefs name the builder functions. CONTROLS * class_deser("FutSquadSave") must return 0x180171a60, class_deser("FutSquadList") -> 0x180172140, class_deser("FutCreateMatch") -> 0x180120380. If these fail the batch is void. * b"RS4:FutSquadSave" must be found by the census scan (known to exist at static 0x18022c618 per the ground-truth controls). * Every decompile prints len(src) FIRST so no absence is concluded from a truncated body. """ import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres" try: print("########## CONTROL BLOCK ##########") for nm, want in (("FutSquadSave", 0x180171a60), ("FutSquadList", 0x180172140), ("FutCreateMatch", 0x180120380)): r = class_deser(nm) got = sorted(set(x[0] for x in r)) print("CONTROL class_deser(%-16s) -> %s expect %#x %s" % (nm, [hex(g) for g in got], want, "PASS" if want in got else "FAIL")) print("\n########## Q3a RS4:Fut CENSUS ##########") hits = find_all(b"RS4:Fut", blocks=(".rdata", ".data", ".text")) names = {} for h in hits: s = rd_str(h, 120) nm = s[4:] names.setdefault(nm, []).append(h) print("raw hits: %d distinct names: %d" % (len(hits), len(names))) ctl = "FutSquadSave" in names print("CONTROL census contains FutSquadSave: %s" % ctl) for nm in sorted(names): print(" %-60s %s" % (nm, [hex(a) for a in names[nm]])) # also catch RS4: names that do not start with Fut, for completeness print("\n########## Q3a-bis ALL RS4: CLASS NAMES ##########") hits2 = find_all(b"RS4:", blocks=(".rdata", ".data", ".text")) n2 = {} for h in hits2: s = rd_str(h, 120)[4:] if not s: continue n2.setdefault(s, []).append(h) print("raw hits: %d distinct: %d" % (len(hits2), len(n2))) for nm in sorted(n2): if not nm.startswith("Fut"): print(" %-60s %s" % (nm, [hex(a) for a in n2[nm]])) print("\n########## Q3b ROUTE FRAGMENTS ##########") for frag in (b"ut/%s/", b"purchased", b"unassigned", b"purchasegroup", b"unopened", b"gift", b"entitlement", b"reward"): hs = find_all(frag, blocks=(".rdata", ".data", ".text")) print("\n--- %r : %d hits" % (frag, len(hs))) for h in hs[:80]: try: s = rd_str(h - 0 if True else h, 140) except Exception: s = "?" # back up to string start (previous NUL) for context start = h for k in range(1, 90): try: if (mem.getByte(addr(h - k)) & 0xFF) == 0: start = h - k + 1 break except Exception: break full = rd_str(start, 200) xr = xrefs_to(start) print(" @%#x start=%#x %r" % (h, start, full)) for (fr, ty, fn, en) in xr[:8]: print(" ref %#x %-10s %s @%#x" % (fr, ty, fn, en)) print("\n########## Q1 FutCreateUserServerResponse deser 0x18014cc60 ##########") src = dec(0x18014cc60) print("len(src) = %d (FULL BODY FOLLOWS, untruncated)" % len(src)) print(src) open(OUT + "/d1_createuser_18014cc60.txt", "w").write(src) print("\n########## Q2 userInfo deser 0x18013ec10 ##########") src2 = dec(0x18013ec10) print("len(src) = %d (FULL BODY FOLLOWS, untruncated)" % len(src2)) print(src2) open(OUT + "/d1_userinfo_18013ec10.txt", "w").write(src2) print("\n########## Q4 pack element deser 0x18013af30 ##########") src3 = dec(0x18013af30) print("len(src) = %d (FULL BODY FOLLOWS, untruncated)" % len(src3)) print(src3) open(OUT + "/d1_packelem_18013af30.txt", "w").write(src3) print("\nDONE q_pack_inv_1") except Exception: traceback.print_exc()