Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rcheevos/src/rc_client_external.c
4246 views
1
#include "rc_client_external.h"
2
3
#include "rc_client_external_versions.h"
4
#include "rc_client_internal.h"
5
6
#include "rc_api_runtime.h"
7
8
#define RC_CONVERSION_FILL(obj, obj_type, src_type) memset((uint8_t*)obj + sizeof(src_type), 0, sizeof(obj_type) - sizeof(src_type))
9
10
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
11
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
12
13
typedef struct rc_client_external_conversions_t {
14
rc_client_user_t user;
15
rc_client_game_t game;
16
rc_client_subset_t subsets[4];
17
rc_client_achievement_t achievements[16];
18
char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
19
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
20
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
21
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
22
char achievement_badge_locked_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
23
uint32_t next_subset_index;
24
uint32_t next_achievement_index;
25
} rc_client_external_conversions_t;
26
27
static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)
28
{
29
rc_api_fetch_image_request_t image_request;
30
rc_api_request_t request;
31
int result;
32
33
memset(&image_request, 0, sizeof(image_request));
34
image_request.image_type = image_type;
35
image_request.image_name = image_name;
36
37
result = rc_api_init_fetch_image_request(&request, &image_request);
38
if (result != RC_OK)
39
return NULL;
40
41
strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);
42
return buffer;
43
}
44
45
static void rc_client_external_conversions_init(const rc_client_t* client)
46
{
47
if (!client->state.external_client_conversions) {
48
rc_client_t* mutable_client = (rc_client_t*)client;
49
rc_client_external_conversions_t* conversions = (rc_client_external_conversions_t*)
50
rc_buffer_alloc(&mutable_client->state.buffer, sizeof(rc_client_external_conversions_t));
51
52
memset(conversions, 0, sizeof(*conversions));
53
54
mutable_client->state.external_client_conversions = conversions;
55
}
56
}
57
58
const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* client, const rc_client_user_t* v1_user)
59
{
60
rc_client_user_t* converted;
61
62
if (!v1_user)
63
return NULL;
64
65
rc_client_external_conversions_init(client);
66
67
converted = &client->state.external_client_conversions->user;
68
memcpy(converted, v1_user, sizeof(v1_rc_client_user_t));
69
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
70
71
converted->avatar_url = rc_client_external_build_avatar_url(
72
client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);
73
74
return converted;
75
}
76
77
const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* client, const rc_client_game_t* v1_game)
78
{
79
rc_client_game_t* converted;
80
81
if (!v1_game)
82
return NULL;
83
84
rc_client_external_conversions_init(client);
85
86
converted = &client->state.external_client_conversions->game;
87
memcpy(converted, v1_game, sizeof(v1_rc_client_game_t));
88
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
89
90
converted->badge_url = rc_client_external_build_avatar_url(
91
client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);
92
93
return converted;
94
}
95
96
const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t* client, const rc_client_subset_t* v1_subset)
97
{
98
rc_client_subset_t* converted = NULL;
99
char* badge_url = NULL;
100
const uint32_t num_subsets = sizeof(client->state.external_client_conversions->subsets) / sizeof(client->state.external_client_conversions->subsets[0]);
101
uint32_t index;
102
103
if (!v1_subset)
104
return NULL;
105
106
rc_client_external_conversions_init(client);
107
108
for (index = 0; index < num_subsets; ++index) {
109
if (client->state.external_client_conversions->subsets[index].id == v1_subset->id) {
110
converted = &client->state.external_client_conversions->subsets[index];
111
badge_url = client->state.external_client_conversions->subset_badge_url[index];
112
break;
113
}
114
}
115
116
if (!converted) {
117
index = client->state.external_client_conversions->next_subset_index;
118
converted = &client->state.external_client_conversions->subsets[index];
119
badge_url = client->state.external_client_conversions->subset_badge_url[client->state.external_client_conversions->next_subset_index];
120
client->state.external_client_conversions->next_subset_index = (index + 1) % num_subsets;
121
}
122
123
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
124
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
125
126
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
127
128
return converted;
129
}
130
131
const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const rc_client_t* client, const rc_client_achievement_t* v1_achievement)
132
{
133
rc_client_achievement_t* converted = NULL;
134
char* badge_url = NULL;
135
char* badge_locked_url = NULL;
136
const uint32_t num_achievements = sizeof(client->state.external_client_conversions->achievements) / sizeof(client->state.external_client_conversions->achievements[0]);
137
uint32_t index;
138
139
if (!v1_achievement)
140
return NULL;
141
142
rc_client_external_conversions_init(client);
143
144
for (index = 0; index < num_achievements; ++index) {
145
if (client->state.external_client_conversions->achievements[index].id == v1_achievement->id) {
146
converted = &client->state.external_client_conversions->achievements[index];
147
badge_url = client->state.external_client_conversions->achievement_badge_url[index];
148
badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];
149
break;
150
}
151
}
152
153
if (!converted) {
154
index = client->state.external_client_conversions->next_achievement_index;
155
converted = &client->state.external_client_conversions->achievements[index];
156
badge_url = client->state.external_client_conversions->achievement_badge_url[index];
157
badge_locked_url = client->state.external_client_conversions->achievement_badge_locked_url[index];
158
client->state.external_client_conversions->next_achievement_index = (index + 1) % num_achievements;
159
}
160
161
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
162
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
163
164
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
165
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
166
167
return converted;
168
}
169
170
typedef struct rc_client_achievement_list_wrapper_t {
171
rc_client_achievement_list_info_t info;
172
rc_client_achievement_list_t* source_list;
173
rc_client_achievement_t* achievements;
174
rc_client_achievement_t** achievements_pointers;
175
char* badge_url_buffer;
176
} rc_client_achievement_list_wrapper_t;
177
178
static void rc_client_destroy_achievement_list_wrapper(rc_client_achievement_list_info_t* info)
179
{
180
rc_client_achievement_list_wrapper_t* wrapper = (rc_client_achievement_list_wrapper_t*)info;
181
182
if (wrapper->achievements)
183
free(wrapper->achievements);
184
if (wrapper->achievements_pointers)
185
free(wrapper->achievements_pointers);
186
if (wrapper->info.public_.buckets)
187
free((void*)wrapper->info.public_.buckets);
188
if (wrapper->badge_url_buffer)
189
free(wrapper->badge_url_buffer);
190
191
rc_client_destroy_achievement_list(wrapper->source_list);
192
193
free(wrapper);
194
}
195
196
rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(const rc_client_t* client, rc_client_achievement_list_t* v1_achievement_list)
197
{
198
rc_client_achievement_list_wrapper_t* new_list;
199
(void)client;
200
201
if (!v1_achievement_list)
202
return NULL;
203
204
new_list = (rc_client_achievement_list_wrapper_t*)calloc(1, sizeof(*new_list));
205
if (!new_list)
206
return NULL;
207
208
new_list->source_list = v1_achievement_list;
209
new_list->info.destroy_func = rc_client_destroy_achievement_list_wrapper;
210
211
if (v1_achievement_list->num_buckets) {
212
const v1_rc_client_achievement_bucket_t* src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];
213
const v1_rc_client_achievement_bucket_t* stop_bucket = src_bucket + v1_achievement_list->num_buckets;
214
rc_client_achievement_bucket_t* bucket;
215
uint32_t num_achievements = 0;
216
char* badge_url = NULL;
217
218
new_list->info.public_.buckets = bucket = (rc_client_achievement_bucket_t*)calloc(v1_achievement_list->num_buckets, sizeof(*new_list->info.public_.buckets));
219
if (!new_list->info.public_.buckets)
220
return (rc_client_achievement_list_t*)new_list;
221
222
for (; src_bucket < stop_bucket; src_bucket++)
223
num_achievements += src_bucket->num_achievements;
224
225
if (num_achievements) {
226
new_list->achievements = (rc_client_achievement_t*)calloc(num_achievements, sizeof(*new_list->achievements));
227
new_list->achievements_pointers = (rc_client_achievement_t**)malloc(num_achievements * sizeof(rc_client_achievement_t*));
228
new_list->badge_url_buffer = badge_url = (char*)malloc(num_achievements * 2 * RC_CLIENT_IMAGE_URL_BUFFER_SIZE);
229
if (!new_list->achievements || !new_list->achievements_pointers || !new_list->badge_url_buffer)
230
return (rc_client_achievement_list_t*)new_list;
231
}
232
233
num_achievements = 0;
234
src_bucket = (const v1_rc_client_achievement_bucket_t*)&v1_achievement_list->buckets[0];
235
for (; src_bucket < stop_bucket; src_bucket++, bucket++) {
236
memcpy(bucket, src_bucket, sizeof(*src_bucket));
237
238
if (src_bucket->num_achievements) {
239
const v1_rc_client_achievement_t** src_achievement = (const v1_rc_client_achievement_t**)src_bucket->achievements;
240
const v1_rc_client_achievement_t** stop_achievement = src_achievement + src_bucket->num_achievements;
241
rc_client_achievement_t** achievement = &new_list->achievements_pointers[num_achievements];
242
243
bucket->achievements = (const rc_client_achievement_t**)achievement;
244
245
for (; src_achievement < stop_achievement; ++src_achievement, ++achievement) {
246
*achievement = &new_list->achievements[num_achievements++];
247
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
248
249
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
250
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
251
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
252
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
253
}
254
}
255
}
256
257
new_list->info.public_.num_buckets = v1_achievement_list->num_buckets;
258
}
259
260
return (rc_client_achievement_list_t*)new_list;
261
}
262
263