"""D3 store-price q3.

HYPOTHESES
 H7  Omitting extPrice from the store pack JSON leaves the pack record's currency
     vector with no "mtx" entry, so FUN_18002c3c0 never sets vm+0xb5 and the
     real-money price line is suppressed.
     THREAT TO H7: the pack-element deserializer FUN_18013af30 ALSO references the
     "mtx" literal (0x18013ba76) and ALSO calls the commerce singleton FUN_1801a0040
     (0x18013ba98, 0x18013bab7). If it creates the "mtx" row unconditionally, H7 is
     false. Decompile it IN FULL and read that block.
 H8  DAT_1802de0d0 is a 0x2c0-byte singleton built by FUN_180012a50; its vtable
     slot +0x30 is the boolean that disables the "points" currency branch.
 H9  Enumerate every atom FUN_18013af30 dispatches on, by instruction-level scalar
     census, so the "which keys does the pack record accept" question is answered
     without the absence trap. CONTROL: the census must find the atoms we already
     know it reads (0xd9 displayGroup, 0xc5 currencies, and the extPrice atoms).
"""
import traceback

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store"
ATOMS = {}
for line in open("/home/alex/Documents/OpenFUT/fifa17-recon/docs/fut_atoms.tsv"):
    parts = line.rstrip("\n").split("\t")
    if len(parts) >= 3:
        try:
            ATOMS[int(parts[0])] = parts[2]
        except ValueError:
            pass

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

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

    # ---- H7 / H9 : the pack element deserializer ----
    src = dec(0x18013af30, 300)
    p("=" * 70)
    p("H7 pack element deser FUN_18013af30  len(src)=%d  PRINTED IN FULL" % len(src))
    p(src)

    p("")
    p("=" * 70)
    p("H9 scalar census of FUN_18013af30 -- every scalar operand, atom-annotated")
    f = func(0x18013af30)
    sc = {}
    n = 0
    it = listing.getInstructions(f.getBody(), True)
    while it.hasNext():
        ins = it.next()
        n += 1
        for i in range(ins.getNumOperands()):
            for o in ins.getOpObjects(i):
                try:
                    v = int(o.getValue()) & 0xFFFFFFFFFFFFFFFF
                except Exception:
                    continue
                sc.setdefault(v, []).append((int(ins.getAddress().getOffset()),
                                             str(ins.getMnemonicString())))
    p("instructions=%d distinct scalars=%d" % (n, len(sc)))
    ind = []
    it = listing.getInstructions(f.getBody(), True)
    while it.hasNext():
        ins = it.next()
        if ins.getFlowType().isJump() and ins.getFlowType().isComputed():
            ind.append(hex(int(ins.getAddress().getOffset())))
    p("computed/indirect jumps: %s" % (ind or "NONE"))
    p("")
    p("scalars in the plausible atom range 1..0x400 (CMP/SUB/DEC sites shown):")
    for v in sorted(k for k in sc if 0 < k <= 0x400):
        sites = [h for h in sc[v] if h[1] in ("CMP", "SUB", "DEC", "ADD", "MOV", "LEA")]
        p("   %-6s %-28s %s" % (hex(v), ATOMS.get(v, ""),
                                ", ".join("%x/%s" % s for s in sc[v][:6])))

    # ---- H8 the points gate object ----
    p("")
    p("=" * 70)
    p("H8 FUN_180012a50 (ctor of the DAT_1802de0d0 singleton)")
    s2 = dec(0x180012a50)
    p("len=%d FULL" % len(s2))
    p(s2)

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