import struct, sys
from capstone import *

PATH = r"C:\git\bt411\content\BTL4OPT.EXE"
data = open(PATH, "rb").read()
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
coff = e_lfanew + 4
num_sec = struct.unpack_from("<H", data, coff+2)[0]
opt_size = struct.unpack_from("<H", data, coff+16)[0]
opt = coff + 20
image_base = struct.unpack_from("<I", data, opt+28)[0]
sec_tbl = opt + opt_size
secs = []
for i in range(num_sec):
    off = sec_tbl + i*40
    vsize, va, rawsize, rawptr = struct.unpack_from("<IIII", data, off+8)
    secs.append((va, vsize, rawptr, rawsize))

def va_to_off(va):
    rva = va - image_base
    for sva, vsize, rawptr, rawsize in secs:
        if sva <= rva < sva + max(vsize, rawsize):
            return rawptr + (rva - sva)
    return None

def read_va(va, n):
    o = va_to_off(va)
    return data[o:o+n] if o is not None else None

def fconst(va):
    b = read_va(va, 4)
    return struct.unpack("<f", b)[0] if b else None

FN = int(sys.argv[1], 16)
LEN = int(sys.argv[2], 16) if len(sys.argv) > 2 else 0x140
code = read_va(FN, LEN)
md = Cs(CS_ARCH_X86, CS_MODE_32); md.detail = True
known = {"0x408328":"SinCos","0x4dcd94":"Round(ftol ST0)","0x40954c":"Quat->Euler",
         "0x4700bc":"NumericDisplay::ctor","0x4700ac":"nameCopy","0x442aec":"bitMapBin.Get",
         "0x442c12":"bitMapBin.Release","0x444818":"GraphicGauge::ctor","0x474855":"GaugeConnDirectOf::ctor",
         "0x4c2ff8":"tiledBlit","0x402298":"operator new","0x4dcdec":"Round64"}
print(f"=== disasm @0x{FN:x} len=0x{LEN:x} ===")
for ins in md.disasm(code, FN):
    line = f"0x{ins.address:x}:  {ins.mnemonic:<7} {ins.op_str}"
    for tok in ins.op_str.replace("[", " ").replace("]", " ").split():
        if tok.startswith("0x"):
            try:
                a = int(tok, 16)
                if 0x4b0000 <= a <= 0x520000:
                    fv = fconst(a)
                    if fv is not None:
                        line += f"    ; [0x{a:x}]={fv:g}f"
            except ValueError:
                pass
    if ins.mnemonic == "call" and ins.op_str in known:
        line += f"    ; {known[ins.op_str]}"
    print(line)
    if ins.mnemonic == "ret":
        break
