diff --git a/dep/rcheevos/include/rc_api_info.h b/dep/rcheevos/include/rc_api_info.h index ee5121df0..0051fe23e 100644 --- a/dep/rcheevos/include/rc_api_info.h +++ b/dep/rcheevos/include/rc_api_info.h @@ -211,6 +211,8 @@ typedef struct rc_api_game_title_entry_t { const char* title; /* The image name for the game badge */ const char* image_name; + /* The URL for the game badge image */ + const char* image_url; } rc_api_game_title_entry_t; diff --git a/dep/rcheevos/include/rc_client.h b/dep/rcheevos/include/rc_client.h index ed43baccd..6dff3729b 100644 --- a/dep/rcheevos/include/rc_client.h +++ b/dep/rcheevos/include/rc_client.h @@ -369,6 +369,22 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary); +typedef struct rc_client_subset_list_t { + const rc_client_subset_t** subsets; + uint32_t num_subsets; +} rc_client_subset_list_t; + +/** + * Creates a list of subsets for the currently loaded game. + * Returns an allocated list that must be free'd by calling rc_client_destroy_subset_list. + */ +RC_EXPORT rc_client_subset_list_t* RC_CCONV rc_client_create_subset_list(rc_client_t* client); + +/** + * Destroys a list allocated by rc_client_create_subset_list_list. + */ +RC_EXPORT void RC_CCONV rc_client_destroy_subset_list(rc_client_subset_list_t* list); + /*****************************************************************************\ | Fetch Game Hashes | \*****************************************************************************/ @@ -411,6 +427,7 @@ typedef struct rc_client_game_title_entry_t { uint32_t game_id; const char* title; char badge_name[16]; + const char* badge_url; } rc_client_game_title_entry_t; typedef struct rc_client_game_title_list_t { @@ -543,7 +560,7 @@ enum { RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping); /** - * Destroys a list allocated by rc_client_get_achievement_list. + * Destroys a list allocated by rc_client_create_achievement_list. */ RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list); diff --git a/dep/rcheevos/src/rapi/rc_api_info.c b/dep/rcheevos/src/rapi/rc_api_info.c index 84423ae0e..5ab970928 100644 --- a/dep/rcheevos/src/rapi/rc_api_info.c +++ b/dep/rcheevos/src/rapi/rc_api_info.c @@ -448,7 +448,8 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re rc_json_field_t entry_fields[] = { RC_JSON_NEW_FIELD("ID"), RC_JSON_NEW_FIELD("Title"), - RC_JSON_NEW_FIELD("ImageIcon") + RC_JSON_NEW_FIELD("ImageIcon"), + RC_JSON_NEW_FIELD("ImageUrl") }; memset(response, 0, sizeof(*response)); @@ -482,6 +483,10 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon")) return RC_MISSING_VALUE; + rc_json_get_optional_string(&entry->image_url, &response->response, &entry_fields[3], "ImageUrl", ""); + if (!entry->image_url[0]) + entry->image_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_GAME, entry->image_name); + ++entry; } } diff --git a/dep/rcheevos/src/rc_client.c b/dep/rcheevos/src/rc_client.c index e0a27c22a..9532093bd 100644 --- a/dep/rcheevos/src/rc_client.c +++ b/dep/rcheevos/src/rc_client.c @@ -3493,6 +3493,58 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_ return NULL; } +rc_client_subset_list_t* rc_client_create_subset_list(rc_client_t* client) +{ + rc_client_subset_list_info_t* list; + const rc_client_subset_info_t* subset; + const rc_client_subset_t** subset_ptr; + const uint32_t list_size = RC_ALIGN(sizeof(*list)); + uint32_t num_subsets = 0; + + if (!client) + return (rc_client_subset_list_t*)calloc(1, list_size); + +#ifdef RC_CLIENT_SUPPORTS_EXTERNAL + if (client->state.external_client && client->state.external_client->create_subset_list) + return (rc_client_subset_list_t*)client->state.external_client->create_subset_list(); +#endif + + if (!client->game) + return (rc_client_subset_list_t*)calloc(1, list_size); + + rc_mutex_lock(&client->state.mutex); + + subset = client->game->subsets; + for (; subset; subset = subset->next) { + if (subset->active) + num_subsets++; + } + + list = (rc_client_subset_list_info_t*)malloc(list_size + num_subsets * sizeof(rc_client_subset_t*)); + list->public_.subsets = subset_ptr = (const rc_client_subset_t**)((uint8_t*)list + list_size); + + subset = client->game->subsets; + for (; subset; subset = subset->next) { + if (subset->active) + *subset_ptr++ = &subset->public_; + } + + rc_mutex_unlock(&client->state.mutex); + + list->destroy_func = NULL; + list->public_.num_subsets = (uint32_t)(subset_ptr - list->public_.subsets); + return &list->public_; +} + +void rc_client_destroy_subset_list(rc_client_subset_list_t* list) +{ + rc_client_subset_list_info_t* info = (rc_client_subset_list_info_t*)list; + if (info->destroy_func) + info->destroy_func(info); + else + free(list); +} + /* ===== Fetch Game Hashes ===== */ typedef struct rc_client_fetch_hash_library_callback_data_t { @@ -3649,6 +3701,8 @@ static void rc_client_fetch_game_titles_callback(const rc_api_server_response_t* for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src) { if (src->title) strings_size += strlen(src->title) + 1; + if (src->image_url) + strings_size += strlen(src->image_url) + 1; } list_size = sizeof(*list) + sizeof(rc_client_game_title_entry_t) * titles_response.num_entries + strings_size; @@ -3678,6 +3732,16 @@ static void rc_client_fetch_game_titles_callback(const rc_api_server_response_t* snprintf(entry->badge_name, sizeof(entry->badge_name), "%s", src->image_name); else entry->badge_name[0] = '\0'; + + if (src->image_url) { + const size_t len = strlen(src->image_url) + 1; + entry->badge_url = strings; + memcpy(strings, src->image_url, len); + strings += len; + } + else { + entry->badge_url = NULL; + } } list->num_entries = titles_response.num_entries; diff --git a/dep/rcheevos/src/rc_client_external.h b/dep/rcheevos/src/rc_client_external.h index fcf83ec76..d73e581b8 100644 --- a/dep/rcheevos/src/rc_client_external.h +++ b/dep/rcheevos/src/rc_client_external.h @@ -61,6 +61,11 @@ typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_lead typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client, uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata); +/* NOTE: rc_client_external_create_subset_list_func_t returns an internal wrapper structure which contains the public list + * and a destructor function. */ +struct rc_client_subset_list_info_t; +typedef struct rc_client_subset_list_info_t* (RC_CCONV* rc_client_external_create_subset_list_func_t)(); + typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void); typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer, size_t buffer_size); @@ -144,6 +149,9 @@ typedef struct rc_client_external_t rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5; rc_client_external_get_user_subset_summary_func_t get_user_subset_summary; + /* VERSION 6 */ + rc_client_external_create_subset_list_func_t create_subset_list; + } rc_client_external_t; #define RC_CLIENT_EXTERNAL_VERSION 5 diff --git a/dep/rcheevos/src/rc_client_internal.h b/dep/rcheevos/src/rc_client_internal.h index 2311c58cb..fad288d62 100644 --- a/dep/rcheevos/src/rc_client_internal.h +++ b/dep/rcheevos/src/rc_client_internal.h @@ -222,6 +222,14 @@ typedef struct rc_client_subset_info_t { uint8_t pending_events; } rc_client_subset_info_t; +struct rc_client_subset_list_info_t; +typedef void (RC_CCONV* rc_client_destroy_subset_list_func_t)(struct rc_client_subset_list_info_t* list); + +typedef struct rc_client_subset_list_info_t { + rc_client_subset_list_t public_; + rc_client_destroy_subset_list_func_t destroy_func; +} rc_client_subset_list_info_t; + /*****************************************************************************\ | Game | \*****************************************************************************/