From 3fd9625f1c2a1edf808c9bf723d05ca40f4e4129 Mon Sep 17 00:00:00 2001 From: JordanTheToaster Date: Thu, 9 Oct 2025 15:49:39 +0100 Subject: [PATCH] 3rdparty: Update rcheevos v12.1.0 --- 3rdparty/rcheevos/CHANGELOG.md | 16 ++ 3rdparty/rcheevos/src/rc_client.c | 2 +- .../rcheevos/src/rc_client_raintegration.c | 2 + 3rdparty/rcheevos/src/rc_version.h | 2 +- 3rdparty/rcheevos/src/rcheevos/condset.c | 4 +- 3rdparty/rcheevos/src/rcheevos/rc_internal.h | 2 + 3rdparty/rcheevos/src/rcheevos/rc_validate.c | 156 ++++++++++++------ 3rdparty/rcheevos/src/rcheevos/richpresence.c | 9 +- 8 files changed, 139 insertions(+), 54 deletions(-) diff --git a/3rdparty/rcheevos/CHANGELOG.md b/3rdparty/rcheevos/CHANGELOG.md index c06399e59e..2962246877 100644 --- a/3rdparty/rcheevos/CHANGELOG.md +++ b/3rdparty/rcheevos/CHANGELOG.md @@ -1,3 +1,19 @@ +# v12.1.0 +* add rc_client_get_user_subset_summary +* add validation warning for using MeasuredIf without Measured +* add validation warning for using ResetIf without hit targets +* add rapi function for update_rich_presence +* add gap to RC_CONSOLE_WII memory map to make it easier to convert pointers +* improve range validation logic +* fix error Remembering float value +* fix MeasuredIf evaluation in rich presence +* fix parsing of code notes with addresses above 0x7FFFFFFF +* fix double evaluation of rich presence parameters +* fix validation of SubSource chain +* fix error if rc_client_allow_background_memory_reads called before calling rc_client_begin_load_raintegration +* fix memory corruption when mixing legacy and new-format macros in rich presence +* fix invalid pointer reference when iterator gets cloned + # v12.0.0 * rc_client changes * add RC_CLIENT_EVENT_SUBSET_COMPLETED event diff --git a/3rdparty/rcheevos/src/rc_client.c b/3rdparty/rcheevos/src/rc_client.c index 5562b236c8..9a5688872c 100644 --- a/3rdparty/rcheevos/src/rc_client.c +++ b/3rdparty/rcheevos/src/rc_client.c @@ -5880,7 +5880,7 @@ void rc_client_do_frame(rc_client_t* client) richpresence = client->game->runtime.richpresence; if (richpresence && richpresence->richpresence) - rc_update_richpresence(richpresence->richpresence, client->state.legacy_peek, client, NULL); + rc_update_richpresence_internal(richpresence->richpresence, client->state.legacy_peek, client); rc_mutex_unlock(&client->state.mutex); diff --git a/3rdparty/rcheevos/src/rc_client_raintegration.c b/3rdparty/rcheevos/src/rc_client_raintegration.c index 8134bff2fd..e89d3de168 100644 --- a/3rdparty/rcheevos/src/rc_client_raintegration.c +++ b/3rdparty/rcheevos/src/rc_client_raintegration.c @@ -223,6 +223,8 @@ static void rc_client_init_raintegration(rc_client_t* client, external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client)); if (external_client->set_spectator_mode_enabled) external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client)); + if (external_client->set_allow_background_memory_reads) + external_client->set_allow_background_memory_reads(client->state.allow_background_memory_reads); /* attach the external client and call the callback */ client->state.external_client = external_client; diff --git a/3rdparty/rcheevos/src/rc_version.h b/3rdparty/rcheevos/src/rc_version.h index bc8add3017..18b91284de 100644 --- a/3rdparty/rcheevos/src/rc_version.h +++ b/3rdparty/rcheevos/src/rc_version.h @@ -8,7 +8,7 @@ RC_BEGIN_C_DECLS #define RCHEEVOS_VERSION_MAJOR 12 -#define RCHEEVOS_VERSION_MINOR 0 +#define RCHEEVOS_VERSION_MINOR 1 #define RCHEEVOS_VERSION_PATCH 0 #define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch) diff --git a/3rdparty/rcheevos/src/rcheevos/condset.c b/3rdparty/rcheevos/src/rcheevos/condset.c index 21bab51ef4..38507ffcd4 100644 --- a/3rdparty/rcheevos/src/rcheevos/condset.c +++ b/3rdparty/rcheevos/src/rcheevos/condset.c @@ -607,8 +607,8 @@ static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state); } -static void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, - rc_eval_state_t* eval_state, int can_short_circuit) { +void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, + rc_eval_state_t* eval_state, int can_short_circuit) { const rc_condition_t* condition_end = condition + num_conditions; for (; condition < condition_end; ++condition) { switch (condition->type) { diff --git a/3rdparty/rcheevos/src/rcheevos/rc_internal.h b/3rdparty/rcheevos/src/rcheevos/rc_internal.h index 7c013d9dc1..5432e0e4cd 100644 --- a/3rdparty/rcheevos/src/rcheevos/rc_internal.h +++ b/3rdparty/rcheevos/src/rcheevos/rc_internal.h @@ -311,6 +311,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse); 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_condset_get_conditions(rc_condset_t* self); +void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, rc_eval_state_t* eval_state, int can_short_circuit); enum { RC_PROCESSING_COMPARE_DEFAULT = 0, @@ -379,6 +380,7 @@ int rc_lboard_state_active(int state); void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse); rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self); void rc_reset_richpresence_triggers(rc_richpresence_t* self); +void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud); int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address); int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id); diff --git a/3rdparty/rcheevos/src/rcheevos/rc_validate.c b/3rdparty/rcheevos/src/rcheevos/rc_validate.c index 7b7f085aaa..c648f24e9a 100644 --- a/3rdparty/rcheevos/src/rcheevos/rc_validate.c +++ b/3rdparty/rcheevos/src/rcheevos/rc_validate.c @@ -133,79 +133,93 @@ static uint32_t rc_max_value(const rc_operand_t* operand) } } -static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* operand) +static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper, uint32_t oper_min_val, uint32_t oper_max_val) { switch (oper) { case RC_OPERATOR_MULT: { - unsigned long long scaled = ((unsigned long long)value) * rc_max_value(operand); - if (scaled > 0xFFFFFFFF) - return 0xFFFFFFFF; + unsigned long long scaled = ((unsigned long long)*min_val) * oper_min_val; + *min_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled; - return (uint32_t)scaled; + scaled = ((unsigned long long)*max_val) * oper_max_val; + *max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled; + break; } case RC_OPERATOR_DIV: - { - const uint32_t min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1; - return value / min_val; - } + *min_val = (oper_max_val == 0) ? *min_val : (*min_val / oper_max_val); + *max_val = (oper_min_val == 0) ? *max_val : (*max_val / oper_min_val); + break; case RC_OPERATOR_AND: - return rc_max_value(operand); + *min_val = 0; + *max_val &= oper_max_val; + break; case RC_OPERATOR_XOR: - return value | rc_max_value(operand); + *min_val = 0; + *max_val |= oper_max_val; + break; case RC_OPERATOR_MOD: - { - const uint32_t divisor = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1; - return (divisor >= value) ? (divisor - 1) : value; - } + *min_val = 0; + *max_val = (*max_val >= oper_max_val) ? oper_max_val - 1 : *max_val; + break; case RC_OPERATOR_ADD: - { - unsigned long scaled = ((unsigned long)value) + rc_max_value(operand); - if (scaled > 0xFFFFFFFF) - return 0xFFFFFFFF; + if (*min_val > *max_val) { /* underflow occurred */ + *max_val += oper_max_val; + } + else { + unsigned long scaled = ((unsigned long)*max_val) + oper_max_val; + *max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled; + } - return (uint32_t)scaled; - } + *min_val += oper_min_val; + break; case RC_OPERATOR_SUB: - { - const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0; - if (value >= op_max) - return value - op_max; - - return 0xFFFFFFFF; - } + *min_val -= oper_max_val; + *max_val -= oper_min_val; + break; case RC_OPERATOR_SUB_PARENT: { - const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand); - if (op_max > value) - return op_max - value; - - return 0xFFFFFFFF; + uint32_t temp = oper_min_val - *max_val; + *max_val = oper_max_val - *min_val; + *min_val = temp; + break; } default: - return value; + break; } } -static uint32_t rc_max_chain_value(const rc_operand_t* operand) +static void rc_chain_get_value_range(const rc_operand_t* operand, uint32_t* min_val, uint32_t* max_val) { if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) { const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref; if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) { - const uint32_t op_max = rc_max_chain_value(&modified_memref->parent); - return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier); + if (modified_memref->modifier_type == RC_OPERATOR_DIV && + rc_operand_is_memref(&modified_memref->modifier) && + rc_operands_are_equal(&modified_memref->modifier, &modified_memref->parent)) { + /* division by self can only return 0 or 1. */ + *min_val = 0; + *max_val = 1; + } + else { + uint32_t modifier_min_val, modifier_max_val; + rc_chain_get_value_range(&modified_memref->parent, min_val, max_val); + rc_chain_get_value_range(&modified_memref->modifier, &modifier_min_val, &modifier_max_val); + rc_combine_ranges(min_val, max_val, modified_memref->modifier_type, modifier_min_val, modifier_max_val); + } + return; } } - return rc_max_value(operand); + *min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0; + *max_val = rc_max_value(operand); } static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition) @@ -291,7 +305,7 @@ static int rc_validate_range(uint32_t min_val, uint32_t max_val, char oper, uint return 1; } -static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address) +static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address, int has_hits) { const rc_condition_t* cond; char buffer[128]; @@ -299,6 +313,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result int in_add_hits = 0; int in_add_address = 0; int is_combining = 0; + int has_measured = 0; + int measuredif_index = -1; if (!condset) { *result = '\0'; @@ -383,6 +399,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result is_combining = 0; break; } + if (!has_hits) { + snprintf(result, result_size, "Condition %d: No captured hits to reset", index); + return 0; + } if (cond->required_hits == 1) { snprintf(result, result_size, "Condition %d: Hit target of 1 is redundant on ResetIf", index); return 0; @@ -398,6 +418,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result in_add_hits = 0; } + has_measured |= (cond->type == RC_CONDITION_MEASURED); + if (cond->type == RC_CONDITION_MEASURED_IF && measuredif_index == -1) + measuredif_index = index; + is_combining = 0; break; } @@ -424,10 +448,16 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index); const rc_operand_t* operand2 = &cond->operand2; uint8_t oper = cond->oper; - uint32_t max = rc_max_chain_value(operand1); + uint32_t min, max; uint32_t max_val = rc_max_value(operand2); uint32_t min_val; + rc_chain_get_value_range(operand1, &min, &max); + if (min > max) { /* underflow */ + min = 0; + max = 0xFFFFFFFF; + } + if (!is_memref1) { /* pretend constant was on right side */ operand2 = operand1; @@ -477,6 +507,7 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result break; } + /* min_val and max_val are the range allowed by operand2. max is the upper value from operand1. */ if (!rc_validate_range(min_val, max_val, oper, max, result + prefix_length, result_size - prefix_length)) return 0; } @@ -487,19 +518,48 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result return 0; } + if (measuredif_index != -1 && !has_measured) { + snprintf(result, result_size, "Condition %d: MeasuredIf without Measured", measuredif_index); + return 0; + } + *result = '\0'; return 1; } +static int rc_condset_has_hittargets(const rc_condset_t* condset) +{ + if (condset->num_hittarget_conditions > 0) + return 1; + + /* pause and reset conditions may have hittargets and won't be classified as hittarget conditions. + * measured conditions may also have hittargets. + */ + if (condset->num_pause_conditions || condset->num_reset_conditions || condset->num_measured_conditions) { + const rc_condition_t* condition = rc_condset_get_conditions((rc_condset_t*)condset); + /* ASSERT: don't need to add num_hittarget_conditions because it must be 0 per earlier check */ + const rc_condition_t* stop = condition + condset->num_pause_conditions + + condset->num_reset_conditions + condset->num_measured_conditions; + for (; condition < stop; ++condition) { + if (condition->required_hits) + return 1; + } + } + + return 0; +} + int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t max_address) { - return rc_validate_condset_internal(condset, result, result_size, 0, max_address); + int has_hits = rc_condset_has_hittargets(condset); + return rc_validate_condset_internal(condset, result, result_size, 0, max_address, has_hits); } int rc_validate_condset_for_console(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id) { const uint32_t max_address = rc_console_max_address(console_id); - return rc_validate_condset_internal(condset, result, result_size, console_id, max_address); + int has_hits = rc_condset_has_hittargets(condset); + return rc_validate_condset_internal(condset, result, result_size, console_id, max_address, has_hits); } static int rc_validate_is_combining_condition(const rc_condition_t* condition) @@ -845,7 +905,7 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co switch (compare_condition->type) { case RC_CONDITION_PAUSE_IF: - if (conditions != compare_conditions) + if (conditions != compare_conditions) /* PauseIf only affects conditions in same group */ break; /* fallthrough */ case RC_CONDITION_RESET_IF: @@ -955,10 +1015,10 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result { const rc_condset_t* alt; int index; - int has_hits = (trigger->requirement && trigger->requirement->num_hittarget_conditions > 0); + int has_hits = trigger->requirement && rc_condset_has_hittargets(trigger->requirement); if (!has_hits) { for (alt = trigger->alternative; alt; alt = alt->next) { - if (alt->num_hittarget_conditions > 0) { + if (rc_condset_has_hittargets(alt)) { has_hits = 1; break; } @@ -966,14 +1026,14 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result } if (!trigger->alternative) { - if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address)) + if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address, has_hits)) return 0; return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, has_hits, "", "", result, result_size); } snprintf(result, result_size, "Core "); - if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address)) + if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address, has_hits)) return 0; /* compare core to itself */ @@ -984,7 +1044,7 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result for (alt = trigger->alternative; alt; alt = alt->next, ++index) { char altname[16]; const size_t prefix_length = snprintf(result, result_size, "Alt%d ", index); - if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address)) + if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address, has_hits)) return 0; /* compare alt to itself */ diff --git a/3rdparty/rcheevos/src/rcheevos/richpresence.c b/3rdparty/rcheevos/src/rcheevos/richpresence.c index 3bee5081d9..b2f85c69e1 100644 --- a/3rdparty/rcheevos/src/rcheevos/richpresence.c +++ b/3rdparty/rcheevos/src/rcheevos/richpresence.c @@ -723,14 +723,19 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self) { } void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, void* unused_L) { - rc_richpresence_display_t* display; + (void)unused_L; rc_update_richpresence_memrefs(richpresence, peek, peek_ud); rc_update_values(richpresence->values, peek, peek_ud); + rc_update_richpresence_internal(richpresence, peek, peek_ud); +} + +void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud) { + rc_richpresence_display_t* display; for (display = richpresence->first_display; display; display = display->next) { if (display->has_required_hits) - rc_test_trigger(&display->trigger, peek, peek_ud, unused_L); + rc_test_trigger(&display->trigger, peek, peek_ud, NULL); } }