From 75448944d7c97df879a25a5a60c58dbeee9a5478 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 5 Sep 2026 21:59:19 +1000 Subject: [PATCH] dep/rcheevos: Bump to e871ef5 --- dep/rcheevos/src/rapi/rc_api_common.c | 19 +++++++++++++++---- dep/rcheevos/src/rapi/rc_api_runtime.c | 12 +++++++----- dep/rcheevos/src/rc_util.c | 11 +++++++++++ dep/rcheevos/src/rcheevos/condset.c | 3 ++- dep/rcheevos/src/rcheevos/operand.c | 24 ++++++++++++++++++++---- dep/rcheevos/src/rcheevos/rc_validate.c | 4 +++- dep/rcheevos/src/rcheevos/richpresence.c | 2 +- dep/rcheevos/src/rcheevos/value.c | 8 +++++++- 8 files changed, 66 insertions(+), 17 deletions(-) diff --git a/dep/rcheevos/src/rapi/rc_api_common.c b/dep/rcheevos/src/rapi/rc_api_common.c index 36cf13573..3b3528605 100644 --- a/dep/rcheevos/src/rapi/rc_api_common.c +++ b/dep/rcheevos/src/rapi/rc_api_common.c @@ -710,10 +710,15 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel } *out = dst = (char*)rc_buffer_reserve(buffer, len - 1); /* -2 for quotes, +1 for null terminator */ + if (!dst) + return 0; - do { + while (src < field->value_end && *src != '\"') { if (*src == '\\') { ++src; + if (src >= field->value_end) + return 0; + if (*src == 'n') { /* newline */ ++src; @@ -731,7 +736,7 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel if (*src == 'u') { /* unicode character */ uint32_t ucs32_char; - if (src + 5 >= field->value_end) /* incomplete unicode character */ + if ((size_t)(field->value_end - src) < 6) /* incomplete unicode character */ return 0; ucs32_char = rc_json_decode_hex4(src + 1); @@ -739,7 +744,7 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel if (ucs32_char >= 0xD800 && ucs32_char < 0xE000) { /* surrogate lead - look for surrogate tail */ - if (ucs32_char < 0xDC00 && src[0] == '\\' && src[1] == 'u') { + if (ucs32_char < 0xDC00 && (size_t)(field->value_end - src) >= 6 && src[0] == '\\' && src[1] == 'u') { const uint32_t surrogate = rc_json_decode_hex4(src + 2); src += 6; @@ -770,10 +775,16 @@ int rc_json_get_string(const char** out, rc_buffer_t* buffer, const rc_json_fiel } *dst++ = *src++; - } while (*src != '\"'); + } + + if (src >= field->value_end) + return 0; } else { *out = dst = (char*)rc_buffer_reserve(buffer, len + 1); /* +1 for null terminator */ + if (!dst) + return 0; + memcpy(dst, src, len); dst += len; } diff --git a/dep/rcheevos/src/rapi/rc_api_runtime.c b/dep/rcheevos/src/rapi/rc_api_runtime.c index afecabbac..261741d55 100644 --- a/dep/rcheevos/src/rapi/rc_api_runtime.c +++ b/dep/rcheevos/src/rapi/rc_api_runtime.c @@ -140,7 +140,7 @@ static int rc_api_process_fetch_game_data_achievements(rc_api_response_t* respon while (rc_json_get_array_entry_object(achievement_fields, sizeof(achievement_fields) / sizeof(achievement_fields[0]), &iterator)) { if (!rc_json_get_required_unum(&achievement->id, response, &achievement_fields[0], "ID")) return RC_MISSING_VALUE; - if (!rc_json_get_required_string(&achievement->title, response, &achievement_fields[1], "Title")) + if (!rc_json_get_required_string(&achievement->title, response, &achievement_fields[1], "Title") || !achievement->title) return RC_MISSING_VALUE; if (!rc_json_get_required_string(&achievement->description, response, &achievement_fields[2], "Description")) return RC_MISSING_VALUE; @@ -197,16 +197,18 @@ static int rc_api_process_fetch_game_data_achievements(rc_api_response_t* respon achievement->type = RC_ACHIEVEMENT_TYPE_STANDARD; /* legacy support : if title contains[m], change type to missable and remove[m] from title */ - if (memcmp(achievement->title, "[m]", 3) == 0) { + /* since we're comparing a suffix, calculate the length in case it contains null bytes */ + len = strlen(achievement->title); + if (len >= 3 && memcmp(achievement->title, "[m]", 3) == 0) { len = 3; while (achievement->title[len] == ' ') ++len; achievement->title += len; achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE; } - else if (achievement_fields[1].value_end && memcmp(achievement_fields[1].value_end - 4, "[m]", 3) == 0) { - len = strlen(achievement->title) - 3; - while (achievement->title[len - 1] == ' ') + else if (len >= 3 && memcmp(achievement->title + len - 3, "[m]", 3) == 0) { + len -= 3; + while (len > 0 && achievement->title[len - 1] == ' ') --len; ((char*)achievement->title)[len] = '\0'; achievement->type = RC_ACHIEVEMENT_TYPE_MISSABLE; diff --git a/dep/rcheevos/src/rc_util.c b/dep/rcheevos/src/rc_util.c index 95de413dc..3efdf04da 100644 --- a/dep/rcheevos/src/rc_util.c +++ b/dep/rcheevos/src/rc_util.c @@ -56,6 +56,11 @@ uint8_t* rc_buffer_reserve(rc_buffer_t* buffer, size_t amount) { rc_buffer_chunk_t* chunk = &buffer->chunk; size_t remaining; + + /* we should never need 1GB of space for anything, and avoids any underflow below */ + if (amount >= 0x40000000) + return NULL; + while (chunk) { remaining = chunk->end - chunk->write; @@ -112,6 +117,9 @@ void rc_buffer_consume(rc_buffer_t* buffer, const uint8_t* start, uint8_t* end) void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount) { uint8_t* ptr = rc_buffer_reserve(buffer, amount); + if (!ptr) + return NULL; + rc_buffer_consume(buffer, ptr, ptr + amount); return (void*)ptr; } @@ -119,6 +127,9 @@ void* rc_buffer_alloc(rc_buffer_t* buffer, size_t amount) char* rc_buffer_strncpy(rc_buffer_t* buffer, const char* src, size_t len) { uint8_t* dst = rc_buffer_reserve(buffer, len + 1); + if (!dst) + return NULL; + memcpy(dst, src, len); dst[len] = '\0'; rc_buffer_consume(buffer, dst, dst + len + 1); diff --git a/dep/rcheevos/src/rcheevos/condset.c b/dep/rcheevos/src/rcheevos/condset.c index b19d3fd22..c9f984bff 100644 --- a/dep/rcheevos/src/rcheevos/condset.c +++ b/dep/rcheevos/src/rcheevos/condset.c @@ -363,7 +363,8 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse) { classification = combining_classification; } - else { + else if (classification != RC_CONDITION_CLASSIFICATION_INDIRECT) { + /* if it's not COMBINING or INDIRECT, reset so it will get recalculated for the next clause */ combining_classification = RC_CONDITION_CLASSIFICATION_COMBINING; } diff --git a/dep/rcheevos/src/rcheevos/operand.c b/dep/rcheevos/src/rcheevos/operand.c index ae18d9315..5d9c81a5c 100644 --- a/dep/rcheevos/src/rcheevos/operand.c +++ b/dep/rcheevos/src/rcheevos/operand.c @@ -319,13 +319,30 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) { case RC_OPERAND_FP: return (left->value.dbl == right->value.dbl); case RC_OPERAND_RECALL: - return (left->value.memref == right->value.memref); + if (left->memref_access_type != right->memref_access_type) + return 0; + switch (left->memref_access_type) { + case RC_OPERAND_CONST: + return (left->value.num == right->value.num); + case RC_OPERAND_FP: + return (left->value.dbl == right->value.dbl); + default: + if (!left->value.memref || !right->value.memref) /* could not find remember */ + return 0; + break; /* fallthrough to memref comparison */ + } default: break; } /* comparing two memrefs - look for exact matches on type and size */ - if (left->size != right->size || left->value.memref->value.memref_type != right->value.memref->value.memref_type) + if (left->size != right->size) + return 0; + + if (left->value.memref == right->value.memref) /* quick match if pointing to same memref */ + return 1; + + if (left->value.memref->value.memref_type != right->value.memref->value.memref_type) return 0; switch (left->value.memref->value.memref_type) { @@ -336,8 +353,7 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) { return (left_memref->modifier_type == right_memref->modifier_type && left_memref->depth == right_memref->depth && rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier) && - rc_operands_are_equal(&left_memref->parent, &right_memref->parent) && - 1 == 1 + rc_operands_are_equal(&left_memref->parent, &right_memref->parent) ); } diff --git a/dep/rcheevos/src/rcheevos/rc_validate.c b/dep/rcheevos/src/rcheevos/rc_validate.c index 2d55c2f46..843c2ad30 100644 --- a/dep/rcheevos/src/rcheevos/rc_validate.c +++ b/dep/rcheevos/src/rcheevos/rc_validate.c @@ -1210,8 +1210,10 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co /* combining field didn't match, or there's more unmatched combining fields. ignore this condition */ if (!chain_matches || rc_validate_is_combining_condition(compare_condition)) { - while (compare_condition->next && rc_validate_is_combining_condition(compare_condition)) + while (compare_condition->next && rc_validate_is_combining_condition(compare_condition)) { + ++state->cond_index; compare_condition = compare_condition->next; + } continue; } diff --git a/dep/rcheevos/src/rcheevos/richpresence.c b/dep/rcheevos/src/rcheevos/richpresence.c index 229266730..c6a3393e2 100644 --- a/dep/rcheevos/src/rcheevos/richpresence.c +++ b/dep/rcheevos/src/rcheevos/richpresence.c @@ -802,7 +802,7 @@ static int rc_evaluate_richpresence_display(rc_richpresence_display_part_t* part value.value.u32 = '?'; tmp[chars++] = (char)value.value.u32; - if (chars == sizeof(tmp) || !part->next || part->next->display_type != RC_FORMAT_ASCIICHAR) + if (chars == sizeof(tmp) - 1 || !part->next || part->next->display_type != RC_FORMAT_ASCIICHAR) break; part = part->next; diff --git a/dep/rcheevos/src/rcheevos/value.c b/dep/rcheevos/src/rcheevos/value.c index 8876f5bce..9a451c63c 100644 --- a/dep/rcheevos/src/rcheevos/value.c +++ b/dep/rcheevos/src/rcheevos/value.c @@ -159,8 +159,14 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par /* if it looks like a floating point number, add the 'f' prefix */ while (isdigit((unsigned char)*buffer_ptr)) ++buffer_ptr; - if (*buffer_ptr == '.') + if (*buffer_ptr == '.') { + if (ptr == &buffer[sizeof(buffer)]) { + parse->offset = RC_INVALID_VALUE; + return; + } + *ptr++ = 'f'; + } continue; default: