"""DIMENSION 1 / query 1.

HYPOTHESIS: a CardsDLL function converts the 0x158 pack deser records into
0x1a8 pack model records grouped into 0x108 display-group records, and a
second function resolves a display group back to a pack at render time. If
the resolver keys on a field that collides between our two GOLD packs
(packType), that explains Gold-group -> Premium-Gold.

CONTROL: FUN_1800150d0 is documented as walking the pack array and comparing
displayGroup.value against the literal "mypacks" at 0x1801ec008. Decompiling it
with the same helper proves the helper works and shows the house style of a
group filter. Every "not present" claim below prints len(src) first.
"""
import traceback

OUT = "/tmp/claude-1000/-home-alex-Documents-OpenFUT/8e521ca1-ca3e-4138-bb96-df1744dd1d30/scratchpad/store/q1_out.txt"
buf = []
def P(*a):
    s = " ".join(str(x) for x in a)
    buf.append(s)
    print(s)

try:
    P("=" * 78)
    P("A. CONTROL: FUN_1800150d0, the documented mypacks filter")
    P("=" * 78)
    s = dec(0x1800150d0)
    P("len(src) =", len(s))
    P(s)

    P("=" * 78)
    P("B. FUN_1801340e0 (0x108 vector grow) - full decompile + callers")
    P("=" * 78)
    s = dec(0x1801340e0)
    P("len(src) =", len(s))
    P(s)
    P("--- callers of 0x1801340e0 ---")
    for f in callers(0x1801340e0):
        P("   ", f.getName(), hex(int(f.getEntryPoint().getOffset())))

    P("=" * 78)
    P("C. FUN_180132180 (0x158) - callers only")
    P("=" * 78)
    for f in callers(0x180132180):
        P("   ", f.getName(), hex(int(f.getEntryPoint().getOffset())))

    P("=" * 78)
    P("D. callers of the pack element deser 0x18013af30")
    P("=" * 78)
    for f in callers(0x18013af30):
        P("   ", f.getName(), hex(int(f.getEntryPoint().getOffset())))

    P("=" * 78)
    P("E. where does imm 0x1a8 appear? (the live pack model record size)")
    P("=" * 78)
    # scan .text for the 32-bit immediate 0xa8 0x01 0x00 0x00 in instructions
    import java.lang as _jl  # noqa
    from ghidra.program.model.address import AddressSet  # noqa
    cnt = 0
    it = listing.getInstructions(True)
    hits = []
    while it.hasNext():
        ins = it.next()
        for i in range(ins.getNumOperands()):
            for o in ins.getOpObjects(i):
                try:
                    v = int(o.getValue())
                except Exception:
                    continue
                if v == 0x1a8:
                    hits.append((int(ins.getAddress().getOffset()), str(ins)))
    P("0x1a8 immediate occurrences:", len(hits))
    seen = {}
    for a, t in hits:
        f = fm.getFunctionContaining(addr(a))
        n = f.getName() if f else "?"
        seen.setdefault(n, []).append((hex(a), t))
    for n in sorted(seen, key=lambda k: -len(seen[k])):
        P("  %-28s x%d  %s" % (n, len(seen[n]), seen[n][:4]))

except Exception:
    P(traceback.format_exc())

open(OUT, "w").write("\n".join(buf))
print("WROTE", OUT)
