"""D4 Q1b/Q2: find the READERS of the settings gate byte at FutDataManagerImpl+0x1fd45 (the byte written from settings field [0x1d], which is the packOpeningAnimationEnabled arm found by q_pack_reveal_1). HYPOTHESIS: some accessor reads [reg + 0x1fd45] and is called from the pack-reveal path. If no IS_* key exists (the publisher FUN_18006cc60 does not mention it), the byte must be read by a direct getter instead. CONTROLS: the same byte-displacement scan for 0x1fd3a (friendlySeasonsEnabled) and 0x1fd3d (enableDraftMode) must find the accessors that FUN_18006cc60 calls through vtable slots +0x2b0 and +0x2c8 respectively. If the scan cannot reproduce those, the scan technique is wrong and the 0x1fd45 result means nothing. Also dumps PACK/REVEAL/WALKOUT/ANIM strings using find_all's REAL signature (block NAMES, not block objects -- q_pack_reveal_1 passed objects and silently got 0). """ import traceback, struct OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres/" def w(name, text): with open(OUT + name, "w") as f: f.write(text) print("WROTE %s%s (%d chars)" % (OUT, name, len(text))) def disp_scan(off): """functions containing the little-endian dword `off` inside .text.""" pat = struct.pack(" accessor -> byte offset print("=== strings ===") pats = [b"PACK", b"Pack", b"WALKOUT", b"Walkout", b"walkout", b"REVEAL", b"Reveal", b"ANIMATION", b"Animation", b"nimation"] seen = {} for p in pats: for h in find_all(p, (".text", ".rdata", ".data")): start = h for k in range(1, 128): try: c = mem.getByte(addr(h - k)) & 0xFF except Exception: break if c < 0x20 or c > 0x7E: start = h - k + 1 break s = rd_str(start) if s and 3 < len(s) < 200: seen.setdefault(s, start) lines = ["%#x %s" % (v, k) for k, v in sorted(seen.items(), key=lambda kv: kv[1])] print("%d distinct strings" % len(lines)) w("d4_strings.txt", "\n".join(lines)) for l in lines: print(l) except Exception: traceback.print_exc()