"""D2 QUICK SELL, batch 5: find the SECOND fcc_discardcoins computation site.

THE CONTRADICTION THIS RESOLVES.
Measured live (read-only, pid 134663): persistent FUT item objects carry, at
item+0x3c, exactly round_half_up(item_rating * price / 100) where price comes from
the fcc_discardcoins row (cardtype = item+0x4c, level = item+0x54, rare = item+0x58).
Two items with identical cardtype 6 / rare 0 but item+0x54 = 1 and 3 got 3 and 38,
which is only explicable if the level key really varies per item.
BUT inside FUN_18013fe00 the slot that feeds the "level" argument, [RBP+0x1b4], is
provably never written: a whole-DLL byte-pattern search for a modrm with
mod=10 rm=101 disp32=0x000001b4 finds exactly one operand in that function and it
is the LOAD at 0x18014109a (44 8b 8d b4 01 00 00). So a SECOND site must exist.

SEARCHES (all four dispatch forms considered; this is a byte/immediate search, not
a "== 0x" grep)
  A. every function containing the divide-by-100 magic B8 1F 85 EB 51
     (MOV EAX,0x51eb851f) or 0x51eb851f in any instruction, cross-referenced with
     whether it also calls the db-query wrappers.
  B. xrefs to the four literals "price" 0x1802231e4, "level" 0x180207848,
     "cardtype" 0x180223208, "rare" 0x18022315c, "fcc_discardcoins" 0x1802231f0.
  C. every caller of the db wrappers FUN_1801a0000 (from-table) and FUN_18019ff00
     (where) and FUN_1801a0080 (get cell).
  D. what writes item+0x54? search the whole .text for a dword store with
     disp8/disp32 0x54 is hopeless, so instead: decompile the manager entry
     mgr->vt[0xa08] target reached from FUN_18013fe00 and look for a level/tier
     computation, and decompile the tier helper FUN_1800a9fe0 that an earlier
     agent found returning 1/2/3.

CONTROL: search A must report FUN_18013fe00 (it contains MOV EAX,0x51eb851f at
0x180141123). Search B must report the single known xref 0x18014106d for
fcc_discardcoins. If either control misses, the search is broken.
"""
import traceback

try:
    print("##### CONTROL + A: functions containing the /100 magic 0x51eb851f #####")
    hits = {}
    it = fm.getFunctions(True)
    n = 0
    while it.hasNext():
        f = it.next()
        n += 1
        ii = listing.getInstructions(f.getBody(), True)
        got = []
        while ii.hasNext():
            i = ii.next()
            t = str(i)
            if "0x51eb851f" in t:
                got.append((int(i.getAddress().getOffset()), t))
        if got:
            hits[int(f.getEntryPoint().getOffset())] = (f.getName(), got)
    print("  scanned %d functions, %d contain the magic" % (n, len(hits)))
    print("  FUN_18013fe00 present: %s" % (0x18013fe00 in hits))
    for e in sorted(hits):
        print("   %#x %s  (%d sites)" % (e, hits[e][0], len(hits[e][1])))

    print("\n##### B: xrefs to the query literals #####")
    for nm, a in (("price", 0x1802231e4), ("level", 0x180207848),
                  ("cardtype", 0x180223208), ("rare", 0x18022315c),
                  ("fcc_discardcoins", 0x1802231f0)):
        xs = xrefs_to(a)
        print("  %-18s %#x : %d xrefs" % (nm, a, len(xs)))
        for frm, typ, fn, ent in xs:
            print("      %#x %s %s %#x" % (frm, typ, fn, ent))

    print("\n##### C: callers of the db wrappers #####")
    for nm, a in (("from-table 0x1801a0000", 0x1801a0000),
                  ("where 0x18019ff00", 0x18019ff00),
                  ("getcell 0x1801a0080", 0x1801a0080),
                  ("rowcount 0x1801a00a0", 0x1801a00a0),
                  ("select 0x1801a0280", 0x1801a0280)):
        xs = xrefs_to(a)
        fns = sorted({(ent, fn) for frm, typ, fn, ent in xs if ent})
        print("  %-24s %d xrefs from %d functions" % (nm, len(xs), len(fns)))
        for ent, fn in fns:
            print("      %#x %s" % (ent, fn))

    print("\n##### D: tier helper and the registration entry #####")
    for a in (0x1800a9fe0,):
        src = dec(a)
        print("=" * 78)
        print("FUN_%x len=%d" % (a, len(src)))
        print("=" * 78)
        print(src)
        print("  xrefs to %#x:" % a)
        for frm, typ, fn, ent in xrefs_to(a):
            print("      %#x %s %s %#x" % (frm, typ, fn, ent))

except Exception:
    traceback.print_exc()
