mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 16:45:47 -04:00
dep: Bump rcheevos to 3af1e2fc5188d6e932ee379942f4049ea877e648
This commit is contained in:
@ -10,7 +10,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#define RETROACHIEVEMENTS_HOST "https://retroachievements.org"
|
||||
#define RETROACHIEVEMENTS_IMAGE_HOST "http://i.retroachievements.org"
|
||||
#define RETROACHIEVEMENTS_IMAGE_HOST "https://media.retroachievements.org"
|
||||
#define RETROACHIEVEMENTS_HOST_NONSSL "http://retroachievements.org"
|
||||
#define RETROACHIEVEMENTS_IMAGE_HOST_NONSSL "http://media.retroachievements.org"
|
||||
static char* g_host = NULL;
|
||||
static char* g_imagehost = NULL;
|
||||
|
||||
@ -18,157 +20,174 @@ static char* g_imagehost = NULL;
|
||||
|
||||
/* --- rc_json --- */
|
||||
|
||||
static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen);
|
||||
static int rc_json_parse_array(const char** json_ptr, rc_json_field_t* field);
|
||||
static int rc_json_parse_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen);
|
||||
static int rc_json_parse_array(rc_json_iterator_t* iterator, rc_json_field_t* field);
|
||||
|
||||
static int rc_json_parse_field(const char** json_ptr, rc_json_field_t* field) {
|
||||
static int rc_json_match_char(rc_json_iterator_t* iterator, char c)
|
||||
{
|
||||
if (iterator->json < iterator->end && *iterator->json == c) {
|
||||
++iterator->json;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rc_json_skip_whitespace(rc_json_iterator_t* iterator)
|
||||
{
|
||||
while (iterator->json < iterator->end && isspace((unsigned char)*iterator->json))
|
||||
++iterator->json;
|
||||
}
|
||||
|
||||
static int rc_json_find_closing_quote(rc_json_iterator_t* iterator)
|
||||
{
|
||||
while (iterator->json < iterator->end) {
|
||||
if (*iterator->json == '"')
|
||||
return 1;
|
||||
|
||||
if (*iterator->json == '\\') {
|
||||
++iterator->json;
|
||||
if (iterator->json == iterator->end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*iterator->json == '\0')
|
||||
return 0;
|
||||
|
||||
++iterator->json;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rc_json_parse_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
|
||||
int result;
|
||||
|
||||
field->value_start = *json_ptr;
|
||||
if (iterator->json >= iterator->end)
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
switch (**json_ptr)
|
||||
field->value_start = iterator->json;
|
||||
|
||||
switch (*iterator->json)
|
||||
{
|
||||
case '"': /* quoted string */
|
||||
++(*json_ptr);
|
||||
while (**json_ptr != '"') {
|
||||
if (**json_ptr == '\\')
|
||||
++(*json_ptr);
|
||||
|
||||
if (**json_ptr == '\0')
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
++(*json_ptr);
|
||||
}
|
||||
++(*json_ptr);
|
||||
++iterator->json;
|
||||
if (!rc_json_find_closing_quote(iterator))
|
||||
return RC_INVALID_JSON;
|
||||
++iterator->json;
|
||||
break;
|
||||
|
||||
case '-':
|
||||
case '+': /* signed number */
|
||||
++(*json_ptr);
|
||||
++iterator->json;
|
||||
/* fallthrough to number */
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': /* number */
|
||||
do {
|
||||
++(*json_ptr);
|
||||
} while (**json_ptr >= '0' && **json_ptr <= '9');
|
||||
if (**json_ptr == '.') {
|
||||
do {
|
||||
++(*json_ptr);
|
||||
} while (**json_ptr >= '0' && **json_ptr <= '9');
|
||||
while (iterator->json < iterator->end && *iterator->json >= '0' && *iterator->json <= '9')
|
||||
++iterator->json;
|
||||
|
||||
if (rc_json_match_char(iterator, '.')) {
|
||||
while (iterator->json < iterator->end && *iterator->json >= '0' && *iterator->json <= '9')
|
||||
++iterator->json;
|
||||
}
|
||||
break;
|
||||
|
||||
case '[': /* array */
|
||||
result = rc_json_parse_array(json_ptr, field);
|
||||
result = rc_json_parse_array(iterator, field);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
return result;
|
||||
|
||||
break;
|
||||
|
||||
case '{': /* object */
|
||||
result = rc_json_parse_object(json_ptr, NULL, 0, &field->array_size);
|
||||
result = rc_json_parse_object(iterator, NULL, 0, &field->array_size);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
break;
|
||||
|
||||
default: /* non-quoted text [true,false,null] */
|
||||
if (!isalpha((unsigned char)**json_ptr))
|
||||
if (!isalpha((unsigned char)*iterator->json))
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
do {
|
||||
++(*json_ptr);
|
||||
} while (isalnum((unsigned char)**json_ptr));
|
||||
while (iterator->json < iterator->end && isalnum((unsigned char)*iterator->json))
|
||||
++iterator->json;
|
||||
break;
|
||||
}
|
||||
|
||||
field->value_end = *json_ptr;
|
||||
field->value_end = iterator->json;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_json_parse_array(const char** json_ptr, rc_json_field_t* field) {
|
||||
static int rc_json_parse_array(rc_json_iterator_t* iterator, rc_json_field_t* field) {
|
||||
rc_json_field_t unused_field;
|
||||
const char* json = *json_ptr;
|
||||
int result;
|
||||
|
||||
if (*json != '[')
|
||||
if (!rc_json_match_char(iterator, '['))
|
||||
return RC_INVALID_JSON;
|
||||
++json;
|
||||
|
||||
field->array_size = 0;
|
||||
if (*json != ']') {
|
||||
do
|
||||
{
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
|
||||
result = rc_json_parse_field(&json, &unused_field);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
if (rc_json_match_char(iterator, ']')) /* empty array */
|
||||
return RC_OK;
|
||||
|
||||
++field->array_size;
|
||||
do
|
||||
{
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
result = rc_json_parse_field(iterator, &unused_field);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
if (*json != ',')
|
||||
break;
|
||||
++field->array_size;
|
||||
|
||||
++json;
|
||||
} while (1);
|
||||
rc_json_skip_whitespace(iterator);
|
||||
} while (rc_json_match_char(iterator, ','));
|
||||
|
||||
if (*json != ']')
|
||||
return RC_INVALID_JSON;
|
||||
}
|
||||
if (!rc_json_match_char(iterator, ']'))
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
*json_ptr = ++json;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_json_get_next_field(rc_json_object_field_iterator_t* iterator) {
|
||||
const char* json = iterator->json;
|
||||
static int rc_json_get_next_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
|
||||
if (*json != '"')
|
||||
if (!rc_json_match_char(iterator, '"'))
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
iterator->field.name = ++json;
|
||||
while (*json != '"') {
|
||||
if (!*json)
|
||||
field->name = iterator->json;
|
||||
while (iterator->json < iterator->end && *iterator->json != '"') {
|
||||
if (!*iterator->json)
|
||||
return RC_INVALID_JSON;
|
||||
++json;
|
||||
++iterator->json;
|
||||
}
|
||||
iterator->name_len = json - iterator->field.name;
|
||||
++json;
|
||||
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
|
||||
if (*json != ':')
|
||||
if (iterator->json == iterator->end)
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
++json;
|
||||
field->name_len = iterator->json - field->name;
|
||||
++iterator->json;
|
||||
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
if (rc_json_parse_field(&json, &iterator->field) < 0)
|
||||
if (!rc_json_match_char(iterator, ':'))
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
while (isspace((unsigned char)*json))
|
||||
++json;
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
if (rc_json_parse_field(iterator, field) < 0)
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
iterator->json = json;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen) {
|
||||
rc_json_object_field_iterator_t iterator;
|
||||
const char* json = *json_ptr;
|
||||
static int rc_json_parse_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, unsigned* fields_seen) {
|
||||
size_t i;
|
||||
unsigned num_fields = 0;
|
||||
rc_json_field_t field;
|
||||
int result;
|
||||
|
||||
if (fields_seen)
|
||||
@ -177,60 +196,105 @@ static int rc_json_parse_object(const char** json_ptr, rc_json_field_t* fields,
|
||||
for (i = 0; i < field_count; ++i)
|
||||
fields[i].value_start = fields[i].value_end = NULL;
|
||||
|
||||
if (*json != '{')
|
||||
if (!rc_json_match_char(iterator, '{'))
|
||||
return RC_INVALID_JSON;
|
||||
++json;
|
||||
|
||||
if (*json == '}') {
|
||||
*json_ptr = ++json;
|
||||
if (rc_json_match_char(iterator, '}')) /* empty object */
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = json;
|
||||
|
||||
do
|
||||
{
|
||||
result = rc_json_get_next_field(&iterator);
|
||||
result = rc_json_get_next_field(iterator, &field);
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
for (i = 0; i < field_count; ++i) {
|
||||
if (!fields[i].value_start && strncmp(fields[i].name, iterator.field.name, iterator.name_len) == 0 &&
|
||||
fields[i].name[iterator.name_len] == '\0') {
|
||||
fields[i].value_start = iterator.field.value_start;
|
||||
fields[i].value_end = iterator.field.value_end;
|
||||
fields[i].array_size = iterator.field.array_size;
|
||||
if (!fields[i].value_start && fields[i].name_len == field.name_len &&
|
||||
memcmp(fields[i].name, field.name, field.name_len) == 0) {
|
||||
fields[i].value_start = field.value_start;
|
||||
fields[i].value_end = field.value_end;
|
||||
fields[i].array_size = field.array_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++num_fields;
|
||||
if (*iterator.json != ',')
|
||||
break;
|
||||
|
||||
++iterator.json;
|
||||
} while (1);
|
||||
} while (rc_json_match_char(iterator, ','));
|
||||
|
||||
if (*iterator.json != '}')
|
||||
if (!rc_json_match_char(iterator, '}'))
|
||||
return RC_INVALID_JSON;
|
||||
|
||||
if (fields_seen)
|
||||
*fields_seen = num_fields;
|
||||
|
||||
*json_ptr = ++iterator.json;
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
int rc_json_get_next_object_field(rc_json_object_field_iterator_t* iterator) {
|
||||
if (*iterator->json != ',' && *iterator->json != '{')
|
||||
int rc_json_get_next_object_field(rc_json_iterator_t* iterator, rc_json_field_t* field) {
|
||||
if (!rc_json_match_char(iterator, ',') && !rc_json_match_char(iterator, '{'))
|
||||
return 0;
|
||||
|
||||
++iterator->json;
|
||||
return (rc_json_get_next_field(iterator) == RC_OK);
|
||||
return (rc_json_get_next_field(iterator, field) == RC_OK);
|
||||
}
|
||||
|
||||
int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_json_field_t* fields, size_t field_count) {
|
||||
int rc_json_get_object_string_length(const char* json) {
|
||||
const char* json_start = json;
|
||||
|
||||
rc_json_iterator_t iterator;
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = json;
|
||||
iterator.end = json + (1024 * 1024 * 1024); /* arbitrary 1GB limit on JSON response */
|
||||
|
||||
rc_json_parse_object(&iterator, NULL, 0, NULL);
|
||||
|
||||
return (int)(iterator.json - json_start);
|
||||
}
|
||||
|
||||
static int rc_json_extract_html_error(rc_api_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
const char* json = server_response->body;
|
||||
const char* end = json;
|
||||
|
||||
const char* title_start = strstr(json, "<title>");
|
||||
if (title_start) {
|
||||
title_start += 7;
|
||||
if (isdigit((int)*title_start)) {
|
||||
const char* title_end = strstr(title_start + 7, "</title>");
|
||||
if (title_end) {
|
||||
char* dst = rc_buf_reserve(&response->buffer, (title_end - title_start) + 1);
|
||||
response->error_message = dst;
|
||||
memcpy(dst, title_start, title_end - title_start);
|
||||
dst += (title_end - title_start);
|
||||
*dst++ = '\0';
|
||||
rc_buf_consume(&response->buffer, response->error_message, dst);
|
||||
response->succeeded = 0;
|
||||
return RC_INVALID_JSON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (*end && *end != '\n' && end - json < 200)
|
||||
++end;
|
||||
|
||||
if (end > json && end[-1] == '\r')
|
||||
--end;
|
||||
|
||||
if (end > json) {
|
||||
char* dst = rc_buf_reserve(&response->buffer, (end - json) + 1);
|
||||
response->error_message = dst;
|
||||
memcpy(dst, json, end - json);
|
||||
dst += (end - json);
|
||||
*dst++ = '\0';
|
||||
rc_buf_consume(&response->buffer, response->error_message, dst);
|
||||
}
|
||||
|
||||
response->succeeded = 0;
|
||||
return RC_INVALID_JSON;
|
||||
}
|
||||
|
||||
int rc_json_parse_server_response(rc_api_response_t* response, const rc_api_server_response_t* server_response, rc_json_field_t* fields, size_t field_count) {
|
||||
int result;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (field_count < 2)
|
||||
return RC_INVALID_STATE;
|
||||
@ -240,37 +304,28 @@ int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_jso
|
||||
return RC_INVALID_STATE;
|
||||
#endif
|
||||
|
||||
if (*json == '{') {
|
||||
int result = rc_json_parse_object(&json, fields, field_count, NULL);
|
||||
response->error_message = NULL;
|
||||
|
||||
if (!server_response || !server_response->body || !*server_response->body) {
|
||||
response->succeeded = 0;
|
||||
return RC_NO_RESPONSE;
|
||||
}
|
||||
|
||||
if (*server_response->body != '{') {
|
||||
result = rc_json_extract_html_error(response, server_response);
|
||||
}
|
||||
else {
|
||||
rc_json_iterator_t iterator;
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = server_response->body;
|
||||
iterator.end = server_response->body + server_response->body_length;
|
||||
result = rc_json_parse_object(&iterator, fields, field_count, NULL);
|
||||
|
||||
rc_json_get_optional_string(&response->error_message, response, &fields[1], "Error", NULL);
|
||||
rc_json_get_optional_bool(&response->succeeded, &fields[0], "Success", 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
response->error_message = NULL;
|
||||
|
||||
if (*json) {
|
||||
const char* end = json;
|
||||
while (*end && *end != '\n' && end - json < 200)
|
||||
++end;
|
||||
|
||||
if (end > json && end[-1] == '\r')
|
||||
--end;
|
||||
|
||||
if (end > json) {
|
||||
char* dst = rc_buf_reserve(&response->buffer, (end - json) + 1);
|
||||
response->error_message = dst;
|
||||
memcpy(dst, json, end - json);
|
||||
dst += (end - json);
|
||||
*dst++ = '\0';
|
||||
rc_buf_consume(&response->buffer, response->error_message, dst);
|
||||
}
|
||||
}
|
||||
|
||||
response->succeeded = 0;
|
||||
return RC_INVALID_JSON;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int rc_json_missing_field(rc_api_response_t* response, const rc_json_field_t* field) {
|
||||
@ -293,42 +348,48 @@ static int rc_json_missing_field(rc_api_response_t* response, const rc_json_fiel
|
||||
}
|
||||
|
||||
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) {
|
||||
const char* json = field->value_start;
|
||||
rc_json_iterator_t iterator;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!json)
|
||||
if (!field->value_start)
|
||||
return rc_json_missing_field(response, field);
|
||||
|
||||
return (rc_json_parse_object(&json, fields, field_count, &field->array_size) == RC_OK);
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = field->value_start;
|
||||
iterator.end = field->value_end;
|
||||
return (rc_json_parse_object(&iterator, fields, field_count, &field->array_size) == RC_OK);
|
||||
}
|
||||
|
||||
static int rc_json_get_array_entry_value(rc_json_field_t* field, rc_json_field_t* iterator) {
|
||||
if (!iterator->array_size)
|
||||
static int rc_json_get_array_entry_value(rc_json_field_t* field, rc_json_iterator_t* iterator) {
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
if (iterator->json >= iterator->end)
|
||||
return 0;
|
||||
|
||||
while (isspace((unsigned char)*iterator->value_start))
|
||||
++iterator->value_start;
|
||||
if (rc_json_parse_field(iterator, field) != RC_OK)
|
||||
return 0;
|
||||
|
||||
rc_json_parse_field(&iterator->value_start, field);
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
while (isspace((unsigned char)*iterator->value_start))
|
||||
++iterator->value_start;
|
||||
if (!rc_json_match_char(iterator, ','))
|
||||
rc_json_match_char(iterator, ']');
|
||||
|
||||
++iterator->value_start; /* skip , or ] */
|
||||
|
||||
--iterator->array_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
|
||||
rc_json_field_t iterator;
|
||||
rc_json_iterator_t iterator;
|
||||
rc_json_field_t array;
|
||||
rc_json_field_t value;
|
||||
unsigned* entry;
|
||||
|
||||
if (!rc_json_get_required_array(num_entries, &iterator, response, field, field_name))
|
||||
if (!rc_json_get_required_array(num_entries, &array, response, field, field_name))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (*num_entries) {
|
||||
@ -338,6 +399,10 @@ int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, r
|
||||
|
||||
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_unum(entry, &value, field_name))
|
||||
@ -353,10 +418,12 @@ int rc_json_get_required_unum_array(unsigned** entries, unsigned* num_entries, r
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name) {
|
||||
int rc_json_get_required_array(unsigned* 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)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!field->value_start || *field->value_start != '[') {
|
||||
@ -364,28 +431,27 @@ int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator,
|
||||
return rc_json_missing_field(response, field);
|
||||
}
|
||||
|
||||
memcpy(iterator, field, sizeof(*iterator));
|
||||
++iterator->value_start; /* skip [ */
|
||||
memcpy(array_field, field, sizeof(*array_field));
|
||||
++array_field->value_start; /* skip [ */
|
||||
|
||||
*num_entries = field->array_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_field_t* iterator) {
|
||||
if (!iterator->array_size)
|
||||
int rc_json_get_array_entry_object(rc_json_field_t* fields, size_t field_count, rc_json_iterator_t* iterator) {
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
if (iterator->json >= iterator->end)
|
||||
return 0;
|
||||
|
||||
while (isspace((unsigned char)*iterator->value_start))
|
||||
++iterator->value_start;
|
||||
if (rc_json_parse_object(iterator, fields, field_count, NULL) != RC_OK)
|
||||
return 0;
|
||||
|
||||
rc_json_parse_object(&iterator->value_start, fields, field_count, NULL);
|
||||
rc_json_skip_whitespace(iterator);
|
||||
|
||||
while (isspace((unsigned char)*iterator->value_start))
|
||||
++iterator->value_start;
|
||||
if (!rc_json_match_char(iterator, ','))
|
||||
rc_json_match_char(iterator, ']');
|
||||
|
||||
++iterator->value_start; /* skip , or ] */
|
||||
|
||||
--iterator->array_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -451,6 +517,8 @@ int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -562,6 +630,8 @@ int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_na
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -613,6 +683,8 @@ int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* fi
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (!src) {
|
||||
@ -654,11 +726,13 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (*field->value_start == '\"') {
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if (sscanf(field->value_start + 1, "%d-%d-%d %d:%d:%d",
|
||||
if (sscanf_s(field->value_start + 1, "%d-%d-%d %d:%d:%d",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
|
||||
tm.tm_mon--; /* 0-based */
|
||||
tm.tm_year -= 1900; /* 1900 based */
|
||||
@ -669,9 +743,11 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
|
||||
* timezone conversion twice and manually removing the difference */
|
||||
{
|
||||
time_t local_timet = mktime(&tm);
|
||||
struct tm* gmt_tm = gmtime(&local_timet);
|
||||
time_t skewed_timet = mktime(gmt_tm); /* applies local time adjustment second time */
|
||||
time_t tz_offset = skewed_timet - local_timet;
|
||||
time_t skewed_timet, tz_offset;
|
||||
struct tm gmt_tm;
|
||||
gmtime_s(&gmt_tm, &local_timet);
|
||||
skewed_timet = mktime(&gmt_tm); /* applies local time adjustment second time */
|
||||
tz_offset = skewed_timet - local_timet;
|
||||
*out = local_timet - tz_offset;
|
||||
}
|
||||
|
||||
@ -696,6 +772,8 @@ int rc_json_get_bool(int* out, const rc_json_field_t* field, const char* field_n
|
||||
#ifndef NDEBUG
|
||||
if (strcmp(field->name, field_name) != 0)
|
||||
return 0;
|
||||
#else
|
||||
(void)field_name;
|
||||
#endif
|
||||
|
||||
if (src) {
|
||||
@ -731,31 +809,32 @@ int rc_json_get_required_bool(int* out, rc_api_response_t* response, const rc_js
|
||||
/* --- rc_buf --- */
|
||||
|
||||
void rc_buf_init(rc_api_buffer_t* buffer) {
|
||||
buffer->write = &buffer->data[0];
|
||||
buffer->end = &buffer->data[sizeof(buffer->data)];
|
||||
buffer->next = NULL;
|
||||
buffer->chunk.write = buffer->chunk.start = &buffer->data[0];
|
||||
buffer->chunk.end = &buffer->data[sizeof(buffer->data)];
|
||||
buffer->chunk.next = NULL;
|
||||
}
|
||||
|
||||
void rc_buf_destroy(rc_api_buffer_t* buffer) {
|
||||
rc_api_buffer_chunk_t *chunk;
|
||||
#ifdef DEBUG_BUFFERS
|
||||
int count = 0;
|
||||
int wasted = 0;
|
||||
int total = 0;
|
||||
#endif
|
||||
|
||||
/* first buffer is not allocated */
|
||||
buffer = buffer->next;
|
||||
/* first chunk is not allocated. skip it. */
|
||||
chunk = buffer->chunk.next;
|
||||
|
||||
/* deallocate any additional buffers */
|
||||
while (buffer) {
|
||||
rc_api_buffer_t* next = buffer->next;
|
||||
while (chunk) {
|
||||
rc_api_buffer_chunk_t* next = chunk->next;
|
||||
#ifdef DEBUG_BUFFERS
|
||||
total += (int)(buffer->end - buffer->data);
|
||||
wasted += (int)(buffer->end - buffer->write);
|
||||
total += (int)(chunk->end - chunk->data);
|
||||
wasted += (int)(chunk->end - chunk->write);
|
||||
++count;
|
||||
#endif
|
||||
free(buffer);
|
||||
buffer = next;
|
||||
free(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_BUFFERS
|
||||
@ -765,47 +844,50 @@ void rc_buf_destroy(rc_api_buffer_t* buffer) {
|
||||
}
|
||||
|
||||
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount) {
|
||||
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
|
||||
size_t remaining;
|
||||
while (buffer) {
|
||||
remaining = buffer->end - buffer->write;
|
||||
while (chunk) {
|
||||
remaining = chunk->end - chunk->write;
|
||||
if (remaining >= amount)
|
||||
return buffer->write;
|
||||
return chunk->write;
|
||||
|
||||
if (!buffer->next) {
|
||||
/* allocate a chunk of memory that is a multiple of 256-bytes. casting it to an rc_api_buffer_t will
|
||||
* effectively unbound the data field, so use write and end pointers to track how data is being used.
|
||||
if (!chunk->next) {
|
||||
/* allocate a chunk of memory that is a multiple of 256-bytes. the first 32 bytes will be associated
|
||||
* to the chunk header, and the remaining will be used for data.
|
||||
*/
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_t) - sizeof(buffer->data);
|
||||
const size_t alloc_size = (amount + buffer_prefix_size + 0xFF) & ~0xFF;
|
||||
buffer->next = (rc_api_buffer_t*)malloc(alloc_size);
|
||||
if (!buffer->next)
|
||||
const size_t chunk_header_size = sizeof(rc_api_buffer_chunk_t);
|
||||
const size_t alloc_size = (chunk_header_size + amount + 0xFF) & ~0xFF;
|
||||
chunk->next = (rc_api_buffer_chunk_t*)malloc(alloc_size);
|
||||
if (!chunk->next)
|
||||
break;
|
||||
|
||||
buffer->next->write = buffer->next->data;
|
||||
buffer->next->end = buffer->next->write + (alloc_size - buffer_prefix_size);
|
||||
buffer->next->next = NULL;
|
||||
chunk->next->start = (char*)chunk->next + chunk_header_size;
|
||||
chunk->next->write = chunk->next->start;
|
||||
chunk->next->end = (char*)chunk->next + alloc_size;
|
||||
chunk->next->next = NULL;
|
||||
}
|
||||
|
||||
buffer = buffer->next;
|
||||
chunk = chunk->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end) {
|
||||
rc_api_buffer_chunk_t* chunk = &buffer->chunk;
|
||||
do {
|
||||
if (buffer->write == start) {
|
||||
size_t offset = (end - buffer->data);
|
||||
if (chunk->write == start) {
|
||||
size_t offset = (end - chunk->start);
|
||||
offset = (offset + 7) & ~7;
|
||||
buffer->write = &buffer->data[offset];
|
||||
chunk->write = &chunk->start[offset];
|
||||
|
||||
if (buffer->write > buffer->end)
|
||||
buffer->write = buffer->end;
|
||||
if (chunk->write > chunk->end)
|
||||
chunk->write = chunk->end;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = buffer->next;
|
||||
} while (buffer);
|
||||
chunk = chunk->next;
|
||||
} while (chunk);
|
||||
}
|
||||
|
||||
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount) {
|
||||
@ -814,6 +896,18 @@ void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount) {
|
||||
return (void*)ptr;
|
||||
}
|
||||
|
||||
char* rc_buf_strncpy(rc_api_buffer_t* buffer, const char* src, size_t len) {
|
||||
char* dst = rc_buf_reserve(buffer, len + 1);
|
||||
memcpy(dst, src, len);
|
||||
dst[len] = '\0';
|
||||
rc_buf_consume(buffer, dst, dst + len + 2);
|
||||
return dst;
|
||||
}
|
||||
|
||||
char* rc_buf_strcpy(rc_api_buffer_t* buffer, const char* src) {
|
||||
return rc_buf_strncpy(buffer, src, strlen(src));
|
||||
}
|
||||
|
||||
void rc_api_destroy_request(rc_api_request_t* request) {
|
||||
rc_buf_destroy(&request->buffer);
|
||||
}
|
||||
@ -828,13 +922,13 @@ void rc_api_format_md5(char checksum[33], const unsigned char digest[16]) {
|
||||
/* --- rc_url_builder --- */
|
||||
|
||||
void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer, size_t estimated_size) {
|
||||
rc_api_buffer_t* used_buffer;
|
||||
rc_api_buffer_chunk_t* used_buffer;
|
||||
|
||||
memset(builder, 0, sizeof(*builder));
|
||||
builder->buffer = buffer;
|
||||
builder->write = builder->start = rc_buf_reserve(buffer, estimated_size);
|
||||
|
||||
used_buffer = buffer;
|
||||
used_buffer = &buffer->chunk;
|
||||
while (used_buffer && used_buffer->write != builder->write)
|
||||
used_buffer = used_buffer->next;
|
||||
|
||||
@ -857,7 +951,7 @@ static int rc_url_builder_reserve(rc_api_url_builder_t* builder, size_t amount)
|
||||
if (remaining < amount) {
|
||||
const size_t used = builder->write - builder->start;
|
||||
const size_t current_size = builder->end - builder->start;
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_t) - sizeof(builder->buffer->data);
|
||||
const size_t buffer_prefix_size = sizeof(rc_api_buffer_chunk_t);
|
||||
char* new_start;
|
||||
size_t new_size = (current_size < 256) ? 256 : current_size * 2;
|
||||
do {
|
||||
@ -1054,6 +1148,16 @@ static void rc_api_update_host(char** host, const char* hostname) {
|
||||
|
||||
void rc_api_set_host(const char* hostname) {
|
||||
rc_api_update_host(&g_host, hostname);
|
||||
|
||||
if (!hostname) {
|
||||
/* also clear out the image hostname */
|
||||
rc_api_set_image_host(NULL);
|
||||
}
|
||||
else if (strcmp(hostname, RETROACHIEVEMENTS_HOST_NONSSL) == 0) {
|
||||
/* if just pointing at the non-HTTPS host, explicitly use the default image host
|
||||
* so it doesn't try to use the web host directly */
|
||||
rc_api_set_image_host(RETROACHIEVEMENTS_IMAGE_HOST_NONSSL);
|
||||
}
|
||||
}
|
||||
|
||||
void rc_api_set_image_host(const char* hostname) {
|
||||
|
@ -10,10 +10,13 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RC_CONTENT_TYPE_URLENCODED "application/x-www-form-urlencoded"
|
||||
|
||||
typedef struct rc_api_url_builder_t {
|
||||
char* write;
|
||||
char* start;
|
||||
char* end;
|
||||
/* pointer to a preallocated rc_api_buffer_t */
|
||||
rc_api_buffer_t* buffer;
|
||||
int result;
|
||||
}
|
||||
@ -23,22 +26,24 @@ void rc_url_builder_init(rc_api_url_builder_t* builder, rc_api_buffer_t* buffer,
|
||||
void rc_url_builder_append(rc_api_url_builder_t* builder, const char* data, size_t len);
|
||||
const char* rc_url_builder_finalize(rc_api_url_builder_t* builder);
|
||||
|
||||
#define RC_JSON_NEW_FIELD(n) {NULL,NULL,n,sizeof(n)-1,0}
|
||||
|
||||
typedef struct rc_json_field_t {
|
||||
const char* name;
|
||||
const char* value_start;
|
||||
const char* value_end;
|
||||
const char* name;
|
||||
size_t name_len;
|
||||
unsigned array_size;
|
||||
}
|
||||
rc_json_field_t;
|
||||
|
||||
typedef struct rc_json_object_field_iterator_t {
|
||||
rc_json_field_t field;
|
||||
typedef struct rc_json_iterator_t {
|
||||
const char* json;
|
||||
size_t name_len;
|
||||
const char* end;
|
||||
}
|
||||
rc_json_object_field_iterator_t;
|
||||
rc_json_iterator_t;
|
||||
|
||||
int rc_json_parse_response(rc_api_response_t* response, const char* json, rc_json_field_t* fields, size_t field_count);
|
||||
int rc_json_parse_server_response(rc_api_response_t* response, const rc_api_server_response_t* server_response, rc_json_field_t* fields, size_t field_count);
|
||||
int rc_json_get_string(const char** out, rc_api_buffer_t* buffer, const rc_json_field_t* field, const char* field_name);
|
||||
int rc_json_get_num(int* out, const rc_json_field_t* field, const char* field_name);
|
||||
int rc_json_get_unum(unsigned* out, const rc_json_field_t* field, const char* field_name);
|
||||
@ -55,15 +60,18 @@ 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(unsigned** entries, unsigned* num_entries, rc_api_response_t* response, const rc_json_field_t* field, const char* field_name);
|
||||
int rc_json_get_required_array(unsigned* num_entries, rc_json_field_t* iterator, 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_field_t* iterator);
|
||||
int rc_json_get_next_object_field(rc_json_object_field_iterator_t* iterator);
|
||||
int rc_json_get_required_array(unsigned* 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);
|
||||
int rc_json_get_object_string_length(const char* json);
|
||||
|
||||
void rc_buf_init(rc_api_buffer_t* buffer);
|
||||
void rc_buf_destroy(rc_api_buffer_t* buffer);
|
||||
char* rc_buf_reserve(rc_api_buffer_t* buffer, size_t amount);
|
||||
void rc_buf_consume(rc_api_buffer_t* buffer, const char* start, char* end);
|
||||
void* rc_buf_alloc(rc_api_buffer_t* buffer, size_t amount);
|
||||
char* rc_buf_strcpy(rc_api_buffer_t* buffer, const char* src);
|
||||
char* rc_buf_strncpy(rc_api_buffer_t* buffer, const char* src, size_t len);
|
||||
|
||||
void rc_url_builder_append_encoded_str(rc_api_url_builder_t* builder, const char* str);
|
||||
void rc_url_builder_append_num_param(rc_api_url_builder_t* builder, const char* param, int value);
|
||||
|
@ -22,12 +22,24 @@ int rc_api_init_fetch_code_notes_request(rc_api_request_t* request, const rc_api
|
||||
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_code_notes_response(rc_api_fetch_code_notes_response_t* response, const char* server_response) {
|
||||
rc_json_field_t iterator;
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_code_notes_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_code_notes_server_response(rc_api_fetch_code_notes_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
rc_json_field_t array_field;
|
||||
rc_json_iterator_t iterator;
|
||||
rc_api_code_note_t* note;
|
||||
const char* address_str;
|
||||
const char* last_author = "";
|
||||
@ -36,25 +48,25 @@ int rc_api_process_fetch_code_notes_response(rc_api_fetch_code_notes_response_t*
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"CodeNotes"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("CodeNotes")
|
||||
};
|
||||
|
||||
rc_json_field_t note_fields[] = {
|
||||
{"Address"},
|
||||
{"User"},
|
||||
{"Note"}
|
||||
RC_JSON_NEW_FIELD("Address"),
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Note")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_notes, &iterator, &response->response, &fields[2], "CodeNotes"))
|
||||
if (!rc_json_get_required_array(&response->num_notes, &array_field, &response->response, &fields[2], "CodeNotes"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_notes) {
|
||||
@ -62,15 +74,21 @@ int rc_api_process_fetch_code_notes_response(rc_api_fetch_code_notes_response_t*
|
||||
if (!response->notes)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
note = response->notes;
|
||||
while (rc_json_get_array_entry_object(note_fields, sizeof(note_fields) / sizeof(note_fields[0]), &iterator)) {
|
||||
/* an empty note represents a record that was deleted on the server */
|
||||
/* a note set to '' also represents a deleted note (remnant of a bug) */
|
||||
/* NOTE: len will include the quotes */
|
||||
len = note_fields[2].value_end - note_fields[2].value_start;
|
||||
if (len == 2 || (len == 4 && note_fields[2].value_start[1] == '\'' && note_fields[2].value_start[2] == '\'')) {
|
||||
--response->num_notes;
|
||||
continue;
|
||||
if (note_fields[2].value_start) {
|
||||
len = note_fields[2].value_end - note_fields[2].value_start;
|
||||
if (len == 2 || (len == 4 && note_fields[2].value_start[1] == '\'' && note_fields[2].value_start[2] == '\'')) {
|
||||
--response->num_notes;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc_json_get_required_string(&address_str, &response->response, ¬e_fields[0], "Address"))
|
||||
@ -123,27 +141,38 @@ int rc_api_init_update_code_note_request(rc_api_request_t* request, const rc_api
|
||||
rc_url_builder_append_str_param(&builder, "n", api_params->note);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_update_code_note_response(rc_api_update_code_note_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_update_code_note_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_update_code_note_server_response(rc_api_update_code_note_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error")
|
||||
/* unused fields
|
||||
{"GameID"},
|
||||
{"Address"},
|
||||
{"Note"}
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("Address"),
|
||||
RC_JSON_NEW_FIELD("Note")
|
||||
*/
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -206,23 +235,34 @@ int rc_api_init_update_achievement_request(rc_api_request_t* request, const rc_a
|
||||
rc_url_builder_append_str_param(&builder, "h", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_update_achievement_response(rc_api_update_achievement_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_update_achievement_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_update_achievement_server_response(rc_api_update_achievement_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"AchievementID"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("AchievementID")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -297,23 +337,34 @@ int rc_api_init_update_leaderboard_request(rc_api_request_t* request, const rc_a
|
||||
rc_url_builder_append_str_param(&builder, "h", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_update_leaderboard_response(rc_api_update_leaderboard_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_update_leaderboard_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_update_leaderboard_server_response(rc_api_update_leaderboard_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"LeaderboardID"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("LeaderboardID")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -338,24 +389,37 @@ int rc_api_init_fetch_badge_range_request(rc_api_request_t* request, const rc_ap
|
||||
rc_url_builder_append_str_param(&builder, "r", "badgeiter");
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
(void)api_params;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_badge_range_response(rc_api_fetch_badge_range_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_badge_range_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_badge_range_server_response(rc_api_fetch_badge_range_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"FirstBadge"},
|
||||
{"NextBadge"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("FirstBadge"),
|
||||
RC_JSON_NEW_FIELD("NextBadge")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -399,33 +463,44 @@ int rc_api_init_add_game_hash_request(rc_api_request_t* request, const rc_api_ad
|
||||
rc_url_builder_append_str_param(&builder, "d", api_params->hash_description);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_add_game_hash_response(rc_api_add_game_hash_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_add_game_hash_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_add_game_hash_server_response(rc_api_add_game_hash_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Response"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Response")
|
||||
};
|
||||
|
||||
rc_json_field_t response_fields[] = {
|
||||
{"GameID"}
|
||||
RC_JSON_NEW_FIELD("GameID")
|
||||
/* unused fields
|
||||
{"MD5"},
|
||||
{"ConsoleID"},
|
||||
{"GameTitle"},
|
||||
{"Success"}
|
||||
RC_JSON_NEW_FIELD("MD5"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
RC_JSON_NEW_FIELD("GameTitle"),
|
||||
RC_JSON_NEW_FIELD("Success")
|
||||
*/
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
|
@ -27,45 +27,57 @@ int rc_api_init_fetch_achievement_info_request(rc_api_request_t* request, const
|
||||
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_achievement_info_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
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) {
|
||||
rc_api_achievement_awarded_entry_t* entry;
|
||||
rc_json_field_t iterator;
|
||||
rc_json_field_t array_field;
|
||||
rc_json_iterator_t iterator;
|
||||
unsigned timet;
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"AchievementID"},
|
||||
{"Response"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("AchievementID"),
|
||||
RC_JSON_NEW_FIELD("Response")
|
||||
/* unused fields
|
||||
{"Offset"},
|
||||
{"Count"},
|
||||
{"FriendsOnly"},
|
||||
RC_JSON_NEW_FIELD("Offset"),
|
||||
RC_JSON_NEW_FIELD("Count"),
|
||||
RC_JSON_NEW_FIELD("FriendsOnly")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t response_fields[] = {
|
||||
{"NumEarned"},
|
||||
{"TotalPlayers"},
|
||||
{"GameID"},
|
||||
{"RecentWinner"} /* array */
|
||||
RC_JSON_NEW_FIELD("NumEarned"),
|
||||
RC_JSON_NEW_FIELD("TotalPlayers"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("RecentWinner") /* array */
|
||||
};
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"DateAwarded"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("DateAwarded")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
@ -81,7 +93,7 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
|
||||
if (!rc_json_get_required_unum(&response->game_id, &response->response, &response_fields[2], "GameID"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_recently_awarded, &iterator, &response->response, &response_fields[3], "RecentWinner"))
|
||||
if (!rc_json_get_required_array(&response->num_recently_awarded, &array_field, &response->response, &response_fields[3], "RecentWinner"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_recently_awarded) {
|
||||
@ -89,6 +101,10 @@ int rc_api_process_fetch_achievement_info_response(rc_api_fetch_achievement_info
|
||||
if (!response->recently_awarded)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
entry = response->recently_awarded;
|
||||
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
||||
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
|
||||
@ -130,57 +146,69 @@ int rc_api_init_fetch_leaderboard_info_request(rc_api_request_t* request, const
|
||||
|
||||
rc_url_builder_append_unum_param(&builder, "c", api_params->count);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_leaderboard_info_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
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) {
|
||||
rc_api_lboard_info_entry_t* entry;
|
||||
rc_json_field_t iterator;
|
||||
rc_json_field_t array_field;
|
||||
rc_json_iterator_t iterator;
|
||||
unsigned timet;
|
||||
int result;
|
||||
size_t len;
|
||||
char format[16];
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"LeaderboardData"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("LeaderboardData")
|
||||
};
|
||||
|
||||
rc_json_field_t leaderboarddata_fields[] = {
|
||||
{"LBID"},
|
||||
{"LBFormat"},
|
||||
{"LowerIsBetter"},
|
||||
{"LBTitle"},
|
||||
{"LBDesc"},
|
||||
{"LBMem"},
|
||||
{"GameID"},
|
||||
{"LBAuthor"},
|
||||
{"LBCreated"},
|
||||
{"LBUpdated"},
|
||||
{"Entries"} /* array */
|
||||
RC_JSON_NEW_FIELD("LBID"),
|
||||
RC_JSON_NEW_FIELD("LBFormat"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("LBTitle"),
|
||||
RC_JSON_NEW_FIELD("LBDesc"),
|
||||
RC_JSON_NEW_FIELD("LBMem"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("LBAuthor"),
|
||||
RC_JSON_NEW_FIELD("LBCreated"),
|
||||
RC_JSON_NEW_FIELD("LBUpdated"),
|
||||
RC_JSON_NEW_FIELD("Entries") /* array */
|
||||
/* unused fields
|
||||
{"GameTitle"},
|
||||
{"ConsoleID"},
|
||||
{"ConsoleName"},
|
||||
{"ForumTopicID"},
|
||||
{"GameIcon"},
|
||||
RC_JSON_NEW_FIELD("GameTitle"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
RC_JSON_NEW_FIELD("ConsoleName"),
|
||||
RC_JSON_NEW_FIELD("ForumTopicID"),
|
||||
RC_JSON_NEW_FIELD("GameIcon")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"Rank"},
|
||||
{"Index"},
|
||||
{"Score"},
|
||||
{"DateSubmitted"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("Index"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("DateSubmitted")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
@ -218,7 +246,7 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
|
||||
response->format = RC_FORMAT_VALUE;
|
||||
}
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_entries, &iterator, &response->response, &leaderboarddata_fields[10], "Entries"))
|
||||
if (!rc_json_get_required_array(&response->num_entries, &array_field, &response->response, &leaderboarddata_fields[10], "Entries"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_entries) {
|
||||
@ -226,6 +254,10 @@ int rc_api_process_fetch_leaderboard_info_response(rc_api_fetch_leaderboard_info
|
||||
if (!response->entries)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
entry = response->entries;
|
||||
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
||||
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
|
||||
@ -270,26 +302,38 @@ int rc_api_init_fetch_games_list_request(rc_api_request_t* request, const rc_api
|
||||
rc_url_builder_append_unum_param(&builder, "c", api_params->console_id);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_games_list_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
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_object_field_iterator_t iterator;
|
||||
rc_json_iterator_t iterator;
|
||||
rc_json_field_t field;
|
||||
int result;
|
||||
char* end;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Response"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Response")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
@ -308,13 +352,14 @@ int rc_api_process_fetch_games_list_response(rc_api_fetch_games_list_response_t*
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = fields[2].value_start;
|
||||
iterator.end = fields[2].value_end;
|
||||
|
||||
entry = response->entries;
|
||||
while (rc_json_get_next_object_field(&iterator)) {
|
||||
entry->id = strtol(iterator.field.name, &end, 10);
|
||||
while (rc_json_get_next_object_field(&iterator, &field)) {
|
||||
entry->id = strtol(field.name, &end, 10);
|
||||
|
||||
iterator.field.name = "";
|
||||
if (!rc_json_get_string(&entry->name, &response->response.buffer, &iterator.field, ""))
|
||||
field.name = "";
|
||||
if (!rc_json_get_string(&entry->name, &response->response.buffer, &field, ""))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
++entry;
|
||||
|
@ -24,22 +24,33 @@ int rc_api_init_resolve_hash_request(rc_api_request_t* request, const rc_api_res
|
||||
rc_url_builder_append_str_param(&builder, "r", "gameid");
|
||||
rc_url_builder_append_str_param(&builder, "m", api_params->game_hash);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_resolve_hash_response(rc_api_resolve_hash_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_resolve_hash_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_resolve_hash_server_response(rc_api_resolve_hash_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"GameID"},
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("GameID")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
@ -65,17 +76,30 @@ int rc_api_init_fetch_game_data_request(rc_api_request_t* request, const rc_api_
|
||||
if (rc_api_url_build_dorequest(&builder, "patch", api_params->username, api_params->api_token)) {
|
||||
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_game_data_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_game_data_server_response(rc_api_fetch_game_data_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
rc_api_achievement_definition_t* achievement;
|
||||
rc_api_leaderboard_definition_t* leaderboard;
|
||||
rc_json_field_t iterator;
|
||||
rc_json_field_t array_field;
|
||||
rc_json_iterator_t iterator;
|
||||
const char* str;
|
||||
const char* last_author = "";
|
||||
const char* last_author_field = "";
|
||||
size_t last_author_len = 0;
|
||||
size_t len;
|
||||
unsigned timet;
|
||||
@ -83,52 +107,52 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
char format[16];
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"PatchData"} /* nested object */
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("PatchData") /* nested object */
|
||||
};
|
||||
|
||||
rc_json_field_t patchdata_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"ConsoleID"},
|
||||
{"ImageIcon"},
|
||||
{"RichPresencePatch"},
|
||||
{"Achievements"}, /* array */
|
||||
{"Leaderboards"} /* array */
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
RC_JSON_NEW_FIELD("ImageIcon"),
|
||||
RC_JSON_NEW_FIELD("RichPresencePatch"),
|
||||
RC_JSON_NEW_FIELD("Achievements"), /* array */
|
||||
RC_JSON_NEW_FIELD("Leaderboards") /* array */
|
||||
/* unused fields
|
||||
{"ForumTopicID"},
|
||||
{"Flags"},
|
||||
RC_JSON_NEW_FIELD("ForumTopicID"),
|
||||
RC_JSON_NEW_FIELD("Flags")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t achievement_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"Description"},
|
||||
{"Flags"},
|
||||
{"Points"},
|
||||
{"MemAddr"},
|
||||
{"Author"},
|
||||
{"BadgeName"},
|
||||
{"Created"},
|
||||
{"Modified"}
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("Description"),
|
||||
RC_JSON_NEW_FIELD("Flags"),
|
||||
RC_JSON_NEW_FIELD("Points"),
|
||||
RC_JSON_NEW_FIELD("MemAddr"),
|
||||
RC_JSON_NEW_FIELD("Author"),
|
||||
RC_JSON_NEW_FIELD("BadgeName"),
|
||||
RC_JSON_NEW_FIELD("Created"),
|
||||
RC_JSON_NEW_FIELD("Modified")
|
||||
};
|
||||
|
||||
rc_json_field_t leaderboard_fields[] = {
|
||||
{"ID"},
|
||||
{"Title"},
|
||||
{"Description"},
|
||||
{"Mem"},
|
||||
{"Format"},
|
||||
{"LowerIsBetter"},
|
||||
{"Hidden"}
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("Description"),
|
||||
RC_JSON_NEW_FIELD("Mem"),
|
||||
RC_JSON_NEW_FIELD("Format"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("Hidden")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -174,7 +198,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
if (!response->rich_presence_script)
|
||||
response->rich_presence_script = "";
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_achievements, &iterator, &response->response, &patchdata_fields[5], "Achievements"))
|
||||
if (!rc_json_get_required_array(&response->num_achievements, &array_field, &response->response, &patchdata_fields[5], "Achievements"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_achievements) {
|
||||
@ -182,6 +206,10 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
if (!response->achievements)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
achievement = response->achievements;
|
||||
while (rc_json_get_array_entry_object(achievement_fields, sizeof(achievement_fields) / sizeof(achievement_fields[0]), &iterator)) {
|
||||
if (!rc_json_get_required_unum(&achievement->id, &response->response, &achievement_fields[0], "ID"))
|
||||
@ -199,8 +227,8 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
if (!rc_json_get_required_string(&achievement->badge_name, &response->response, &achievement_fields[7], "BadgeName"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
len = achievement_fields[7].value_end - achievement_fields[7].value_start;
|
||||
if (len == last_author_len && memcmp(achievement_fields[7].value_start, last_author, len) == 0) {
|
||||
len = achievement_fields[6].value_end - achievement_fields[6].value_start;
|
||||
if (len == last_author_len && memcmp(achievement_fields[6].value_start, last_author_field, len) == 0) {
|
||||
achievement->author = last_author;
|
||||
}
|
||||
else {
|
||||
@ -208,6 +236,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
last_author = achievement->author;
|
||||
last_author_field = achievement_fields[6].value_start;
|
||||
last_author_len = len;
|
||||
}
|
||||
|
||||
@ -222,7 +251,7 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
}
|
||||
}
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_leaderboards, &iterator, &response->response, &patchdata_fields[6], "Leaderboards"))
|
||||
if (!rc_json_get_required_array(&response->num_leaderboards, &array_field, &response->response, &patchdata_fields[6], "Leaderboards"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_leaderboards) {
|
||||
@ -230,6 +259,10 @@ int rc_api_process_fetch_game_data_response(rc_api_fetch_game_data_response_t* r
|
||||
if (!response->leaderboards)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
leaderboard = response->leaderboards;
|
||||
while (rc_json_get_array_entry_object(leaderboard_fields, sizeof(leaderboard_fields) / sizeof(leaderboard_fields[0]), &iterator)) {
|
||||
if (!rc_json_get_required_unum(&leaderboard->id, &response->response, &leaderboard_fields[0], "ID"))
|
||||
@ -284,21 +317,32 @@ int rc_api_init_ping_request(rc_api_request_t* request, const rc_api_ping_reques
|
||||
rc_url_builder_append_str_param(&builder, "m", api_params->rich_presence);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_ping_response(rc_api_ping_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_ping_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_ping_server_response(rc_api_ping_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
return rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
}
|
||||
|
||||
void rc_api_destroy_ping_response(rc_api_ping_response_t* response) {
|
||||
@ -337,25 +381,37 @@ int rc_api_init_award_achievement_request(rc_api_request_t* request, const rc_ap
|
||||
rc_url_builder_append_str_param(&builder, "v", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_award_achievement_response(rc_api_award_achievement_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_award_achievement_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_award_achievement_server_response(rc_api_award_achievement_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Score"},
|
||||
{"AchievementID"},
|
||||
{"AchievementsRemaining"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("SoftcoreScore"),
|
||||
RC_JSON_NEW_FIELD("AchievementID"),
|
||||
RC_JSON_NEW_FIELD("AchievementsRemaining")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK)
|
||||
return result;
|
||||
|
||||
@ -373,8 +429,9 @@ int rc_api_process_award_achievement_response(rc_api_award_achievement_response_
|
||||
}
|
||||
|
||||
rc_json_get_optional_unum(&response->new_player_score, &fields[2], "Score", 0);
|
||||
rc_json_get_optional_unum(&response->awarded_achievement_id, &fields[3], "AchievementID", 0);
|
||||
rc_json_get_optional_unum(&response->achievements_remaining, &fields[4], "AchievementsRemaining", (unsigned)-1);
|
||||
rc_json_get_optional_unum(&response->new_player_score_softcore, &fields[3], "SoftcoreScore", 0);
|
||||
rc_json_get_optional_unum(&response->awarded_achievement_id, &fields[4], "AchievementID", 0);
|
||||
rc_json_get_optional_unum(&response->achievements_remaining, &fields[5], "AchievementsRemaining", (unsigned)-1);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
@ -416,67 +473,79 @@ int rc_api_init_submit_lboard_entry_request(rc_api_request_t* request, const rc_
|
||||
rc_url_builder_append_str_param(&builder, "v", buffer);
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_submit_lboard_entry_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_submit_lboard_entry_server_response(rc_api_submit_lboard_entry_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
rc_api_lboard_entry_t* entry;
|
||||
rc_json_field_t iterator;
|
||||
rc_json_field_t array_field;
|
||||
rc_json_iterator_t iterator;
|
||||
const char* str;
|
||||
int result;
|
||||
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"Response"} /* nested object */
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("Response") /* nested object */
|
||||
};
|
||||
|
||||
rc_json_field_t response_fields[] = {
|
||||
{"Score"},
|
||||
{"BestScore"},
|
||||
{"RankInfo"}, /* nested object */
|
||||
{"TopEntries"} /* array */
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("BestScore"),
|
||||
RC_JSON_NEW_FIELD("RankInfo"), /* nested object */
|
||||
RC_JSON_NEW_FIELD("TopEntries") /* array */
|
||||
/* unused fields
|
||||
{"LBData"}, / * array * /
|
||||
{"ScoreFormatted"},
|
||||
{"TopEntriesFriends"}, / * array * /
|
||||
RC_JSON_NEW_FIELD("LBData"), / * array * /
|
||||
RC_JSON_NEW_FIELD("ScoreFormatted"),
|
||||
RC_JSON_NEW_FIELD("TopEntriesFriends") / * array * /
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
/* unused fields
|
||||
rc_json_field_t lbdata_fields[] = {
|
||||
{"Format"},
|
||||
{"LeaderboardID"},
|
||||
{"GameID"},
|
||||
{"Title"},
|
||||
{"LowerIsBetter"}
|
||||
RC_JSON_NEW_FIELD("Format"),
|
||||
RC_JSON_NEW_FIELD("LeaderboardID"),
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter")
|
||||
};
|
||||
* unused fields */
|
||||
|
||||
rc_json_field_t entry_fields[] = {
|
||||
{"User"},
|
||||
{"Rank"},
|
||||
{"Score"}
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("Score")
|
||||
/* unused fields
|
||||
{"DateSubmitted"},
|
||||
RC_JSON_NEW_FIELD("DateSubmitted")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
rc_json_field_t rank_info_fields[] = {
|
||||
{"Rank"},
|
||||
{"NumEntries"}
|
||||
RC_JSON_NEW_FIELD("Rank"),
|
||||
RC_JSON_NEW_FIELD("NumEntries")
|
||||
/* unused fields
|
||||
{"LowerIsBetter"},
|
||||
{"UserRank"},
|
||||
RC_JSON_NEW_FIELD("LowerIsBetter"),
|
||||
RC_JSON_NEW_FIELD("UserRank")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -495,7 +564,7 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
|
||||
return RC_MISSING_VALUE;
|
||||
response->num_entries = (unsigned)atoi(str);
|
||||
|
||||
if (!rc_json_get_required_array(&response->num_top_entries, &iterator, &response->response, &response_fields[3], "TopEntries"))
|
||||
if (!rc_json_get_required_array(&response->num_top_entries, &array_field, &response->response, &response_fields[3], "TopEntries"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (response->num_top_entries) {
|
||||
@ -503,6 +572,10 @@ int rc_api_process_submit_lboard_entry_response(rc_api_submit_lboard_entry_respo
|
||||
if (!response->top_entries)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
memset(&iterator, 0, sizeof(iterator));
|
||||
iterator.json = array_field.value_start;
|
||||
iterator.end = array_field.value_end;
|
||||
|
||||
entry = response->top_entries;
|
||||
while (rc_json_get_array_entry_object(entry_fields, sizeof(entry_fields) / sizeof(entry_fields[0]), &iterator)) {
|
||||
if (!rc_json_get_required_string(&entry->username, &response->response, &entry_fields[0], "User"))
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "rc_api_user.h"
|
||||
#include "rc_api_common.h"
|
||||
|
||||
#include "../rcheevos/rc_version.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* --- Login --- */
|
||||
@ -25,26 +27,38 @@ int rc_api_init_login_request(rc_api_request_t* request, const rc_api_login_requ
|
||||
return RC_INVALID_STATE;
|
||||
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_login_response(rc_api_login_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_login_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_login_server_response(rc_api_login_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"User"},
|
||||
{"Token"},
|
||||
{"Score"},
|
||||
{"Messages"},
|
||||
{"DisplayName"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("User"),
|
||||
RC_JSON_NEW_FIELD("Token"),
|
||||
RC_JSON_NEW_FIELD("Score"),
|
||||
RC_JSON_NEW_FIELD("SoftcoreScore"),
|
||||
RC_JSON_NEW_FIELD("Messages"),
|
||||
RC_JSON_NEW_FIELD("DisplayName")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
@ -54,9 +68,10 @@ int rc_api_process_login_response(rc_api_login_response_t* response, const char*
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
rc_json_get_optional_unum(&response->score, &fields[4], "Score", 0);
|
||||
rc_json_get_optional_unum(&response->num_unread_messages, &fields[5], "Messages", 0);
|
||||
rc_json_get_optional_unum(&response->score_softcore, &fields[5], "SoftcoreScore", 0);
|
||||
rc_json_get_optional_unum(&response->num_unread_messages, &fields[6], "Messages", 0);
|
||||
|
||||
rc_json_get_optional_string(&response->display_name, &response->response, &fields[6], "DisplayName", response->username);
|
||||
rc_json_get_optional_string(&response->display_name, &response->response, &fields[7], "DisplayName", response->username);
|
||||
|
||||
return RC_OK;
|
||||
}
|
||||
@ -86,22 +101,34 @@ int rc_api_init_start_session_request(rc_api_request_t* request, const rc_api_st
|
||||
*/
|
||||
rc_url_builder_append_unum_param(&builder, "a", 3);
|
||||
rc_url_builder_append_unum_param(&builder, "m", api_params->game_id);
|
||||
rc_url_builder_append_str_param(&builder, "l", RCHEEVOS_VERSION_STRING);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_start_session_response(rc_api_start_session_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_start_session_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_start_session_server_response(rc_api_start_session_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
return rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
return rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
}
|
||||
|
||||
void rc_api_destroy_start_session_response(rc_api_start_session_response_t* response) {
|
||||
@ -120,27 +147,38 @@ int rc_api_init_fetch_user_unlocks_request(rc_api_request_t* request, const rc_a
|
||||
rc_url_builder_append_unum_param(&builder, "g", api_params->game_id);
|
||||
rc_url_builder_append_unum_param(&builder, "h", api_params->hardcore ? 1 : 0);
|
||||
request->post_data = rc_url_builder_finalize(&builder);
|
||||
request->content_type = RC_CONTENT_TYPE_URLENCODED;
|
||||
}
|
||||
|
||||
return builder.result;
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_user_unlocks_response(rc_api_fetch_user_unlocks_response_t* response, const char* server_response) {
|
||||
rc_api_server_response_t response_obj;
|
||||
|
||||
memset(&response_obj, 0, sizeof(response_obj));
|
||||
response_obj.body = server_response;
|
||||
response_obj.body_length = rc_json_get_object_string_length(server_response);
|
||||
|
||||
return rc_api_process_fetch_user_unlocks_server_response(response, &response_obj);
|
||||
}
|
||||
|
||||
int rc_api_process_fetch_user_unlocks_server_response(rc_api_fetch_user_unlocks_response_t* response, const rc_api_server_response_t* server_response) {
|
||||
int result;
|
||||
rc_json_field_t fields[] = {
|
||||
{"Success"},
|
||||
{"Error"},
|
||||
{"UserUnlocks"}
|
||||
RC_JSON_NEW_FIELD("Success"),
|
||||
RC_JSON_NEW_FIELD("Error"),
|
||||
RC_JSON_NEW_FIELD("UserUnlocks")
|
||||
/* unused fields
|
||||
{ "GameID" },
|
||||
{ "HardcoreMode" }
|
||||
RC_JSON_NEW_FIELD("GameID"),
|
||||
RC_JSON_NEW_FIELD("HardcoreMode")
|
||||
* unused fields */
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
rc_buf_init(&response->response.buffer);
|
||||
|
||||
result = rc_json_parse_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
result = rc_json_parse_server_response(&response->response, server_response, fields, sizeof(fields) / sizeof(fields[0]));
|
||||
if (result != RC_OK || !response->response.succeeded)
|
||||
return result;
|
||||
|
||||
|
Reference in New Issue
Block a user