# Pin the BASE template each suffix builder composes onto, by walking the vtable the # builder sits in and reading the sibling slot that returns the base string. BASES = ["ut/%s/sbs", "ut/%s/draft/mode", "ut/%s/club", "ut/%s/item", "ut/%s", "ut/%s/champion", "ut/%s/leaderboards", "ut/%s/season", "ut/%s/tournament", "ut/%s/auctionhouse", "ut/%s/trade", "ut/%s/tradePile", "ut/%s/marketdata", "ut/%s/purchased", "ut/%s/user", "ut/%s/squad", "ut/%s/squad/mode"] for b in BASES: hits = find_all(b.encode() + b"\x00", blocks=(".rdata", ".data", ".text")) print("=== %-22s %s" % (b, [hex(h) for h in hits])) for h in hits: for (fr, ty, fn, en) in xrefs_to(h): print(" ref %#x %-10s %s @%#x" % (fr, ty, fn, en)) # Builders whose base we still need. Print the vtable that holds each. BUILDERS = {"objective/reward FUN_180151610": 0x180151610, "objective/complete FUN_180147780": 0x180147780, "sets FUN_18017a980": 0x18017a980, "consumables FUN_1801308c0": 0x1801308c0, "clubstats FUN_18012f4f0": 0x18012f4f0} for nm, a in BUILDERS.items(): print("\n### vtable slots holding %s" % nm) for h in find_all(a.to_bytes(8, "little"), blocks=(".rdata", ".data")): print(" vt entry @%#x" % h) for k in range(-6, 7): try: q = qword(h + k * 8) except Exception: continue tag = "" if 0x180000000 <= q < 0x181000000: try: s = rd_str(q, 60) if s and all(32 <= ord(c) < 127 for c in s) and len(s) > 2: tag = " STR %r" % s except Exception: pass f = fm.getFunctionAt(addr(q)) if f is not None and not tag: tag = " FN %s" % f.getName() print(" [%+2d] %#018x%s" % (k, q, tag))