"""DIMENSION 2 batch 6: find the concrete FUT request service and the request builders behind DiscardCard / DiscardCardList / MoveCard / SellCard. HYPOTHESES H1 FUN_1800295d0 is a setter; its caller passes the concrete service object, so the caller reveals the vtable. H2 0x180123cc0..0x180124910 is a block of tiny per-action factory thunks that Ghidra never disassembled. Disassembling them yields, for each action, the request class it constructs. H3 The action table's base is below 0x1802cb000 and some function indexes it with the action enum; that function is the request dispatcher. CONTROL: FutSquadSaveServerResponse -> 0x180171a60. """ import traceback, os OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres" try: print("CONTROL FutSquadSaveServerResponse ->", [hex(a) for a, v, e in class_deser("FutSquadSaveServerResponse")]) print("=" * 78) print("SECTION 1 -- callers of the service setter FUN_1800295d0") for r in xrefs_to(0x1800295D0): print(" %#x %s %s %#x" % (r[0], r[1], r[2], r[3])) for e in sorted({r[3] for r in xrefs_to(0x1800295D0) if r[3]}): s = dec(e) print("---- caller %#x len=%d ----" % (e, len(s))) print(s if len(s) < 8000 else s[:8000] + "\n...TRUNCATED, len=%d" % len(s)) print("=" * 78) print("SECTION 2 -- disassembly of the factory thunk block 0x180123900-0x180124950") a = 0x180123900 end = 0x180124950 while a < end: ins = listing.getInstructionAt(addr(a)) if ins is None: b = read_bytes(a, 16) print(" %#x DATA %s" % (a, b.hex())) a += 16 continue print(" %#x %s" % (a, ins)) a += ins.getLength() print("=" * 78) print("SECTION 3 -- action table extent and who indexes it") # walk backwards from 0x1802cb000 in 0x30 steps while row[0] looks like a string base = 0x1802CB000 while True: prev = base - 0x30 try: q = qword(prev) except Exception: break if not (0x1801E5000 <= q <= 0x180290000): break try: s = rd_str(q, 40) except Exception: break if not s or not all(0x20 <= ord(c) < 0x7F for c in s): break base = prev print(" table base ~ %#x" % base) for r in xrefs_to(base): print(" xref to base %#x %s %s %#x" % (r[0], r[1], r[2], r[3])) for e in sorted({r[3] for r in xrefs_to(base) if r[3]}): s = dec(e) print("---- indexer %#x len=%d ----" % (e, len(s))) print(s if len(s) < 9000 else s[:9000] + "\n...TRUNCATED, len=%d" % len(s)) except Exception: traceback.print_exc()