"""ADVERSARIAL BATCH 4.

CARD SIDE
 A. FUN_1801aa7f0 and FUN_1800e6410 read [reg+0xb4] as a byte but sit OUTSIDE the
    accessor range [0x1801a7000,0x1801a9000) the D4 report swept. Do they read an
    item record? If so the "OVERALL_RATING has exactly four publishers, all through
    FUN_1801a87f0" completeness argument has a hole.
 B. FUN_1801356c0 -- the family-2 (manager) merge. Does it clobber +0xdd..+0xfb the
    way the players merge does? That decides whether leagueId at +0xe0 survives for
    managers.
 C. FUN_180134cb0 -- writes +0xfc..+0x101, which FUN_1801a86b0 reads as the
    per-attribute chemistry delta.
 D. disp8 scan for [reg+0x54]: 0x54 fits a signed disp8 so the disp32 trick does
    NOT apply; iterate EVERY instruction in .text instead. Positive control:
    FUN_180141660 and FUN_1801a80c0 must appear.

ROUTE SIDE
 E. FUN_18012ec50 club ?type= switch, FUN_18012f4f0 club/stats switch,
    FUN_1801308c0 consumables suffix, FUN_18012ddf0 query builder -- full, so the
    "exactly 30 / exactly 7 / no /stats/team" absences can be re-tested against
    case labels AND == AND != AND ladders.
"""
import traceback

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

try:
    import re
    bodies = []
    src = {}
    for nm, a in (("rating_reader_1aa7f0", 0x1801AA7F0),
                  ("rating_reader_e6410", 0x1800E6410),
                  ("mgr_merge_1356c0", 0x1801356C0),
                  ("chem_134cb0", 0x180134CB0),
                  ("clubtype_12ec50", 0x18012EC50),
                  ("clubstats_12f4f0", 0x18012F4F0),
                  ("consum_1308c0", 0x1801308C0),
                  ("clubsearch_12ddf0", 0x18012DDF0)):
        s = dec(a, 600)
        src[nm] = s
        bodies.append("=" * 78)
        bodies.append("### %s @ %#x len=%d" % (nm, a, len(s)))
        bodies.append(s)
    open(OUT + "v4_bodies.txt", "w").write("\n".join(bodies))
    print("WROTE v4_bodies.txt")

    for nm in ("clubtype_12ec50", "clubstats_12f4f0"):
        s = src[nm]
        cases = re.findall(r"case\s+(0x[0-9a-fA-F]+|\d+):", s)
        eq = re.findall(r"==\s*(0x[0-9a-fA-F]+|\d+)", s)
        ne = re.findall(r"!=\s*(0x[0-9a-fA-F]+|\d+)", s)
        sub = re.findall(r"-\s*(0x[0-9a-fA-F]+|\d+)U?\s*<", s)
        print("\n%s len=%d  cases=%d %s\n   ==%s  !=%s  ladders=%s"
              % (nm, len(s), len(cases), cases, eq, ne, sub))

    # ---- D: exhaustive instruction walk for [reg+0x54]
    print("\n==== EVERY instruction in .text referencing [reg + 0x54] ====")
    blk = [b for b in mem.getBlocks() if b.getName() == ".text"][0]
    it = listing.getInstructions(blk.getStart(), True)
    n = 0
    found = []
    while it.hasNext():
        ins = it.next()
        if ins.getAddress().getOffset() > int(blk.getEnd().getOffset()):
            break
        n += 1
        t = str(ins)
        if "+ 0x54]" in t:
            f = fm.getFunctionContaining(ins.getAddress())
            found.append((int(ins.getAddress().getOffset()), t,
                          f.getName() if f else "?"))
    print("instructions walked: %d ; hits: %d" % (n, len(found)))
    byf = {}
    for a, t, fn in found:
        byf.setdefault(fn, []).append("%#x %s" % (a, t))
    for fn in sorted(byf):
        print("  %-26s %s" % (fn, "; ".join(byf[fn])))
except Exception:
    traceback.print_exc()
