"""ADVERSARIAL VERIFICATION BATCH 1.

HYPOTHESES UNDER ATTACK (from the D4 report):
 H-A  record+0x54 is card LEVEL derived from rating by an unconditional ladder in
      the tail of FUN_180141660, NOT itemType.
 H-B  FUN_1801a87f0 is a one-byte read of record+0xb4 and all four OVERALL_RATING
      publishers call it.
 H-C  playStyle lands at record+0x88, FUN_180136480 accepts only 0xfb..0x111.
 H-D  atom 0x173 itemType never becomes an int.

CONTROLS.
 * For every "no such thing" statement I enumerate case labels, `== 0x`, `!= 0x`
   AND sub/dec ladders, and I state which form the positive control used.
 * Positive control for the dispatch enumeration: atoms 0x274 (rating) and 0x287
   (resourceId), both known-present, must be found by the SAME enumerator.
 * Positive control for the literal-xref method: a literal whose xref count is
   independently known.
Everything is written to files; nothing is truncated.
"""
import traceback

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

try:
    import re

    def dump(name, s):
        p = OUT + name
        open(p, "w").write(s)
        print("WROTE %s  (%d chars)" % (p, len(s)))

    targets = {
        "merge_141660": 0x180141660,
        "deser_13fe00": 0x18013FE00,
        "playersmerge_135890": 0x180135890,
        "acc_rating_1a87f0": 0x1801A87F0,
        "acc_cardlevel_1a80c0": 0x1801A80C0,
        "acc_playstyle_1a85c0": 0x1801A85C0,
        "acc_league_1a8550": 0x1801A8550,
        "acc_attr_1a8450": 0x1801A8450,
        "acc_dream_1a8830": 0x1801A8830,
        "acc_assetid_1a8010": 0x1801A8010,
        "acc_asset2_1a8020": 0x1801A8020,
        "mapper_playstyle_136480": 0x180136480,
        "family_d8330": 0x1800D8330,
        "resid_166ca0": 0x180166CA0,
    }
    blob = []
    src = {}
    for nm, a in targets.items():
        f = func(a)
        s = dec(a, 600)
        src[nm] = s
        blob.append("=" * 78)
        blob.append("### %s @ %#x  ghidra_fn=%s  entry=%#x  len=%d" % (
            nm, a, f.getName() if f else "NONE",
            int(f.getEntryPoint().getOffset()) if f else 0, len(s)))
        blob.append(s)
    dump("v1_bodies.txt", "\n".join(blob))

    # ---- dispatch-form enumeration over the item deser, ALL FOUR FORMS
    d = src["deser_13fe00"]
    print("\n--- deser FUN_18013fe00 len=%d ---" % len(d))
    cases = sorted(set(int(x, 16) for x in re.findall(r"case\s+0x([0-9a-fA-F]+)", d)))
    cases += sorted(set(int(x) for x in re.findall(r"case\s+(\d+)", d)))
    eq = sorted(set(int(x, 16) for x in re.findall(r"==\s*0x([0-9a-fA-F]+)", d)))
    ne = sorted(set(int(x, 16) for x in re.findall(r"!=\s*0x([0-9a-fA-F]+)", d)))
    lt = sorted(set(int(x, 16) for x in re.findall(r"<\s*0x([0-9a-fA-F]+)", d)))
    sub = sorted(set(int(x, 16) for x in re.findall(r"-\s*0x([0-9a-fA-F]+)", d)))
    print("case labels (%d): %s" % (len(cases), [hex(c) for c in cases]))
    print("== 0x  (%d): %s" % (len(eq), [hex(c) for c in eq]))
    print("!= 0x  (%d): %s" % (len(ne), [hex(c) for c in ne]))
    print("<  0x  (%d): %s" % (len(lt), [hex(c) for c in lt]))
    print("-  0x ladders (%d): %s" % (len(sub), [hex(c) for c in sub]))
    for probe, label in [(0x274, "rating CONTROL"), (0x287, "resourceId CONTROL"),
                         (0x173, "itemType"), (0x23F, "playStyle"),
                         (0x172, "itemState"), (0x207, "owners"),
                         (0x361, "untradeable"), (0x1B, "amount"),
                         (0x226, "pile"), (0x6B, "cardassetid"), (0x23, "assetId"),
                         (0x18A, "leagueId"), (0x1D1, "nation"), (0x6C, "cardsubtypeid")]:
        forms = []
        if probe in cases:
            forms.append("case")
        if probe in eq:
            forms.append("==")
        if probe in ne:
            forms.append("!=")
        print("  atom %#x %-18s dispatch forms: %s" % (probe, label, forms or "NONE FOUND"))

    # ---- who writes offset 0x54 anywhere in the two functions?
    print("\n--- textual writes to +0x54 / 0x54 in merge and deser ---")
    for nm in ("merge_141660", "deser_13fe00", "playersmerge_135890"):
        for ln_no, ln in enumerate(src[nm].split("\n")):
            if "0x54" in ln or "0xb4" in ln:
                print("  %-20s %4d| %s" % (nm, ln_no, ln.strip()))

    # ---- OVERALL_RATING literal: locate it MYSELF, then xref
    print("\n--- OVERALL_RATING literal census ---")
    hits = find_all(b"OVERALL_RATING\x00")
    print("occurrences of 'OVERALL_RATING\\0':", [hex(h) for h in hits])
    for h in hits:
        xs = xrefs_to(h)
        print("  %#x  xrefs=%d" % (h, len(xs)))
        for frm, t, fn, ent in xs:
            print("     from %#x %s in %s @%#x" % (frm, t, fn, ent))
    # control: a literal with an obviously different xref profile
    for lit in (b"CARD_LEVEL\x00", b"PLAY_STYLE\x00", b"LEAGUE_ID\x00",
                b"ATTRIBUTE_VALUE\x00", b"IS_DREAM_PLAYER\x00", b"ASSET_ID\x00"):
        hs = find_all(lit)
        print("\n%s occurrences: %s" % (lit, [hex(x) for x in hs]))
        for h in hs:
            xs = xrefs_to(h)
            print("   %#x xrefs=%d -> %s" % (h, len(xs), sorted(set(x[2] for x in xs))))

except Exception:
    traceback.print_exc()
