"""Q3: the PLAYER card publisher, the attribute publisher, and the merge. FUN_1800e6e20 emits OVERALL_RATING, CARD_LEVEL, CARD_RARITY, LEAGUE_ID, TEAM_ID, LAST_NAME, PREFERRED_POSITION(+_ID), PLAY_STYLE, NATIONALITY(+ABBR/ABBR15/ASSET_ID), CONTRACT_REMAINING and ASSET_ID -- that is the player card face. Get it in full and map each published key to the accessor and therefore to a record offset. FUN_1800e65d0 / FUN_1800e96f0 emit ATTRIBUTE_NAME / _ABBR / _VALUE: the six bars. FUN_1800ea400 and FUN_1800e86f0 and FUN_1800eb850 are the other publishers. HYPOTHESIS: every one of these is a pure field read off the same 0x180 record, so the "ours vs the client's" split is decided ONLY in the merge FUN_180141660 / FUN_180135890, never at draw time. CONTROL: FUN_1800e6e20 must publish LAST_NAME from record+0xc8 (an inline char array the merge WRITES from the local DB) and OVERALL_RATING from record+0xb4 (a field the merge is documented never to touch). Both in the same function, same call form. If they do not both appear as record reads, my model of the publisher is wrong. Also dump FUN_180135890 (players merge, the Q4 miss path) and FUN_180141660 in full, plus the CARD_TYPE getter FUN_18003b800 and FUN_18003cbf0. """ import traceback FUNCS = [ ("FUN_1800e6e20 PLAYER card publisher", 0x1800E6E20), ("FUN_1800e65d0 attribute publisher A", 0x1800E65D0), ("FUN_1800e96f0 attribute publisher B", 0x1800E96F0), ("FUN_1800ea400 publisher", 0x1800EA400), ("FUN_1800e86f0 publisher", 0x1800E86F0), ("FUN_1800eb850 publisher", 0x1800EB850), ("FUN_18015fa80 publisher", 0x18015FA80), ("FUN_1800e80f0 publisher", 0x1800E80F0), ] try: for label, ea in FUNCS: src = dec(ea) print("=" * 70) print("### %s @%#x len=%d" % (label, ea, len(src))) print(src) except Exception: traceback.print_exc()