<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Main DLL
  *
  * ----
  *
  * Breakpoint handling.
  */

#include "thcrap.h"

/// Functions
/// ---------
// Breakpoint hook function, implemented in assembly. A CALL to this function
// is written to every breakpoint's address.
extern "C" void bp_entry(void);

// Performs breakpoint lookup, invocation and stack adjustments. Returns the
// number of bytes the stack has to be moved downwards by breakpoint_entry().
extern "C" size_t breakpoint_process(breakpoint_local_t *bp_local, x86_reg_t *regs);
/// ---------

/// Constants
/// ---------
#define BP_Offset 32
#define CALL_LEN (sizeof(void*) + 1)
static const size_t BP_SourceCave_Limits[2] = {CALL_LEN, (BP_Offset - CALL_LEN)};
/// ---------

#define EAX 0x00786165
#define ECX 0x00786365
#define EDX 0x00786465
#define EBX 0x00786265
#define ESP 0x00707365
#define EBP 0x00706265
#define ESI 0x00697365
#define EDI 0x00696465

size_t* reg(x86_reg_t *regs, const char *regname, const char **endptr)
{
	uint32_t cmp;

	if(!regs || !regname || !regname[0] || !regname[1] || !regname[2]) {
		return NULL;
	}
	memcpy(&cmp, regname, 3);
	cmp &= 0x00FFFFFF;
	strlwr((char *)&cmp);

	if(endptr) {
		*endptr = regname + 3;
	}
	switch(cmp) {
		case EAX: return &regs->eax;
		case ECX: return &regs->ecx;
		case EDX: return &regs->edx;
		case EBX: return &regs->ebx;
		case ESP: return &regs->esp;
		case EBP: return &regs->ebp;
		case ESI: return &regs->esi;
		case EDI: return &regs->edi;
	}
	if(endptr) {
		*endptr = regname;
	}
	return NULL;
}

static size_t eval_expr(const char **expr_ptr, x86_reg_t *regs, char end)
{
	const char *expr = *expr_ptr;
	size_t value = NULL;
	char op = '+';

	/// Parser functions
	/// ----------------
	auto consume = [&expr] (const stringref_t token) {
		if(!strnicmp(expr, token.str, token.len)) {
			expr += token.len;
			return true;
		}
		return false;
	};

	auto consume_whitespace = [&expr] () {
		while(expr[0] == ' ' || expr[0] == '\t') {
			expr++;
		}
	};

	auto dereference = [] (size_t addr, size_t len) {
		size_t ret = 0;
		memcpy(&ret, reinterpret_cast<void *>(addr), len);
		return ret;
	};
	/// ----------------

	while (*expr && *expr != end) {
		consume_whitespace();
		if (strchr("+-*/%", *expr)) {
			op = *expr;
			expr++;
			continue;
		}

		auto ptr_size = sizeof(size_t);
		if(consume("byte ptr")) {
			ptr_size = 1;
		} else if(consume("word ptr")) {
			ptr_size = 2;
		} else if(consume("dword ptr")) {
			ptr_size = 4;
		}
		consume_whitespace();

		size_t cur_value;
		if (*expr == '(') {
			expr++;
			cur_value = eval_expr(&expr, regs, ')');
		}
		else if (*expr == '[') {
			expr++;
			cur_value = eval_expr(&expr, regs, ']');
			if (cur_value) {
				cur_value = dereference(cur_value, ptr_size);
			}
		}
		else if ((cur_value = (size_t)reg(regs, expr, &expr)) != 0) {
			cur_value = dereference(cur_value, ptr_size);
		}
		else {
			str_address_ret_t addr_ret;
			cur_value = str_address_value(expr, nullptr, &addr_ret);
			if (expr == addr_ret.endptr || (addr_ret.error && addr_ret.error != STR_ADDRESS_ERROR_GARBAGE)) {
				// TODO: Print a message specific to the error code.
				log_printf("Error while evaluating expression around '%s': unknown character.\n", expr);
				return 0;
			}
			expr = addr_ret.endptr;
		}

		switch (op) {
		case '+':
			value += cur_value;
			break;
		case '-':
			value -= cur_value;
			break;
		case '*':
			value *= cur_value;
			break;
		case '/':
			value /= cur_value;
			break;
		case '%':
			value %= cur_value;
			break;
		}
	}

	if (end == ']' && *expr != end) {
		log_printf("Error while evaluating expression around '%s': '[' without matching ']'.\n", expr);
		return 0;
	}

	expr++;
	*expr_ptr = expr;
	return value;
}

size_t json_immediate_value(json_t *val, x86_reg_t *regs)
{
	if (!val || json_is_null(val)) {
		return 0;<commit_msg>thcrap: Expressions: Support conditional operators.<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Main DLL
  *
  * ----
  *
  * Breakpoint handling.
  */

#include "thcrap.h"

/// Functions
/// ---------
// Breakpoint hook function, implemented in assembly. A CALL to this function
// is written to every breakpoint's address.
extern "C" void bp_entry(void);

// Performs breakpoint lookup, invocation and stack adjustments. Returns the
// number of bytes the stack has to be moved downwards by breakpoint_entry().
extern "C" size_t breakpoint_process(breakpoint_local_t *bp_local, x86_reg_t *regs);
/// ---------

/// Constants
/// ---------
#define BP_Offset 32
#define CALL_LEN (sizeof(void*) + 1)
static const size_t BP_SourceCave_Limits[2] = {CALL_LEN, (BP_Offset - CALL_LEN)};
/// ---------

#define EAX 0x00786165
#define ECX 0x00786365
#define EDX 0x00786465
#define EBX 0x00786265
#define ESP 0x00707365
#define EBP 0x00706265
#define ESI 0x00697365
#define EDI 0x00696465

size_t* reg(x86_reg_t *regs, const char *regname, const char **endptr)
{
	uint32_t cmp;

	if(!regs || !regname || !regname[0] || !regname[1] || !regname[2]) {
		return NULL;
	}
	memcpy(&cmp, regname, 3);
	cmp &= 0x00FFFFFF;
	strlwr((char *)&cmp);

	if(endptr) {
		*endptr = regname + 3;
	}
	switch(cmp) {
		case EAX: return &regs->eax;
		case ECX: return &regs->ecx;
		case EDX: return &regs->edx;
		case EBX: return &regs->ebx;
		case ESP: return &regs->esp;
		case EBP: return &regs->ebp;
		case ESI: return &regs->esi;
		case EDI: return &regs->edi;
	}
	if(endptr) {
		*endptr = regname;
	}
	return NULL;
}

static size_t eval_expr(const char **expr_ptr, x86_reg_t *regs, char end)
{
	const char *expr = *expr_ptr;
	size_t value = NULL;
	unsigned char op = '+';

	enum twochar_op_t : unsigned char {
		EQ = 0x80,
		NE,
		LE,
		GE
	};

	/// Parser functions
	/// ----------------
	auto consume = [&expr] (const stringref_t token) {
		if(!strnicmp(expr, token.str, token.len)) {
			expr += token.len;
			return true;
		}
		return false;
	};

	auto consume_whitespace = [&expr] () {
		while(expr[0] == ' ' || expr[0] == '\t') {
			expr++;
		}
	};

	auto dereference = [] (size_t addr, size_t len) {
		size_t ret = 0;
		memcpy(&ret, reinterpret_cast<void *>(addr), len);
		return ret;
	};
	/// ----------------

	while (*expr && *expr != end) {
		consume_whitespace();
		if(consume("==")) {
			op = EQ;
			continue;
		} else if(consume("!=")) {
			op = NE;
			continue;
		} else if(consume("<=")) {
			op = LE;
			continue;
		} else if(consume(">=")) {
			op = GE;
			continue;
		} else if (strchr("+-*/%<>", *expr)) {
			op = *expr;
			expr++;
			continue;
		}

		auto ptr_size = sizeof(size_t);
		if(consume("byte ptr")) {
			ptr_size = 1;
		} else if(consume("word ptr")) {
			ptr_size = 2;
		} else if(consume("dword ptr")) {
			ptr_size = 4;
		}
		consume_whitespace();

		size_t cur_value;
		if (*expr == '(') {
			expr++;
			cur_value = eval_expr(&expr, regs, ')');
		}
		else if (*expr == '[') {
			expr++;
			cur_value = eval_expr(&expr, regs, ']');
			if (cur_value) {
				cur_value = dereference(cur_value, ptr_size);
			}
		}
		else if ((cur_value = (size_t)reg(regs, expr, &expr)) != 0) {
			cur_value = dereference(cur_value, ptr_size);
		}
		else {
			str_address_ret_t addr_ret;
			cur_value = str_address_value(expr, nullptr, &addr_ret);
			if (expr == addr_ret.endptr || (addr_ret.error && addr_ret.error != STR_ADDRESS_ERROR_GARBAGE)) {
				// TODO: Print a message specific to the error code.
				log_printf("Error while evaluating expression around '%s': unknown character.\n", expr);
				return 0;
			}
			expr = addr_ret.endptr;
		}

		switch (op) {
		case '+':
			value += cur_value;
			break;
		case '-':
			value -= cur_value;
			break;
		case '*':
			value *= cur_value;
			break;
		case '/':
			value /= cur_value;
			break;
		case '%':
			value %= cur_value;
			break;
		case '<':
			value = value < cur_value;
			break;
		case '>':
			value = value > cur_value;
			break;
		case EQ:
			value = value == cur_value;
			break;
		case NE:
			value = value != cur_value;
			break;
		case LE:
			value = value <= cur_value;
			break;
		case GE:
			value = value >= cur_value;
			break;
		}
	}

	if (end == ']' && *expr != end) {
		log_printf("Error while evaluating expression around '%s': '[' without matching ']'.\n", expr);
		return 0;
	}

	expr++;
	*expr_ptr = expr;
	return value;
}

size_t json_immediate_value(json_t *val, x86_reg_t *regs)
{
	if (!val || json_is_null(val)) {
		return 0;
{'fraction_non_alphanumeric': 0.17474687313877307, 'fraction_numerical': 0.025134008338296605, 'mean_word_length': 3.0816723383568303, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2235139', 'n_tokens_mistral': 3706, 'n_tokens_neox': 3186, 'n_words': 813}