Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/rc_api_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,26 @@ rc_api_fetch_games_list_request_t;
typedef struct rc_api_game_list_entry_t {
/* The unique identifier of the game */
uint32_t id;
/* The number of achievements in the game */
uint32_t num_achievements;
/* The number of leaderboards in the game */
uint32_t num_leaderboards;
/* The number of points in the game */
uint32_t points;
/* The name of the game */
const char* name;
/* The image name for the game badge */
const char* image_name;
/* The URL for the game badge image */
const char* image_url;
/* An array of supported hashes */
const char** supported_hashes;
/* An array of unsupported hashes */
const char** unsupported_hashes;
/* The number of items in the supported_hashes array */
uint32_t num_supported_hashes;
/* The number of items in the unsupported_hashes array */
uint32_t num_unsupported_hashes;
}
rc_api_game_list_entry_t;

Expand Down
51 changes: 51 additions & 0 deletions src/rapi/rc_api_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,57 @@ int rc_json_get_required_unum_array(uint32_t** entries, uint32_t* num_entries, r
return RC_OK;
}

static int rc_json_get_string_array(const char*** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* array, const char* field_name) {
if (*num_entries) {
rc_json_iterator_t iterator;
rc_json_field_t value;
const char** entry;

*entries = (const char**)rc_buffer_alloc(&response->buffer, *num_entries * sizeof(const char*));
if (!*entries)
return RC_OUT_OF_MEMORY;

value.name = field_name;

memset(&iterator, 0, sizeof(iterator));
iterator.json = array->value_start;
iterator.end = array->value_end;

entry = *entries;
while (rc_json_get_array_entry_value(&value, &iterator)) {
if (!rc_json_get_string(entry, &response->buffer, &value, field_name))
return RC_MISSING_VALUE;

++entry;
}
}
else {
*entries = NULL;
}

return RC_OK;
}

int rc_json_get_required_string_array(const char*** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
rc_json_field_t array;

memset(&array, 0, sizeof(array));
if (!rc_json_get_required_array(num_entries, &array, response, field, field_name))
return RC_MISSING_VALUE;

return rc_json_get_string_array(entries, num_entries, response, &array, field_name);
}

int rc_json_get_optional_string_array(const char*** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
rc_json_field_t array;

memset(&array, 0, sizeof(array));
if (!rc_json_get_optional_array(num_entries, &array, field, field_name))
*num_entries = 0;

return rc_json_get_string_array(entries, num_entries, response, &array, field_name);
}

int rc_json_get_required_array(uint32_t* num_entries, rc_json_field_t* array_field, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
#ifndef NDEBUG
if (strcmp(field->name, field_name) != 0)
Expand Down
2 changes: 2 additions & 0 deletions src/rapi/rc_api_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_js
int rc_json_get_required_datetime(time_t* out, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_object(rc_json_field_t* fields, size_t field_count, rc_api_response_t* response, rc_json_field_t* field, const char* field_name);
int rc_json_get_required_unum_array(uint32_t** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_string_array(const char*** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_optional_string_array(const char*** entries, uint32_t* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_required_array(uint32_t* num_entries, rc_json_field_t* array_field, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_iterator_t* iterator);
int rc_json_get_next_object_field(rc_json_iterator_t* iterator, rc_json_field_t* field);
Expand Down
75 changes: 52 additions & 23 deletions src/rapi/rc_api_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,8 @@ int rc_api_init_fetch_games_list_request_hosted(rc_api_request_t* request,
return RC_INVALID_STATE;

rc_url_builder_init(&builder, &request->buffer, 48);
rc_url_builder_append_str_param(&builder, "r", "gameslist");
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
rc_url_builder_append_str_param(&builder, "r", "systemgames");
rc_url_builder_append_unum_param(&builder, "s", api_params->console_id);

request->post_data = rc_url_builder_finalize(&builder);
request->content_type = RC_CONTENT_TYPE_URLENCODED;
Expand All @@ -347,49 +347,78 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
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) {
rc_api_game_list_entry_t* entry;
rc_json_iterator_t iterator;
rc_json_field_t field;
rc_json_field_t array_field;
int result;
char* end;

rc_json_field_t fields[] = {
RC_JSON_NEW_FIELD("Success"),
RC_JSON_NEW_FIELD("Error"),
RC_JSON_NEW_FIELD("Response")
};

rc_json_field_t game_fields[] = {
RC_JSON_NEW_FIELD("ID"),
RC_JSON_NEW_FIELD("Title"),
RC_JSON_NEW_FIELD("ImageIcon"),
RC_JSON_NEW_FIELD("ImageUrl"),
RC_JSON_NEW_FIELD("NumAchievements"),
RC_JSON_NEW_FIELD("NumLeaderboards"),
RC_JSON_NEW_FIELD("Points"),
RC_JSON_NEW_FIELD("SupportedHashes"), /* array */
RC_JSON_NEW_FIELD("UnsupportedHashes"), /* array */
};

memset(response, 0, sizeof(*response));
rc_buffer_init(&response->response.buffer);

result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
if (result != RC_OK)
return result;

if (!fields[2].value_start) {
/* call rc_json_get_required_object to generate the error message */
rc_json_get_required_object(NULL, 0, &response->response, &fields[2], "Response");
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &fields[2], "Response"))
return RC_MISSING_VALUE;
}

response->num_entries = fields[2].array_size;
rc_buffer_reserve(&response->response.buffer, response->num_entries * (32 + sizeof(rc_api_game_list_entry_t)));
if (response->num_entries) {
/* 8=image_name, 32=title, 64=image_url, 32=one hash */
rc_buffer_reserve(&response->response.buffer, response->num_entries * (8 + 32 + 64 + 32 + sizeof(rc_api_game_list_entry_t)));

response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
if (!response->entries)
return RC_OUT_OF_MEMORY;
response->entries = (rc_api_game_list_entry_t*)rc_buffer_alloc(&response->response.buffer, response->num_entries * sizeof(rc_api_game_list_entry_t));
if (!response->entries)
return RC_OUT_OF_MEMORY;

memset(&iterator, 0, sizeof(iterator));
iterator.json = fields[2].value_start;
iterator.end = fields[2].value_end;
memset(&iterator, 0, sizeof(iterator));
iterator.json = array_field.value_start;
iterator.end = array_field.value_end;

entry = response->entries;
while (rc_json_get_next_object_field(&iterator, &field)) {
entry->id = strtol(field.name, &end, 10);
entry = response->entries;
while (rc_json_get_array_entry_object(game_fields, sizeof(game_fields) / sizeof(game_fields[0]), &iterator)) {
if (!rc_json_get_required_unum(&entry->id, &response->response, &game_fields[0], "ID"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&entry->name, &response->response, &game_fields[1], "Title"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&entry->num_achievements, &response->response, &game_fields[4], "NumAchievements"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&entry->num_leaderboards, &response->response, &game_fields[5], "NumLeaderboards"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_unum(&entry->points, &response->response, &game_fields[6], "Points"))
return RC_MISSING_VALUE;

field.name = "";
if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, ""))
return RC_MISSING_VALUE;
/* ImageIcon will be '/Images/0123456.png' - only return the '0123456' */
rc_json_extract_filename(&game_fields[2]);
if (!rc_json_get_required_string(&entry->image_name, &response->response, &game_fields[2], "ImageIcon"))
return RC_MISSING_VALUE;
if (!rc_json_get_required_string(&entry->image_url, &response->response, &game_fields[3], "ImageUrl"))
return RC_MISSING_VALUE;

++entry;
result = rc_json_get_required_string_array(&entry->supported_hashes, &entry->num_supported_hashes, &response->response, &game_fields[7], "SupportedHashes");
if (result != RC_OK)
return result;
result = rc_json_get_optional_string_array(&entry->unsupported_hashes, &entry->num_unsupported_hashes, &response->response, &game_fields[8], "UnsupportedHashes");
if (result != RC_OK)
return result;

++entry;
}
}

return RC_OK;
Expand Down
69 changes: 47 additions & 22 deletions test/rapi/test_rc_api_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ static void test_init_fetch_games_list_request() {

ASSERT_NUM_EQUALS(rc_api_init_fetch_games_list_request(&request, &fetch_games_list_request), RC_OK);
ASSERT_STR_EQUALS(request.url, DOREQUEST_URL);
ASSERT_STR_EQUALS(request.post_data, "r=gameslist&c=12");
ASSERT_STR_EQUALS(request.post_data, "r=systemgames&s=12");
ASSERT_STR_EQUALS(request.content_type, RC_CONTENT_TYPE_URLENCODED);

rc_api_destroy_request(&request);
Expand All @@ -333,40 +333,65 @@ static void test_init_fetch_games_list_request() {
static void test_process_fetch_games_list_response() {
rc_api_fetch_games_list_response_t fetch_games_list_response;
rc_api_game_list_entry_t* entry;
const char* server_response = "{\"Success\":true,\"Response\":{"
"\"1234\":\"Game Name 1\","
"\"17\":\"Game Name 2\","
"\"9923\":\"Game Name 3\","
"\"12303\":\"Game Name 4\","
"\"4338\":\"Game Name 5\","
"\"5437\":\"Game Name 6\""
"}}";
const char* server_response = "{\"Success\":true,\"Response\":["
"{\"ID\":111,\"Title\":\"Game Name 1\",\"NumAchievements\":6,\"NumLeaderboards\":0,\"Points\":40,"
"\"ImageIcon\":\"/Images/001110.png\",\"ImageUrl\":\"http://host/Images/001110.png\","
"\"SupportedHashes\":[\"0123456789abcdeffedcba9876543210\"]},"
"{\"ID\":222,\"Title\":\"Game Name 2\",\"NumAchievements\":0,\"NumLeaderboards\":0,\"Points\":0,"
"\"ImageIcon\":\"/Images/002220.png\",\"ImageUrl\":\"http://host/Images/002220.png\","
"\"SupportedHashes\":[]},"
"{\"ID\":333,\"Title\":\"Game Name 3\",\"NumAchievements\":14,\"NumLeaderboards\":3,\"Points\":200,"
"\"ImageIcon\":\"/Images/003330.png\",\"ImageUrl\":\"http://host/Images/003330.png\","
"\"SupportedHashes\":[\"deadbeefdeadbeefdeadbeefdeadbeef\",\"00112233445566778899aabbccddeeff\"],"
"\"UnsupportedHashes\":[\"abababababababababababababababab\"]}"
"]}";

memset(&fetch_games_list_response, 0, sizeof(fetch_games_list_response));

ASSERT_NUM_EQUALS(rc_api_process_fetch_games_list_response(&fetch_games_list_response, server_response), RC_OK);
ASSERT_NUM_EQUALS(fetch_games_list_response.response.succeeded, 1);
ASSERT_PTR_NULL(fetch_games_list_response.response.error_message);
ASSERT_NUM_EQUALS(fetch_games_list_response.num_entries, 6);
ASSERT_NUM_EQUALS(fetch_games_list_response.num_entries, 3);

entry = &fetch_games_list_response.entries[0];
ASSERT_NUM_EQUALS(entry->id, 1234);
ASSERT_NUM_EQUALS(entry->id, 111);
ASSERT_STR_EQUALS(entry->name, "Game Name 1");
ASSERT_NUM_EQUALS(entry->num_achievements, 6);
ASSERT_NUM_EQUALS(entry->num_leaderboards, 0);
ASSERT_NUM_EQUALS(entry->points, 40);
ASSERT_STR_EQUALS(entry->image_name, "001110");
ASSERT_STR_EQUALS(entry->image_url, "http://host/Images/001110.png");
ASSERT_NUM_EQUALS(entry->num_supported_hashes, 1);
ASSERT_STR_EQUALS(entry->supported_hashes[0], "0123456789abcdeffedcba9876543210");
ASSERT_NUM_EQUALS(entry->num_unsupported_hashes, 0);
ASSERT_PTR_NULL(entry->unsupported_hashes);

entry = &fetch_games_list_response.entries[1];
ASSERT_NUM_EQUALS(entry->id, 17);
ASSERT_NUM_EQUALS(entry->id, 222);
ASSERT_STR_EQUALS(entry->name, "Game Name 2");
ASSERT_NUM_EQUALS(entry->num_achievements, 0);
ASSERT_NUM_EQUALS(entry->num_leaderboards, 0);
ASSERT_NUM_EQUALS(entry->points, 0);
ASSERT_STR_EQUALS(entry->image_name, "002220");
ASSERT_STR_EQUALS(entry->image_url, "http://host/Images/002220.png");
ASSERT_NUM_EQUALS(entry->num_supported_hashes, 0);
ASSERT_PTR_NULL(entry->supported_hashes);
ASSERT_NUM_EQUALS(entry->num_unsupported_hashes, 0);
ASSERT_PTR_NULL(entry->unsupported_hashes);

entry = &fetch_games_list_response.entries[2];
ASSERT_NUM_EQUALS(entry->id, 9923);
ASSERT_NUM_EQUALS(entry->id, 333);
ASSERT_STR_EQUALS(entry->name, "Game Name 3");
entry = &fetch_games_list_response.entries[3];
ASSERT_NUM_EQUALS(entry->id, 12303);
ASSERT_STR_EQUALS(entry->name, "Game Name 4");
entry = &fetch_games_list_response.entries[4];
ASSERT_NUM_EQUALS(entry->id, 4338);
ASSERT_STR_EQUALS(entry->name, "Game Name 5");
entry = &fetch_games_list_response.entries[5];
ASSERT_NUM_EQUALS(entry->id, 5437);
ASSERT_STR_EQUALS(entry->name, "Game Name 6");
ASSERT_NUM_EQUALS(entry->num_achievements, 14);
ASSERT_NUM_EQUALS(entry->num_leaderboards, 3);
ASSERT_NUM_EQUALS(entry->points, 200);
ASSERT_STR_EQUALS(entry->image_name, "003330");
ASSERT_STR_EQUALS(entry->image_url, "http://host/Images/003330.png");
ASSERT_NUM_EQUALS(entry->num_supported_hashes, 2);
ASSERT_STR_EQUALS(entry->supported_hashes[0], "deadbeefdeadbeefdeadbeefdeadbeef");
ASSERT_STR_EQUALS(entry->supported_hashes[1], "00112233445566778899aabbccddeeff");
ASSERT_NUM_EQUALS(entry->num_unsupported_hashes, 1);
ASSERT_STR_EQUALS(entry->unsupported_hashes[0], "abababababababababababababababab");

rc_api_destroy_fetch_games_list_response(&fetch_games_list_response);
}
Expand Down
Loading