"""D4 Q1: trace packOpeningAnimationEnabled (atom 0x20e = 526) through the settings switch FUN_18013c6d0 -> settings-struct field index -> the applier FUN_18011dc50 -> gate byte offset -> IS_* key published by FUN_18006cc60. HYPOTHESIS: atom 0x20e has an arm in FUN_18013c6d0 that stores into some field index N of the settings struct; FUN_18011dc50 copies index N to a gate byte; FUN_18006cc60 publishes that byte under an IS_* string key. CONTROLS (must reproduce before trusting the new answer): friendlySeasonsEnabled -> field [0x16] -> gate 0x1fd3a -> IS_FRIENDLY_SEASON_ENABLED enableDraftMode -> field [0x17] -> gate 0x1fd3d -> IS_DRAFT_MODE_ENABLED Also dumps every string in the image matching PACK/ANIM/REVEAL/WALKOUT so Q2/Q3 have a target list. """ import traceback, re OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/packres/" def w(name, text): p = OUT + name with open(p, "w") as f: f.write(text) print("WROTE %s (%d chars)" % (p, len(text))) try: # ---------- 1. the settings deserializer ---------- src = dec(0x18013C6D0, 300) print("=== FUN_18013c6d0 len=%d ===" % len(src)) w("d4_settings_deser.txt", src) for needle in ("526", "0x20e", "0x20E"): for m in re.finditer(re.escape(needle), src): a, b = max(0, m.start() - 400), min(len(src), m.end() + 400) print("--- hit %r at %d ---" % (needle, m.start())) print(src[a:b]) print("--- end hit ---") # ---------- 2. the applier ---------- ap = dec(0x18011DC50, 300) print("=== FUN_18011dc50 len=%d ===" % len(ap)) w("d4_applier.txt", ap) # ---------- 3. the IS_* publisher ---------- pub = dec(0x18006CC60, 300) print("=== FUN_18006cc60 len=%d ===" % len(pub)) w("d4_publisher.txt", pub) # ---------- 4. strings of interest ---------- pats = [b"PACK", b"Pack", b"pack", b"WALKOUT", b"Walkout", b"walkout", b"REVEAL", b"Reveal", b"reveal", b"ANIMATION", b"Animation", b"animation", b"Anim"] blocks = [b for b in mem.getBlocks() if b.isInitialized()] seen = {} for p in pats: try: hits = find_all(p, blocks) except Exception as e: print("find_all failed for %r: %s" % (p, e)) continue for h in hits: va = int(h.getOffset()) if hasattr(h, "getOffset") else int(h) # walk back to string start start = va for k in range(1, 96): try: bb = read_bytes(va - k, 1) except Exception: break c = bb[0] & 0xFF if c < 0x20 or c > 0x7E: start = va - k + 1 break else: start = va - 95 s = rd_str(start) if s and 3 < len(s) < 160: 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[:400]: print(l) except Exception: traceback.print_exc()