Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/rc_runtime_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ enum {
RC_OPERATOR_SUB,

RC_OPERATOR_SUB_PARENT, /* internal use */
RC_OPERATOR_ADD_ACCUMULATOR, /* internal use */
RC_OPERATOR_SUB_ACCUMULATOR, /* internal use */
RC_OPERATOR_INDIRECT_READ /* internal use */
};

Expand Down
13 changes: 8 additions & 5 deletions src/rcheevos/condition.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
}

parse->addsource_oper = RC_OPERATOR_ADD;
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
parse->indirect_parent.type = RC_OPERAND_NONE;
break;

Expand All @@ -388,13 +388,13 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
/* type determined by parent */
const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS;

if (parse->addsource_oper == RC_OPERATOR_ADD && !rc_operand_is_memref(&parse->addsource_parent)) {
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR && !rc_operand_is_memref(&parse->addsource_parent)) {
/* if the previous element was a constant we have to turn it into a memref by adding zero */
rc_modified_memref_t* memref;
rc_operand_t zero;
rc_operand_set_const(&zero, 0);
memref = rc_alloc_modified_memref(parse,
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD, &zero);
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD_ACCUMULATOR, &zero);
parse->addsource_parent.value.memref = (rc_memref_t*)memref;
parse->addsource_parent.type = RC_OPERAND_ADDRESS;
}
Expand All @@ -414,13 +414,13 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
}

/* subtract the condition from the chain */
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB : RC_OPERATOR_SUB_PARENT;
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB_ACCUMULATOR : RC_OPERATOR_SUB_PARENT;
rc_condition_convert_to_operand(condition, &cond_operand, parse);
rc_operand_addsource(&cond_operand, parse, new_size);
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));

/* indicate the next value can be added to the chain */
parse->addsource_oper = RC_OPERATOR_ADD;
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
}

parse->indirect_parent.type = RC_OPERAND_NONE;
Expand Down Expand Up @@ -465,6 +465,9 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
default:
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
/* type determined by leaf */
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR)
parse->addsource_oper = RC_OPERATOR_ADD;

rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
condition->operand1.is_combining = 1;

Expand Down
23 changes: 23 additions & 0 deletions src/rcheevos/memref.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,11 +729,34 @@ uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_pee
break;

case RC_OPERATOR_SUB_PARENT:
/* sub parent is "-parent + modifier" */
rc_typed_value_negate(&value);
rc_typed_value_add(&value, &modifier);
rc_typed_value_convert(&value, memref->memref.value.type);
break;

case RC_OPERATOR_SUB_ACCUMULATOR:
rc_typed_value_negate(&modifier);
/* fallthrough */ /* to case RC_OPERATOR_SUB_ACCUMULATOR */

case RC_OPERATOR_ADD_ACCUMULATOR:
/* when modifying the accumulator, force the modifier to match the accumulator
* type instead of promoting them both to the less restrictive type.
*
* 18 - 17.5 will result in an integer. should it be 0 or 1?
*
* default: float is less restrictive, convert both to float for combine,
* then convert to the memref type.
* (int)((float)18 - 17.5) -> (int)(0.5) -> 0
*
* accumulator is integer: force modifier to be integer before combining
* (int)(18 - (int)17.5) -> (int)(18 - 17) -> 1
*/
rc_typed_value_convert(&modifier, value.type);
rc_typed_value_add(&value, &modifier);
rc_typed_value_convert(&value, memref->memref.value.type);
break;

default:
rc_typed_value_combine(&value, &modifier, memref->modifier_type);
rc_typed_value_convert(&value, memref->memref.value.type);
Expand Down
36 changes: 23 additions & 13 deletions src/rcheevos/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
next_clause = &self->conditions;
do {
/* count the number of joiners and add one to determine the number of clauses. */
buffer[0] = 'A'; /* reset to AddSource */
done = 0;
num_measured_conditions = 1;
buffer_ptr = *memaddr;
Expand Down Expand Up @@ -97,8 +98,8 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
}
} while (!done);

/* if last condition is SubSource, we'll need to add a dummy condition for the Measured */
if (buffer[0] == 'B')
/* if last condition is not AddSource, we'll need to add a dummy condition for the Measured */
if (buffer[0] != 'A')
++num_measured_conditions;

condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
Expand All @@ -121,10 +122,18 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
for (;; ++(*memaddr)) {
switch (**memaddr) {
case '_': /* add next */
*ptr = '\0';
break;

case '$': /* maximum of */
case '\0': /* end of string */
case ':': /* end of leaderboard clause */
case ')': /* end of rich presence macro */
/* the last condition needs to be Measured - AddSource can be changed here,
* SubSource will be handled later */
if (buffer[0] == 'A')
buffer[0] = 'M';

*ptr = '\0';
break;

Expand Down Expand Up @@ -176,33 +185,34 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
return;
}

rc_condition_update_parse_state(cond, parse);

*next = cond;
next = &cond->next;

if (**memaddr != '_') /* add next */
break;

rc_condition_update_parse_state(cond, parse);
++cond;
}

/* end of clause */
if (cond->type == RC_CONDITION_SUB_SOURCE) {
/* cannot change SubSource to Measured. add a dummy condition */
rc_condition_update_parse_state(cond, parse);
if (parse->buffer)
/* -- end of clause -- */

/* clause must end in a Measured. if it doesn't, append one */
if (cond->type != RC_CONDITION_MEASURED) {
if (!parse->buffer)
cond = &local_cond;
else
++cond;

buffer_ptr = "A:0";
buffer_ptr = "M:0";
rc_parse_condition_internal(cond, &buffer_ptr, parse);
*next = cond;
next = &cond->next;
rc_condition_update_parse_state(cond, parse);
}

/* convert final AddSource condition to Measured */
cond->type = RC_CONDITION_MEASURED;
cond->next = NULL;
rc_condition_update_parse_state(cond, parse);
*next = NULL;

/* finalize clause */
*next_clause = condset;
Expand Down
29 changes: 29 additions & 0 deletions test/rcheevos/test_condset.c
Original file line number Diff line number Diff line change
Expand Up @@ -3125,6 +3125,34 @@ static void test_addhits_unfinished() {
ASSERT_NUM_EQUALS(condset->num_other_conditions, 2);
}

static void test_addhits_float_coercion() {
uint8_t ram[] = { 0x00, 0x06, 0x34, 0xAB, 0x00, 0x00, 0xC0, 0x3F }; /* fF0004 = 1.5 */
memory_t memory;
rc_condset_t* condset;
rc_memrefs_t memrefs;
char buffer[2048];

memory.ram = ram;
memory.size = sizeof(ram);

/* 0 + float(4) * 10 - prev(float(4)) * 10 + 0 + 0 = 1 */
assert_parse_condset(&condset, &memrefs, buffer, "A:0_A:fF0004*10_B:dfF0004*10_A:0_0=1");

/* float(4) = 1.5, prev(float(4)) = 0.0. 0+15-0+0+0=1 = false */
assert_evaluate_condset(condset, memrefs, &memory, 0);

/* float(4) = 2.0, prev(float(4)) = 1.5. 0+20-15+0+0=1 = false */
ram[6] = 0x00;
ram[7] = 0x40;
assert_evaluate_condset(condset, memrefs, &memory, 0);

/* float(4) = 2.1, prev(float(4)) = 2.0. 0+21-20+0+0=1 = true */
ram[6] = 0x06;
ram[5] = 0x66;
ram[4] = 0x66;
assert_evaluate_condset(condset, memrefs, &memory, 1);
}

static void test_andnext() {
uint8_t ram[] = {0x00, 0x12, 0x34, 0xAB, 0x56};
memory_t memory;
Expand Down Expand Up @@ -5012,6 +5040,7 @@ void test_condset(void) {
TEST(test_subhits);
TEST(test_subhits_below_zero);
TEST(test_addhits_unfinished);
TEST(test_addhits_float_coercion)

/* andnext */
TEST(test_andnext);
Expand Down
79 changes: 79 additions & 0 deletions test/rcheevos/test_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,82 @@ static void test_typed_value_negation() {
TEST_PARAMS6(test_typed_value_negate, RC_VALUE_TYPE_FLOAT, 0, -2.7, RC_VALUE_TYPE_FLOAT, 0, 2.7);
}

static void test_addhits_float_coercion() {
rc_value_t* self;
uint8_t ram[] = { 0x00, 0x06, 0x34, 0xAB, 0x00, 0x00, 0xC0, 0x3F }; /* fF0004 = 1.5 */
memory_t memory;
char buffer[2048];
/* measured(tally(0, (0 + float(4) * 10 - prev(float(4)) * 10) == 1)) */
const char* memaddr = "A:0_A:fF0004*10_B:dfF0004*10_C:0=1_M:0=1";
int ret;

memory.ram = ram;
memory.size = sizeof(ram);

ret = rc_value_size(memaddr);
ASSERT_NUM_GREATER_EQUALS(ret, 0);

self = rc_parse_value(buffer, memaddr, NULL, 0);
ASSERT_PTR_NOT_NULL(self);

/* The 0+ at the start of the expression changes the accumulator to an integer,
* so when float(4)*10 is added and prev(float(4))*10 is subtracted, they'll also
* be converted to integers before they're combined.
*/

/* float(4) = 1.5, prev(float(4)) = 0.0. 0+15-0=1 is false => 0 */
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 0);

/* float(4) = 1.75, prev(float(4)) = 1.5. 0+17-15 => 2 => 2=1 is false => 0 */
ram[7] = 0x3f; ram[6] = 0xe0;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 0);

/* float(4) = 1.82, prev(float(4)) = 1.75. 0+18-17 => 1 => 1=1 is true => 1 */
ram[6] = 0xe8; ram[5] = 0xf5; ram[4] = 0xc3;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 1);

/* float(4) = 2.06, prev(float(4)) = 1.82. 0+20-18 => 2 => 2=1 is false => 1 */
ram[7] = 0x40; ram[6] = 0x03; ram[5] = 0xd7; ram[4] = 0x0a;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 1);
}

static void test_addhits_float_coercion_remembered() {
rc_value_t* self;
uint8_t ram[] = { 0x00, 0x06, 0x34, 0xAB, 0x00, 0x00, 0xC0, 0x3F }; /* fF0004 = 1.5 */
memory_t memory;
char buffer[2048];
/* measured(tally(0, remembered(0 - prev(float(4)) * 10) + (0 + float(4) * 10) == 1)) */
const char* memaddr = "A:0_B:dfF0004*10_K:0_A:0_A:fF0004*10_C:{recall}=1_M:0=1";
int ret;

memory.ram = ram;
memory.size = sizeof(ram);

ret = rc_value_size(memaddr);
ASSERT_NUM_GREATER_EQUALS(ret, 0);

self = rc_parse_value(buffer, memaddr, NULL, 0);
ASSERT_PTR_NOT_NULL(self);

/* using remember allows for both sides to explicitly be cast to integer before
* performing the subtraction. */

/* float(4) = 1.5, prev(float(4)) = 0.0. 15-0 => 15=1 is false => 0 */
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 0);

/* float(4) = 1.75, prev(float(4)) = 1.5. 17-15 => 2=1 is false => 0 */
ram[7] = 0x3f; ram[6] = 0xe0;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 0);

/* float(4) = 1.82, prev(float(4)) = 1.75. 18-17 => 1=1 is true => 1 */
ram[6] = 0xe8; ram[5] = 0xf5; ram[4] = 0xc3;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 1);

/* float(4) = 2.06, prev(float(4)) = 1.82. 20-18 => 2=1 is false => 1 */
ram[7] = 0x40; ram[6] = 0x03; ram[5] = 0xd7; ram[4] = 0x0a;
ASSERT_NUM_EQUALS(rc_evaluate_value(self, peek, &memory, NULL), 1);
}

void test_value(void) {
TEST_SUITE_BEGIN();

Expand Down Expand Up @@ -785,5 +861,8 @@ void test_value(void) {
test_typed_value_modulus();
test_typed_value_negation();

test_addhits_float_coercion();
test_addhits_float_coercion_remembered();

TEST_SUITE_END();
}
Loading