"""D3 store-price q2.

HYPOTHESES
 H5  Some function reads the 0x1a8 store-tile viewmodel's currency flags
     vm+0xb5 (has mtx) / vm+0xb6 (has coins) / vm+0xb7 (has points) and the
     formatted price string vm+0x168, and that function is the Scaleform push
     that supplies the "or %1s" argument.
 H6  DAT_1802de0d0 vtable slot +0x30 is the gate that disables the "points"
     currency branch in FUN_18002c3c0.

METHOD / CONTROL. For H5 I scan EVERY function in .text and collect the set of
scalar operands, then report functions whose scalar set contains the distinctive
triple {0xb5,0xb6,0xb7}. The positive control is that FUN_18002c3c0 (known to
write all three) MUST appear in the result; if it does not, the scan is broken and
every negative is worthless.
"""
import traceback

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

try:
    w = open(OUT + "/q2_raw.txt", "w")

    def p(*a):
        s = " ".join(str(x) for x in a)
        print(s)
        w.write(s + "\n")

    # ---------- H5 whole-.text scalar-set scan ----------
    want = {0xb5, 0xb6, 0xb7}
    hits = []
    nfun = 0
    it = fm.getFunctions(True)
    while it.hasNext():
        f = it.next()
        nfun += 1
        sc = set()
        ii = listing.getInstructions(f.getBody(), True)
        while ii.hasNext():
            ins = ii.next()
            for i in range(ins.getNumOperands()):
                for o in ins.getOpObjects(i):
                    try:
                        sc.add(int(o.getValue()) & 0xFFFFFFFFFFFFFFFF)
                    except Exception:
                        pass
        if want <= sc:
            hits.append((int(f.getEntryPoint().getOffset()), f.getName(),
                         0x168 in sc, 0xa4 in sc, 0xa8 in sc, 0xa0 in sc, 0x6c in sc))
    p("=" * 70)
    p("H5 scanned %d functions; %d contain the {0xb5,0xb6,0xb7} triple" % (nfun, len(hits)))
    p("CONTROL: FUN_18002c3c0 present? %s" %
      any(h[0] == 0x18002c3c0 for h in hits))
    p("%-12s %-24s %-7s %-6s %-6s %-6s %-5s" %
      ("entry", "name", "has168", "hasa4", "hasa8", "hasa0", "has6c"))
    for h in hits:
        p("%-12x %-24s %-7s %-6s %-6s %-6s %-5s" % h)

    # ---------- H6 the points gate ----------
    p("")
    p("=" * 70)
    p("H6 writers of DAT_1802de0d0")
    for a in (0x180013ed0, 0x180013f20):
        src = dec(a)
        p("")
        p("--- FUN_%x len=%d FULL" % (a, len(src)))
        p(src)

    p("")
    p("H6 the gate call site, FUN_18002c3c0 around 0x18002c77f")
    ii = listing.getInstructions(addr(0x18002c750), True)
    n = 0
    while ii.hasNext() and n < 40:
        ins = ii.next()
        p("   %x  %s" % (int(ins.getAddress().getOffset()), ins))
        n += 1

    # try to resolve the vtable of the gate object: find its ctor via the writer
    p("")
    p("H6 vtable candidates: qword at DAT_1802de0d0 in the static image = %#x" %
      qword(0x1802de0d0))

    # ---------- extra: literal 0x1801f04f0 / 0x1801f04f8 used by FUN_18002e680 ----------
    p("")
    p("=" * 70)
    p("entitlement-category literals used by FUN_18002e680")
    for a in (0x1801f04f0, 0x1801f04f8, 0x180228338, 0x1801eab80, 0x1802055ea):
        p("   %#x = %r" % (a, rd_str(a, 40)))

    # ---------- extra: CreatePack request serializer currency vocabulary ----------
    p("")
    p("=" * 70)
    p("CreatePack request serializer FUN_180162530 (writes MTX / POINTS literals)")
    src = dec(0x180162530)
    p("len=%d FULL" % len(src))
    p(src)

    w.close()
except Exception:
    traceback.print_exc()
    try:
        w.write(traceback.format_exc()); w.close()
    except Exception:
        pass
