From 453030182034c9db2d68189c308460dbdf6f1290 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 11 May 2025 14:08:24 +1000 Subject: [PATCH] dep/rcheevos: Bump to 8a0178d --- dep/rcheevos/include/rc_runtime_types.h | 3 + dep/rcheevos/src/rapi/rc_api_common.c | 4 +- dep/rcheevos/src/rapi/rc_api_common.h | 2 +- dep/rcheevos/src/rc_client.c | 103 +++++++++++++----- dep/rcheevos/src/rc_client_external.c | 74 ++++++++++++- dep/rcheevos/src/rc_client_types.natvis | 2 +- dep/rcheevos/src/rcheevos/condition.c | 45 ++++++++ dep/rcheevos/src/rcheevos/consoleinfo.c | 7 +- dep/rcheevos/src/rcheevos/memref.c | 2 +- dep/rcheevos/src/rcheevos/operand.c | 20 ++-- dep/rcheevos/src/rcheevos/rc_internal.h | 2 + .../src/rcheevos/rc_runtime_types.natvis | 2 +- dep/rcheevos/src/rcheevos/rc_validate.c | 40 +++++-- 13 files changed, 243 insertions(+), 63 deletions(-) diff --git a/dep/rcheevos/include/rc_runtime_types.h b/dep/rcheevos/include/rc_runtime_types.h index a2ff02ea3..28f456477 100644 --- a/dep/rcheevos/include/rc_runtime_types.h +++ b/dep/rcheevos/include/rc_runtime_types.h @@ -124,6 +124,9 @@ typedef struct rc_operand_t { /* specifies how to read the memref for some types (RC_OPERAND_*) */ uint8_t memref_access_type; + + /* if set, this operand is combining the current condition with the previous one */ + uint8_t is_combining; } rc_operand_t; diff --git a/dep/rcheevos/src/rapi/rc_api_common.c b/dep/rcheevos/src/rapi/rc_api_common.c index 3eeb79cf9..4caef42a2 100644 --- a/dep/rcheevos/src/rapi/rc_api_common.c +++ b/dep/rcheevos/src/rapi/rc_api_common.c @@ -1304,14 +1304,14 @@ int rc_api_init_fetch_image_request_hosted(rc_api_request_t* request, const rc_a return builder.result; } -const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* username) { +const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* image_name) { rc_api_fetch_image_request_t image_request; rc_api_request_t request; int result; memset(&image_request, 0, sizeof(image_request)); image_request.image_type = image_type; - image_request.image_name = username; + image_request.image_name = image_name; result = rc_api_init_fetch_image_request(&request, &image_request); if (result == RC_OK) diff --git a/dep/rcheevos/src/rapi/rc_api_common.h b/dep/rcheevos/src/rapi/rc_api_common.h index 3cefc2fd6..d44260aa7 100644 --- a/dep/rcheevos/src/rapi/rc_api_common.h +++ b/dep/rcheevos/src/rapi/rc_api_common.h @@ -80,7 +80,7 @@ const char* rc_api_default_host(void); void rc_api_url_build_dorequest_url(rc_api_request_t* request, const rc_api_host_t* host); int rc_api_url_build_dorequest(rc_api_url_builder_t* builder, const char* api, const char* username, const char* api_token); -const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* username); +const char* rc_api_build_avatar_url(rc_buffer_t* buffer, uint32_t image_type, const char* image_name); RC_END_C_DECLS diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index 5c758c677..d45b45c28 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -64,6 +64,7 @@ typedef struct rc_client_load_state_t #ifdef RC_CLIENT_SUPPORTS_HASH rc_hash_iterator_t hash_iterator; + rc_client_game_hash_t* tried_hashes[4]; #endif rc_client_pending_media_t* pending_media; @@ -2235,40 +2236,31 @@ static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state) return; } - if (load_state->game->media_hash && - load_state->game->media_hash->game_hash && - load_state->game->media_hash->game_hash->next) { + if (load_state->tried_hashes[1]) { /* multiple hashes were tried, create a CSV */ - struct rc_client_game_hash_t* game_hash = load_state->game->media_hash->game_hash; - int count = 1; + size_t i; + size_t count = 0; char* ptr; - size_t size; + size_t size = 0; - size = strlen(game_hash->hash) + 1; - while (game_hash->next) { - game_hash = game_hash->next; - size += strlen(game_hash->hash) + 1; + for (i = 0; i < sizeof(load_state->tried_hashes) / sizeof(load_state->tried_hashes[0]); ++i) { + if (!load_state->tried_hashes[i]) + break; + + size += strlen(load_state->tried_hashes[i]->hash) + 1; count++; } ptr = (char*)rc_buffer_alloc(&load_state->game->buffer, size); - ptr += size - 1; - *ptr = '\0'; - game_hash = load_state->game->media_hash->game_hash; - do { - const size_t hash_len = strlen(game_hash->hash); - ptr -= hash_len; - memcpy(ptr, game_hash->hash, hash_len); - - game_hash = game_hash->next; - if (!game_hash) - break; - - ptr--; - *ptr = ','; - } while (1); - load_state->game->public_.hash = ptr; + for (i = 0; i < count; i++) { + const size_t hash_len = strlen(load_state->tried_hashes[i]->hash); + memcpy(ptr, load_state->tried_hashes[i]->hash, hash_len); + ptr += hash_len; + *ptr++ = ','; + } + *(ptr - 1) = '\0'; + load_state->game->public_.console_id = RC_CONSOLE_UNKNOWN; } else { /* only a single hash was tried, capture it */ @@ -2293,6 +2285,12 @@ static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state) if (load_state->hash->game_id == 0) { #ifdef RC_CLIENT_SUPPORTS_EXTERNAL + #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION + if (client->state.raintegration && client->state.raintegration->set_console_id) { + if (load_state->game->public_.console_id != RC_CONSOLE_UNKNOWN) + client->state.raintegration->set_console_id(load_state->game->public_.console_id); + } + #endif if (client->state.external_client) { if (client->state.external_client->load_unknown_game) { client->state.external_client->load_unknown_game(load_state->game->public_.hash); @@ -2504,6 +2502,9 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa { rc_client_t* client = load_state->client; rc_client_game_hash_t* old_hash; +#ifdef RC_CLIENT_SUPPORTS_HASH + size_t i; +#endif if (!rc_client_attach_load_state(client, load_state)) { rc_client_free_load_state(load_state); @@ -2513,6 +2514,24 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa old_hash = load_state->hash; load_state->hash = rc_client_find_game_hash(client, hash); +#ifdef RC_CLIENT_SUPPORTS_HASH + i = 0; + do { + if (!load_state->tried_hashes[i]) { + load_state->tried_hashes[i] = load_state->hash; + break; + } + + if (load_state->tried_hashes[i] == load_state->hash) + break; + + if (++i == sizeof(load_state->tried_hashes) / sizeof(load_state->tried_hashes[0])) { + RC_CLIENT_LOG_VERBOSE(client, "tried_hashes buffer is full"); + break; + } + } while (1); +#endif + if (file_path) { rc_client_media_hash_t* media_hash = (rc_client_media_hash_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(*media_hash)); @@ -2553,6 +2572,11 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa rc_api_destroy_request(&request); } + else if (load_state->hash->game_id != RC_CLIENT_UNKNOWN_GAME_ID && + client->state.external_client && client->state.external_client->begin_load_game) { + rc_client_begin_async(client, &load_state->async_handle); + client->state.external_client->begin_load_game(client, load_state->hash->hash, rc_client_external_load_state_callback, load_state); + } #endif else { rc_client_begin_fetch_game_data(load_state); @@ -2561,6 +2585,26 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa return (client->state.load == load_state) ? &load_state->async_handle : NULL; } +static void rc_client_abort_load_in_progress(rc_client_t* client) +{ + rc_client_load_state_t* load_state; + + rc_mutex_lock(&client->state.mutex); + + load_state = client->state.load; + if (load_state) { + /* this mimics rc_client_abort_async without nesting the lock */ + load_state->async_handle.aborted = RC_CLIENT_ASYNC_ABORTED; + + client->state.load = NULL; + } + + rc_mutex_unlock(&client->state.mutex); + + if (load_state && load_state->callback) + load_state->callback(RC_ABORTED, "The requested game is no longer active", load_state->client, load_state->callback_userdata); +} + rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const char* hash, rc_client_callback_t callback, void* callback_userdata) { rc_client_load_state_t* load_state; @@ -2575,6 +2619,8 @@ rc_client_async_handle_t* rc_client_begin_load_game(rc_client_t* client, const c return NULL; } + rc_client_abort_load_in_progress(client); + #ifdef RC_CLIENT_SUPPORTS_EXTERNAL if (client->state.external_client && client->state.external_client->begin_load_game) return client->state.external_client->begin_load_game(client, hash, callback, callback_userdata); @@ -2616,6 +2662,8 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl return NULL; } + rc_client_abort_load_in_progress(client); + #ifdef RC_CLIENT_SUPPORTS_EXTERNAL /* if a add_game_hash handler exists, do the identification locally, then pass the * resulting game_id/hash to the external client */ @@ -5991,6 +6039,9 @@ void rc_client_reset(rc_client_t* client) int rc_client_can_pause(rc_client_t* client, uint32_t* frames_remaining) { + if (!client) + return 1; + #ifdef RC_CLIENT_SUPPORTS_EXTERNAL if (client->state.external_client && client->state.external_client->can_pause) return client->state.external_client->can_pause(frames_remaining); diff --git a/dep/rcheevos/src/rc_client_external.c b/dep/rcheevos/src/rc_client_external.c index acfcf1740..fd8f76cbd 100644 --- a/dep/rcheevos/src/rc_client_external.c +++ b/dep/rcheevos/src/rc_client_external.c @@ -3,17 +3,45 @@ #include "rc_client_external_versions.h" #include "rc_client_internal.h" +#include "rc_api_runtime.h" + #define RC_CONVERSION_FILL(obj, obj_type, src_type) memset((uint8_t*)obj + sizeof(src_type), 0, sizeof(obj_type) - sizeof(src_type)) +/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */ +#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64 + typedef struct rc_client_external_conversions_t { rc_client_user_t user; rc_client_game_t game; rc_client_subset_t subsets[4]; rc_client_achievement_t achievements[16]; + char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE]; + char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE]; + char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE]; + char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE]; + char achievement_badge_locked_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE]; uint32_t next_subset_index; uint32_t next_achievement_index; } rc_client_external_conversions_t; +static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name) +{ + rc_api_fetch_image_request_t image_request; + rc_api_request_t request; + int result; + + memset(&image_request, 0, sizeof(image_request)); + image_request.image_type = image_type; + image_request.image_name = image_name; + + result = rc_api_init_fetch_image_request(&request, &image_request); + if (result != RC_OK) + return NULL; + + strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url); + return buffer; +} + static void rc_client_external_conversions_init(const rc_client_t* client) { if (!client->state.external_client_conversions) { @@ -39,6 +67,10 @@ const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* cl converted = &client->state.external_client_conversions->user; memcpy(converted, v1_user, sizeof(v1_rc_client_user_t)); RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t); + + converted->avatar_url = rc_client_external_build_avatar_url( + client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username); + return converted; } @@ -54,12 +86,17 @@ const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* cl converted = &client->state.external_client_conversions->game; memcpy(converted, v1_game, sizeof(v1_rc_client_game_t)); RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t); + + converted->badge_url = rc_client_external_build_avatar_url( + client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name); + return converted; } const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t* client, const rc_client_subset_t* v1_subset) { rc_client_subset_t* converted = NULL; + char* badge_url = NULL; const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]); uint32_t index; @@ -71,23 +108,31 @@ const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t for (index = 0; index < num_subsets; ++index) { if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) { converted = &client->state.external_client_conversions->subsets[index]; + badge_url = client->state.external_client_conversions->subset_badge_url[index]; break; } } if (!converted) { - converted = &client->state.external_client_conversions->subsets[client->state.external_client_conversions->next_subset_index]; - client->state.external_client_conversions->next_subset_index = (client->state.external_client_conversions->next_subset_index + 1) % num_subsets; + index = client->state.external_client_conversions->next_subset_index; + converted = &client->state.external_client_conversions->subsets[index]; + badge_url = client->state.external_client_conversions->subset_badge_url[client->state.external_client_conversions->next_subset_index]; + client->state.external_client_conversions->next_subset_index = (index + 1) % num_subsets; } memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t)); RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t); + + converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name); + return converted; } const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const rc_client_t* client, const rc_client_achievement_t* v1_achievement) { rc_client_achievement_t* converted = NULL; + char* badge_url = NULL; + char* badge_locked_url = NULL; const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]); uint32_t index; @@ -99,17 +144,26 @@ const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const r for (index = 0; index < num_achievements; ++index) { if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) { converted = &client->state.external_client_conversions->achievements[index]; + badge_url = client->state.external_client_conversions->achievement_badge_url[index]; + badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index]; break; } } if (!converted) { - converted = &client->state.external_client_conversions->achievements[client->state.external_client_conversions->next_achievement_index]; - client->state.external_client_conversions->next_achievement_index = (client->state.external_client_conversions->next_achievement_index + 1) % num_achievements; + index = client->state.external_client_conversions->next_achievement_index; + converted = &client->state.external_client_conversions->achievements[index]; + badge_url = client->state.external_client_conversions->achievement_badge_url[index]; + badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index]; + client->state.external_client_conversions->next_achievement_index = (index + 1) % num_achievements; } memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t)); RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t); + + converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name); + converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name); + return converted; } @@ -118,6 +172,7 @@ typedef struct rc_client_achievement_list_wrapper_t { rc_client_achievement_list_t* source_list; rc_client_achievement_t* achievements; rc_client_achievement_t** achievements_pointers; + char* badge_url_buffer; } rc_client_achievement_list_wrapper_t; static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info) @@ -130,6 +185,8 @@ static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_lis free(wrapper->achievements_pointers); if (wrapper->info.public_.buckets) free((void*)wrapper->info.public_.buckets); + if (wrapper->badge_url_buffer) + free(wrapper->badge_url_buffer); rc_client_destroy_achievement_list(wrapper->source_list); @@ -156,6 +213,7 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets; rc_client_achievement_bucket_t* bucket; uint32_t num_achievements = 0; + char* badge_url = NULL; new_list->info.public_.buckets = bucket = (rc_client_achievement_bucket_t*)calloc(v1_achievement_list->num_buckets, sizeof(*new_list->info.public_.buckets)); if (!new_list->info.public_.buckets) @@ -167,7 +225,8 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con if (num_achievements) { new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->achievements)); new_list->achievements_pointers = (rc_client_achievement_t**)malloc(num_achievements * sizeof(rc_client_achievement_t*)); - if (!new_list->achievements || !new_list->achievements_pointers) + new_list->badge_url_buffer = badge_url = (char*)malloc(num_achievements * 2 * RC_CLIENT_IMAGE_URL_BUFFER_SIZE); + if (!new_list->achievements || !new_list->achievements_pointers || !new_list->badge_url_buffer) return (rc_client_achievement_list_t*)new_list; } @@ -186,6 +245,11 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) { *achievement = &new_list->achievements[num_achievements++]; memcpy(*achievement, *src_achievement, sizeof(**src_achievement)); + + (*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name); + badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE; + (*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name); + badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE; } } } diff --git a/dep/rcheevos/src/rc_client_types.natvis b/dep/rcheevos/src/rc_client_types.natvis index 01b06026a..ddb71817f 100644 --- a/dep/rcheevos/src/rc_client_types.natvis +++ b/dep/rcheevos/src/rc_client_types.natvis @@ -364,7 +364,7 @@ game - *((__rc_client_game_hash_list_t*)&hashes) + *((__rc_client_game_hash_list_t*)this) user callbacks state diff --git a/dep/rcheevos/src/rcheevos/condition.c b/dep/rcheevos/src/rcheevos/condition.c index df8cb5049..320ca0a62 100644 --- a/dep/rcheevos/src/rcheevos/condition.c +++ b/dep/rcheevos/src/rcheevos/condition.c @@ -432,6 +432,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t if (parse->addsource_parent.type != RC_OPERAND_NONE) { /* type determined by leaf */ rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); + condition->operand1.is_combining = 1; } memcpy(&parse->remember, &condition->operand1, sizeof(parse->remember)); @@ -465,6 +466,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t if (parse->addsource_parent.type != RC_OPERAND_NONE) { /* type determined by leaf */ rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); + condition->operand1.is_combining = 1; if (parse->buffer) condition->optimized_comparator = rc_condition_determine_comparator(condition); @@ -476,6 +478,49 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t } } +static const rc_modified_memref_t* rc_operand_get_modified_memref(const rc_operand_t* operand) { + if (!rc_operand_is_memref(operand)) + return NULL; + + if (operand->value.memref->value.memref_type != RC_MEMREF_TYPE_MODIFIED_MEMREF) + return NULL; + + return (rc_modified_memref_t*)operand->value.memref; +} + +/* rc_condition_update_parse_state will mutate the operand1 to point at the modified memref + * containing the accumulated result up until that point. this function returns the original + * unmodified operand1 from parsing the definition. + */ +const rc_operand_t* rc_condition_get_real_operand1(const rc_condition_t* self) { + const rc_operand_t* operand = &self->operand1; + const rc_modified_memref_t* modified_memref; + + if (operand->is_combining) { + /* operand = "previous + current" - extract current */ + const rc_modified_memref_t* combining_modified_memref = rc_operand_get_modified_memref(operand); + if (combining_modified_memref) + operand = &combining_modified_memref->modifier; + } + + /* modifying operators are merged into an rc_modified_memref_t + * if operand1 is a modified memref, assume it's been merged with the right side and + * extract the parent which is the actual operand1. */ + modified_memref = rc_operand_get_modified_memref(operand); + if (modified_memref) { + if (modified_memref->modifier_type == RC_OPERATOR_INDIRECT_READ) { + /* if the modified memref is an indirect read, the parent is the indirect + * address and the modifier is the offset. the actual size and address are + * stored in the modified memref - use it */ + } else if (rc_operator_is_modifying(self->oper) && self->oper != RC_OPERATOR_NONE) { + /* operand = "parent*modifier" - extract parent.modifier will already be in operand2 */ + operand = &modified_memref->parent; + } + } + + return operand; +} + int rc_condition_is_combining(const rc_condition_t* self) { switch (self->type) { case RC_CONDITION_STANDARD: diff --git a/dep/rcheevos/src/rcheevos/consoleinfo.c b/dep/rcheevos/src/rcheevos/consoleinfo.c index 0fa9f14db..e25d74c5e 100644 --- a/dep/rcheevos/src/rcheevos/consoleinfo.c +++ b/dep/rcheevos/src/rcheevos/consoleinfo.c @@ -833,12 +833,13 @@ static const rc_memory_region_t _rc_memory_regions_pokemini[] = { static const rc_memory_regions_t rc_memory_regions_pokemini = { _rc_memory_regions_pokemini, 2 }; /* ===== Sega CD ===== */ -/* https://en.wikibooks.org/wiki/Genesis_Programming#MegaCD_Changes */ +/* https://en.wikibooks.org/wiki/Genesis_Programming/68K_Memory_map/ */ static const rc_memory_region_t _rc_memory_regions_segacd[] = { { 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "68000 RAM" }, - { 0x010000U, 0x08FFFFU, 0x80020000U, RC_MEMORY_TYPE_SAVE_RAM, "CD PRG RAM" } /* normally banked into $020000-$03FFFF */ + { 0x010000U, 0x08FFFFU, 0x80020000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD PRG RAM" }, /* normally banked into $020000-$03FFFF */ + { 0x090000U, 0x0AFFFFU, 0x00200000U, RC_MEMORY_TYPE_SYSTEM_RAM, "CD WORD RAM" } }; -static const rc_memory_regions_t rc_memory_regions_segacd = { _rc_memory_regions_segacd, 2 }; +static const rc_memory_regions_t rc_memory_regions_segacd = { _rc_memory_regions_segacd, 3 }; /* ===== Sega Saturn ===== */ /* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */ diff --git a/dep/rcheevos/src/rcheevos/memref.c b/dep/rcheevos/src/rcheevos/memref.c index ca94077ad..5b5d80cf8 100644 --- a/dep/rcheevos/src/rcheevos/memref.c +++ b/dep/rcheevos/src/rcheevos/memref.c @@ -150,7 +150,7 @@ rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t memset(modified_memref, 0, sizeof(*modified_memref)); modified_memref->memref.value.memref_type = RC_MEMREF_TYPE_MODIFIED_MEMREF; modified_memref->memref.value.size = size; - modified_memref->memref.value.type = (size == RC_MEMSIZE_FLOAT) ? RC_VALUE_TYPE_FLOAT : RC_VALUE_TYPE_UNSIGNED; + modified_memref->memref.value.type = rc_memsize_is_float(size) ? RC_VALUE_TYPE_FLOAT : RC_VALUE_TYPE_UNSIGNED; memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent)); memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier)); modified_memref->modifier_type = modifier_type; diff --git a/dep/rcheevos/src/rcheevos/operand.c b/dep/rcheevos/src/rcheevos/operand.c index 84e620bd6..e79050fa9 100644 --- a/dep/rcheevos/src/rcheevos/operand.c +++ b/dep/rcheevos/src/rcheevos/operand.c @@ -55,6 +55,7 @@ static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr, r } else { memcpy(self, &parse->remember, sizeof(*self)); + self->is_combining = 0; self->memref_access_type = self->type; } self->type = RC_OPERAND_RECALL; @@ -117,16 +118,11 @@ static int rc_parse_operand_memory(rc_operand_t* self, const char** memaddr, rc_ } if (parse->indirect_parent.type != RC_OPERAND_NONE) { - if (parse->indirect_parent.type == RC_OPERAND_CONST) { - self->value.memref = rc_alloc_memref(parse, address + parse->indirect_parent.value.num, size); - } - else { - rc_operand_t offset; - rc_operand_set_const(&offset, address); + rc_operand_t offset; + rc_operand_set_const(&offset, address); - self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse, - size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset); - } + self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse, + size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset); } else { self->value.memref = rc_alloc_memref(parse, address, size); @@ -147,6 +143,8 @@ int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t* int negative; int allow_decimal = 0; + self->is_combining = 0; + switch (*aux) { case 'h': case 'H': /* hex constant */ if (aux[2] == 'x' || aux[2] == 'X') { @@ -318,7 +316,7 @@ 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 1; + return (left->value.memref == right->value.memref); default: break; } @@ -360,7 +358,7 @@ int rc_operator_is_modifying(int oper) { } } -static int rc_memsize_is_float(uint8_t size) { +int rc_memsize_is_float(uint8_t size) { switch (size) { case RC_MEMSIZE_FLOAT: case RC_MEMSIZE_FLOAT_BE: diff --git a/dep/rcheevos/src/rcheevos/rc_internal.h b/dep/rcheevos/src/rcheevos/rc_internal.h index f63d90b90..7c013d9dc 100644 --- a/dep/rcheevos/src/rcheevos/rc_internal.h +++ b/dep/rcheevos/src/rcheevos/rc_internal.h @@ -335,10 +335,12 @@ int rc_test_condition(rc_condition_t* self, rc_eval_state_t* eval_state); void rc_evaluate_condition_value(rc_typed_value_t* value, rc_condition_t* self, rc_eval_state_t* eval_state); int rc_condition_is_combining(const rc_condition_t* self); void rc_condition_convert_to_operand(const rc_condition_t* condition, rc_operand_t* operand, rc_parse_state_t* parse); +const rc_operand_t* rc_condition_get_real_operand1(const rc_condition_t* self); int rc_parse_operand(rc_operand_t* self, const char** memaddr, rc_parse_state_t* parse); void rc_evaluate_operand(rc_typed_value_t* value, const rc_operand_t* self, rc_eval_state_t* eval_state); int rc_operator_is_modifying(int oper); +int rc_memsize_is_float(uint8_t size); int rc_operand_is_float_memref(const rc_operand_t* self); int rc_operand_is_float(const rc_operand_t* self); int rc_operand_is_recall(const rc_operand_t* self); diff --git a/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis b/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis index e086e9ad2..45e31921d 100644 --- a/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis +++ b/dep/rcheevos/src/rcheevos/rc_runtime_types.natvis @@ -120,7 +120,7 @@ - ... {*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x} + ... {*((__rc_memsize_enum_func_t*)&operand.size)} {((rc_modified_memref_t*)operand.value.memref)->parent,na} value {((rc_value_t*)operand.value.memref)->name,s} {*((__rc_memsize_enum_func_t*)&operand.size)} {operand.value.memref->address,x} diff --git a/dep/rcheevos/src/rcheevos/rc_validate.c b/dep/rcheevos/src/rcheevos/rc_validate.c index a01e362ee..6a5c658a7 100644 --- a/dep/rcheevos/src/rcheevos/rc_validate.c +++ b/dep/rcheevos/src/rcheevos/rc_validate.c @@ -199,8 +199,10 @@ static uint32_t rc_max_chain_value(const rc_operand_t* operand) { 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; - 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_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); + } } return rc_max_value(operand); @@ -304,11 +306,13 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result } for (cond = condset->conditions; cond; cond = cond->next, ++index) { - const int is_memref1 = rc_operand_is_memref(&cond->operand1); + /* validate the original operands first */ + const rc_operand_t* operand1 = rc_condition_get_real_operand1(cond); + int is_memref1 = rc_operand_is_memref(operand1); const int is_memref2 = rc_operand_is_memref(&cond->operand2); if (!in_add_address) { - if (is_memref1 && !rc_validate_memref(cond->operand1.value.memref, buffer, sizeof(buffer), console_id, max_address)) { + if (is_memref1 && !rc_validate_memref(operand1->value.memref, buffer, sizeof(buffer), console_id, max_address)) { snprintf(result, result_size, "Condition %d: %s", index, buffer); return 0; } @@ -321,8 +325,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result in_add_address = 0; } - if (rc_operand_is_recall(&cond->operand1)) { - if (rc_operand_type_is_memref(cond->operand1.memref_access_type) && !cond->operand1.value.memref) { + if (rc_operand_is_recall(operand1)) { + if (rc_operand_type_is_memref(operand1->memref_access_type) && !operand1->value.memref) { snprintf(result, result_size, "Condition %d: Recall used before Remember", index); return 0; } @@ -343,10 +347,18 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result continue; case RC_CONDITION_ADD_ADDRESS: - if (cond->operand1.type == RC_OPERAND_DELTA || cond->operand1.type == RC_OPERAND_PRIOR) { + if (operand1->type == RC_OPERAND_DELTA || operand1->type == RC_OPERAND_PRIOR) { snprintf(result, result_size, "Condition %d: Using pointer from previous frame", index); return 0; } + if (rc_operand_is_float(&cond->operand1) || rc_operand_is_float(&cond->operand2)) { + snprintf(result, result_size, "Condition %d: Using non-integer value in AddAddress calcuation", index); + return 0; + } + if (rc_operand_type_is_transform(cond->operand1.type)) { + snprintf(result, result_size, "Condition %d: Using transformed value in AddAddress calcuation", index); + return 0; + } in_add_address = 1; is_combining = 1; continue; @@ -390,22 +402,26 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result break; } + /* original operands are valid. now switch to the derived operands for logic + * combining/comparing them */ + operand1 = &cond->operand1; + is_memref1 = rc_operand_is_memref(operand1); + /* check for comparing two differently sized memrefs */ if (is_memref1 && is_memref2 && - cond->operand1.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && + operand1->value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && cond->operand2.value.memref->value.memref_type == RC_MEMREF_TYPE_MEMREF && - rc_max_value(&cond->operand1) != rc_max_value(&cond->operand2)) { + rc_max_value(operand1) != rc_max_value(&cond->operand2)) { snprintf(result, result_size, "Condition %d: Comparing different memory sizes", index); return 0; } - if (is_memref1 && rc_operand_is_float(&cond->operand1)) { + if (is_memref1 && rc_operand_is_float(operand1)) { /* if left side is a float, right side will be converted to a float, so don't do range validation */ } else if (is_memref1 || is_memref2) { /* if either side is a memref, check for impossible comparisons */ const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index); - const rc_operand_t* operand1 = &cond->operand1; const rc_operand_t* operand2 = &cond->operand2; uint8_t oper = cond->oper; uint32_t max = rc_max_chain_value(operand1); @@ -414,8 +430,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result if (!is_memref1) { /* pretend constant was on right side */ + operand2 = operand1; operand1 = &cond->operand2; - operand2 = &cond->operand1; max_val = max; max = rc_max_value(&cond->operand2);