"""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(" %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()