"""THE DECISIVE ONE. Does FUN_180173e00 actually run, or is every gate byte a
constructor default?

WHY IT DECIDES MY RECOMMENDATION: FUN_180173e00 calls the gate applier (vt+0x988)
and the CAPACITY applier (vt+0x998) in the SAME branch, after the same two guards.
They run together or not at all. If the callback never runs, serving
pileSizeClientData cannot possibly move TRANSFER LIST capacity and my recommendation
is void.

THE OBSERVATION THAT FORCES THE QUESTION (live, this session, controls passed):
model+0x1fd2f..+0x1fd3f all read 1, and FUN_18011dc50 writes every one of them as
`byte = (respField == 1)`. We serve {"configs": []}, so if the response struct were
zero-initialised and the applier ran, they would ALL be 0. They are 1. So exactly one
of these is true:
   (A) the applier never runs and those bytes are FutDataManagerImpl ctor defaults
   (B) the applier runs and the RESPONSE struct is constructed with 1s
(A) and (B) predict opposite outcomes for serving pileSizeClientData, and the live
byte pattern alone cannot separate them. The response constructor can.

TEST: decompile the FutGetUserMassInfoServerResponse constructor and read the
initialiser for resp+0x28..+0xc4 (the settings block), resp+0x50 (tradingEnabled),
and resp+0xc8..+0xd4 (the capacity block).
CONTROL inside the answer: whatever the ctor sets resp+0x80 (friendlySeasonsEnabled)
to must equal the live model+0x1fd3a, which is 1. If the ctor sets it to 1, (B) is
confirmed and the applier demonstrably runs. If the ctor zeroes it, (A) holds.
"""
import struct, traceback

try:
    print("########## descriptor vtable slot +0x10 -> 0x18011f940 ##########")
    print(dec(0x18011f940, 300))
except Exception:
    traceback.print_exc()

try:
    # the ctor should reference the RS4 name 0x18022d110 (-4 rule: the lea points at
    # the "RS4:" header itself)
    print("\n\n########## refs to RS4:FutGetUserMassInfoServerResponse (0x18022d110) ##########")
    for frm, typ, fn, ent in xrefs_to(0x18022d110):
        print("   %#x %s in %s (%#x)" % (frm, typ, fn, ent))
    def sect(name):
        for b in mem.getBlocks():
            if b.getName() == name:
                return int(b.getStart().getOffset()), int(b.getEnd().getOffset()) - int(b.getStart().getOffset()) + 1
    TB, TS = sect(".text")
    TEXT = read_bytes(TB, TS)
    for tgt in (0x18022d110, 0x18022d000):
        out = []
        for i in range(0, len(TEXT) - 4):
            d = struct.unpack_from("<i", TEXT, i)[0]
            if TB + i + 4 + d == tgt:
                out.append(TB + i)
        print("\n  rip-disp32 refs to %#x: %s" % (tgt, [hex(x) for x in out]))
        for a in out:
            f = fm.getFunctionContaining(addr(a))
            print("     %#x in %s" % (a, f.getName() if f else "?"))
except Exception:
    traceback.print_exc()

try:
    print("\n\n########## FUN_18016c330 (installs the descriptor / issues request) ##########")
    s = dec(0x18016c330, 300)
    print("len=%d" % len(s)); print(s)
except Exception:
    traceback.print_exc()
