"""HYPOTHESIS: CardsDLL holds named-state literals for online/connection state, in the
same family as IS_TRADING_ENABLED (an OUTPUT name published by FUN_18006cc60).
If an ONLINE/CONNECT/SESSION named state exists, the UI's market refusal and the
Seasons refusal may both read it.

CONTROL: IS_TRADING_ENABLED (0x1801fc118) MUST appear in the enumeration, with exactly
one rip-relative xref (the lea in the publisher). If the enumeration misses it, the
enumeration is broken.

Enumerates .rdata ASCII literals matching online-ish tokens, and for each prints
xref count + containing functions.
"""
import re, traceback

try:
    blocks = {}
    for b in mem.getBlocks():
        blocks[str(b.getName())] = (int(b.getStart().getOffset()), int(b.getEnd().getOffset()))
    print("BLOCKS:", {k: ("%#x-%#x" % v) for k, v in blocks.items()})

    def block_bytes(name):
        s, e = blocks[name]
        out = bytearray()
        a = s
        while a <= e:
            n = min(1 << 20, e - a + 1)
            out += read_bytes(a, n)
            a += n
        return s, bytes(out)

    rs, rdata = block_bytes(".rdata")
    ds, data = block_bytes(".data")
    ts, text = block_bytes(".text")
    print("LEN .rdata=%d .data=%d .text=%d" % (len(rdata), len(data), len(text)))

    TOKENS = [b"ONLINE", b"OFFLINE", b"RECONNECT", b"RE-CONNECT", b"CONNECT",
              b"DISCONNECT", b"SESSION", b"HEARTBEAT", b"PING", b"NUCLEUS",
              b"PERSONA", b"SEASON", b"UNAVAILABLE", b"UNREACHABLE",
              b"Online", b"Offline", b"Reconnect", b"reconnect", b"connected",
              b"isOnline", b"online"]

    strre = re.compile(rb"[\x20-\x7e]{5,120}")
    found = {}
    for blkname, base, buf in ((".rdata", rs, rdata), (".data", ds, data)):
        for m in strre.finditer(buf):
            s = m.group()
            if not any(t in s for t in TOKENS):
                continue
            # require NUL termination to be a real C string
            end = m.end()
            if end < len(buf) and buf[end] != 0:
                continue
            va = base + m.start()
            found.setdefault(va, (blkname, s.decode("latin1")))

    print("TOTAL candidate literals:", len(found))

    # rip-relative xref counting over .text, form-independent: find any 4-byte
    # displacement d such that (insn_end + d) == va. We approximate by scanning for
    # the exact 4-byte LE of (va - (ts + i + 4)) at each i -- too slow. Instead use
    # Ghidra's reference manager, and ALSO a raw disp scan for the control.
    def xr(va):
        try:
            return xrefs_to(va)
        except Exception:
            return []

    ctrl = 0x1801fc118
    print("\n=== CONTROL IS_TRADING_ENABLED %#x ===" % ctrl)
    print("  str:", repr(rd_str(ctrl)))
    print("  xrefs:", [(hex(a), t, n) for a, t, n, e in xr(ctrl)])
    print("  in enumeration:", ctrl in found)

    print("\n=== ENUMERATION (va | block | xrefcount | funcs | string) ===")
    for va in sorted(found):
        blk, s = found[va]
        x = xr(va)
        fns = sorted({n for a, t, n, e in x})
        print("%#x %s xr=%d %s | %s" % (va, blk, len(x), ",".join(fns[:5]), s))
except Exception:
    traceback.print_exc()
