"""D3 Q7: the eight card-action flags, and the code that switches on itemState. ESTABLISHED. FUN_18003e370 publishes eight per-card booleans to Flash under the names DISCARD, MODIFY, TO_ACTIVE_SQUAD, TO_TRADE_PILE, TO_STICKER_BOOK, MAY_BE_REMOVED, QUICK_SEARCH, DREAM_REPLACE, filled by FUN_1800e2a40 in that byte order: [0] FUN_1801a71c0 [1] FUN_1801a7210 [2] inline (isPlayer && squad room && not in squad) [3] FUN_1801a7260 [4] FUN_1801a7180 [5] constant 1 [6] FUN_1801a7320 [7] FUN_1801a71e0 and a precise, controlled scan (control displacements 0x3c and 0x4c returned 25 and 37 load/compare pairs) showed that item+0x49 is compared in EXACTLY TWO places in the whole DLL -- the getter FUN_1801a8940 and the TO_TRADE_PILE predicate FUN_1801a7260 -- while item+0x5c is compared against 0x64..0x68 in six functions. POLARITY IS THE OPEN QUESTION. Byte [2] is computed inline as isPlayer && squadHasRoom && !alreadyInSquad -> 1 which can only be an ENABLE flag, so 1 = action offered. Under that reading FUN_1801a7260 returns 1 (offered) whenever item+0x49 is 0, which is the opposite of what "tradeable" should do. Either the flag array is a DISABLE mask, or the service call at vtable+0x270 inverts the sense. This query decompiles all eight predicates and the service so the polarity is READ, not assumed. CONTROL: FUN_1801a7250 is already known to be the +0x4c (card family) accessor and byte [2] uses it as "is a player". If the decompile of FUN_1801a7250 is not a +0x4c read the whole byte-order attribution is wrong and nothing here counts. """ import traceback, os OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/cards/" os.makedirs(OUT, exist_ok=True) def dump(tag, va, full=6000): f = func(va) if f is None: print("%s %#x -> NO FUNCTION" % (tag, va)) return "" src = dec(va) print("=" * 78) print("%s %#x %s len(src)=%d" % (tag, va, f.getName(), len(src))) print("=" * 78) with open(OUT + "q7_%s_%x.c" % (tag, va), "w") as fh: fh.write(src) if len(src) <= full: print(src, " [PRINTED IN FULL]") else: print(" [TOO LONG TO PRINT IN FULL -- written to q7_%s_%x.c; printing every" " line mentioning 0x5c plus 6 lines of context]" % (tag, va)) lines = src.split("\n") keep = set() for i, l in enumerate(lines): if "0x5c" in l or "0x49" in l or "0x48" in l: for j in range(max(0, i - 6), min(len(lines), i + 7)): keep.add(j) prev = -2 for i in sorted(keep): if i != prev + 1: print(" ...") print(" %4d %s" % (i, lines[i])) prev = i return src try: print("###### A. the eight card-action predicates, in Flash byte order") for tag, va in (("a0_DISCARD_1801a71c0", 0x1801a71c0), ("a1_MODIFY_1801a7210", 0x1801a7210), ("a2_family_acc_1801a7250", 0x1801a7250), ("a3_TO_TRADE_PILE_1801a7260", 0x1801a7260), ("a4_TO_STICKER_BOOK_1801a7180", 0x1801a7180), ("a6_QUICK_SEARCH_1801a7320", 0x1801a7320), ("a7_DREAM_REPLACE_1801a71e0", 0x1801a71e0), ("view_init_1801a78f0", 0x1801a78f0), ("view_isnull_1801a8850", 0x1801a8850), ("view_4c_1801a8110", 0x1801a8110), ("helper_1801aa190", 0x1801aa190)): dump(tag, va) print() print("###### B. the itemState (0x5c) switches") for tag, va in (("s_180084720", 0x180084720), ("s_180094220", 0x180094220), ("s_180043880", 0x180043880), ("s_180094ae0", 0x180094ae0), ("s_180113870", 0x180113870), ("s_1801c3480", 0x1801c3480), ("s_1801b3640", 0x1801b3640), ("s_18011dc50", 0x18011dc50), ("s_180051cd0", 0x180051cd0)): dump(tag, va) print() print("###### C. the CardInventoryAdapter registration (what else it publishes)") dump("adapter_18003ec30", 0x18003ec30) dump("listpanel_18003e550", 0x18003e550) print() print("###### D. the service behind FUN_180009c80 -- what is vtable slot 0x270?") dump("svc_ctor_180009c80", 0x180009c80) dump("svc_ctor_180009b60", 0x180009b60) except Exception: traceback.print_exc()