/* radare2 - LGPL - Copyright 2025 - pancake */

#include <r_anal.h>
#include <r_lib.h>
#include <r_util.h>

typedef enum {
	CIL_OP_NONE,
	CIL_OP_I1, // 1 byte int
	CIL_OP_I4, // 4 byte int
	CIL_OP_I8, // 8 byte int
	CIL_OP_R4, // 4 byte float
	CIL_OP_R8, // 8 byte float
	CIL_OP_BR_S, // 1 byte branch offset
	CIL_OP_BR_L, // 4 byte branch offset
	CIL_OP_TOKEN, // 4 byte token
	CIL_OP_VAR_S, // 1 byte var index
	CIL_OP_VAR_L, // 2 byte var index
} CilOperandType;

typedef struct cil_instruction_t {
	const char *mnemonic;
	_RAnalOpType type;
	int size;
	CilOperandType operand;
} CilInstruction;

static const CilInstruction cil_instructions[256] = {
	[0x00] = { "nop", R_ANAL_OP_TYPE_NOP, 1, CIL_OP_NONE },
	[0x01] = { "break", R_ANAL_OP_TYPE_TRAP, 1, CIL_OP_NONE },
	[0x02] = { "ldarg.0", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x03] = { "ldarg.1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x04] = { "ldarg.2", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x05] = { "ldarg.3", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x06] = { "ldloc.0", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x07] = { "ldloc.1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x08] = { "ldloc.2", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x09] = { "ldloc.3", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x0a] = { "stloc.0", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x0b] = { "stloc.1", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x0c] = { "stloc.2", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x0d] = { "stloc.3", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x0e] = { "ldarg.s", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_VAR_S },
	[0x0f] = { "ldarga.s", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_VAR_S },
	[0x10] = { "starg.s", R_ANAL_OP_TYPE_STORE, 2, CIL_OP_VAR_S },
	[0x11] = { "ldloc.s", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_VAR_S },
	[0x12] = { "ldloca.s", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_VAR_S },
	[0x13] = { "stloc.s", R_ANAL_OP_TYPE_STORE, 2, CIL_OP_VAR_S },
	[0x14] = { "ldnull", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x15] = { "ldc.i4.m1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x16] = { "ldc.i4.0", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x17] = { "ldc.i4.1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x18] = { "ldc.i4.2", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x19] = { "ldc.i4.3", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1a] = { "ldc.i4.4", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1b] = { "ldc.i4.5", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1c] = { "ldc.i4.6", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1d] = { "ldc.i4.7", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1e] = { "ldc.i4.8", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x1f] = { "ldc.i4.s", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_I1 },
	[0x20] = { "ldc.i4", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_I4 },
	[0x21] = { "ldc.i8", R_ANAL_OP_TYPE_LOAD, 9, CIL_OP_I8 },
	[0x22] = { "ldc.r4", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_R4 },
	[0x23] = { "ldc.r8", R_ANAL_OP_TYPE_LOAD, 9, CIL_OP_R8 },
	[0x25] = { "dup", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x26] = { "pop", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x28] = { "call", R_ANAL_OP_TYPE_CALL, 5, CIL_OP_TOKEN },
	[0x2a] = { "ret", R_ANAL_OP_TYPE_RET, 1, CIL_OP_NONE },
	[0x2b] = { "br.s", R_ANAL_OP_TYPE_JMP, 2, CIL_OP_BR_S },
	[0x2c] = { "brfalse.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x2d] = { "brtrue.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x2e] = { "beq.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x2f] = { "bge.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x30] = { "bgt.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x31] = { "ble.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x32] = { "blt.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x33] = { "bne.un.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x34] = { "bge.un.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x35] = { "bgt.un.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x36] = { "ble.un.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x37] = { "blt.un.s", R_ANAL_OP_TYPE_CJMP, 2, CIL_OP_BR_S },
	[0x38] = { "br", R_ANAL_OP_TYPE_JMP, 5, CIL_OP_BR_L },
	[0x39] = { "brfalse", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3a] = { "brtrue", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3b] = { "beq", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3c] = { "bge", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3d] = { "bgt", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3e] = { "ble", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x3f] = { "blt", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x40] = { "bne.un", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x41] = { "bge.un", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x42] = { "bgt.un", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x43] = { "ble.un", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x44] = { "blt.un", R_ANAL_OP_TYPE_CJMP, 5, CIL_OP_BR_L },
	[0x45] = { "switch", R_ANAL_OP_TYPE_SWITCH, 0, CIL_OP_NONE }, // variable size
	[0x46] = { "ldind.i1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x47] = { "ldind.u1", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x48] = { "ldind.i2", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x49] = { "ldind.u2", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4a] = { "ldind.i4", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4b] = { "ldind.u4", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4c] = { "ldind.i8", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4d] = { "ldind.i", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4e] = { "ldind.r4", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x4f] = { "ldind.r8", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x50] = { "ldind.ref", R_ANAL_OP_TYPE_LOAD, 1, CIL_OP_NONE },
	[0x51] = { "stind.ref", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x52] = { "stind.i1", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x53] = { "stind.i2", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x54] = { "stind.i4", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x55] = { "stind.i8", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x56] = { "stind.r4", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x57] = { "stind.r8", R_ANAL_OP_TYPE_STORE, 1, CIL_OP_NONE },
	[0x58] = { "add", R_ANAL_OP_TYPE_ADD, 1, CIL_OP_NONE },
	[0x59] = { "sub", R_ANAL_OP_TYPE_SUB, 1, CIL_OP_NONE },
	[0x5a] = { "mul", R_ANAL_OP_TYPE_MUL, 1, CIL_OP_NONE },
	[0x5b] = { "div", R_ANAL_OP_TYPE_DIV, 1, CIL_OP_NONE },
	[0x5c] = { "div.un", R_ANAL_OP_TYPE_DIV, 1, CIL_OP_NONE },
	[0x5d] = { "rem", R_ANAL_OP_TYPE_MOD, 1, CIL_OP_NONE },
	[0x5e] = { "rem.un", R_ANAL_OP_TYPE_MOD, 1, CIL_OP_NONE },
	[0x5f] = { "and", R_ANAL_OP_TYPE_AND, 1, CIL_OP_NONE },
	[0x60] = { "or", R_ANAL_OP_TYPE_OR, 1, CIL_OP_NONE },
	[0x61] = { "xor", R_ANAL_OP_TYPE_XOR, 1, CIL_OP_NONE },
	[0x62] = { "shl", R_ANAL_OP_TYPE_SHL, 1, CIL_OP_NONE },
	[0x63] = { "shr", R_ANAL_OP_TYPE_SHR, 1, CIL_OP_NONE },
	[0x64] = { "shr.un", R_ANAL_OP_TYPE_SHR, 1, CIL_OP_NONE },
	[0x65] = { "neg", R_ANAL_OP_TYPE_NOT, 1, CIL_OP_NONE },
	[0x66] = { "not", R_ANAL_OP_TYPE_NOT, 1, CIL_OP_NONE },
	[0x67] = { "conv.i1", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x68] = { "conv.i2", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x69] = { "conv.i4", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6a] = { "conv.i8", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6b] = { "conv.r4", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6c] = { "conv.r8", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6d] = { "conv.u4", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6e] = { "conv.u8", R_ANAL_OP_TYPE_CAST, 1, CIL_OP_NONE },
	[0x6f] = { "callvirt", R_ANAL_OP_TYPE_CALL, 5, CIL_OP_TOKEN },
	[0x70] = { "cpobj", R_ANAL_OP_TYPE_STORE, 5, CIL_OP_TOKEN },
	[0x71] = { "ldobj", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x72] = { "ldstr", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x73] = { "newobj", R_ANAL_OP_TYPE_CALL, 5, CIL_OP_TOKEN },
	[0x74] = { "castclass", R_ANAL_OP_TYPE_CAST, 5, CIL_OP_TOKEN },
	[0x75] = { "isinst", R_ANAL_OP_TYPE_CMP, 5, CIL_OP_TOKEN },
	[0x79] = { "unbox", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x7b] = { "ldfld", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x7c] = { "ldflda", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x7d] = { "stfld", R_ANAL_OP_TYPE_STORE, 5, CIL_OP_TOKEN },
	[0x7e] = { "ldsfld", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x7f] = { "ldsflda", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x80] = { "stsfld", R_ANAL_OP_TYPE_STORE, 5, CIL_OP_TOKEN },
	[0x8c] = { "box", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x8d] = { "newarr", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0x8f] = { "ldelema", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xa3] = { "ldelem", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xa4] = { "stelem", R_ANAL_OP_TYPE_STORE, 5, CIL_OP_TOKEN },
	[0xa5] = { "unbox.any", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xc2] = { "refanyval", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xc6] = { "mkrefany", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xd0] = { "ldtoken", R_ANAL_OP_TYPE_LOAD, 5, CIL_OP_TOKEN },
	[0xd1] = { "conv.u2", R_ANAL_OP_TYPE_CAST, 1 },
	[0xd2] = { "conv.u1", R_ANAL_OP_TYPE_CAST, 1 },
	[0xd3] = { "conv.i", R_ANAL_OP_TYPE_CAST, 1 },
	[0xd4] = { "conv.ovf.i", R_ANAL_OP_TYPE_CAST, 1 },
	[0xd5] = { "conv.ovf.u", R_ANAL_OP_TYPE_CAST, 1 },
	[0xd6] = { "add.ovf", R_ANAL_OP_TYPE_ADD, 1 },
	[0xd7] = { "add.ovf.un", R_ANAL_OP_TYPE_ADD, 1 },
	[0xd8] = { "mul.ovf", R_ANAL_OP_TYPE_MUL, 1 },
	[0xd9] = { "mul.ovf.un", R_ANAL_OP_TYPE_MUL, 1 },
	[0xda] = { "sub.ovf", R_ANAL_OP_TYPE_SUB, 1 },
	[0xdb] = { "sub.ovf.un", R_ANAL_OP_TYPE_SUB, 1 },
	[0xdc] = { "endfinally", R_ANAL_OP_TYPE_RET, 1 },
	[0xdd] = { "leave", R_ANAL_OP_TYPE_JMP, 5 },
	[0xde] = { "leave.s", R_ANAL_OP_TYPE_JMP, 2 },
	[0xdf] = { "stind.i", R_ANAL_OP_TYPE_STORE, 1 },
	[0xe0] = { "conv.u", R_ANAL_OP_TYPE_CAST, 1 },
};

static const CilInstruction cil_fe_instructions[256] = {
	[0x00] = { "arglist", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_NONE },
	[0x01] = { "ceq", R_ANAL_OP_TYPE_CMP, 2, CIL_OP_NONE },
	[0x02] = { "cgt", R_ANAL_OP_TYPE_CMP, 2, CIL_OP_NONE },
	[0x03] = { "cgt.un", R_ANAL_OP_TYPE_CMP, 2, CIL_OP_NONE },
	[0x04] = { "clt", R_ANAL_OP_TYPE_CMP, 2, CIL_OP_NONE },
	[0x05] = { "clt.un", R_ANAL_OP_TYPE_CMP, 2, CIL_OP_NONE },
	[0x06] = { "ldftn", R_ANAL_OP_TYPE_LOAD, 6, CIL_OP_TOKEN },
	[0x07] = { "ldvirtftn", R_ANAL_OP_TYPE_LOAD, 6, CIL_OP_TOKEN },
	[0x09] = { "ldarg", R_ANAL_OP_TYPE_LOAD, 4, CIL_OP_VAR_L },
	[0x0a] = { "ldarga", R_ANAL_OP_TYPE_LOAD, 4, CIL_OP_VAR_L },
	[0x0b] = { "starg", R_ANAL_OP_TYPE_STORE, 4, CIL_OP_VAR_L },
	[0x0c] = { "ldloc", R_ANAL_OP_TYPE_LOAD, 4, CIL_OP_VAR_L },
	[0x0d] = { "ldloca", R_ANAL_OP_TYPE_LOAD, 4, CIL_OP_VAR_L },
	[0x0e] = { "stloc", R_ANAL_OP_TYPE_STORE, 4, CIL_OP_VAR_L },
	[0x0f] = { "localloc", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_NONE },
	[0x11] = { "endfilter", R_ANAL_OP_TYPE_RET, 2, CIL_OP_NONE },
	[0x12] = { "unaligned.", R_ANAL_OP_TYPE_LOAD, 3, CIL_OP_I1 },
	[0x13] = { "volatile.", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_NONE },
	[0x14] = { "tail.", R_ANAL_OP_TYPE_CALL, 2, CIL_OP_NONE },
	[0x15] = { "initobj", R_ANAL_OP_TYPE_STORE, 6, CIL_OP_TOKEN },
	[0x16] = { "constrained.", R_ANAL_OP_TYPE_LOAD, 6, CIL_OP_TOKEN },
	[0x17] = { "cpblk", R_ANAL_OP_TYPE_STORE, 2, CIL_OP_NONE },
	[0x18] = { "initblk", R_ANAL_OP_TYPE_STORE, 2, CIL_OP_NONE },
	[0x1a] = { "rethrow", R_ANAL_OP_TYPE_TRAP, 2, CIL_OP_NONE },
	[0x1c] = { "sizeof", R_ANAL_OP_TYPE_LOAD, 6, CIL_OP_TOKEN },
	[0x1d] = { "refanytype", R_ANAL_OP_TYPE_LOAD, 2, CIL_OP_NONE },
};
