"""DIMENSION 4 q5: map model vtable slots +0x2b0..+0x320 to their accessor
displacements, so slot +0x2d0 (the offline-draft-specific gate in FUN_1800b2680)
and slot +0x320 (cVar9) can be measured live.

Model vtable static = 0x18021c2a0 (from ground truth / card doc). For each slot read
the target function's first bytes; if it is the accessor stub 0f b6 81 <disp32> c3
(movzx eax,byte [rcx+disp]; ret) decode disp.

Control: slot +0x270 must decode to disp 0x1fd2e (IS_TRADING), slot +0x2c8 to 0x1fd3d
(IS_DRAFT_MODE_ENABLED) -- both established in the card-subsystem doc.
"""
import traceback
try:
    VT = 0x18021c2a0
    names = {0x270:"IS_TRADING(+0x1fd2e)", 0x280:"IS_STORE", 0x2b0:"FRIENDLY_SEASON(+0x1fd3a)",
             0x2b8:"TOURNAMENT_QUIT(+0x1fd3b)", 0x2c0:"PROCESSING(+0x1fd3c)",
             0x2c8:"DRAFT_MODE(+0x1fd3d)", 0x2d0:"?offline-draft gate?",
             0x2d8:"STORY_MODE_REWARD", 0x2e0:"packAnim(+0x1fd45)",
             0x2f0:"RETURNING_USER", 0x320:"cVar9(FUN_1800b2680)"}
    for slot in range(0x2a0, 0x330, 8):
        tgt = qword(VT + slot)
        b = read_bytes(tgt, 8)
        disp = None
        if b[:3] == b"\x0f\xb6\x81":  # movzx eax, byte [rcx+disp32]
            import struct
            disp = struct.unpack("<i", b[3:7])[0]
        note = names.get(slot, "")
        print("slot +%#05x -> %#012x  stub=%s  disp=%s  %s" %
              (slot, tgt, b.hex(), hex(disp) if disp is not None else "(not a byte-accessor)", note))
        if disp is None:
            # decompile non-trivial accessors (offline draft gate / cVar9 may compute)
            if slot in (0x2d0, 0x320):
                print("   --- dec slot +%#x target ---" % slot)
                print(dec(tgt))
except Exception:
    traceback.print_exc()
print("QUERY_DONE")
