"""DIMENSION 2 Q3/Q4 finish: draft-tile gating in FUN_1800b2680; SBC/Objectives
accessor slots and whether any native code reads them.

HYPOTHESIS: IS_DRAFT_MODE_ENABLED (cVar7) gates whether the draft hub tile is drawn
/ enabled. SBC(0x1fd2c,0x1fd42) and Objectives(0x1fd44) have accessor stubs at some
model vtable slots but NO IS_* publisher name; either a vtable-slot caller reads
them or they are consumed only by their own accessor (i.e. no native mode gate).

CONTROL: draft accessor is model slot 0x2c8 (proven). Walking the vtable and
matching disp must reproduce 0x2c8->0x1fd3d and 0x270->0x1fd2e.
"""
import traceback
try:
    MODEL_VT = 0x18021c2a0
    # find slots for the SBC/objectives displacements by walking vtable
    want = {0x1fd2c: "allowUntradeableForSBC", 0x1fd42: "allowGracePeriodForSBC",
            0x1fd44: "enableObjectives", 0x1fd28: "sbcGracePeriodMinutes",
            0x1fd3e: "offlineDraft(0x2d0?)", 0x1fd2e: "trading(ctl)",
            0x1fd3d: "draft(ctl)"}
    slot_for_disp = {}
    print("=" * 70)
    print("vtable walk: slot -> accessor disp (0x200..0x340)")
    print("=" * 70)
    for slot in range(0x200, 0x340, 8):
        tgt = qword(MODEL_VT + slot)
        if not (0x180000000 <= tgt < 0x181000000):
            continue
        stub = read_bytes(tgt, 8)
        disp = None
        if stub[0:3] == b"\x0f\xb6\x81" and stub[7] == 0xc3:
            disp = int.from_bytes(stub[3:7], "little")
        elif stub[0:2] == b"\x8b\x81" and stub[6] == 0xc3:
            disp = int.from_bytes(stub[2:6], "little")
        if disp in want:
            slot_for_disp[disp] = slot
            print("slot +%#05x -> %#011x disp %#x  %s"
                  % (slot, tgt, disp, want[disp]))

    # scan slot-callers for the objectives + SBC slots
    print()
    print("=" * 70)
    print("slot-call readers for SBC/Objectives accessor slots")
    print("=" * 70)
    call_modrm = [0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97]
    for disp in (0x1fd44, 0x1fd2c, 0x1fd42):
        slot = slot_for_disp.get(disp)
        if slot is None:
            print("\ndisp %#x: no vtable slot found in range" % disp)
            continue
        pat_disp = slot.to_bytes(4, "little")
        found = []
        for pre in ([], [0x41]):
            for mrm in call_modrm:
                pat = bytes(pre + [0xff, mrm]) + pat_disp
                for h in find_all(pat, blocks=(".text",)):
                    f = fm.getFunctionContaining(addr(h))
                    found.append((h, f.getName() if f else "?",
                                  int(f.getEntryPoint().getOffset()) if f else 0))
        print("\ndisp %#x  slot +%#05x  %-24s (%d call-site(s))"
              % (disp, slot, want[disp], len(found)))
        for h, fn, ent in found:
            print("    %#011x  in %-16s (%#x)" % (h, fn, ent))

    # rest of the draft hub-tile builder: how cVar7/8/9 gate the tile
    print()
    print("=" * 70)
    print("FUN_1800b2680 draft/tile gating region (search cVar / DRAFT in decompile)")
    print("=" * 70)
    d = dec(0x1800b2680)
    lines = d.splitlines()
    for i, ln in enumerate(lines):
        if any(k in ln for k in ("cVar7", "cVar8", "cVar9", "DRAFT", "0x70", "0x60",
                                 "DESTINATION", "GOTO_", "SBC", "OBJECTIVE", "case 0xc",
                                 "caseD_")):
            print("%4d: %s" % (i, ln.strip()))
except Exception:
    traceback.print_exc()
