"""DIMENSION 4 (duplicates), pass 1.

HYPOTHESIS: duplicateItemIdList (atom 0xec, element deser 0x180138e10, 0x20-byte
records) is consumed by (a) the CreatePack deser 0x180162880 and (b) the shared
IS-list body 0x18013e7f0. In CreatePack the only fields read from each record are
+0x00 (itemId) and +0x10 (duplicateItemId); the loans fields look dead there.
We want every caller and the exact per-caller consumption.

CONTROL: FUN_180138e10 must appear as a called-function of every caller we claim,
and we also enumerate xrefs by the reference manager (not by grepping text), so
"absent" verdicts are not derived from a text search. Additionally we print
len(src) for every decompile and state FULL/TRUNCATED.
"""
import traceback

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

def w(fh, s=""):
    fh.write(str(s) + "\n")

try:
    fh = open(OUT, "w")

    w(fh, "=" * 70)
    w(fh, "A. xrefs_to(0x180138e10)  -- the duplicateItemIdList element deser")
    w(fh, "=" * 70)
    for frm, typ, fn, ent in xrefs_to(0x180138e10):
        w(fh, "  from %#x  %-14s in %s @ %#x" % (frm, typ, fn, ent))

    w(fh, "")
    w(fh, "=" * 70)
    w(fh, "B. xrefs to the ATOM constant 0xec is meaningless (too common);")
    w(fh, "   instead: callers of each caller, to place the consumers.")
    w(fh, "=" * 70)
    callers_of_deser = sorted(set(e for _, _, _, e in xrefs_to(0x180138e10) if e))
    for c in callers_of_deser:
        w(fh, "-- callers of %#x (%s):" % (c, fname(c) if callable(globals().get("fname")) else "?"))
        for frm, typ, fn, ent in xrefs_to(c):
            w(fh, "     %#x %s in %s @ %#x" % (frm, typ, fn, ent))

    w(fh, "")
    w(fh, "=" * 70)
    w(fh, "C. FULL decompile: IS-list shared body 0x18013e7f0")
    w(fh, "=" * 70)
    s = dec(0x18013E7F0)
    w(fh, "// len(src)=%d  FULL (printed in its entirety, no truncation)" % len(s))
    w(fh, s)

    w(fh, "")
    w(fh, "=" * 70)
    w(fh, "D. FULL decompile of every OTHER caller found in A")
    w(fh, "=" * 70)
    for c in callers_of_deser:
        if c in (0x18013E7F0, 0x180162880):
            w(fh, "// %#x printed elsewhere / already known" % c)
            continue
        s = dec(c)
        w(fh, "---- %#x  len(src)=%d  FULL ----" % (c, len(s)))
        w(fh, s)

    w(fh, "")
    w(fh, "=" * 70)
    w(fh, "E. the item-model singleton FUN_18011a830 and its vtable slot 0xa08")
    w(fh, "   (CreatePack reaches the item via node+0x10 which this slot sets)")
    w(fh, "=" * 70)
    s = dec(0x18011A830)
    w(fh, "// FUN_18011a830 len(src)=%d FULL" % len(s))
    w(fh, s)

    w(fh, "")
    w(fh, "=" * 70)
    w(fh, "F. strings containing dup/Dup/DUP/loan/Loan/LOAN/swap/Swap/SWAP in rdata")
    w(fh, "=" * 70)
    for pat in (b"uplicate", b"UPLICATE", b"oanItem", b"LOAN", b"Loan", b"SWAP", b"Swap"):
        hits = find_all(pat, blocks=(".rdata", ".data", ".text"))
        w(fh, "-- pattern %r : %d hits" % (pat, len(hits)))
        seen = set()
        for h in hits[:400]:
            # walk back to string start
            st = h
            for _ in range(120):
                try:
                    b = mem.getByte(addr(st - 1)) & 0xFF
                except Exception:
                    break
                if b < 0x20 or b > 0x7E:
                    break
                st -= 1
            if st in seen:
                continue
            seen.add(st)
            txt = rd_str(st, 160)
            xr = xrefs_to(st)
            w(fh, "   %#x  %-60r  xrefs=%d %s" % (st, txt, len(xr),
                  ",".join("%#x/%s" % (e, fn) for _, _, fn, e in xr[:6])))

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