import struct
data = open(r'C:/git/bt411/content/BTL4OPT.EXE','rb').read()
pe = struct.unpack_from('<I', data, 0x3c)[0]
nsec = struct.unpack_from('<H', data, pe+6)[0]
opt = struct.unpack_from('<H', data, pe+20)[0]
secs = []
for i in range(nsec):
    o = pe+24+opt+i*40
    name = data[o:o+8].rstrip(b'\0').decode()
    vsz, vaddr_, rsz, roff = struct.unpack_from('<4I', data, o+8)
    secs.append((name, vaddr_+0x400000, vsz, roff, rsz))
def vaddr(o):
    for name, va, vsz, roff, rsz in secs:
        if roff <= o < roff+rsz: return o - roff + va
    return None
n = len(data)
def valid_entry(i):
    if i+24 > n: return False
    k, ch, t, ridx, mask, mid = struct.unpack_from('<6I', data, i)
    return (k in (0,1,3) and ch < 0x60 and t in (0,1)
            and (ridx < 0x100 or ridx == 0xffffffff)
            and mask != 0
            and mid < 0x1000)
found = 0
i = 0
while i < n - 8:
    cnt = struct.unpack_from('<I', data, i)[0]
    if 3 <= cnt <= 80:
        ok = all(valid_entry(i+4+j*24) for j in range(cnt))
        # also require the NEXT dword after table to NOT be a valid entry (table boundary) - skip, just report
        if ok:
            print(f"--- table @VA 0x{vaddr(i):06x} count={cnt}")
            for j in range(cnt):
                o = i+4+j*24
                k, ch, t, ridx, mask, mid = struct.unpack_from('<6I', data, o)
                r = 'SELF' if ridx == 0xffffffff else f'{ridx}'
                print(f"  kind={k} chan=0x{ch:02x} typ={'evt' if t==1 else 'dir'} roster={r} mask=0x{mask:x} id/ofs=0x{mid:x}")
            found += 1
            i += 4 + cnt*24
            continue
    i += 4
print('tables:', found)
