/* Capstone testing regression */
/* By Do Minh Tuan <tuanit96@gmail.com>, 02-2019 */


#include "capstone_test.h"

single_dict arches[] = {
	{"CS_ARCH_ARM", CS_ARCH_ARM},
	{"CS_ARCH_ARM64", CS_ARCH_ARM64},
	{"CS_ARCH_MIPS", CS_ARCH_MIPS},
	{"CS_ARCH_PPC", CS_ARCH_PPC},
	{"CS_ARCH_SPARC", CS_ARCH_SPARC},
	{"CS_ARCH_SYSZ", CS_ARCH_SYSZ},
	{"CS_ARCH_X86", CS_ARCH_X86},
	{"CS_ARCH_XCORE", CS_ARCH_XCORE},
	{"CS_ARCH_M68K", CS_ARCH_M68K}
};

single_dict modes[] = {
	{"CS_MODE_LITTLE_ENDIAN", CS_MODE_LITTLE_ENDIAN},
	{"CS_MODE_ARM", CS_MODE_ARM},
	{"CS_MODE_16", CS_MODE_16},
	{"CS_MODE_32", CS_MODE_32},
	{"CS_MODE_64", CS_MODE_64},
	{"CS_MODE_THUMB", CS_MODE_THUMB},
	{"CS_MODE_MCLASS", CS_MODE_MCLASS},
	{"CS_MODE_V8", CS_MODE_V8},
	{"CS_MODE_MICRO", CS_MODE_MICRO},
	{"CS_MODE_MIPS3", CS_MODE_MIPS3},
	{"CS_MODE_MIPS32R6", CS_MODE_MIPS32R6},
	{"CS_MODE_MIPS2", CS_MODE_MIPS2},
	{"CS_MODE_V9", CS_MODE_V9},
	{"CS_MODE_QPX", CS_MODE_QPX},
	{"CS_MODE_M68K_000", CS_MODE_M68K_000},
	{"CS_MODE_M68K_010", CS_MODE_M68K_010},
	{"CS_MODE_M68K_020", CS_MODE_M68K_020},
	{"CS_MODE_M68K_030", CS_MODE_M68K_030},
	{"CS_MODE_M68K_040", CS_MODE_M68K_040},
	{"CS_MODE_M68K_060", CS_MODE_M68K_060},
	{"CS_MODE_BIG_ENDIAN", CS_MODE_BIG_ENDIAN},
	{"CS_MODE_MIPS32", CS_MODE_MIPS32},
	{"CS_MODE_MIPS64", CS_MODE_MIPS64},
	{"CS_MODE_M680X_6301", CS_MODE_M680X_6301},
	{"CS_MODE_M680X_6309", CS_MODE_M680X_6309},
	{"CS_MODE_M680X_6800", CS_MODE_M680X_6800},
	{"CS_MODE_M680X_6801", CS_MODE_M680X_6801},
	{"CS_MODE_M680X_6805", CS_MODE_M680X_6805},
	{"CS_MODE_M680X_6808", CS_MODE_M680X_6808},
	{"CS_MODE_M680X_6809", CS_MODE_M680X_6809},
	{"CS_MODE_M680X_6811", CS_MODE_M680X_6811},
	{"CS_MODE_M680X_CPU12", CS_MODE_M680X_CPU12},
	{"CS_MODE_M680X_HCS08", CS_MODE_M680X_HCS08}	
};

double_dict options[] = {
	{"CS_OPT_DETAIL", CS_OPT_DETAIL, CS_OPT_ON},
	{"CS_OPT_SKIPDATA", CS_OPT_SKIPDATA, CS_OPT_ON},
	{"CS_OPT_SYNTAX_DEFAULT", CS_OPT_SYNTAX, CS_OPT_SYNTAX_DEFAULT},
	{"CS_OPT_SYNTAX_INTEL", CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL},
	{"CS_OPT_SYNTAX_ATT", CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT},
	{"CS_OPT_SYNTAX_NOREGNAME", CS_OPT_SYNTAX, CS_OPT_SYNTAX_NOREGNAME},
	{"CS_OPT_SYNTAX_MASM", CS_OPT_SYNTAX, CS_OPT_SYNTAX_MASM},
	{"CS_MODE_LITTLE_ENDIAN", CS_OPT_MODE, CS_MODE_LITTLE_ENDIAN},
	{"CS_MODE_ARM", CS_OPT_MODE, CS_MODE_ARM},
	{"CS_MODE_16", CS_OPT_MODE, CS_MODE_16},
	{"CS_MODE_32", CS_OPT_MODE, CS_MODE_32},
	{"CS_MODE_64", CS_OPT_MODE, CS_MODE_64},
	{"CS_MODE_THUMB", CS_OPT_MODE, CS_MODE_THUMB},
	{"CS_MODE_MCLASS", CS_OPT_MODE, CS_MODE_MCLASS},
	{"CS_MODE_V8", CS_OPT_MODE, CS_MODE_V8},
	{"CS_MODE_MICRO", CS_OPT_MODE, CS_MODE_MICRO},
	{"CS_MODE_MIPS3", CS_OPT_MODE, CS_MODE_MIPS3},
	{"CS_MODE_MIPS32R6", CS_OPT_MODE, CS_MODE_MIPS32R6},
	{"CS_MODE_MIPS2", CS_OPT_MODE, CS_MODE_MIPS2},
	{"CS_MODE_V9", CS_OPT_MODE, CS_MODE_V9},
	{"CS_MODE_QPX", CS_OPT_MODE, CS_MODE_QPX},
	{"CS_MODE_M68K_000", CS_OPT_MODE, CS_MODE_M68K_000},
	{"CS_MODE_M68K_010", CS_OPT_MODE, CS_MODE_M68K_010},
	{"CS_MODE_M68K_020", CS_OPT_MODE, CS_MODE_M68K_020},
	{"CS_MODE_M68K_030", CS_OPT_MODE, CS_MODE_M68K_030},
	{"CS_MODE_M68K_040", CS_OPT_MODE, CS_MODE_M68K_040},
	{"CS_MODE_M68K_060", CS_OPT_MODE, CS_MODE_M68K_060},
	{"CS_MODE_BIG_ENDIAN", CS_OPT_MODE, CS_MODE_BIG_ENDIAN},
	{"CS_MODE_MIPS32", CS_OPT_MODE, CS_MODE_MIPS32},
	{"CS_MODE_MIPS64", CS_OPT_MODE, CS_MODE_MIPS64},
	{"CS_MODE_M680X_6301", CS_OPT_MODE, CS_MODE_M680X_6301},
	{"CS_MODE_M680X_6309", CS_OPT_MODE, CS_MODE_M680X_6309},
	{"CS_MODE_M680X_6800", CS_OPT_MODE, CS_MODE_M680X_6800},
	{"CS_MODE_M680X_6801", CS_OPT_MODE, CS_MODE_M680X_6801},
	{"CS_MODE_M680X_6805", CS_OPT_MODE, CS_MODE_M680X_6805},
	{"CS_MODE_M680X_6808", CS_OPT_MODE, CS_MODE_M680X_6808},
	{"CS_MODE_M680X_6809", CS_OPT_MODE, CS_MODE_M680X_6809},
	{"CS_MODE_M680X_6811", CS_OPT_MODE, CS_MODE_M680X_6811},
	{"CS_MODE_M680X_CPU12", CS_OPT_MODE, CS_MODE_M680X_CPU12},
	{"CS_MODE_M680X_HCS08", CS_OPT_MODE, CS_MODE_M680X_HCS08},
	{"CS_OPT_UNSIGNED", CS_OPT_UNSIGNED, CS_OPT_ON}
};

char *(*function)(csh *, cs_mode, cs_insn*) = NULL;

void test_single_MC(csh *handle, int mc_mode, char *line)
{
	char **list_part, **list_byte;
	int size_part, size_byte, size_data, size_insn;
	int i, count, count_noreg;
	unsigned char *code;
	cs_insn *insn;
	char tmp[MAXMEM], tmp_mc[MAXMEM], origin[MAXMEM], tmp_noreg[MAXMEM];
	char **offset_opcode;
	int size_offset_opcode;
	unsigned long offset;
	char *p;

	list_part = split(line, " = ", &size_part);
	offset_opcode = split(list_part[0], ": ", &size_offset_opcode);
	if (size_offset_opcode > 1) {
		offset = (unsigned int)strtol(offset_opcode[0], NULL, 16);
		list_byte = split(offset_opcode[1], ",", &size_byte);
	} else {
		offset = 0;
		list_byte = split(offset_opcode[0], ",", &size_byte);
	}

	code = (unsigned char *)malloc(size_byte * sizeof(char));
	for (i = 0; i < size_byte; ++i) {
		code[i] = (unsigned char)strtol(list_byte[i], NULL, 16);
	}

	count = cs_disasm(*handle, code, size_byte, offset, 0, &insn);
	if (count == 0) {
		fprintf(stderr, "[  ERROR   ] --- %s --- Failed to disassemble given code!\n", list_part[0]);
		free_strs(list_part, size_part);
		free_strs(offset_opcode, size_offset_opcode);
		free_strs(list_byte, size_byte);
		free(code);
		_fail(__FILE__, __LINE__);
	}
	if (count > 1) {
		fprintf(stderr, "[  ERROR   ] --- %s --- Multiple instructions(%d) disassembling doesn't support!\n", list_part[0], count);
		free_strs(list_part, size_part);
		free_strs(offset_opcode, size_offset_opcode);
		free_strs(list_byte, size_byte);
		free(code);
		_fail(__FILE__, __LINE__);
	}

	for (p = list_part[1]; *p; ++p) *p = tolower(*p);
	for (p = list_part[1]; *p; ++p)
		if (*p == '\t') *p = ' ';
	trim_str(list_part[1]);
	strcpy(tmp_mc, list_part[1]);
	replace_hex(tmp_mc);
	replace_negative(tmp_mc, mc_mode);

	strcpy(tmp, insn[0].mnemonic);
	if (strlen(insn[0].op_str) > 0) {
		tmp[strlen(insn[0].mnemonic)] = ' ';
		strcpy(tmp + strlen(insn[0].mnemonic) + 1, insn[0].op_str);
	}

	trim_str(tmp);
	strcpy(origin, tmp);
	replace_hex(tmp);
	replace_negative(tmp, mc_mode);

	if (cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_NOREGNAME) == CS_ERR_OK) {
		count_noreg = cs_disasm(*handle, code, size_byte, offset, 0, &insn);
		strcpy(tmp_noreg, insn[0].mnemonic);
		if (strlen(insn[0].op_str) > 0) {
			tmp_noreg[strlen(insn[0].mnemonic)] = ' ';
			strcpy(tmp_noreg + strlen(insn[0].mnemonic) + 1, insn[0].op_str);
		}

		trim_str(tmp_noreg);
		replace_hex(tmp_noreg);
		replace_negative(tmp_noreg, mc_mode);

		if (strcmp(tmp, tmp_mc) && strcmp(tmp_noreg, tmp_mc)) {
			fprintf(stderr, "[  ERROR   ] --- %s --- \"%s\" != \"%s\" ( \"%s\" != \"%s\" and \"%s\" != \"%s\" )\n", list_part[0], origin, list_part[1], tmp, tmp_mc, tmp_noreg, tmp_mc);
			free_strs(list_part, size_part);
			free_strs(offset_opcode, size_offset_opcode);
			free_strs(list_byte, size_byte);
			free(code);
			cs_free(insn, count);
			_fail(__FILE__, __LINE__);
		}

		cs_option(*handle, CS_OPT_SYNTAX, 0);

	} else if (strcmp(tmp, tmp_mc)) {
		fprintf(stderr, "[  ERROR   ] --- %s --- \"%s\" != \"%s\" ( \"%s\" != \"%s\" )\n", list_part[0], origin, list_part[1], tmp, tmp_mc);
		free_strs(list_part, size_part);
		free_strs(offset_opcode, size_offset_opcode);
		free_strs(list_byte, size_byte);
		free(code);
		cs_free(insn, count);
		_fail(__FILE__, __LINE__);
	}

	free_strs(list_part, size_part);
	free_strs(offset_opcode, size_offset_opcode);
	free_strs(list_byte, size_byte);
	free(code);
	cs_free(insn, count);
}

int get_value(single_dict d[], unsigned int size, const char *str)
{
	int i;

	for (i = 0; i < size; ++i)
		if (!strcmp(d[i].str, str))
			return d[i].value;
	return -1;
}

int get_index(double_dict d[], unsigned int size, const char *s)
{
	int i;

	for (i = 0; i < size; ++i) {
		if (!strcmp(s, d[i].str))
			return i;
	}
	return -1;
}

int set_function(int arch)
{
	switch(arch) {
		case CS_ARCH_ARM:
			function = get_detail_arm;
			break;
		case CS_ARCH_ARM64:
			function = get_detail_arm64;
			break;
		case CS_ARCH_MIPS:
			function = get_detail_mips;
			break;
		case CS_ARCH_PPC:
			function = get_detail_ppc;
			break;
		case CS_ARCH_SPARC:
			function = get_detail_sparc;
			break;
		case CS_ARCH_SYSZ:
			function = get_detail_sysz;
			break;
		case CS_ARCH_X86:
			function = get_detail_x86;
			break;
		case CS_ARCH_XCORE:
			function = get_detail_xcore;
			break;
		case CS_ARCH_M68K:
			function = get_detail_m68k;
			break;
		case CS_ARCH_M680X:
			function = get_detail_m680x;
			break;
		case CS_ARCH_EVM:
			function = get_detail_evm;
			break;
		case CS_ARCH_MOS65XX:
			function = get_detail_mos65xx;
			break;
		case CS_ARCH_TMS320C64X:
			function = get_detail_tms320c64x;
			break;
		default:
			return -1;
	}
	return 0;
}

void test_single_issue(csh *handle, cs_mode mode, char *line, int detail)
{
	char **list_part, **list_byte, **list_part_cs_result, **list_part_issue_result;
	int size_part, size_byte, size_part_cs_result, size_part_issue_result;
	char *tmptmp;
	int i, count, j;
	unsigned char *code;
	cs_insn *insn;
	char *cs_result, *tmp, *p;
	char **offset_opcode;
	int size_offset_opcode;
	unsigned long offset;

	cs_result = (char *)malloc(sizeof(char));
	cs_result[0] = '\0';

	list_part = split(line, " == ", &size_part);

	offset_opcode = split(list_part[0], ": ", &size_offset_opcode);
	if (size_offset_opcode > 1) {
		offset = (unsigned int)strtol(offset_opcode[0], NULL, 16);
		list_byte = split(offset_opcode[1], ",", &size_byte);
	} else {
		offset = 0;
		list_byte = split(offset_opcode[0], ",", &size_byte);
	}

	code = (unsigned char *)malloc(sizeof(char) * size_byte);
	for (i = 0; i < size_byte; ++i) {
		code[i] = (unsigned char)strtol(list_byte[i], NULL, 16);
	}

	count = cs_disasm(*handle, code, size_byte, offset, 0, &insn);
	for (i = 0; i < count; ++i) {
		tmp = (char *)malloc(strlen(insn[i].mnemonic) + strlen(insn[i].op_str) + 100);
		strcpy(tmp, insn[i].mnemonic);
		if (strlen(insn[i].op_str) > 0) {
			tmp[strlen(insn[i].mnemonic)] = ' ';
			strcpy(tmp + strlen(insn[i].mnemonic) + 1, insn[i].op_str);
		}
		add_str(&cs_result, "%s", tmp);
		free(tmp);
	}

	if (detail == 1) {
		tmp = (*function)(handle, mode, insn);
		add_str(&cs_result, "%s", tmp);
		free(tmp);

		if (insn->detail->groups_count) {
			add_str(&cs_result, " ; Groups: ");
			for (j = 0; j < insn->detail->groups_count; j++) {
				add_str(&cs_result, "%s ", cs_group_name(*handle, insn->detail->groups[j]));
			}
		}
	}
	
	trim_str(cs_result);
	add_str(&cs_result, " ;");
	//	list_part_cs_result = split(cs_result, " ; ", &size_part_cs_result);
	for (p = list_part[1]; *p; ++p) if (*p == '\t') *p = ' ';
	list_part_issue_result = split(list_part[1], " ; ", &size_part_issue_result);

	for (i = 0; i < size_part_issue_result; ++i) {
		trim_str(list_part_issue_result[i]);
		memset(tmptmp, MAXMEM, 0);
		
		tmptmp = (char *)malloc(sizeof(char));
		tmptmp[0] = '\0';
		add_str(&tmptmp, "%s", list_part_issue_result[i]);
		add_str(&tmptmp, " ;");

		if ((strstr(cs_result, tmptmp)) == NULL) {
			fprintf(stderr, "[  ERROR   ] --- %s --- \"%s\" not in \"%s\"\n", list_part[0], list_part_issue_result[i], cs_result);
			cs_free(insn, count);
			free_strs(list_part, size_part);
			free_strs(list_byte, size_byte);
			free(cs_result);
			//	free_strs(list_part_cs_result, size_part_cs_result);
			free_strs(list_part_issue_result, size_part_issue_result);
			free(tmptmp);
			_fail(__FILE__, __LINE__);
		}
	}

	cs_free(insn, count);
	free_strs(list_part, size_part);
	free_strs(list_byte, size_byte);
	free(cs_result);
	//	free_strs(list_part_cs_result, size_part_cs_result);
	free_strs(list_part_issue_result, size_part_issue_result);
}
