Path: blob/master/dep/rcheevos/src/rc_client_external.c
4246 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#define RC_CONVERSION_FILL(obj, obj_type, src_type) memset((uint8_t*)obj + sizeof(src_type), 0, sizeof(obj_type) - sizeof(src_type))89/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */10#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 641112typedef struct rc_client_external_conversions_t {13rc_client_user_t user;14rc_client_game_t game;15rc_client_subset_t subsets[4];16rc_client_achievement_t achievements[16];17char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];18char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];19char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];20char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];21char achievement_badge_locked_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];22uint32_t next_subset_index;23uint32_t next_achievement_index;24} rc_client_external_conversions_t;2526static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)27{28rc_api_fetch_image_request_t image_request;29rc_api_request_t request;30int result;3132memset(&image_request, 0, sizeof(image_request));33image_request.image_type = image_type;34image_request.image_name = image_name;3536result = rc_api_init_fetch_image_request(&request, &image_request);37if (result != RC_OK)38return NULL;3940strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);41return buffer;42}4344static void rc_client_external_conversions_init(const rc_client_t* client)45{46if (!client->state.external_client_conversions) {47rc_client_t* mutable_client = (rc_client_t*)client;48rc_client_external_conversions_t* conversions = (rc_client_external_conversions_t*)49rc_buffer_alloc(&mutable_client->state.buffer, sizeof(rc_client_external_conversions_t));5051memset(conversions, 0, sizeof(*conversions));5253mutable_client->state.external_client_conversions = conversions;54}55}5657const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* client, const rc_client_user_t* v1_user)58{59rc_client_user_t* converted;6061if (!v1_user)62return NULL;6364rc_client_external_conversions_init(client);6566converted = &client->state.external_client_conversions->user;67memcpy(converted, v1_user, sizeof(v1_rc_client_user_t));68RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);6970converted->avatar_url = rc_client_external_build_avatar_url(71client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);7273return converted;74}7576const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* client, const rc_client_game_t* v1_game)77{78rc_client_game_t* converted;7980if (!v1_game)81return NULL;8283rc_client_external_conversions_init(client);8485converted = &client->state.external_client_conversions->game;86memcpy(converted, v1_game, sizeof(v1_rc_client_game_t));87RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);8889converted->badge_url = rc_client_external_build_avatar_url(90client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);9192return converted;93}9495const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t* client, const rc_client_subset_t* v1_subset)96{97rc_client_subset_t* converted = NULL;98char* badge_url = NULL;99const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]);100uint32_t index;101102if (!v1_subset)103return NULL;104105rc_client_external_conversions_init(client);106107for (index = 0; index < num_subsets; ++index) {108if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) {109converted = &client->state.external_client_conversions->subsets[index];110badge_url = client->state.external_client_conversions->subset_badge_url[index];111break;112}113}114115if (!converted) {116index = client->state.external_client_conversions->next_subset_index;117converted = &client->state.external_client_conversions->subsets[index];118badge_url = client->state.external_client_conversions->subset_badge_url[client->state.external_client_conversions->next_subset_index];119client->state.external_client_conversions->next_subset_index = (index + 1) % num_subsets;120}121122memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));123RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);124125converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);126127return converted;128}129130const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const rc_client_t* client, const rc_client_achievement_t* v1_achievement)131{132rc_client_achievement_t* converted = NULL;133char* badge_url = NULL;134char* badge_locked_url = NULL;135const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]);136uint32_t index;137138if (!v1_achievement)139return NULL;140141rc_client_external_conversions_init(client);142143for (index = 0; index < num_achievements; ++index) {144if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) {145converted = &client->state.external_client_conversions->achievements[index];146badge_url = client->state.external_client_conversions->achievement_badge_url[index];147badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];148break;149}150}151152if (!converted) {153index = client->state.external_client_conversions->next_achievement_index;154converted = &client->state.external_client_conversions->achievements[index];155badge_url = client->state.external_client_conversions->achievement_badge_url[index];156badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];157client->state.external_client_conversions->next_achievement_index = (index + 1) % num_achievements;158}159160memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));161RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);162163converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);164converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);165166return converted;167}168169typedef struct rc_client_achievement_list_wrapper_t {170rc_client_achievement_list_info_t info;171rc_client_achievement_list_t* source_list;172rc_client_achievement_t* achievements;173rc_client_achievement_t** achievements_pointers;174char* badge_url_buffer;175} rc_client_achievement_list_wrapper_t;176177static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info)178{179rc_client_achievement_list_wrapper_t* wrapper = (rc_client_achievement_list_wrapper_t*)info;180181if (wrapper->achievements)182free(wrapper->achievements);183if (wrapper->achievements_pointers)184free(wrapper->achievements_pointers);185if (wrapper->info.public_.buckets)186free((void*)wrapper->info.public_.buckets);187if (wrapper->badge_url_buffer)188free(wrapper->badge_url_buffer);189190rc_client_destroy_achievement_list(wrapper->source_list);191192free(wrapper);193}194195rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(const rc_client_t* client, rc_client_achievement_list_t* v1_achievement_list)196{197rc_client_achievement_list_wrapper_t* new_list;198(void)client;199200if (!v1_achievement_list)201return NULL;202203new_list = (rc_client_achievement_list_wrapper_t*)calloc(1, sizeof(*new_list));204if (!new_list)205return NULL;206207new_list->source_list = v1_achievement_list;208new_list->info.destroy_func = rc_client_destroy_achievement_list_wrapper;209210if (v1_achievement_list->num_buckets) {211const v1_rc_client_achievement_bucket_t* src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];212const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets;213rc_client_achievement_bucket_t* bucket;214uint32_t num_achievements = 0;215char* badge_url = NULL;216217new_list->info.public_.buckets = bucket = (rc_client_achievement_bucket_t*)calloc(v1_achievement_list->num_buckets, sizeof(*new_list->info.public_.buckets));218if (!new_list->info.public_.buckets)219return (rc_client_achievement_list_t*)new_list;220221for (; src_bucket < stop_bucket; src_bucket++)222num_achievements += src_bucket->num_achievements;223224if (num_achievements) {225new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->achievements));226new_list->achievements_pointers = (rc_client_achievement_t**)malloc(num_achievements * sizeof(rc_client_achievement_t*));227new_list->badge_url_buffer = badge_url = (char*)malloc(num_achievements * 2 * RC_CLIENT_IMAGE_URL_BUFFER_SIZE);228if (!new_list->achievements || !new_list->achievements_pointers || !new_list->badge_url_buffer)229return (rc_client_achievement_list_t*)new_list;230}231232num_achievements = 0;233src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];234for (; src_bucket < stop_bucket; src_bucket++, bucket++) {235memcpy(bucket, src_bucket, sizeof(*src_bucket));236237if (src_bucket->num_achievements) {238const v1_rc_client_achievement_t** src_achievement = (const v1_rc_client_achievement_t**)src_bucket->achievements;239const v1_rc_client_achievement_t** stop_achievement = src_achievement + src_bucket->num_achievements;240rc_client_achievement_t** achievement = &new_list->achievements_pointers[num_achievements];241242bucket->achievements = (const rc_client_achievement_t**)achievement;243244for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) {245*achievement = &new_list->achievements[num_achievements++];246memcpy(*achievement, *src_achievement, sizeof(**src_achievement));247248(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);249badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;250(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);251badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;252}253}254}255256new_list->info.public_.num_buckets = v1_achievement_list->num_buckets;257}258259return (rc_client_achievement_list_t*)new_list;260}261262263