"""DIM4. Several VAs listed as "deserializer" in ENDPOINT_MAP are in fact the
response object's CONSTRUCTOR or FACTORY (they end in `*obj = &PTR_FUN_<vtable>`).
Resolve the REAL deserializer properly: RS4 name -> factory (xref) -> the .rdata
vtable the factory installs -> slot +0x08.

CONTROL: FutISSearchServerResponse must resolve to 0x180163420 and
FutGetTradePileServerResponse to 0x180170810 -- the two VAs just PROVEN correct in
q_mk_wire_4 (both visibly tail-call the shared IS-list body 0x18013e7f0). If the
method reproduces those two it can be trusted for the four that were only MED.
"""
import traceback
try:
    NAMES = ["FutISSearchServerResponse", "FutGetTradePileServerResponse",
             "FutISStartServerResponse", "FutISViewTradeServerResponse",
             "FutISWatchListServerResponse", "FutISWatchTradeServerResponse",
             "FutISOfferTradeServerResponse", "FutISRemoveTradeServerResponse",
             "FutISRemoveWatchServerResponse", "FutRelistAllServerResponse",
             "FutGetAuctionCountServerResponse", "FutGetSuggestedPricingServerResponse"]

    def vtables_in(ent):
        out = set()
        f = func(ent)
        if f is None:
            return out
        it = refs.getReferencesFrom(f.getEntryPoint())
        body = f.getBody()
        ai = listing.getInstructions(body, True)
        while ai.hasNext():
            ins = ai.next()
            for r in ins.getReferencesFrom():
                t = int(r.getToAddress().getOffset())
                if 0x1801e5000 <= t < 0x18028a000:
                    try:
                        s0 = qword(t); s1 = qword(t + 8)
                    except Exception:
                        continue
                    if 0x180001000 <= s0 < 0x1801e5000 and 0x180001000 <= s1 < 0x1801e5000:
                        out.add(t)
        return out

    resolved = {}
    for cls in NAMES:
        print("\n===== %s =====" % cls)
        hits = find_all(b"RS4:" + cls.encode() + b"\x00")
        print("  RS4 literal at %s" % [hex(h) for h in hits])
        for h in hits:
            for frm, typ, fn, ent in xrefs_to(h):
                print("   factory ref from %#x in %s @ %#x" % (frm, fn, ent))
                for vt in sorted(vtables_in(ent)):
                    d8 = qword(vt + 8)
                    f8 = fm.getFunctionAt(addr(d8))
                    print("     vtable %#x  slot+0x00=%#x  slot+0x08=%#x %s"
                          % (vt, qword(vt), d8, f8.getName() if f8 else "(undef)"))
                    resolved.setdefault(cls, set()).add(d8)
    print("\n\n==================== REAL DESERIALIZERS ====================")
    for cls in NAMES:
        print("  %-40s %s" % (cls, [hex(x) for x in sorted(resolved.get(cls, []))]))
    done = set()
    for cls in NAMES:
        for a in sorted(resolved.get(cls, [])):
            if a in done:
                continue
            done.add(a)
            print("\n########## DESER for %s @ %#x ##########" % (cls, a))
            s = dec(a)
            print("len(src)=%d" % len(s)); print(s)
except Exception:
    traceback.print_exc()
