From 481fba357cc35bec99404f9b16ef002a1a3775f4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 16 May 2025 23:12:22 +1000 Subject: [PATCH] dep/rcheevos: Sync with branch --- dep/rcheevos/include/rc_client.h | 90 ++++----- dep/rcheevos/src/rapi/rc_api_info.c | 3 +- dep/rcheevos/src/rc_client.c | 303 ++++++++++++---------------- 3 files changed, 171 insertions(+), 225 deletions(-) diff --git a/dep/rcheevos/include/rc_client.h b/dep/rcheevos/include/rc_client.h index b09fd3264..2ec438c02 100644 --- a/dep/rcheevos/include/rc_client.h +++ b/dep/rcheevos/include/rc_client.h @@ -224,6 +224,39 @@ typedef struct rc_client_user_game_summary_t { */ RC_EXPORT void RC_CCONV rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_game_summary_t* summary); +typedef struct rc_client_all_user_progress_entry_t { + uint32_t game_id; + uint32_t num_achievements; + uint32_t num_unlocked_achievements; + uint32_t num_unlocked_achievements_hardcore; +} rc_client_all_user_progress_entry_t; + +typedef struct rc_client_all_user_progress_t { + rc_client_all_user_progress_entry_t* entries; + uint32_t num_entries; +} rc_client_all_user_progress_t; + +/** + * Callback that is fired when an all progress query completes. list may be null if the query failed. + */ +typedef void(RC_CCONV* rc_client_fetch_all_user_progress_callback_t)(int result, const char* error_message, + rc_client_all_user_progress_t* list, + rc_client_t* client, void* callback_userdata); + +/** + * Starts an asynchronous request for all progress for the given console. + * This query returns the total number of achievements for all games tracked by this console, as well as + * the user's achievement unlock count for both softcore and hardcore modes. + */ +RC_EXPORT rc_client_async_handle_t* RC_CCONV +rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id, + rc_client_fetch_all_user_progress_callback_t callback, void* callback_userdata); + +/** + * Destroys a previously-allocated result from the rc_client_begin_fetch_all_progress_list() callback. + */ +RC_EXPORT void RC_CCONV rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list); + /*****************************************************************************\ | Game | \*****************************************************************************/ @@ -326,19 +359,13 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client | Fetch Game Hashes | \*****************************************************************************/ -typedef struct rc_client_hash_library_entry_t -{ - /* The hash for the game */ - const char* hash; - /* The unique identifier of the game */ +typedef struct rc_client_hash_library_entry_t { + char hash[33]; uint32_t game_id; } rc_client_hash_library_entry_t; -typedef struct rc_client_hash_library_t -{ - /* An array of entries, one per game */ +typedef struct rc_client_hash_library_t { rc_client_hash_library_entry_t* entries; - /* The number of items in the entries array */ uint32_t num_entries; } rc_client_hash_library_t; @@ -362,51 +389,6 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_hash_library( */ RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list); -/*****************************************************************************\ -| Game Info | -\*****************************************************************************/ - -typedef struct rc_client_all_user_progress_entry_t -{ - /* The unique identifier of the game */ - uint32_t game_id; - /* The total number of achievements for this game */ - uint32_t num_achievements; - /* The total number of unlocked achievements for this game in softcore mode */ - uint32_t num_unlocked_achievements; - /* The total number of unlocked achievements for this game in hardcore mode */ - uint32_t num_unlocked_achievements_hardcore; -} rc_client_all_user_progress_entry_t; - -typedef struct rc_client_all_user_progress_t -{ - /* An array of entries, one per game */ - rc_client_all_user_progress_entry_t* entries; - /* The number of items in the entries array */ - uint32_t num_entries; -} rc_client_all_user_progress_t; - -/** - * Callback that is fired when an all progress query completes. list may be null if the query failed. - */ -typedef void(RC_CCONV* rc_client_fetch_all_user_progress_callback_t)(int result, const char* error_message, - rc_client_all_user_progress_t* list, - rc_client_t* client, void* callback_userdata); - -/** - * Starts an asynchronous request for all progress for the given console. - * This query returns the total number of achievements for all games tracked by this console, as well as - * the user's achievement unlock count for both softcore and hardcore modes. - */ -RC_EXPORT rc_client_async_handle_t* RC_CCONV -rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id, - rc_client_fetch_all_user_progress_callback_t callback, void* callback_userdata); - -/** - * Destroys a previously-allocated result from the rc_client_begin_fetch_all_progress_list() callback. - */ -RC_EXPORT void RC_CCONV rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list); - /*****************************************************************************\ | Achievements | \*****************************************************************************/ diff --git a/dep/rcheevos/src/rapi/rc_api_info.c b/dep/rcheevos/src/rapi/rc_api_info.c index eb28c4d75..f9d4642b7 100644 --- a/dep/rcheevos/src/rapi/rc_api_info.c +++ b/dep/rcheevos/src/rapi/rc_api_info.c @@ -511,7 +511,8 @@ int rc_api_init_fetch_hash_library_request_hosted(rc_api_request_t* request, /* note: unauthenticated request */ rc_url_builder_init(&builder, &request->buffer, 48); rc_url_builder_append_str_param(&builder, "r", "hashlibrary"); - rc_url_builder_append_unum_param(&builder, "c", api_params->console_id); + if (api_params->console_id != 0) + rc_url_builder_append_unum_param(&builder, "c", api_params->console_id); request->post_data = rc_url_builder_finalize(&builder); request->content_type = RC_CONTENT_TYPE_URLENCODED; diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index d45b45c28..291b1af86 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -978,6 +978,126 @@ void rc_client_get_user_game_summary(const rc_client_t* client, rc_client_user_g rc_mutex_unlock((rc_mutex_t*)&client->state.mutex); /* remove const cast for mutex access */ } +typedef struct rc_client_fetch_all_user_progress_callback_data_t { + rc_client_t* client; + rc_client_fetch_all_user_progress_callback_t callback; + void* callback_userdata; + uint32_t console_id; + rc_client_async_handle_t async_handle; +} rc_client_fetch_all_user_progress_callback_data_t; + +static void rc_client_fetch_all_user_progress_callback(const rc_api_server_response_t* server_response, + void* callback_data) +{ + rc_client_fetch_all_user_progress_callback_data_t* ap_callback_data = + (rc_client_fetch_all_user_progress_callback_data_t*)callback_data; + rc_client_t* client = ap_callback_data->client; + rc_api_fetch_all_user_progress_response_t ap_response; + const char* error_message; + int result; + + result = rc_client_end_async(client, &ap_callback_data->async_handle); + if (result) { + if (result != RC_CLIENT_ASYNC_DESTROYED) + RC_CLIENT_LOG_VERBOSE(client, "Fetch all progress aborted"); + + free(ap_callback_data); + return; + } + + result = rc_api_process_fetch_all_user_progress_server_response(&ap_response, server_response); + error_message = rc_client_server_error_message(&result, server_response->http_status_code, &ap_response.response); + if (error_message) { + RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch all progress for console %u failed: %s", ap_callback_data->console_id, + error_message); + ap_callback_data->callback(result, error_message, NULL, client, ap_callback_data->callback_userdata); + } else { + rc_client_all_user_progress_t* list; + const size_t list_size = sizeof(*list) + sizeof(rc_client_all_user_progress_entry_t) * ap_response.num_entries; + + list = (rc_client_all_user_progress_t*)malloc(list_size); + if (!list) { + ap_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, + ap_callback_data->callback_userdata); + } else { + rc_client_all_user_progress_entry_t* entry = list->entries = + (rc_client_all_user_progress_entry_t*)((uint8_t*)list + sizeof(*list)); + const rc_api_all_user_progress_entry_t* hlentry = ap_response.entries; + const rc_api_all_user_progress_entry_t* stop = hlentry + ap_response.num_entries; + + for (; hlentry < stop; ++hlentry, ++entry) + { + entry->game_id = hlentry->game_id; + entry->num_achievements = hlentry->num_achievements; + entry->num_unlocked_achievements = hlentry->num_unlocked_achievements; + entry->num_unlocked_achievements_hardcore = hlentry->num_unlocked_achievements_hardcore; + } + + list->num_entries = ap_response.num_entries; + + ap_callback_data->callback(RC_OK, NULL, list, client, ap_callback_data->callback_userdata); + } + } + + rc_api_destroy_fetch_all_user_progress_response(&ap_response); + free(ap_callback_data); +} + +rc_client_async_handle_t* rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id, + rc_client_fetch_all_user_progress_callback_t callback, + void* callback_userdata) +{ + rc_api_fetch_all_user_progress_request_t api_params; + rc_client_fetch_all_user_progress_callback_data_t* callback_data; + rc_client_async_handle_t* async_handle; + rc_api_request_t request; + int result; + const char* error_message; + + if (!client) { + callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata); + return NULL; + } else if (client->state.user != RC_CLIENT_USER_STATE_LOGGED_IN) { + callback(RC_INVALID_STATE, "client must be logged in", NULL, client, callback_userdata); + return NULL; + } + + api_params.username = client->user.username; + api_params.api_token = client->user.token; + api_params.console_id = console_id; + + result = rc_api_init_fetch_all_user_progress_request_hosted(&request, &api_params, &client->state.host); + + if (result != RC_OK) { + error_message = rc_error_str(result); + callback(result, error_message, NULL, client, callback_userdata); + return NULL; + } + + callback_data = (rc_client_fetch_all_user_progress_callback_data_t*)calloc(1, sizeof(*callback_data)); + if (!callback_data) { + callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata); + return NULL; + } + + callback_data->client = client; + callback_data->callback = callback; + callback_data->callback_userdata = callback_userdata; + callback_data->console_id = console_id; + + async_handle = &callback_data->async_handle; + rc_client_begin_async(client, async_handle); + client->callbacks.server_call(&request, rc_client_fetch_all_user_progress_callback, callback_data, client); + rc_api_destroy_request(&request); + + return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL; +} + +void rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list) +{ + free(list); +} + /* ===== Game ===== */ static void rc_client_free_game(rc_client_game_info_t* game) @@ -3253,10 +3373,9 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_ return NULL; } -/* ===== Game Info ===== */ +/* ===== Fetch Game Hashes ===== */ -typedef struct rc_client_fetch_hash_library_callback_data_t -{ +typedef struct rc_client_fetch_hash_library_callback_data_t { rc_client_t* client; rc_client_fetch_hash_library_callback_t callback; void* callback_userdata; @@ -3274,8 +3393,7 @@ static void rc_client_fetch_hash_library_callback(const rc_api_server_response_t int result; result = rc_client_end_async(client, &hashlib_callback_data->async_handle); - if (result) - { + if (result) { if (result != RC_CLIENT_ASYNC_DESTROYED) RC_CLIENT_LOG_VERBOSE(client, "Fetch hash library aborted"); @@ -3286,43 +3404,26 @@ static void rc_client_fetch_hash_library_callback(const rc_api_server_response_t result = rc_api_process_fetch_hash_library_server_response(&hashlib_response, server_response); error_message = rc_client_server_error_message(&result, server_response->http_status_code, &hashlib_response.response); - if (error_message) - { + if (error_message) { RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch hash library for console %u failed: %s", hashlib_callback_data->console_id, error_message); hashlib_callback_data->callback(result, error_message, NULL, client, hashlib_callback_data->callback_userdata); - } - else - { + } else { rc_client_hash_library_t* list; const size_t list_size = sizeof(*list) + sizeof(rc_client_hash_library_entry_t) * hashlib_response.num_entries; - size_t needed_size = list_size; - uint32_t i; - - for (i = 0; i < hashlib_response.num_entries; i++) - needed_size += strlen(hashlib_response.entries[i].hash) + 1; - - list = (rc_client_hash_library_t*)malloc(needed_size); - if (!list) - { + list = (rc_client_hash_library_t*)malloc(list_size); + if (!list) { hashlib_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, hashlib_callback_data->callback_userdata); - } - else - { + } else { rc_client_hash_library_entry_t* entry = list->entries = (rc_client_hash_library_entry_t*)((uint8_t*)list + sizeof(*list)); - char* hash = (char*)((uint8_t*)list + list_size); const rc_api_hash_library_entry_t* hlentry = hashlib_response.entries; const rc_api_hash_library_entry_t* stop = hlentry + hashlib_response.num_entries; - for (; hlentry < stop; ++hlentry, ++entry) - { - const size_t len = strlen(hlentry->hash) + 1; - entry->hash = hash; + for (; hlentry < stop; ++hlentry, ++entry) { + snprintf(entry->hash, sizeof(entry->hash), "%s", hlentry->hash); entry->game_id = hlentry->game_id; - memcpy(hash, hlentry->hash, len); - hash += len; } list->num_entries = hashlib_response.num_entries; @@ -3346,8 +3447,7 @@ rc_client_async_handle_t* rc_client_begin_fetch_hash_library(rc_client_t* client int result; const char* error_message; - if (!client) - { + if (!client) { callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata); return NULL; } @@ -3355,16 +3455,14 @@ rc_client_async_handle_t* rc_client_begin_fetch_hash_library(rc_client_t* client api_params.console_id = console_id; result = rc_api_init_fetch_hash_library_request_hosted(&request, &api_params, &client->state.host); - if (result != RC_OK) - { + if (result != RC_OK) { error_message = rc_error_str(result); callback(result, error_message, NULL, client, callback_userdata); return NULL; } callback_data = (rc_client_fetch_hash_library_callback_data_t*)calloc(1, sizeof(*callback_data)); - if (!callback_data) - { + if (!callback_data) { callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata); return NULL; } @@ -3387,141 +3485,6 @@ void rc_client_destroy_hash_library(rc_client_hash_library_t* list) free(list); } -/* ===== Fetch Game Hashes ===== */ - -typedef struct rc_client_fetch_all_user_progress_callback_data_t -{ - rc_client_t* client; - rc_client_fetch_all_user_progress_callback_t callback; - void* callback_userdata; - uint32_t console_id; - rc_client_async_handle_t async_handle; -} rc_client_fetch_all_user_progress_callback_data_t; - -static void rc_client_fetch_all_user_progress_callback(const rc_api_server_response_t* server_response, - void* callback_data) -{ - rc_client_fetch_all_user_progress_callback_data_t* ap_callback_data = - (rc_client_fetch_all_user_progress_callback_data_t*)callback_data; - rc_client_t* client = ap_callback_data->client; - rc_api_fetch_all_user_progress_response_t ap_response; - const char* error_message; - int result; - - result = rc_client_end_async(client, &ap_callback_data->async_handle); - if (result) - { - if (result != RC_CLIENT_ASYNC_DESTROYED) - RC_CLIENT_LOG_VERBOSE(client, "Fetch all progress aborted"); - - free(ap_callback_data); - return; - } - - result = rc_api_process_fetch_all_user_progress_server_response(&ap_response, server_response); - error_message = rc_client_server_error_message(&result, server_response->http_status_code, &ap_response.response); - if (error_message) - { - RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch all progress for console %u failed: %s", ap_callback_data->console_id, - error_message); - ap_callback_data->callback(result, error_message, NULL, client, ap_callback_data->callback_userdata); - } - else - { - rc_client_all_user_progress_t* list; - const size_t list_size = sizeof(*list) + sizeof(rc_client_all_user_progress_entry_t) * ap_response.num_entries; - - list = (rc_client_all_user_progress_t*)malloc(list_size); - if (!list) - { - ap_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, - ap_callback_data->callback_userdata); - } - else - { - rc_client_all_user_progress_entry_t* entry = list->entries = - (rc_client_all_user_progress_entry_t*)((uint8_t*)list + sizeof(*list)); - const rc_api_all_user_progress_entry_t* hlentry = ap_response.entries; - const rc_api_all_user_progress_entry_t* stop = hlentry + ap_response.num_entries; - - for (; hlentry < stop; ++hlentry, ++entry) - { - entry->game_id = hlentry->game_id; - entry->num_achievements = hlentry->num_achievements; - entry->num_unlocked_achievements = hlentry->num_unlocked_achievements; - entry->num_unlocked_achievements_hardcore = hlentry->num_unlocked_achievements_hardcore; - } - - list->num_entries = ap_response.num_entries; - - ap_callback_data->callback(RC_OK, NULL, list, client, ap_callback_data->callback_userdata); - } - } - - rc_api_destroy_fetch_all_user_progress_response(&ap_response); - free(ap_callback_data); -} - -rc_client_async_handle_t* rc_client_begin_fetch_all_user_progress(rc_client_t* client, uint32_t console_id, - rc_client_fetch_all_user_progress_callback_t callback, - void* callback_userdata) -{ - rc_api_fetch_all_user_progress_request_t api_params; - rc_client_fetch_all_user_progress_callback_data_t* callback_data; - rc_client_async_handle_t* async_handle; - rc_api_request_t request; - int result; - const char* error_message; - - if (!client) - { - callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata); - return NULL; - } - else if (client->state.user != RC_CLIENT_USER_STATE_LOGGED_IN) - { - callback(RC_INVALID_STATE, "client must be logged in", NULL, client, callback_userdata); - return NULL; - } - - api_params.username = client->user.username; - api_params.api_token = client->user.token; - api_params.console_id = console_id; - - result = rc_api_init_fetch_all_user_progress_request_hosted(&request, &api_params, &client->state.host); - - if (result != RC_OK) - { - error_message = rc_error_str(result); - callback(result, error_message, NULL, client, callback_userdata); - return NULL; - } - - callback_data = (rc_client_fetch_all_user_progress_callback_data_t*)calloc(1, sizeof(*callback_data)); - if (!callback_data) - { - callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata); - return NULL; - } - - callback_data->client = client; - callback_data->callback = callback; - callback_data->callback_userdata = callback_userdata; - callback_data->console_id = console_id; - - async_handle = &callback_data->async_handle; - rc_client_begin_async(client, async_handle); - client->callbacks.server_call(&request, rc_client_fetch_all_user_progress_callback, callback_data, client); - rc_api_destroy_request(&request); - - return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL; -} - -void rc_client_destroy_all_user_progress(rc_client_all_user_progress_t* list) -{ - free(list); -} - /* ===== Achievements ===== */ static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)