"""D3 run 7: the RPC descriptor row's handler, and the end of the pack-record trail.

FOUND IN RUN 6: a descriptor table in .data at ~0x1802cb800 with 0x30-byte rows
  [display-name ptr, 0x1b, COMMAND-token ptr, 0, 0, function ptr]
  0x1802cb860 "PurchaseItems"      "PURCHASEITEMS"       -> 0x180124240
  0x1802cb890 "StorePackTypes"     "STOREPACKTYPES"      -> 0x180124810
  0x1802cb8c0 "StorePackQuantities" "STOREPACKQUANTITIES" -> ?
Also a factory table at 0x18021ddf8 holding 0x180123480.
CardsDLL exports only PlugInitialize_ / PlugDeinitialize_ / entry, so everything the
packed exe can see comes through interfaces those hand out.

THIS RUN
  1. Walk the descriptor table rows around 0x1802cb800 +/- 0x300 and print each row.
  2. Decompile the StorePackTypes handler 0x180124810 and the CreatePack handler,
     then follow their callees/callers, looking for anything that touches the
     response object's vector at +0x28.
  3. Same for the CreatePack response: who reads numberItems at obj+0x28 or the
     itemList vector at obj+0x30/0x38, i.e. what sizes the reveal.
CONTROL: 0x180124810 must decompile to something that mentions the store response
  factory 0x180123480 or the vtable 0x18021dd68 or the path string "store"; if it
  looks unrelated the table row reading is wrong.
"""
import traceback, sys, os

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres/"
os.makedirs(OUT, exist_ok=True)


def dump(tag, va, path, echo=True):
    src = dec(va)
    hdr = "%s  %#x  fname=%s  len(src)=%d  (FULL, NOT TRUNCATED)" % (
        tag, va, fname(va), len(src))
    if echo:
        print("=" * 78)
        print(hdr)
        print("=" * 78)
        print(src)
    with open(path, "w") as fh:
        fh.write("// " + hdr + "\n" + src)
    return src


def sstr(q):
    if 0x1801e5000 <= q < 0x1802e0000:
        try:
            s = rd_str(q, 64)
            if s and all(32 <= ord(c) < 127 for c in s):
                return s
        except Exception:
            pass
    return None


try:
    print("### 1. DESCRIPTOR TABLE WALK")
    base = 0x1802cb500
    for row in range(0, 0x600, 0x30):
        a = base + row
        try:
            qs = [qword(a + i * 8) for i in range(6)]
        except Exception:
            continue
        n0, n2 = sstr(qs[0]), sstr(qs[2])
        if not (n0 and n2):
            continue
        fn = qs[5]
        print("  %#x  %-24s %-24s flags=%#x  fn=%#x %s"
              % (a, n0, n2, qs[1], fn, fname(fn) if fn else ""))

    print("\n### 2. HANDLERS")
    seen = set()
    for va, tag in ((0x180124810, "StorePackTypes handler"),
                    (0x180124240, "PurchaseItems handler")):
        dump(tag, va, OUT + "d3_h_%x.txt" % va, echo=True)
        print("  callees:")
        for a, n in callees(va):
            print("    %#x %s" % (a, n))
        print("  callers:")
        for a, n in callers(va):
            print("    %#x %s" % (a, n))
        seen.add(va)

    print("\n### 3. WHO ELSE MENTIONS THE STORE FACTORY / VTABLE / FACTORY TABLE SLOT")
    for tgt in (0x18021ddf8, 0x18021dd68, 0x180123480, 0x1801234e0, 0x180123030):
        print("  xrefs to %#x:" % tgt)
        for frm, typ, fn, ent in xrefs_to(tgt):
            print("      %#x %s %s" % (frm, typ, fn))

    print("\n### 4. CREATEPACK RESPONSE CONSUMERS")
    dump("FutCreatePack ctor", 0x180162420, OUT + "d3_cp_ctor.txt", echo=True)
    print("  callers of ctor: %s" % [(hex(a), n) for a, n in callers(0x180162420)])
    dump("FutCreatePack factory 0x180162770", 0x180162770, OUT + "d3_cp_factory.txt",
         echo=True)
    print("  callers of factory: %s" % [(hex(a), n) for a, n in callers(0x180162770)])
    for tgt in (0x180162770, 0x180162880, 0x180228260):
        pat = tgt.to_bytes(8, "little")
        for h in find_all(pat, blocks=(".rdata", ".data")):
            print("  %#x embedded at %#x" % (tgt, h))
except Exception:
    traceback.print_exc()
    sys.stdout.flush()
