"""D4 Q1d: dump the FutDataManagerImpl primary vtable (PTR_LAB_18021c2a0, assigned last in ctor FUN_18010cdc0) slots 0x240..0x330 and decompile each, to map vtable slot -> settings gate byte. CONTROL: slot +0x2b0 must read byte 0x1fd3a (IS_FRIENDLY_SEASON_ENABLED per FUN_18006cc60) and slot +0x2c8 must read 0x1fd3d (IS_DRAFT_MODE_ENABLED). If those two do not come out right, the vtable is wrong and nothing else here counts. q_pack_reveal_3 failed to auto-detect this vtable because its first entries are PTR_LAB_ thunks that Ghidra did not turn into functions, so the "first two qwords are functions" filter rejected it. Addresses are hardcoded here on purpose. """ import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres/" BUF = [] def p(*a): s = " ".join(str(x) for x in a) print(s) BUF.append(s) try: for vt in (0x18021C2A0,): p("=== vtable %#x ===" % vt) for off in range(0x240, 0x340, 8): try: t = qword(vt + off) except Exception: p(" +%#05x " % off) continue fn = fm.getFunctionAt(addr(t)) nm = fn.getName() if fn else "?" body = "" if 0x180001000 <= t < 0x1801E5000: body = " ".join(dec(t, 120).split()) p(" +%#05x %#x %-22s :: %s" % (off, t, nm, body[:300])) # who calls the accessor at whatever slot reads 0x1fd45? find it first, then xref. except Exception: traceback.print_exc() finally: with open(OUT + "d4_fdm_vtable.txt", "w") as f: f.write("\n".join(BUF)) print("WROTE d4_fdm_vtable.txt")