# -*- coding: utf-8 -*-
"""
q_pack_inv_1 -- DIMENSION 1 (pack inventory) pass 1.

HYPOTHESIS
  (a) FutCreateUserServerResponse deser 0x18014cc60 dispatches atom 0x2e5
      (starterPack) and 0x5d (bonusPacks) to dedicated sub-deserializers whose
      addresses appear in its decompile.
  (b) userInfo deser 0x18013ec10 dispatches atom 0x35e (unopenedPacks) to a
      sub-deserializer.
  (c) The image contains response classes beyond those in ENDPOINT_MAP.md; a
      census of b"RS4:Fut" enumerates them all.
  (d) Route/format fragments "ut/%s/", "purchased", "unassigned", "purchasegroup"
      appear as literals whose xrefs name the builder functions.

CONTROLS
  * class_deser("FutSquadSave") must return 0x180171a60,
    class_deser("FutSquadList") -> 0x180172140,
    class_deser("FutCreateMatch") -> 0x180120380.  If these fail the batch is void.
  * b"RS4:FutSquadSave" must be found by the census scan (known to exist at
    static 0x18022c618 per the ground-truth controls).
  * Every decompile prints len(src) FIRST so no absence is concluded from a
    truncated body.
"""
import traceback

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

try:
    print("########## CONTROL BLOCK ##########")
    for nm, want in (("FutSquadSave", 0x180171a60),
                     ("FutSquadList", 0x180172140),
                     ("FutCreateMatch", 0x180120380)):
        r = class_deser(nm)
        got = sorted(set(x[0] for x in r))
        print("CONTROL class_deser(%-16s) -> %s   expect %#x  %s"
              % (nm, [hex(g) for g in got], want,
                 "PASS" if want in got else "FAIL"))

    print("\n########## Q3a  RS4:Fut CENSUS ##########")
    hits = find_all(b"RS4:Fut", blocks=(".rdata", ".data", ".text"))
    names = {}
    for h in hits:
        s = rd_str(h, 120)
        nm = s[4:]
        names.setdefault(nm, []).append(h)
    print("raw hits: %d   distinct names: %d" % (len(hits), len(names)))
    ctl = "FutSquadSave" in names
    print("CONTROL census contains FutSquadSave: %s" % ctl)
    for nm in sorted(names):
        print("  %-60s %s" % (nm, [hex(a) for a in names[nm]]))

    # also catch RS4: names that do not start with Fut, for completeness
    print("\n########## Q3a-bis  ALL RS4: CLASS NAMES ##########")
    hits2 = find_all(b"RS4:", blocks=(".rdata", ".data", ".text"))
    n2 = {}
    for h in hits2:
        s = rd_str(h, 120)[4:]
        if not s:
            continue
        n2.setdefault(s, []).append(h)
    print("raw hits: %d   distinct: %d" % (len(hits2), len(n2)))
    for nm in sorted(n2):
        if not nm.startswith("Fut"):
            print("  %-60s %s" % (nm, [hex(a) for a in n2[nm]]))

    print("\n########## Q3b  ROUTE FRAGMENTS ##########")
    for frag in (b"ut/%s/", b"purchased", b"unassigned", b"purchasegroup",
                 b"unopened", b"gift", b"entitlement", b"reward"):
        hs = find_all(frag, blocks=(".rdata", ".data", ".text"))
        print("\n--- %r : %d hits" % (frag, len(hs)))
        for h in hs[:80]:
            try:
                s = rd_str(h - 0 if True else h, 140)
            except Exception:
                s = "?"
            # back up to string start (previous NUL) for context
            start = h
            for k in range(1, 90):
                try:
                    if (mem.getByte(addr(h - k)) & 0xFF) == 0:
                        start = h - k + 1
                        break
                except Exception:
                    break
            full = rd_str(start, 200)
            xr = xrefs_to(start)
            print("  @%#x start=%#x  %r" % (h, start, full))
            for (fr, ty, fn, en) in xr[:8]:
                print("        ref %#x %-10s %s @%#x" % (fr, ty, fn, en))

    print("\n########## Q1  FutCreateUserServerResponse deser 0x18014cc60 ##########")
    src = dec(0x18014cc60)
    print("len(src) = %d  (FULL BODY FOLLOWS, untruncated)" % len(src))
    print(src)
    open(OUT + "/d1_createuser_18014cc60.txt", "w").write(src)

    print("\n########## Q2  userInfo deser 0x18013ec10 ##########")
    src2 = dec(0x18013ec10)
    print("len(src) = %d  (FULL BODY FOLLOWS, untruncated)" % len(src2))
    print(src2)
    open(OUT + "/d1_userinfo_18013ec10.txt", "w").write(src2)

    print("\n########## Q4  pack element deser 0x18013af30 ##########")
    src3 = dec(0x18013af30)
    print("len(src) = %d  (FULL BODY FOLLOWS, untruncated)" % len(src3))
    print(src3)
    open(OUT + "/d1_packelem_18013af30.txt", "w").write(src3)

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