diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index 0b60a0096f..a472781797 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -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 diff --git a/deps/rcheevos/include/rc_runtime.h b/deps/rcheevos/include/rc_runtime.h index 56fa62312d..d6f913ca2f 100644 --- a/deps/rcheevos/include/rc_runtime.h +++ b/deps/rcheevos/include/rc_runtime.h @@ -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); diff --git a/deps/rcheevos/src/rcheevos/condition.c b/deps/rcheevos/src/rcheevos/condition.c index cdf79d9ab2..a0f8ffec5e 100644 --- a/deps/rcheevos/src/rcheevos/condition.c +++ b/deps/rcheevos/src/rcheevos/condition.c @@ -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: diff --git a/deps/rcheevos/src/rcheevos/consoleinfo.c b/deps/rcheevos/src/rcheevos/consoleinfo.c index 71eeec3b65..ad6a655a0d 100644 --- a/deps/rcheevos/src/rcheevos/consoleinfo.c +++ b/deps/rcheevos/src/rcheevos/consoleinfo.c @@ -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 }; diff --git a/deps/rcheevos/src/rcheevos/memref.c b/deps/rcheevos/src/rcheevos/memref.c index e6bdd331df..398ef474d7 100644 --- a/deps/rcheevos/src/rcheevos/memref.c +++ b/deps/rcheevos/src/rcheevos/memref.c @@ -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; } diff --git a/deps/rcheevos/src/rcheevos/operand.c b/deps/rcheevos/src/rcheevos/operand.c index bfb151aea2..3a2c436a42 100644 --- a/deps/rcheevos/src/rcheevos/operand.c +++ b/deps/rcheevos/src/rcheevos/operand.c @@ -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) diff --git a/deps/rcheevos/src/rcheevos/rc_internal.h b/deps/rcheevos/src/rcheevos/rc_internal.h index 98ee2c7789..dbe99a9d23 100644 --- a/deps/rcheevos/src/rcheevos/rc_internal.h +++ b/deps/rcheevos/src/rcheevos/rc_internal.h @@ -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); diff --git a/deps/rcheevos/src/rcheevos/rc_libretro.c b/deps/rcheevos/src/rcheevos/rc_libretro.c index 229526dfec..4a76e3c2a4 100644 --- a/deps/rcheevos/src/rcheevos/rc_libretro.c +++ b/deps/rcheevos/src/rcheevos/rc_libretro.c @@ -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 ®ions->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 { diff --git a/deps/rcheevos/src/rcheevos/richpresence.c b/deps/rcheevos/src/rcheevos/richpresence.c index f738dc9bd3..fc1c8baf02 100644 --- a/deps/rcheevos/src/rcheevos/richpresence.c +++ b/deps/rcheevos/src/rcheevos/richpresence.c @@ -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; } } diff --git a/deps/rcheevos/src/rcheevos/runtime.c b/deps/rcheevos/src/rcheevos/runtime.c index 2e1536cf44..539d646086 100644 --- a/deps/rcheevos/src/rcheevos/runtime.c +++ b/deps/rcheevos/src/rcheevos/runtime.c @@ -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); + } + } + } +}