"""Q4 ANSWER CANDIDATE: FUN_18013adb0 is the pileSizeClientData deserialiser and it
writes response+0xc8..+0xd4, which FUN_180173e00 forwards to the capacity applier
vt+0x998 -> model+0x1fd1c (TRADE_PILE_SIZE) and model+0x1fd20.

CHAIN UNDER TEST (each link already printed in full elsewhere):
  atom 0x227 pileSizeClientData -> FUN_18013adb0(reader, resp+0xc8)
  resp+0xc8..0xd4 -> local_108..uStack_fc -> vt+0x998 = FUN_18011dbf0
  FUN_18011dbf0: model+0x1fd1c = S[+0x08] (= resp+0xd0); model+0x1fd20 = S[+0x0c]
  model+0x1fd1c -> vt+0xa58 -> published as "TRADE_PILE_SIZE"  (measured 0 live)

CONTROL, same method, already closed end to end: atom 0x336 tradingEnabled ->
settings deser param_2[0xa] = resp+0x50 -> S2[10] -> model+0x1fd2e (measured 0 live,
and 0x1fd2e is the PROVEN service gate at vt+0x270). The offset arithmetic is
therefore validated on a field whose whole chain is independently established.

Remaining unknowns this query must settle:
  * the ATOM NAMES of the four ints inside FUN_18013adb0 (which JSON key is capacity)
  * who writes response+0x1c, the field that makes FUN_180173e00 skip BOTH appliers
  * the response class name, to name the route in the write-up
"""
import struct, traceback

try:
    for a, tag in [
        (0x18013adb0, "pileSizeClientData deserialiser -> resp+0xc8"),
    ]:
        src = dec(a, 300)
        print("\n\n########## %#x %s len=%d FULL ##########" % (a, tag, len(src)))
        print(src)
except Exception:
    traceback.print_exc()

try:
    print("\n\n########## head of FUN_180174630 (init of resp fields) ##########")
    src = dec(0x180174630, 300)
    print("\n".join(src.split("\n")[:110]))
except Exception:
    traceback.print_exc()

try:
    print("\n\n########## descriptor vtable 0x18022d000 + class name hunt ##########")
    for i in range(0, 12):
        t = qword(0x18022d000 + i * 8)
        f = fm.getFunctionAt(addr(t)) if 0x180001000 <= t < 0x1801e5000 else None
        print("  +%#04x -> %#x %s" % (i * 8, t, f.getName() if f else ""))
    # RS4: class names whose factory/vtable is near 0x18022d000
    hits = find_all(b"RS4:")
    print("  total RS4: literals:", len(hits))
    for h in hits:
        s = rd_str(h, 80)
        if "MassInfo" in s or "Mass" in s or "UserMass" in s or "Pile" in s:
            print("   %#x  %r" % (h, s))
except Exception:
    traceback.print_exc()

try:
    print("\n\n########## who writes response+0x1c ##########")
    # the response object is allocated/initialised by the descriptor; find the
    # constructor by looking at what FUN_180174580 does, printed FULL
    src = dec(0x180174580, 300)
    print("len=%d" % len(src))
    print(src)
except Exception:
    traceback.print_exc()
