# Capstone Python bindings, by Nguyen Anh Quynnh <aquynh@gmail.com>
import os
import sys

__all__ = [
    "Cs",
    "CsInsn",
    "cs_disasm_quick",
    "cs_disasm_lite",
    "cs_version",
    "cs_support",
    "version_bind",
    "debug",
    "CS_API_MAJOR",
    "CS_API_MINOR",
    "CS_VERSION_MAJOR",
    "CS_VERSION_MINOR",
    "CS_VERSION_EXTRA",
    "CS_ARCH_ARM",
    "CS_ARCH_AARCH64",
    "CS_ARCH_MIPS",
    "CS_ARCH_X86",
    "CS_ARCH_PPC",
    "CS_ARCH_SPARC",
    "CS_ARCH_SYSTEMZ",
    "CS_ARCH_XCORE",
    "CS_ARCH_M68K",
    "CS_ARCH_TMS320C64X",
    "CS_ARCH_M680X",
    "CS_ARCH_EVM",
    "CS_ARCH_MOS65XX",
    "CS_ARCH_WASM",
    "CS_ARCH_BPF",
    "CS_ARCH_RISCV",
    "CS_ARCH_SH",
    "CS_ARCH_TRICORE",
    "CS_ARCH_ALPHA",
    "CS_ARCH_HPPA",
    "CS_ARCH_LOONGARCH",
    "CS_ARCH_XTENSA",
    "CS_ARCH_ARC",
    "CS_ARCH_ALL",
    "CS_MODE_LITTLE_ENDIAN",
    "CS_MODE_BIG_ENDIAN",
    "CS_MODE_16",
    "CS_MODE_32",
    "CS_MODE_64",
    "CS_MODE_ARM",
    "CS_MODE_THUMB",
    "CS_MODE_MCLASS",
    "CS_MODE_V8",
    "CS_MODE_APPLE_PROPRIETARY",
    "CS_MODE_V9",
    "CS_MODE_QPX",
    "CS_MODE_SPE",
    "CS_MODE_BOOKE",
    "CS_MODE_PS",
    "CS_MODE_MIPS16",
    "CS_MODE_MIPS32",
    "CS_MODE_MIPS64",
    "CS_MODE_MICRO",
    "CS_MODE_MIPS1",
    "CS_MODE_MIPS2",
    "CS_MODE_MIPS32R2",
    "CS_MODE_MIPS32R3",
    "CS_MODE_MIPS32R5",
    "CS_MODE_MIPS32R6",
    "CS_MODE_MIPS3",
    "CS_MODE_MIPS4",
    "CS_MODE_MIPS5",
    "CS_MODE_MIPS64R2",
    "CS_MODE_MIPS64R3",
    "CS_MODE_MIPS64R5",
    "CS_MODE_MIPS64R6",
    "CS_MODE_OCTEON",
    "CS_MODE_OCTEONP",
    "CS_MODE_NANOMIPS",
    "CS_MODE_NMS1",
    "CS_MODE_I7200",
    "CS_MODE_MIPS_NOFLOAT",
    "CS_MODE_MIPS_PTR64",
    "CS_MODE_MICRO32R3",
    "CS_MODE_MICRO32R6",
    "CS_MODE_M68K_000",
    "CS_MODE_M68K_010",
    "CS_MODE_M68K_020",
    "CS_MODE_M68K_030",
    "CS_MODE_M68K_040",
    "CS_MODE_M68K_060",
    "CS_MODE_M68K_CPU32",
    "CS_MODE_M680X_6301",
    "CS_MODE_M680X_6309",
    "CS_MODE_M680X_6800",
    "CS_MODE_M680X_6801",
    "CS_MODE_M680X_6805",
    "CS_MODE_M680X_6808",
    "CS_MODE_M680X_6809",
    "CS_MODE_M680X_6811",
    "CS_MODE_M680X_CPU12",
    "CS_MODE_M680X_HCS08",
    "CS_MODE_M680X_RS08",
    "CS_MODE_M680X_HCS12X",
    "CS_MODE_BPF_CLASSIC",
    "CS_MODE_BPF_EXTENDED",
    "CS_MODE_RISCV32",
    "CS_MODE_RISCV64",
    "CS_MODE_RISCV_C",
    "CS_MODE_RISCVC",
    "CS_MODE_RISCV_FD",
    "CS_MODE_RISCV_F",
    "CS_MODE_RISCV_D",
    "CS_MODE_RISCV_V",
    "CS_MODE_RISCV_ZFINX",
    "CS_MODE_RISCV_ZDINX",
    "CS_MODE_RISCV_ZHINX",
    "CS_MODE_RISCV_ZHINXMIN",
    "CS_MODE_RISCV_ZCMP_ZCMT_ZCE",
    "CS_MODE_RISCV_ZCE",
    "CS_MODE_RISCV_ZCMP",
    "CS_MODE_RISCV_ZCMT",
    "CS_MODE_RISCV_ZICFISS",
    "CS_MODE_RISCV_EXPERIMENTAL_ZICFISS",
    "CS_MODE_RISCV_E",
    "CS_MODE_RISCV_A",
    "CS_MODE_RISCV_COREV",
    "CS_MODE_RISCV_XCVALU",
    "CS_MODE_RISCV_XCVBI",
    "CS_MODE_RISCV_XCVBITMANIP",
    "CS_MODE_RISCV_XCVELW",
    "CS_MODE_RISCV_XCVMAC",
    "CS_MODE_RISCV_XCVMEM",
    "CS_MODE_RISCV_XCVSIMD",
    "CS_MODE_RISCV_THEAD",
    "CS_MODE_RISCV_XTHEADBA",
    "CS_MODE_RISCV_XTHEADBS",
    "CS_MODE_RISCV_XTHEADCMO",
    "CS_MODE_RISCV_XTHEADCONDMOV",
    "CS_MODE_RISCV_XTHEADFMEMIDX",
    "CS_MODE_RISCV_XTHEADMAC",
    "CS_MODE_RISCV_XTHEADMEMIDX",
    "CS_MODE_RISCV_XTHEADMEMPAIR",
    "CS_MODE_RISCV_XTHEADSYNC",
    "CS_MODE_RISCV_XTHEADVDOT",
    "CS_MODE_RISCV_SIFIVE",
    "CS_MODE_RISCV_XSFVCP",
    "CS_MODE_RISCV_XSFVFNRCLIPXFQF",
    "CS_MODE_RISCV_XSFVFWMACCQQQ",
    "CS_MODE_RISCV_XSFVQMACCDOD",
    "CS_MODE_RISCV_XSFVQMACCQOQ",
    "CS_MODE_RISCV_BITMANIP",
    "CS_MODE_RISCV_ZBA",
    "CS_MODE_RISCV_ZBB",
    "CS_MODE_RISCV_ZBC",
    "CS_MODE_RISCV_ZBKB",
    "CS_MODE_RISCV_ZBKC",
    "CS_MODE_RISCV_ZBKX",
    "CS_MODE_RISCV_ZBS",
    "CS_MODE_MOS65XX_6502",
    "CS_MODE_MOS65XX_65C02",
    "CS_MODE_MOS65XX_W65C02",
    "CS_MODE_MOS65XX_65816",
    "CS_MODE_MOS65XX_65816_LONG_M",
    "CS_MODE_MOS65XX_65816_LONG_X",
    "CS_MODE_MOS65XX_65816_LONG_MX",
    "CS_MODE_SH2",
    "CS_MODE_SH2A",
    "CS_MODE_SH3",
    "CS_MODE_SH4",
    "CS_MODE_SH4A",
    "CS_MODE_SHFPU",
    "CS_MODE_SHDSP",
    "CS_MODE_TRICORE_110",
    "CS_MODE_TRICORE_120",
    "CS_MODE_TRICORE_130",
    "CS_MODE_TRICORE_131",
    "CS_MODE_TRICORE_160",
    "CS_MODE_TRICORE_161",
    "CS_MODE_TRICORE_162",
    "CS_MODE_TRICORE_180",
    "CS_MODE_HPPA_11",
    "CS_MODE_HPPA_20",
    "CS_MODE_HPPA_20W",
    "CS_MODE_LOONGARCH32",
    "CS_MODE_LOONGARCH64",
    "CS_MODE_SYSTEMZ_ARCH8",
    "CS_MODE_SYSTEMZ_ARCH9",
    "CS_MODE_SYSTEMZ_ARCH10",
    "CS_MODE_SYSTEMZ_ARCH11",
    "CS_MODE_SYSTEMZ_ARCH12",
    "CS_MODE_SYSTEMZ_ARCH13",
    "CS_MODE_SYSTEMZ_ARCH14",
    "CS_MODE_SYSTEMZ_Z10",
    "CS_MODE_SYSTEMZ_Z196",
    "CS_MODE_SYSTEMZ_ZEC12",
    "CS_MODE_SYSTEMZ_Z13",
    "CS_MODE_SYSTEMZ_Z14",
    "CS_MODE_SYSTEMZ_Z15",
    "CS_MODE_SYSTEMZ_Z16",
    "CS_MODE_SYSTEMZ_GENERIC",
    "CS_OPT_SYNTAX",
    "CS_OPT_SYNTAX_DEFAULT",
    "CS_OPT_SYNTAX_INTEL",
    "CS_OPT_SYNTAX_ATT",
    "CS_OPT_SYNTAX_NOREGNAME",
    "CS_OPT_SYNTAX_MASM",
    "CS_OPT_SYNTAX_MOTOROLA",
    "CS_OPT_SYNTAX_CS_REG_ALIAS",
    "CS_OPT_SYNTAX_NO_DOLLAR",
    "CS_OPT_SYNTAX_NO_ALIAS_TEXT",
    "CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED",
    "CS_OPT_DETAIL",
    "CS_OPT_DETAIL_REAL",
    "CS_OPT_MODE",
    "CS_OPT_ON",
    "CS_OPT_OFF",
    "CS_OPT_INVALID",
    "CS_OPT_MEM",
    "CS_OPT_SKIPDATA",
    "CS_OPT_SKIPDATA_SETUP",
    "CS_OPT_MNEMONIC",
    "CS_OPT_UNSIGNED",
    "CS_OPT_ONLY_OFFSET_BRANCH",
    "CS_ERR_OK",
    "CS_ERR_MEM",
    "CS_ERR_ARCH",
    "CS_ERR_HANDLE",
    "CS_ERR_CSH",
    "CS_ERR_MODE",
    "CS_ERR_OPTION",
    "CS_ERR_DETAIL",
    "CS_ERR_VERSION",
    "CS_ERR_MEMSETUP",
    "CS_ERR_DIET",
    "CS_ERR_SKIPDATA",
    "CS_ERR_X86_ATT",
    "CS_ERR_X86_INTEL",
    "CS_ERR_X86_MASM",
    "CS_SUPPORT_DIET",
    "CS_SUPPORT_X86_REDUCE",
    "CS_SKIPDATA_CALLBACK",
    "CS_OP_INVALID",
    "CS_OP_REG",
    "CS_OP_IMM",
    "CS_OP_FP",
    "CS_OP_PRED",
    "CS_OP_RESERVED_5",
    "CS_OP_RESERVED_6",
    "CS_OP_RESERVED_7",
    "CS_OP_RESERVED_8",
    "CS_OP_RESERVED_9",
    "CS_OP_RESERVED_10",
    "CS_OP_RESERVED_11",
    "CS_OP_RESERVED_12",
    "CS_OP_RESERVED_13",
    "CS_OP_RESERVED_14",
    "CS_OP_RESERVED_15",
    "CS_OP_SPECIAL",
    "CS_OP_MEM",
    "CS_OP_MEM_REG",
    "CS_OP_MEM_IMM",
    "CS_GRP_INVALID",
    "CS_GRP_JUMP",
    "CS_GRP_CALL",
    "CS_GRP_RET",
    "CS_GRP_INT",
    "CS_GRP_IRET",
    "CS_GRP_PRIVILEGE",
    "CS_GRP_BRANCH_RELATIVE",
    "CS_AC_INVALID",
    "CS_AC_READ",
    "CS_AC_WRITE",
    "CsError",
    "__version__",
]

UINT8_MAX = 0xFF
UINT16_MAX = 0xFFFF

# Capstone C interface

# API version
CS_API_MAJOR = 6
CS_API_MINOR = 0

# Package version
CS_VERSION_MAJOR = CS_API_MAJOR
CS_VERSION_MINOR = CS_API_MINOR
CS_VERSION_EXTRA = 0

__version__ = "%u.%u.%u" % (CS_VERSION_MAJOR, CS_VERSION_MINOR, CS_VERSION_EXTRA)

# architectures
CS_ARCH_ARM = 0
CS_ARCH_AARCH64 = 1
CS_ARCH_SYSTEMZ = 2
CS_ARCH_MIPS = 3
CS_ARCH_X86 = 4
CS_ARCH_PPC = 5
CS_ARCH_SPARC = 6
CS_ARCH_XCORE = 7
CS_ARCH_M68K = 8
CS_ARCH_TMS320C64X = 9
CS_ARCH_M680X = 10
CS_ARCH_EVM = 11
CS_ARCH_MOS65XX = 12
CS_ARCH_WASM = 13
CS_ARCH_BPF = 14
CS_ARCH_RISCV = 15
CS_ARCH_SH = 16
CS_ARCH_TRICORE = 17
CS_ARCH_ALPHA = 18
CS_ARCH_HPPA = 19
CS_ARCH_LOONGARCH = 20
CS_ARCH_XTENSA = 21
CS_ARCH_ARC = 22
CS_ARCH_MAX = 22
CS_ARCH_ALL = 0xFFFF

CS_MODE_AARCH64_ISA_BITS = 0x00FFFFF8
CS_MODE_VENDOR_AARCH64_BIT0 = 30

# disasm mode
CS_MODE_LITTLE_ENDIAN = 0  # little-endian mode (default mode)
CS_MODE_ARM = 0  # ARM mode
CS_MODE_16 = 1 << 1  # 16-bit mode (for X86)
CS_MODE_32 = 1 << 2  # 32-bit mode (for X86)
CS_MODE_64 = 1 << 3  # 64-bit mode (for X86, PPC)
CS_MODE_THUMB = 1 << 4  # ARM's Thumb mode, including Thumb-2
CS_MODE_MCLASS = 1 << 5  # ARM's Cortex-M series
CS_MODE_V8 = 1 << 6  # ARMv8 A32 encodings for ARM
CS_MODE_APPLE_PROPRIETARY = (
    1 << CS_MODE_VENDOR_AARCH64_BIT0
)  # Apple proprietary AArch64 instructions like AMX, MUL53, and others.
CS_MODE_MICRO = 1 << 4  # MicroMips mode (MIPS architecture)
CS_MODE_MIPS3 = 1 << 5  # Mips III ISA
CS_MODE_MIPS32R6 = 1 << 6  # Mips32r6 ISA
CS_MODE_MIPS2 = 1 << 7  # Mips II ISA
CS_MODE_V9 = 1 << 4  # Sparc V9 mode (for Sparc)
CS_MODE_QPX = 1 << 4  # Quad Processing eXtensions mode (PPC)
CS_MODE_SPE = 1 << 5  # Signal Processing Engine mode (PPC)
CS_MODE_BOOKE = 1 << 6  # Book-E mode (PPC)
CS_MODE_PS = 1 << 7  # Paired-singles mode (PPC)
CS_MODE_AIX_OS = 1 << 8  # PowerPC AIX-OS
CS_MODE_PWR7 = 1 << 9  # Power 7
CS_MODE_PWR8 = 1 << 10  # Power 8
CS_MODE_PWR9 = 1 << 11  # Power 9
CS_MODE_PWR10 = 1 << 12  # Power 10
CS_MODE_PPC_ISA_FUTURE = 1 << 13  # Power ISA Future
CS_MODE_MODERN_AIX_AS = 1 << 14  # PowerPC AIX-OS with modern assembly
CS_MODE_MSYNC = (
    1 << 15
)  # PowerPC Has only the msync instruction instead of sync. Implies BOOKE
CS_MODE_M68K_000 = 1 << 1  # M68K 68000 mode
CS_MODE_M68K_010 = 1 << 2  # M68K 68010 mode
CS_MODE_M68K_020 = 1 << 3  # M68K 68020 mode
CS_MODE_M68K_030 = 1 << 4  # M68K 68030 mode
CS_MODE_M68K_040 = 1 << 5  # M68K 68040 mode
CS_MODE_M68K_060 = 1 << 6  # M68K 68060 mode
CS_MODE_M68K_CPU32 = 1 << 7
CS_MODE_BIG_ENDIAN = 1 << 31  # big-endian mode
CS_MODE_MIPS16 = CS_MODE_16  # Generic mips16
CS_MODE_MIPS32 = CS_MODE_32  # Generic mips32
CS_MODE_MIPS64 = CS_MODE_64  # Generic mips64
CS_MODE_MICRO = 1 << 4  # microMips
CS_MODE_MIPS1 = 1 << 5  # Mips I ISA Support
CS_MODE_MIPS2 = 1 << 6  # Mips II ISA Support
CS_MODE_MIPS32R2 = 1 << 7  # Mips32r2 ISA Support
CS_MODE_MIPS32R3 = 1 << 8  # Mips32r3 ISA Support
CS_MODE_MIPS32R5 = 1 << 9  # Mips32r5 ISA Support
CS_MODE_MIPS32R6 = 1 << 10  # Mips32r6 ISA Support
CS_MODE_MIPS3 = 1 << 11  # MIPS III ISA Support
CS_MODE_MIPS4 = 1 << 12  # MIPS IV ISA Support
CS_MODE_MIPS5 = 1 << 13  # MIPS V ISA Support
CS_MODE_MIPS64R2 = 1 << 14  # Mips64r2 ISA Support
CS_MODE_MIPS64R3 = 1 << 15  # Mips64r3 ISA Support
CS_MODE_MIPS64R5 = 1 << 16  # Mips64r5 ISA Support
CS_MODE_MIPS64R6 = 1 << 17  # Mips64r6 ISA Support
CS_MODE_OCTEON = 1 << 18  # Octeon cnMIPS Support
CS_MODE_OCTEONP = 1 << 19  # Octeon+ cnMIPS Support
CS_MODE_NANOMIPS = 1 << 20  # Generic nanomips
CS_MODE_NMS1 = (1 << 21) | CS_MODE_NANOMIPS  # nanoMips NMS1
CS_MODE_I7200 = (1 << 22) | CS_MODE_NANOMIPS  # nanoMips I7200
CS_MODE_MIPS_NOFLOAT = 1 << 23  # Disable floating points ops
CS_MODE_MIPS_PTR64 = 1 << 24  # Mips pointers are 64-bit
CS_MODE_MICRO32R3 = CS_MODE_MICRO | CS_MODE_MIPS32R3  # microMips32r3
CS_MODE_MICRO32R6 = CS_MODE_MICRO | CS_MODE_MIPS32R6  # microMips32r6
CS_MODE_M680X_6301 = 1 << 1  # M680X HD6301/3 mode
CS_MODE_M680X_6309 = 1 << 2  # M680X HD6309 mode
CS_MODE_M680X_6800 = 1 << 3  # M680X M6800/2 mode
CS_MODE_M680X_6801 = 1 << 4  # M680X M6801/3 mode
CS_MODE_M680X_6805 = 1 << 5  # M680X M6805 mode
CS_MODE_M680X_6808 = 1 << 6  # M680X M68HC08 mode
CS_MODE_M680X_6809 = 1 << 7  # M680X M6809 mode
CS_MODE_M680X_6811 = 1 << 8  # M680X M68HC11 mode
CS_MODE_M680X_CPU12 = 1 << 9  # M680X CPU12 mode
CS_MODE_M680X_HCS08 = 1 << 10  # M680X HCS08 mode
CS_MODE_M680X_RS08 = 1 << 11  # M680X RS08 mode
CS_MODE_M680X_HCS12X = 1 << 12  # M680X HCS12X mode
CS_MODE_BPF_CLASSIC = 0  # Classic BPF mode (default)
CS_MODE_BPF_EXTENDED = 1 << 0  # Extended BPF mode
CS_MODE_RISCV32 = 1 << 0  # RISCV32 mode
CS_MODE_RISCV64 = 1 << 1  # RISCV64 mode
CS_MODE_RISCV_C = 1 << 2  # RISCV compressed instructure mode
CS_MODE_RISCVC = CS_MODE_RISCV_C  # Backward compatibility alias
CS_MODE_RISCV_FD = 1 << 3
CS_MODE_RISCV_F = CS_MODE_RISCV_FD
CS_MODE_RISCV_D = CS_MODE_RISCV_FD
CS_MODE_RISCV_V = 1 << 4
CS_MODE_RISCV_ZFINX = 1 << 5
CS_MODE_RISCV_ZDINX = CS_MODE_RISCV_ZFINX
CS_MODE_RISCV_ZHINX = CS_MODE_RISCV_ZFINX
CS_MODE_RISCV_ZHINXMIN = CS_MODE_RISCV_ZFINX
CS_MODE_RISCV_ZCMP_ZCMT_ZCE = 1 << 6
CS_MODE_RISCV_ZCE = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
CS_MODE_RISCV_ZCMP = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
CS_MODE_RISCV_ZCMT = CS_MODE_RISCV_ZCMP_ZCMT_ZCE
CS_MODE_RISCV_ZICFISS = 1 << 7
CS_MODE_RISCV_EXPERIMENTAL_ZICFISS = CS_MODE_RISCV_ZICFISS
CS_MODE_RISCV_E = 1 << 8
CS_MODE_RISCV_A = 1 << 9
CS_MODE_RISCV_COREV = 1 << 10
CS_MODE_RISCV_XCVALU = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVBI = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVBITMANIP = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVELW = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVMAC = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVMEM = CS_MODE_RISCV_COREV
CS_MODE_RISCV_XCVSIMD = CS_MODE_RISCV_COREV
CS_MODE_RISCV_THEAD = 1 << 11
CS_MODE_RISCV_XTHEADBA = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADBS = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADCMO = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADCONDMOV = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADFMEMIDX = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADMAC = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADMEMIDX = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADMEMPAIR = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADSYNC = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_XTHEADVDOT = CS_MODE_RISCV_THEAD
CS_MODE_RISCV_SIFIVE = 1 << 12
CS_MODE_RISCV_XSFVCP = CS_MODE_RISCV_SIFIVE
CS_MODE_RISCV_XSFVFNRCLIPXFQF = CS_MODE_RISCV_SIFIVE
CS_MODE_RISCV_XSFVFWMACCQQQ = CS_MODE_RISCV_SIFIVE
CS_MODE_RISCV_XSFVQMACCDOD = CS_MODE_RISCV_SIFIVE
CS_MODE_RISCV_XSFVQMACCQOQ = CS_MODE_RISCV_SIFIVE
CS_MODE_RISCV_BITMANIP = 1 << 13
CS_MODE_RISCV_ZBA = 1 << 14
CS_MODE_RISCV_ZBB = 1 << 15
CS_MODE_RISCV_ZBC = 1 << 16
CS_MODE_RISCV_ZBKB = 1 << 17
CS_MODE_RISCV_ZBKC = 1 << 18
CS_MODE_RISCV_ZBKX = 1 << 19
CS_MODE_RISCV_ZBS = 1 << 20
CS_MODE_MOS65XX_6502 = 1 << 1  # MOS65XXX MOS 6502
CS_MODE_MOS65XX_65C02 = 1 << 2  # MOS65XXX WDC 65c02
CS_MODE_MOS65XX_W65C02 = 1 << 3  # MOS65XXX WDC W65c02
CS_MODE_MOS65XX_65816 = 1 << 4  # MOS65XXX WDC 65816, 8-bit m/x
CS_MODE_MOS65XX_65816_LONG_M = 1 << 5  # MOS65XXX WDC 65816, 16-bit m, 8-bit x
CS_MODE_MOS65XX_65816_LONG_X = 1 << 6  # MOS65XXX WDC 65816, 8-bit m, 16-bit x
CS_MODE_MOS65XX_65816_LONG_MX = (
    CS_MODE_MOS65XX_65816_LONG_M | CS_MODE_MOS65XX_65816_LONG_X
)
CS_MODE_SH2 = 1 << 1  # SH2
CS_MODE_SH2A = 1 << 2  # SH2A
CS_MODE_SH3 = 1 << 3  # SH3
CS_MODE_SH4 = 1 << 4  # SH4
CS_MODE_SH4A = 1 << 5  # SH4A
CS_MODE_SHFPU = 1 << 6  # w/ FPU
CS_MODE_SHDSP = 1 << 7  # w/ DSP
CS_MODE_TRICORE_110 = 1 << 1  # Tricore 1.1
CS_MODE_TRICORE_120 = 1 << 2  # Tricore 1.2
CS_MODE_TRICORE_130 = 1 << 3  # Tricore 1.3
CS_MODE_TRICORE_131 = 1 << 4  # Tricore 1.3.1
CS_MODE_TRICORE_160 = 1 << 5  # Tricore 1.6
CS_MODE_TRICORE_161 = 1 << 6  # Tricore 1.6.1
CS_MODE_TRICORE_162 = 1 << 7  # Tricore 1.6.2
CS_MODE_TRICORE_180 = 1 << 8  # Tricore 1.8.0
CS_MODE_HPPA_11 = 1 << 1  # HPPA 1.1
CS_MODE_HPPA_20 = 1 << 2  # HPPA 2.0
CS_MODE_HPPA_20W = CS_MODE_HPPA_20 | (1 << 3)  # HPPA 2.0 wide
CS_MODE_LOONGARCH32 = 1 << 0
CS_MODE_LOONGARCH64 = 1 << 1
CS_MODE_SYSTEMZ_ARCH8 = 1 << 1
CS_MODE_SYSTEMZ_ARCH9 = 1 << 2
CS_MODE_SYSTEMZ_ARCH10 = 1 << 3
CS_MODE_SYSTEMZ_ARCH11 = 1 << 4
CS_MODE_SYSTEMZ_ARCH12 = 1 << 5
CS_MODE_SYSTEMZ_ARCH13 = 1 << 6
CS_MODE_SYSTEMZ_ARCH14 = 1 << 7
CS_MODE_SYSTEMZ_Z10 = 1 << 8
CS_MODE_SYSTEMZ_Z196 = 1 << 9
CS_MODE_SYSTEMZ_ZEC12 = 1 << 10
CS_MODE_SYSTEMZ_Z13 = 1 << 11
CS_MODE_SYSTEMZ_Z14 = 1 << 12
CS_MODE_SYSTEMZ_Z15 = 1 << 13
CS_MODE_SYSTEMZ_Z16 = 1 << 14
CS_MODE_SYSTEMZ_GENERIC = 1 << 15

# Capstone option type
CS_OPT_INVALID = 0  # No option specified
CS_OPT_SYNTAX = 1  # Intel X86 asm syntax (CS_ARCH_X86 arch)
CS_OPT_DETAIL = 2  # Break down instruction structure into details
CS_OPT_MODE = 3  # Change engine's mode at run-time
CS_OPT_MEM = 4  # Change engine's mode at run-time
CS_OPT_SKIPDATA = 5  # Skip data when disassembling
CS_OPT_SKIPDATA_SETUP = 6  # Setup user-defined function for SKIPDATA option
CS_OPT_MNEMONIC = 7  # Customize instruction mnemonic
CS_OPT_UNSIGNED = 8  # Print immediate in unsigned form
CS_OPT_ONLY_OFFSET_BRANCH = 9  # ARM, prints branch immediates without offset.

# Capstone option value
CS_OPT_OFF = 0  # Turn OFF an option - default option of CS_OPT_DETAIL
CS_OPT_ON = 1 << 0  # Turn ON an option (CS_OPT_DETAIL)

# Common instruction operand types - to be consistent across all architectures.
CS_OP_INVALID = 0  # uninitialized/invalid operand.
CS_OP_REG = 1  # Register operand.
CS_OP_IMM = 2  # Immediate operand.
CS_OP_FP = 3  # Floating-Point operand.
CS_OP_PRED = 4  # Predicate operand.
CS_OP_RESERVED_5 = 5
CS_OP_RESERVED_6 = 6
CS_OP_RESERVED_7 = 7
CS_OP_RESERVED_8 = 8
CS_OP_RESERVED_9 = 9
CS_OP_RESERVED_10 = 10
CS_OP_RESERVED_11 = 11
CS_OP_RESERVED_12 = 12
CS_OP_RESERVED_13 = 13
CS_OP_RESERVED_14 = 14
CS_OP_RESERVED_15 = 15
CS_OP_SPECIAL = 0x10  # Special operands from archs
CS_OP_MEM = 0x80  # Memory operand. Can be ORed with another operand type.
CS_OP_MEM_REG = CS_OP_MEM | CS_OP_REG  # Memory referencing register operand.
CS_OP_MEM_IMM = CS_OP_MEM | CS_OP_IMM  # Memory referencing immediate operand.

# Common instruction groups - to be consistent across all architectures.
CS_GRP_INVALID = 0  # uninitialized/invalid group.
CS_GRP_JUMP = 1  # all jump instructions (conditional+direct+indirect jumps)
CS_GRP_CALL = 2  # all call instructions
CS_GRP_RET = 3  # all return instructions
CS_GRP_INT = 4  # all interrupt instructions (int+syscall)
CS_GRP_IRET = 5  # all interrupt return instructions
CS_GRP_PRIVILEGE = 6  # all privileged instructions
CS_GRP_BRANCH_RELATIVE = 7  # all relative branching instructions

# Access types for instruction operands.
CS_AC_INVALID = 0  # Invalid/uninitialized access type.
CS_AC_READ = 1 << 0  # Operand that is read from.
CS_AC_WRITE = 1 << 1  # Operand that is written to.
CS_AC_READ_WRITE = CS_AC_READ | CS_AC_WRITE

# Capstone syntax value
CS_OPT_SYNTAX_DEFAULT = (
    1 << 1
)  # Default assembly syntax of all platforms (CS_OPT_SYNTAX)
CS_OPT_SYNTAX_INTEL = (
    1 << 2
)  # Intel X86 asm syntax - default syntax on X86 (CS_OPT_SYNTAX, CS_ARCH_X86)
CS_OPT_SYNTAX_ATT = 1 << 3  # ATT asm syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
CS_OPT_SYNTAX_NOREGNAME = (
    1 << 4
)  # Asm syntax prints register name with only number - (CS_OPT_SYNTAX, CS_ARCH_PPC, CS_ARCH_ARM)
CS_OPT_SYNTAX_MASM = 1 << 5  # MASM syntax (CS_OPT_SYNTAX, CS_ARCH_X86)
CS_OPT_SYNTAX_MOTOROLA = 1 << 6  # MOS65XX use $ as hex prefix
CS_OPT_SYNTAX_CS_REG_ALIAS = (
    1 << 7
)  # Prints common register alias which are not defined in LLVM (ARM: r9 = sb etc.)
CS_OPT_SYNTAX_PERCENT = 1 << 8  # Prints the % in front of PPC registers.
CS_OPT_SYNTAX_NO_DOLLAR = (
    1 << 9
)  # Does not print the $ in front of Mips, LoongArch registers.
CS_OPT_SYNTAX_NO_ALIAS_TEXT = (
    1 << 10
)  # Does not print an instruction's alias test if the instruction is an alias
CS_OPT_SYNTAX_NO_ALIAS_TEXT_COMPRESSED = (
    1 << 11
)  # Like the one above it, but only supresses compressed instruction aliases
CS_OPT_DETAIL_REAL = (
    1 << 1
)  # If enabled, always sets the real instruction detail.Even if the instruction is an alias.

# Capstone error type
CS_ERR_OK = 0  # No error: everything was fine
CS_ERR_MEM = 1  # Out-Of-Memory error: cs_open(), cs_disasm()
CS_ERR_ARCH = 2  # Unsupported architecture: cs_open()
CS_ERR_HANDLE = 3  # Invalid handle: cs_op_count(), cs_op_index()
CS_ERR_CSH = 4  # Invalid csh argument: cs_close(), cs_errno(), cs_option()
CS_ERR_MODE = 5  # Invalid/unsupported mode: cs_open()
CS_ERR_OPTION = 6  # Invalid/unsupported option: cs_option()
CS_ERR_DETAIL = 7  # Invalid/unsupported option: cs_option()
CS_ERR_MEMSETUP = 8
CS_ERR_VERSION = 9  # Unsupported version (bindings)
CS_ERR_DIET = 10  # Information irrelevant in diet engine
CS_ERR_SKIPDATA = 11  # Access irrelevant data for "data" instruction in SKIPDATA mode
CS_ERR_X86_ATT = 12  # X86 AT&T syntax is unsupported (opt-out at compile time)
CS_ERR_X86_INTEL = 13  # X86 Intel syntax is unsupported (opt-out at compile time)
CS_ERR_X86_MASM = 14  # X86 Intel syntax is unsupported (opt-out at compile time)

# query id for cs_support()
CS_SUPPORT_DIET = CS_ARCH_ALL + 1
CS_SUPPORT_X86_REDUCE = CS_ARCH_ALL + 2

# Capstone reverse lookup
CS_AC = {v: k for k, v in locals().items() if k.startswith("CS_AC_")}
CS_ARCH = {v: k for k, v in locals().items() if k.startswith("CS_ARCH_")}
CS_ERR = {v: k for k, v in locals().items() if k.startswith("CS_ERR_")}
CS_GRP = {v: k for k, v in locals().items() if k.startswith("CS_GRP_")}
CS_MODE = {v: k for k, v in locals().items() if k.startswith("CS_MODE_")}
CS_OP = {v: k for k, v in locals().items() if k.startswith("CS_OP_")}
CS_OPT = {v: k for k, v in locals().items() if k.startswith("CS_OPT_")}

import ctypes, ctypes.util
from os.path import split, join, dirname
import sysconfig
from pathlib import PurePath

import inspect

if sys.version_info >= (3, 9):
    import importlib.resources as resources
else:
    import importlib_resources as resources

if not hasattr(sys.modules[__name__], "__file__"):
    __file__ = inspect.getfile(inspect.currentframe())

mode = 0
if sys.platform == "darwin":
    _lib = "libcapstone.dylib"
elif sys.platform in ("win32", "cygwin"):
    _lib = "capstone.dll"
else:
    _lib = "libcapstone.so"
    mode = getattr(os, 'RTLD_DEEPBIND', 0)

_found = False


def _load_lib(path):
    lib_file = join(path, _lib)
    if os.path.exists(lib_file):
        return ctypes.CDLL(lib_file, mode=mode)
    else:
        # if we're on linux, try again with .so.5 extension
        if lib_file.endswith(".so"):
            if os.path.exists(lib_file + ".{}".format(CS_VERSION_MAJOR)):
                return ctypes.CDLL(
                    lib_file + ".{}".format(CS_VERSION_MAJOR),
                    mode=mode
                )
    return None


_cs = None

# Loading attempts, in order
# - user-provided environment variable
# - importlib.resources can get us the path to the local libraries
# - we can get the path to the local libraries by parsing our filename
# - global load
# - python's lib directory
# - last-gasp attempt at some hardcoded paths on darwin and linux

_path_list = [
    os.getenv("LIBCAPSTONE_PATH", None),
    str(resources.files(__name__) / "lib"),
    join(split(__file__)[0], "lib"),
    "",
    sysconfig.get_path("platlib"),
    "/usr/local/lib/" if sys.platform == "darwin" else "/usr/lib64",
]

for _path in _path_list:
    if _path is None:
        continue
    _cs = _load_lib(_path)
    if _cs is not None:
        break
else:
    raise ImportError("ERROR: fail to load the dynamic library.")


# low-level structure for C code


def copy_ctypes(src):
    """Returns a new ctypes object which is a bitwise copy of an existing one"""
    dst = type(src)()
    ctypes.memmove(ctypes.byref(dst), ctypes.byref(src), ctypes.sizeof(type(src)))
    return dst


def copy_ctypes_list(src):
    return [copy_ctypes(n) for n in src]


# Weird import placement because these modules are needed by the below code but need the above functions
from . import (
    arm,
    aarch64,
    m68k,
    mips,
    ppc,
    sparc,
    systemz,
    x86,
    xcore,
    tms320c64x,
    m680x,
    evm,
    mos65xx,
    wasm,
    bpf,
    riscv,
    sh,
    tricore,
    alpha,
    hppa,
    loongarch,
    arc,
    xtensa,
)


class _cs_arch(ctypes.Union):
    _fields_ = (
        ("aarch64", aarch64.CsAArch64),
        ("arm", arm.CsArm),
        ("m68k", m68k.CsM68K),
        ("mips", mips.CsMips),
        ("x86", x86.CsX86),
        ("ppc", ppc.CsPpc),
        ("sparc", sparc.CsSparc),
        ("systemz", systemz.CsSystemZ),
        ("xcore", xcore.CsXcore),
        ("tms320c64x", tms320c64x.CsTMS320C64x),
        ("m680x", m680x.CsM680x),
        ("evm", evm.CsEvm),
        ("mos65xx", mos65xx.CsMOS65xx),
        ("wasm", wasm.CsWasm),
        ("bpf", bpf.CsBPF),
        ("riscv", riscv.CsRISCV),
        ("sh", sh.CsSH),
        ("tricore", tricore.CsTriCore),
        ("alpha", alpha.CsAlpha),
        ("hppa", hppa.CsHPPA),
        ("loongarch", loongarch.CsLoongArch),
        ("xtensa", xtensa.CsXtensa),
        ("arc", arc.CsARC),
    )


class _cs_detail(ctypes.Structure):
    _fields_ = (
        ("regs_read", ctypes.c_uint16 * 20),
        ("regs_read_count", ctypes.c_ubyte),
        ("regs_write", ctypes.c_uint16 * 47),
        ("regs_write_count", ctypes.c_ubyte),
        ("groups", ctypes.c_ubyte * 16),
        ("groups_count", ctypes.c_ubyte),
        ("writeback", ctypes.c_bool),
        ("arch", _cs_arch),
    )


class _cs_insn(ctypes.Structure):
    _fields_ = (
        ("id", ctypes.c_uint),
        ("alias_id", ctypes.c_uint64),
        ("address", ctypes.c_uint64),
        ("size", ctypes.c_uint16),
        ("bytes", ctypes.c_ubyte * 24),
        ("mnemonic", ctypes.c_char * 32),
        ("op_str", ctypes.c_char * 160),
        ("is_alias", ctypes.c_bool),
        ("usesAliasDetails", ctypes.c_bool),
        ("illegal", ctypes.c_bool),
        ("detail", ctypes.POINTER(_cs_detail)),
    )


# callback for SKIPDATA option
CS_SKIPDATA_CALLBACK = ctypes.CFUNCTYPE(
    ctypes.c_size_t,
    ctypes.POINTER(ctypes.c_char),
    ctypes.c_size_t,
    ctypes.c_size_t,
    ctypes.c_void_p,
)


class _cs_opt_skipdata(ctypes.Structure):
    _fields_ = (
        ("mnemonic", ctypes.c_char_p),
        ("callback", CS_SKIPDATA_CALLBACK),
        ("user_data", ctypes.c_void_p),
    )


class _cs_opt_mnem(ctypes.Structure):
    _fields_ = (
        ("id", ctypes.c_uint),
        ("mnemonic", ctypes.c_char_p),
    )


# setup all the function prototype
def _setup_prototype(lib, fname, restype, *argtypes):
    getattr(lib, fname).restype = restype
    getattr(lib, fname).argtypes = argtypes


_setup_prototype(
    _cs,
    "cs_open",
    ctypes.c_int,
    ctypes.c_uint,
    ctypes.c_uint,
    ctypes.POINTER(ctypes.c_size_t),
)
_setup_prototype(
    _cs,
    "cs_disasm",
    ctypes.c_size_t,
    ctypes.c_size_t,
    ctypes.POINTER(ctypes.c_char),
    ctypes.c_size_t,
    ctypes.c_uint64,
    ctypes.c_size_t,
    ctypes.POINTER(ctypes.POINTER(_cs_insn)),
)
_setup_prototype(
    _cs,
    "cs_disasm_iter",
    ctypes.c_bool,
    ctypes.c_size_t,
    ctypes.POINTER(ctypes.POINTER(ctypes.c_char)),
    ctypes.POINTER(ctypes.c_size_t),
    ctypes.POINTER(ctypes.c_uint64),
    ctypes.POINTER(_cs_insn),
)
_setup_prototype(_cs, "cs_free", None, ctypes.c_void_p, ctypes.c_size_t)
_setup_prototype(_cs, "cs_close", ctypes.c_int, ctypes.POINTER(ctypes.c_size_t))
_setup_prototype(_cs, "cs_reg_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
_setup_prototype(_cs, "cs_insn_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
_setup_prototype(_cs, "cs_group_name", ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint)
_setup_prototype(
    _cs,
    "cs_op_count",
    ctypes.c_int,
    ctypes.c_size_t,
    ctypes.POINTER(_cs_insn),
    ctypes.c_uint,
)
_setup_prototype(
    _cs,
    "cs_op_index",
    ctypes.c_int,
    ctypes.c_size_t,
    ctypes.POINTER(_cs_insn),
    ctypes.c_uint,
    ctypes.c_uint,
)
_setup_prototype(_cs, "cs_errno", ctypes.c_int, ctypes.c_size_t)
_setup_prototype(
    _cs, "cs_option", ctypes.c_int, ctypes.c_size_t, ctypes.c_int, ctypes.c_void_p
)
_setup_prototype(
    _cs,
    "cs_version",
    ctypes.c_int,
    ctypes.POINTER(ctypes.c_int),
    ctypes.POINTER(ctypes.c_int),
)
_setup_prototype(_cs, "cs_support", ctypes.c_bool, ctypes.c_int)
_setup_prototype(_cs, "cs_strerror", ctypes.c_char_p, ctypes.c_int)
_setup_prototype(
    _cs,
    "cs_regs_access",
    ctypes.c_int,
    ctypes.c_size_t,
    ctypes.POINTER(_cs_insn),
    ctypes.POINTER(ctypes.c_uint16 * 64),
    ctypes.POINTER(ctypes.c_uint8),
    ctypes.POINTER(ctypes.c_uint16 * 64),
    ctypes.POINTER(ctypes.c_uint8),
)


# access to error code via @errno of CsError
class CsError(Exception):
    def __init__(self, errno):
        self.errno = errno

    def __str__(self):
        return _cs.cs_strerror(self.errno).decode()


# return the core's version
def cs_version():
    major = ctypes.c_int()
    minor = ctypes.c_int()
    combined = _cs.cs_version(ctypes.byref(major), ctypes.byref(minor))
    return (major.value, minor.value, combined)


# return the binding's version
def version_bind():
    return (CS_API_MAJOR, CS_API_MINOR, (CS_API_MAJOR << 8) + CS_API_MINOR)


def cs_support(query):
    return _cs.cs_support(query)


# dummy class resembling Cs class, just for cs_disasm_quick()
# this class only need to be referenced to via 2 fields: @csh & @arch
class _dummy_cs(object):
    def __init__(self, csh, arch):
        self.csh = csh
        self.arch = arch
        self._detail = False


# Quick & dirty Python function to disasm raw binary code
# This function return CsInsn objects
# NOTE: you might want to use more efficient Cs class & its methods.
def cs_disasm_quick(arch, mode, code, offset, count=0):
    # verify version compatibility with the core before doing anything
    (major, minor, _combined) = cs_version()
    if major != CS_API_MAJOR or minor != CS_API_MINOR:
        # our binding version is different from the core's API version
        raise CsError(CS_ERR_VERSION)

    csh = ctypes.c_size_t()
    status = _cs.cs_open(arch, mode, ctypes.byref(csh))
    if status != CS_ERR_OK:
        raise CsError(status)

    all_insn = ctypes.POINTER(_cs_insn)()
    res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
    if res > 0:
        try:
            for i in range(res):
                yield CsInsn(_dummy_cs(csh, arch), all_insn[i])
        finally:
            _cs.cs_free(all_insn, res)
    else:
        status = _cs.cs_errno(csh)
        if status != CS_ERR_OK:
            raise CsError(status)
        return
        yield

    status = _cs.cs_close(ctypes.byref(csh))
    if status != CS_ERR_OK:
        raise CsError(status)


# Another quick, but lighter function to disasm raw binary code.
# This function is faster than cs_disasm_quick() around 20% because
# cs_disasm_lite() only return tuples of (address, size, mnemonic, op_str),
# rather than CsInsn objects.
# NOTE: you might want to use more efficient Cs class & its methods.
def cs_disasm_lite(arch, mode, code, offset, count=0):
    # verify version compatibility with the core before doing anything
    (major, minor, _combined) = cs_version()
    if major != CS_API_MAJOR or minor != CS_API_MINOR:
        # our binding version is different from the core's API version
        raise CsError(CS_ERR_VERSION)

    if cs_support(CS_SUPPORT_DIET):
        # Diet engine cannot provide @mnemonic & @op_str
        raise CsError(CS_ERR_DIET)

    csh = ctypes.c_size_t()
    status = _cs.cs_open(arch, mode, ctypes.byref(csh))
    if status != CS_ERR_OK:
        raise CsError(status)

    all_insn = ctypes.POINTER(_cs_insn)()
    res = _cs.cs_disasm(csh, code, len(code), offset, count, ctypes.byref(all_insn))
    if res > 0:
        try:
            for i in range(res):
                insn = all_insn[i]
                yield (
                    insn.address,
                    insn.size,
                    insn.mnemonic.decode("ascii"),
                    insn.op_str.decode("ascii"),
                )
        finally:
            _cs.cs_free(all_insn, res)
    else:
        status = _cs.cs_errno(csh)
        if status != CS_ERR_OK:
            raise CsError(status)
        return
        yield

    status = _cs.cs_close(ctypes.byref(csh))
    if status != CS_ERR_OK:
        raise CsError(status)


def _ascii_name_or_default(name, default):
    return default if name is None else name.decode("ascii")


# Python-style class to disasm code
class CsInsn(object):
    def __init__(self, cs, all_info):
        self._raw = copy_ctypes(all_info)
        self._cs = cs
        if self._cs._detail and not self.is_invalid_insn():
            # save detail
            self._raw.detail = ctypes.pointer(all_info.detail._type_())
            ctypes.memmove(
                ctypes.byref(self._raw.detail[0]),
                ctypes.byref(all_info.detail[0]),
                ctypes.sizeof(type(all_info.detail[0])),
            )

    def __repr__(self):
        return "<CsInsn 0x%x [%s]: %s %s>" % (
            self.address,
            self.bytes.hex(),
            self.mnemonic,
            self.op_str,
        )

    # return if the instruction is invalid
    def is_invalid_insn(self):
        arch = self._cs.arch
        if arch == CS_ARCH_EVM:
            return self.id == evm.EVM_INS_INVALID
        else:
            return self.id == 0

    # return instruction's ID.
    @property
    def id(self):
        return self._raw.id

    # return instruction's address.
    @property
    def address(self):
        return self._raw.address

    # return instruction's size.
    @property
    def size(self):
        return self._raw.size

    # return instruction's is_alias flag
    @property
    def is_alias(self):
        return self._raw.is_alias

    # return instruction's illegal flag
    # Set if instruction can be decoded but is invalid
    # due to context or illegal operands.
    @property
    def illegal(self):
        return self._raw.illegal

    # return instruction's alias_id
    @property
    def alias_id(self):
        return self._raw.alias_id

    # return instruction's flag if it uses alias details
    @property
    def uses_alias_details(self):
        return self._raw.usesAliasDetails

    # return instruction's machine bytes (which should have @size bytes).
    @property
    def bytes(self):
        return bytearray(self._raw.bytes)[: self._raw.size]

    # return instruction's mnemonic.
    @property
    def mnemonic(self):
        if self._cs._diet:
            # Diet engine cannot provide @mnemonic.
            raise CsError(CS_ERR_DIET)

        return self._raw.mnemonic.decode("ascii")

    # return instruction's operands (in string).
    @property
    def op_str(self):
        if self._cs._diet:
            # Diet engine cannot provide @op_str.
            raise CsError(CS_ERR_DIET)

        return self._raw.op_str.decode("ascii")

    # return list of all implicit registers being read.
    @property
    def regs_read(self):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide @regs_read.
            raise CsError(CS_ERR_DIET)

        if self._cs._detail:
            return self._raw.detail.contents.regs_read[
                : self._raw.detail.contents.regs_read_count
            ]

        raise CsError(CS_ERR_DETAIL)

    # return list of all implicit registers being modified
    @property
    def regs_write(self):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide @regs_write
            raise CsError(CS_ERR_DIET)

        if self._cs._detail:
            return self._raw.detail.contents.regs_write[
                : self._raw.detail.contents.regs_write_count
            ]

        raise CsError(CS_ERR_DETAIL)

    # return list of semantic groups this instruction belongs to.
    @property
    def groups(self):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide @groups
            raise CsError(CS_ERR_DIET)

        if self._cs._detail:
            return self._raw.detail.contents.groups[
                : self._raw.detail.contents.groups_count
            ]

        raise CsError(CS_ERR_DETAIL)

    # return whether instruction has writeback operands.
    @property
    def writeback(self):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide @writeback.
            raise CsError(CS_ERR_DIET)

        if self._cs._detail:
            return self._raw.detail.contents.writeback

        raise CsError(CS_ERR_DETAIL)

    def __gen_detail(self):
        if self.is_invalid_insn():
            # do nothing in skipdata mode
            return

        arch = self._cs.arch
        if arch == CS_ARCH_ARM:
            (
                self.usermode,
                self.vector_size,
                self.vector_data,
                self.cps_mode,
                self.cps_flag,
                self.cc,
                self.vcc,
                self.update_flags,
                self.post_index,
                self.mem_barrier,
                self.pred_mask,
                self.operands,
            ) = arm.get_arch_info(self._raw.detail.contents.arch.arm)
        elif arch == CS_ARCH_AARCH64:
            (self.cc, self.update_flags, self.post_index, self.operands) = (
                aarch64.get_arch_info(self._raw.detail.contents.arch.aarch64)
            )
        elif arch == CS_ARCH_X86:
            (
                self.prefix,
                self.opcode,
                self.rex,
                self.addr_size,
                self.modrm,
                self.sib,
                self.disp,
                self.sib_index,
                self.sib_scale,
                self.sib_base,
                self.xop_cc,
                self.sse_cc,
                self.avx_cc,
                self.avx_sae,
                self.avx_rm,
                self.eflags,
                self.fpu_flags,
                self.encoding,
                self.modrm_offset,
                self.disp_offset,
                self.disp_size,
                self.imm_offset,
                self.imm_size,
                self.operands,
            ) = x86.get_arch_info(self._raw.detail.contents.arch.x86)
        elif arch == CS_ARCH_M68K:
            (self.operands, self.op_size) = m68k.get_arch_info(
                self._raw.detail.contents.arch.m68k
            )
        elif arch == CS_ARCH_MIPS:
            self.operands = mips.get_arch_info(self._raw.detail.contents.arch.mips)
        elif arch == CS_ARCH_PPC:
            (self.bc, self.update_cr0, self.format, self.operands) = ppc.get_arch_info(
                self._raw.detail.contents.arch.ppc
            )
        elif arch == CS_ARCH_SPARC:
            (self.cc, self.cc_field, self.hint, self.format, self.operands) = (
                sparc.get_arch_info(self._raw.detail.contents.arch.sparc)
            )
        elif arch == CS_ARCH_SYSTEMZ:
            (self.cc, self.format, self.operands) = systemz.get_arch_info(
                self._raw.detail.contents.arch.systemz
            )
        elif arch == CS_ARCH_XCORE:
            (self.operands) = xcore.get_arch_info(self._raw.detail.contents.arch.xcore)
        elif arch == CS_ARCH_TMS320C64X:
            (self.condition, self.funit, self.parallel, self.operands) = (
                tms320c64x.get_arch_info(self._raw.detail.contents.arch.tms320c64x)
            )
        elif arch == CS_ARCH_M680X:
            (self.flags, self.operands) = m680x.get_arch_info(
                self._raw.detail.contents.arch.m680x
            )
        elif arch == CS_ARCH_EVM:
            (self.pop, self.push, self.fee) = evm.get_arch_info(
                self._raw.detail.contents.arch.evm
            )
        elif arch == CS_ARCH_MOS65XX:
            (self.am, self.modifies_flags, self.operands) = mos65xx.get_arch_info(
                self._raw.detail.contents.arch.mos65xx
            )
        elif arch == CS_ARCH_WASM:
            (self.operands) = wasm.get_arch_info(self._raw.detail.contents.arch.wasm)
        elif arch == CS_ARCH_BPF:
            (self.operands) = bpf.get_arch_info(self._raw.detail.contents.arch.bpf)
        elif arch == CS_ARCH_RISCV:
            (self.need_effective_addr, self.operands) = riscv.get_arch_info(
                self._raw.detail.contents.arch.riscv
            )
        elif arch == CS_ARCH_SH:
            (self.sh_insn, self.sh_size, self.operands) = sh.get_arch_info(
                self._raw.detail.contents.arch.sh
            )
        elif arch == CS_ARCH_TRICORE:
            (self.update_flags, self.operands) = tricore.get_arch_info(
                self._raw.detail.contents.arch.tricore
            )
        elif arch == CS_ARCH_ALPHA:
            (self.operands) = alpha.get_arch_info(self._raw.detail.contents.arch.alpha)
        elif arch == CS_ARCH_HPPA:
            (self.operands) = hppa.get_arch_info(self._raw.detail.contents.arch.hppa)
        elif arch == CS_ARCH_LOONGARCH:
            (self.format, self.operands) = loongarch.get_arch_info(
                self._raw.detail.contents.arch.loongarch
            )
        elif arch == CS_ARCH_ARC:
            (self.operands) = arc.get_arch_info(self._raw.detail.contents.arch.arc)
        elif arch == CS_ARCH_XTENSA:
            (self.operands) = xtensa.get_arch_info(
                self._raw.detail.contents.arch.xtensa
            )

    def __getattr__(self, name):
        if not self._cs._detail:
            raise CsError(CS_ERR_DETAIL)

        attr = object.__getattribute__
        if not attr(self, "_cs")._detail:
            raise AttributeError(f"'CsInsn' has no attribute '{name}'")
        _dict = attr(self, "__dict__")
        if "operands" not in _dict:
            self.__gen_detail()
        if name not in _dict:
            if self.is_invalid_insn():
                raise CsError(CS_ERR_SKIPDATA)
            raise AttributeError(f"'CsInsn' has no attribute '{name}'")
        return _dict[name]

    # get the last error code
    def errno(self):
        return _cs.cs_errno(self._cs.csh)

    # get the register name, given the register ID
    def reg_name(self, reg_id, default=None):
        if self._cs._diet:
            # Diet engine cannot provide register name
            raise CsError(CS_ERR_DIET)

        return _ascii_name_or_default(_cs.cs_reg_name(self._cs.csh, reg_id), default)

    # get the instruction name
    def insn_name(self, default=None):
        if self._cs._diet:
            # Diet engine cannot provide instruction name
            raise CsError(CS_ERR_DIET)

        if self.is_invalid_insn():
            return default

        return _ascii_name_or_default(_cs.cs_insn_name(self._cs.csh, self.id), default)

    # get the group name
    def group_name(self, group_id, default=None):
        if self._cs._diet:
            # Diet engine cannot provide group name
            raise CsError(CS_ERR_DIET)

        return _ascii_name_or_default(
            _cs.cs_group_name(self._cs.csh, group_id), default
        )

    # verify if this insn belong to group with id as @group_id
    def group(self, group_id):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide group information
            raise CsError(CS_ERR_DIET)

        return group_id in self.groups

    # verify if this instruction implicitly read register @reg_id
    def reg_read(self, reg_id):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide regs_read information
            raise CsError(CS_ERR_DIET)

        return reg_id in self.regs_read

    # verify if this instruction implicitly modified register @reg_id
    def reg_write(self, reg_id):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        if self._cs._diet:
            # Diet engine cannot provide regs_write information
            raise CsError(CS_ERR_DIET)

        return reg_id in self.regs_write

    # return number of operands having same operand type @op_type
    def op_count(self, op_type):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        c = 0
        for op in self.operands:
            if op.type == op_type:
                c += 1
        return c

    # get the operand at position @position of all operands having the same type @op_type
    def op_find(self, op_type, position):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        c = 0
        for op in self.operands:
            if op.type == op_type:
                c += 1
            if c == position:
                return op

    # Return (list-of-registers-read, list-of-registers-modified) by this instructions.
    # This includes all the implicit & explicit registers.
    def regs_access(self):
        if self.is_invalid_insn():
            raise CsError(CS_ERR_SKIPDATA)

        regs_read = (ctypes.c_uint16 * 64)()
        regs_read_count = ctypes.c_uint8()
        regs_write = (ctypes.c_uint16 * 64)()
        regs_write_count = ctypes.c_uint8()

        status = _cs.cs_regs_access(
            self._cs.csh,
            self._raw,
            ctypes.byref(regs_read),
            ctypes.byref(regs_read_count),
            ctypes.byref(regs_write),
            ctypes.byref(regs_write_count),
        )
        if status != CS_ERR_OK:
            raise CsError(status)

        if regs_read_count.value > 0:
            regs_read = regs_read[: regs_read_count.value]
        else:
            regs_read = []

        if regs_write_count.value > 0:
            regs_write = regs_write[: regs_write_count.value]
        else:
            regs_write = []

        return (regs_read, regs_write)


class Cs(object):
    def __init__(self, arch, mode):
        # verify version compatibility with the core before doing anything
        (major, minor, _combined) = cs_version()
        if major != CS_API_MAJOR or minor != CS_API_MINOR:
            self.csh = None
            # our binding version is different from the core's API version
            raise CsError(CS_ERR_VERSION)

        self.arch, self._mode = arch, mode
        self.csh = ctypes.c_size_t()
        status = _cs.cs_open(arch, mode, ctypes.byref(self.csh))
        if status != CS_ERR_OK:
            self.csh = None
            raise CsError(status)

        try:
            from . import ccapstone

            # rewire disasm to use the faster version
            setattr(self, "disasm", ccapstone.Cs(self).disasm)
        except:
            pass

        if arch == CS_ARCH_X86:
            # Intel syntax is default for X86
            self._syntax = CS_OPT_SYNTAX_INTEL
        else:
            self._syntax = None

        self._detail = False  # by default, do not produce instruction details
        self._imm_unsigned = (
            False  # by default, print immediate operands as signed numbers
        )
        self._diet = cs_support(CS_SUPPORT_DIET)
        self._x86reduce = cs_support(CS_SUPPORT_X86_REDUCE)

        # default mnemonic for SKIPDATA
        self._skipdata_mnem = ".byte"
        self._skipdata_cb = (None, None)
        # store reference to option object to avoid it being freed
        # because C code uses it by reference
        self._skipdata_opt = _cs_opt_skipdata()
        self._skipdata = False

    # destructor to be called automatically when object is destroyed.
    def __del__(self):
        if self.csh:
            try:
                status = _cs.cs_close(ctypes.byref(self.csh))
                if status != CS_ERR_OK:
                    raise CsError(status)
            except:  # _cs might be pulled from under our feet
                pass

    def option(self, opt_type, opt_value):
        status = _cs.cs_option(self.csh, opt_type, opt_value)
        if status != CS_ERR_OK:
            raise CsError(status)
        if opt_type == CS_OPT_DETAIL or opt_type == CS_OPT_DETAIL_REAL:
            self._detail = (opt_value & CS_OPT_ON) != 0
        elif opt_type == CS_OPT_SKIPDATA:
            self._skipdata = opt_value == CS_OPT_ON
        elif opt_type == CS_OPT_UNSIGNED:
            self._imm_unsigned = opt_value == CS_OPT_ON

    # is this a diet engine?
    @property
    def diet(self):
        return self._diet

    # is this engine compiled with X86-reduce option?
    @property
    def x86_reduce(self):
        return self._x86reduce

    # return assembly syntax.
    @property
    def syntax(self):
        return self._syntax

    # syntax setter: modify assembly syntax.
    @syntax.setter
    def syntax(self, style):
        status = _cs.cs_option(self.csh, CS_OPT_SYNTAX, style)
        if status != CS_ERR_OK:
            raise CsError(status)
        # save syntax
        self._syntax = style

    # return current skipdata status
    @property
    def skipdata(self):
        return self._skipdata

    # setter: modify skipdata status
    @skipdata.setter
    def skipdata(self, opt):
        if opt == False:
            status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_OFF)
        else:
            status = _cs.cs_option(self.csh, CS_OPT_SKIPDATA, CS_OPT_ON)
        if status != CS_ERR_OK:
            raise CsError(status)

        # save this option
        self._skipdata = opt

    @property
    def skipdata_setup(self):
        return (self._skipdata_mnem,) + self._skipdata_cb

    @skipdata_setup.setter
    def skipdata_setup(self, opt):
        _mnem, _cb, _ud = opt
        self._skipdata_opt.mnemonic = _mnem.encode()
        self._skipdata_opt.callback = CS_SKIPDATA_CALLBACK(_cb or 0)
        self._skipdata_opt.user_data = ctypes.cast(_ud, ctypes.c_void_p)
        status = _cs.cs_option(
            self.csh,
            CS_OPT_SKIPDATA_SETUP,
            ctypes.cast(ctypes.byref(self._skipdata_opt), ctypes.c_void_p),
        )
        if status != CS_ERR_OK:
            raise CsError(status)

        self._skipdata_mnem = _mnem
        self._skipdata_cb = (_cb, _ud)

    @property
    def skipdata_mnem(self):
        return self._skipdata_mnem

    @skipdata_mnem.setter
    def skipdata_mnem(self, mnem):
        self.skipdata_setup = (mnem,) + self._skipdata_cb

    @property
    def skipdata_callback(self):
        return self._skipdata_cb

    @skipdata_callback.setter
    def skipdata_callback(self, val):
        if not isinstance(val, tuple):
            val = (val, None)
        func, data = val
        self.skipdata_setup = (self._skipdata_mnem, func, data)

    # customize instruction mnemonic
    def mnemonic_setup(self, id, mnem):
        _mnem_opt = _cs_opt_mnem()
        _mnem_opt.id = id
        if mnem:
            _mnem_opt.mnemonic = mnem.encode()
        else:
            _mnem_opt.mnemonic = mnem
        status = _cs.cs_option(
            self.csh,
            CS_OPT_MNEMONIC,
            ctypes.cast(ctypes.byref(_mnem_opt), ctypes.c_void_p),
        )
        if status != CS_ERR_OK:
            raise CsError(status)

    # check to see if this engine supports a particular arch,
    # or diet mode (depending on @query).
    def support(self, query):
        return cs_support(query)

    # is detail mode enable?
    @property
    def detail(self):
        return self._detail

    # modify detail mode.
    @detail.setter
    def detail(self, opt):  # opt is boolean type, so must be either 'True' or 'False'
        if opt == False:
            status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_OFF)
        else:
            status = _cs.cs_option(self.csh, CS_OPT_DETAIL, CS_OPT_ON)
        if status != CS_ERR_OK:
            raise CsError(status)
        # save detail
        self._detail = opt

    # is detail mode enable?
    @property
    def imm_unsigned(self):
        return self._imm_unsigned

    # modify detail mode.
    @imm_unsigned.setter
    def imm_unsigned(
        self, opt
    ):  # opt is boolean type, so must be either 'True' or 'False'
        if opt == False:
            status = _cs.cs_option(self.csh, CS_OPT_UNSIGNED, CS_OPT_OFF)
        else:
            status = _cs.cs_option(self.csh, CS_OPT_UNSIGNED, CS_OPT_ON)
        if status != CS_ERR_OK:
            raise CsError(status)
        # save detail
        self._imm_unsigned = opt

    # return disassembly mode of this engine.
    @property
    def mode(self):
        return self._mode

    # modify engine's mode at run-time.
    @mode.setter
    def mode(self, opt):  # opt is new disasm mode, of int type
        status = _cs.cs_option(self.csh, CS_OPT_MODE, opt)
        if status != CS_ERR_OK:
            raise CsError(status)
        # save mode
        self._mode = opt

    # get the last error code
    def errno(self):
        return _cs.cs_errno(self.csh)

    # get the register name, given the register ID
    def reg_name(self, reg_id, default=None):
        if self._diet:
            # Diet engine cannot provide register name
            raise CsError(CS_ERR_DIET)

        return _ascii_name_or_default(_cs.cs_reg_name(self.csh, reg_id), default)

    # get the instruction name, given the instruction ID
    def insn_name(self, insn_id, default=None):
        if self._diet:
            # Diet engine cannot provide instruction name
            raise CsError(CS_ERR_DIET)

        return _ascii_name_or_default(_cs.cs_insn_name(self.csh, insn_id), default)

    # get the group name
    def group_name(self, group_id, default=None):
        if self._diet:
            # Diet engine cannot provide group name
            raise CsError(CS_ERR_DIET)

        return _ascii_name_or_default(_cs.cs_group_name(self.csh, group_id), default)

    # Disassemble binary & return disassembled instructions in CsInsn objects
    def disasm(self, code, offset, count=0):
        all_insn = ctypes.POINTER(_cs_insn)()
        # Pass a bytearray by reference
        size = len(code)
        view = memoryview(code)
        if not view.readonly:
            code = ctypes.byref(ctypes.c_char.from_buffer(view))
        elif not isinstance(code, bytes):
            code = view.tobytes()
        res = _cs.cs_disasm(self.csh, code, size, offset, count, ctypes.byref(all_insn))
        if res > 0:
            try:
                for i in range(res):
                    yield CsInsn(self, all_insn[i])
            finally:
                _cs.cs_free(all_insn, res)
        else:
            status = _cs.cs_errno(self.csh)
            if status != CS_ERR_OK:
                raise CsError(status)
            return
            yield

    # This function matches the cs_disasm_iter implementation which
    # *should* be much faster via the C API due to pre-allocating
    # memory (https://www.capstone-engine.org/iteration.html).
    # Note: It is unclear whether this function via the Python
    # binding provides the same speedup like cs_disasm_lite.
    def disasm_iter(self, code, offset):
        if self._diet:
            # Diet engine cannot provide @mnemonic & @op_str
            raise CsError(CS_ERR_DIET)
        insn = _cs_insn()
        size = ctypes.c_size_t(len(code))

        # Pass a bytearray by reference
        view = memoryview(code)
        code = ctypes.pointer(ctypes.c_char.from_buffer_copy(view))
        if view.readonly:
            code = (ctypes.c_char * len(view)).from_buffer_copy(view)
        else:
            code = ctypes.pointer(ctypes.c_char.from_buffer(view))

        # since we are taking a pointer to a pointer, ctypes does not do
        # the typical auto conversion, so we have to cast it here.
        code = ctypes.cast(code, ctypes.POINTER(ctypes.c_char))
        address = ctypes.c_uint64(offset)
        while _cs.cs_disasm_iter(
            self.csh,
            ctypes.byref(code),
            ctypes.byref(size),
            ctypes.byref(address),
            ctypes.byref(insn),
        ):
            yield (
                insn.address,
                insn.size,
                insn.mnemonic.decode("ascii"),
                insn.op_str.decode("ascii"),
            )

    # Light function to disassemble binary. This is about 20% faster than disasm() because
    # unlike disasm(), disasm_lite() only return tuples of (address, size, mnemonic, op_str),
    # rather than CsInsn objects.
    def disasm_lite(self, code, offset, count=0):
        if self._diet:
            # Diet engine cannot provide @mnemonic & @op_str
            raise CsError(CS_ERR_DIET)

        all_insn = ctypes.POINTER(_cs_insn)()
        size = len(code)
        # Pass a bytearray by reference
        view = memoryview(code)
        if not view.readonly:
            code = ctypes.byref(ctypes.c_char.from_buffer(view))
        elif not isinstance(code, bytes):
            code = view.tobytes()
        res = _cs.cs_disasm(self.csh, code, size, offset, count, ctypes.byref(all_insn))
        if res > 0:
            try:
                for i in range(res):
                    insn = all_insn[i]
                    yield (
                        insn.address,
                        insn.size,
                        insn.mnemonic.decode("ascii"),
                        insn.op_str.decode("ascii"),
                    )
            finally:
                _cs.cs_free(all_insn, res)
        else:
            status = _cs.cs_errno(self.csh)
            if status != CS_ERR_OK:
                raise CsError(status)
            return
            yield


# print out debugging info
def debug():
    # is Cython there?
    try:
        from . import ccapstone

        return ccapstone.debug()
    except:
        # no Cython, fallback to Python code below
        pass

    if cs_support(CS_SUPPORT_DIET):
        diet = "diet"
    else:
        diet = "standard"

    archs = {
        "arm": CS_ARCH_ARM,
        "aarch64": CS_ARCH_AARCH64,
        "m68k": CS_ARCH_M68K,
        "mips": CS_ARCH_MIPS,
        "ppc": CS_ARCH_PPC,
        "sparc": CS_ARCH_SPARC,
        "systemz": CS_ARCH_SYSTEMZ,
        "xcore": CS_ARCH_XCORE,
        "tms320c64x": CS_ARCH_TMS320C64X,
        "m680x": CS_ARCH_M680X,
        "evm": CS_ARCH_EVM,
        "mos65xx": CS_ARCH_MOS65XX,
        "bpf": CS_ARCH_BPF,
        "riscv": CS_ARCH_RISCV,
        "tricore": CS_ARCH_TRICORE,
        "wasm": CS_ARCH_WASM,
        "sh": CS_ARCH_SH,
        "alpha": CS_ARCH_ALPHA,
        "hppa": CS_ARCH_HPPA,
        "loongarch": CS_ARCH_LOONGARCH,
        "xtensa": CS_ARCH_XTENSA,
        "arc": CS_ARCH_ARC,
    }

    all_archs = ""
    keys = archs.keys()
    for k in sorted(keys):
        if cs_support(archs[k]):
            all_archs += "-%s" % k

    if cs_support(CS_ARCH_X86):
        all_archs += "-x86"
        if cs_support(CS_SUPPORT_X86_REDUCE):
            all_archs += "_reduce"

    (major, minor, _combined) = cs_version()

    return "python-%s%s-c%u.%u-b%u.%u" % (
        diet,
        all_archs,
        major,
        minor,
        CS_API_MAJOR,
        CS_API_MINOR,
    )
