"""VERIFY PASS 2.

Attacks:
  D1-4  displayGroup(0xd9) is an OBJECT {priority,value}, not an ARRAY
  D1-6  unopened(0x35d) is TOP-LEVEL in the pack element, not inside packContentInfo
  D1-13 seven "SKIP" keys are actually parsed
  D1-5  FUN_1800150d0 walks the same 0x158 array and matches "mypacks"
  D1-9  atom 0x20d packList is not a wire key  -- ATTACKED WITH A DIFFERENT METHOD:
        a raw byte scan of .text for the 4-byte immediate 0d 02 00 00, reporting
        the containing function and the two preceding opcode bytes. Their method
        was a decompile-based atlas of hasher callers; if the byte scan finds a
        dispatch site in a function their atlas missed, the claim falls.
        CONTROL for the scan: the same scan for 0x35e (unopenedPacks) MUST hit
        FUN_18013ec10, and for 0x2e5 (starterPack) MUST hit FUN_18014cc60.
  D1-15 model vtable 0x18021c2a0 slots
"""
import traceback

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres/v2_raw.txt"

try:
    lines = []
    def P(*a):
        s = " ".join(str(x) for x in a)
        print(s)
        lines.append(s)

    # ---------- immediate byte scan ----------
    def imm_scan(val, label, expect=None):
        pat = bytes([val & 0xFF, (val >> 8) & 0xFF, (val >> 16) & 0xFF, (val >> 24) & 0xFF])
        hits = find_all(pat, blocks=(".text",))
        seen = {}
        for h in hits:
            f = fm.getFunctionContaining(addr(h))
            if f is None:
                continue
            pre = read_bytes(h - 3, 3).hex()
            e = int(f.getEntryPoint().getOffset())
            seen.setdefault(e, []).append((h, pre))
        P("")
        P("--- imm_scan %s (0x%x) : %d raw hits in .text, %d containing functions"
          % (label, val, len(hits), len(seen)))
        for e in sorted(seen):
            P("    %-14s %s  sites=%s" % (fname(e), hex(e),
              ",".join("%x[pre=%s]" % (h, p) for h, p in seen[e][:6])))
        if expect is not None:
            P("    CONTROL expect %s present: %s" % (hex(expect), expect in seen))
        return seen

    imm_scan(0x35e, "unopenedPacks", expect=0x18013ec10)
    imm_scan(0x2e5, "starterPack", expect=0x18014cc60)
    imm_scan(0x20d, "packList")
    imm_scan(0x35d, "unopened")
    imm_scan(0x20c, "packContentInfo")

    # ---------- string xrefs, independent of the atlas ----------
    P("")
    P("--- literal xrefs")
    for lit in [b"packs/dreamsquad/dreamsquadpacklist.json\x00", b"mypacks\x00",
                b"RELOAD_CENTRAL_PANEL\x00", b"GOTO_STORE_MYPACK\x00",
                b"fcc_discardcoins\x00", b"/purchasegroup\x00", b"?ppInfo=true\x00"]:
        for a in find_all(lit):
            P("  %-42s @ %#x  xrefs=%s" % (lit[:-1].decode(), a,
              [(hex(x[0]), x[2]) for x in xrefs_to(a)]))

    # ---------- model vtable ----------
    P("")
    P("--- model vtable 0x18021c2a0 selected slots")
    for off in (0x160, 0x1f8, 0x480, 0x4e0, 0x4e8, 0x940, 0xa30, 0xa48, 0xc0, 0x120):
        try:
            t = qword(0x18021c2a0 + off)
            P("   +0x%03x -> %#x  %s" % (off, t, fname(t) if fm.getFunctionAt(addr(t)) else "<no func>"))
        except Exception as e:
            P("   +0x%03x ERR %s" % (off, e))

    # ---------- decompiles ----------
    for va, tag in [(0x18013af30, "D1-4/6/13 store pack element deser"),
                    (0x1800150d0, "D1-5 My Packs screen builder"),
                    (0x18002c3c0, "D1-6 pack tile view-model copy"),
                    (0x180123430, "D1-12 purchasegroup URL builder"),
                    (0x18011e120, "D1-7 model vt+0x4e0 setter"),
                    (0x18017fc20, "D1-9 packList file parser")]:
        f = func(va)
        src = dec(va)
        P("")
        P("=" * 70)
        P("### %s @ %#x fname=%s entry=%s len(src)=%d" %
          (tag, va, fname(va) if f else "?",
           hex(int(f.getEntryPoint().getOffset())) if f else "NONE", len(src)))
        P("=" * 70)
        P(src)

    with open(OUT, "w") as fh:
        fh.write("\n".join(lines))
    print("WROTE", OUT)
except Exception:
    traceback.print_exc()
