"""ADVERSARIAL BATCH 5 -- consequences of the one serve-changing action, and the
service gate the other agent left open.

 1. untradeable:false flips item+0x49 to 1 on EVERY card. Besides TO_TRADE_PILE that
    byte feeds FUN_1800bc580, which counts untradeable members of the 11-slot active
    squad. Who consumes that count, and does flipping it change anything else?
 2. FUN_1801a7260's other gate: slot +0x270 of the service FUN_180009c80 resolves.
    Identify the service vtable and that slot if possible.
"""
import traceback, struct

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/cards/adv2/q5_raw.txt"
try:
    f = open(OUT, "w")

    def P(*a):
        f.write(" ".join(str(x) for x in a) + "\n")

    P("=" * 25, "1. consumers of the untradeable-squad count", "=" * 25)
    for a in (0x1800BB2A0, 0x1800BBA10):
        d = dec(a, timeout=600)
        P("### %#x len=%d" % (a, len(d)))
        P(d)
        P()

    P("=" * 25, "2. the service locator used by FUN_1801a7260", "=" * 25)
    for nm, a in (("FUN_1800d7170", 0x1800D7170), ("FUN_180009c80", 0x180009C80),
                  ("FUN_180018bd0", 0x180018BD0), ("FUN_180009b60", 0x180009B60)):
        P("### " + nm)
        P(dec(a))
        P()

    P("=" * 25, "3. any vtable with >= 0x280 bytes containing plausible slot 0x270", "=" * 25)
    # find .rdata runs of >= 0x50 consecutive .text pointers; report those long enough
    for b in mem.getBlocks():
        if b.getName() != ".rdata" or not b.isInitialized():
            continue
        s = int(b.getStart().getOffset())
        e = int(b.getEnd().getOffset())
        a = (s + 7) & ~7
        run_start = None
        while a + 8 <= e:
            try:
                v = qword(a)
            except Exception:
                break
            ok = 0x180001000 <= v < 0x1801E5000
            if ok and run_start is None:
                run_start = a
            elif not ok and run_start is not None:
                ln = a - run_start
                if ln >= 0x280:
                    P("  vtable-ish run %#x..%#x  len %#x   slot+0x270 -> %#x %s" %
                      (run_start, a, ln, qword(run_start + 0x270),
                       (lambda fn: fn.getName() if fn else "")(fm.getFunctionAt(addr(qword(run_start + 0x270))))))
                run_start = None
            a += 8

    f.close()
    print("WROTE", OUT)
except Exception:
    traceback.print_exc()
