# -*- coding: utf-8 -*-
"""ADVERSARIAL VERIFY 3: the reveal brain, the tier table, the headline predicate,
the sort, the NUM_*_IN_PACK provider, and the playerType ABSENCE claim.

H1 FUN_1800aa440 ranks on item+0x38 with fallback item+0x3c, gates on cardtype==1,
   fires 0x33 / 0x34, and issues NO network request.
   ATTACK on the "no network request" ABSENCE: instead of grepping the decompile text,
   I enumerate EVERY call target in the function FROM THE DISASSEMBLY and print its name,
   then check them against the known URL-builder / request machinery.
H2 FUN_1800a9fe0(rareflag, rating) -> 1/2/3 with the quoted thresholds.
H3 FUN_1800aa330 = (loans < 1) && (rating > 0x57 || playerid in fcc_GrandStandPlayers)
H4 FUN_1800a96a0 is a stable descending sort keyed on tuple[0]
H5 FUN_180015d80 reads NUM_*_IN_PACK from packdef +0xc0..+0xd0
H6 ABSENCE: atom 0x23d playerType has no arm in 0x18013fe00.
   ATTACK with a DIFFERENT METHOD than grepping the decompile: I reconstruct the
   atom ladder from the DISASSEMBLY of 0x18013fe00 by walking every SUB/CMP/DEC
   immediate on the dispatch register and accumulating the running sum, then report
   the full set of atom ids the ladder can reach. CONTROL: 0x23f playStyle, 0xd7
   discardValue, 0x6c cardsubtypeid, 0x274 rating and 0x271 rareflag must all appear.
"""
import traceback, re

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres"

def dump(n, s):
    p = "%s/v_%s.txt" % (OUT, n)
    open(p, "w").write(s)
    print("[wrote %s  %d chars]" % (p, len(s)))

try:
    print("=" * 78)
    print("H1: FUN_1800aa440 FULL")
    s = dec(0x1800aa440)
    print("len(src) = %d chars, %d lines" % (len(s), s.count("\n") + 1))
    dump("q3_reveal", s)
    print(s)

    print("--- disassembly: EVERY call target inside FUN_1800aa440 ---")
    f = func(0x1800aa440)
    it = listing.getInstructions(f.getBody(), True)
    direct, indirect = [], []
    while it.hasNext():
        ins = it.next()
        t = str(ins)
        if not t.startswith("CALL"):
            continue
        a = int(ins.getAddress().getOffset())
        fl = ins.getFlows()
        if fl and len(fl) > 0:
            tgt = int(fl[0].getOffset())
            direct.append((a, tgt, fname(tgt)))
        else:
            indirect.append((a, t))
    print("  direct calls: %d" % len(direct))
    for a, tgt, nm in direct:
        print("    %#x -> %#x  %s" % (a, tgt, nm))
    print("  indirect calls: %d" % len(indirect))
    for a, t in indirect:
        print("    %#x  %s" % (a, t))
    print("  URL builder 0x180129200 called?", any(t == 0x180129200 for _, t, _ in direct))

    print("=" * 78)
    print("H2: FUN_1800a9fe0 FULL")
    s2 = dec(0x1800a9fe0)
    print("len=%d" % len(s2)); dump("q3_tier", s2); print(s2)

    print("=" * 78)
    print("H3: FUN_1800aa330 FULL")
    s3 = dec(0x1800aa330)
    print("len=%d" % len(s3)); dump("q3_pred", s3); print(s3)

    print("=" * 78)
    print("H4: FUN_1800a96a0 FULL")
    s4 = dec(0x1800a96a0)
    print("len=%d" % len(s4)); dump("q3_sort", s4); print(s4)

    print("=" * 78)
    print("H5: FUN_180015d80 FULL")
    s5 = dec(0x180015d80)
    print("len=%d" % len(s5)); dump("q3_numpack", s5); print(s5)

    print("=" * 78)
    print("H6: atom ladder of 0x18013fe00 reconstructed FROM DISASSEMBLY")
    s6 = dec(0x18013fe00)
    print("item deser decompile len=%d chars, %d lines" % (len(s6), s6.count("\n") + 1))
    dump("q3_item_deser", s6)
    # decompile-side case list, for cross-check
    cases = sorted(set(int(x, 16) for x in re.findall(r"case 0x([0-9a-f]+):", s6)))
    print("  decompile 'case 0x..' arms: %d -> %s" % (len(cases), [hex(c) for c in cases]))
    print("  decompile contains '0x23d'? ", "0x23d" in s6)
    print("  decompile contains '0x23f'? ", "0x23f" in s6)

    f6 = func(0x18013fe00)
    print("  function body: %s - %s" % (f6.getBody().getMinAddress(), f6.getBody().getMaxAddress()))
    it = listing.getInstructions(f6.getBody(), True)
    running = 0
    ladder = []
    seq = []
    while it.hasNext():
        ins = it.next()
        mn = ins.getMnemonicString()
        t = str(ins)
        a = int(ins.getAddress().getOffset())
        if mn in ("SUB", "CMP", "DEC", "ADD"):
            m = re.search(r",\s*(0x[0-9a-fA-F]+)$", t)
            imm = None
            if m:
                imm = int(m.group(1), 16)
            elif mn == "DEC":
                imm = 1
            if imm is None:
                continue
            if mn == "SUB" or mn == "DEC":
                running += imm
                ladder.append((a, running, t))
            elif mn == "CMP":
                ladder.append((a, running + imm, t + "   [CMP => atom %#x]" % (running + imm)))
            elif mn == "ADD":
                running -= imm
                ladder.append((a, running, t))
            seq.append((a, mn, imm, running))
    reach = sorted(set(v for _, v, _ in ladder))
    print("  ladder entries: %d ; distinct running-sum values: %d" % (len(ladder), len(reach)))
    print("  reachable atom-ish values (hex): %s" % [hex(v) for v in reach])
    for probe, nm in ((0x23d, "playerType"), (0x23f, "playStyle"), (0xd7, "discardValue"),
                      (0x6c, "cardsubtypeid"), (0x274, "rating"), (0x271, "rareflag"),
                      (0x172, "itemState"), (0x19b, "loans"), (0x287, "resourceId")):
        print("    atom %#-6x %-14s in ladder? %s   in decompile cases? %s"
              % (probe, nm, probe in reach, probe in cases))
    print("  --- raw ladder (first 400) ---")
    for a, v, t in ladder[:400]:
        print("    %#x  sum=%#-6x  %s" % (a, v, t))

except Exception:
    traceback.print_exc()
print("QUERY DONE")
