# -*- coding: utf-8 -*-
"""
q_pack_inv_10 -- DIMENSION 1 pass 10: the READERS of the unopenedPacks total.

CHAIN ESTABLISHED SO FAR
  userInfo.unopenedPacks{preOrderPacks,recoveredPacks}
    -> FUN_18013ec10 sums them and calls model->vtbl[0x4e0] @0x18013f223
    -> FUN_18011e120 stores the sum at model+0x20950 and broadcasts event 0x273d

A byte scan of .text (modrm mod=10, disp32 == 0x20950) finds exactly THREE accesses:
  0x18011e131  the setter itself
  0x18011c202  inside 0x18011c1f0, which is vtable slot +0x4e8 -> the GETTER
  0x18010e06d  the only other reader
and the event id 0x273d appears at 0x18011e159 (the broadcast) plus 0x1800b3946,
0x18007e861, 0x180199e08.

HYPOTHESIS: those four addresses are the complete client-side consumer set.

CONTROLS
  * class_deser("FutSquadSaveServerResponse") -> 0x180171a60.
  * 0x18011c1f0 must be vtable(0x18021c2a0) slot +0x4e8 (it is, per the live read),
    and its body must READ +0x20950 -- if it writes, the getter/setter call is
    inverted and the reader analysis is void.
"""
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("CONTROL vtable+0x4e8 = %#x (expect 0x18011c1f0)" % qword(0x18021C2A0 + 0x4E8))

    for va, tag in ((0x18011C1F0, "getter_4e8"), (0x18010E06D, "reader_18010e06d"),
                    (0x1800B3946, "evt_1800b3946"), (0x18007E861, "evt_18007e861"),
                    (0x180199E08, "evt_180199e08")):
        f = func(va)
        print("\n########## %s  addr %#x  in %s @%#x ##########"
              % (tag, va, f.getName() if f else "?",
                 int(f.getEntryPoint().getOffset()) if f else 0))
        s = dec(va)
        print("len=%d" % len(s))
        print(s)
        open(OUT + "/d1_%s.txt" % tag, "w").write(s)

    print("\n########## callers of the getter 0x18011c1f0 ##########")
    for (fr, ty, fn, en) in xrefs_to(0x18011C1F0):
        print("   %#x %-10s %s @%#x" % (fr, ty, fn, en))

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