"""DIMENSION 4 (duplicates), pass 7: the ONE flow that branches on the duplicate
flag, and the request it does or does not emit.

FOUND IN PASS 6: FUN_18009bc40 looks up an item by resourceId in the manager's
vt[0x160] container, reads item+0x10 (duplicateItemId) into a bool, and then:
    duplicate     -> UI command "GotoNewItems", NO server call
    not duplicate -> builds a 1-element FUT Vector of 0x20-byte records
                     {itemId, 7, 0, 0} and calls manager vt[0xc0] with the
                     callback FUN_18009bec0
This is the only branch on the flag in the whole binary (pass 6 was exhaustive
over 13308 functions with controls passing).

THIS PASS: name the actors.
  1. the concrete manager class: who calls FUN_18011d780 (the setter for the
     singleton pointer DAT_1802e6398) and with what object -> its vtable ->
     slots 0xc0, 0x160, 0x7d8, 0xa08.
  2. FUN_18009bec0 (the completion callback), FUN_18009c360 (the owner of the
     function pointer), FUN_18009b480 (the loan-player message builder that
     sits next to it in .text).
  3. what 7 means in the 0x20-byte record.

CONTROL for the vtable: print raw qwords and require slot 0 and 8 to resolve to
real functions before believing any slot; also print slot 0x160 and check it
looks like a small getter (the CreatePack deser calls it and then uses the
result+0x30 as a vector).
"""
import traceback

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store/dup7_out.txt"

def w(fh, s=""):
    fh.write(str(s) + "\n")

def dump(fh, a, label):
    s = dec(a)
    w(fh, "")
    w(fh, "#" * 70)
    w(fh, "# %s  %#x  len(src)=%d  FULL" % (label, a, len(s)))
    w(fh, "#" * 70)
    w(fh, s)

try:
    fh = open(OUT, "w")

    w(fh, "=== callers of FUN_18011d780 (singleton setter) ===")
    for frm, typ, fn, ent in xrefs_to(0x18011D780):
        w(fh, "   %#x %s in %s @ %#x" % (frm, typ, fn, ent))

    for frm, typ, fn, ent in xrefs_to(0x18011D780):
        if ent and "CALL" in typ:
            dump(fh, ent, "caller of singleton setter")

    dump(fh, 0x18009BEC0, "completion callback FUN_18009bec0")
    dump(fh, 0x18009C360, "owner of the fn-ptr FUN_18009c360")
    dump(fh, 0x18009B480, "loan-player message builder FUN_18009b480")

    w(fh, "")
    w(fh, "=== xrefs to FUN_18009bc40 users' neighbours: callers of FUN_18009c360 ===")
    for frm, typ, fn, ent in xrefs_to(0x18009C360):
        w(fh, "   %#x %s in %s @ %#x" % (frm, typ, fn, ent))

    fh.close()
    print("WROTE", OUT)
except Exception:
    traceback.print_exc()
