You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
7.3 KiB

#include "rc_internal.h"
#include <stdlib.h>
static int rc_parse_operator(const char** memaddr) {
const char* oper = *memaddr;
switch (*oper) {
case '=':
++(*memaddr);
(*memaddr) += (**memaddr == '=');
return RC_OPERATOR_EQ;
case '!':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_NE;
}
/* fall through */
default:
return RC_INVALID_OPERATOR;
case '<':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_LE;
}
++(*memaddr);
return RC_OPERATOR_LT;
case '>':
if (oper[1] == '=') {
(*memaddr) += 2;
return RC_OPERATOR_GE;
}
++(*memaddr);
return RC_OPERATOR_GT;
case '*':
++(*memaddr);
return RC_OPERATOR_MULT;
case '/':
++(*memaddr);
return RC_OPERATOR_DIV;
case '&':
++(*memaddr);
return RC_OPERATOR_AND;
case '^':
++(*memaddr);
return RC_OPERATOR_XOR;
case '\0':/* end of string */
case '_': /* next condition */
case 'S': /* next condset */
case ')': /* end of macro */
case '$': /* maximum of values */
/* valid condition separator, condition may not have an operator */
return RC_OPERATOR_NONE;
}
}
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
rc_condition_t* self;
const char* aux;
int result;
int can_modify = 0;
aux = *memaddr;
self = RC_ALLOC(rc_condition_t, parse);
self->current_hits = 0;
self->is_true = 0;
self->pause = 0;
if (*aux != 0 && aux[1] == ':') {
switch (*aux) {
case 'p': case 'P': self->type = RC_CONDITION_PAUSE_IF; break;
case 'r': case 'R': self->type = RC_CONDITION_RESET_IF; break;
case 'a': case 'A': self->type = RC_CONDITION_ADD_SOURCE; can_modify = 1; break;
case 'b': case 'B': self->type = RC_CONDITION_SUB_SOURCE; can_modify = 1; break;
case 'c': case 'C': self->type = RC_CONDITION_ADD_HITS; break;
case 'd': case 'D': self->type = RC_CONDITION_SUB_HITS; break;
case 'n': case 'N': self->type = RC_CONDITION_AND_NEXT; break;
case 'o': case 'O': self->type = RC_CONDITION_OR_NEXT; break;
case 'm': case 'M': self->type = RC_CONDITION_MEASURED; break;
case 'q': case 'Q': self->type = RC_CONDITION_MEASURED_IF; break;
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; can_modify = 1; break;
case 't': case 'T': self->type = RC_CONDITION_TRIGGER; break;
case 'z': case 'Z': self->type = RC_CONDITION_RESET_NEXT_IF; break;
case 'g': case 'G':
parse->measured_as_percent = 1;
self->type = RC_CONDITION_MEASURED;
break;
/* e f h j k l s u v w x y */
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
}
aux += 2;
}
else {
self->type = RC_CONDITION_STANDARD;
}
result = rc_parse_operand(&self->operand1, &aux, is_indirect, parse);
if (result < 0) {
parse->offset = result;
return 0;
}
result = rc_parse_operator(&aux);
if (result < 0) {
parse->offset = result;
return 0;
}
self->oper = (char)result;
switch (self->oper) {
case RC_OPERATOR_NONE:
/* non-modifying statements must have a second operand */
if (!can_modify) {
/* measured does not require a second operand when used in a value */
if (self->type != RC_CONDITION_MEASURED) {
parse->offset = RC_INVALID_OPERATOR;
return 0;
}
}
/* provide dummy operand of '1' and no required hits */
self->operand2.type = RC_OPERAND_CONST;
self->operand2.value.num = 1;
self->required_hits = 0;
*memaddr = aux;
return self;
case RC_OPERATOR_MULT:
case RC_OPERATOR_DIV:
case RC_OPERATOR_AND:
case RC_OPERATOR_XOR:
/* modifying operators are only valid on modifying statements */
if (can_modify)
break;
/* fallthrough */
default:
/* comparison operators are not valid on modifying statements */
if (can_modify) {
switch (self->type) {
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_ADD_ADDRESS:
/* prevent parse errors on legacy achievements where a condition was present before changing the type */
self->oper = RC_OPERATOR_NONE;
break;
default:
parse->offset = RC_INVALID_OPERATOR;
return 0;
}
}
break;
}
result = rc_parse_operand(&self->operand2, &aux, is_indirect, parse);
if (result < 0) {
parse->offset = result;
return 0;
}
if (self->oper == RC_OPERATOR_NONE) {
/* if operator is none, explicitly clear out the right side */
self->operand2.type = RC_OPERAND_CONST;
self->operand2.value.num = 0;
}
if (*aux == '(') {
char* end;
self->required_hits = (unsigned)strtoul(++aux, &end, 10);
if (end == aux || *end != ')') {
parse->offset = RC_INVALID_REQUIRED_HITS;
return 0;
}
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else if (*aux == '.') {
char* end;
self->required_hits = (unsigned)strtoul(++aux, &end, 10);
if (end == aux || *end != '.') {
parse->offset = RC_INVALID_REQUIRED_HITS;
return 0;
}
/* if operator is none, explicitly clear out the required hits */
if (self->oper == RC_OPERATOR_NONE)
self->required_hits = 0;
else
parse->has_required_hits = 1;
aux = end + 1;
}
else {
self->required_hits = 0;
}
*memaddr = aux;
return self;
}
int rc_condition_is_combining(const rc_condition_t* self) {
switch (self->type) {
case RC_CONDITION_STANDARD:
case RC_CONDITION_PAUSE_IF:
case RC_CONDITION_RESET_IF:
case RC_CONDITION_MEASURED_IF:
case RC_CONDITION_TRIGGER:
case RC_CONDITION_MEASURED:
return 0;
default:
return 1;
}
}
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) {
rc_typed_value_t value1, value2;
rc_evaluate_operand(&value1, &self->operand1, eval_state);
if (eval_state->add_value.type != RC_VALUE_TYPE_NONE)
rc_typed_value_add(&value1, &eval_state->add_value);
rc_evaluate_operand(&value2, &self->operand2, eval_state);
return rc_typed_value_compare(&value1, &value2, self->oper);
}
void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state) {
rc_typed_value_t amount;
rc_evaluate_operand(value, &self->operand1, eval_state);
rc_evaluate_operand(&amount, &self->operand2, eval_state);
switch (self->oper) {
case RC_OPERATOR_MULT:
rc_typed_value_multiply(value, &amount);
break;
case RC_OPERATOR_DIV:
rc_typed_value_divide(value, &amount);
break;
case RC_OPERATOR_AND:
rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED);
value->value.u32 &= amount.value.u32;
break;
case RC_OPERATOR_XOR:
rc_typed_value_convert(value, RC_VALUE_TYPE_UNSIGNED);
rc_typed_value_convert(&amount, RC_VALUE_TYPE_UNSIGNED);
value->value.u32 ^= amount.value.u32;
break;
}
}