"""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