"""D5 finishing pass: CreatePack's URL/descriptor, the pack-record constructor
defaults, the generic HTTP-error path, and the FUT store service that picks the mode.

HYPOTHESES
 H6: FUN_1801342d0 is the pack-record constructor and its stores give the DEFAULT
     value of every pack field when the server omits the key (critical for Q4:
     what an omitted purchaseLimit/quantity/state means).
 H7: the CreatePack ServerCall's URL + body builders live in a static descriptor
     like the CARDPACK one at 0x1801f0458; find it by scanning .rdata/.data for the
     qword 0x180162530.
 H8: FUN_1801844c0 (reached from the generic slot-12 HTTP handler FUN_18016c060)
     maps an HTTP status/body to a FUT error code; that is the whole error path.

CONTROL: dec(0x180162880) must be the FutCreatePack deserializer (contains atom
0xbe / a SAX loop). Printed with its length.
"""
import traceback, os, struct

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

def w(name, text):
    p = os.path.join(OUT, name)
    with open(p, "w") as f:
        f.write(text)
    print("WROTE %s (%d bytes)" % (p, len(text)))

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

    s = dec(0x180162880)
    P("CONTROL dec(0x180162880) len=%d sax=%s" % (len(s), "FUN_1801c7f10" in s))

    P("")
    P("=== scan for descriptor qwords ===")
    for tgt, tag in ((0x180162530, "createpack_req_ser"),
                     (0x180162770, "createpack_resp_factory"),
                     (0x180123480, "packtypes_resp_factory"),
                     (0x1801756d0, "packquantities_?"),
                     (0x180126440, "purchaseitems_req_ser")):
        pat = struct.pack("<Q", tgt)
        hits = find_all(pat, (".rdata", ".data"))
        P("  %s %#x -> %d hits: %s" % (tag, tgt, len(hits), ["%#x" % h for h in hits]))
        for h in hits:
            for j in range(-8, 6):
                a = h + j * 8
                try:
                    q = qword(a)
                except Exception:
                    continue
                f = fm.getFunctionAt(addr(q)) if 0x180000000 <= q < 0x181000000 else None
                extra = ""
                try:
                    raw = read_bytes(a, 8)
                    if raw[0] != 0 and all(32 <= b < 127 or b == 0 for b in raw):
                        extra = "ascii %r" % raw
                except Exception:
                    pass
                if f is None and 0x180000000 <= q < 0x181000000:
                    try:
                        t = rd_str(q, 40)
                        if t and all(32 <= ord(c) < 127 for c in t):
                            extra += " ->str %r" % t
                    except Exception:
                        pass
                P("     %+3d %#x -> %#x %s %s" % (j, a, q, f.getName() if f else "", extra))
            P("     ---")

    P("")
    P("=== url string xrefs ===")
    for va, nm in ((0x18021e670, "ut/%s/store"),
                   (0x18021e860, "ut/v2/%s/store"),
                   (0x18021e650, "ut/%s/purchased"),
                   (0x18021de48, "/purchasegroup"),
                   (0x1802203e8, "/transaction"),
                   (0x180223110, "CREATEPACK"),
                   (0x18021f2f8, "PURCHASEITEMS")):
        try:
            xs = xrefs_to(va)
        except Exception as e:
            P("  %s ERR %s" % (nm, e)); continue
        P("  %-18s %#x : %s" % (nm, va, [("%#x" % f, n) for f, t, n, e2 in xs]))

    w("d5_q4_notes.txt", "\n".join(buf) + "\n")

    TG = {
        "pack_record_ctor_1801342d0": 0x1801342d0,
        "pack_record_copy_1801340e0": 0x1801340e0,
        "pack_helper_180133210": 0x180133210,
        "pack_helper_18012c990": 0x18012c990,
        "http_err_1801844c0": 0x1801844c0,
        "storeservice_180199bf0": 0x180199bf0,
        "purchase_ui_1800a5650": 0x1800a5650,
        "purchase_ui_1800a5f90": 0x1800a5f90,
        "packpile_1800dd300": 0x1800dd300,
        "call_ctor_1801623d0": 0x1801623d0,
        "call_ctor_1801263a0": 0x1801263a0,
        "call_ctor_1801263f0": 0x1801263f0,
        "vt9_180122420": 0x180122420,
        "vt_18016ca60": 0x18016ca60,
        "vt_18016c950": 0x18016c950,
        "vt_1801631e0": 0x1801631e0,
        "fp_store_18002dd40": 0x18002dd40,
        "fp_store_18002ecb0": 0x18002ecb0,
        "fp_store_18002fd40": 0x18002fd40,
        "fp_store_18002fff0": 0x18002fff0,
        "fp_store_18002dca0": 0x18002dca0,
    }
    for tag, va in sorted(TG.items()):
        try:
            src = dec(va)
        except Exception as e:
            src = "// ERR %s" % e
        w("d5_q4_dec_%s.txt" % tag, "// %s %#x len=%d\n" % (tag, va, len(src)) + src)

    w("d5_q4_notes.txt", "\n".join(buf) + "\n")
    print("DONE")
except Exception:
    traceback.print_exc()
