"""ADVERSARIAL BATCH 2.

MAIN ATTACK: the claim "cardtype 9 has NO resolver at all, so ball and leaguelogo
display strings must come off the wire (localizedName + description)". That claim
CHANGES WHAT WE SERVE, so it is priority 1.

Counter-evidence to chase: .rdata at 0x1802041d0 holds 'fcc_leaguelogos' and
0x1802041e0 holds 'LeagueName_Abbr_15_%d', sitting immediately beside 'FUT_UC_KITS'
(0x180204180) which IS a resolver literal. If some function formats
LeagueName_Abbr_15_%d for a league logo, the "must come off the wire" claim is wrong.

  H6 vtable+0x490 = FUN_18011a860 is a GENERIC name resolver taking
     (cardtype@+0x4c, cardsubtypeid@+0x50, resourceId@+0x18). Does it have a
     cardtype-9 arm?
  H7 'fcc_leaguelogos' / 'LeagueName_Abbr_15_%d' are referenced by some function.
  H8 FUN_18012ee20 has EXACTLY ONE caller (the club URL builder).  [absence claim]
  H9 FUN_1800fed90 is the ONLY function whose switch case set is exactly
     {0x91..0x96}.  [absence claim -- re-tested here by a DIFFERENT method than
     the original caseD_ symbol enumeration: I enumerate switch tables from the
     instruction/flow side via getBasicBlocks + scalar operands, AND repeat the
     symbol method, and compare the two.]
  H10 FUN_180141660 (the merge) is called on every deserialized item.

CONTROL for the xref questions: 'FUT_UC_KITS' at 0x180204180 MUST come back with
>=1 referencing function (we already know FUN_180119bd0 uses it). If the xref
method returns 0 for FUT_UC_KITS the method is broken and every negative is void.
Same syntactic form (a .rdata string address referenced by a LEA) as the targets.
"""
import traceback, os

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

try:
    print("=== CONTROL + targets: xrefs to .rdata string addresses ===")
    STRS = {
        "FUT_UC_KITS  (CONTROL)": 0x180204180,
        "FUT_UC_BALL": 0x180239120,
        "fcc_leaguelogos": 0x1802041d0,
        "LeagueName_Abbr_15_%d": 0x1802041e0,
        "leagues": 0x1802041b0,
        "Badge (0x1802041b8)": 0x1802041b8,
        "countryid": 0x1802041c0,
        "fcc_myclubs": 0x180204190,
        "TeamName_Abbr15_%d?": None,
    }
    for name, a in STRS.items():
        if a is None:
            continue
        try:
            xs = xrefs_to(a)
        except Exception as e:
            print("  %-24s XREF ERROR %s" % (name, e)); continue
        fns = sorted(set((x[2], x[3]) for x in xs))
        print("  %-24s 0x%x  %d refs, funcs: %s" %
              (name, a, len(xs), ["%s@0x%x" % (n, e) for n, e in fns]))

    print()
    print("=== find TeamName_Abbr15_%d and StadiumName_%d addresses then xref ===")
    for lit in (b"TeamName_Abbr15_%d\x00", b"StadiumName_%d\x00", b"LeagueName_Abbr_15_%d\x00",
                b"fcc_leaguelogos\x00", b"fcc_balls\x00", b"fcc_stadium\x00",
                b"fcc_badgecards\x00", b"fcc_kitcards\x00", b"fcc_misccards\x00"):
        hits = find_all(lit, blocks=(".rdata", ".data", ".text"))
        print("  %-26s %d hit(s) at %s" % (lit.rstrip(b"\x00").decode(), len(hits),
                                           [hex(h) for h in hits]))
        for h in hits:
            xs = xrefs_to(h)
            fns = sorted(set((x[2], x[3]) for x in xs))
            print("        -> %d refs: %s" % (len(xs), ["%s@0x%x" % (n, e) for n, e in fns]))

    print()
    print("=== H6: generic resolver FUN_18011a860 (vtable +0x490) FULL ===")
    src = dec(0x18011a860)
    open(os.path.join(OUT, "FUN_18011a860.c"), "w").write(src)
    print("len=%d" % len(src))
    print(src)

    print()
    print("=== H8: callers of FUN_18012ee20 (itemState code -> atom) ===")
    for fa in (0x18012ee20, 0x180141660, 0x180166660, 0x1800fed90):
        try:
            cs = callers(fa)
        except Exception:
            cs = [(x[0], x[2], x[3]) for x in xrefs_to(fa)]
        print("  FUN_%x callers: %s" % (fa, cs))

    print()
    print("=== H9: switch case-set enumeration, TWO methods ===")
    st = prog.getSymbolTable()
    # method 1: caseD_ symbols grouped by containing function
    import collections
    bysym = collections.defaultdict(set)
    n = 0
    for sym in st.getAllSymbols(True):
        nm = str(sym.getName())
        if not nm.startswith("caseD_"):
            continue
        n += 1
        a2 = sym.getAddress()
        f = fm.getFunctionContaining(a2)
        if f is None:
            continue
        try:
            v = int(nm.split("_")[-1], 16)
        except ValueError:
            continue
        bysym[int(f.getEntryPoint().getOffset())].add(v)
    print("  method1: %d caseD_ symbols over %d functions" % (n, len(bysym)))
    TARGET = set(range(0x91, 0x97))
    exact = [hex(k) for k, v in bysym.items() if v == TARGET]
    superset = [hex(k) for k, v in bysym.items() if TARGET <= v and v != TARGET]
    overlap = [hex(k) for k, v in bysym.items() if (TARGET & v) and not (TARGET <= v)]
    print("  functions with case set EXACTLY {0x91..0x96}: %s" % exact)
    print("  functions whose case set is a SUPERSET: %s" % superset)
    print("  functions with PARTIAL overlap: %s" % overlap)
    print("  CONTROL FUN_1800d8330 present in method1? %s -> %s" %
          (0x1800d8330 in bysym, sorted(hex(x) for x in bysym.get(0x1800d8330, []))))

    print()
    print("=== H10: callers of the merge FUN_180141660 ===")
    xs = xrefs_to(0x180141660)
    print("  %d refs: %s" % (len(xs), sorted(set("%s@0x%x" % (x[2], x[3]) for x in xs))))

except Exception:
    traceback.print_exc()
