"""Q: which value does the UI actually receive for the quick-sell price? LEAD: the UI layer is bound by NAME. q4 showed calls of the shape (**(code **)(*view + 0x48))(view, "LATEST_GAMES_WON", value) so every number the client renders is pushed through a named setter. If the quick-sell price is pushed under a name, finding that name finds the reader, and the reader tells us whether it reads item+0x38 (the wire discardValue) or item+0x3c (the client's own fcc_discardcoins result). METHOD: enumerate every printable string in .rdata/.data whose text contains DISCARD / discard / QUICK / Quick / SELL / Sell / COIN / Coin, print it with its xrefs and the containing function, then decompile the functions that push a discard-ish name. CONTROL: the search must find the strings we already know exist -- the SQL fragments "fcc_discardcoins" (0x1802231f0) and "price" (0x1802231e4) both contain the target substrings, and the known UI name "LATEST_GAMES_WON" must show up under COIN?? no -- it must show up in a separate positive check that the string enumerator sees UI names at all. Both checks are printed explicitly. If either fails the enumerator is broken and its silence means nothing. """ import re import traceback OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/cards/q5_out.txt" try: lines = [] def P(*a): lines.append(" ".join(str(x) for x in a)) NEEDLES = ("DISCARD", "discard", "Discard", "QUICK", "Quick", "quick", "SELL", "Sell", "sell", "COIN", "Coin", "coin") # gather printable C strings out of .rdata/.data blocks = {} for b in mem.getBlocks(): if b.getName() in (".rdata", ".data") and b.isInitialized(): s = int(b.getStart().getOffset()) n = int(b.getEnd().getOffset()) - s + 1 blocks[b.getName()] = (s, n) P("blocks: %s" % {k: (hex(v[0]), v[1]) for k, v in blocks.items()}) RE_STR = re.compile(rb"[ -~]{4,120}\x00") found = [] for bn, (s, n) in blocks.items(): off = 0 CH = 1 << 20 while off < n: ln = min(CH, n - off) data = read_bytes(s + off, ln) for m in RE_STR.finditer(data): txt = m.group()[:-1].decode("ascii") if any(x in txt for x in NEEDLES): found.append((s + off + m.start(), bn, txt)) off += ln - 130 if ln == CH else ln # dedupe seen = set() found = [f for f in found if not (f[0] in seen or seen.add(f[0]))] P("strings matching %s: %d" % (list(NEEDLES), len(found))) P("") P("=== CONTROL 1: the two known SQL literals must be in the hit list ===") hits = {a for a, _, _ in found} P(" 0x1802231f0 'fcc_discardcoins' present: %s" % (0x1802231F0 in hits)) P(" 0x1802231e4 'price' present (should be False, no needle): %s" % (0x1802231E4 in hits)) P("") P("=== CONTROL 2: the enumerator sees UI names -- LATEST_GAMES_WON ===") la = find_all(b"LATEST_GAMES_WON\x00", blocks=(".rdata", ".data")) P(" LATEST_GAMES_WON found at %s" % [hex(x) for x in la]) P("") P("=== HITS WITH XREFS ===") for a, bn, txt in sorted(found): xs = xrefs_to(a) fns = sorted({(fn, ent) for _, _, fn, ent in xs if ent}) P("%#x [%s] %-46r xrefs=%d %s" % (a, bn, txt, len(xs), ", ".join("%s@%#x" % f for f in fns[:8]))) with open(OUT, "w") as fh: fh.write("\n".join(lines)) print("wrote %s (%d lines)" % (OUT, len(lines))) except Exception: traceback.print_exc()