"""Q5: resourceId decomposition, the value mappers, and who reads assetId (+0x20). Established so far, and the reason for each question here: * FUN_180141660's TAIL, reached on EVERY family including the no-merge default, does record+0x54 = 3 if record+0xb4 >= 0x4b else 2 if >= 0x41 else 1. So +0x54 is the bronze/silver/gold CARD LEVEL derived from rating, not itemType. * The players merge queries `players` by record+0x18 & 0xffffff and, on 0 rows, OVERWRITES rating=0x32, attrs=1, teamid=0x78d, nation=0xe, position=2, name=" ". * The item deser routes atom 0x1d1 nation to +0x148 for family 1 and +0xde for family 2 and NOWHERE otherwise, and atom 0x18a leagueId always to +0xe0. Remaining: (a) FUN_180166ca0(resourceId, &out_byte, buf, &out_dword) -- the resourceId decomposition. out_dword lands at record+0x18 and out_byte at record+0x24, and +0x24 gates the "p%d.dds" FUTPlayerHeads photo registration. This decides Q3: what resourceId actually has to look like. CONTROL: DAT_7364642e642570 must read as "p%d.dds"; if it does not, my read of the photo path is wrong. (b) FUN_180136480 playStyle mapper, FUN_180166810 preferredPosition mapper, FUN_180166660 itemState mapper, FUN_1801666f0 injuryType mapper. (c) WHO READS record+0x20 (assetId)? Accessors FUN_1801a8020 and FUN_1801a8990 read it. Enumerate their callers. HYPOTHESIS: only the staff/manager card art path, which the merge itself fills from the local DB, so a wire assetId is dead for every family. CONTROL: the same enumeration run on FUN_1801a87f0 (+0xb4 rating) must return the publishers we already found. An enumeration that returns nothing for rating is broken. (d) FUN_1800d84e0 -- the value written to record+0x30. (e) FUN_1801356c0 manager merge, for the family table. """ import traceback try: print("=== CONTROL: the format string at 0x1801eaf90..0x1801eafa8 region ===") for a in (0x1801EAF98,): print(" %#x -> %r" % (a, rd_str(a, 32))) hits = find_all(b"p%d.dds") print(" 'p%%d.dds' occurrences: %s" % " ".join("%#x" % h for h in hits)) for h in find_all(b"FUTPlayerHeads"): print(" FUTPlayerHeads @%#x" % h) print() print("=== (a) FUN_180166ca0 resourceId decomposition ===") print(dec(0x180166CA0)) print("=== (b) mappers ===") for lbl, a in (("playStyle FUN_180136480", 0x180136480), ("preferredPosition FUN_180166810", 0x180166810), ("itemState FUN_180166660", 0x180166660), ("injuryType FUN_1801666f0", 0x1801666F0)): s = dec(a) print("--- %s len=%d" % (lbl, len(s))) print(s) print("=== (c) callers of the accessors ===") for lbl, a in (("+0x20 assetId FUN_1801a8020", 0x1801A8020), ("+0x20/+0x4c FUN_1801a8990", 0x1801A8990), ("+0x1c cardassetid FUN_1801a8010", 0x1801A8010), ("+0x18 resourceId FUN_1801a80a0", 0x1801A80A0), ("CONTROL +0xb4 rating FUN_1801a87f0", 0x1801A87F0), ("+0x54 level FUN_1801a80c0", 0x1801A80C0), ("+0x54 FUN_1801a8870", 0x1801A8870), ("+0x54 FUN_1801a88f0", 0x1801A88F0)): print("--- %s" % lbl) for frm, typ, fn, ent in xrefs_to(a): print(" %#x %-10s %s(%#x)" % (frm, typ, fn, ent)) print() print("=== (d) FUN_1800d84e0 (record+0x30) ===") print(dec(0x1800D84E0)) print("=== (e) FUN_1801356c0 manager merge ===") s = dec(0x1801356C0) print("len=%d" % len(s)) print(s) except Exception: traceback.print_exc()