"""DIMENSION 3 SEASONS q4.
ESTABLISHED: SeasonList deser 0x1801683f0 clears+repopulates the model season-list
vector (model vtable +0x898). FUN_180057330 reads that vector; empty -> NOSEASONS.
NOW: (a) confirm +0x898 getter returns this+0x5c68 and +0x588 getter -> this+0x7138;
(b) find WHO ISSUES the GET /season (SEASONLIST) RPC and its callers -- is the
request reachable, or is it never issued; (c) find every writer of the count short
at this+0x7138+0x96/+0x98 via a disp32 scan (form-independent).
CONTROL for disp32 scan: also scan for a KNOWN-written model offset (0x1fd2e, the
trading gate byte, known to have exactly one writer FUN_18011dc50) -> must find >=1
hit, else the scan form is broken.
"""
import traceback, struct
try:
    MODEL_VT = 0x18021c2a0
    print("### model vtable getters ###")
    for slot in (0x588, 0x898, 0x850):
        t = qword(MODEL_VT + slot)
        print("slot +%#x -> %#x %s" % (slot, t, fname(t)))
        print(dec(t)[:600])
        print("-" * 40)

    def disp32_scan(off, label, blocks=(".text",)):
        le = struct.pack("<i", off)
        hits = find_all(le, blocks=blocks)
        print("DISP32", label, hex(off), "->", len(hits), "hits")
        for h in hits:
            f = fm.getFunctionContaining(addr(h))
            print("   ", hex(h), f.getName() if f else "?")
        return hits

    print("\n### disp32 scans (form-independent) ###")
    disp32_scan(0x1fd2e, "CONTROL trading gate byte")
    disp32_scan(0x5c68, "season list vector base")
    disp32_scan(0x7138, "season sub-struct base")

    # the +0x96 / +0x98 short lives INSIDE the +0x7138 struct; its writers deref a
    # pointer to that struct then +0x96. Hard to disp32-scan directly; instead show
    # readers/writers of the +0x7138 getter result are the callers of slot +0x588.

    # SEASONLIST RPC: descriptor row 69, stride 0x30, base 0x1802caa28
    print("\n### RPC descriptor row 69 (SEASONLIST) ###")
    base = 0x1802caa28
    row = base + 69 * 0x30
    print("row addr", hex(row), "bytes:", read_bytes(row, 0x30).hex())
    # first qword often a name ptr, look for a char* to 'season'
    for o in range(0, 0x30, 8):
        v = qword(row + o)
        s = ""
        if 0x180000000 <= v < 0x181000000:
            try:
                s = rd_str(v, 40)
            except Exception:
                s = ""
        print("   +%#x %#x %r" % (o, v, s))

    # find the 'ut/%s/season' or 'season' URL template and its xref (the issuer)
    print("\n### 'season' url template search ###")
    for lit in (b"ut/%s/season\x00", b"/season\x00", b"season\x00"):
        hits = find_all(lit, blocks=(".rdata",))
        print("  ", lit, "->", [hex(h) for h in hits][:8])
        for h in hits[:4]:
            for x in xrefs_to(h):
                print("      xref", hex(x[0]), x[2], hex(x[3]))

    sys.stdout.flush()
    os._exit(0)
except Exception:
    traceback.print_exc()
    sys.stdout.flush()
    os._exit(0)
