"""ADVERSARIAL BATCH 4. CARD SIDE A. FUN_1801aa7f0 and FUN_1800e6410 read [reg+0xb4] as a byte but sit OUTSIDE the accessor range [0x1801a7000,0x1801a9000) the D4 report swept. Do they read an item record? If so the "OVERALL_RATING has exactly four publishers, all through FUN_1801a87f0" completeness argument has a hole. B. FUN_1801356c0 -- the family-2 (manager) merge. Does it clobber +0xdd..+0xfb the way the players merge does? That decides whether leagueId at +0xe0 survives for managers. C. FUN_180134cb0 -- writes +0xfc..+0x101, which FUN_1801a86b0 reads as the per-attribute chemistry delta. D. disp8 scan for [reg+0x54]: 0x54 fits a signed disp8 so the disp32 trick does NOT apply; iterate EVERY instruction in .text instead. Positive control: FUN_180141660 and FUN_1801a80c0 must appear. ROUTE SIDE E. FUN_18012ec50 club ?type= switch, FUN_18012f4f0 club/stats switch, FUN_1801308c0 consumables suffix, FUN_18012ddf0 query builder -- full, so the "exactly 30 / exactly 7 / no /stats/team" absences can be re-tested against case labels AND == AND != AND ladders. """ import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/cards/adv/" try: import re bodies = [] src = {} for nm, a in (("rating_reader_1aa7f0", 0x1801AA7F0), ("rating_reader_e6410", 0x1800E6410), ("mgr_merge_1356c0", 0x1801356C0), ("chem_134cb0", 0x180134CB0), ("clubtype_12ec50", 0x18012EC50), ("clubstats_12f4f0", 0x18012F4F0), ("consum_1308c0", 0x1801308C0), ("clubsearch_12ddf0", 0x18012DDF0)): s = dec(a, 600) src[nm] = s bodies.append("=" * 78) bodies.append("### %s @ %#x len=%d" % (nm, a, len(s))) bodies.append(s) open(OUT + "v4_bodies.txt", "w").write("\n".join(bodies)) print("WROTE v4_bodies.txt") for nm in ("clubtype_12ec50", "clubstats_12f4f0"): s = src[nm] cases = re.findall(r"case\s+(0x[0-9a-fA-F]+|\d+):", s) eq = re.findall(r"==\s*(0x[0-9a-fA-F]+|\d+)", s) ne = re.findall(r"!=\s*(0x[0-9a-fA-F]+|\d+)", s) sub = re.findall(r"-\s*(0x[0-9a-fA-F]+|\d+)U?\s*<", s) print("\n%s len=%d cases=%d %s\n ==%s !=%s ladders=%s" % (nm, len(s), len(cases), cases, eq, ne, sub)) # ---- D: exhaustive instruction walk for [reg+0x54] print("\n==== EVERY instruction in .text referencing [reg + 0x54] ====") blk = [b for b in mem.getBlocks() if b.getName() == ".text"][0] it = listing.getInstructions(blk.getStart(), True) n = 0 found = [] while it.hasNext(): ins = it.next() if ins.getAddress().getOffset() > int(blk.getEnd().getOffset()): break n += 1 t = str(ins) if "+ 0x54]" in t: f = fm.getFunctionContaining(ins.getAddress()) found.append((int(ins.getAddress().getOffset()), t, f.getName() if f else "?")) print("instructions walked: %d ; hits: %d" % (n, len(found))) byf = {} for a, t, fn in found: byf.setdefault(fn, []).append("%#x %s" % (a, t)) for fn in sorted(byf): print(" %-26s %s" % (fn, "; ".join(byf[fn]))) except Exception: traceback.print_exc()