Path: blob/master/dep/rcheevos/src/rc_client_external.c
7513 views
#include "rc_client_external.h"12#include "rc_client_external_versions.h"3#include "rc_client_internal.h"45#include "rc_api_runtime.h"67#ifdef RC_CLIENT_SUPPORTS_EXTERNAL89#define RC_CONVERSION_FILL(obj, obj_type, src_type) memset((uint8_t*)obj + sizeof(src_type), 0, sizeof(obj_type) - sizeof(src_type))1011/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */12#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 6413/* https://media.retroachievements.org/UserPic/TwentyCharUserNameXX.png is 69 with null terminator */14#define RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE 801516typedef struct rc_client_external_conversions_t {17rc_client_user_t user;18rc_client_game_t game;19rc_client_subset_t subsets[4];20rc_client_achievement_t achievements[16];21char user_avatar_url[RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE];22char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];23char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];24char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];25char achievement_badge_locked_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];26uint32_t next_subset_index;27uint32_t next_achievement_index;28} rc_client_external_conversions_t;2930static const char* rc_client_external_build_avatar_url(char buffer[], size_t buffer_size, uint32_t image_type, const char* image_name)31{32rc_api_fetch_image_request_t image_request;33rc_api_request_t request;34int result;3536memset(&image_request, 0, sizeof(image_request));37image_request.image_type = image_type;38image_request.image_name = image_name;3940result = rc_api_init_fetch_image_request(&request, &image_request);41if (result != RC_OK)42return NULL;4344snprintf(buffer, buffer_size, "%s", request.url);45return buffer;46}4748static void rc_client_external_conversions_init(const rc_client_t* client)49{50if (!client->state.external_client_conversions) {51rc_client_t* mutable_client = (rc_client_t*)client;52rc_client_external_conversions_t* conversions = (rc_client_external_conversions_t*)53rc_buffer_alloc(&mutable_client->state.buffer, sizeof(rc_client_external_conversions_t));5455memset(conversions, 0, sizeof(*conversions));5657mutable_client->state.external_client_conversions = conversions;58}59}6061const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* client, const rc_client_user_t* v1_user)62{63rc_client_user_t* converted;6465if (!v1_user)66return NULL;6768rc_client_external_conversions_init(client);6970converted = &client->state.external_client_conversions->user;71memcpy(converted, v1_user, sizeof(v1_rc_client_user_t));72RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);7374converted->avatar_url = rc_client_external_build_avatar_url(75client->state.external_client_conversions->user_avatar_url,76sizeof(client->state.external_client_conversions->user_avatar_url),77RC_IMAGE_TYPE_USER, v1_user->username);7879return converted;80}8182const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* client, const rc_client_game_t* v1_game)83{84rc_client_game_t* converted;8586if (!v1_game)87return NULL;8889rc_client_external_conversions_init(client);9091converted = &client->state.external_client_conversions->game;92memcpy(converted, v1_game, sizeof(v1_rc_client_game_t));93RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);9495converted->badge_url = rc_client_external_build_avatar_url(96client->state.external_client_conversions->game_badge_url,97sizeof(client->state.external_client_conversions->game_badge_url),98RC_IMAGE_TYPE_GAME, v1_game->badge_name);99100return converted;101}102103const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t* client, const rc_client_subset_t* v1_subset)104{105rc_client_subset_t* converted = NULL;106char* badge_url = NULL;107const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]);108uint32_t index;109110if (!v1_subset)111return NULL;112113rc_client_external_conversions_init(client);114115for (index = 0; index < num_subsets; ++index) {116if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) {117converted = &client->state.external_client_conversions->subsets[index];118badge_url = client->state.external_client_conversions->subset_badge_url[index];119break;120}121}122123if (!converted) {124index = client->state.external_client_conversions->next_subset_index;125converted = &client->state.external_client_conversions->subsets[index];126badge_url = client->state.external_client_conversions->subset_badge_url[client->state.external_client_conversions->next_subset_index];127client->state.external_client_conversions->next_subset_index = (index + 1) % num_subsets;128}129130memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));131RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);132133converted->badge_url = rc_client_external_build_avatar_url(badge_url,134sizeof(client->state.external_client_conversions->subset_badge_url[0]),135RC_IMAGE_TYPE_GAME, v1_subset->badge_name);136137return converted;138}139140const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const rc_client_t* client, const rc_client_achievement_t* v1_achievement)141{142rc_client_achievement_t* converted = NULL;143char* badge_url = NULL;144char* badge_locked_url = NULL;145const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]);146uint32_t index;147148if (!v1_achievement)149return NULL;150151rc_client_external_conversions_init(client);152153for (index = 0; index < num_achievements; ++index) {154if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) {155converted = &client->state.external_client_conversions->achievements[index];156badge_url = client->state.external_client_conversions->achievement_badge_url[index];157badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];158break;159}160}161162if (!converted) {163index = client->state.external_client_conversions->next_achievement_index;164converted = &client->state.external_client_conversions->achievements[index];165badge_url = client->state.external_client_conversions->achievement_badge_url[index];166badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];167client->state.external_client_conversions->next_achievement_index = (index + 1) % num_achievements;168}169170memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));171RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);172173converted->badge_url = rc_client_external_build_avatar_url(badge_url,174sizeof(client->state.external_client_conversions->achievement_badge_url[0]),175RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);176converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url,177sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),178RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);179180return converted;181}182183typedef struct rc_client_achievement_list_wrapper_t {184rc_client_achievement_list_info_t info;185rc_client_achievement_list_t* source_list;186rc_client_achievement_t* achievements;187rc_client_achievement_t** achievements_pointers;188char* badge_url_buffer;189} rc_client_achievement_list_wrapper_t;190191static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info)192{193rc_client_achievement_list_wrapper_t* wrapper = (rc_client_achievement_list_wrapper_t*)info;194195if (wrapper->achievements)196free(wrapper->achievements);197if (wrapper->achievements_pointers)198free(wrapper->achievements_pointers);199if (wrapper->info.public_.buckets)200free((void*)wrapper->info.public_.buckets);201if (wrapper->badge_url_buffer)202free(wrapper->badge_url_buffer);203204rc_client_destroy_achievement_list(wrapper->source_list);205206free(wrapper);207}208209rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(const rc_client_t* client, rc_client_achievement_list_t* v1_achievement_list)210{211rc_client_achievement_list_wrapper_t* new_list;212(void)client;213214if (!v1_achievement_list)215return NULL;216217new_list = (rc_client_achievement_list_wrapper_t*)calloc(1, sizeof(*new_list));218if (!new_list)219return NULL;220221new_list->source_list = v1_achievement_list;222new_list->info.destroy_func = rc_client_destroy_achievement_list_wrapper;223224if (v1_achievement_list->num_buckets) {225const v1_rc_client_achievement_bucket_t* src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];226const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets;227rc_client_achievement_bucket_t* bucket;228uint32_t num_achievements = 0;229char* badge_url = NULL;230231new_list->info.public_.buckets = bucket = (rc_client_achievement_bucket_t*)calloc(v1_achievement_list->num_buckets, sizeof(*new_list->info.public_.buckets));232if (!new_list->info.public_.buckets)233return (rc_client_achievement_list_t*)new_list;234235for (; src_bucket < stop_bucket; src_bucket++)236num_achievements += src_bucket->num_achievements;237238if (num_achievements) {239new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->achievements));240new_list->achievements_pointers = (rc_client_achievement_t**)malloc(num_achievements * sizeof(rc_client_achievement_t*));241new_list->badge_url_buffer = badge_url = (char*)malloc(num_achievements * 2 * RC_CLIENT_IMAGE_URL_BUFFER_SIZE);242if (!new_list->achievements || !new_list->achievements_pointers || !new_list->badge_url_buffer)243return (rc_client_achievement_list_t*)new_list;244}245246num_achievements = 0;247src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];248for (; src_bucket < stop_bucket; src_bucket++, bucket++) {249memcpy(bucket, src_bucket, sizeof(*src_bucket));250251if (src_bucket->num_achievements) {252const v1_rc_client_achievement_t** src_achievement = (const v1_rc_client_achievement_t**)src_bucket->achievements;253const v1_rc_client_achievement_t** stop_achievement = src_achievement + src_bucket->num_achievements;254rc_client_achievement_t** achievement = &new_list->achievements_pointers[num_achievements];255256bucket->achievements = (const rc_client_achievement_t**)achievement;257258for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) {259*achievement = &new_list->achievements[num_achievements++];260memcpy(*achievement, *src_achievement, sizeof(**src_achievement));261262(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url,263sizeof(client->state.external_client_conversions->achievement_badge_url[0]),264RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);265badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;266(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url,267sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),268RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);269badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;270}271}272}273274new_list->info.public_.num_buckets = v1_achievement_list->num_buckets;275}276277return (rc_client_achievement_list_t*)new_list;278}279280#endif /* RC_CLIENT_SUPPORTS_EXTERNAL */281282283