"""TRANSFER LIST shows 0/0. What feeds the trade-pile capacity?

STATE. tradingEnabled (gate byte 0x1fd2e) is now 1, proven live, delivered via the Blaze
client-config store after /settings was proven incapable of reaching the applier. Every
card ships untradeable:false. Both documented gates of TO_TRADE_PILE (FUN_1801a7260) are
therefore satisfied, and yet "Place on Transfer List" and "List on Transfer Market" are
STILL greyed. The header reads TRANSFER LIST 0/0 in red, so the capacity is zero and that
is the obvious third condition.

We send maximumTradePileSize=77 through /settings. That cannot work: the applier
FUN_18011dc50 has no caller Ghidra can see, is unreachable from the settings deserializer,
and a live probe showed no int gate field carrying 77 (+0x1fd14=0, +0x1fd4c=0,
+0x1fd54=480). So the capacity must arrive some other way, or default to 0.

CANDIDATE LITERALS found in CardsDLL: TRADE_PILE_SIZE, GetMaxPileSize, NUM_MAX_AUCTIONS,
IS_MAX_AUCTIONS, pileSizeClientData, TradePileFull, maximumTradePileSize.

QUESTIONS
 Q1 Find the reader of the trade-pile capacity. Start from GetMaxPileSize and
    TradePileFull and walk back to the field they read. Which struct offset holds it?
 Q2 Is that offset the SAME one the settings applier would write (i.e. one of the int
    fields at 0x1fd14 / 0x1fd4c / 0x1fd54), or a different home entirely? If it is an
    applier field then capacity is unreachable for the same reason tradingEnabled was,
    and the Blaze store is the only remaining lever.
 Q3 Is TRADE_PILE_SIZE a CLIENT-CONFIG key, read the same way IS_TRADING_ENABLED is?
    That is the decisive question, because IS_TRADING_ENABLED via the Blaze store worked
    on the first try and the same delivery path would fix this. Find its xrefs and the
    lookup that consumes it. Do NOT assume: an UPPER_SNAKE literal could equally be a
    localisation key or a telemetry tag.
 Q4 pileSizeClientData smells like the ut/%s/clientdata route, which we already serve.
    Check whether the capacity is read from a clientdata blob instead. If so that is a
    UTAS-side fix and needs no Blaze restart, which makes it much cheaper to test.

CONTROL: IS_TRADING_ENABLED is known to work through the Blaze client-config path. Find
how IT is consumed, and use that as the template for judging whether TRADE_PILE_SIZE is
consumed the same way. A candidate that is NOT read by the same mechanism is not a
candidate, however plausible the name looks.

COVERAGE: print decompiles in full with lengths. Enumerate all four dispatch forms before
any absence claim: == , != , case labels, and sub/dec ladders.
"""
import traceback

LITERALS = [b"TRADE_PILE_SIZE\x00", b"GetMaxPileSize\x00", b"NUM_MAX_AUCTIONS\x00",
            b"IS_MAX_AUCTIONS\x00", b"pileSizeClientData\x00", b"TradePileFull\x00",
            b"maximumTradePileSize\x00", b"IS_TRADING_ENABLED\x00"]


def dump(va, title, limit=None):
    try:
        f = func(va)
        src = dec(va)
        print("\n" + "=" * 78)
        print("%#x  %s   body %d / decompile %d chars%s"
              % (va, title, f.getBody().getNumAddresses() if f else -1, len(src),
                 "" if limit is None else " (first %d shown)" % limit))
        print("=" * 78)
        print(src if limit is None else src[:limit])
    except Exception:
        print("!! failed %#x" % va)
        traceback.print_exc()


try:
    print("=" * 78)
    print("LITERAL LOCATIONS AND XREFS")
    print("=" * 78)
    interesting = {}
    for lit in LITERALS:
        name = lit[:-1].decode()
        hits = find_all(lit)
        print("\n%-24s %d hit(s)" % (name, len(hits)))
        for h in hits:
            print("   at %#x : %r" % (h, rd_str(h, 40)))
            xs = xrefs_to(h)
            if not xs:
                print("      no direct xref (may be reached via a table)")
            for frm, typ, fn, ent in xs:
                print("      xref %#x in %s (entry %#x)" % (frm, fn, ent))
                if ent:
                    interesting.setdefault(name, set()).add(ent)

    print("\n" + "=" * 78)
    print("CONSUMERS, decompiled")
    print("=" * 78)
    # IS_TRADING_ENABLED first: it is the working control and the template.
    for name in ("IS_TRADING_ENABLED", "TRADE_PILE_SIZE", "pileSizeClientData",
                 "GetMaxPileSize", "TradePileFull", "NUM_MAX_AUCTIONS"):
        for ent in sorted(interesting.get(name, []))[:2]:
            dump(ent, "consumer of %s" % name, limit=6000)

except Exception:
    traceback.print_exc()
