"""DIMENSION 1 Q2: trace what the feature.trade byte gates at massinfo END_OBJECT,
and hunt for ANY other feature-style END_OBJECT zeroing (mode restrictions beyond trade).

Findings so far (q_md_feature_1): userInfo deser FUN_18013ec10 feature-object (case 0x11c)
recognises EXACTLY ONE sub-key, trade 0x330, writing byte *(u8*)(param_1 + 0x29). param_1
is undefined4* so this is byte offset 0x29*4 = 0xa4. But prior notes / q_feature_trade say
the massinfo check reads +0x17c and zeroes +0x50. Resolve the offset, and enumerate every
`cmp byte [rec+X],0 ; jz ; mov ... [rec+Y],0` restriction site in the massinfo root.

CONTROL: the known trade zero-site 0x180174f19 (mov dword [rsi+0x50],0) MUST appear.

Method: decompile massinfo root FUN_180174630 in full; print it; then walk its instruction
listing for every `mov ...,0` guarded by a `cmp byte [reg+disp],0 ; jz`, printing disp/target.
"""
import re, traceback

MASSINFO = 0x180174630

try:
    f = func(MASSINFO)
    src = dec(MASSINFO, 300)
    print("=== FUN_%08x massinfo root  body=%d insns  decompile=%d chars ===" %
          (MASSINFO, f.getBody().getNumAddresses() if f else -1, len(src)))
    print(src)

    # walk raw instructions for the restriction pattern: cmp byte [r+d],0 ; jz ; mov [r+d2],imm
    print("\n=== raw scan: cmp byte [reg+disp],0x0  sites in massinfo body ===")
    it = f.getBody().getAddresses(True)
    prev = []
    for ad in it:
        ins = listing.getInstructionAt(ad)
        if ins is None:
            continue
        s = str(ins)
        prev.append((int(ad.getOffset()), s))
        if len(prev) > 8:
            prev.pop(0)
        # detect cmp of a byte ptr against 0
        if s.startswith("CMP") and "byte ptr" in s.lower() and s.rstrip().endswith(",0x0"):
            print("  --- window around %#x ---" % int(ad.getOffset()))
            for a2, s2 in prev[-3:]:
                print("      %#x  %s" % (a2, s2))
            # print next 5 insns
            nxt = ins
            for _ in range(5):
                nxt = listing.getInstructionAt(nxt.getAddress().add(nxt.getLength()))
                if nxt is None:
                    break
                print("      %#x  %s" % (int(nxt.getAddress().getOffset()), str(nxt)))
except Exception:
    traceback.print_exc()
