"""Q17: enumerate EVERY switch case label in the binary, then find the ones that
distinguish club subtypes.

Q5's scalar scan failed its control because jump-table case labels are not
instruction immediates. Ghidra, however, names them: it creates symbols of the form
switchD_<addr>_caseD_<n> (and caseD_<n>) at each case target. Walking the symbol
table therefore enumerates switch dispatch in the one form a scalar scan cannot see.

Report every function whose case-value set intersects the club-subtype candidates
{0x1e,0x1f,9,10,11,0x91..0x96} and print the full case set for each.

CONTROL: FUN_1800d8330 must appear with case labels including 0x1e, 0x1f, 0x91..0x96,
0xe7..0xe9 and 0xec. If it does not, the symbol-based enumeration is broken and no
absence claim may be made from it.
"""
import re
import traceback

try:
    st = prog.getSymbolTable()
    it = st.getAllSymbols(True)
    pat = re.compile(r"caseD_([0-9a-fA-F]+)$")
    per = {}
    n = 0
    while it.hasNext():
        s = it.next()
        m = pat.search(s.getName())
        if not m:
            continue
        n += 1
        try:
            v = int(m.group(1), 16)
        except ValueError:
            continue
        f = fm.getFunctionContaining(s.getAddress())
        key = (f.getName(), int(f.getEntryPoint().getOffset())) if f else ("?", 0)
        per.setdefault(key, set()).add(v)
    print("case labels found: %d in %d functions" % (n, len(per)))

    CAND = {0x1E, 0x1F, 9, 10, 11, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96}
    print()
    print("=== functions whose case set meets the club-subtype candidates ===")
    rows = []
    for (name, ent), vals in per.items():
        inter = vals & CAND
        if len(inter) >= 2:
            rows.append((len(inter), name, ent, vals))
    rows.sort(reverse=True)
    for k, name, ent, vals in rows:
        print("  %-26s %#x  hits=%d  cases=%s" %
              (name, ent, k, sorted("%#x" % v for v in vals)))
    print()
    print("=== control: FUN_1800d8330 ===")
    for (name, ent), vals in per.items():
        if ent == 0x1800D8330:
            print("  YES cases=%s" % sorted("%#x" % v for v in vals))
except Exception:
    traceback.print_exc()
