"""D4 Q2/Q3/Q4: the reveal path itself.

(a) Scan .text for indirect calls through FutDataManagerImpl vtable slot +0x2e0
    (the packOpeningAnimationEnabled accessor 0x18011c590), i.e. the byte encodings
    ff 90 e0 02 00 00 / ff 92 .. / ff 93 .. etc.
    CONTROL: the same scan for slot +0x2b0 (IS_FRIENDLY_SEASON_ENABLED) MUST land
    inside FUN_18006cc60, which we already know calls it. If the control finds
    nothing the scan is broken and the 0x2e0 result is meaningless.

(b) Full decompiles of the functions that reference the reveal strings:
    USE_ANIMATION_STYLE          -> FUN_1800706e0
    gmLoadFUTPackOpenSublevel    -> FUN_18001eb90
    gmUnloadFUTPackOpenAnimation -> FUN_1800aa440
    CREATE_PACK_STATUS           -> FUN_1800a8010
    PACK_CREATE_UNOPENED_PACK    -> FUN_1800a5650, FUN_180015720
    NUM_RARES_IN_PACK            -> FUN_180015d80
    plus the FutCreatePackServerResponse deser 0x180162880 for Q4 (item ordering).

HYPOTHESIS for Q3: the reveal tier is chosen client-side from item fields the server
already sends (rating / rareflag / cardsubtypeid / playerType), and USE_ANIMATION_STYLE
is the data-provider key it is written to.
"""
import traceback, struct

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)


def indirect_call_sites(slot):
    """call qword ptr [reg + slot32]  -- ff /2 with disp32, modrm 90..97 (except 94)."""
    out = []
    d = struct.pack("<I", slot)
    for modrm in range(0x90, 0x98):
        if modrm == 0x94:
            continue
        pat = bytes([0xFF, modrm]) + d
        for h in find_all(pat, (".text",)):
            f = fm.getFunctionContaining(addr(h))
            out.append((h, f.getName() if f else "?",
                        int(f.getEntryPoint().getOffset()) if f else 0))
    return out


try:
    for slot, label in ((0x2B0, "CONTROL slot+0x2b0 IS_FRIENDLY_SEASON_ENABLED"),
                        (0x2C8, "CONTROL slot+0x2c8 IS_DRAFT_MODE_ENABLED"),
                        (0x2E0, "TARGET  slot+0x2e0 packOpeningAnimationEnabled")):
        sites = indirect_call_sites(slot)
        p("=== %s : %d indirect call sites ===" % (label, len(sites)))
        for h, nm, ent in sites:
            p("   %#x  in %s @ %#x" % (h, nm, ent))

    targets = [(0x1800706E0, "USE_ANIMATION_STYLE_ref"),
               (0x18001EB90, "gmLoadFUTPackOpenSublevel_ref"),
               (0x1800AA440, "gmUnloadFUTPackOpenAnimation_ref"),
               (0x1800A8010, "CREATE_PACK_STATUS_ref"),
               (0x1800A5650, "PACK_CREATE_UNOPENED_PACK_ref_a"),
               (0x180015720, "PACK_CREATE_UNOPENED_PACK_ref_b"),
               (0x180015D80, "NUM_RARES_IN_PACK_ref"),
               (0x180162880, "FutCreatePackServerResponse_deser")]
    for va, nm in targets:
        src = dec(va, 300)
        p("\n\n########## %s  %#x  len=%d ##########" % (nm, va, len(src)))
        p(src)
        with open(OUT + "d4_%s.txt" % nm, "w") as f:
            f.write(src)

except Exception:
    traceback.print_exc()
finally:
    with open(OUT + "d4_reveal_path.txt", "w") as f:
        f.write("\n".join(BUF))
    print("WROTE d4_reveal_path.txt")
