# -*- coding: utf-8 -*-
"""
q_pack_inv_3 -- DIMENSION 1 pass 3: the consumers and the route table.

HYPOTHESES
  (a) .rdata around 0x18021e100 is a ROUTE TABLE (command -> "ut/%s/..." literal);
      dumping it enumerates every HTTP path CardsDLL can build, which answers Q3
      exhaustively rather than by keyword luck.
  (b) 0x1802cb800.. is the matching COMMAND-NAME table (PurchasedItems,
      PurchasePack, StorePackTypes, StorePackQuantities were all found there).
  (c) The unopenedPacks total is pushed through model->vtbl[0x4e0]; the vtable can
      be reached from the singleton storage DAT_1802e6398, and the reader is
      another slot on the same vtable.
  (d) Functions flagged HOT for atoms 0x35e/0x24b/0x20c in pass 2 are further
      deserializers that touch pack inventory.

CONTROLS
  * The route-table dump MUST contain "ut/%s/squad" and "ut/%s/item", two routes we
    already serve. If it does not, the table base is wrong.
  * class_deser("FutSquadSaveServerResponse") is re-run and must still be
    0x180171a60 (guards against a stale/corrupt project).
  * Each decompile prints len() first.
"""
import struct
import traceback

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

try:
    got = sorted(set(x[0] for x in class_deser("FutSquadSaveServerResponse")))
    print("CONTROL FutSquadSaveServerResponse -> %s  %s"
          % ([hex(g) for g in got], "PASS" if 0x180171a60 in got else "FAIL"))

    print("\n########## ROUTE TABLE DUMP .rdata 0x18021df00..0x18021e300 ##########")
    for va in range(0x18021df00, 0x18021e300, 8):
        try:
            q = qword(va)
        except Exception:
            continue
        s = ""
        if 0x180001000 <= q <= 0x1802efc08:
            try:
                s = rd_str(q, 120)
            except Exception:
                s = ""
        if s and s.isprintable() and len(s) > 1:
            print("  %#x -> %#x  %r" % (va, q, s))
    print("CONTROL route table contains ut/%s/squad and ut/%s/item: see above")

    print("\n########## COMMAND-NAME TABLE .rdata/.data 0x1802cb600..0x1802cbb00 ##########")
    for va in range(0x1802cb600, 0x1802cbb00, 8):
        try:
            q = qword(va)
        except Exception:
            continue
        s = ""
        if 0x180001000 <= q <= 0x1802efc08:
            try:
                s = rd_str(q, 120)
            except Exception:
                s = ""
        if s and s.isprintable() and len(s) > 1:
            print("  %#x -> %#x  %r" % (va, q, s))

    print("\n########## SINGLETON STORAGE DAT_1802e6398 ##########")
    for (fr, ty, fn, en) in xrefs_to(0x1802e6398):
        print("   %#x %-10s %s @%#x" % (fr, ty, fn, en))

    print("\n########## call [reg+0x4e0] SITES ##########")
    for modrm in (0x90, 0x91, 0x92, 0x93, 0x96, 0x97):
        pat = bytes([0xFF, modrm]) + struct.pack("<I", 0x4E0)
        for h in find_all(pat, blocks=(".text",)):
            f = fm.getFunctionContaining(addr(h))
            print("   %#x  in %s @%#x" % (h, f.getName() if f else "?",
                                          int(f.getEntryPoint().getOffset()) if f else 0))

    DECS = [
        (0x180136c90, "atom0x35e_HOT"),
        (0x18013df20, "atom0x24b_HOT"),
        (0x18013c0e0, "atom0x20c_HOT"),
        (0x1801680b0, "atom0x20c_HOT2"),
        (0x180137ea0, "atom0x1a5_HOT"),
        (0x180124ee0, "FutGetPurchasedItems_deser"),
        (0x180123430, "purchasegroup_builder"),
        (0x1800b2680, "CentralUnclaimedPack"),
        (0x18002fff0, "items_pack_body"),
        (0x180083990, "starting_pack_opened"),
        (0x1800150d0, "mypacks_ui"),
    ]
    for va, tag in DECS:
        print("\n########## %s  %#x ##########" % (tag, va))
        s = dec(va)
        print("len=%d" % len(s))
        print(s)
        open(OUT + "/d1_%s_%x.txt" % (tag, va), "w").write(s)

    print("\nDONE q_pack_inv_3")
except Exception:
    traceback.print_exc()
