replace coroutines with tasks (#13178)

This commit is contained in:
Jamiras 2021-11-01 11:02:40 -06:00 committed by GitHub
parent abce5e1c6f
commit 20264aaced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1618 additions and 2114 deletions

View File

@ -2120,7 +2120,6 @@ ifeq ($(HAVE_NETWORKING), 1)
OBJ += cheevos/cheevos.o \
cheevos/cheevos_client.o \
cheevos/cheevos_menu.o \
cheevos/cheevos_parser.o \
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
deps/rcheevos/src/rcheevos/alloc.o \
deps/rcheevos/src/rcheevos/compat.o \
@ -2141,7 +2140,6 @@ ifeq ($(HAVE_NETWORKING), 1)
deps/rcheevos/src/rapi/rc_api_common.o \
deps/rcheevos/src/rapi/rc_api_runtime.o \
deps/rcheevos/src/rapi/rc_api_user.o \
deps/rcheevos/src/rurl/url.o
ifeq ($(HAVE_LUA), 1)
DEFINES += -DHAVE_LUA \

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -20,13 +20,29 @@
RETRO_BEGIN_DECLS
typedef void (*rcheevos_client_callback)(void* userdata);
void rcheevos_client_initialize(void);
void rcheevos_client_login_with_password(const char* username, const char* password,
rcheevos_client_callback callback, void* userdata);
void rcheevos_client_login_with_token(const char* username, const char* token,
rcheevos_client_callback callback, void* userdata);
void rcheevos_client_identify_game(const char* hash, rcheevos_client_callback callback, void* userdata);
void rcheevos_client_initialize_runtime(unsigned game_id, rcheevos_client_callback callback, void* userdata);
void rcheevos_client_start_session(unsigned game_id);
void rcheevos_client_award_achievement(unsigned achievement_id);
void rcheevos_client_submit_lboard_entry(unsigned leaderboard_id, int value);
void rcheevos_client_fetch_badges(rcheevos_client_callback callback, void* userdata);
void rcheevos_log_url(const char* api, const char* url);
void rcheevos_get_user_agent(rcheevos_locals_t *locals, char *buffer, size_t len);
RETRO_END_DECLS
#endif /* __RARCH_CHEEVOS_MENU_H */

View File

@ -61,7 +61,8 @@ enum
{
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2,
RCHEEVOS_ACTIVE_UNSUPPORTED = 1 << 3
};
typedef struct rcheevos_racheevo_t
@ -94,20 +95,45 @@ typedef struct rcheevos_ralboard_t
unsigned format;
} rcheevos_ralboard_t;
typedef struct rcheevos_rapatchdata_t
{
char* title;
rcheevos_racheevo_t* core;
rcheevos_racheevo_t* unofficial;
rcheevos_ralboard_t* lboards;
char* richpresence_script;
unsigned game_id;
unsigned console_id;
unsigned core_count;
unsigned unofficial_count;
unsigned lboard_count;
} rcheevos_rapatchdata_t;
enum rcheevos_load_state
{
RCHEEVOS_LOAD_STATE_NONE,
RCHEEVOS_LOAD_STATE_IDENTIFYING_GAME,
RCHEEVOS_LOAD_STATE_FETCHING_GAME_DATA,
RCHEEVOS_LOAD_STATE_STARTING_SESSION,
RCHEEVOS_LOAD_STATE_FETCHING_BADGES,
RCHEEVOS_LOAD_STATE_DONE,
RCHEEVOS_LOAD_STATE_UNKNOWN_GAME,
RCHEEVOS_LOAD_STATE_NETWORK_ERROR,
RCHEEVOS_LOAD_STATE_LOGIN_FAILED,
RCHEEVOS_LOAD_STATE_ABORTED
};
typedef struct rcheevos_load_info_t
{
enum rcheevos_load_state state;
int hashes_tried;
int outstanding_requests;
#ifdef HAVE_THREADS
slock_t* request_lock;
#endif
} rcheevos_load_info_t;
typedef struct rcheevos_game_info_t
{
int id;
int console_id;
char* title;
char hash[33];
rcheevos_racheevo_t* achievements;
rcheevos_ralboard_t* leaderboards;
unsigned achievement_count;
unsigned leaderboard_count;
} rcheevos_game_info_t;
#ifdef HAVE_MENU
@ -124,18 +150,15 @@ void rcheevos_menu_reset_badges(void);
typedef struct rcheevos_locals_t
{
rc_runtime_t runtime; /* rcheevos runtime state */
rcheevos_rapatchdata_t patchdata; /* achievement/leaderboard data from the server */
rcheevos_game_info_t game; /* information about the current game */
rc_libretro_memory_regions_t memory;/* achievement addresses to core memory mappings */
retro_task_t* task; /* load task */
#ifdef HAVE_THREADS
slock_t* task_lock; /* mutex for starting/stopping load task */
enum event_command queued_command; /* action queued by background thread to be run on main thread */
#endif
char username[32]; /* case-corrected username */
char token[32]; /* user's session token */
char hash[33]; /* retroachievements hash for current content */
char user_agent_prefix[128]; /* RetroArch/OS version information */
char user_agent_core[256]; /* RetroArch/OS/Core version information */
@ -145,16 +168,28 @@ typedef struct rcheevos_locals_t
unsigned menuitem_count; /* current number of items in the menuitems array */
#endif
rcheevos_load_info_t load_info; /* load info */
bool hardcore_active; /* hardcore functionality is active */
bool loaded; /* load task has completed */
bool core_supports; /* false if core explicitly disables achievements */
bool network_error; /* hash lookup or login failed with network error */
bool leaderboards_enabled; /* leaderboards are enabled */
bool leaderboard_notifications; /* leaderboard notifications are enabled */
bool leaderboard_trackers; /* leaderboard trackers are enabled */
} rcheevos_locals_t;
rcheevos_locals_t* get_rcheevos_locals(void);
void rcheevos_begin_load_state(enum rcheevos_load_state state);
int rcheevos_end_load_state(void);
bool rcheevos_load_aborted(void);
#ifdef HAVE_THREADS
#define CHEEVOS_LOCK(l) do { slock_lock(l); } while (0)
#define CHEEVOS_UNLOCK(l) do { slock_unlock(l); } while (0)
#else
#define CHEEVOS_LOCK(l)
#define CHEEVOS_UNLOCK(l)
#endif
RETRO_END_DECLS

View File

@ -35,6 +35,7 @@ enum rcheevos_menuitem_bucket
RCHEEVOS_MENUITEM_BUCKET_LOCKED,
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED,
RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED,
RCHEEVOS_MENUITEM_BUCKET_UNOFFICIAL,
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED,
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE,
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE
@ -44,7 +45,7 @@ static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
{
cheevo->menu_progress = 0;
if (!cheevo->memaddr)
if (cheevo->active & RCHEEVOS_ACTIVE_UNSUPPORTED)
{
/* non-active unsupported achievement */
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED;
@ -67,7 +68,10 @@ static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
}
/* active achievement */
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_LOCKED;
if (cheevo->active & RCHEEVOS_ACTIVE_UNOFFICIAL)
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNOFFICIAL;
else
cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_LOCKED;
trigger = rc_runtime_get_achievement(&rcheevos_locals->runtime, cheevo->id);
if (trigger)
@ -88,29 +92,17 @@ static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
}
}
static void rcheevos_menu_update_buckets(bool cheevos_test_unofficial)
static void rcheevos_menu_update_buckets(void)
{
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
rcheevos_racheevo_t* cheevo = rcheevos_locals->game.achievements;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->game.achievement_count;
while (cheevo < stop)
{
rcheevos_menu_update_bucket(cheevo);
++cheevo;
}
if (cheevos_test_unofficial)
{
cheevo = rcheevos_locals->patchdata.unofficial;
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
while (cheevo < stop)
{
rcheevos_menu_update_bucket(cheevo);
++cheevo;
}
}
}
bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
@ -165,18 +157,8 @@ bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
void rcheevos_menu_reset_badges(void)
{
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
while (cheevo < stop)
{
if (cheevo->menu_badge_texture)
video_driver_texture_unload(&cheevo->menu_badge_texture);
++cheevo;
}
cheevo = rcheevos_locals->patchdata.unofficial;
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
rcheevos_racheevo_t* cheevo = rcheevos_locals->game.achievements;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->game.achievement_count;
while (cheevo < stop)
{
@ -250,6 +232,7 @@ static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
switch (cheevo->menu_bucket)
{
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
case RCHEEVOS_MENUITEM_BUCKET_UNOFFICIAL:
case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
@ -292,27 +275,15 @@ static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
}
static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
bool cheevos_test_unofficial, enum rcheevos_menuitem_bucket bucket)
enum rcheevos_menuitem_bucket bucket)
{
const settings_t *settings = config_get_ptr();
rcheevos_racheevo_t* cheevo = rcheevos_locals->patchdata.core;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->patchdata.core_count;
bool processing_unofficial = false;
rcheevos_racheevo_t* cheevo = rcheevos_locals->game.achievements;
rcheevos_racheevo_t* stop = cheevo + rcheevos_locals->game.achievement_count;
const unsigned first_index = rcheevos_locals->menuitem_count;
do
while (cheevo < stop)
{
if (cheevo == stop)
{
if (!cheevos_test_unofficial || processing_unofficial)
break;
processing_unofficial = true;
cheevo = rcheevos_locals->patchdata.unofficial;
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
continue;
}
if (cheevo->menu_bucket == bucket)
{
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, cheevo);
@ -371,7 +342,7 @@ static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
/* fallthrough to default */
default:
if (processing_unofficial)
if (cheevo->active & RCHEEVOS_ACTIVE_UNOFFICIAL)
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
else if (!(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
@ -388,7 +359,7 @@ static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
}
++cheevo;
} while (true);
}
}
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset)
@ -472,7 +443,6 @@ void rcheevos_menu_populate(void* data)
{
const retro_time_t now = cpu_features_get_time_usec();
const retro_time_t recent_unlock_time = now - (10 * 60 * 1000000); /* 10 minutes ago */
bool processing_unofficial = false;
rcheevos_racheevo_t* cheevo = NULL;
rcheevos_racheevo_t* stop = NULL;
@ -494,25 +464,14 @@ void rcheevos_menu_populate(void* data)
}
/* update the bucket for each achievement */
rcheevos_menu_update_buckets(cheevos_test_unofficial);
rcheevos_menu_update_buckets();
/* count items in each bucket */
cheevo = rcheevos_locals->patchdata.core;
stop = cheevo + rcheevos_locals->patchdata.core_count;
cheevo = rcheevos_locals->game.achievements;
stop = cheevo + rcheevos_locals->game.achievement_count;
do
while (cheevo < stop)
{
if (cheevo == stop)
{
if (!cheevos_test_unofficial || processing_unofficial)
break;
processing_unofficial = true;
cheevo = rcheevos_locals->patchdata.unofficial;
stop = cheevo + rcheevos_locals->patchdata.unofficial_count;
continue;
}
switch (cheevo->menu_bucket)
{
case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
@ -528,6 +487,7 @@ void rcheevos_menu_populate(void* data)
break;
case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
case RCHEEVOS_MENUITEM_BUCKET_UNOFFICIAL:
++num_locked;
break;
@ -545,14 +505,12 @@ void rcheevos_menu_populate(void* data)
}
++cheevo;
} while(true);
}
if (!rcheevos_locals->menuitems)
{
/* reserve space for all achievements and up to 6 headers before we need to realloc */
rcheevos_locals->menuitem_capacity = rcheevos_locals->patchdata.core_count + 6;
if (cheevos_test_unofficial)
rcheevos_locals->menuitem_capacity += rcheevos_locals->patchdata.unofficial_count;
rcheevos_locals->menuitem_capacity = rcheevos_locals->game.achievement_count + 6;
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
@ -570,7 +528,7 @@ void rcheevos_menu_populate(void* data)
rcheevos_menu_append_header(rcheevos_locals,
MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY);
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE);
}
@ -580,7 +538,7 @@ void rcheevos_menu_populate(void* data)
rcheevos_menu_append_header(rcheevos_locals,
MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY);
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED);
}
@ -590,7 +548,7 @@ void rcheevos_menu_populate(void* data)
rcheevos_menu_append_header(rcheevos_locals,
MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY);
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE);
}
@ -603,8 +561,23 @@ void rcheevos_menu_populate(void* data)
MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY);
}
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_LOCKED);
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_UNOFFICIAL);
}
/* unsupported */
if (num_unsupported)
{
if (rcheevos_locals->menuitem_count > 0)
{
rcheevos_menu_append_header(rcheevos_locals,
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY);
}
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED);
}
/* unlocked */
@ -616,7 +589,7 @@ void rcheevos_menu_populate(void* data)
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY);
}
rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
rcheevos_menu_append_items(rcheevos_locals,
RCHEEVOS_MENUITEM_BUCKET_UNLOCKED);
}
@ -662,7 +635,7 @@ void rcheevos_menu_populate(void* data)
MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE,
FILE_TYPE_NONE, 0, 0);
}
else if (rcheevos_locals->network_error)
else if (rcheevos_locals->load_info.state == RCHEEVOS_LOAD_STATE_NETWORK_ERROR)
{
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETWORK_ERROR),
@ -670,7 +643,7 @@ void rcheevos_menu_populate(void* data)
MENU_ENUM_LABEL_NETWORK_ERROR,
FILE_TYPE_NONE, 0, 0);
}
else if (!rcheevos_locals->patchdata.game_id)
else if (!rcheevos_locals->game.id)
{
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN_GAME),

View File

@ -1,692 +0,0 @@
#include "cheevos_parser.h"
#include "cheevos_locals.h"
#include <encodings/utf.h>
#include <formats/rjson.h>
#include <string/stdstring.h>
#include <compat/strl.h>
#include "../deps/rcheevos/include/rcheevos.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_USERNAME 0x7c8da264U
#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
typedef struct
{
char* value;
int is_key;
size_t length;
unsigned key_hash;
} rcheevos_getvalueud_t;
#define CHEEVOS_RJSON_OPTIONS \
/* Inside the field RichPresencePatch newlines are
* encoded as '\r\n'. This will filter the \r out. */ \
RJSON_OPTION_IGNORE_STRING_CARRIAGE_RETURN \
/* This matches the behavior of the previously used
* json parser. It is probably not required */ \
| RJSON_OPTION_ALLOW_TRAILING_DATA
/*****************************************************************************
Gets a value in a JSON
*****************************************************************************/
static uint32_t rcheevos_djb2(const char* str, size_t length)
{
const unsigned char* aux = (const unsigned char*)str;
uint32_t hash = 5381;
while (length--)
hash = (hash << 5) + hash + *aux++;
return hash;
}
static bool 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 true;
}
static bool rcheevos_getvalue_string(void* userdata,
const char* string, size_t length)
{
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
if (ud->is_key && ud->length > length)
{
strlcpy(ud->value, string, ud->length);
ud->is_key = 2;
return false;
}
return true;
}
static bool rcheevos_getvalue_boolean(void* userdata, bool istrue)
{
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
if (ud->is_key)
{
if (istrue && ud->length > 4)
{
strlcpy(ud->value, "true", ud->length);
ud->is_key = 2;
return false;
}
if (!istrue && ud->length > 5)
{
strlcpy(ud->value, "false", ud->length);
ud->is_key = 2;
return false;
}
}
return true;
}
static bool rcheevos_getvalue_null(void* userdata)
{
rcheevos_getvalueud_t* ud = (rcheevos_getvalueud_t*)userdata;
if (ud->is_key && ud->length > 4)
{
strlcpy(ud->value, "null", ud->length);
ud->is_key = 2;
return false;
}
return true;
}
static int rcheevos_get_value(const char* json, unsigned key_hash,
char* value, size_t length)
{
rcheevos_getvalueud_t ud;
ud.key_hash = key_hash;
ud.is_key = 0;
ud.value = value;
ud.length = length;
*value = 0;
rjson_parse_quick(json, &ud, CHEEVOS_RJSON_OPTIONS,
rcheevos_getvalue_key,
rcheevos_getvalue_string,
rcheevos_getvalue_string, /* number */
NULL, NULL, NULL, NULL,
rcheevos_getvalue_boolean,
rcheevos_getvalue_null, NULL);
return (ud.is_key == 2 ? 0 : -1);
}
/*****************************************************************************
Returns the token or the error message
*****************************************************************************/
int rcheevos_get_token(const char* json, char* username, size_t username_length,
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) +
rcheevos_get_value(json, CHEEVOS_JSON_KEY_USERNAME, username, username_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 bool rcheevos_count_end_array(void* userdata)
{
rcheevos_countud_t* ud = (rcheevos_countud_t*)userdata;
ud->in_cheevos = 0;
ud->in_lboards = 0;
return true;
}
static bool 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 true;
}
static bool 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 true;
}
static bool rcheevos_count_cheevos(const char* json,
unsigned* core_count, unsigned* unofficial_count,
unsigned* lboard_count, int* has_error)
{
bool 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 = rjson_parse_quick(json, &ud, CHEEVOS_RJSON_OPTIONS,
rcheevos_count_key,
NULL,
rcheevos_count_number,
NULL, NULL, NULL,
rcheevos_count_end_array,
NULL, NULL, NULL);
*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
{
int in_cheevos;
int in_lboards;
int lboard_had_id;
unsigned core_count;
unsigned unofficial_count;
unsigned lboard_count;
unsigned cheevo_flags;
const char* lboard_format;
const char** field_string;
unsigned* field_unsigned;
rcheevos_racheevo_t cheevo;
rcheevos_ralboard_t lboard;
rcheevos_rapatchdata_t* patchdata;
} rcheevos_readud_t;
static bool rcheevos_new_cheevo(rcheevos_readud_t* ud)
{
rcheevos_racheevo_t* cheevo = NULL;
if (ud->cheevo_flags == 3)
cheevo = ud->patchdata->core + ud->core_count++;
else if (ud->cheevo_flags == 5)
cheevo = ud->patchdata->unofficial + ud->unofficial_count++;
ud->cheevo_flags = 0;
if (!cheevo
|| !ud->cheevo.title
|| !ud->cheevo.description
|| !ud->cheevo.badge
|| !ud->cheevo.memaddr)
{
CHEEVOS_FREE(ud->cheevo.title);
CHEEVOS_FREE(ud->cheevo.description);
CHEEVOS_FREE(ud->cheevo.badge);
CHEEVOS_FREE(ud->cheevo.memaddr);
memset(&ud->cheevo, 0, sizeof(ud->cheevo));
return (cheevo ? false : true);
}
*cheevo = ud->cheevo;
memset(&ud->cheevo, 0, sizeof(ud->cheevo));
return true;
}
static bool rcheevos_new_lboard(rcheevos_readud_t* ud)
{
rcheevos_ralboard_t* lboard = NULL;
if (ud->lboard_had_id)
lboard = ud->patchdata->lboards + ud->lboard_count++;
ud->lboard_had_id = 0;
if (!lboard
|| !ud->lboard.title
|| !ud->lboard.description
|| !ud->lboard.mem)
{
CHEEVOS_FREE(ud->lboard.title);
CHEEVOS_FREE(ud->lboard.description);
CHEEVOS_FREE(ud->lboard.mem);
memset(&ud->lboard, 0, sizeof(ud->lboard));
CHEEVOS_FREE(ud->lboard_format);
ud->lboard_format = NULL;
return (lboard ? false : true);
}
*lboard = ud->lboard;
memset(&ud->lboard, 0, sizeof(ud->lboard));
if (ud->lboard_format)
{
lboard->format = rc_parse_format(ud->lboard_format);
CHEEVOS_FREE(ud->lboard_format);
ud->lboard_format = NULL;
}
return true;
}
static bool 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 true;
}
static bool rcheevos_read_end_array(void* userdata)
{
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
ud->in_cheevos = 0;
ud->in_lboards = 0;
return true;
}
static bool rcheevos_read_key(void* userdata,
const char* name, size_t length)
{
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
uint32_t hash = rcheevos_djb2(name, length);
ud->field_unsigned = NULL;
ud->field_string = 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->field_unsigned = &ud->patchdata->console_id;
break;
case CHEEVOS_JSON_KEY_RICHPRESENCE:
ud->field_string = (const char**)&ud->patchdata->richpresence_script;
break;
case CHEEVOS_JSON_KEY_ID:
if (ud->in_cheevos)
ud->field_unsigned = &ud->cheevo.id;
else if (ud->in_lboards)
{
ud->field_unsigned = &ud->lboard.id;
ud->lboard_had_id = 1;
}
else
ud->field_unsigned = &ud->patchdata->game_id;
break;
case CHEEVOS_JSON_KEY_MEMADDR:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.memaddr;
break;
case CHEEVOS_JSON_KEY_MEM:
if (ud->in_lboards)
ud->field_string = &ud->lboard.mem;
break;
case CHEEVOS_JSON_KEY_TITLE:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.title;
else if (ud->in_lboards)
ud->field_string = &ud->lboard.title;
else
ud->field_string = (const char**)&ud->patchdata->title;
break;
case CHEEVOS_JSON_KEY_DESCRIPTION:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.description;
else if (ud->in_lboards)
ud->field_string = &ud->lboard.description;
break;
case CHEEVOS_JSON_KEY_POINTS:
if (ud->in_cheevos)
ud->field_unsigned = &ud->cheevo.points;
break;
/* UNUSED
case CHEEVOS_JSON_KEY_AUTHOR:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.author;
break;
case CHEEVOS_JSON_KEY_MODIFIED:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.modified;
break;
case CHEEVOS_JSON_KEY_CREATED:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.created;
break; */
case CHEEVOS_JSON_KEY_BADGENAME:
if (ud->in_cheevos)
ud->field_string = &ud->cheevo.badge;
break;
case CHEEVOS_JSON_KEY_FLAGS:
if (ud->in_cheevos)
ud->field_unsigned = &ud->cheevo_flags;
break;
case CHEEVOS_JSON_KEY_FORMAT:
if (ud->in_lboards)
ud->field_string = &ud->lboard_format;
break;
default:
break;
}
return true;
}
static bool rcheevos_read_string(void* userdata,
const char* string, size_t length)
{
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
if (ud->field_string)
{
if (*ud->field_string)
CHEEVOS_FREE((*ud->field_string));
*ud->field_string = strdup(string);
ud->field_string = NULL;
}
return true;
}
static bool rcheevos_read_number(void* userdata,
const char* number, size_t length)
{
rcheevos_readud_t* ud = (rcheevos_readud_t*)userdata;
if (ud->field_unsigned)
{
*ud->field_unsigned = (unsigned)strtol(number, NULL, 10);
ud->field_unsigned = NULL;
}
return true;
}
int rcheevos_get_patchdata(const char* json, rcheevos_rapatchdata_t* patchdata)
{
rcheevos_readud_t ud;
bool 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 || 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. */
memset(&ud, 0, sizeof(ud));
ud.patchdata = patchdata;
res = rjson_parse_quick(json, &ud, CHEEVOS_RJSON_OPTIONS,
rcheevos_read_key,
rcheevos_read_string,
rcheevos_read_number,
NULL, rcheevos_read_end_object,
NULL, rcheevos_read_end_array,
NULL, NULL, NULL);
CHEEVOS_FREE(ud.cheevo.title);
CHEEVOS_FREE(ud.cheevo.description);
CHEEVOS_FREE(ud.cheevo.badge);
CHEEVOS_FREE(ud.cheevo.memaddr);
CHEEVOS_FREE(ud.lboard.title);
CHEEVOS_FREE(ud.lboard.description);
CHEEVOS_FREE(ud.lboard.mem);
CHEEVOS_FREE(ud.lboard_format);
if (!res)
{
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->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->title = NULL;
patchdata->richpresence_script = 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 bool rcheevos_deactivate_elements_begin(void* userdata)
{
rcheevos_deactivate_t* ud = (rcheevos_deactivate_t*)userdata;
ud->is_element = 1;
return true;
}
static bool rcheevos_deactivate_elements_stop(void* userdata)
{
rcheevos_deactivate_t* ud = (rcheevos_deactivate_t*)userdata;
ud->is_element = 0;
return true;
}
static bool 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)
{
id = (unsigned)strtol(number, NULL, 10);
ud->unlock_cb(id, ud->userdata);
}
return true;
}
void rcheevos_deactivate_unlocks(const char* json, rcheevos_unlock_cb_t unlock_cb, void* userdata)
{
rcheevos_deactivate_t ud;
ud.is_element = 0;
ud.unlock_cb = unlock_cb;
ud.userdata = userdata;
rjson_parse_quick(json, &ud, CHEEVOS_RJSON_OPTIONS,
NULL, NULL,
rcheevos_deactivate_number,
rcheevos_deactivate_elements_stop,
rcheevos_deactivate_elements_stop,
rcheevos_deactivate_elements_begin,
rcheevos_deactivate_elements_stop,
NULL, NULL, NULL);
}
/*****************************************************************************
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);
}

View File

@ -1,39 +0,0 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2015-2018 - Andre Leiradella
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __RARCH_CHEEVOS_PARSER_H
#define __RARCH_CHEEVOS_PARSER_H
#include "cheevos_locals.h"
RETRO_BEGIN_DECLS
typedef void (*rcheevos_unlock_cb_t)(unsigned id, void* userdata);
int rcheevos_get_json_error(const char* json, char* token, size_t length);
int rcheevos_get_token(const char* json, char* username, size_t username_length,
char* token, size_t length);
int rcheevos_get_patchdata(const char* json, rcheevos_rapatchdata_t* patchdata);
void rcheevos_free_patchdata(rcheevos_rapatchdata_t* patchdata);
void rcheevos_deactivate_unlocks(const char* json, rcheevos_unlock_cb_t unlock_cb, void* userdata);
unsigned chevos_get_gameid(const char* json);
RETRO_END_DECLS
#endif

View File

@ -1,75 +0,0 @@
#ifndef __RARCH_CHEEVOS_CORO_H
#define __RARCH_CHEEVOS_CORO_H
/*
Released under the CC0: https://creativecommons.org/publicdomain/zero/1.0/
*/
/* Use at the beginning of the coroutine, you must have declared a variable rcheevos_coro_t* coro */
#define CORO_ENTER() \
{ \
CORO_again: ; \
switch ( coro->step ) { \
case CORO_BEGIN: ;
/* Use to define labels which are targets to GOTO and GOSUB */
#define CORO_SUB( x ) \
case x: ;
/* Use at the end of the coroutine */
#define CORO_LEAVE() \
} } \
do { return 0; } while ( 0 )
/* Go to the x label */
#define CORO_GOTO( x ) \
do { \
coro->step = ( x ); \
goto CORO_again; \
} while ( 0 )
/* Go to a subroutine, execution continues until the subroutine returns via RET */
#define CORO_GOSUB( x ) \
do { \
coro->stack[ coro->sp++ ] = __LINE__; \
coro->step = ( x ); \
goto CORO_again; \
case __LINE__: ; \
} while ( 0 )
/* Returns from a subroutine */
#define CORO_RET() \
do { \
coro->step = coro->stack[ --coro->sp ]; \
goto CORO_again; \
} while ( 0 )
/* Yields to the caller, execution continues from this point when the coroutine is resumed */
#define CORO_YIELD() \
do { \
coro->step = __LINE__; \
return 1; \
case __LINE__: ; \
} while ( 0 )
/* The coroutine entry point, never use 0 as a label */
#define CORO_BEGIN 0
/* Sets up the coroutine */
#define CORO_SETUP() \
do { \
coro->step = CORO_BEGIN; \
coro->sp = 0; \
} while ( 0 )
#define CORO_STOP() \
do { \
return 0; \
} while ( 0 )
/* Add this macro to your rcheevos_coro_t structure containing the variables for the coroutine */
#define CORO_FIELDS \
int step, sp; \
int stack[ 8 ];
#endif /* __RARCH_CHEEVOS_CORO_H */

View File

@ -193,7 +193,6 @@ ACHIEVEMENTS
#include "../cheevos/cheevos.c"
#include "../cheevos/cheevos_client.c"
#include "../cheevos/cheevos_menu.c"
#include "../cheevos/cheevos_parser.c"
#include "../deps/rcheevos/src/rapi/rc_api_common.c"
#include "../deps/rcheevos/src/rapi/rc_api_runtime.c"
@ -214,7 +213,6 @@ ACHIEVEMENTS
#include "../deps/rcheevos/src/rcheevos/trigger.c"
#include "../deps/rcheevos/src/rcheevos/value.c"
#include "../deps/rcheevos/src/rhash/hash.c"
#include "../deps/rcheevos/src/rurl/url.c"
#endif