"""Decompile the gate accessors themselves.

The publisher calls virtuals on the object returned by the service locator:
  +0x270 IS_TRADING_ENABLED   +0x280 IS_STORE_ENABLED  (CONTROL: works today)
  +0x2b0 IS_FRIENDLY_SEASON_ENABLED   +0x2b8 IS_TOURNAMENT_QUIT_ENABLED
  +0x2c0 IS_PROCESSING_STATE_ENABLED  +0x2c8 IS_DRAFT_MODE_ENABLED
  +0x2d8 IS_STORY_MODE_REWARD_ENABLED +0x2f0 IS_RETURNING_USER_REWARDS_...

FutDataManagerImpl's ctor installs three vtables (multiple inheritance):
PTR_LAB_18021c2a0 at +0, PTR_FUN_18021cda8 at +8, PTR_LAB_18021cdb8 at +0x10.
The locator hands back one of the sub-objects, so try all three and keep whichever
resolves these slots to real functions. Reading the accessor settles what each
gate actually reads, which the field-offset arithmetic can only guess at.
"""
SLOTS = {0x270: "IS_TRADING_ENABLED", 0x280: "IS_STORE_ENABLED (CONTROL)",
         0x2B0: "IS_FRIENDLY_SEASON_ENABLED", 0x2B8: "IS_TOURNAMENT_QUIT_ENABLED",
         0x2C0: "IS_PROCESSING_STATE_ENABLED", 0x2C8: "IS_DRAFT_MODE_ENABLED",
         0x2D8: "IS_STORY_MODE_REWARD_ENABLED", 0x2F0: "IS_RETURNING_USER_REWARDS"}

for vt in (0x18021C2A0, 0x18021CDA8, 0x18021CDB8):
    print("#" * 78)
    print("# vtable %#x" % vt)
    print("#" * 78)
    ok = 0
    for off, name in sorted(SLOTS.items()):
        try:
            t = qword(vt + off)
        except Exception as e:
            print("  +%#05x %-34s <unreadable>" % (off, name))
            continue
        f = fm.getFunctionAt(addr(t)) if 0x180000000 <= t < 0x181000000 else None
        print("  +%#05x %-34s -> %#x %s" % (off, name, t, f.getName() if f else "(not a function)"))
        if f:
            ok += 1
    print("  resolved %d/%d" % (ok, len(SLOTS)))
    if ok >= len(SLOTS) - 1:
        print("\n  -- accessor bodies --")
        for off, name in sorted(SLOTS.items()):
            t = qword(vt + off)
            if fm.getFunctionAt(addr(t)):
                print("\n  === +%#05x %s ===" % (off, name))
                print(dec(t))
    print()
