update rcheevos to v8.1

This commit is contained in:
Jamiras 2019-11-10 15:28:43 -07:00
parent d3ed02dd87
commit 9983976ddb
18 changed files with 758 additions and 247 deletions

View File

@ -110,7 +110,7 @@ typedef struct
rc_lboard_t* lboard;
const rcheevos_ralboard_t* info;
bool active;
unsigned last_value;
int last_value;
int format;
} rcheevos_lboard_t;
@ -355,6 +355,13 @@ static const char* rcheevos_rc_error(int ret)
case RC_MISSING_SUBMIT: return "Missing submit condition";
case RC_MISSING_VALUE: return "Missing value expression";
case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
case RC_MISSING_DISPLAY_STRING: return "Missing display string";
case RC_OUT_OF_MEMORY: return "Out of memory";
case RC_INVALID_VALUE_FLAG: return "Invalid flag in value expression";
case RC_MISSING_VALUE_MEASURED: return "Missing value (measured)";
case RC_MULTIPLE_MEASURED: return "Multiple measured targets";
case RC_INVALID_MEASURED_TARGET: return "Invalid measured target";
default: return "Unknown error";
}
}
@ -803,7 +810,6 @@ static void rcheevos_test_leaderboards(void)
CHEEVOS_LOG(RCHEEVOS_TAG "Leaderboard started: %s\n", lboard->info->title);
lboard->active = 1;
lboard->last_value = 0;
snprintf(buffer, sizeof(buffer),
"Leaderboard Active: %s", lboard->info->title);
@ -824,19 +830,29 @@ void rcheevos_reset_game(void)
cheevo = rcheevos_locals.core;
for (i = 0; i < rcheevos_locals.patchdata.core_count; i++, cheevo++)
{
rc_reset_trigger(cheevo->trigger);
cheevo->last = 1;
}
cheevo = rcheevos_locals.unofficial;
for (i = 0; i < rcheevos_locals.patchdata.unofficial_count; i++, cheevo++)
{
rc_reset_trigger(cheevo->trigger);
cheevo->last = 1;
}
lboard = rcheevos_locals.lboards;
for (i = 0; i < rcheevos_locals.patchdata.lboard_count; i++, lboard++)
{
rc_reset_lboard(lboard->lboard);
if (lboard->active)
{
lboard->active = 0;
/* this ensures the leaderboard won't restart until the start trigger is false for at least one frame */
lboard->lboard->submitted = 1;
}
}
}

View File

@ -53,7 +53,13 @@ enum {
RC_MISSING_CANCEL = -14,
RC_MISSING_SUBMIT = -15,
RC_MISSING_VALUE = -16,
RC_INVALID_LBOARD_FIELD = -17
RC_INVALID_LBOARD_FIELD = -17,
RC_MISSING_DISPLAY_STRING = -18,
RC_OUT_OF_MEMORY = -19,
RC_INVALID_VALUE_FLAG = -20,
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23
};
```
@ -190,16 +196,14 @@ struct rc_condition_t {
/* Number of hits so far. */
unsigned current_hits;
/**
* Set if the condition needs to processed as part of the "check if paused"
* pass
*/
char pause;
/* The type of the condition. */
char type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Whether or not the condition evaluated as true on the last check. */
char is_true;
};
```
@ -212,7 +216,10 @@ enum {
RC_CONDITION_RESET_IF,
RC_CONDITION_ADD_SOURCE,
RC_CONDITION_SUB_SOURCE,
RC_CONDITION_ADD_HITS
RC_CONDITION_ADD_HITS,
RC_CONDITION_AND_NEXT,
RC_CONDITION_MEASURED,
RC_CONDITION_ADD_ADDRESS
};
```
@ -225,7 +232,8 @@ enum {
RC_CONDITION_LE,
RC_CONDITION_GT,
RC_CONDITION_GE,
RC_CONDITION_NE
RC_CONDITION_NE,
RC_CONDITION_NONE
};
```
@ -279,10 +287,10 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
`buffer` is the caller-allocated buffer, which must have enough space for the trigger. `memaddr` describes the trigger, and must be the same one used to compute the trigger's size with `rc_trigger_size`. `L` must be a valid Lua state, and `funcs_ndx` must be an index to the current Lua stack which contains a table which is a map of names to functions. This map is used to look for operands which are Lua functions.
Once the trigger is created, `rc_test_trigger` can be called to test whether the trigger fires or not.
Once the trigger is created, `rc_evaluate_trigger` can be called to test whether the trigger fires or not.
```c
int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
```
`trigger` is the trigger to test. `peek` is a callback used to read bytes from the emulated memory. `ud` is an user-provided opaque value that is passed to `peek`. `L` is the Lua state in which context the Lua functions are looked for and called, if necessary.
@ -297,6 +305,18 @@ where `address` is the starting address to read from, `num_bytes` the number of
> Addresses passed to `peek` do *not* map 1:1 to the emulated memory. (**TODO**: document the mapping from `peek` addresses to emulated memory for each supported system.)
The return value of `rc_evaluate_trigger` is one of the following:
```c
enum {
RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */
RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
RC_TRIGGER_STATE_TRIGGERED /* achievement has triggered */
};
```
Finally, `rc_reset_trigger` can be used to reset the internal state of a trigger.
```c
@ -348,6 +368,12 @@ A value is a collection of expressions. It's used to give the value for a leader
typedef struct {
/* The list of expression to evaluate. */
rc_expression_t* expressions;
/* The list of conditions to evaluate. */
rc_condset_t* conditions;
/* The memory references required by the value. */
rc_memref_value_t* memrefs;
}
rc_value_t;
```
@ -369,7 +395,7 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
To compute the value, use `rc_evaluate_value`:
```c
unsigned rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
```
`value` is the value to compute the value of, and `peek`, `ud`, and `L`, are as in [`rc_test_trigger`](#rc_test_trigger).
@ -404,7 +430,7 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
A leaderboard can be evaluated with the `rc_evaluate_lboard` function:
```c
int rc_evaluate_lboard(rc_lboard_t* lboard, unsigned* value, rc_peek_t peek, void* peek_ud, lua_State* L);
int rc_evaluate_lboard(rc_lboard_t* lboard, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
```
The function returns an action that must be performed by the caller, and `value` contains the value to be used for that action when the function returns. The action can be one of:
@ -451,7 +477,8 @@ enum {
RC_FORMAT_CENTISECS,
RC_FORMAT_SCORE,
RC_FORMAT_VALUE,
RC_FORMAT_OTHER,
RC_FORMAT_MINUTES,
RC_FORMAT_SECONDS_AS_MINUTES
};
```
@ -460,10 +487,10 @@ enum {
`rc_format_value` can be used to format the given value into the provided buffer:
```c
void rc_format_value(char* buffer, int size, unsigned value, int format);
int rc_format_value(char* buffer, int size, int value, int format);
```
`buffer` receives `value` formatted according to `format`. No more than `size` characters will be written to `buffer`. 32 characters are enough to hold any valid value with any format.
`buffer` receives `value` formatted according to `format`. No more than `size` characters will be written to `buffer`. 32 characters are enough to hold any valid value with any format. The returned value is the number of characters written.
# **rurl**

View File

@ -31,7 +31,11 @@ enum {
RC_MISSING_VALUE = -16,
RC_INVALID_LBOARD_FIELD = -17,
RC_MISSING_DISPLAY_STRING = -18,
RC_OUT_OF_MEMORY = -19
RC_OUT_OF_MEMORY = -19,
RC_INVALID_VALUE_FLAG = -20,
RC_MISSING_VALUE_MEASURED = -21,
RC_MULTIPLE_MEASURED = -22,
RC_INVALID_MEASURED_TARGET = -23
};
/*****************************************************************************\
@ -112,6 +116,8 @@ typedef struct {
char size;
/* True if the value is in BCD. */
char is_bcd;
/* True if the reference will be used in indirection */
char is_indirect;
} rc_memref_t;
typedef struct rc_memref_value_t rc_memref_value_t;
@ -176,7 +182,9 @@ enum {
RC_CONDITION_ADD_SOURCE,
RC_CONDITION_SUB_SOURCE,
RC_CONDITION_ADD_HITS,
RC_CONDITION_AND_NEXT
RC_CONDITION_AND_NEXT,
RC_CONDITION_MEASURED,
RC_CONDITION_ADD_ADDRESS
};
/* operators */
@ -186,7 +194,8 @@ enum {
RC_CONDITION_LE,
RC_CONDITION_GT,
RC_CONDITION_GE,
RC_CONDITION_NE
RC_CONDITION_NE,
RC_CONDITION_NONE
};
typedef struct rc_condition_t rc_condition_t;
@ -204,16 +213,17 @@ struct rc_condition_t {
/* The next condition in the chain. */
rc_condition_t* next;
/**
* Set if the condition needs to processed as part of the "check if paused"
* pass
*/
char pause;
/* The type of the condition. */
char type;
/* The comparison operator to use. */
char oper; /* operator is a reserved word in C++. */
/* Set if the condition needs to processed as part of the "check if paused" pass. */
char pause;
/* Whether or not the condition evaluated true on the last check */
char is_true;
};
/*****************************************************************************\
@ -231,12 +241,24 @@ struct rc_condset_t {
/* True if any condition in the set is a pause condition. */
char has_pause;
/* True if the set is currently paused. */
char is_paused;
};
/*****************************************************************************\
| Trigger |
\*****************************************************************************/
enum {
RC_TRIGGER_STATE_INACTIVE, /* achievement is not being processed */
RC_TRIGGER_STATE_WAITING, /* achievement cannot trigger until it has been false for at least one frame */
RC_TRIGGER_STATE_ACTIVE, /* achievement is active and may trigger */
RC_TRIGGER_STATE_PAUSED, /* achievement is currently paused and will not trigger */
RC_TRIGGER_STATE_RESET, /* achievement hit counts were reset */
RC_TRIGGER_STATE_TRIGGERED /* achievement has triggered */
};
typedef struct {
/* The main condition set. */
rc_condset_t* requirement;
@ -246,11 +268,24 @@ typedef struct {
/* The memory references required by the trigger. */
rc_memref_value_t* memrefs;
/* The current state of the MEASURED condition. */
unsigned measured_value;
/* The target state of the MEASURED condition */
unsigned measured_target;
/* The current state of the trigger */
char state;
/* True if at least one condition has a non-zero hit count */
char has_hits;
}
rc_trigger_t;
int rc_trigger_size(const char* memaddr);
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
int rc_test_trigger(rc_trigger_t* trigger, rc_peek_t peek, void* ud, lua_State* L);
void rc_reset_trigger(rc_trigger_t* self);
@ -287,6 +322,9 @@ typedef struct {
/* The list of expression to evaluate. */
rc_expression_t* expressions;
/* The list of conditions to evaluate. */
rc_condset_t* conditions;
/* The memory references required by the value. */
rc_memref_value_t* memrefs;
}
@ -294,7 +332,7 @@ rc_value_t;
int rc_value_size(const char* memaddr);
rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
unsigned rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
int rc_evaluate_value(rc_value_t* value, rc_peek_t peek, void* ud, lua_State* L);
/*****************************************************************************\
| Leaderboards |
@ -324,7 +362,7 @@ rc_lboard_t;
int rc_lboard_size(const char* memaddr);
rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx);
int rc_evaluate_lboard(rc_lboard_t* lboard, unsigned* value, rc_peek_t peek, void* peek_ud, lua_State* L);
int rc_evaluate_lboard(rc_lboard_t* lboard, int* value, rc_peek_t peek, void* peek_ud, lua_State* L);
void rc_reset_lboard(rc_lboard_t* lboard);
/*****************************************************************************\
@ -338,11 +376,12 @@ enum {
RC_FORMAT_CENTISECS,
RC_FORMAT_SCORE,
RC_FORMAT_VALUE,
RC_FORMAT_OTHER
RC_FORMAT_MINUTES,
RC_FORMAT_SECONDS_AS_MINUTES
};
int rc_parse_format(const char* format_str);
int rc_format_value(char* buffer, int size, unsigned value, int format);
int rc_format_value(char* buffer, int size, int value, int format);
/*****************************************************************************\
| Rich Presence |

View File

@ -9,7 +9,7 @@ extern "C" {
int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned cheevo_id, int hardcore);
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, unsigned value, unsigned char hash[16]);
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value, unsigned char hash[16]);
int rc_url_get_gameid(char* buffer, size_t size, unsigned char hash[16]);

View File

@ -44,6 +44,7 @@ void rc_init_parse_state(rc_parse_state_t* parse, void* buffer, lua_State* L, in
parse->scratch.memref_size = sizeof(parse->scratch.memref_buffer) / sizeof(parse->scratch.memref_buffer[0]);
parse->scratch.memref_count = 0;
parse->first_memref = 0;
parse->measured_target = 0;
}
void rc_destroy_parse_state(rc_parse_state_t* parse)

View File

@ -2,7 +2,7 @@
#include <stdlib.h>
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse) {
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 ret2;
@ -19,6 +19,8 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
case 'b': case 'B': self->type = RC_CONDITION_SUB_SOURCE; break;
case 'c': case 'C': self->type = RC_CONDITION_ADD_HITS; break;
case 'n': case 'N': self->type = RC_CONDITION_AND_NEXT; break;
case 'm': case 'M': self->type = RC_CONDITION_MEASURED; break;
case 'i': case 'I': self->type = RC_CONDITION_ADD_ADDRESS; break;
default: parse->offset = RC_INVALID_CONDITION_TYPE; return 0;
}
@ -28,7 +30,7 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
self->type = RC_CONDITION_STANDARD;
}
ret2 = rc_parse_operand(&self->operand1, &aux, 1, parse);
ret2 = rc_parse_operand(&self->operand1, &aux, 1, is_indirect, parse);
if (ret2 < 0) {
parse->offset = ret2;
@ -71,9 +73,19 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
}
break;
case '_':
case ')':
case '\0':
self->oper = RC_CONDITION_NONE;
self->operand2.type = RC_OPERAND_CONST;
self->operand2.value.num = 1;
self->required_hits = 0;
*memaddr = aux - 1;
return self;
}
ret2 = rc_parse_operand(&self->operand2, &aux, 1, parse);
ret2 = rc_parse_operand(&self->operand2, &aux, 1, is_indirect, parse);
if (ret2 < 0) {
parse->offset = ret2;
@ -110,9 +122,9 @@ rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse
return self;
}
int rc_test_condition(rc_condition_t* self, unsigned add_buffer, rc_peek_t peek, void* ud, lua_State* L) {
unsigned value1 = rc_evaluate_operand(&self->operand1, peek, ud, L) + add_buffer;
unsigned value2 = rc_evaluate_operand(&self->operand2, peek, ud, L);
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state) {
unsigned value1 = rc_evaluate_operand(&self->operand1, eval_state) + eval_state->add_value;
unsigned value2 = rc_evaluate_operand(&self->operand2, eval_state);
switch (self->oper) {
case RC_CONDITION_EQ: return value1 == value2;
@ -121,6 +133,7 @@ int rc_test_condition(rc_condition_t* self, unsigned add_buffer, rc_peek_t peek,
case RC_CONDITION_LE: return value1 <= value2;
case RC_CONDITION_GT: return value1 > value2;
case RC_CONDITION_GE: return value1 >= value2;
case RC_CONDITION_NONE: return 1;
default: return 1;
}
}

View File

@ -14,6 +14,7 @@ static void rc_update_condition_pause(rc_condition_t* condition, int* in_pause)
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_ADD_ADDRESS:
condition->pause = *in_pause;
break;
@ -27,9 +28,10 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
rc_condset_t* self;
rc_condition_t** next;
int in_pause;
int in_add_address;
self = RC_ALLOC(rc_condset_t, parse);
self->has_pause = 0;
self->has_pause = self->is_paused = 0;
next = &self->conditions;
if (**memaddr == 'S' || **memaddr == 's' || !**memaddr) {
@ -38,14 +40,54 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
return self;
}
in_add_address = 0;
for (;;) {
*next = rc_parse_condition(memaddr, parse);
*next = rc_parse_condition(memaddr, parse, in_add_address);
if (parse->offset < 0) {
return 0;
}
if ((*next)->oper == RC_CONDITION_NONE) {
switch ((*next)->type) {
case RC_CONDITION_ADD_ADDRESS:
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_AND_NEXT:
break;
default:
parse->offset = RC_INVALID_OPERATOR;
return 0;
}
}
self->has_pause |= (*next)->type == RC_CONDITION_PAUSE_IF;
in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS;
if ((*next)->type == RC_CONDITION_MEASURED) {
unsigned measured_target = 0;
if ((*next)->required_hits == 0) {
if ((*next)->operand2.type != RC_OPERAND_CONST) {
parse->offset = RC_INVALID_MEASURED_TARGET;
return 0;
}
measured_target = (*next)->operand2.value.num;
}
else {
measured_target = (*next)->required_hits;
}
if (parse->measured_target && measured_target != parse->measured_target) {
parse->offset = RC_MULTIPLE_MEASURED;
return 0;
}
parse->measured_target = measured_target;
}
next = &(*next)->next;
if (**memaddr != '_') {
@ -66,14 +108,13 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) {
return self;
}
static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, int* reset, rc_peek_t peek, void* ud, lua_State* L) {
static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, rc_eval_state_t* eval_state) {
rc_condition_t* condition;
int set_valid, cond_valid, prev_cond;
unsigned add_buffer, add_hits;
set_valid = 1;
prev_cond = 1;
add_buffer = add_hits = 0;
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
for (condition = self->conditions; condition != 0; condition = condition->next) {
if (condition->pause != processing_pause) {
@ -82,39 +123,60 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, in
switch (condition->type) {
case RC_CONDITION_ADD_SOURCE:
add_buffer += rc_evaluate_operand(&condition->operand1, peek, ud, L);
eval_state->add_value += rc_evaluate_operand(&condition->operand1, eval_state);
eval_state->add_address = 0;
continue;
case RC_CONDITION_SUB_SOURCE:
add_buffer -= rc_evaluate_operand(&condition->operand1, peek, ud, L);
eval_state->add_value -= rc_evaluate_operand(&condition->operand1, eval_state);
eval_state->add_address = 0;
continue;
case RC_CONDITION_ADD_HITS:
if (rc_test_condition(condition, add_buffer, peek, ud, L)) {
/* always evaluate the condition to ensure everything is updated correctly */
cond_valid = rc_test_condition(condition, eval_state);
/* merge AndNext value and reset it for the next condition */
cond_valid &= prev_cond;
prev_cond = 1;
/* if the condition is true, tally it */
if (cond_valid) {
if (condition->required_hits == 0 || condition->current_hits < condition->required_hits) {
condition->current_hits++;
}
condition->is_true = (condition->required_hits == 0 || condition->current_hits >= condition->required_hits);
}
else {
condition->is_true = 0;
}
add_buffer = 0;
add_hits += condition->current_hits;
eval_state->add_value = 0;
eval_state->add_address = 0;
eval_state->add_hits += condition->current_hits;
continue;
case RC_CONDITION_AND_NEXT:
prev_cond &= rc_test_condition(condition, add_buffer, peek, ud, L);
add_buffer = 0;
prev_cond &= rc_test_condition(condition, eval_state);
eval_state->add_value = 0;
eval_state->add_address = 0;
continue;
case RC_CONDITION_ADD_ADDRESS:
eval_state->add_address = rc_evaluate_operand(&condition->operand1, eval_state);
continue;
}
/* always evaluate the condition to ensure delta values get tracked correctly */
cond_valid = rc_test_condition(condition, add_buffer, peek, ud, L);
/* always evaluate the condition to ensure everything is updated correctly */
cond_valid = rc_test_condition(condition, eval_state);
/* merge AndNext value and reset it for the next condition */
cond_valid &= prev_cond;
prev_cond = 1;
/* if the condition has a target hit count that has already been met, it's automatically true, even if not currently true. */
if (condition->required_hits != 0 && (condition->current_hits + add_hits) >= condition->required_hits) {
if (condition->required_hits != 0 && (condition->current_hits + eval_state->add_hits) >= condition->required_hits) {
cond_valid = 1;
}
else if (cond_valid) {
@ -123,14 +185,28 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, in
if (condition->required_hits == 0) {
/* not a hit-based requirement: ignore any additional logic! */
}
else if ((condition->current_hits + add_hits) < condition->required_hits) {
else if ((condition->current_hits + eval_state->add_hits) < condition->required_hits) {
/* HitCount target has not yet been met, condition is not yet valid */
cond_valid = 0;
}
}
condition->is_true = cond_valid;
eval_state->has_hits |= (condition->current_hits || eval_state->add_hits);
/* capture measured state */
if (condition->type == RC_CONDITION_MEASURED) {
unsigned int measured_value;
if (condition->required_hits > 0)
measured_value = condition->current_hits + eval_state->add_hits;
else
measured_value = rc_evaluate_operand(&condition->operand1, eval_state) + eval_state->add_value;
if (measured_value > eval_state->measured_value)
eval_state->measured_value = measured_value;
}
/* reset AddHits and AddSource/SubSource values */
add_buffer = add_hits = 0;
eval_state->add_value = eval_state->add_hits = eval_state->add_address = 0;
switch (condition->type) {
case RC_CONDITION_PAUSE_IF:
@ -155,7 +231,7 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, in
case RC_CONDITION_RESET_IF:
if (cond_valid) {
*reset = 1; /* let caller know to reset all hit counts */
eval_state->was_reset = 1; /* let caller know to reset all hit counts */
set_valid = 0; /* cannot be valid if we've hit a reset condition */
}
@ -170,18 +246,20 @@ static int rc_test_condset_internal(rc_condset_t* self, int processing_pause, in
return set_valid;
}
int rc_test_condset(rc_condset_t* self, int* reset, rc_peek_t peek, void* ud, lua_State* L) {
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state) {
if (self->conditions == 0) {
/* important: empty group must evaluate true */
return 1;
}
if (self->has_pause && rc_test_condset_internal(self, 1, reset, peek, ud, L)) {
if (self->has_pause) {
if ((self->is_paused = rc_test_condset_internal(self, 1, eval_state))) {
/* one or more Pause conditions exists, if any of them are true, stop processing this group */
return 0;
}
}
return rc_test_condset_internal(self, 0, reset, peek, ud, L);
return rc_test_condset_internal(self, 0, eval_state);
}
void rc_reset_condset(rc_condset_t* self) {

View File

@ -8,7 +8,7 @@ rc_expression_t* rc_parse_expression(const char** memaddr, rc_parse_state_t* par
next = &self->terms;
for (;;) {
*next = rc_parse_term(memaddr, parse);
*next = rc_parse_term(memaddr, 0, parse);
if (parse->offset < 0) {
return 0;
@ -27,14 +27,14 @@ rc_expression_t* rc_parse_expression(const char** memaddr, rc_parse_state_t* par
return self;
}
unsigned rc_evaluate_expression(rc_expression_t* self, rc_peek_t peek, void* ud, lua_State* L) {
int rc_evaluate_expression(rc_expression_t* self, rc_eval_state_t* eval_state) {
rc_term_t* term;
unsigned value;
int value;
value = 0;
for (term = self->terms; term != 0; term = term->next) {
value += rc_evaluate_term(term, peek, ud, L);
value += rc_evaluate_term(term, eval_state);
}
return value;

View File

@ -29,6 +29,9 @@ int rc_parse_format(const char* format_str) {
if (!strcmp(format_str, "CORE")) {
return RC_FORMAT_SCORE;
}
if (!strcmp(format_str, "ECS_AS_MINS")) {
return RC_FORMAT_SECONDS_AS_MINUTES;
}
break;
@ -36,6 +39,9 @@ int rc_parse_format(const char* format_str) {
if (!strcmp(format_str, "ILLISECS")) {
return RC_FORMAT_CENTISECS;
}
if (!strcmp(format_str, "INUTES")) {
return RC_FORMAT_MINUTES;
}
break;
@ -55,7 +61,7 @@ int rc_parse_format(const char* format_str) {
case 'O':
if (!strcmp(format_str, "THER")) {
return RC_FORMAT_OTHER;
return RC_FORMAT_SCORE;
}
break;
@ -64,45 +70,82 @@ int rc_parse_format(const char* format_str) {
return RC_FORMAT_VALUE;
}
int rc_format_value(char* buffer, int size, unsigned value, int format) {
unsigned a, b, c;
static int rc_format_value_minutes(char* buffer, int size, unsigned minutes) {
unsigned hours;
hours = minutes / 60;
minutes -= hours * 60;
return snprintf(buffer, size, "%uh%02u", hours, minutes);
}
static int rc_format_value_seconds(char* buffer, int size, unsigned seconds) {
unsigned hours, minutes;
/* apply modulus math to split the seconds into hours/minutes/seconds */
minutes = seconds / 60;
seconds -= minutes * 60;
if (minutes < 60) {
return snprintf(buffer, size, "%u:%02u", minutes, seconds);
}
hours = minutes / 60;
minutes -= hours * 60;
return snprintf(buffer, size, "%uh%02u:%02u", hours, minutes, seconds);
}
static int rc_format_value_centiseconds(char* buffer, int size, unsigned centiseconds) {
unsigned seconds;
int chars, chars2;
/* modulus off the centiseconds */
seconds = centiseconds / 100;
centiseconds -= seconds * 100;
chars = rc_format_value_seconds(buffer, size, seconds);
if (chars > 0) {
chars2 = snprintf(buffer + chars, size - chars, ".%02u", centiseconds);
if (chars2 > 0) {
chars += chars2;
} else {
chars = chars2;
}
}
return chars;
}
int rc_format_value(char* buffer, int size, int value, int format) {
int chars;
switch (format) {
case RC_FORMAT_FRAMES:
a = value * 10 / 6; /* centisecs */
b = a / 100; /* seconds */
a -= b * 100;
c = b / 60; /* minutes */
b -= c * 60;
chars = snprintf(buffer, size, "%02u:%02u.%02u", c, b, a);
/* 60 frames per second = 100 centiseconds / 60 frames; multiply frames by 100 / 60 */
chars = rc_format_value_centiseconds(buffer, size, value * 10 / 6);
break;
case RC_FORMAT_SECONDS:
a = value / 60; /* minutes */
value -= a * 60;
chars = snprintf(buffer, size, "%02u:%02u", a, value);
chars = rc_format_value_seconds(buffer, size, value);
break;
case RC_FORMAT_CENTISECS:
a = value / 100; /* seconds */
value -= a * 100;
b = a / 60; /* minutes */
a -= b * 60;
chars = snprintf(buffer, size, "%02u:%02u.%02u", b, a, value);
chars = rc_format_value_centiseconds(buffer, size, value);
break;
case RC_FORMAT_SECONDS_AS_MINUTES:
chars = rc_format_value_minutes(buffer, size, value / 60);
break;
case RC_FORMAT_MINUTES:
chars = rc_format_value_minutes(buffer, size, value);
break;
case RC_FORMAT_SCORE:
chars = snprintf(buffer, size, "%06u Points", value);
chars = snprintf(buffer, size, "%06d", value);
break;
case RC_FORMAT_VALUE:
chars = snprintf(buffer, size, "%01u", value);
break;
case RC_FORMAT_OTHER:
default:
chars = snprintf(buffer, size, "%06u", value);
case RC_FORMAT_VALUE:
chars = snprintf(buffer, size, "%d", value);
break;
}

View File

@ -1,8 +1,7 @@
#ifndef INTERNAL_H
#define INTERNAL_H
#include <stddef.h>
#include <rcheevos.h>
#include "rcheevos.h"
#define RC_ALLOW_ALIGN(T) struct __align_ ## T { char ch; T t; };
RC_ALLOW_ALIGN(rc_condition_t)
@ -21,8 +20,6 @@ RC_ALLOW_ALIGN(rc_trigger_t)
RC_ALLOW_ALIGN(rc_value_t)
RC_ALLOW_ALIGN(char)
#define RC_TAG2(x,y) x ## y
#define RC_TAG(x,y) RC_TAG2(x,y)
#define RC_ALIGNOF(T) (sizeof(struct __align_ ## T) - sizeof(T))
#define RC_ALLOC(t, p) ((t*)rc_alloc((p)->buffer, &(p)->offset, sizeof(t), RC_ALIGNOF(t), &(p)->scratch))
@ -52,6 +49,21 @@ typedef struct {
}
rc_scratch_t;
typedef struct {
unsigned add_value; /* AddSource/SubSource */
unsigned add_hits; /* AddHits */
unsigned add_address; /* AddAddress */
rc_peek_t peek;
void* peek_userdata;
lua_State* L;
unsigned measured_value; /* Measured */
char was_reset; /* ResetIf triggered */
char has_hits; /* one of more hit counts is non-zero */
}
rc_eval_state_t;
typedef struct {
int offset;
@ -62,6 +74,8 @@ typedef struct {
rc_scratch_t scratch;
rc_memref_value_t** first_memref;
unsigned measured_target;
}
rc_parse_state_t;
@ -72,26 +86,28 @@ void rc_destroy_parse_state(rc_parse_state_t* parse);
void* rc_alloc(void* pointer, int* offset, int size, int alignment, rc_scratch_t* scratch);
char* rc_alloc_str(rc_parse_state_t* parse, const char* text, int length);
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd);
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect);
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud);
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state);
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
int rc_test_condset(rc_condset_t* self, int* reset, rc_peek_t peek, void* ud, lua_State* L);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
void rc_reset_condset(rc_condset_t* self);
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse);
int rc_test_condition(rc_condition_t* self, unsigned add_buffer, rc_peek_t peek, void* ud, lua_State* L);
rc_condition_t* rc_parse_condition(const char** memaddr, rc_parse_state_t* parse, int is_indirect);
int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state);
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, rc_parse_state_t* parse);
unsigned rc_evaluate_operand(rc_operand_t* self, rc_peek_t peek, void* ud, lua_State* L);
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse);
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state);
rc_term_t* rc_parse_term(const char** memaddr, rc_parse_state_t* parse);
unsigned rc_evaluate_term(rc_term_t* self, rc_peek_t peek, void* ud, lua_State* L);
rc_term_t* rc_parse_term(const char** memaddr, int is_indirect, rc_parse_state_t* parse);
int rc_evaluate_term(rc_term_t* self, rc_eval_state_t* eval_state);
rc_expression_t* rc_parse_expression(const char** memaddr, rc_parse_state_t* parse);
unsigned rc_evaluate_expression(rc_expression_t* self, rc_peek_t peek, void* ud, lua_State* L);
int rc_evaluate_expression(rc_expression_t* self, rc_eval_state_t* eval_state);
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse);

View File

@ -162,7 +162,7 @@ rc_lboard_t* rc_parse_lboard(void* buffer, const char* memaddr, lua_State* L, in
return parse.offset >= 0 ? self : 0;
}
int rc_evaluate_lboard(rc_lboard_t* self, unsigned* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
int rc_evaluate_lboard(rc_lboard_t* self, int* value, rc_peek_t peek, void* peek_ud, lua_State* L) {
int start_ok, cancel_ok, submit_ok;
int action = -1;
@ -217,8 +217,11 @@ int rc_evaluate_lboard(rc_lboard_t* self, unsigned* value, rc_peek_t peek, void*
/* Calculate the value */
switch (action) {
case RC_LBOARD_ACTIVE: /* fall through */
case RC_LBOARD_STARTED:
if (self->value.conditions)
rc_reset_condset(self->value.conditions);
/* fall through */
case RC_LBOARD_ACTIVE:
*value = rc_evaluate_value(self->progress != 0 ? self->progress : &self->value, peek, peek_ud, L);
break;

View File

@ -3,14 +3,21 @@
#include <stdlib.h> /* malloc/realloc */
#include <string.h> /* memcpy */
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd) {
rc_memref_value_t** next_memref_value;
rc_memref_value_t* memref_value;
#define MEMREF_PLACEHOLDER_ADDRESS 0xFFFFFFFF
static rc_memref_value_t* rc_alloc_memref_value_sizing_mode(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
rc_memref_t* memref;
int i;
if (!parse->first_memref) {
/* sizing mode - have to track unique address/size/bcd combinations */
/* indirect address always creates two new entries; don't bother tracking them */
if (is_indirect) {
RC_ALLOC(rc_memref_value_t, parse);
return RC_ALLOC(rc_memref_value_t, parse);
}
memref = NULL;
/* have to track unique address/size/bcd combinations - use scratch.memref for sizing mode */
for (i = 0; i < parse->scratch.memref_count; ++i) {
memref = &parse->scratch.memref[i];
if (memref->address == address && memref->size == size && memref->is_bcd == is_bcd) {
@ -18,7 +25,7 @@ rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned addre
}
}
/* resize unique tracking buffer if necessary */
/* no match found - resize unique tracking buffer if necessary */
if (parse->scratch.memref_count == parse->scratch.memref_size) {
if (parse->scratch.memref == parse->scratch.memref_buffer) {
parse->scratch.memref_size += 16;
@ -51,27 +58,46 @@ rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned addre
memref->address = address;
memref->size = size;
memref->is_bcd = is_bcd;
memref->is_indirect = is_indirect;
}
/* allocate memory but don't actually populate, as it might overwrite the self object referencing the rc_memref_value_t */
return RC_ALLOC(rc_memref_value_t, parse);
}
}
/* construction mode - find or create the appropriate rc_memref_value_t */
static rc_memref_value_t* rc_alloc_memref_value_constuct_mode(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
rc_memref_value_t** next_memref_value;
rc_memref_value_t* memref_value;
rc_memref_value_t* indirect_memref_value;
if (!is_indirect) {
/* attempt to find an existing rc_memref_value_t */
next_memref_value = parse->first_memref;
while (*next_memref_value) {
memref_value = *next_memref_value;
if (memref_value->memref.address == address && memref_value->memref.size == size && memref_value->memref.is_bcd == is_bcd) {
if (!memref_value->memref.is_indirect && memref_value->memref.address == address &&
memref_value->memref.size == size && memref_value->memref.is_bcd == is_bcd) {
return memref_value;
}
next_memref_value = &memref_value->next;
}
}
else {
/* indirect address always creates two new entries - one for the original address, and one for
the indirect dereference - just skip ahead to the end of the list */
next_memref_value = parse->first_memref;
while (*next_memref_value) {
next_memref_value = &(*next_memref_value)->next;
}
}
/* no match found, create a new entry */
memref_value = RC_ALLOC(rc_memref_value_t, parse);
memref_value->memref.address = address;
memref_value->memref.size = size;
memref_value->memref.is_bcd = is_bcd;
memref_value->memref.is_indirect = is_indirect;
memref_value->value = 0;
memref_value->previous = 0;
memref_value->prior = 0;
@ -79,9 +105,31 @@ rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned addre
*next_memref_value = memref_value;
/* also create the indirect deference entry for indirect references */
if (is_indirect) {
indirect_memref_value = RC_ALLOC(rc_memref_value_t, parse);
indirect_memref_value->memref.address = MEMREF_PLACEHOLDER_ADDRESS;
indirect_memref_value->memref.size = size;
indirect_memref_value->memref.is_bcd = is_bcd;
indirect_memref_value->memref.is_indirect = 1;
indirect_memref_value->value = 0;
indirect_memref_value->previous = 0;
indirect_memref_value->prior = 0;
indirect_memref_value->next = 0;
memref_value->next = indirect_memref_value;
}
return memref_value;
}
rc_memref_value_t* rc_alloc_memref_value(rc_parse_state_t* parse, unsigned address, char size, char is_bcd, char is_indirect) {
if (!parse->first_memref)
return rc_alloc_memref_value_sizing_mode(parse, address, size, is_bcd, is_indirect);
return rc_alloc_memref_value_constuct_mode(parse, address, size, is_bcd, is_indirect);
}
static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud) {
unsigned value;
@ -149,6 +197,7 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
break;
case RC_MEMSIZE_24_BITS:
/* peek 4 bytes - don't expect the caller to understand 24-bit numbers */
value = peek(self->address, 4, ud);
if (self->is_bcd) {
@ -158,6 +207,8 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
+ ((value >> 8) & 0x0f) * 100
+ ((value >> 4) & 0x0f) * 10
+ ((value >> 0) & 0x0f) * 1;
} else {
value &= 0x00FFFFFF;
}
break;
@ -186,19 +237,45 @@ static unsigned rc_memref_get_value(rc_memref_t* self, rc_peek_t peek, void* ud)
return value;
}
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
while (memref) {
void rc_update_memref_value(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
memref->previous = memref->value;
memref->value = rc_memref_get_value(&memref->memref, peek, ud);
if (memref->value != memref->previous)
memref->prior = memref->previous;
}
void rc_update_memref_values(rc_memref_value_t* memref, rc_peek_t peek, void* ud) {
while (memref) {
if (memref->memref.address != MEMREF_PLACEHOLDER_ADDRESS)
rc_update_memref_value(memref, peek, ud);
memref = memref->next;
}
}
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs)
{
void rc_init_parse_state_memrefs(rc_parse_state_t* parse, rc_memref_value_t** memrefs) {
parse->first_memref = memrefs;
*memrefs = 0;
}
rc_memref_value_t* rc_get_indirect_memref(rc_memref_value_t* memref, rc_eval_state_t* eval_state) {
unsigned new_address;
if (eval_state->add_address == 0)
return memref;
if (!memref->memref.is_indirect)
return memref;
new_address = memref->memref.address + eval_state->add_address;
/* an extra rc_memref_value_t is allocated for offset calculations */
memref = memref->next;
/* if the adjusted address has changed, update the record */
if (memref->memref.address != new_address) {
memref->memref.address = new_address;
rc_update_memref_value(memref, eval_state->peek, eval_state->peek_userdata);
}
return memref;
}

View File

@ -66,7 +66,7 @@ static int rc_parse_operand_lua(rc_operand_t* self, const char** memaddr, rc_par
return RC_OK;
}
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) {
static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse, int is_indirect) {
const char* aux = *memaddr;
char* end;
unsigned long address;
@ -135,7 +135,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
address = 0xffffffffU;
}
self->value.memref = rc_alloc_memref_value(parse, (unsigned)address, size, is_bcd);
self->value.memref = rc_alloc_memref_value(parse, address, size, is_bcd, is_indirect);
if (parse->offset < 0)
return parse->offset;
@ -143,7 +143,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
return RC_OK;
}
static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) {
static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
const char* aux = *memaddr;
char* end;
int ret;
@ -151,6 +151,11 @@ static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, rc
switch (*aux) {
case 'h': case 'H':
if (aux[2] == 'x' || aux[2] == 'X') {
/* H0x1234 is a typo - either H1234 or 0xH1234 was probably meant */
return RC_INVALID_CONST_OPERAND;
}
value = strtoul(++aux, &end, 16);
if (end == aux) {
@ -171,7 +176,7 @@ static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, rc
if (aux[1] == 'x' || aux[1] == 'X') {
/* fall through */
default:
ret = rc_parse_operand_memory(self, &aux, parse);
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
if (ret < 0) {
return ret;
@ -214,11 +219,12 @@ static int rc_parse_operand_trigger(rc_operand_t* self, const char** memaddr, rc
return RC_OK;
}
static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse) {
static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
const char* aux = *memaddr;
char* end;
int ret;
unsigned long value;
long svalue;
switch (*aux) {
case 'h': case 'H':
@ -239,18 +245,18 @@ static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, rc_pa
break;
case 'v': case 'V':
value = strtoul(++aux, &end, 10);
svalue = strtol(++aux, &end, 10);
if (end == aux) {
return RC_INVALID_CONST_OPERAND;
}
if (value > 0xffffffffU) {
value = 0xffffffffU;
if (svalue > 0xffffffffU) {
svalue = 0xffffffffU;
}
self->type = RC_OPERAND_CONST;
self->value.num = (unsigned)value;
self->value.num = (unsigned)svalue;
aux = end;
break;
@ -259,7 +265,7 @@ static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, rc_pa
if (aux[1] == 'x' || aux[1] == 'X') {
/* fall through */
default:
ret = rc_parse_operand_memory(self, &aux, parse);
ret = rc_parse_operand_memory(self, &aux, parse, is_indirect);
if (ret < 0) {
return ret;
@ -303,12 +309,12 @@ static int rc_parse_operand_term(rc_operand_t* self, const char** memaddr, rc_pa
return RC_OK;
}
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, rc_parse_state_t* parse) {
int rc_parse_operand(rc_operand_t* self, const char** memaddr, int is_trigger, int is_indirect, rc_parse_state_t* parse) {
if (is_trigger) {
return rc_parse_operand_trigger(self, memaddr, parse);
return rc_parse_operand_trigger(self, memaddr, is_indirect, parse);
}
else {
return rc_parse_operand_term(self, memaddr, parse);
return rc_parse_operand_term(self, memaddr, is_indirect, parse);
}
}
@ -333,7 +339,7 @@ static int rc_luapeek(lua_State* L) {
#endif /* RC_DISABLE_LUA */
unsigned rc_evaluate_operand(rc_operand_t* self, rc_peek_t peek, void* ud, lua_State* L) {
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
#ifndef RC_DISABLE_LUA
rc_luapeek_t luapeek;
#endif /* RC_DISABLE_LUA */
@ -352,25 +358,25 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_peek_t peek, void* ud, lua_S
case RC_OPERAND_LUA:
#ifndef RC_DISABLE_LUA
if (L != 0) {
lua_rawgeti(L, LUA_REGISTRYINDEX, self->value.luafunc);
lua_pushcfunction(L, rc_luapeek);
if (eval_state->L != 0) {
lua_rawgeti(eval_state->L, LUA_REGISTRYINDEX, self->value.luafunc);
lua_pushcfunction(eval_state->L, rc_luapeek);
luapeek.peek = peek;
luapeek.ud = ud;
luapeek.peek = eval_state->peek;
luapeek.ud = eval_state->peek_userdata;
lua_pushlightuserdata(L, &luapeek);
lua_pushlightuserdata(eval_state->L, &luapeek);
if (lua_pcall(L, 2, 1, 0) == LUA_OK) {
if (lua_isboolean(L, -1)) {
value = lua_toboolean(L, -1);
if (lua_pcall(eval_state->L, 2, 1, 0) == LUA_OK) {
if (lua_isboolean(eval_state->L, -1)) {
value = lua_toboolean(eval_state->L, -1);
}
else {
value = (unsigned)lua_tonumber(L, -1);
value = (unsigned)lua_tonumber(eval_state->L, -1);
}
}
lua_pop(L, 1);
lua_pop(eval_state->L, 1);
}
#endif /* RC_DISABLE_LUA */
@ -378,15 +384,15 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_peek_t peek, void* ud, lua_S
break;
case RC_OPERAND_ADDRESS:
value = self->value.memref->value;
value = rc_get_indirect_memref(self->value.memref, eval_state)->value;
break;
case RC_OPERAND_DELTA:
value = self->value.memref->previous;
value = rc_get_indirect_memref(self->value.memref, eval_state)->previous;
break;
case RC_OPERAND_PRIOR:
value = self->value.memref->prior;
value = rc_get_indirect_memref(self->value.memref, eval_state)->prior;
break;
}

View File

@ -51,8 +51,10 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
const char* in;
char* out;
if (endline - line < 1)
if (endline - line < 1) {
parse->offset = RC_MISSING_DISPLAY_STRING;
return 0;
}
{
self = RC_ALLOC(rc_richpresence_display_t, parse);
@ -103,6 +105,11 @@ static rc_richpresence_display_t* rc_parse_richpresence_display_internal(const c
while (ptr < endline && *ptr != '(')
++ptr;
if (ptr == endline) {
parse->offset = RC_MISSING_VALUE;
return 0;
}
if (ptr > line) {
if (!parse->buffer) {
/* just calculating size, can't confirm lookup exists */
@ -179,6 +186,7 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
const char* line;
const char* endline;
const char* defaultlabel = 0;
char* endptr = 0;
unsigned key;
int chars;
@ -208,9 +216,14 @@ static const char* rc_parse_richpresence_lookup(rc_richpresence_lookup_t* lookup
}
if (number[0] == '0' && number[1] == 'x')
key = (unsigned)strtoul(&number[2], 0, 16);
key = strtoul(&number[2], &endptr, 16);
else
key = (unsigned)strtoul(&number[0], 0, 10);
key = strtoul(&number[0], &endptr, 10);
if (*endptr && !isspace(*endptr)) {
parse->offset = RC_INVALID_CONST_OPERAND;
return nextline;
}
item = RC_ALLOC(rc_richpresence_lookup_item_t, parse);
item->value = key;
@ -263,6 +276,8 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
nextlookup = &lookup->next;
nextline = rc_parse_richpresence_lookup(lookup, nextline, parse);
if (parse->offset < 0)
return;
} else if (strncmp(line, "Format:", 7) == 0) {
line += 7;
@ -316,6 +331,8 @@ void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script,
if (ptr < endline) {
*nextdisplay = rc_parse_richpresence_display_internal(ptr + 1, endline, parse, self);
if (parse->offset < 0)
return;
trigger = &((*nextdisplay)->trigger);
rc_parse_trigger_internal(trigger, &line, parse);
trigger->memrefs = 0;

View File

@ -1,6 +1,6 @@
#include "internal.h"
rc_term_t* rc_parse_term(const char** memaddr, rc_parse_state_t* parse) {
rc_term_t* rc_parse_term(const char** memaddr, int is_indirect, rc_parse_state_t* parse) {
rc_term_t* self;
const char* aux;
char size;
@ -10,7 +10,7 @@ rc_term_t* rc_parse_term(const char** memaddr, rc_parse_state_t* parse) {
self = RC_ALLOC(rc_term_t, parse);
self->invert = 0;
ret2 = rc_parse_operand(&self->operand1, &aux, 0, parse);
ret2 = rc_parse_operand(&self->operand1, &aux, 0, is_indirect, parse);
if (ret2 < 0) {
parse->offset = ret2;
@ -25,7 +25,7 @@ rc_term_t* rc_parse_term(const char** memaddr, rc_parse_state_t* parse) {
self->invert = 1;
}
ret2 = rc_parse_operand(&self->operand2, &aux, 0, parse);
ret2 = rc_parse_operand(&self->operand2, &aux, 0, is_indirect, parse);
if (ret2 < 0) {
parse->offset = ret2;
@ -88,12 +88,16 @@ rc_term_t* rc_parse_term(const char** memaddr, rc_parse_state_t* parse) {
return self;
}
unsigned rc_evaluate_term(rc_term_t* self, rc_peek_t peek, void* ud, lua_State* L) {
unsigned value = rc_evaluate_operand(&self->operand1, peek, ud, L);
int rc_evaluate_term(rc_term_t* self, rc_eval_state_t* eval_state) {
/* Operands are usually memory references and are always retrieved as unsigned. The floating
* point operand is signed, and will automatically make the result signed. Otherwise, multiply
* by the secondary operand (which is usually 1) and cast to signed.
*/
unsigned value = rc_evaluate_operand(&self->operand1, eval_state);
if (self->operand2.type != RC_OPERAND_FP) {
return value * (rc_evaluate_operand(&self->operand2, peek, ud, L) ^ self->invert);
return (int)(value * (rc_evaluate_operand(&self->operand2, eval_state) ^ self->invert));
}
return (unsigned)((double)value * self->operand2.value.dbl);
return (int)((double)value * self->operand2.value.dbl);
}

View File

@ -1,6 +1,7 @@
#include "internal.h"
#include <stddef.h>
#include <memory.h>
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse) {
rc_condset_t** next;
@ -35,6 +36,11 @@ void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_pars
*next = 0;
*memaddr = aux;
self->measured_value = 0;
self->measured_target = parse->measured_target;
self->state = RC_TRIGGER_STATE_WAITING;
self->has_hits = 0;
}
int rc_trigger_size(const char* memaddr) {
@ -63,36 +69,7 @@ rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L,
return parse.offset >= 0 ? self : 0;
}
int rc_test_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) {
int ret, reset;
rc_condset_t* condset;
rc_update_memref_values(self->memrefs, peek, ud);
reset = 0;
ret = self->requirement != 0 ? rc_test_condset(self->requirement, &reset, peek, ud, L) : 1;
condset = self->alternative;
if (condset) {
int sub = 0;
do {
sub |= rc_test_condset(condset, &reset, peek, ud, L);
condset = condset->next;
}
while (condset != 0);
ret &= sub && !reset;
}
if (reset) {
rc_reset_trigger(self);
}
return ret;
}
void rc_reset_trigger(rc_trigger_t* self) {
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
rc_condset_t* condset;
if (self->requirement != 0) {
@ -106,3 +83,102 @@ void rc_reset_trigger(rc_trigger_t* self) {
condset = condset->next;
}
}
int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) {
rc_eval_state_t eval_state;
rc_condset_t* condset;
int ret;
char is_paused;
/* previously triggered, do nothing - return INACTIVE so caller doesn't report a repeated trigger */
if (self->state == RC_TRIGGER_STATE_TRIGGERED)
return RC_TRIGGER_STATE_INACTIVE;
rc_update_memref_values(self->memrefs, peek, ud);
/* not yet active, only update the memrefs - so deltas are corrent when it becomes active */
if (self->state == RC_TRIGGER_STATE_INACTIVE)
return RC_TRIGGER_STATE_INACTIVE;
/* process the trigger */
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
eval_state.peek_userdata = ud;
eval_state.L = L;
ret = self->requirement != 0 ? rc_test_condset(self->requirement, &eval_state) : 1;
condset = self->alternative;
if (condset) {
int sub = 0;
do {
sub |= rc_test_condset(condset, &eval_state);
condset = condset->next;
}
while (condset != 0);
ret &= sub;
}
self->measured_value = eval_state.measured_value;
/* if the state is WAITING and the trigger is ready to fire, ignore it and reset the hit counts */
/* otherwise, if the state is WAITING, proceed to activating the trigger */
if (self->state == RC_TRIGGER_STATE_WAITING && ret) {
rc_reset_trigger(self);
self->has_hits = 0;
return RC_TRIGGER_STATE_WAITING;
}
if (eval_state.was_reset) {
/* if any ResetIf condition was true, reset the hit counts */
rc_reset_trigger_hitcounts(self);
/* if there were hit counts to clear, return RESET, but don't change the state */
if (self->has_hits) {
self->has_hits = 0;
return RC_TRIGGER_STATE_RESET;
}
/* any hits that were tallied were just reset */
eval_state.has_hits = 0;
}
else if (ret) {
/* trigger was triggered */
self->state = RC_TRIGGER_STATE_TRIGGERED;
return RC_TRIGGER_STATE_TRIGGERED;
}
/* did not trigger this frame - update the information we'll need for next time */
self->has_hits = eval_state.has_hits;
/* check to see if the trigger is paused */
is_paused = (self->requirement != NULL) ? self->requirement->is_paused : 0;
if (!is_paused) {
/* if the core is not paused, all alts must be paused to count as a paused trigger */
is_paused = (self->alternative != NULL);
for (condset = self->alternative; condset != NULL; condset = condset->next) {
if (!condset->is_paused) {
is_paused = 0;
break;
}
}
}
self->state = is_paused ? RC_TRIGGER_STATE_PAUSED : RC_TRIGGER_STATE_ACTIVE;
return self->state;
}
int rc_test_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) {
/* for backwards compatibilty, rc_test_trigger always assumes the achievement is active */
self->state = RC_TRIGGER_STATE_ACTIVE;
return (rc_evaluate_trigger(self, peek, ud, L) == RC_TRIGGER_STATE_TRIGGERED);
}
void rc_reset_trigger(rc_trigger_t* self) {
rc_reset_trigger_hitcounts(self);
self->state = RC_TRIGGER_STATE_WAITING;
}

View File

@ -1,8 +1,88 @@
#include "internal.h"
#include <memory.h>
static void rc_parse_cond_value(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
rc_condition_t** next;
int has_measured;
int in_add_address;
has_measured = 0;
in_add_address = 0;
self->expressions = 0;
/* this largely duplicates rc_parse_condset, but we cannot call it directly, as we need to check the
* type of each condition as we go */
self->conditions = RC_ALLOC(rc_condset_t, parse);
self->conditions->next = 0;
self->conditions->has_pause = 0;
next = &self->conditions->conditions;
for (;;) {
*next = rc_parse_condition(memaddr, parse, in_add_address);
if (parse->offset < 0) {
return;
}
in_add_address = (*next)->type == RC_CONDITION_ADD_ADDRESS;
switch ((*next)->type) {
case RC_CONDITION_ADD_HITS:
case RC_CONDITION_ADD_SOURCE:
case RC_CONDITION_SUB_SOURCE:
case RC_CONDITION_AND_NEXT:
case RC_CONDITION_ADD_ADDRESS:
/* combining flags are allowed */
break;
case RC_CONDITION_RESET_IF:
/* ResetIf is allowed (primarily for rich presense - leaderboard will typically cancel instead of resetting) */
break;
case RC_CONDITION_MEASURED:
if (has_measured) {
parse->offset = RC_MULTIPLE_MEASURED;
return;
}
has_measured = 1;
if ((*next)->required_hits == 0 && (*next)->oper != RC_CONDITION_NONE)
(*next)->required_hits = (unsigned)-1;
break;
default:
/* non-combinding flags and PauseIf are not allowed */
parse->offset = RC_INVALID_VALUE_FLAG;
return;
}
(*next)->pause = 0;
next = &(*next)->next;
if (**memaddr != '_') {
break;
}
(*memaddr)++;
}
*next = 0;
if (!has_measured) {
parse->offset = RC_MISSING_VALUE_MEASURED;
}
}
void rc_parse_value_internal(rc_value_t* self, const char** memaddr, rc_parse_state_t* parse) {
rc_expression_t** next;
/* if it starts with a condition flag (M: A: B: C:), parse the conditions */
if ((*memaddr)[1] == ':') {
rc_parse_cond_value(self, memaddr, parse);
return;
}
self->conditions = 0;
next = &self->expressions;
for (;;) {
@ -50,17 +130,15 @@ rc_value_t* rc_parse_value(void* buffer, const char* memaddr, lua_State* L, int
return parse.offset >= 0 ? self : 0;
}
unsigned rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
static int rc_evaluate_expr_value(rc_value_t* self, rc_eval_state_t* eval_state) {
rc_expression_t* exp;
unsigned value, max;
rc_update_memref_values(self->memrefs, peek, ud);
int value, max;
exp = self->expressions;
max = rc_evaluate_expression(exp, peek, ud, L);
max = rc_evaluate_expression(exp, eval_state);
for (exp = exp->next; exp != 0; exp = exp->next) {
value = rc_evaluate_expression(exp, peek, ud, L);
value = rc_evaluate_expression(exp, eval_state);
if (value > max) {
max = value;
@ -69,3 +147,20 @@ unsigned rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State
return max;
}
int rc_evaluate_value(rc_value_t* self, rc_peek_t peek, void* ud, lua_State* L) {
rc_eval_state_t eval_state;
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
eval_state.peek_userdata = ud;
eval_state.L = L;
rc_update_memref_values(self->memrefs, peek, ud);
if (self->expressions) {
return rc_evaluate_expr_value(self, &eval_state);
}
rc_test_condset(self->conditions, &eval_state);
return (int)eval_state.measured_value;
}

View File

@ -69,7 +69,7 @@ int rc_url_award_cheevo(char* buffer, size_t size, const char* user_name, const
return (size_t)written >= size ? -1 : 0;
}
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, unsigned value, unsigned char hash[16]) {
int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const char* login_token, unsigned lboard_id, int value, unsigned char hash[16]) {
char urle_user_name[64];
char urle_login_token[64];
int written;
@ -85,7 +85,7 @@ int rc_url_submit_lboard(char* buffer, size_t size, const char* user_name, const
written = snprintf(
buffer,
size,
"http://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%u&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
"http://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
urle_user_name,
urle_login_token,
lboard_id,