rc_api_info.c (17573B)
1 #include "rc_api_info.h" 2 #include "rc_api_common.h" 3 4 #include "rc_runtime_types.h" 5 6 #include "../rc_compat.h" 7 8 #include <stdlib.h> 9 #include <string.h> 10 11 /* --- Fetch Achievement Info --- */ 12 13 int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const rc_api_fetch_achievement_info_request_t* api_params) { 14 rc_api_url_builder_t builder; 15 16 rc_api_url_build_dorequest_url(request); 17 18 if (api_params->achievement_id == 0) 19 return RC_INVALID_STATE; 20 21 rc_url_builder_init(&builder, &request->buffer, 48); 22 if (rc_api_url_build_dorequest(&builder, "achievementwondata", api_params->username, api_params->api_token)) { 23 rc_url_builder_append_unum_param(&builder, "a", api_params->achievement_id); 24 25 if (api_params->friends_only) 26 rc_url_builder_append_unum_param(&builder, "f", 1); 27 if (api_params->first_entry > 1) 28 rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */ 29 rc_url_builder_append_unum_param(&builder, "c", api_params->count); 30 31 request->post_data = rc_url_builder_finalize(&builder); 32 request->content_type = RC_CONTENT_TYPE_URLENCODED; 33 } 34 35 return builder.result; 36 } 37 38 int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response) { 39 rc_api_server_response_t response_obj; 40 41 memset(&response_obj, 0, sizeof(response_obj)); 42 response_obj.body = server_response; 43 response_obj.body_length = rc_json_get_object_string_length(server_response); 44 45 return rc_api_process_fetch_achievement_info_server_response(response, &response_obj); 46 } 47 48 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) { 49 rc_api_achievement_awarded_entry_t* entry; 50 rc_json_field_t array_field; 51 rc_json_iterator_t iterator; 52 uint32_t timet; 53 int result; 54 55 rc_json_field_t fields[] = { 56 RC_JSON_NEW_FIELD("Success"), 57 RC_JSON_NEW_FIELD("Error"), 58 RC_JSON_NEW_FIELD("AchievementID"), 59 RC_JSON_NEW_FIELD("Response") 60 /* unused fields 61 RC_JSON_NEW_FIELD("Offset"), 62 RC_JSON_NEW_FIELD("Count"), 63 RC_JSON_NEW_FIELD("FriendsOnly") 64 * unused fields */ 65 }; 66 67 rc_json_field_t response_fields[] = { 68 RC_JSON_NEW_FIELD("NumEarned"), 69 RC_JSON_NEW_FIELD("TotalPlayers"), 70 RC_JSON_NEW_FIELD("GameID"), 71 RC_JSON_NEW_FIELD("RecentWinner") /* array */ 72 }; 73 74 rc_json_field_t entry_fields[] = { 75 RC_JSON_NEW_FIELD("User"), 76 RC_JSON_NEW_FIELD("DateAwarded") 77 }; 78 79 memset(response, 0, sizeof(*response)); 80 rc_buffer_init(&response->response.buffer); 81 82 result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0])); 83 if (result != RC_OK) 84 return result; 85 86 if (!rc_json_get_required_unum(&response->id, &response->response, &fields[2], "AchievementID")) 87 return RC_MISSING_VALUE; 88 if (!rc_json_get_required_object(response_fields, sizeof(response_fields) / sizeof(response_fields[0]), &response->response, &fields[3], "Response")) 89 return RC_MISSING_VALUE; 90 91 if (!rc_json_get_required_unum(&response->num_awarded, &response->response, &response_fields[0], "NumEarned")) 92 return RC_MISSING_VALUE; 93 if (!rc_json_get_required_unum(&response->num_players, &response->response, &response_fields[1], "TotalPlayers")) 94 return RC_MISSING_VALUE; 95 if (!rc_json_get_required_unum(&response->game_id, &response->response, &response_fields[2], "GameID")) 96 return RC_MISSING_VALUE; 97 98 if (!rc_json_get_required_array(&response->num_recently_awarded, &array_field, &response->response, &response_fields[3], "RecentWinner")) 99 return RC_MISSING_VALUE; 100 101 if (response->num_recently_awarded) { 102 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)); 103 if (!response->recently_awarded) 104 return RC_OUT_OF_MEMORY; 105 106 memset(&iterator, 0, sizeof(iterator)); 107 iterator.json = array_field.value_start; 108 iterator.end = array_field.value_end; 109 110 entry = response->recently_awarded; 111 while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) { 112 if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User")) 113 return RC_MISSING_VALUE; 114 115 if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[1], "DateAwarded")) 116 return RC_MISSING_VALUE; 117 entry->awarded = (time_t)timet; 118 119 ++entry; 120 } 121 } 122 123 return RC_OK; 124 } 125 126 void rc_api_destroy_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response) { 127 rc_buffer_destroy(&response->response.buffer); 128 } 129 130 /* --- Fetch Leaderboard Info --- */ 131 132 int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const rc_api_fetch_leaderboard_info_request_t* api_params) { 133 rc_api_url_builder_t builder; 134 135 rc_api_url_build_dorequest_url(request); 136 137 if (api_params->leaderboard_id == 0) 138 return RC_INVALID_STATE; 139 140 rc_url_builder_init(&builder, &request->buffer, 48); 141 rc_url_builder_append_str_param(&builder, "r", "lbinfo"); 142 rc_url_builder_append_unum_param(&builder, "i", api_params->leaderboard_id); 143 144 if (api_params->username) 145 rc_url_builder_append_str_param(&builder, "u", api_params->username); 146 else if (api_params->first_entry > 1) 147 rc_url_builder_append_unum_param(&builder, "o", api_params->first_entry - 1); /* number of entries to skip */ 148 149 rc_url_builder_append_unum_param(&builder, "c", api_params->count); 150 request->post_data = rc_url_builder_finalize(&builder); 151 request->content_type = RC_CONTENT_TYPE_URLENCODED; 152 153 return builder.result; 154 } 155 156 int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response) { 157 rc_api_server_response_t response_obj; 158 159 memset(&response_obj, 0, sizeof(response_obj)); 160 response_obj.body = server_response; 161 response_obj.body_length = rc_json_get_object_string_length(server_response); 162 163 return rc_api_process_fetch_leaderboard_info_server_response(response, &response_obj); 164 } 165 166 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) { 167 rc_api_lboard_info_entry_t* entry; 168 rc_json_field_t array_field; 169 rc_json_iterator_t iterator; 170 uint32_t timet; 171 int result; 172 size_t len; 173 char format[16]; 174 175 rc_json_field_t fields[] = { 176 RC_JSON_NEW_FIELD("Success"), 177 RC_JSON_NEW_FIELD("Error"), 178 RC_JSON_NEW_FIELD("LeaderboardData") 179 }; 180 181 rc_json_field_t leaderboarddata_fields[] = { 182 RC_JSON_NEW_FIELD("LBID"), 183 RC_JSON_NEW_FIELD("LBFormat"), 184 RC_JSON_NEW_FIELD("LowerIsBetter"), 185 RC_JSON_NEW_FIELD("LBTitle"), 186 RC_JSON_NEW_FIELD("LBDesc"), 187 RC_JSON_NEW_FIELD("LBMem"), 188 RC_JSON_NEW_FIELD("GameID"), 189 RC_JSON_NEW_FIELD("LBAuthor"), 190 RC_JSON_NEW_FIELD("LBCreated"), 191 RC_JSON_NEW_FIELD("LBUpdated"), 192 RC_JSON_NEW_FIELD("Entries"), /* array */ 193 RC_JSON_NEW_FIELD("TotalEntries") 194 /* unused fields 195 RC_JSON_NEW_FIELD("GameTitle"), 196 RC_JSON_NEW_FIELD("ConsoleID"), 197 RC_JSON_NEW_FIELD("ConsoleName"), 198 RC_JSON_NEW_FIELD("ForumTopicID"), 199 RC_JSON_NEW_FIELD("GameIcon") 200 * unused fields */ 201 }; 202 203 rc_json_field_t entry_fields[] = { 204 RC_JSON_NEW_FIELD("User"), 205 RC_JSON_NEW_FIELD("Rank"), 206 RC_JSON_NEW_FIELD("Index"), 207 RC_JSON_NEW_FIELD("Score"), 208 RC_JSON_NEW_FIELD("DateSubmitted") 209 }; 210 211 memset(response, 0, sizeof(*response)); 212 rc_buffer_init(&response->response.buffer); 213 214 result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0])); 215 if (result != RC_OK) 216 return result; 217 218 if (!rc_json_get_required_object(leaderboarddata_fields, sizeof(leaderboarddata_fields) / sizeof(leaderboarddata_fields[0]), &response->response, &fields[2], "LeaderboardData")) 219 return RC_MISSING_VALUE; 220 221 if (!rc_json_get_required_unum(&response->id, &response->response, &leaderboarddata_fields[0], "LBID")) 222 return RC_MISSING_VALUE; 223 if (!rc_json_get_required_unum(&response->lower_is_better, &response->response, &leaderboarddata_fields[2], "LowerIsBetter")) 224 return RC_MISSING_VALUE; 225 if (!rc_json_get_required_string(&response->title, &response->response, &leaderboarddata_fields[3], "LBTitle")) 226 return RC_MISSING_VALUE; 227 if (!rc_json_get_required_string(&response->description, &response->response, &leaderboarddata_fields[4], "LBDesc")) 228 return RC_MISSING_VALUE; 229 if (!rc_json_get_required_string(&response->definition, &response->response, &leaderboarddata_fields[5], "LBMem")) 230 return RC_MISSING_VALUE; 231 if (!rc_json_get_required_unum(&response->game_id, &response->response, &leaderboarddata_fields[6], "GameID")) 232 return RC_MISSING_VALUE; 233 if (!rc_json_get_required_string(&response->author, &response->response, &leaderboarddata_fields[7], "LBAuthor")) 234 return RC_MISSING_VALUE; 235 if (!rc_json_get_required_datetime(&response->created, &response->response, &leaderboarddata_fields[8], "LBCreated")) 236 return RC_MISSING_VALUE; 237 if (!rc_json_get_required_datetime(&response->updated, &response->response, &leaderboarddata_fields[9], "LBUpdated")) 238 return RC_MISSING_VALUE; 239 if (!rc_json_get_required_unum(&response->total_entries, &response->response, &leaderboarddata_fields[11], "TotalEntries")) 240 return RC_MISSING_VALUE; 241 242 if (!leaderboarddata_fields[1].value_end) 243 return RC_MISSING_VALUE; 244 len = leaderboarddata_fields[1].value_end - leaderboarddata_fields[1].value_start - 2; 245 if (len < sizeof(format) - 1) { 246 memcpy(format, leaderboarddata_fields[1].value_start + 1, len); 247 format[len] = '\0'; 248 response->format = rc_parse_format(format); 249 } 250 else { 251 response->format = RC_FORMAT_VALUE; 252 } 253 254 if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &leaderboarddata_fields[10], "Entries")) 255 return RC_MISSING_VALUE; 256 257 if (response->num_entries) { 258 response->entries = (rc_api_lboard_info_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_lboard_info_entry_t)); 259 if (!response->entries) 260 return RC_OUT_OF_MEMORY; 261 262 memset(&iterator, 0, sizeof(iterator)); 263 iterator.json = array_field.value_start; 264 iterator.end = array_field.value_end; 265 266 entry = response->entries; 267 while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) { 268 if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User")) 269 return RC_MISSING_VALUE; 270 271 if (!rc_json_get_required_unum(&entry->rank, &response->response, &entry_fields[1], "Rank")) 272 return RC_MISSING_VALUE; 273 274 if (!rc_json_get_required_unum(&entry->index, &response->response, &entry_fields[2], "Index")) 275 return RC_MISSING_VALUE; 276 277 if (!rc_json_get_required_num(&entry->score, &response->response, &entry_fields[3], "Score")) 278 return RC_MISSING_VALUE; 279 280 if (!rc_json_get_required_unum(&timet, &response->response, &entry_fields[4], "DateSubmitted")) 281 return RC_MISSING_VALUE; 282 entry->submitted = (time_t)timet; 283 284 ++entry; 285 } 286 } 287 288 return RC_OK; 289 } 290 291 void rc_api_destroy_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response) { 292 rc_buffer_destroy(&response->response.buffer); 293 } 294 295 /* --- Fetch Games List --- */ 296 297 int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api_fetch_games_list_request_t* api_params) { 298 rc_api_url_builder_t builder; 299 300 rc_api_url_build_dorequest_url(request); 301 302 if (api_params->console_id == 0) 303 return RC_INVALID_STATE; 304 305 rc_url_builder_init(&builder, &request->buffer, 48); 306 rc_url_builder_append_str_param(&builder, "r", "gameslist"); 307 rc_url_builder_append_unum_param(&builder, "c", api_params->console_id); 308 309 request->post_data = rc_url_builder_finalize(&builder); 310 request->content_type = RC_CONTENT_TYPE_URLENCODED; 311 312 return builder.result; 313 } 314 315 int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response) { 316 rc_api_server_response_t response_obj; 317 318 memset(&response_obj, 0, sizeof(response_obj)); 319 response_obj.body = server_response; 320 response_obj.body_length = rc_json_get_object_string_length(server_response); 321 322 return rc_api_process_fetch_games_list_server_response(response, &response_obj); 323 } 324 325 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) { 326 rc_api_game_list_entry_t* entry; 327 rc_json_iterator_t iterator; 328 rc_json_field_t field; 329 int result; 330 char* end; 331 332 rc_json_field_t fields[] = { 333 RC_JSON_NEW_FIELD("Success"), 334 RC_JSON_NEW_FIELD("Error"), 335 RC_JSON_NEW_FIELD("Response") 336 }; 337 338 memset(response, 0, sizeof(*response)); 339 rc_buffer_init(&response->response.buffer); 340 341 result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0])); 342 if (result != RC_OK) 343 return result; 344 345 if (!fields[2].value_start) { 346 /* call rc_json_get_required_object to generate the error message */ 347 rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "Response"); 348 return RC_MISSING_VALUE; 349 } 350 351 response->num_entries = fields[2].array_size; 352 rc_buffer_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t))); 353 354 response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t)); 355 if (!response->entries) 356 return RC_OUT_OF_MEMORY; 357 358 memset(&iterator, 0, sizeof(iterator)); 359 iterator.json = fields[2].value_start; 360 iterator.end = fields[2].value_end; 361 362 entry = response->entries; 363 while (rc_json_get_next_object_field(&iterator, &field)) { 364 entry->id = strtol(field.name, &end, 10); 365 366 field.name = ""; 367 if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, "")) 368 return RC_MISSING_VALUE; 369 370 ++entry; 371 } 372 373 return RC_OK; 374 } 375 376 void rc_api_destroy_fetch_games_list_response(rc_api_fetch_games_list_response_t* response) { 377 rc_buffer_destroy(&response->response.buffer); 378 } 379 380 /* --- Fetch Game Titles --- */ 381 382 int rc_api_init_fetch_game_titles_request(rc_api_request_t* request, const rc_api_fetch_game_titles_request_t* api_params) { 383 rc_api_url_builder_t builder; 384 char num[16]; 385 uint32_t i; 386 387 rc_api_url_build_dorequest_url(request); 388 389 if (api_params->num_game_ids == 0) 390 return RC_INVALID_STATE; 391 392 rc_url_builder_init(&builder, &request->buffer, 48); 393 rc_url_builder_append_str_param(&builder, "r", "gameinfolist"); 394 rc_url_builder_append_unum_param(&builder, "g", api_params->game_ids[0]); 395 396 for (i = 1; i < api_params->num_game_ids; i++) { 397 int chars = snprintf(num, sizeof(num), "%u", api_params->game_ids[i]); 398 rc_url_builder_append(&builder, ",", 1); 399 rc_url_builder_append(&builder, num, chars); 400 } 401 402 request->post_data = rc_url_builder_finalize(&builder); 403 request->content_type = RC_CONTENT_TYPE_URLENCODED; 404 405 return builder.result; 406 } 407 408 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) { 409 rc_api_game_title_entry_t* entry; 410 rc_json_iterator_t iterator; 411 rc_json_field_t array_field; 412 int result; 413 414 rc_json_field_t fields[] = { 415 RC_JSON_NEW_FIELD("Success"), 416 RC_JSON_NEW_FIELD("Error"), 417 RC_JSON_NEW_FIELD("Response") 418 }; 419 420 rc_json_field_t entry_fields[] = { 421 RC_JSON_NEW_FIELD("ID"), 422 RC_JSON_NEW_FIELD("Title"), 423 RC_JSON_NEW_FIELD("ImageIcon") 424 }; 425 426 memset(response, 0, sizeof(*response)); 427 rc_buffer_init(&response->response.buffer); 428 429 result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0])); 430 if (result != RC_OK) 431 return result; 432 433 if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &fields[2], "Response")) 434 return RC_MISSING_VALUE; 435 436 if (response->num_entries) { 437 response->entries = (rc_api_game_title_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_title_entry_t)); 438 if (!response->entries) 439 return RC_OUT_OF_MEMORY; 440 441 memset(&iterator, 0, sizeof(iterator)); 442 iterator.json = array_field.value_start; 443 iterator.end = array_field.value_end; 444 445 entry = response->entries; 446 while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) { 447 if (!rc_json_get_required_unum(&entry->id, &response->response, &entry_fields[0], "ID")) 448 return RC_MISSING_VALUE; 449 if (!rc_json_get_required_string(&entry->title, &response->response, &entry_fields[1], "Title")) 450 return RC_MISSING_VALUE; 451 452 /* ImageIcon will be '/Images/0123456.png' - only return the '0123456' */ 453 rc_json_extract_filename(&entry_fields[2]); 454 if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon")) 455 return RC_MISSING_VALUE; 456 457 ++entry; 458 } 459 } 460 461 return RC_OK; 462 } 463 464 void rc_api_destroy_fetch_game_titles_response(rc_api_fetch_game_titles_response_t* response) { 465 rc_buffer_destroy(&response->response.buffer); 466 }