#include "internal.h" #include "rurl.h" #include "smw_snes.h" #include "galaga_nes.h" #include #include #include #include "lua.h" #include "lauxlib.h" typedef struct { unsigned char* ram; int size; } memory_t; static unsigned peekb(unsigned address, memory_t* memory) { return address < memory->size ? memory->ram[address] : 0; } static unsigned peek(unsigned address, unsigned num_bytes, void* ud) { memory_t* memory = (memory_t*)ud; switch (num_bytes) { case 1: return peekb(address, memory); case 2: return peekb(address, memory) | peekb(address + 1, memory) << 8; case 4: return peekb(address, memory) | peekb(address + 1, memory) << 8 | peekb(address + 2, memory) << 16 | peekb(address + 3, memory) << 24; } return 0; } static void parse_operand(rc_operand_t* self, const char** memaddr) { int ret = rc_parse_operand(self, memaddr, 1, NULL, 0); assert(ret >= 0); assert(**memaddr == 0); self->previous = 0; } static void comp_operand(rc_operand_t* self, char expected_type, char expected_size, unsigned expected_value) { assert(expected_type == self->type); assert(expected_size == self->size); assert(expected_value == self->value); } static void parse_comp_operand(const char* memaddr, char expected_type, char expected_size, unsigned expected_value) { rc_operand_t self; int ret; ret = rc_parse_operand(&self, &memaddr, 1, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); comp_operand(&self, expected_type, expected_size, expected_value); } static void parse_error_operand(const char* memaddr, int valid_chars) { rc_operand_t self; int ret; const char* begin = memaddr; ret = rc_parse_operand(&self, &memaddr, 1, NULL, 0); assert(ret < 0); assert(memaddr - begin == valid_chars); } static void parse_comp_operand_value(const char* memaddr, memory_t* memory, unsigned expected_value) { rc_operand_t self; unsigned value; rc_parse_operand(&self, &memaddr, 1, NULL, 0); value = rc_evaluate_operand(&self, peek, memory, NULL); assert(value == expected_value); } static void test_operand(void) { { /*------------------------------------------------------------------------ TestParseVariableAddress ------------------------------------------------------------------------*/ /* sizes */ parse_comp_operand("0xH1234", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U); parse_comp_operand("0x 1234", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x1234U); parse_comp_operand("0x1234", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x1234U); parse_comp_operand("0xW1234", RC_OPERAND_ADDRESS, RC_OPERAND_24_BITS, 0x1234U); parse_comp_operand("0xX1234", RC_OPERAND_ADDRESS, RC_OPERAND_32_BITS, 0x1234U); parse_comp_operand("0xL1234", RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U); parse_comp_operand("0xU1234", RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x1234U); parse_comp_operand("0xM1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_0, 0x1234U); parse_comp_operand("0xN1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_1, 0x1234U); parse_comp_operand("0xO1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_2, 0x1234U); parse_comp_operand("0xP1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_3, 0x1234U); parse_comp_operand("0xQ1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_4, 0x1234U); parse_comp_operand("0xR1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_5, 0x1234U); parse_comp_operand("0xS1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_6, 0x1234U); parse_comp_operand("0xT1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_7, 0x1234U); /* sizes (ignore case) */ parse_comp_operand("0Xh1234", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U); parse_comp_operand("0xx1234", RC_OPERAND_ADDRESS, RC_OPERAND_32_BITS, 0x1234U); parse_comp_operand("0xl1234", RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U); parse_comp_operand("0xu1234", RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x1234U); parse_comp_operand("0xm1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_0, 0x1234U); parse_comp_operand("0xn1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_1, 0x1234U); parse_comp_operand("0xo1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_2, 0x1234U); parse_comp_operand("0xp1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_3, 0x1234U); parse_comp_operand("0xq1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_4, 0x1234U); parse_comp_operand("0xr1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_5, 0x1234U); parse_comp_operand("0xs1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_6, 0x1234U); parse_comp_operand("0xt1234", RC_OPERAND_ADDRESS, RC_OPERAND_BIT_7, 0x1234U); /* addresses */ parse_comp_operand("0xH0000", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x0000U); parse_comp_operand("0xH12345678", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x12345678U); parse_comp_operand("0xHABCD", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0xABCDU); parse_comp_operand("0xhabcd", RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0xABCDU); } { /*------------------------------------------------------------------------ TestParseVariableDeltaMem ------------------------------------------------------------------------*/ /* sizes */ parse_comp_operand("d0xH1234", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U); parse_comp_operand("d0x 1234", RC_OPERAND_DELTA, RC_OPERAND_16_BITS, 0x1234U); parse_comp_operand("d0x1234", RC_OPERAND_DELTA, RC_OPERAND_16_BITS, 0x1234U); parse_comp_operand("d0xW1234", RC_OPERAND_DELTA, RC_OPERAND_24_BITS, 0x1234U); parse_comp_operand("d0xX1234", RC_OPERAND_DELTA, RC_OPERAND_32_BITS, 0x1234U); parse_comp_operand("d0xL1234", RC_OPERAND_DELTA, RC_OPERAND_LOW, 0x1234U); parse_comp_operand("d0xU1234", RC_OPERAND_DELTA, RC_OPERAND_HIGH, 0x1234U); parse_comp_operand("d0xM1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_0, 0x1234U); parse_comp_operand("d0xN1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_1, 0x1234U); parse_comp_operand("d0xO1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_2, 0x1234U); parse_comp_operand("d0xP1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_3, 0x1234U); parse_comp_operand("d0xQ1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_4, 0x1234U); parse_comp_operand("d0xR1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_5, 0x1234U); parse_comp_operand("d0xS1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_6, 0x1234U); parse_comp_operand("d0xT1234", RC_OPERAND_DELTA, RC_OPERAND_BIT_7, 0x1234U); /* ignores case */ parse_comp_operand("D0Xh1234", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U); /* addresses */ parse_comp_operand("d0xH0000", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x0000U); parse_comp_operand("d0xH12345678", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x12345678U); parse_comp_operand("d0xHABCD", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0xABCDU); parse_comp_operand("d0xhabcd", RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0xABCDU); } { /*------------------------------------------------------------------------ TestParseVariableValue ------------------------------------------------------------------------*/ /* decimal - values don't actually have size, default is RC_OPERAND_8_BITS */ parse_comp_operand("123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 123U); parse_comp_operand("123456", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 123456U); parse_comp_operand("0", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0U); parse_comp_operand("0000000000", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0U); parse_comp_operand("4294967295", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U); /* hex - 'H' prefix, not '0x'! */ parse_comp_operand("H123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0x123U); parse_comp_operand("HABCD", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0xABCDU); parse_comp_operand("h123", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0x123U); parse_comp_operand("habcd", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 0xABCDU); parse_comp_operand("HFFFFFFFF", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U); /* '0x' is an address */ parse_comp_operand("0x123", RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x123U); /* hex without prefix */ parse_error_operand("ABCD", 0); /* more than 32-bits (error), will be constrained to 32-bits */ parse_comp_operand("4294967296", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U); /* negative value (error), will be "wrapped around": -1 = 0x100000000 - 1 = 0xFFFFFFFF = 4294967295 */ parse_comp_operand("-1", RC_OPERAND_CONST, RC_OPERAND_8_BITS, 4294967295U); } { /*------------------------------------------------------------------------ TestVariableGetValue ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; memory.ram = ram; memory.size = sizeof(ram); /* value */ parse_comp_operand_value("0", &memory, 0x00U); /* eight-bit */ parse_comp_operand_value("0xh0", &memory, 0x00U); parse_comp_operand_value("0xh1", &memory, 0x12U); parse_comp_operand_value("0xh4", &memory, 0x56U); parse_comp_operand_value("0xh5", &memory, 0x00U); /* out of range */ /* sixteen-bit */ parse_comp_operand_value("0x 0", &memory, 0x1200U); parse_comp_operand_value("0x 3", &memory, 0x56ABU); parse_comp_operand_value("0x 4", &memory, 0x0056U); /* out of range */ /* thirty-two-bit */ parse_comp_operand_value("0xx0", &memory, 0xAB341200U); parse_comp_operand_value("0xx1", &memory, 0x56AB3412U); parse_comp_operand_value("0xx3", &memory, 0x000056ABU); /* out of range */ /* nibbles */ parse_comp_operand_value("0xu0", &memory, 0x0U); parse_comp_operand_value("0xu1", &memory, 0x1U); parse_comp_operand_value("0xu4", &memory, 0x5U); parse_comp_operand_value("0xu5", &memory, 0x0U); /* out of range */ parse_comp_operand_value("0xl0", &memory, 0x0U); parse_comp_operand_value("0xl1", &memory, 0x2U); parse_comp_operand_value("0xl4", &memory, 0x6U); parse_comp_operand_value("0xl5", &memory, 0x0U); /* out of range */ /* bits */ parse_comp_operand_value("0xm0", &memory, 0x0U); parse_comp_operand_value("0xm3", &memory, 0x1U); parse_comp_operand_value("0xn3", &memory, 0x1U); parse_comp_operand_value("0xo3", &memory, 0x0U); parse_comp_operand_value("0xp3", &memory, 0x1U); parse_comp_operand_value("0xq3", &memory, 0x0U); parse_comp_operand_value("0xr3", &memory, 0x1U); parse_comp_operand_value("0xs3", &memory, 0x0U); parse_comp_operand_value("0xt3", &memory, 0x1U); parse_comp_operand_value("0xm5", &memory, 0x0U); /* out of range */ } { /*------------------------------------------------------------------------ TestVariableGetValueDelta ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_operand_t op; const char* memaddr; memory.ram = ram; memory.size = sizeof(ram); memaddr = "d0xh1"; parse_operand(&op, &memaddr); assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x00); /* first call gets uninitialized value */ assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x12); /* second gets current value */ /* RC_OPERAND_DELTA is always one frame behind */ ram[1] = 0x13; assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x12U); ram[1] = 0x14; assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x13U); ram[1] = 0x15; assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x14U); ram[1] = 0x16; assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x15U); assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x16U); assert(rc_evaluate_operand(&op, peek, &memory, NULL) == 0x16U); } } static void parse_condition(rc_condition_t* self, const char* memaddr) { int ret; rc_scratch_t scratch; ret = 0; rc_parse_condition(&ret, self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); } static void parse_comp_condition( const char* memaddr, char expected_type, char expected_left_type, char expected_left_size, unsigned expected_left_value, char expected_operator, char expected_right_type, char expected_right_size, unsigned expected_right_value, int expected_required_hits ) { rc_condition_t self; parse_condition(&self, memaddr); assert(self.type == expected_type); comp_operand(&self.operand1, expected_left_type, expected_left_size, expected_left_value); assert(self.oper == expected_operator); comp_operand(&self.operand2, expected_right_type, expected_right_size, expected_right_value); assert(self.required_hits == expected_required_hits); } static void parse_test_condition(const char* memaddr, memory_t* memory, int value) { rc_condition_t self; int ret; rc_scratch_t scratch; ret = 0; rc_parse_condition(&ret, &self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); ret = rc_test_condition(&self, 0, peek, memory, NULL); assert((ret && value) || (!ret && !value)); } static void test_condition(void) { { /*------------------------------------------------------------------------ TestParseConditionMemoryComparisonValue ------------------------------------------------------------------------*/ /* different comparisons */ parse_comp_condition( "0xH1234=8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234==8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234!=8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_NE, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234<8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_LT, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234<=8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_LE, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234>8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_GT, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "0xH1234>=8", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_GE, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); /* delta */ parse_comp_condition( "d0xH1234=8", RC_CONDITION_STANDARD, RC_OPERAND_DELTA, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); /* flags */ parse_comp_condition( "R:0xH1234=8", RC_CONDITION_RESET_IF, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "P:0xH1234=8", RC_CONDITION_PAUSE_IF, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "A:0xH1234=8", RC_CONDITION_ADD_SOURCE, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "B:0xH1234=8", RC_CONDITION_SUB_SOURCE, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); parse_comp_condition( "C:0xH1234=8", RC_CONDITION_ADD_HITS, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 0 ); /* hit count */ parse_comp_condition( "0xH1234=8(1)", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 1 ); parse_comp_condition( "0xH1234=8.1.", /* legacy format */ RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 1 ); parse_comp_condition( "0xH1234=8(100)", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_CONST, RC_OPERAND_8_BITS, 8U, 100 ); } { /*------------------------------------------------------------------------ TestParseConditionMemoryComparisonHexValue ------------------------------------------------------------------------*/ /* hex value is interpreted as a 16-bit memory reference */ parse_comp_condition( "0xH1234=0x80", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_8_BITS, 0x1234U, RC_CONDITION_EQ, RC_OPERAND_ADDRESS, RC_OPERAND_16_BITS, 0x80U, 0 ); } { /*------------------------------------------------------------------------ TestParseConditionMemoryComparisonMemory ------------------------------------------------------------------------*/ parse_comp_condition( "0xL1234!=0xU3456", RC_CONDITION_STANDARD, RC_OPERAND_ADDRESS, RC_OPERAND_LOW, 0x1234U, RC_CONDITION_NE, RC_OPERAND_ADDRESS, RC_OPERAND_HIGH, 0x3456U, 0 ); } { /*------------------------------------------------------------------------ TestConditionCompare ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; memory.ram = ram; memory.size = sizeof(ram); /* values */ parse_test_condition("0xH0001=18", &memory, 1); parse_test_condition("0xH0001!=18", &memory, 0); parse_test_condition("0xH0001<=18", &memory, 1); parse_test_condition("0xH0001>=18", &memory, 1); parse_test_condition("0xH0001<18", &memory, 0); parse_test_condition("0xH0001>18", &memory, 0); parse_test_condition("0xH0001>0", &memory, 1); parse_test_condition("0xH0001!=0", &memory, 1); /* memory */ parse_test_condition("0xH0001<0xH0002", &memory, 1); parse_test_condition("0xH0001>0xH0002", &memory, 0); parse_test_condition("0xH0001=0xH0001", &memory, 1); parse_test_condition("0xH0001!=0xH0002", &memory, 1); } { /*------------------------------------------------------------------------ TestConditionCompareDelta ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_condition_t cond; memory.ram = ram; memory.size = sizeof(ram); parse_condition(&cond, "0xH0001>d0xH0001"); /* initial delta value is 0, 0x12 > 0 */ assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 1); /* delta value is now 0x12, 0x12 = 0x12 */ assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 0); /* delta value is now 0x12, 0x11 < 0x12 */ ram[1] = 0x11; assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 0); /* delta value is now 0x13, 0x12 > 0x11 */ ram[1] = 0x12; assert(rc_test_condition(&cond, 0, peek, &memory, NULL) == 1); } } static void parse_trigger(rc_trigger_t** self, void* buffer, const char* memaddr) { assert(rc_trigger_size(memaddr) >= 0); *self = rc_parse_trigger(buffer, memaddr, NULL, 0); assert(*self != NULL); } static void comp_trigger(rc_trigger_t* self, memory_t* memory, int expected_result) { int ret = rc_test_trigger(self, peek, memory, NULL); assert(expected_result == ret); } static rc_condition_t* condset_get_cond(rc_condset_t* condset, int ndx) { rc_condition_t* cond = condset->conditions; while (ndx-- != 0) { assert(cond != NULL); cond = cond->next; } assert(cond != NULL); return cond; } static rc_condset_t* trigger_get_set(rc_trigger_t* trigger, int ndx) { rc_condset_t* condset = trigger->alternative; if (ndx-- == 0) { assert(trigger->requirement != NULL); return trigger->requirement; } while (ndx-- != 0) { condset = condset->next; assert(condset != NULL); } assert(condset != NULL); return condset; } static void test_trigger(void) { { /*------------------------------------------------------------------------ TestSimpleSets Only standard conditions, no alt groups ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18"); /* one condition, true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); parse_trigger(&trigger, buffer, "0xH0001!=18"); /* one condition, false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52"); /* two conditions, true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002>52"); /* two conditions, false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52_0xL0004=6"); /* three conditions, true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); parse_trigger(&trigger, buffer, "0xH0001=16_0xH0002=52_0xL0004=6"); /* three conditions, first false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=50_0xL0004=6"); /* three conditions, first false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); parse_trigger(&trigger, buffer, "0xH0001=18_0xH0002=52_0xL0004=4"); /* three conditions, first false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); parse_trigger(&trigger, buffer, "0xH0001=16_0xH0002=50_0xL0004=4"); /* three conditions, all false */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); } { /*------------------------------------------------------------------------ TestPauseIf ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52_P:0xL0x0004=6"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* Also true, but processing stops on first PauseIf */ ram[2] = 0; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* PauseIf goes to 0 when false */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); /* PauseIf stays at 1 when false */ ram[4] = 0; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* PauseIf goes to 0 when false */ } { /*------------------------------------------------------------------------ TestPauseIfHitCountOne ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.1."); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[2] = 0; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); /* PauseIf with HitCount doesn't automatically go back to 0 */ } { /*------------------------------------------------------------------------ TestPauseIfHitCountTwo ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.2."); comp_trigger(trigger, &memory, 1); /* PauseIf counter hasn't reached HitCount target, non-PauseIf condition still true */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); comp_trigger(trigger, &memory, 0); /* PauseIf counter has reached HitCount target, non-PauseIf conditions ignored */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); ram[2] = 0; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); /* PauseIf with HitCount doesn't automatically go back to 0 */ } { /*------------------------------------------------------------------------ TestPauseIfHitReset ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0002=52.1._R:0xH0003=1SR:0xH0003=2"); comp_trigger(trigger, &memory, 0); /* Trigger PauseIf, non-PauseIf conditions ignored */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); ram[2] = 0; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); /* PauseIf with HitCount doesn't automatically go back to 0 */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); ram[3] = 1; comp_trigger(trigger, &memory, 0); /* ResetIf in Paused group is ignored */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); ram[3] = 2; comp_trigger(trigger, &memory, 0); /* ResetIf in alternate group is honored, PauseIf does not retrigger and non-PauseIf condition is true */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* ResetIf causes entire achievement to fail */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); ram[3] = 3; comp_trigger(trigger, &memory, 1); /* ResetIf no longer true, achievement allowed to trigger */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); } { /*------------------------------------------------------------------------ TestResetIf ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_R:0xH0002=50_R:0xL0x0004=4"); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); ram[2] = 50; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* True, but ResetIf also resets true marker */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); ram[4] = 0x54; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* True, but ResetIf also resets true marker */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* Also true, but processing stop on first ResetIf */ ram[2] = 52; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* True, but ResetIf also resets true marker */ ram[4] = 0x56; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); } { /*------------------------------------------------------------------------ TestHitCount ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=20(2)_0xH0002=52"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[1] = 20; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* hits stop increment once count it reached */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 4U); } { /*------------------------------------------------------------------------ TestHitCountResetIf Verifies that ResetIf resets HitCounts ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); ram[4] = 0x54; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); ram[4] = 0x56; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); } { /*------------------------------------------------------------------------ TestHitCountResetIfHitCount Verifies that ResetIf with HitCount target only resets HitCounts when target is met ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4.2."); comp_trigger(trigger, &memory, 0); /* HitCounts on conditions 1 and 2 are incremented */ comp_trigger(trigger, &memory, 1); /* HitCounts on conditions 1 and 2 are incremented, cond 1 is now true so entire achievement is true */ comp_trigger(trigger, &memory, 1); /* HitCount on condition 2 is incremented, cond 1 already met its target HitCount */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should still be 0 */ ram[4] = 0x54; /* first hit on ResetIf should not reset anything */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* condition 1 stopped at it's HitCount target */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 4U); /* condition 2 continues to increment */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); /* ResetIf HitCount should be 1 */ /* second hit on ResetIf should reset everything */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should also be reset */ } { /*------------------------------------------------------------------------ TestAddHitsResetIf Verifies that ResetIf works with AddHits ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "C:0xH0001=18_R:0xL0004=6(3)"); /* never(repeated(3, byte(1) == 18 || low(4) == 6)) */ comp_trigger(trigger, &memory, 1); /* result is true, no non-reset conditions */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); comp_trigger(trigger, &memory, 0); /* total hits met (2 for each condition, only needed 3 total) (2 hits on condition 2 is not enough), result is always false if reset */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); } { /*------------------------------------------------------------------------ TestHitCountResetIfHitCountOne Verifies that ResetIf HitCount(1) behaves like ResetIf without a HitCount ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_R:0xL0004=4.1."); comp_trigger(trigger, &memory, 0); /* HitCounts on conditions 1 and 2 are incremented */ comp_trigger(trigger, &memory, 1); /* HitCounts on conditions 1 and 2 are incremented, cond 1 is now true so entire achievement is true */ comp_trigger(trigger, &memory, 1); /* HitCount on condition 2 is incremented, cond 1 already met its target HitCount */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should still be 0 */ ram[4] = 0x54; /* ResetIf HitCount(1) should behave just like ResetIf without a HitCount - all items, including ResetIf should be reset. */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); /* ResetIf HitCount should also be reset */ } { /*------------------------------------------------------------------------ TestHitCountPauseIf Verifies that PauseIf stops HitCount processing ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(2)_0xH0002=52_P:0xL0004=4"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[4] = 0x54; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[4] = 0x56; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); ram[4] = 0x54; comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); ram[4] = 0x56; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); } { /*------------------------------------------------------------------------ TestHitCountPauseIfResetIf Verifies that PauseIf prevents ResetIf processing ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(2)_R:0xH0002=50_P:0xL0004=4"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[4] = 0x54; /* pause */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[2] = 50; /* reset (but still paused) */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[4] = 0x56; /* unpause (still reset) */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); ram[2] = 52; /* unreset */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); } { /*------------------------------------------------------------------------ TestAddSource ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "A:0xH0001=0_0xH0002=22"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); ram[2] = 4; /* sum is correct */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[1] = 0; /* first condition is true, but not sum */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[2] = 22; /* first condition is true, sum is correct */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); } { /*------------------------------------------------------------------------ TestSubSource ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "B:0xH0002=0_0xH0001=14"); /* NOTE: SubSource subtracts the first value from the second! */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); ram[2] = 4; /* difference is correct */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[1] = 0; /* first condition is true, but not difference */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[2] = 14; /* first condition is true, value is negative inverse of expected value */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[1] = 28; /* difference is correct again */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); } { /*------------------------------------------------------------------------ TestAddSubSource ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "A:0xH0001=0_B:0xL0002=0_0xL0004=14"); /* byte(1) - low(2) + low(4) == 14 */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 0U); ram[1] = 12; /* total is correct */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); ram[1] = 0; /* first condition is true, but not total */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); ram[4] = 18; /* byte(4) would make total true, but not low(4) */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 1U); ram[2] = 1; ram[4] = 15; /* difference is correct again */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); /* AddSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 0U); /* SubSource condition does not have hit tracking */ assert(condset_get_cond(trigger_get_set(trigger, 0), 2)->current_hits == 2U); } { /*------------------------------------------------------------------------ TestAddHits ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; rc_condset_t* condset; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "C:0xH0001=18(2)_0xL0004=6(4)"); /* repeated(4, byte(1) == 18 || low(4) == 6) */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); comp_trigger(trigger, &memory, 1); /* total hits met (2 for each condition) */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); /* threshold met, stop incrementing */ assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); /* total met prevents incrementing even though individual tally has not reached total */ rc_reset_condset(trigger->requirement); for (condset = trigger->alternative; condset != NULL; condset = condset->next) { rc_reset_condset(condset); } comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 1U); ram[1] = 16; comp_trigger(trigger, &memory, 0); /* 1 + 2 < 4, not met */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 2U); comp_trigger(trigger, &memory, 1); /* 1 + 3 = 4, met */ assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 0), 1)->current_hits == 3U); } { /*------------------------------------------------------------------------ TestAltGroups ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=16S0xH0002=52S0xL0004=6"); /* core not true, both alts are */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U); ram[1] = 16; /* core and both alts true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U); ram[4] = 0; /* core and first alt true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U); ram[2] = 0; /* core true, but neither alt is */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U); ram[4] = 6; /* core and second alt true */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 4U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U); } { /*------------------------------------------------------------------------ TestResetIfInAltGroup Verifies that a ResetIf resets everything regardless of where it is ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18(1)_R:0xH0000=1S0xH0002=52(1)S0xL0004=6(1)_R:0xH0000=2"); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U); ram[0] = 1; /* reset in core group resets everything */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 0U); ram[0] = 0; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U); ram[0] = 2; /* reset in alt group resets everything */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 0U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 0U); } { /*------------------------------------------------------------------------ TestPauseIfInAltGroup Verifies that PauseIf only pauses the group it's in ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0001=18_P:0xH0000=1S0xH0002=52S0xL0004=6_P:0xH0000=2"); comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 1U); ram[0] = 1; /* pause in core group only pauses core group */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 2U); ram[0] = 0; comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 2U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U); ram[0] = 2; /* pause in alt group only pauses alt group */ comp_trigger(trigger, &memory, 1); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 3U); assert(condset_get_cond(trigger_get_set(trigger, 1), 0)->current_hits == 4U); assert(condset_get_cond(trigger_get_set(trigger, 2), 0)->current_hits == 3U); } { /*------------------------------------------------------------------------ TestPauseIfResetIfAltGroup ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); parse_trigger(&trigger, buffer, "0xH0000=0.1._0xH0000=2SP:0xH0001=18_R:0xH0002=52"); comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[0] = 1; /* move off HitCount */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[1] = 16; /* unpause alt group, HitCount should be reset */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 0U); ram[0] = 0; ram[1] = 18; /* repause alt group, reset hitcount target, hitcount should be set */ comp_trigger(trigger, &memory, 0); assert(condset_get_cond(trigger_get_set(trigger, 0), 0)->current_hits == 1U); ram[0] = 2; /* trigger win condition. alt group has no normal conditions, it should be considered false */ comp_trigger(trigger, &memory, 0); } } static void parse_comp_term(const char* memaddr, char expected_var_size, unsigned expected_address, int is_bcd, int is_const) { rc_term_t self; rc_scratch_t scratch; int ret; ret = 0; rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); assert(is_const || self.operand1.size == expected_var_size); assert(self.operand1.value == expected_address); assert(self.operand1.is_bcd == is_bcd); assert(self.invert == 0); assert(self.operand2.size == RC_OPERAND_8_BITS); assert(self.operand2.value == 0U); assert(!is_const || self.operand1.type == RC_OPERAND_CONST); } static void parse_comp_term_fp(const char* memaddr, char expected_var_size, unsigned expected_address, double fp) { rc_term_t self; rc_scratch_t scratch; int ret; ret = 0; rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); assert(self.operand1.size == expected_var_size); assert(self.operand1.value == expected_address); assert(self.operand2.type == RC_OPERAND_FP); assert(self.operand2.fp_value == fp); } static void parse_comp_term_mem(const char* memaddr, char expected_size_1, unsigned expected_address_1, char expected_size_2, unsigned expected_address_2) { rc_term_t self; rc_scratch_t scratch; int ret; ret = 0; rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); assert(self.operand1.size == expected_size_1); assert(self.operand1.value == expected_address_1); assert(self.operand2.size == expected_size_2); assert(self.operand2.value == expected_address_2); } static void parse_comp_term_value(const char* memaddr, memory_t* memory, unsigned value) { rc_term_t self; rc_scratch_t scratch; int ret; ret = 0; rc_parse_term(&ret, &self, &scratch, &memaddr, NULL, 0); assert(ret >= 0); assert(*memaddr == 0); assert(rc_evaluate_term(&self, peek, memory, NULL) == value); } static void test_term(void) { { /*------------------------------------------------------------------------ TestClauseParseFromString ------------------------------------------------------------------------*/ /* sizes */ parse_comp_term("0xH1234", RC_OPERAND_8_BITS, 0x1234U, 0, 0); parse_comp_term("0x 1234", RC_OPERAND_16_BITS, 0x1234U, 0, 0); parse_comp_term("0x1234", RC_OPERAND_16_BITS, 0x1234U, 0, 0); parse_comp_term("0xW1234", RC_OPERAND_24_BITS, 0x1234U, 0, 0); parse_comp_term("0xX1234", RC_OPERAND_32_BITS, 0x1234U, 0, 0); parse_comp_term("0xL1234", RC_OPERAND_LOW, 0x1234U, 0, 0); parse_comp_term("0xU1234", RC_OPERAND_HIGH, 0x1234U, 0, 0); parse_comp_term("0xM1234", RC_OPERAND_BIT_0, 0x1234U, 0, 0); parse_comp_term("0xN1234", RC_OPERAND_BIT_1, 0x1234U, 0, 0); parse_comp_term("0xO1234", RC_OPERAND_BIT_2, 0x1234U, 0, 0); parse_comp_term("0xP1234", RC_OPERAND_BIT_3, 0x1234U, 0, 0); parse_comp_term("0xQ1234", RC_OPERAND_BIT_4, 0x1234U, 0, 0); parse_comp_term("0xR1234", RC_OPERAND_BIT_5, 0x1234U, 0, 0); parse_comp_term("0xS1234", RC_OPERAND_BIT_6, 0x1234U, 0, 0); parse_comp_term("0xT1234", RC_OPERAND_BIT_7, 0x1234U, 0, 0); /* BCD */ parse_comp_term("B0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1, 0); parse_comp_term("B0xX1234", RC_OPERAND_32_BITS, 0x1234U, 1, 0); parse_comp_term("b0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1, 0); /* Value */ parse_comp_term("V1234", 0, 1234, 0, 1); parse_comp_term("V+1", 0, 1, 0, 1); parse_comp_term("V-1", 0, 0xFFFFFFFFU, 0, 1); parse_comp_term("V-2", 0, 0xFFFFFFFEU, 0, 1); /* twos compliment still works for addition */ } { /*------------------------------------------------------------------------ TestClauseParseFromStringMultiply ------------------------------------------------------------------------*/ parse_comp_term_fp("0xH1234", RC_OPERAND_8_BITS, 0x1234U, 1.0); parse_comp_term_fp("0xH1234*1", RC_OPERAND_8_BITS, 0x1234U, 1.0); parse_comp_term_fp("0xH1234*3", RC_OPERAND_8_BITS, 0x1234U, 3.0); parse_comp_term_fp("0xH1234*0.5", RC_OPERAND_8_BITS, 0x1234U, 0.5); parse_comp_term_fp("0xH1234*-1", RC_OPERAND_8_BITS, 0x1234U, -1.0); } { /*------------------------------------------------------------------------ TestClauseParseFromStringMultiplyAddress ------------------------------------------------------------------------*/ parse_comp_term_mem("0xH1234", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_8_BITS, 0U); parse_comp_term_mem("0xH1234*0xH3456", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_8_BITS, 0x3456U); parse_comp_term_mem("0xH1234*0xL2222", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_LOW, 0x2222U); parse_comp_term_mem("0xH1234*0x1111", RC_OPERAND_8_BITS, 0x1234U, RC_OPERAND_16_BITS, 0x1111U); } { /*------------------------------------------------------------------------ TestClauseGetValue ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; memory.ram = ram; memory.size = sizeof(ram); /* value */ parse_comp_term_value("V6", &memory, 6); parse_comp_term_value("V6*2", &memory, 12); parse_comp_term_value("V6*0.5", &memory, 3); /* memory */ parse_comp_term_value("0xH01", &memory, 0x12); parse_comp_term_value("0x0001", &memory, 0x3412); /* BCD encoding */ parse_comp_term_value("B0xH01", &memory, 12); parse_comp_term_value("B0x0001", &memory, 3412); /* multiplication */ parse_comp_term_value("0xH01*4", &memory, 0x12 * 4); /* multiply by constant */ parse_comp_term_value("0xH01*0.5", &memory, 0x12 / 2); /* multiply by fraction */ parse_comp_term_value("0xH01*0xH02", &memory, 0x12 * 0x34); /* multiply by second address */ parse_comp_term_value("0xH01*0xT02", &memory, 0); /* multiply by bit */ parse_comp_term_value("0xH01*~0xT02", &memory, 0x12); /* multiply by inverse bit */ parse_comp_term_value("0xH01*~0xH02", &memory, 0x12 * (0x34 ^ 0xff)); /* multiply by inverse byte */ } } static void parse_comp_value(const char* memaddr, memory_t* memory, unsigned expected_value) { rc_value_t* self; char buffer[2048]; int ret; ret = rc_value_size(memaddr); assert(ret >= 0); self = rc_parse_value(buffer, memaddr, NULL, 0); assert(self != NULL); assert(rc_evaluate_value(self, peek, memory, NULL) == expected_value); } static void test_value(void) { { /*------------------------------------------------------------------------ TestAdditionSimple ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; memory.ram = ram; memory.size = sizeof(ram); parse_comp_value("0xH0001_0xH0002", &memory, 0x12U + 0x34U); /* TestAdditionSimple */ parse_comp_value("0xH0001*100_0xH0002*0.5_0xL0003", &memory, 0x12U * 100 + 0x34U / 2 + 0x0B);/* TestAdditionComplex */ parse_comp_value("0xH0001$0xH0002", &memory, 0x34U);/* TestMaximumSimple */ parse_comp_value("0xH0001_0xH0004*3$0xH0002*0xL0003", &memory, 0x34U * 0xBU);/* TestMaximumComplex */ } { /*------------------------------------------------------------------------ TestFormatValue ------------------------------------------------------------------------*/ char buffer[64]; rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_VALUE); assert(!strcmp("12345", buffer)); rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_OTHER); assert(!strcmp("012345", buffer)); rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_SCORE); assert(!strcmp("012345 Points", buffer)); rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_SECONDS); assert(!strcmp("205:45", buffer)); rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_CENTISECS); assert(!strcmp("02:03.45", buffer)); rc_format_value(buffer, sizeof(buffer), 12345, RC_FORMAT_FRAMES); assert(!strcmp("03:25.75", buffer)); rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_SECONDS); assert(!strcmp("05:45", buffer)); rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_CENTISECS); assert(!strcmp("00:03.45", buffer)); rc_format_value(buffer, sizeof(buffer), 345, RC_FORMAT_FRAMES); assert(!strcmp("00:05.75", buffer)); } { /*------------------------------------------------------------------------ TestParseMemValueFormat ------------------------------------------------------------------------*/ assert(rc_parse_format("VALUE") == RC_FORMAT_VALUE); assert(rc_parse_format("SECS") == RC_FORMAT_SECONDS); assert(rc_parse_format("TIMESECS") == RC_FORMAT_SECONDS); assert(rc_parse_format("TIME") == RC_FORMAT_FRAMES); assert(rc_parse_format("FRAMES") == RC_FORMAT_FRAMES); assert(rc_parse_format("SCORE") == RC_FORMAT_SCORE); assert(rc_parse_format("POINTS") == RC_FORMAT_SCORE); assert(rc_parse_format("MILLISECS") == RC_FORMAT_CENTISECS); assert(rc_parse_format("OTHER") == RC_FORMAT_OTHER); assert(rc_parse_format("INVALID") == RC_FORMAT_VALUE); } } static rc_lboard_t* parse_lboard(const char* memaddr, void* buffer) { int ret; rc_lboard_t* self; ret = rc_lboard_size(memaddr); assert(ret >= 0); self = rc_parse_lboard(buffer, memaddr, NULL, 0); assert(self != NULL); return self; } static void lboard_check(const char* memaddr, int expected_ret) { int ret = rc_lboard_size(memaddr); assert(ret == expected_ret); } typedef struct { int active, submitted; } lboard_test_state_t; static void lboard_reset(rc_lboard_t* lboard, lboard_test_state_t* state) { rc_reset_lboard(lboard); state->active = state->submitted = 0; } static unsigned lboard_evaluate(rc_lboard_t* lboard, lboard_test_state_t* test, memory_t* memory) { unsigned value; switch (rc_evaluate_lboard(lboard, &value, peek, memory, NULL)) { case RC_LBOARD_STARTED: test->active = 1; break; case RC_LBOARD_CANCELED: test->active = 0; break; case RC_LBOARD_TRIGGERED: test->active = 0; test->submitted = 1; break; } return value; } static void test_lboard(void) { { /*------------------------------------------------------------------------ TestSimpleLeaderboard ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; unsigned value; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=1::CAN:0xH00=2::SUB:0xH00=3::VAL:0xH02", buffer); state.active = state.submitted = 0; assert(!state.active); assert(!state.submitted); value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 3; /* submit value, but not active */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 2; /* cancel value, but not active */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 1; /* start value */ value = lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(!state.submitted); ram[0] = 2; /* cancel value */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 3; /* submit value, but not active */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 1; /* start value */ value = lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(!state.submitted); ram[0] = 3; /* submit value */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(state.submitted); assert(value == 0x34U); } { /*------------------------------------------------------------------------ TestStartAndCancelSameFrame ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=18::SUB:0xH00=3::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[1] = 0x13; /* disables cancel */ lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(!state.submitted); ram[1] = 0x12; /* enables cancel */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[1] = 0x13; /* disables cancel, but start condition still true, so it shouldn't restart */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 0x01; /* disables start; no effect this frame, but next frame can restart */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(!state.submitted); ram[0] = 0x00; /* enables start */ lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(!state.submitted); } { /*------------------------------------------------------------------------ TestStartAndSubmitSameFrame ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; unsigned value; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer); state.active = state.submitted = 0; value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(state.submitted); assert(value == 0x34U); ram[1] = 0; /* disable submit, value should not be resubmitted, */ value = lboard_evaluate(lboard, &state, &memory); /* start is still true, but leaderboard should not reactivate */ assert(!state.active); ram[0] = 1; /* disable start */ value = lboard_evaluate(lboard, &state, &memory); assert(!state.active); ram[0] = 0; /* reenable start, leaderboard should reactivate */ value = lboard_evaluate(lboard, &state, &memory); assert(state.active); } { /*------------------------------------------------------------------------ TestProgress ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; unsigned value; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", buffer); state.active = state.submitted = 0; value = lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(value == 0x56U); lboard = parse_lboard("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::VAL:0xH02", buffer); state.active = state.submitted = 0; value = lboard_evaluate(lboard, &state, &memory); assert(state.active); assert(value == 0x34U); } { /*------------------------------------------------------------------------ TestStartAndCondition ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0_0xH01=0::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(!state.active); ram[1] = 0; /* second part of start condition is true */ lboard_evaluate(lboard, &state, &memory); assert(state.active); } { /*------------------------------------------------------------------------ TestStartOrCondition ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:S0xH00=1S0xH01=1::CAN:0xH01=10::SUB:0xH01=18::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(!state.active); ram[1] = 1; /* second part of start condition is true */ lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[1] = 0; lboard_reset(lboard, &state); lboard_evaluate(lboard, &state, &memory); assert(!state.active); ram[0] = 1; /* first part of start condition is true */ lboard_evaluate(lboard, &state, &memory); assert(state.active); } { /*------------------------------------------------------------------------ TestCancelOrCondition ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:S0xH01=12S0xH02=12::SUB:0xH00=3::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[2] = 12; /* second part of cancel condition is true */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); ram[2] = 0; /* second part of cancel condition is false */ lboard_reset(lboard, &state); lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[1] = 12; /* first part of cancel condition is true */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); } { /*------------------------------------------------------------------------ TestSubmitAndCondition ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:0xH01=18_0xH03=18::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[3] = 18; lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(state.submitted); } { /*------------------------------------------------------------------------ TestSubmitOrCondition ------------------------------------------------------------------------*/ unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_lboard_t* lboard; lboard_test_state_t state; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); lboard = parse_lboard("STA:0xH00=0::CAN:0xH01=10::SUB:S0xH01=12S0xH03=12::VAL:0xH02", buffer); state.active = state.submitted = 0; lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[3] = 12; /* second part of submit condition is true */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(state.submitted); ram[3] = 0; lboard_reset(lboard, &state); lboard_evaluate(lboard, &state, &memory); assert(state.active); ram[1] = 12; /* first part of submit condition is true */ lboard_evaluate(lboard, &state, &memory); assert(!state.active); assert(state.submitted); } { /*------------------------------------------------------------------------ TestUnparsableStringWillNotStart We'll test for errors in the memaddr field instead ------------------------------------------------------------------------*/ lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::GARBAGE", RC_INVALID_LBOARD_FIELD); lboard_check("CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", RC_MISSING_START); lboard_check("STA:0xH00=0::SUB:0xH00=3::PRO:0xH04::VAL:0xH02", RC_MISSING_CANCEL); lboard_check("STA:0xH00=0::CAN:0xH00=2::PRO:0xH04::VAL:0xH02", RC_MISSING_SUBMIT); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04", RC_MISSING_VALUE); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::STA:0=0", RC_DUPLICATED_START); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::CAN:0=0", RC_DUPLICATED_CANCEL); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::SUB:0=0", RC_DUPLICATED_SUBMIT); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::VAL:0", RC_DUPLICATED_VALUE); lboard_check("STA:0xH00=0::CAN:0xH00=2::SUB:0xH00=3::PRO:0xH04::VAL:0xH02::PRO:0", RC_DUPLICATED_PROGRESS); } } static void test_lua(void) { { /*------------------------------------------------------------------------ TestLua ------------------------------------------------------------------------*/ #ifndef RC_DISABLE_LUA lua_State* L; const char* luacheevo = "return { test = function(peek, ud) return peek(0, 4, ud) end }"; unsigned char ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56}; memory_t memory; rc_trigger_t* trigger; char buffer[2048]; memory.ram = ram; memory.size = sizeof(ram); L = luaL_newstate(); luaL_loadbufferx(L, luacheevo, strlen(luacheevo), "luacheevo.lua", "t"); lua_call(L, 0, 1); memory.ram = ram; memory.size = sizeof(ram); trigger = rc_parse_trigger(buffer, "@test=0xX0", L, 1); assert(rc_test_trigger(trigger, peek, &memory, L) != 0); #endif /* RC_DISABLE_LUA */ } } int main(void) { test_operand(); test_condition(); test_trigger(); test_term(); test_value(); test_lboard(); test_lua(); return 0; }