Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/rcheevos/src/rapi/rc_api_info.c
4253 views
1
#include "rc_api_info.h"
2
#include "rc_api_common.h"
3
#include "rc_api_runtime.h"
4
5
#include "rc_runtime_types.h"
6
7
#include "../rc_compat.h"
8
9
#include <stdlib.h>
10
#include <string.h>
11
12
/* --- Fetch Achievement Info --- */
13
14
int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const rc_api_fetch_achievement_info_request_t* api_params) {
15
return rc_api_init_fetch_achievement_info_request_hosted(request, api_params, &g_host);
16
}
17
18
int rc_api_init_fetch_achievement_info_request_hosted(rc_api_request_t* request,
19
const rc_api_fetch_achievement_info_request_t* api_params,
20
const rc_api_host_t* host) {
21
rc_api_url_builder_t builder;
22
23
rc_api_url_build_dorequest_url(request, host);
24
25
if (api_params->achievement_id == 0)
26
return RC_INVALID_STATE;
27
28
rc_url_builder_init(&builder, &request->buffer, 48);
29
if (rc_api_url_build_dorequest(&builder, "achievementwondata", api_params->username, api_params->api_token)) {
30
rc_url_builder_append_unum_param(&builder, "a", api_params->achievement_id);
31
32
if (api_params->friends_only)
33
rc_url_builder_append_unum_param(&builder, "f", 1);
34
if (api_params->first_entry > 1)
35
rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */
36
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
37
38
request->post_data = rc_url_builder_finalize(&builder);
39
request->content_type = RC_CONTENT_TYPE_URLENCODED;
40
}
41
42
return builder.result;
43
}
44
45
int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response) {
46
rc_api_server_response_t response_obj;
47
48
memset(&response_obj, 0, sizeof(response_obj));
49
response_obj.body = server_response;
50
response_obj.body_length = rc_json_get_object_string_length(server_response);
51
52
return rc_api_process_fetch_achievement_info_server_response(response, &response_obj);
53
}
54
55
int rc_api_process_fetch_achievement_info_server_response(rc_api_fetch_achievement_info_response_t* response, const rc_api_server_response_t* server_response) {
56
rc_api_achievement_awarded_entry_t* entry;
57
rc_json_field_t array_field;
58
rc_json_iterator_t iterator;
59
uint32_t timet;
60
int result;
61
62
rc_json_field_t fields[] = {
63
RC_JSON_NEW_FIELD("Success"),
64
RC_JSON_NEW_FIELD("Error"),
65
RC_JSON_NEW_FIELD("AchievementID"),
66
RC_JSON_NEW_FIELD("Response")
67
/* unused fields
68
RC_JSON_NEW_FIELD("Offset"),
69
RC_JSON_NEW_FIELD("Count"),
70
RC_JSON_NEW_FIELD("FriendsOnly")
71
* unused fields */
72
};
73
74
rc_json_field_t response_fields[] = {
75
RC_JSON_NEW_FIELD("NumEarned"),
76
RC_JSON_NEW_FIELD("TotalPlayers"),
77
RC_JSON_NEW_FIELD("GameID"),
78
RC_JSON_NEW_FIELD("RecentWinner") /* array */
79
};
80
81
rc_json_field_t entry_fields[] = {
82
RC_JSON_NEW_FIELD("User"),
83
RC_JSON_NEW_FIELD("DateAwarded"),
84
RC_JSON_NEW_FIELD("AvatarUrl")
85
};
86
87
memset(response, 0, sizeof(*response));
88
rc_buffer_init(&response->response.buffer);
89
90
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
91
if (result != RC_OK)
92
return result;
93
94
if (!rc_json_get_required_unum(&response->id, &response->response, &fields[2], "AchievementID"))
95
return RC_MISSING_VALUE;
96
if (!rc_json_get_required_object(response_fields, sizeof(response_fields) / sizeof(response_fields[0]), &response->response, &fields[3], "Response"))
97
return RC_MISSING_VALUE;
98
99
if (!rc_json_get_required_unum(&response->num_awarded, &response->response, &response_fields[0], "NumEarned"))
100
return RC_MISSING_VALUE;
101
if (!rc_json_get_required_unum(&response->num_players, &response->response, &response_fields[1], "TotalPlayers"))
102
return RC_MISSING_VALUE;
103
if (!rc_json_get_required_unum(&response->game_id, &response->response, &response_fields[2], "GameID"))
104
return RC_MISSING_VALUE;
105
106
if (!rc_json_get_required_array(&response->num_recently_awarded, &array_field, &response->response, &response_fields[3], "RecentWinner"))
107
return RC_MISSING_VALUE;
108
109
if (response->num_recently_awarded) {
110
response->recently_awarded = (rc_api_achievement_awarded_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_recently_awarded * sizeof(rc_api_achievement_awarded_entry_t));
111
if (!response->recently_awarded)
112
return RC_OUT_OF_MEMORY;
113
114
memset(&iterator, 0, sizeof(iterator));
115
iterator.json = array_field.value_start;
116
iterator.end = array_field.value_end;
117
118
entry = response->recently_awarded;
119
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
120
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
121
return RC_MISSING_VALUE;
122
123
if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[1], "DateAwarded"))
124
return RC_MISSING_VALUE;
125
entry->awarded = (time_t)timet;
126
127
rc_json_get_optional_string(&entry->avatar_url, &response->response, &entry_fields[2], "AvatarUrl", NULL);
128
if (!entry->avatar_url)
129
entry->avatar_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_USER, entry->username);
130
131
++entry;
132
}
133
}
134
135
return RC_OK;
136
}
137
138
void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response) {
139
rc_buffer_destroy(&response->response.buffer);
140
}
141
142
/* --- Fetch Leaderboard Info --- */
143
144
int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const rc_api_fetch_leaderboard_info_request_t* api_params) {
145
return rc_api_init_fetch_leaderboard_info_request_hosted(request, api_params, &g_host);
146
}
147
148
int rc_api_init_fetch_leaderboard_info_request_hosted(rc_api_request_t* request,
149
const rc_api_fetch_leaderboard_info_request_t* api_params,
150
const rc_api_host_t* host) {
151
rc_api_url_builder_t builder;
152
153
rc_api_url_build_dorequest_url(request, host);
154
155
if (api_params->leaderboard_id == 0)
156
return RC_INVALID_STATE;
157
158
rc_url_builder_init(&builder, &request->buffer, 48);
159
rc_url_builder_append_str_param(&builder, "r", "lbinfo");
160
rc_url_builder_append_unum_param(&builder, "i", api_params->leaderboard_id);
161
162
if (api_params->username)
163
rc_url_builder_append_str_param(&builder, "u", api_params->username);
164
else if (api_params->first_entry > 1)
165
rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */
166
167
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
168
request->post_data = rc_url_builder_finalize(&builder);
169
request->content_type = RC_CONTENT_TYPE_URLENCODED;
170
171
return builder.result;
172
}
173
174
int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response) {
175
rc_api_server_response_t response_obj;
176
177
memset(&response_obj, 0, sizeof(response_obj));
178
response_obj.body = server_response;
179
response_obj.body_length = rc_json_get_object_string_length(server_response);
180
181
return rc_api_process_fetch_leaderboard_info_server_response(response, &response_obj);
182
}
183
184
int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboard_info_response_t* response, const rc_api_server_response_t* server_response) {
185
rc_api_lboard_info_entry_t* entry;
186
rc_json_field_t array_field;
187
rc_json_iterator_t iterator;
188
uint32_t timet;
189
int result;
190
size_t len;
191
char format[16];
192
193
rc_json_field_t fields[] = {
194
RC_JSON_NEW_FIELD("Success"),
195
RC_JSON_NEW_FIELD("Error"),
196
RC_JSON_NEW_FIELD("LeaderboardData")
197
};
198
199
rc_json_field_t leaderboarddata_fields[] = {
200
RC_JSON_NEW_FIELD("LBID"),
201
RC_JSON_NEW_FIELD("LBFormat"),
202
RC_JSON_NEW_FIELD("LowerIsBetter"),
203
RC_JSON_NEW_FIELD("LBTitle"),
204
RC_JSON_NEW_FIELD("LBDesc"),
205
RC_JSON_NEW_FIELD("LBMem"),
206
RC_JSON_NEW_FIELD("GameID"),
207
RC_JSON_NEW_FIELD("LBAuthor"),
208
RC_JSON_NEW_FIELD("LBCreated"),
209
RC_JSON_NEW_FIELD("LBUpdated"),
210
RC_JSON_NEW_FIELD("Entries"), /* array */
211
RC_JSON_NEW_FIELD("TotalEntries")
212
};
213
214
rc_json_field_t entry_fields[] = {
215
RC_JSON_NEW_FIELD("User"),
216
RC_JSON_NEW_FIELD("Rank"),
217
RC_JSON_NEW_FIELD("Index"),
218
RC_JSON_NEW_FIELD("Score"),
219
RC_JSON_NEW_FIELD("DateSubmitted"),
220
RC_JSON_NEW_FIELD("AvatarUrl")
221
};
222
223
memset(response, 0, sizeof(*response));
224
rc_buffer_init(&response->response.buffer);
225
226
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
227
if (result != RC_OK)
228
return result;
229
230
if (!rc_json_get_required_object(leaderboarddata_fields, sizeof(leaderboarddata_fields) / sizeof(leaderboarddata_fields[0]), &response->response, &fields[2], "LeaderboardData"))
231
return RC_MISSING_VALUE;
232
233
if (!rc_json_get_required_unum(&response->id, &response->response, &leaderboarddata_fields[0], "LBID"))
234
return RC_MISSING_VALUE;
235
if (!rc_json_get_required_unum(&response->lower_is_better, &response->response, &leaderboarddata_fields[2], "LowerIsBetter"))
236
return RC_MISSING_VALUE;
237
if (!rc_json_get_required_string(&response->title, &response->response, &leaderboarddata_fields[3], "LBTitle"))
238
return RC_MISSING_VALUE;
239
if (!rc_json_get_required_string(&response->description, &response->response, &leaderboarddata_fields[4], "LBDesc"))
240
return RC_MISSING_VALUE;
241
if (!rc_json_get_required_string(&response->definition, &response->response, &leaderboarddata_fields[5], "LBMem"))
242
return RC_MISSING_VALUE;
243
if (!rc_json_get_required_unum(&response->game_id, &response->response, &leaderboarddata_fields[6], "GameID"))
244
return RC_MISSING_VALUE;
245
if (!rc_json_get_required_string(&response->author, &response->response, &leaderboarddata_fields[7], "LBAuthor"))
246
return RC_MISSING_VALUE;
247
if (!rc_json_get_required_datetime(&response->created, &response->response, &leaderboarddata_fields[8], "LBCreated"))
248
return RC_MISSING_VALUE;
249
if (!rc_json_get_required_datetime(&response->updated, &response->response, &leaderboarddata_fields[9], "LBUpdated"))
250
return RC_MISSING_VALUE;
251
if (!rc_json_get_required_unum(&response->total_entries, &response->response, &leaderboarddata_fields[11], "TotalEntries"))
252
return RC_MISSING_VALUE;
253
254
if (!leaderboarddata_fields[1].value_end)
255
return RC_MISSING_VALUE;
256
len = leaderboarddata_fields[1].value_end - leaderboarddata_fields[1].value_start - 2;
257
if (len < sizeof(format) - 1) {
258
memcpy(format, leaderboarddata_fields[1].value_start + 1, len);
259
format[len] = '\0';
260
response->format = rc_parse_format(format);
261
}
262
else {
263
response->format = RC_FORMAT_VALUE;
264
}
265
266
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &leaderboarddata_fields[10], "Entries"))
267
return RC_MISSING_VALUE;
268
269
if (response->num_entries) {
270
response->entries = (rc_api_lboard_info_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_lboard_info_entry_t));
271
if (!response->entries)
272
return RC_OUT_OF_MEMORY;
273
274
memset(&iterator, 0, sizeof(iterator));
275
iterator.json = array_field.value_start;
276
iterator.end = array_field.value_end;
277
278
entry = response->entries;
279
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
280
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
281
return RC_MISSING_VALUE;
282
283
if (!rc_json_get_required_unum(&entry->rank, &response->response, &entry_fields[1], "Rank"))
284
return RC_MISSING_VALUE;
285
286
if (!rc_json_get_required_unum(&entry->index, &response->response, &entry_fields[2], "Index"))
287
return RC_MISSING_VALUE;
288
289
if (!rc_json_get_required_num(&entry->score, &response->response, &entry_fields[3], "Score"))
290
return RC_MISSING_VALUE;
291
292
if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[4], "DateSubmitted"))
293
return RC_MISSING_VALUE;
294
entry->submitted = (time_t)timet;
295
296
rc_json_get_optional_string(&entry->avatar_url, &response->response, &entry_fields[5], "AvatarUrl", NULL);
297
if (!entry->avatar_url)
298
entry->avatar_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_USER, entry->username);
299
300
++entry;
301
}
302
}
303
304
return RC_OK;
305
}
306
307
void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response) {
308
rc_buffer_destroy(&response->response.buffer);
309
}
310
311
/* --- Fetch Games List --- */
312
313
int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api_fetch_games_list_request_t* api_params) {
314
return rc_api_init_fetch_games_list_request_hosted(request, api_params, &g_host);
315
}
316
317
int rc_api_init_fetch_games_list_request_hosted(rc_api_request_t* request,
318
const rc_api_fetch_games_list_request_t* api_params,
319
const rc_api_host_t* host) {
320
rc_api_url_builder_t builder;
321
322
rc_api_url_build_dorequest_url(request, host);
323
324
if (api_params->console_id == 0)
325
return RC_INVALID_STATE;
326
327
rc_url_builder_init(&builder, &request->buffer, 48);
328
rc_url_builder_append_str_param(&builder, "r", "gameslist");
329
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
330
331
request->post_data = rc_url_builder_finalize(&builder);
332
request->content_type = RC_CONTENT_TYPE_URLENCODED;
333
334
return builder.result;
335
}
336
337
int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response) {
338
rc_api_server_response_t response_obj;
339
340
memset(&response_obj, 0, sizeof(response_obj));
341
response_obj.body = server_response;
342
response_obj.body_length = rc_json_get_object_string_length(server_response);
343
344
return rc_api_process_fetch_games_list_server_response(response, &response_obj);
345
}
346
347
int rc_api_process_fetch_games_list_server_response(rc_api_fetch_games_list_response_t* response, const rc_api_server_response_t* server_response) {
348
rc_api_game_list_entry_t* entry;
349
rc_json_iterator_t iterator;
350
rc_json_field_t field;
351
int result;
352
char* end;
353
354
rc_json_field_t fields[] = {
355
RC_JSON_NEW_FIELD("Success"),
356
RC_JSON_NEW_FIELD("Error"),
357
RC_JSON_NEW_FIELD("Response")
358
};
359
360
memset(response, 0, sizeof(*response));
361
rc_buffer_init(&response->response.buffer);
362
363
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
364
if (result != RC_OK)
365
return result;
366
367
if (!fields[2].value_start) {
368
/* call rc_json_get_required_object to generate the error message */
369
rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "Response");
370
return RC_MISSING_VALUE;
371
}
372
373
response->num_entries = fields[2].array_size;
374
rc_buffer_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t)));
375
376
response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
377
if (!response->entries)
378
return RC_OUT_OF_MEMORY;
379
380
memset(&iterator, 0, sizeof(iterator));
381
iterator.json = fields[2].value_start;
382
iterator.end = fields[2].value_end;
383
384
entry = response->entries;
385
while (rc_json_get_next_object_field(&iterator, &field)) {
386
entry->id = strtol(field.name, &end, 10);
387
388
field.name = "";
389
if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, ""))
390
return RC_MISSING_VALUE;
391
392
++entry;
393
}
394
395
return RC_OK;
396
}
397
398
void rc_api_destroy_fetch_games_list_response(rc_api_fetch_games_list_response_t* response) {
399
rc_buffer_destroy(&response->response.buffer);
400
}
401
402
/* --- Fetch Game Titles --- */
403
404
int rc_api_init_fetch_game_titles_request(rc_api_request_t* request, const rc_api_fetch_game_titles_request_t* api_params) {
405
return rc_api_init_fetch_game_titles_request_hosted(request, api_params, &g_host);
406
}
407
408
int rc_api_init_fetch_game_titles_request_hosted(rc_api_request_t* request,
409
const rc_api_fetch_game_titles_request_t* api_params,
410
const rc_api_host_t* host) {
411
rc_api_url_builder_t builder;
412
char num[16];
413
uint32_t i;
414
415
rc_api_url_build_dorequest_url(request, host);
416
417
if (api_params->num_game_ids == 0)
418
return RC_INVALID_STATE;
419
420
rc_url_builder_init(&builder, &request->buffer, 48);
421
rc_url_builder_append_str_param(&builder, "r", "gameinfolist");
422
rc_url_builder_append_unum_param(&builder, "g", api_params->game_ids[0]);
423
424
for (i = 1; i < api_params->num_game_ids; i++) {
425
int chars = snprintf(num, sizeof(num), "%u", api_params->game_ids[i]);
426
rc_url_builder_append(&builder, ",", 1);
427
rc_url_builder_append(&builder, num, chars);
428
}
429
430
request->post_data = rc_url_builder_finalize(&builder);
431
request->content_type = RC_CONTENT_TYPE_URLENCODED;
432
433
return builder.result;
434
}
435
436
int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_response_t* response, const rc_api_server_response_t* server_response) {
437
rc_api_game_title_entry_t* entry;
438
rc_json_iterator_t iterator;
439
rc_json_field_t array_field;
440
int result;
441
442
rc_json_field_t fields[] = {
443
RC_JSON_NEW_FIELD("Success"),
444
RC_JSON_NEW_FIELD("Error"),
445
RC_JSON_NEW_FIELD("Response")
446
};
447
448
rc_json_field_t entry_fields[] = {
449
RC_JSON_NEW_FIELD("ID"),
450
RC_JSON_NEW_FIELD("Title"),
451
RC_JSON_NEW_FIELD("ImageIcon")
452
};
453
454
memset(response, 0, sizeof(*response));
455
rc_buffer_init(&response->response.buffer);
456
457
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
458
if (result != RC_OK)
459
return result;
460
461
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &fields[2], "Response"))
462
return RC_MISSING_VALUE;
463
464
if (response->num_entries) {
465
response->entries = (rc_api_game_title_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_title_entry_t));
466
if (!response->entries)
467
return RC_OUT_OF_MEMORY;
468
469
memset(&iterator, 0, sizeof(iterator));
470
iterator.json = array_field.value_start;
471
iterator.end = array_field.value_end;
472
473
entry = response->entries;
474
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
475
if (!rc_json_get_required_unum(&entry->id, &response->response, &entry_fields[0], "ID"))
476
return RC_MISSING_VALUE;
477
if (!rc_json_get_required_string(&entry->title, &response->response, &entry_fields[1], "Title"))
478
return RC_MISSING_VALUE;
479
480
/* ImageIcon will be '/Images/0123456.png' - only return the '0123456' */
481
rc_json_extract_filename(&entry_fields[2]);
482
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
483
return RC_MISSING_VALUE;
484
485
++entry;
486
}
487
}
488
489
return RC_OK;
490
}
491
492
void rc_api_destroy_fetch_game_titles_response(rc_api_fetch_game_titles_response_t* response) {
493
rc_buffer_destroy(&response->response.buffer);
494
}
495
496
/* --- Fetch Game Hashes --- */
497
498
int rc_api_init_fetch_hash_library_request(rc_api_request_t* request,
499
const rc_api_fetch_hash_library_request_t* api_params)
500
{
501
return rc_api_init_fetch_hash_library_request_hosted(request, api_params, &g_host);
502
}
503
504
int rc_api_init_fetch_hash_library_request_hosted(rc_api_request_t* request,
505
const rc_api_fetch_hash_library_request_t* api_params,
506
const rc_api_host_t* host)
507
{
508
rc_api_url_builder_t builder;
509
rc_api_url_build_dorequest_url(request, host);
510
511
/* note: unauthenticated request */
512
rc_url_builder_init(&builder, &request->buffer, 48);
513
rc_url_builder_append_str_param(&builder, "r", "hashlibrary");
514
if (api_params->console_id != 0)
515
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
516
517
request->post_data = rc_url_builder_finalize(&builder);
518
request->content_type = RC_CONTENT_TYPE_URLENCODED;
519
520
return builder.result;
521
}
522
523
int rc_api_process_fetch_hash_library_server_response(rc_api_fetch_hash_library_response_t* response,
524
const rc_api_server_response_t* server_response)
525
{
526
rc_api_hash_library_entry_t* entry;
527
rc_json_iterator_t iterator;
528
rc_json_field_t field;
529
int result;
530
531
rc_json_field_t fields[] = {
532
RC_JSON_NEW_FIELD("Success"),
533
RC_JSON_NEW_FIELD("Error"),
534
RC_JSON_NEW_FIELD("MD5List"),
535
};
536
537
memset(response, 0, sizeof(*response));
538
rc_buffer_init(&response->response.buffer);
539
540
result =
541
rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
542
if (result != RC_OK)
543
return result;
544
545
if (!fields[2].value_start) {
546
/* call rc_json_get_required_object to generate the error message */
547
rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "MD5List");
548
return RC_MISSING_VALUE;
549
}
550
551
response->num_entries = fields[2].array_size;
552
if (response->num_entries > 0) {
553
rc_buffer_reserve(&response->response.buffer, response->num_entries * (33 + sizeof(rc_api_hash_library_entry_t)));
554
555
response->entries = (rc_api_hash_library_entry_t*)rc_buffer_alloc(
556
&response->response.buffer, response->num_entries * sizeof(rc_api_hash_library_entry_t));
557
if (!response->entries)
558
return RC_OUT_OF_MEMORY;
559
560
memset(&iterator, 0, sizeof(iterator));
561
iterator.json = fields[2].value_start;
562
iterator.end = fields[2].value_end;
563
564
entry = response->entries;
565
while (rc_json_get_next_object_field(&iterator, &field)) {
566
entry->hash = rc_buffer_strncpy(&response->response.buffer, field.name, field.name_len);
567
568
field.name = "";
569
if (!rc_json_get_unum(&entry->game_id, &field, ""))
570
return RC_MISSING_VALUE;
571
572
++entry;
573
}
574
}
575
576
return RC_OK;
577
}
578
579
void rc_api_destroy_fetch_hash_library_response(rc_api_fetch_hash_library_response_t* response)
580
{
581
rc_buffer_destroy(&response->response.buffer);
582
}
583
584