mirror of
https://github.com/libretro/RetroArch.git
synced 2024-12-01 04:00:32 +00:00
783 lines
20 KiB
C
783 lines
20 KiB
C
#include "parser.h"
|
|
|
|
#include "hash.h"
|
|
#include "util.h"
|
|
|
|
#include <encodings/utf.h>
|
|
#include <formats/jsonsax.h>
|
|
#include <string/stdstring.h>
|
|
#include <compat/strl.h>
|
|
|
|
/* C89 wants only int values in enums. */
|
|
#define CHEEVOS_JSON_KEY_GAMEID 0xb4960eecU
|
|
#define CHEEVOS_JSON_KEY_ACHIEVEMENTS 0x69749ae1U
|
|
#define CHEEVOS_JSON_KEY_ID 0x005973f2U
|
|
#define CHEEVOS_JSON_KEY_MEMADDR 0x1e76b53fU
|
|
#define CHEEVOS_JSON_KEY_TITLE 0x0e2a9a07U
|
|
#define CHEEVOS_JSON_KEY_DESCRIPTION 0xe61a1f69U
|
|
#define CHEEVOS_JSON_KEY_POINTS 0xca8fce22U
|
|
#define CHEEVOS_JSON_KEY_AUTHOR 0xa804edb8U
|
|
#define CHEEVOS_JSON_KEY_MODIFIED 0xdcea4fe6U
|
|
#define CHEEVOS_JSON_KEY_CREATED 0x3a84721dU
|
|
#define CHEEVOS_JSON_KEY_BADGENAME 0x887685d9U
|
|
#define CHEEVOS_JSON_KEY_CONSOLE_ID 0x071656e5U
|
|
#define CHEEVOS_JSON_KEY_TOKEN 0x0e2dbd26U
|
|
#define CHEEVOS_JSON_KEY_FLAGS 0x0d2e96b2U
|
|
#define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU
|
|
#define CHEEVOS_JSON_KEY_RICHPRESENCE 0xf18dd230U
|
|
#define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U
|
|
#define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU
|
|
#define CHEEVOS_JSON_KEY_SUCCESS 0x110461deU
|
|
#define CHEEVOS_JSON_KEY_ERROR 0x0d2011cfU
|
|
|
|
/*****************************************************************************
|
|
Gets a value in a JSON
|
|
*****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
unsigned key_hash;
|
|
int is_key;
|
|
const char* value;
|
|
size_t length;
|
|
} rcheevos_getvalueud_t;
|
|
|
|
static int rcheevos_getvalue_key(void* userdata,
|
|
const char* name, size_t length)
|
|
{
|
|
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
|
|
|
|
ud->is_key = rcheevos_djb2(name, length) == ud->key_hash;
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_getvalue_string(void* userdata,
|
|
const char* string, size_t length)
|
|
{
|
|
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
|
|
|
|
if (ud->is_key)
|
|
{
|
|
ud->value = string;
|
|
ud->length = length;
|
|
ud->is_key = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_getvalue_boolean(void* userdata, int istrue)
|
|
{
|
|
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
|
|
|
|
if (ud->is_key)
|
|
{
|
|
if (istrue)
|
|
{
|
|
ud->value = "true";
|
|
ud->length = 4;
|
|
}
|
|
else
|
|
{
|
|
ud->value = "false";
|
|
ud->length = 5;
|
|
}
|
|
|
|
ud->is_key = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_getvalue_null(void* userdata)
|
|
{
|
|
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
|
|
|
|
if (ud->is_key )
|
|
{
|
|
ud->value = "null";
|
|
ud->length = 4;
|
|
ud->is_key = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_get_value(const char* json, unsigned key_hash,
|
|
char* value, size_t length)
|
|
{
|
|
static const jsonsax_handlers_t handlers =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rcheevos_getvalue_key,
|
|
NULL,
|
|
rcheevos_getvalue_string,
|
|
rcheevos_getvalue_string, /* number */
|
|
rcheevos_getvalue_boolean,
|
|
rcheevos_getvalue_null
|
|
};
|
|
|
|
rcheevos_getvalueud_t ud;
|
|
|
|
ud.key_hash = key_hash;
|
|
ud.is_key = 0;
|
|
ud.value = NULL;
|
|
ud.length = 0;
|
|
*value = 0;
|
|
|
|
if ((jsonsax_parse(json, &handlers, (void*)&ud) == JSONSAX_OK)
|
|
&& ud.value && ud.length < length)
|
|
{
|
|
strlcpy(value, ud.value, ud.length + 1);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Returns the token or the error message
|
|
*****************************************************************************/
|
|
|
|
int rcheevos_get_token(const char* json, char* token, size_t length)
|
|
{
|
|
rcheevos_get_value(json, CHEEVOS_JSON_KEY_ERROR, token, length);
|
|
|
|
if (!string_is_empty(token))
|
|
return -1;
|
|
|
|
return rcheevos_get_value(json, CHEEVOS_JSON_KEY_TOKEN, token, length);
|
|
}
|
|
|
|
int rcheevos_get_json_error(const char* json, char* token, size_t length)
|
|
{
|
|
return rcheevos_get_value(json, CHEEVOS_JSON_KEY_ERROR, token, length);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Count number of achievements in a JSON file
|
|
*****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
int in_cheevos;
|
|
int in_lboards;
|
|
int has_error;
|
|
uint32_t field_hash;
|
|
unsigned core_count;
|
|
unsigned unofficial_count;
|
|
unsigned lboard_count;
|
|
} rcheevos_countud_t;
|
|
|
|
static int rcheevos_count_end_array(void* userdata)
|
|
{
|
|
rcheevos_countud_t* ud = (rcheevos_countud_t*)userdata;
|
|
|
|
ud->in_cheevos = 0;
|
|
ud->in_lboards = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_count_key(void* userdata,
|
|
const char* name, size_t length)
|
|
{
|
|
rcheevos_countud_t* ud = (rcheevos_countud_t*)userdata;
|
|
|
|
ud->field_hash = rcheevos_djb2(name, length);
|
|
|
|
if (ud->field_hash == CHEEVOS_JSON_KEY_ACHIEVEMENTS)
|
|
ud->in_cheevos = 1;
|
|
else if (ud->field_hash == CHEEVOS_JSON_KEY_LEADERBOARDS)
|
|
ud->in_lboards = 1;
|
|
else if (ud->field_hash == CHEEVOS_JSON_KEY_ERROR)
|
|
ud->has_error = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_count_number(void* userdata,
|
|
const char* number, size_t length)
|
|
{
|
|
rcheevos_countud_t* ud = (rcheevos_countud_t*)userdata;
|
|
|
|
if (ud->in_cheevos && ud->field_hash == CHEEVOS_JSON_KEY_FLAGS)
|
|
{
|
|
long flags = strtol(number, NULL, 10);
|
|
|
|
if (flags == 3)
|
|
ud->core_count++; /* Core achievements */
|
|
else if (flags == 5)
|
|
ud->unofficial_count++; /* Unofficial achievements */
|
|
}
|
|
else if (ud->in_lboards && ud->field_hash == CHEEVOS_JSON_KEY_ID)
|
|
ud->lboard_count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_count_cheevos(const char* json,
|
|
unsigned* core_count, unsigned* unofficial_count,
|
|
unsigned* lboard_count, int* has_error)
|
|
{
|
|
static const jsonsax_handlers_t handlers =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rcheevos_count_end_array,
|
|
rcheevos_count_key,
|
|
NULL,
|
|
NULL,
|
|
rcheevos_count_number,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
int res;
|
|
rcheevos_countud_t ud;
|
|
ud.in_cheevos = 0;
|
|
ud.in_lboards = 0;
|
|
ud.has_error = 0;
|
|
ud.core_count = 0;
|
|
ud.unofficial_count = 0;
|
|
ud.lboard_count = 0;
|
|
|
|
res = jsonsax_parse(json, &handlers, (void*)&ud);
|
|
|
|
*core_count = ud.core_count;
|
|
*unofficial_count = ud.unofficial_count;
|
|
*lboard_count = ud.lboard_count;
|
|
*has_error = ud.has_error;
|
|
|
|
return res;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Parses the cheevos in the JSON
|
|
*****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
const char* string;
|
|
size_t length;
|
|
} rcheevos_field_t;
|
|
|
|
typedef struct
|
|
{
|
|
int in_cheevos;
|
|
int in_lboards;
|
|
int is_game_id;
|
|
int is_title;
|
|
int is_console_id;
|
|
int is_richpresence;
|
|
unsigned core_count;
|
|
unsigned unofficial_count;
|
|
unsigned lboard_count;
|
|
|
|
rcheevos_field_t* field;
|
|
rcheevos_field_t id, memaddr, title, desc, points, author;
|
|
rcheevos_field_t modified, created, badge, flags, format;
|
|
|
|
rcheevos_rapatchdata_t* patchdata;
|
|
} rcheevos_readud_t;
|
|
|
|
static char* rcheevos_unescape_string(const char* string, size_t length)
|
|
{
|
|
const char* end = string + length;
|
|
char* buffer = (char*)malloc(length + 1);
|
|
char* buffer_it = buffer;
|
|
|
|
if (buffer == NULL)
|
|
return NULL;
|
|
|
|
while (string < end)
|
|
{
|
|
if (*string == '\\')
|
|
{
|
|
char escaped_char = string[1];
|
|
switch (escaped_char)
|
|
{
|
|
case 'r': /* Ignore carriage return */
|
|
string += 2;
|
|
break;
|
|
|
|
case 'n': /* Accept newlines */
|
|
*buffer_it++ = '\n';
|
|
string += 2;
|
|
break;
|
|
|
|
case 'u': /* Accept UTF-16 unicode characters */
|
|
{
|
|
#define MAX_SEQUENCES 16
|
|
uint16_t utf16[MAX_SEQUENCES];
|
|
char utf8[MAX_SEQUENCES * 4];
|
|
uint8_t i, j;
|
|
|
|
for (i = 1; i < MAX_SEQUENCES - 1; i++)
|
|
if (strncmp((string + 6 * i), "\\u", 2))
|
|
break;
|
|
|
|
/* Get escaped hex values and add them to the string */
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
char temp[5];
|
|
|
|
string += 2;
|
|
memcpy(temp, string, 4);
|
|
temp[4] = '\0';
|
|
utf16[j] = string_hex_to_unsigned(temp);
|
|
string += 4;
|
|
}
|
|
utf16[j] = 0;
|
|
|
|
if (utf16_to_char_string(utf16, utf8, sizeof(utf8)))
|
|
{
|
|
memcpy(buffer_it, utf8, strlen(utf8));
|
|
buffer_it += strlen(utf8);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*buffer_it++ = escaped_char;
|
|
string += 2;
|
|
break;
|
|
};
|
|
}
|
|
else
|
|
{
|
|
*buffer_it++ = *string++;
|
|
}
|
|
}
|
|
*buffer_it = '\0';
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static int rcheevos_new_cheevo(rcheevos_readud_t* ud)
|
|
{
|
|
rcheevos_racheevo_t* cheevo = NULL;
|
|
unsigned flags = (unsigned)strtol(ud->flags.string, NULL, 10);
|
|
|
|
if (flags == 3)
|
|
cheevo = ud->patchdata->core + ud->core_count++;
|
|
else if (flags == 5)
|
|
cheevo = ud->patchdata->unofficial + ud->unofficial_count++;
|
|
else
|
|
return 0;
|
|
|
|
cheevo->title = rcheevos_unescape_string(ud->title.string, ud->title.length);
|
|
cheevo->description = rcheevos_unescape_string(ud->desc.string, ud->desc.length);
|
|
cheevo->badge = rcheevos_unescape_string(ud->badge.string, ud->badge.length);
|
|
cheevo->memaddr = rcheevos_unescape_string(ud->memaddr.string, ud->memaddr.length);
|
|
cheevo->points = (unsigned)strtol(ud->points.string, NULL, 10);
|
|
cheevo->id = (unsigned)strtol(ud->id.string, NULL, 10);
|
|
|
|
if ( !cheevo->title
|
|
|| !cheevo->description
|
|
|| !cheevo->badge
|
|
|| !cheevo->memaddr)
|
|
{
|
|
CHEEVOS_FREE(cheevo->title);
|
|
CHEEVOS_FREE(cheevo->description);
|
|
CHEEVOS_FREE(cheevo->badge);
|
|
CHEEVOS_FREE(cheevo->memaddr);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_new_lboard(rcheevos_readud_t* ud)
|
|
{
|
|
rcheevos_ralboard_t* lboard = ud->patchdata->lboards + ud->lboard_count++;
|
|
|
|
lboard->title = rcheevos_unescape_string(ud->title.string, ud->title.length);
|
|
lboard->description = rcheevos_unescape_string(ud->desc.string, ud->desc.length);
|
|
lboard->format = rcheevos_unescape_string(ud->format.string, ud->format.length);
|
|
lboard->mem = rcheevos_unescape_string(ud->memaddr.string, ud->memaddr.length);
|
|
lboard->id = (unsigned)strtol(ud->id.string, NULL, 10);
|
|
|
|
if ( !lboard->title
|
|
|| !lboard->description
|
|
|| !lboard->format
|
|
|| !lboard->mem)
|
|
{
|
|
CHEEVOS_FREE(lboard->title);
|
|
CHEEVOS_FREE(lboard->description);
|
|
CHEEVOS_FREE(lboard->format);
|
|
CHEEVOS_FREE(lboard->mem);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_read_end_object(void* userdata)
|
|
{
|
|
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
|
|
|
|
if (ud->in_cheevos)
|
|
return rcheevos_new_cheevo(ud);
|
|
|
|
if (ud->in_lboards)
|
|
return rcheevos_new_lboard(ud);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_read_end_array(void* userdata)
|
|
{
|
|
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
|
|
|
|
ud->in_cheevos = 0;
|
|
ud->in_lboards = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_read_key(void* userdata,
|
|
const char* name, size_t length)
|
|
{
|
|
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
|
|
|
|
int common = ud->in_cheevos || ud->in_lboards;
|
|
uint32_t hash = rcheevos_djb2(name, length);
|
|
ud->field = NULL;
|
|
|
|
switch (hash)
|
|
{
|
|
case CHEEVOS_JSON_KEY_ACHIEVEMENTS:
|
|
ud->in_cheevos = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_LEADERBOARDS:
|
|
ud->in_lboards = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_CONSOLE_ID:
|
|
ud->is_console_id = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_RICHPRESENCE:
|
|
ud->is_richpresence = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_ID:
|
|
if (common)
|
|
ud->field = &ud->id;
|
|
else
|
|
ud->is_game_id = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_MEMADDR:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->memaddr;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_MEM:
|
|
if (ud->in_lboards)
|
|
ud->field = &ud->memaddr;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_TITLE:
|
|
if (common)
|
|
ud->field = &ud->title;
|
|
else
|
|
ud->is_title = 1;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_DESCRIPTION:
|
|
if (common)
|
|
ud->field = &ud->desc;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_POINTS:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->points;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_AUTHOR:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->author;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_MODIFIED:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->modified;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_CREATED:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->created;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_BADGENAME:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->badge;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_FLAGS:
|
|
if (ud->in_cheevos)
|
|
ud->field = &ud->flags;
|
|
break;
|
|
case CHEEVOS_JSON_KEY_FORMAT:
|
|
if (ud->in_lboards)
|
|
ud->field = &ud->format;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_read_string(void* userdata,
|
|
const char* string, size_t length)
|
|
{
|
|
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
|
|
|
|
if (ud->field)
|
|
{
|
|
ud->field->string = string;
|
|
ud->field->length = length;
|
|
}
|
|
else if (ud->is_title)
|
|
{
|
|
ud->patchdata->title = rcheevos_unescape_string(string, length);
|
|
ud->is_title = 0;
|
|
}
|
|
else if (ud->is_richpresence)
|
|
{
|
|
ud->patchdata->richpresence_script = rcheevos_unescape_string(string, length);
|
|
ud->is_richpresence = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_read_number(void* userdata,
|
|
const char* number, size_t length)
|
|
{
|
|
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
|
|
|
|
if (ud->field)
|
|
{
|
|
ud->field->string = number;
|
|
ud->field->length = length;
|
|
}
|
|
else if (ud->is_game_id)
|
|
{
|
|
ud->patchdata->game_id = (unsigned)strtol(number, NULL, 10);
|
|
ud->is_game_id = 0;
|
|
}
|
|
else if (ud->is_console_id)
|
|
{
|
|
ud->patchdata->console_id = (unsigned)strtol(number, NULL, 10);
|
|
ud->is_console_id = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rcheevos_get_patchdata(const char* json, rcheevos_rapatchdata_t* patchdata)
|
|
{
|
|
static const jsonsax_handlers_t handlers =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rcheevos_read_end_object,
|
|
NULL,
|
|
rcheevos_read_end_array,
|
|
rcheevos_read_key,
|
|
NULL,
|
|
rcheevos_read_string,
|
|
rcheevos_read_number,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
rcheevos_readud_t ud;
|
|
int res;
|
|
int has_error;
|
|
|
|
/* Count the number of achievements in the JSON file. */
|
|
res = rcheevos_count_cheevos(json, &patchdata->core_count,
|
|
&patchdata->unofficial_count, &patchdata->lboard_count, &has_error);
|
|
|
|
if (res != JSONSAX_OK || has_error)
|
|
return -1;
|
|
|
|
/* Allocate the achievements. */
|
|
|
|
patchdata->core = (rcheevos_racheevo_t*)
|
|
calloc(patchdata->core_count, sizeof(rcheevos_racheevo_t));
|
|
|
|
patchdata->unofficial = (rcheevos_racheevo_t*)
|
|
calloc(patchdata->unofficial_count, sizeof(rcheevos_racheevo_t));
|
|
|
|
patchdata->lboards = (rcheevos_ralboard_t*)
|
|
calloc(patchdata->lboard_count, sizeof(rcheevos_ralboard_t));
|
|
|
|
if (!patchdata->core ||
|
|
!patchdata->unofficial ||
|
|
!patchdata->lboards)
|
|
{
|
|
CHEEVOS_FREE(patchdata->core);
|
|
CHEEVOS_FREE(patchdata->unofficial);
|
|
CHEEVOS_FREE(patchdata->lboards);
|
|
|
|
return -1;
|
|
}
|
|
|
|
patchdata->richpresence_script = NULL;
|
|
patchdata->title = NULL;
|
|
|
|
/* Load the achievements. */
|
|
ud.in_cheevos = 0;
|
|
ud.in_lboards = 0;
|
|
ud.is_game_id = 0;
|
|
ud.is_title = 0;
|
|
ud.is_console_id = 0;
|
|
ud.is_richpresence = 0;
|
|
ud.field = NULL;
|
|
ud.core_count = 0;
|
|
ud.unofficial_count = 0;
|
|
ud.lboard_count = 0;
|
|
ud.patchdata = patchdata;
|
|
|
|
if (jsonsax_parse(json, &handlers, (void*)&ud) != JSONSAX_OK)
|
|
{
|
|
rcheevos_free_patchdata(patchdata);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Frees the patchdata
|
|
*****************************************************************************/
|
|
|
|
void rcheevos_free_patchdata(rcheevos_rapatchdata_t* patchdata)
|
|
{
|
|
unsigned i = 0, count = 0;
|
|
const rcheevos_racheevo_t* cheevo = NULL;
|
|
const rcheevos_ralboard_t* lboard = NULL;
|
|
|
|
cheevo = patchdata->core;
|
|
|
|
for (i = 0, count = patchdata->core_count; i < count; i++, cheevo++)
|
|
{
|
|
CHEEVOS_FREE(cheevo->title);
|
|
CHEEVOS_FREE(cheevo->description);
|
|
CHEEVOS_FREE(cheevo->badge);
|
|
CHEEVOS_FREE(cheevo->memaddr);
|
|
}
|
|
|
|
cheevo = patchdata->unofficial;
|
|
|
|
for (i = 0, count = patchdata->unofficial_count; i < count; i++, cheevo++)
|
|
{
|
|
CHEEVOS_FREE(cheevo->title);
|
|
CHEEVOS_FREE(cheevo->description);
|
|
CHEEVOS_FREE(cheevo->badge);
|
|
CHEEVOS_FREE(cheevo->memaddr);
|
|
}
|
|
|
|
lboard = patchdata->lboards;
|
|
|
|
for (i = 0, count = patchdata->lboard_count; i < count; i++, lboard++)
|
|
{
|
|
CHEEVOS_FREE(lboard->title);
|
|
CHEEVOS_FREE(lboard->description);
|
|
CHEEVOS_FREE(lboard->format);
|
|
CHEEVOS_FREE(lboard->mem);
|
|
}
|
|
|
|
CHEEVOS_FREE(patchdata->core);
|
|
CHEEVOS_FREE(patchdata->unofficial);
|
|
CHEEVOS_FREE(patchdata->lboards);
|
|
CHEEVOS_FREE(patchdata->richpresence_script);
|
|
CHEEVOS_FREE(patchdata->title);
|
|
|
|
patchdata->game_id = 0;
|
|
patchdata->console_id = 0;
|
|
patchdata->core = NULL;
|
|
patchdata->unofficial = NULL;
|
|
patchdata->lboards = NULL;
|
|
patchdata->core_count = 0;
|
|
patchdata->unofficial_count = 0;
|
|
patchdata->lboard_count = 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Deactivates unlocked cheevos
|
|
*****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
int is_element;
|
|
rcheevos_unlock_cb_t unlock_cb;
|
|
void* userdata;
|
|
} rcheevos_deactivate_t;
|
|
|
|
static int rcheevos_deactivate_index(void* userdata, unsigned int index)
|
|
{
|
|
rcheevos_deactivate_t* ud = (rcheevos_deactivate_t*)userdata;
|
|
|
|
ud->is_element = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int rcheevos_deactivate_number(void* userdata,
|
|
const char* number, size_t length)
|
|
{
|
|
rcheevos_deactivate_t* ud = (rcheevos_deactivate_t*)userdata;
|
|
unsigned id = 0;
|
|
|
|
if (ud->is_element)
|
|
{
|
|
ud->is_element = 0;
|
|
id = (unsigned)strtol(number, NULL, 10);
|
|
|
|
ud->unlock_cb(id, ud->userdata);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void rcheevos_deactivate_unlocks(const char* json, rcheevos_unlock_cb_t unlock_cb, void* userdata)
|
|
{
|
|
static const jsonsax_handlers_t handlers =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
rcheevos_deactivate_index,
|
|
NULL,
|
|
rcheevos_deactivate_number,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
rcheevos_deactivate_t ud;
|
|
|
|
ud.is_element = 0;
|
|
ud.unlock_cb = unlock_cb;
|
|
ud.userdata = userdata;
|
|
|
|
jsonsax_parse(json, &handlers, (void*)&ud);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Returns the game ID
|
|
*****************************************************************************/
|
|
|
|
unsigned chevos_get_gameid(const char* json)
|
|
{
|
|
char gameid[32];
|
|
|
|
if (rcheevos_get_value(json, CHEEVOS_JSON_KEY_GAMEID, gameid, sizeof(gameid)) != 0)
|
|
return 0;
|
|
|
|
return (unsigned)strtol(gameid, NULL, 10);
|
|
}
|