update to rcheevos 10.1 (#12512)

This commit is contained in:
Jamiras 2021-06-10 09:20:00 -06:00 committed by GitHub
parent 0caeed6e1d
commit 5d4069cf8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 252 additions and 306 deletions

View File

@ -73,7 +73,7 @@
#include "../network/net_http_special.h"
#include "../tasks/tasks_internal.h"
#include "../deps/rcheevos/include/rc_runtime_types.h"
#include "../deps/rcheevos/include/rc_runtime.h"
#include "../deps/rcheevos/include/rc_url.h"
#include "../deps/rcheevos/include/rc_hash.h"
#include "../deps/rcheevos/src/rcheevos/rc_libretro.h"
@ -171,6 +171,7 @@ static void rcheevos_async_task_callback(
retro_task_t* task, void* task_data, void* user_data, const char* error);
static void rcheevos_async_submit_lboard(rcheevos_locals_t *locals,
rcheevos_async_io_request* request);
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals);
/*****************************************************************************
Supporting functions.
@ -351,48 +352,13 @@ static void rcheevos_log_post_url(
#endif
}
static bool rcheevos_condset_contains_memref(const rc_condset_t* condset,
const rc_memref_t* memref)
{
if (condset)
{
rc_condition_t* cond = NULL;
for (cond = condset->conditions; cond; cond = cond->next)
{
if ( cond->operand1.value.memref == memref
|| cond->operand2.value.memref == memref)
return true;
}
}
return false;
}
static bool rcheevos_trigger_contains_memref(const rc_trigger_t* trigger,
const rc_memref_t* memref)
{
rc_condset_t* condset;
if (!trigger)
return false;
if (rcheevos_condset_contains_memref(trigger->requirement, memref))
return true;
for (condset = trigger->alternative; condset; condset = condset->next)
{
if (rcheevos_condset_contains_memref(condset, memref))
return true;
}
return false;
}
static void rcheevos_achievement_disabled(rcheevos_racheevo_t* cheevo, unsigned address)
{
if (!cheevo)
return;
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement disabled (invalid address %06X): %s\n", address, cheevo->title);
CHEEVOS_ERR(RCHEEVOS_TAG "Achievement %u disabled (invalid address %06X): %s\n",
cheevo->id, address, cheevo->title);
CHEEVOS_FREE(cheevo->memaddr);
cheevo->memaddr = NULL;
}
@ -402,97 +368,12 @@ static void rcheevos_lboard_disabled(rcheevos_ralboard_t* lboard, unsigned addre
if (!lboard)
return;
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard disabled (invalid address %06X): %s\n", address, lboard->title);
CHEEVOS_ERR(RCHEEVOS_TAG "Leaderboard %u disabled (invalid address %06X): %s\n",
lboard->id, address, lboard->title);
CHEEVOS_FREE(lboard->mem);
lboard->mem = NULL;
}
static void rcheevos_invalidate_address(unsigned address)
{
unsigned i, count;
rcheevos_racheevo_t* cheevo = NULL;
rcheevos_ralboard_t* lboard = NULL;
/* Remove the invalid memref from the chain so we don't
* try to evaluate it in the future.
* It's still there, so anything referencing it will
* continue to fetch 0. */
rc_memref_t **last_memref = &rcheevos_locals.runtime.memrefs;
rc_memref_t *memref = *last_memref;
do
{
if (memref->address == address && !memref->value.is_indirect)
{
*last_memref = memref->next;
break;
}
last_memref = &memref->next;
memref = *last_memref;
} while(memref);
/* If the address is only used indirectly,
* don't disable anything dependent on it */
if (!memref)
return;
/* Disable any achievements dependent on the address */
for (i = 0; i < 2; ++i)
{
if (i == 0)
{
cheevo = rcheevos_locals.patchdata.core;
count = rcheevos_locals.patchdata.core_count;
}
else
{
cheevo = rcheevos_locals.patchdata.unofficial;
count = rcheevos_locals.patchdata.unofficial_count;
}
while (count--)
{
if (cheevo->memaddr)
{
const rc_trigger_t* trigger = rc_runtime_get_achievement(
&rcheevos_locals.runtime, cheevo->id);
if (trigger && rcheevos_trigger_contains_memref(trigger, memref))
{
rcheevos_achievement_disabled(cheevo, address);
rc_runtime_deactivate_achievement(&rcheevos_locals.runtime,
cheevo->id);
}
}
++cheevo;
}
}
/* disable any leaderboards dependent on the address */
lboard = rcheevos_locals.patchdata.lboards;
for (i = 0; i < rcheevos_locals.patchdata.lboard_count; ++i, ++lboard)
{
if (lboard->mem)
{
const rc_lboard_t* rc_lboard = rc_runtime_get_lboard(
&rcheevos_locals.runtime, lboard->id);
if ( rc_lboard &&
( rcheevos_trigger_contains_memref(&rc_lboard->start, memref) ||
rcheevos_trigger_contains_memref(&rc_lboard->cancel, memref) ||
rcheevos_trigger_contains_memref(&rc_lboard->submit, memref) ||
rcheevos_condset_contains_memref(rc_lboard->value.conditions,
memref))
)
{
rcheevos_lboard_disabled(lboard, address);
rc_runtime_deactivate_lboard(&rcheevos_locals.runtime, lboard->id);
}
}
}
}
static void rcheevos_handle_log_message(const char* message)
{
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", message);
@ -774,24 +655,6 @@ static void rcheevos_async_task_callback(
}
}
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
{
rc_memref_t* memref = locals->runtime.memrefs;
while (memref)
{
if (!memref->value.is_indirect)
{
uint8_t* data = rc_libretro_memory_find(&rcheevos_locals.memory,
memref->address);
if (!data)
rcheevos_invalidate_address(memref->address);
}
memref = memref->next;
}
}
static void rcheevos_activate_achievements(rcheevos_locals_t *locals,
rcheevos_racheevo_t* cheevo, unsigned count, unsigned flags)
{
@ -859,7 +722,9 @@ static int rcheevos_parse(rcheevos_locals_t *locals, const char* json)
if ( locals->patchdata.core_count == 0
&& locals->patchdata.unofficial_count == 0
&& locals->patchdata.lboard_count == 0)
&& locals->patchdata.lboard_count == 0
&& (!locals->patchdata.richpresence_script ||
!*locals->patchdata.richpresence_script))
{
rcheevos_free_patchdata(&locals->patchdata);
return 0;
@ -1121,7 +986,7 @@ static void rcheevos_lboard_submit(rcheevos_locals_t *locals,
char formatted_value[16];
/* Show the OSD message (regardless of notifications setting). */
rc_format_value(formatted_value, sizeof(formatted_value),
rc_runtime_format_lboard_value(formatted_value, sizeof(formatted_value),
value, lboard->format);
CHEEVOS_LOG(RCHEEVOS_TAG "Submitting %s for leaderboard %u\n",
@ -1191,7 +1056,7 @@ static void rcheevos_lboard_started(rcheevos_ralboard_t * lboard, int value,
#if defined(HAVE_GFX_WIDGETS)
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
{
rc_format_value(buffer, sizeof(buffer), value, lboard->format);
rc_runtime_format_lboard_value(buffer, sizeof(buffer), value, lboard->format);
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
}
#endif
@ -1222,7 +1087,7 @@ static void rcheevos_lboard_updated(rcheevos_ralboard_t* lboard, int value,
if (widgets_ready && rcheevos_locals.leaderboard_trackers)
{
char buffer[32];
rc_format_value(buffer, sizeof(buffer), value, lboard->format);
rc_runtime_format_lboard_value(buffer, sizeof(buffer), value, lboard->format);
gfx_widgets_set_leaderboard_display(lboard->id, buffer);
}
}
@ -1670,6 +1535,17 @@ static void rcheevos_runtime_event_handler(const rc_runtime_event_t* runtime_eve
}
}
static int rcheevos_runtime_address_validator(unsigned address)
{
return (rc_libretro_memory_find(&rcheevos_locals.memory, address) != NULL);
}
static void rcheevos_validate_memrefs(rcheevos_locals_t* locals)
{
rc_runtime_validate_addresses(&locals->runtime,
rcheevos_runtime_event_handler, rcheevos_runtime_address_validator);
}
/*****************************************************************************
Test all the achievements (call once per frame).
*****************************************************************************/
@ -1949,10 +1825,20 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
"This game has no achievements.",
0, 5 * 60, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
rcheevos_pause_hardcore();
if (rcheevos_locals.patchdata.richpresence_script &&
*rcheevos_locals.patchdata.richpresence_script)
{
rcheevos_locals.loaded = true;
}
else
{
rcheevos_pause_hardcore();
}
}
else
{
rcheevos_locals.loaded = true;
}
}
#if HAVE_REWIND

View File

@ -130,6 +130,9 @@ typedef void (*rc_runtime_event_handler_t)(const rc_runtime_event_t* runtime_eve
void rc_runtime_do_frame(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_peek_t peek, void* ud, lua_State* L);
void rc_runtime_reset(rc_runtime_t* runtime);
typedef int (*rc_runtime_validate_address_t)(unsigned address);
void rc_runtime_validate_addresses(rc_runtime_t* runtime, rc_runtime_event_handler_t event_handler, rc_runtime_validate_address_t validate_handler);
void rc_runtime_invalidate_address(rc_runtime_t* runtime, unsigned address);
int rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L);

View File

@ -239,10 +239,18 @@ int rc_evaluate_condition_value(rc_condition_t* self, rc_eval_state_t* eval_stat
switch (self->oper) {
case RC_OPERATOR_MULT:
if (self->operand2.type == RC_OPERAND_FP)
if (self->operand2.type == RC_OPERAND_FP) {
value = (int)((double)value * self->operand2.value.dbl);
else
}
else {
/* the c standard for unsigned multiplication is well defined as non-overflowing truncation
* to the type's size. this allows negative multiplication through twos-complements. i.e.
* 1 * -1 (0xFFFFFFFF) = 0xFFFFFFFF = -1
* 3 * -2 (0xFFFFFFFE) = 0x2FFFFFFFA & 0xFFFFFFFF = 0xFFFFFFFA = -6
* 10 * -5 (0xFFFFFFFB) = 0x9FFFFFFCE & 0xFFFFFFFF = 0xFFFFFFCE = -50
*/
value *= rc_evaluate_operand(&self->operand2, eval_state);
}
break;
case RC_OPERATOR_DIV:

View File

@ -338,10 +338,14 @@ static const rc_memory_region_t _rc_memory_regions_intellivision[] = {
static const rc_memory_regions_t rc_memory_regions_intellivision = { _rc_memory_regions_intellivision, 9 };
/* ===== Magnavox Odyssey 2 ===== */
/* https://sudonull.com/post/76885-Architecture-and-programming-Philips-Videopac-Magnavox-Odyssey-2 */
static const rc_memory_region_t _rc_memory_regions_magnavox_odyssey_2[] = {
{ 0x000000U, 0x00003FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
/* Internal and external RAMs are reachable using unique instructions.
* The real addresses provided are virtual and for mapping purposes only. */
{ 0x000000U, 0x00003FU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "Internal RAM" },
{ 0x000040U, 0x00013FU, 0x000040U, RC_MEMORY_TYPE_SYSTEM_RAM, "External RAM" }
};
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 1 };
static const rc_memory_regions_t rc_memory_regions_magnavox_odyssey_2 = { _rc_memory_regions_magnavox_odyssey_2, 2 };
/* ===== Master System ===== */
/* http://www.smspower.org/Development/MemoryMap */
@ -374,8 +378,9 @@ static const rc_memory_regions_t rc_memory_regions_msx = { _rc_memory_regions_ms
/* ===== Neo Geo Pocket ===== */
/* http://neopocott.emuunlim.com/docs/tech-11.txt */
static const rc_memory_region_t _rc_memory_regions_neo_geo_pocket[] = {
/* MednafenNGP exposes 16KB, but the doc suggests there's 24-32KB */
{ 0x000000U, 0x003FFFU, 0x000000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
/* The docs suggest there's Work RAM exposed from $0000-$6FFF, Sound RAM from $7000-$7FFF, and Video
* RAM from $8000-$BFFF, but both MednafenNGP and FBNeo only expose system RAM from $4000-$7FFF */
{ 0x000000U, 0x003FFFU, 0x004000U, RC_MEMORY_TYPE_SYSTEM_RAM, "System RAM" }
};
static const rc_memory_regions_t rc_memory_regions_neo_geo_pocket = { _rc_memory_regions_neo_geo_pocket, 1 };

View File

@ -95,6 +95,86 @@ int rc_parse_memref(const char** memaddr, char* size, unsigned* address) {
return RC_OK;
}
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
unsigned rc_transform_memref_value(unsigned value, char size)
{
switch (size)
{
case RC_MEMSIZE_BIT_0:
value = (value >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (value >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (value >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (value >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (value >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (value >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (value >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (value >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = value & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (value >> 4) & 0x0f;
break;
case RC_MEMSIZE_BITCOUNT:
value = rc_bits_set[(value & 0x0F)]
+ rc_bits_set[((value >> 4) & 0x0F)];
break;
default:
break;
}
return value;
}
char rc_memref_shared_size(char size)
{
switch (size) {
case RC_MEMSIZE_BIT_0:
case RC_MEMSIZE_BIT_1:
case RC_MEMSIZE_BIT_2:
case RC_MEMSIZE_BIT_3:
case RC_MEMSIZE_BIT_4:
case RC_MEMSIZE_BIT_5:
case RC_MEMSIZE_BIT_6:
case RC_MEMSIZE_BIT_7:
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
case RC_MEMSIZE_BITCOUNT:
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_transform_memref_value */
return RC_MEMSIZE_8_BITS;
default:
return size;
}
}
static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void* ud) {
unsigned value;
@ -103,46 +183,6 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
switch (size)
{
case RC_MEMSIZE_BIT_0:
value = (peek(address, 1, ud) >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (peek(address, 1, ud) >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (peek(address, 1, ud) >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (peek(address, 1, ud) >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (peek(address, 1, ud) >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (peek(address, 1, ud) >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (peek(address, 1, ud) >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (peek(address, 1, ud) >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = peek(address, 1, ud) & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (peek(address, 1, ud) >> 4) & 0x0f;
break;
case RC_MEMSIZE_8_BITS:
value = peek(address, 1, ud);
break;
@ -161,7 +201,15 @@ static unsigned rc_peek_value(unsigned address, char size, rc_peek_t peek, void*
break;
default:
value = 0;
if (rc_memref_shared_size(size) == RC_MEMSIZE_8_BITS)
{
value = peek(address, 1, ud);
value = rc_transform_memref_value(value, size);
}
else
{
value = 0;
}
break;
}

View File

@ -102,27 +102,7 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_
if (ret != RC_OK)
return ret;
switch (self->size) {
case RC_MEMSIZE_BIT_0:
case RC_MEMSIZE_BIT_1:
case RC_MEMSIZE_BIT_2:
case RC_MEMSIZE_BIT_3:
case RC_MEMSIZE_BIT_4:
case RC_MEMSIZE_BIT_5:
case RC_MEMSIZE_BIT_6:
case RC_MEMSIZE_BIT_7:
case RC_MEMSIZE_LOW:
case RC_MEMSIZE_HIGH:
case RC_MEMSIZE_BITCOUNT:
/* these can all share an 8-bit memref and just mask off the appropriate data in rc_evaluate_operand */
size = RC_MEMSIZE_8_BITS;
break;
default:
size = self->size;
break;
}
size = rc_memref_shared_size(self->size);
self->value.memref = rc_alloc_memref(parse, address, size, is_indirect);
if (parse->offset < 0)
return parse->offset;
@ -295,8 +275,6 @@ int rc_operand_is_memref(rc_operand_t* self) {
}
}
static const unsigned char rc_bits_set[16] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
#ifndef RC_DISABLE_LUA
rc_luapeek_t luapeek;
@ -348,56 +326,7 @@ unsigned rc_evaluate_operand(rc_operand_t* self, rc_eval_state_t* eval_state) {
}
/* step 2: mask off appropriate bits */
switch (self->size)
{
case RC_MEMSIZE_BIT_0:
value = (value >> 0) & 1;
break;
case RC_MEMSIZE_BIT_1:
value = (value >> 1) & 1;
break;
case RC_MEMSIZE_BIT_2:
value = (value >> 2) & 1;
break;
case RC_MEMSIZE_BIT_3:
value = (value >> 3) & 1;
break;
case RC_MEMSIZE_BIT_4:
value = (value >> 4) & 1;
break;
case RC_MEMSIZE_BIT_5:
value = (value >> 5) & 1;
break;
case RC_MEMSIZE_BIT_6:
value = (value >> 6) & 1;
break;
case RC_MEMSIZE_BIT_7:
value = (value >> 7) & 1;
break;
case RC_MEMSIZE_LOW:
value = value & 0x0f;
break;
case RC_MEMSIZE_HIGH:
value = (value >> 4) & 0x0f;
break;
case RC_MEMSIZE_BITCOUNT:
value = rc_bits_set[(value & 0x0F)]
+ rc_bits_set[((value >> 4) & 0x0F)];
break;
default:
break;
}
value = rc_transform_memref_value(value, self->size);
/* step 3: apply logic */
switch (self->type)

View File

@ -117,6 +117,8 @@ void rc_update_memref_values(rc_memref_t* memref, rc_peek_t peek, void* ud);
void rc_update_memref_value(rc_memref_value_t* memref, unsigned value);
unsigned rc_get_memref_value(rc_memref_t* memref, int operand_type, rc_eval_state_t* eval_state);
unsigned rc_get_memref_value_value(rc_memref_value_t* memref, int operand_type);
char rc_memref_shared_size(char size);
unsigned rc_transform_memref_value(unsigned value, char size);
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse);
int rc_trigger_state_active(int state);

View File

@ -131,7 +131,7 @@ static int rc_libretro_match_value(const char* val, const char* match) {
if (*match == ',') {
do {
const char* ptr = ++match;
int size;
size_t size;
while (*match && *match != ',')
++match;
@ -218,7 +218,7 @@ unsigned char* rc_libretro_memory_find(const rc_libretro_memory_regions_t* regio
return &regions->data[i][address];
}
address -= size;
address -= (unsigned)size;
}
return NULL;
@ -360,8 +360,8 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
break;
}
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X",
(unsigned)(desc - mmap->descriptors) + 1, (int)offset);
snprintf(description, sizeof(description), "descriptor %u, offset 0x%06X%s",
(unsigned)(desc - mmap->descriptors) + 1, (int)offset, desc->ptr ? "" : " [no pointer]");
if (desc->ptr) {
desc_start = (uint8_t*)desc->ptr + desc->offset;
@ -387,7 +387,7 @@ static void rc_libretro_memory_init_from_memory_map(rc_libretro_memory_regions_t
else {
rc_libretro_memory_register_region(regions, console_region->type, region_start, desc_size, description);
console_region_size -= desc_size;
real_address += desc_size;
real_address += (unsigned)desc_size;
}
}
else {

View File

@ -22,8 +22,9 @@ static rc_memref_value_t* rc_alloc_helper_variable_memref_value(const char* mema
if (rc_parse_memref(&end, &size, &address) == RC_OK) {
/* make sure the entire memaddr was consumed. if not, there's an operator and it's a comparison, not a memory reference */
if (end == &memaddr[memaddr_len]) {
/* just a memory reference, allocate it */
return &rc_alloc_memref(parse, address, size, 0)->value;
/* if it's not a derived size, we can reference the memref directly */
if (rc_memref_shared_size(size) == size)
return &rc_alloc_memref(parse, address, size, 0)->value;
}
}

View File

@ -491,23 +491,32 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
old_state = trigger->state;
new_state = rc_evaluate_trigger(trigger, peek, ud, L);
/* the trigger state doesn't actually change to RESET, RESET just serves as a notification.
* handle the notification, then look at the actual state */
if (new_state == RC_TRIGGER_STATE_RESET)
{
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
new_state = trigger->state;
}
/* if the state hasn't changed, there won't be any events raised */
if (new_state == old_state)
continue;
/* raise an UNPRIMED event when changing from UNPRIMED to anything else */
if (old_state == RC_TRIGGER_STATE_PRIMED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_UNPRIMED;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
}
/* raise events for each of the possible new states */
switch (new_state)
{
case RC_TRIGGER_STATE_RESET:
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_RESET;
runtime_event.id = self->triggers[i].id;
event_handler(&runtime_event);
break;
case RC_TRIGGER_STATE_TRIGGERED:
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_TRIGGERED;
runtime_event.id = self->triggers[i].id;
@ -527,6 +536,8 @@ void rc_runtime_do_frame(rc_runtime_t* self, rc_runtime_event_handler_t event_ha
break;
case RC_TRIGGER_STATE_ACTIVE:
/* only raise ACTIVATED event when transitioning from an inactive state.
* note that inactive in this case means active but cannot trigger. */
if (old_state == RC_TRIGGER_STATE_WAITING || old_state == RC_TRIGGER_STATE_PAUSED) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_ACTIVATED;
runtime_event.id = self->triggers[i].id;
@ -667,32 +678,8 @@ static int rc_trigger_contains_memref(const rc_trigger_t* trigger, const rc_memr
return 0;
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
static void rc_runtime_invalidate_memref(rc_runtime_t* self, rc_memref_t* memref) {
unsigned i;
rc_memref_t* memref;
rc_memref_t** last_memref;
if (!self->memrefs)
return;
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
last_memref = &self->memrefs;
memref = *last_memref;
do {
if (memref->address == address && !memref->value.is_indirect) {
*last_memref = memref->next;
break;
}
last_memref = &memref->next;
memref = *last_memref;
} while (memref);
/* if the address is only used indirectly, don't disable anything dependent on it */
if (!memref)
return;
/* disable any achievements dependent on the address */
for (i = 0; i < self->trigger_count; ++i) {
@ -726,3 +713,80 @@ void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
}
}
}
void rc_runtime_invalidate_address(rc_runtime_t* self, unsigned address) {
rc_memref_t** last_memref = &self->memrefs;
rc_memref_t* memref = self->memrefs;
while (memref) {
if (memref->address == address && !memref->value.is_indirect) {
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
*last_memref = memref->next;
rc_runtime_invalidate_memref(self, memref);
break;
}
last_memref = &memref->next;
memref = *last_memref;
}
}
void rc_runtime_validate_addresses(rc_runtime_t* self, rc_runtime_event_handler_t event_handler,
rc_runtime_validate_address_t validate_handler) {
rc_memref_t** last_memref = &self->memrefs;
rc_memref_t* memref = self->memrefs;
int num_invalid = 0;
while (memref) {
if (!memref->value.is_indirect && !validate_handler(memref->address)) {
/* remove the invalid memref from the chain so we don't try to evaluate it in the future.
* it's still there, so anything referencing it will continue to fetch 0.
*/
*last_memref = memref->next;
rc_runtime_invalidate_memref(self, memref);
++num_invalid;
}
else {
last_memref = &memref->next;
}
memref = *last_memref;
}
if (num_invalid) {
rc_runtime_event_t runtime_event;
int i;
for (i = self->trigger_count - 1; i >= 0; --i) {
rc_trigger_t* trigger = self->triggers[i].trigger;
if (trigger && self->triggers[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_ACHIEVEMENT_DISABLED;
runtime_event.id = self->triggers[i].id;
runtime_event.value = self->triggers[i].invalid_memref->address;
trigger->state = RC_TRIGGER_STATE_DISABLED;
self->triggers[i].invalid_memref = NULL;
event_handler(&runtime_event);
}
}
for (i = self->lboard_count - 1; i >= 0; --i) {
rc_lboard_t* lboard = self->lboards[i].lboard;
if (lboard && self->lboards[i].invalid_memref) {
runtime_event.type = RC_RUNTIME_EVENT_LBOARD_DISABLED;
runtime_event.id = self->lboards[i].id;
runtime_event.value = self->lboards[i].invalid_memref->address;
lboard->state = RC_LBOARD_STATE_DISABLED;
self->lboards[i].invalid_memref = NULL;
event_handler(&runtime_event);
}
}
}
}