dep/rcheevos: Bump to 8a0178d

This commit is contained in:
Stenzek 2025-05-11 14:08:24 +10:00
parent 6cde51b7b5
commit 4530301820
No known key found for this signature in database
13 changed files with 243 additions and 63 deletions

View File

@ -124,6 +124,9 @@ typedef struct rc_operand_t {
/* specifies how to read the memref for some types (RC_OPERAND_*) */ /* specifies how to read the memref for some types (RC_OPERAND_*) */
uint8_t memref_access_type; 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; rc_operand_t;

View File

@ -1304,14 +1304,14 @@ int rc_api_init_fetch_image_request_hosted(rc_api_request_t* request, const rc_a
return builder.result; 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_fetch_image_request_t image_request;
rc_api_request_t request; rc_api_request_t request;
int result; int result;
memset(&image_request, 0, sizeof(image_request)); memset(&image_request, 0, sizeof(image_request));
image_request.image_type = image_type; 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); result = rc_api_init_fetch_image_request(&request, &image_request);
if (result == RC_OK) if (result == RC_OK)

View File

@ -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); 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); 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 RC_END_C_DECLS

View File

@ -64,6 +64,7 @@ typedef struct rc_client_load_state_t
#ifdef RC_CLIENT_SUPPORTS_HASH #ifdef RC_CLIENT_SUPPORTS_HASH
rc_hash_iterator_t hash_iterator; rc_hash_iterator_t hash_iterator;
rc_client_game_hash_t* tried_hashes[4];
#endif #endif
rc_client_pending_media_t* pending_media; 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; return;
} }
if (load_state->game->media_hash && if (load_state->tried_hashes[1]) {
load_state->game->media_hash->game_hash &&
load_state->game->media_hash->game_hash->next) {
/* multiple hashes were tried, create a CSV */ /* multiple hashes were tried, create a CSV */
struct rc_client_game_hash_t* game_hash = load_state->game->media_hash->game_hash; size_t i;
int count = 1; size_t count = 0;
char* ptr; char* ptr;
size_t size; size_t size = 0;
size = strlen(game_hash->hash) + 1; for (i = 0; i < sizeof(load_state->tried_hashes) / sizeof(load_state->tried_hashes[0]); ++i) {
while (game_hash->next) { if (!load_state->tried_hashes[i])
game_hash = game_hash->next; break;
size += strlen(game_hash->hash) + 1;
size += strlen(load_state->tried_hashes[i]->hash) + 1;
count++; count++;
} }
ptr = (char*)rc_buffer_alloc(&load_state->game->buffer, size); 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; 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; load_state->game->public_.console_id = RC_CONSOLE_UNKNOWN;
} else { } else {
/* only a single hash was tried, capture it */ /* 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) { if (load_state->hash->game_id == 0) {
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL #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) {
if (client->state.external_client->load_unknown_game) { if (client->state.external_client->load_unknown_game) {
client->state.external_client->load_unknown_game(load_state->game->public_.hash); 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_t* client = load_state->client;
rc_client_game_hash_t* old_hash; 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)) { if (!rc_client_attach_load_state(client, load_state)) {
rc_client_free_load_state(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; old_hash = load_state->hash;
load_state->hash = rc_client_find_game_hash(client, 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) { if (file_path) {
rc_client_media_hash_t* media_hash = rc_client_media_hash_t* media_hash =
(rc_client_media_hash_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(*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); 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 #endif
else { else {
rc_client_begin_fetch_game_data(load_state); 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; 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_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; 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; return NULL;
} }
rc_client_abort_load_in_progress(client);
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL #ifdef RC_CLIENT_SUPPORTS_EXTERNAL
if (client->state.external_client && client->state.external_client->begin_load_game) 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); 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; return NULL;
} }
rc_client_abort_load_in_progress(client);
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL #ifdef RC_CLIENT_SUPPORTS_EXTERNAL
/* if a add_game_hash handler exists, do the identification locally, then pass the /* if a add_game_hash handler exists, do the identification locally, then pass the
* resulting game_id/hash to the external client */ * 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) int rc_client_can_pause(rc_client_t* client, uint32_t* frames_remaining)
{ {
if (!client)
return 1;
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL #ifdef RC_CLIENT_SUPPORTS_EXTERNAL
if (client->state.external_client && client->state.external_client->can_pause) if (client->state.external_client && client->state.external_client->can_pause)
return client->state.external_client->can_pause(frames_remaining); return client->state.external_client->can_pause(frames_remaining);

View File

@ -3,17 +3,45 @@
#include "rc_client_external_versions.h" #include "rc_client_external_versions.h"
#include "rc_client_internal.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)) #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 { typedef struct rc_client_external_conversions_t {
rc_client_user_t user; rc_client_user_t user;
rc_client_game_t game; rc_client_game_t game;
rc_client_subset_t subsets[4]; rc_client_subset_t subsets[4];
rc_client_achievement_t achievements[16]; 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_subset_index;
uint32_t next_achievement_index; uint32_t next_achievement_index;
} rc_client_external_conversions_t; } 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) static void rc_client_external_conversions_init(const rc_client_t* client)
{ {
if (!client->state.external_client_conversions) { 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; converted = &client->state.external_client_conversions->user;
memcpy(converted, v1_user, sizeof(v1_rc_client_user_t)); memcpy(converted, v1_user, sizeof(v1_rc_client_user_t));
RC_CONVERSION_FILL(converted, rc_client_user_t, 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; 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; converted = &client->state.external_client_conversions->game;
memcpy(converted, v1_game, sizeof(v1_rc_client_game_t)); memcpy(converted, v1_game, sizeof(v1_rc_client_game_t));
RC_CONVERSION_FILL(converted, rc_client_game_t, 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; 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) 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; 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]); const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]);
uint32_t index; 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) { for (index = 0; index < num_subsets; ++index) {
if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) { if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) {
converted = &client->state.external_client_conversions->subsets[index]; converted = &client->state.external_client_conversions->subsets[index];
badge_url = client->state.external_client_conversions->subset_badge_url[index];
break; break;
} }
} }
if (!converted) { if (!converted) {
converted = &client->state.external_client_conversions->subsets[client->state.external_client_conversions->next_subset_index]; index = 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; 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)); memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
RC_CONVERSION_FILL(converted, rc_client_subset_t, 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; 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) 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; 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]); const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]);
uint32_t index; 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) { for (index = 0; index < num_achievements; ++index) {
if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) { if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) {
converted = &client->state.external_client_conversions->achievements[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];
break; break;
} }
} }
if (!converted) { if (!converted) {
converted = &client->state.external_client_conversions->achievements[client->state.external_client_conversions->next_achievement_index]; index = 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; 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)); memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
RC_CONVERSION_FILL(converted, rc_client_achievement_t, 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; return converted;
} }
@ -118,6 +172,7 @@ typedef struct rc_client_achievement_list_wrapper_t {
rc_client_achievement_list_t* source_list; rc_client_achievement_list_t* source_list;
rc_client_achievement_t* achievements; rc_client_achievement_t* achievements;
rc_client_achievement_t** achievements_pointers; rc_client_achievement_t** achievements_pointers;
char* badge_url_buffer;
} rc_client_achievement_list_wrapper_t; } rc_client_achievement_list_wrapper_t;
static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info) 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); free(wrapper->achievements_pointers);
if (wrapper->info.public_.buckets) if (wrapper->info.public_.buckets)
free((void*)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); 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; const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets;
rc_client_achievement_bucket_t* bucket; rc_client_achievement_bucket_t* bucket;
uint32_t num_achievements = 0; 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)); 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) 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) { if (num_achievements) {
new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->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*)); 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; 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) { for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) {
*achievement = &new_list->achievements[num_achievements++]; *achievement = &new_list->achievements[num_achievements++];
memcpy(*achievement, *src_achievement, sizeof(**src_achievement)); 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;
} }
} }
} }

View File

@ -364,7 +364,7 @@
<Type Name="rc_client_t"> <Type Name="rc_client_t">
<Expand> <Expand>
<Item Name="game">game</Item> <Item Name="game">game</Item>
<Item Name="hashes">*((__rc_client_game_hash_list_t*)&amp;hashes)</Item> <Item Name="hashes">*((__rc_client_game_hash_list_t*)this)</Item>
<Item Name="user">user</Item> <Item Name="user">user</Item>
<Item Name="callbacks">callbacks</Item> <Item Name="callbacks">callbacks</Item>
<Item Name="state">state</Item> <Item Name="state">state</Item>

View File

@ -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) { if (parse->addsource_parent.type != RC_OPERAND_NONE) {
/* type determined by leaf */ /* type determined by leaf */
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
condition->operand1.is_combining = 1;
} }
memcpy(&parse->remember, &condition->operand1, sizeof(parse->remember)); 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) { if (parse->addsource_parent.type != RC_OPERAND_NONE) {
/* type determined by leaf */ /* type determined by leaf */
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size); rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
condition->operand1.is_combining = 1;
if (parse->buffer) if (parse->buffer)
condition->optimized_comparator = rc_condition_determine_comparator(condition); 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) { int rc_condition_is_combining(const rc_condition_t* self) {
switch (self->type) { switch (self->type) {
case RC_CONDITION_STANDARD: case RC_CONDITION_STANDARD:

View File

@ -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 }; static const rc_memory_regions_t rc_memory_regions_pokemini = { _rc_memory_regions_pokemini, 2 };
/* ===== Sega CD ===== */ /* ===== 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[] = { static const rc_memory_region_t _rc_memory_regions_segacd[] = {
{ 0x000000U, 0x00FFFFU, 0x00FF0000U, RC_MEMORY_TYPE_SYSTEM_RAM, "68000 RAM" }, { 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 ===== */ /* ===== Sega Saturn ===== */
/* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */ /* https://segaretro.org/Sega_Saturn_hardware_notes_(2004-04-27) */

View File

@ -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)); memset(modified_memref, 0, sizeof(*modified_memref));
modified_memref->memref.value.memref_type = RC_MEMREF_TYPE_MODIFIED_MEMREF; modified_memref->memref.value.memref_type = RC_MEMREF_TYPE_MODIFIED_MEMREF;
modified_memref->memref.value.size = size; 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->parent, parent, sizeof(modified_memref->parent));
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier)); memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
modified_memref->modifier_type = modifier_type; modified_memref->modifier_type = modifier_type;

View File

@ -55,6 +55,7 @@ static int rc_parse_operand_variable(rc_operand_t* self, const char** memaddr, r
} }
else { else {
memcpy(self, &parse->remember, sizeof(*self)); memcpy(self, &parse->remember, sizeof(*self));
self->is_combining = 0;
self->memref_access_type = self->type; self->memref_access_type = self->type;
} }
self->type = RC_OPERAND_RECALL; 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_NONE) {
if (parse->indirect_parent.type == RC_OPERAND_CONST) { rc_operand_t offset;
self->value.memref = rc_alloc_memref(parse, address + parse->indirect_parent.value.num, size); rc_operand_set_const(&offset, address);
}
else {
rc_operand_t offset;
rc_operand_set_const(&offset, address);
self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse, self->value.memref = (rc_memref_t*)rc_alloc_modified_memref(parse,
size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset); size, &parse->indirect_parent, RC_OPERATOR_INDIRECT_READ, &offset);
}
} }
else { else {
self->value.memref = rc_alloc_memref(parse, address, size); 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 negative;
int allow_decimal = 0; int allow_decimal = 0;
self->is_combining = 0;
switch (*aux) { switch (*aux) {
case 'h': case 'H': /* hex constant */ case 'h': case 'H': /* hex constant */
if (aux[2] == 'x' || aux[2] == 'X') { 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: case RC_OPERAND_FP:
return (left->value.dbl == right->value.dbl); return (left->value.dbl == right->value.dbl);
case RC_OPERAND_RECALL: case RC_OPERAND_RECALL:
return 1; return (left->value.memref == right->value.memref);
default: default:
break; 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) { switch (size) {
case RC_MEMSIZE_FLOAT: case RC_MEMSIZE_FLOAT:
case RC_MEMSIZE_FLOAT_BE: case RC_MEMSIZE_FLOAT_BE:

View File

@ -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); 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); 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); 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); 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); 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_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_memref(const rc_operand_t* self);
int rc_operand_is_float(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); int rc_operand_is_recall(const rc_operand_t* self);

View File

@ -120,7 +120,7 @@
</Expand> </Expand>
</Type> </Type>
<Type Name="__rc_operand_memref_t"> <Type Name="__rc_operand_memref_t">
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">... {*((__rc_memsize_enum_func_t*)&amp;operand.size)} {operand.value.memref->address,x}</DisplayString> <DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_MODIFIED_MEMREF">... {*((__rc_memsize_enum_func_t*)&amp;operand.size)} {((rc_modified_memref_t*)operand.value.memref)->parent,na}</DisplayString>
<DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_VALUE">value {((rc_value_t*)operand.value.memref)->name,s}</DisplayString> <DisplayString Condition="operand.value.memref->value.memref_type==RC_MEMREF_TYPE_VALUE">value {((rc_value_t*)operand.value.memref)->name,s}</DisplayString>
<DisplayString>{*((__rc_memsize_enum_func_t*)&amp;operand.size)} {operand.value.memref->address,x}</DisplayString> <DisplayString>{*((__rc_memsize_enum_func_t*)&amp;operand.size)} {operand.value.memref->address,x}</DisplayString>
<Expand> <Expand>

View File

@ -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) { 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 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); if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) {
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier); 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); 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) { 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); const int is_memref2 = rc_operand_is_memref(&cond->operand2);
if (!in_add_address) { 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); snprintf(result, result_size, "Condition %d: %s", index, buffer);
return 0; return 0;
} }
@ -321,8 +325,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
in_add_address = 0; in_add_address = 0;
} }
if (rc_operand_is_recall(&cond->operand1)) { if (rc_operand_is_recall(operand1)) {
if (rc_operand_type_is_memref(cond->operand1.memref_access_type) && !cond->operand1.value.memref) { if (rc_operand_type_is_memref(operand1->memref_access_type) && !operand1->value.memref) {
snprintf(result, result_size, "Condition %d: Recall used before Remember", index); snprintf(result, result_size, "Condition %d: Recall used before Remember", index);
return 0; return 0;
} }
@ -343,10 +347,18 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
continue; continue;
case RC_CONDITION_ADD_ADDRESS: 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); snprintf(result, result_size, "Condition %d: Using pointer from previous frame", index);
return 0; 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; in_add_address = 1;
is_combining = 1; is_combining = 1;
continue; continue;
@ -390,22 +402,26 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
break; 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 */ /* check for comparing two differently sized memrefs */
if (is_memref1 && is_memref2 && 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 && 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); snprintf(result, result_size, "Condition %d: Comparing different memory sizes", index);
return 0; 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 */ /* 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) { else if (is_memref1 || is_memref2) {
/* if either side is a memref, check for impossible comparisons */ /* if either side is a memref, check for impossible comparisons */
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index); 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; const rc_operand_t* operand2 = &cond->operand2;
uint8_t oper = cond->oper; uint8_t oper = cond->oper;
uint32_t max = rc_max_chain_value(operand1); 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) { if (!is_memref1) {
/* pretend constant was on right side */ /* pretend constant was on right side */
operand2 = operand1;
operand1 = &cond->operand2; operand1 = &cond->operand2;
operand2 = &cond->operand1;
max_val = max; max_val = max;
max = rc_max_value(&cond->operand2); max = rc_max_value(&cond->operand2);