mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 07:59:42 +00:00
(cheevos) use rc_client for state management (#15912)
* use rc_client for achievement processing * log disconnect/reconnect messages * address compiler warnings * address c89 warning * address c89 warning
This commit is contained in:
parent
936ff84204
commit
bbe7afcd82
1081
cheevos/cheevos.c
1081
cheevos/cheevos.c
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@ bool rcheevos_set_serialized_data(void* buffer);
|
||||
bool rcheevos_unload(void);
|
||||
|
||||
void rcheevos_test(void);
|
||||
void rcheevos_idle(void);
|
||||
|
||||
void rcheevos_reset_game(bool widgets_ready);
|
||||
void rcheevos_refresh_memory(void);
|
||||
@ -44,6 +45,8 @@ void rcheevos_hardcore_enabled_changed(void);
|
||||
void rcheevos_toggle_hardcore_paused(void);
|
||||
bool rcheevos_hardcore_active(void);
|
||||
|
||||
void rcheevos_spectating_changed(void);
|
||||
|
||||
void rcheevos_validate_config_settings(void);
|
||||
|
||||
void rcheevos_leaderboard_trackers_visibility_changed(void);
|
||||
@ -53,7 +56,7 @@ bool rcheevos_get_support_cheevos(void);
|
||||
|
||||
const char* rcheevos_get_hash(void);
|
||||
int rcheevos_get_richpresence(char *s, size_t len);
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked);
|
||||
uintptr_t rcheevos_get_badge_texture(const char* badge, bool locked, bool download_if_missing);
|
||||
|
||||
uint8_t* rcheevos_patch_address(unsigned address);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
* Copyright (C) 2019-2023 - Brian Weiss
|
||||
*
|
||||
* 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-
|
||||
@ -46,8 +46,8 @@
|
||||
* THIS WILL DISCLOSE THE USER'S PASSWORD, TAKE CARE! */
|
||||
#undef CHEEVOS_LOG_PASSWORD
|
||||
|
||||
/* Define this macro to load a JSON file from disk instead of downloading
|
||||
* from retroachievements.org. */
|
||||
/* Define this macro with a string to load a JSON file from disk with
|
||||
* that name instead of downloading the game data from retroachievements.org. */
|
||||
#undef CHEEVOS_JSON_OVERRIDE
|
||||
|
||||
/* Define this macro with a string to save the JSON file to disk with
|
||||
@ -57,6 +57,8 @@
|
||||
/* Define this macro to log downloaded badge images. */
|
||||
#undef CHEEVOS_LOG_BADGES
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
|
||||
/* Number of usecs to wait between posting rich presence to the site. */
|
||||
/* Keep consistent with SERVER_PING_FREQUENCY from RAIntegration. */
|
||||
#define CHEEVOS_PING_FREQUENCY 2 * 60 * 1000000
|
||||
@ -99,12 +101,16 @@ typedef struct rcheevos_async_io_request
|
||||
char type;
|
||||
} rcheevos_async_io_request;
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#define RCHEEVOS_CONCURRENT_BADGE_DOWNLOADS 2
|
||||
#else
|
||||
#define RCHEEVOS_CONCURRENT_BADGE_DOWNLOADS 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
|
||||
typedef struct rcheevos_fetch_badge_state
|
||||
{
|
||||
unsigned badge_fetch_index;
|
||||
@ -141,6 +147,8 @@ static void rcheevos_async_fetch_badge_callback(
|
||||
struct rcheevos_async_io_request* request,
|
||||
http_transfer_data_t* data, char buffer[], size_t buffer_size);
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
/****************************
|
||||
* user agent construction *
|
||||
****************************/
|
||||
@ -218,6 +226,10 @@ void rcheevos_get_user_agent(rcheevos_locals_t *locals,
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
/****************************
|
||||
* server interaction *
|
||||
****************************/
|
||||
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
#ifndef CHEEVOS_LOG_PASSWORD
|
||||
static void rcheevos_filter_url_param(char* url, char* param)
|
||||
@ -255,7 +267,7 @@ static void rcheevos_filter_url_param(char* url, char* param)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void rcheevos_log_url(const char* api, const char* url)
|
||||
void rcheevos_log_url(const char* url)
|
||||
{
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
#ifdef CHEEVOS_LOG_PASSWORD
|
||||
@ -268,7 +280,6 @@ void rcheevos_log_url(const char* api, const char* url)
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "GET %s\n", copy);
|
||||
#endif
|
||||
#else
|
||||
(void)api;
|
||||
(void)url;
|
||||
#endif
|
||||
}
|
||||
@ -305,11 +316,428 @@ static void rcheevos_log_post_url(const char* url, const char* post)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* dispatch *
|
||||
****************************/
|
||||
|
||||
#ifdef HAVE_RC_CLIENT
|
||||
|
||||
typedef struct rc_client_http_task_data_t
|
||||
{
|
||||
rc_client_server_callback_t callback;
|
||||
void* callback_data;
|
||||
} rc_client_http_task_data_t;
|
||||
|
||||
static void rcheevos_client_http_task_callback(retro_task_t* task,
|
||||
void* task_data, void* user_data, const char* error)
|
||||
{
|
||||
rc_client_http_task_data_t* callback_data = (rc_client_http_task_data_t*)user_data;
|
||||
http_transfer_data_t* http_data = (http_transfer_data_t*)task_data;
|
||||
rc_api_server_response_t server_response;
|
||||
memset(&server_response, 0, sizeof(server_response));
|
||||
|
||||
if (!http_data)
|
||||
{
|
||||
callback_data->callback(&server_response, callback_data->callback_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
server_response.body = http_data->data;
|
||||
server_response.body_length = http_data->len;
|
||||
server_response.http_status_code = http_data->status;
|
||||
|
||||
callback_data->callback(&server_response, callback_data->callback_data);
|
||||
}
|
||||
|
||||
free(callback_data);
|
||||
}
|
||||
|
||||
#ifdef CHEEVOS_SAVE_JSON
|
||||
static void rcheevos_client_http_task_save_callback(retro_task_t* task,
|
||||
void* task_data, void* user_data, const char* error)
|
||||
{
|
||||
http_transfer_data_t* http_data = (http_transfer_data_t*)task_data;
|
||||
|
||||
if (http_data)
|
||||
{
|
||||
filestream_write_file(CHEEVOS_SAVE_JSON, http_data->data, http_data->len);
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Captured game info. Wrote %u bytes to %s\n", http_data->len, CHEEVOS_SAVE_JSON);
|
||||
}
|
||||
|
||||
rcheevos_client_http_task_callback(task, task_data, user_data, error);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_JSON_OVERRIDE
|
||||
void rcheevos_client_http_load_response(const rc_api_request_t* request,
|
||||
rc_client_server_callback_t callback, void* callback_data)
|
||||
{
|
||||
size_t size = 0;
|
||||
char* contents;
|
||||
FILE* file = fopen(CHEEVOS_JSON_OVERRIDE, "rb");
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
contents = (char*)malloc(size + 1);
|
||||
fread((void*)contents, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
contents[size] = 0;
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Loaded game info. Read %u bytes to %s\n", size, CHEEVOS_JSON_OVERRIDE);
|
||||
|
||||
callback(contents, 200, callback_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
void rcheevos_client_server_call(const rc_api_request_t* request,
|
||||
rc_client_server_callback_t callback, void* callback_data, rc_client_t* client)
|
||||
{
|
||||
rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rc_client_http_task_data_t* taskdata = malloc(sizeof(rc_client_http_task_data_t));
|
||||
taskdata->callback = callback;
|
||||
taskdata->callback_data = callback_data;
|
||||
|
||||
if (request->post_data)
|
||||
{
|
||||
rcheevos_log_post_url(request->url, request->post_data);
|
||||
|
||||
#ifdef CHEEVOS_JSON_OVERRIDE
|
||||
if (strstr(request->post_data, "r=patch"))
|
||||
{
|
||||
rcheevos_client_http_load_response(request, callback, callback_data);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_SAVE_JSON
|
||||
if (strstr(request->post_data, "r=patch"))
|
||||
{
|
||||
task_push_http_post_transfer_with_user_agent(request->url,
|
||||
request->post_data, true, "POST", rcheevos_locals->user_agent_core,
|
||||
rcheevos_client_http_task_save_callback, taskdata);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
task_push_http_post_transfer_with_user_agent(request->url,
|
||||
request->post_data, true, "POST", rcheevos_locals->user_agent_core,
|
||||
rcheevos_client_http_task_callback, taskdata);
|
||||
|
||||
#ifdef HAVE_PRESENCE
|
||||
if (strstr(request->post_data, "r=ping"))
|
||||
presence_update(PRESENCE_RETROACHIEVEMENTS);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_log_url(request->url);
|
||||
task_push_http_transfer_with_user_agent(request->url,
|
||||
true, "GET", rcheevos_locals->user_agent_core,
|
||||
rcheevos_client_http_task_callback, taskdata);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************
|
||||
* downloading badges *
|
||||
****************************/
|
||||
|
||||
typedef struct rc_client_download_queue_t
|
||||
{
|
||||
const rc_client_t* client;
|
||||
const rc_client_game_t* game;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_t* lock;
|
||||
#endif
|
||||
|
||||
rc_client_achievement_list_t* list;
|
||||
uint32_t pass;
|
||||
uint32_t bucket_index;
|
||||
uint32_t achievement_index;
|
||||
uint32_t count;
|
||||
uint32_t outstanding_requests;
|
||||
} rc_client_download_queue_t;
|
||||
|
||||
static void rcheevos_client_fetch_next_badge(rc_client_download_queue_t* queue);
|
||||
|
||||
typedef struct rc_client_download_task_data_t
|
||||
{
|
||||
rc_client_download_queue_t* queue;
|
||||
char badge_fullpath[PATH_MAX_LENGTH];
|
||||
char badge_name[32];
|
||||
} rc_client_download_task_data_t;
|
||||
|
||||
static void rcheevos_client_download_task_callback(retro_task_t* task,
|
||||
void* task_data, void* user_data, const char* error)
|
||||
{
|
||||
rc_client_download_task_data_t* callback_data = (rc_client_download_task_data_t*)user_data;
|
||||
http_transfer_data_t* http_data = (http_transfer_data_t*)task_data;
|
||||
|
||||
if (!http_data)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "No data received for badge %s\n", callback_data->badge_name);
|
||||
}
|
||||
else if (http_data->status != 200)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "HTTP status code %d for badge %s\n", http_data->status, callback_data->badge_name);
|
||||
}
|
||||
else if (!filestream_write_file(callback_data->badge_fullpath, http_data->data, http_data->len))
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Error writing %s\n", callback_data->badge_fullpath);
|
||||
}
|
||||
|
||||
if (callback_data->queue)
|
||||
{
|
||||
#ifdef HAVE_THREADS
|
||||
slock_lock(callback_data->queue->lock);
|
||||
#endif
|
||||
callback_data->queue->count++;
|
||||
#ifdef HAVE_THREADS
|
||||
slock_unlock(callback_data->queue->lock);
|
||||
#endif
|
||||
|
||||
rcheevos_client_fetch_next_badge(callback_data->queue);
|
||||
}
|
||||
|
||||
free(callback_data);
|
||||
}
|
||||
|
||||
static bool rcheevos_client_download_badge(rc_client_download_queue_t* queue,
|
||||
const char* url, const char* badge_name)
|
||||
{
|
||||
rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rc_client_download_task_data_t* taskdata;
|
||||
char badge_fullpath[512] = "";
|
||||
char* badge_fullname;
|
||||
size_t badge_fullname_size;
|
||||
|
||||
/* make sure the directory exists */
|
||||
fill_pathname_application_special(badge_fullpath, sizeof(badge_fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!path_is_directory(badge_fullpath))
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Creating %s\n", badge_fullpath);
|
||||
path_mkdir(badge_fullpath);
|
||||
}
|
||||
|
||||
fill_pathname_slash(badge_fullpath, sizeof(badge_fullpath));
|
||||
badge_fullname = badge_fullpath + strlen(badge_fullpath);
|
||||
badge_fullname_size = sizeof(badge_fullpath) - (badge_fullname - badge_fullpath);
|
||||
snprintf(badge_fullname, badge_fullname_size, "%s" FILE_PATH_PNG_EXTENSION, badge_name);
|
||||
|
||||
if (path_is_valid(badge_fullpath))
|
||||
return false;
|
||||
|
||||
#ifdef CHEEVOS_LOG_BADGES
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Downloading %s from %s\n", badge_name, url);
|
||||
#else
|
||||
rcheevos_log_url(url);
|
||||
#endif
|
||||
|
||||
taskdata = (rc_client_download_task_data_t*)malloc(sizeof(*taskdata));
|
||||
taskdata->queue = queue;
|
||||
strlcpy(taskdata->badge_fullpath, badge_fullpath, sizeof(taskdata->badge_fullpath));
|
||||
strlcpy(taskdata->badge_name, badge_name, sizeof(taskdata->badge_name));
|
||||
|
||||
task_push_http_transfer_with_user_agent(url,
|
||||
true, "GET", rcheevos_locals->user_agent_core,
|
||||
rcheevos_client_download_task_callback, taskdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rcheevos_client_download_badge_from_url(const char* url, const char* badge_name)
|
||||
{
|
||||
rcheevos_client_download_badge(NULL, url, badge_name);
|
||||
}
|
||||
|
||||
static void rcheevos_client_fetch_next_badge(rc_client_download_queue_t* queue)
|
||||
{
|
||||
rc_client_achievement_bucket_t* bucket;
|
||||
rc_client_achievement_t* achievement;
|
||||
const char* next_badge;
|
||||
char badge_name[32];
|
||||
char url[256];
|
||||
bool done = false;
|
||||
|
||||
do
|
||||
{
|
||||
next_badge = NULL;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_lock(queue->lock);
|
||||
#endif
|
||||
/* if the game is no longer loaded, stop processing the queue */
|
||||
if (queue->game != rc_client_get_game_info(queue->client))
|
||||
queue->pass = 2;
|
||||
|
||||
while (queue->pass < 2)
|
||||
{
|
||||
if (queue->bucket_index >= queue->list->num_buckets)
|
||||
{
|
||||
queue->bucket_index = 0;
|
||||
queue->pass++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bucket = &queue->list->buckets[queue->bucket_index];
|
||||
|
||||
if (queue->achievement_index >= bucket->num_achievements)
|
||||
{
|
||||
queue->achievement_index = 0;
|
||||
queue->bucket_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
achievement = bucket->achievements[queue->achievement_index++];
|
||||
if (!achievement->badge_name[0])
|
||||
continue;
|
||||
|
||||
if (queue->pass == 0)
|
||||
{
|
||||
/* first pass - get all unlocked badges */
|
||||
if (rc_client_achievement_get_image_url(achievement, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url)) != RC_OK)
|
||||
continue;
|
||||
|
||||
next_badge = achievement->badge_name;
|
||||
}
|
||||
else if (achievement->unlock_time)
|
||||
{
|
||||
/* second pass - don't need locked badge for achievement player has already unlocked */
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* second pass - get locked badge */
|
||||
if (rc_client_achievement_get_image_url(achievement, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url, sizeof(url)) != RC_OK)
|
||||
continue;
|
||||
|
||||
snprintf(badge_name, sizeof(badge_name), "%s_lock", achievement->badge_name);
|
||||
next_badge = badge_name;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!next_badge)
|
||||
{
|
||||
if (--queue->outstanding_requests == 0)
|
||||
done = true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_unlock(queue->lock);
|
||||
#endif
|
||||
|
||||
if (next_badge)
|
||||
{
|
||||
/* if the badge already exists (download_badge returns false), continue
|
||||
* looping to the next item. otherwise, a download was queued, so break
|
||||
* out of the loop. */
|
||||
if (rcheevos_client_download_badge(queue, url, next_badge))
|
||||
break;
|
||||
}
|
||||
} while (next_badge);
|
||||
|
||||
if (done)
|
||||
{
|
||||
/* queue complete */
|
||||
if (queue->count)
|
||||
{
|
||||
CHEEVOS_LOG(RCHEEVOS_TAG "Downloaded %u badges\n", queue->count);
|
||||
}
|
||||
rc_client_destroy_achievement_list(queue->list);
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_free(queue->lock);
|
||||
#endif
|
||||
|
||||
free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_client_download_placeholder_badge(void)
|
||||
{
|
||||
char url[256] = "";
|
||||
|
||||
if (rc_client_achievement_get_image_url(NULL, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url)) == RC_OK)
|
||||
rcheevos_client_download_badge(NULL, url, "00000");
|
||||
}
|
||||
|
||||
void rcheevos_client_download_game_badge(const rc_client_game_t* game)
|
||||
{
|
||||
char url[256] = "";
|
||||
char badge_name[16];
|
||||
|
||||
if (game && rc_client_game_get_image_url(game, url, sizeof(url)) == RC_OK)
|
||||
{
|
||||
snprintf(badge_name, sizeof(badge_name), "i%s", game->badge_name);
|
||||
rcheevos_client_download_badge(NULL, url, badge_name);
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_client_download_achievement_badges(rc_client_t* client)
|
||||
{
|
||||
rc_client_download_queue_t* queue;
|
||||
uint32_t i;
|
||||
|
||||
#if !defined(HAVE_GFX_WIDGETS) /* we always want badges if widgets are enabled */
|
||||
settings_t* settings = config_get_ptr();
|
||||
/* User has explicitly disabled badges */
|
||||
if (!settings->bools.cheevos_badges_enable)
|
||||
return;
|
||||
|
||||
/* badges are only needed for xmb and ozone menus */
|
||||
if (!string_is_equal(settings->arrays.menu_driver, "xmb") &&
|
||||
!string_is_equal(settings->arrays.menu_driver, "ozone"))
|
||||
return;
|
||||
#endif /* !defined(HAVE_GFX_WIDGETS) */
|
||||
|
||||
queue = (rc_client_download_queue_t*)calloc(1, sizeof(*queue));
|
||||
queue->client = client;
|
||||
queue->game = rc_client_get_game_info(client);
|
||||
queue->list = rc_client_create_achievement_list(client,
|
||||
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
||||
queue->outstanding_requests = RCHEEVOS_CONCURRENT_BADGE_DOWNLOADS;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
queue->lock = slock_new();
|
||||
#endif
|
||||
|
||||
for (i = 0; i < RCHEEVOS_CONCURRENT_BADGE_DOWNLOADS; i++)
|
||||
rcheevos_client_fetch_next_badge(queue);
|
||||
}
|
||||
|
||||
#undef RCHEEVOS_CONCURRENT_BADGE_DOWNLOADS
|
||||
|
||||
void rcheevos_client_download_achievement_badge(const char* badge_name, bool locked)
|
||||
{
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
char locked_badge_name[32];
|
||||
|
||||
memset(&image_request, 0, sizeof(image_request));
|
||||
image_request.image_type = locked ? RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED : RC_IMAGE_TYPE_ACHIEVEMENT;
|
||||
image_request.image_name = badge_name;
|
||||
|
||||
if (locked)
|
||||
{
|
||||
snprintf(locked_badge_name, sizeof(locked_badge_name), "%s_lock", badge_name);
|
||||
badge_name = locked_badge_name;
|
||||
}
|
||||
|
||||
if (rc_api_init_fetch_image_request(&request, &image_request) == RC_OK)
|
||||
rcheevos_client_download_badge(NULL, request.url, badge_name);
|
||||
|
||||
rc_api_destroy_request(&request);
|
||||
}
|
||||
|
||||
#else /* !HAVE_RC_CLIENT */
|
||||
|
||||
static void rcheevos_async_begin_http_request(rcheevos_async_io_request* request)
|
||||
{
|
||||
if (request->request.post_data)
|
||||
@ -1858,3 +2286,5 @@ void rcheevos_client_submit_lboard_entry(unsigned leaderboard_id,
|
||||
"Error submitting leaderboard");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
* Copyright (C) 2019-2023 - Brian Weiss
|
||||
*
|
||||
* 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-
|
||||
@ -20,6 +20,19 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
#ifdef HAVE_RC_CLIENT
|
||||
|
||||
void rcheevos_client_download_placeholder_badge(void);
|
||||
void rcheevos_client_download_game_badge(const rc_client_game_t* game);
|
||||
void rcheevos_client_download_achievement_badges(rc_client_t* client);
|
||||
void rcheevos_client_download_achievement_badge(const char* badge_name, bool locked);
|
||||
void rcheevos_client_download_badge_from_url(const char* url, const char* badge_name);
|
||||
|
||||
void rcheevos_client_server_call(const rc_api_request_t* request,
|
||||
rc_client_server_callback_t callback, void* callback_data, rc_client_t* client);
|
||||
|
||||
#else
|
||||
|
||||
typedef void (*rcheevos_client_callback)(void* userdata);
|
||||
|
||||
void rcheevos_client_initialize(void);
|
||||
@ -39,9 +52,11 @@ 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);
|
||||
void rcheevos_log_url(const char* url);
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
void rcheevos_get_user_agent(rcheevos_locals_t* locals, char* buffer, size_t len);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2015-2018 - Andre Leiradella
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
* Copyright (C) 2019-2023 - Brian Weiss
|
||||
*
|
||||
* 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-
|
||||
@ -17,6 +17,9 @@
|
||||
#ifndef __RARCH_CHEEVOS_LOCALS_H
|
||||
#define __RARCH_CHEEVOS_LOCALS_H
|
||||
|
||||
#define HAVE_RC_CLIENT 1
|
||||
|
||||
#include "../deps/rcheevos/include/rc_client.h"
|
||||
#include "../deps/rcheevos/include/rc_runtime.h"
|
||||
#include "../deps/rcheevos/src/rc_libretro.h"
|
||||
|
||||
@ -57,6 +60,7 @@ RETRO_BEGIN_DECLS
|
||||
* State *
|
||||
************************************************************************/
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
enum
|
||||
{
|
||||
RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
|
||||
@ -117,6 +121,8 @@ enum rcheevos_load_state
|
||||
RCHEEVOS_LOAD_STATE_ABORTED
|
||||
};
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
enum rcheevos_summary_notif
|
||||
{
|
||||
RCHEEVOS_SUMMARY_ALLGAMES = 0,
|
||||
@ -125,6 +131,8 @@ enum rcheevos_summary_notif
|
||||
RCHEEVOS_SUMMARY_LAST
|
||||
};
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
|
||||
typedef struct rcheevos_load_info_t
|
||||
{
|
||||
enum rcheevos_load_state state;
|
||||
@ -164,19 +172,42 @@ typedef struct rcheevos_menuitem_t
|
||||
|
||||
#endif
|
||||
|
||||
#else /* HAVE_RC_CLIENT */
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
|
||||
typedef struct rcheevos_menuitem_t
|
||||
{
|
||||
rc_client_achievement_t* achievement;
|
||||
uintptr_t menu_badge_texture;
|
||||
uint32_t subset_id;
|
||||
uint8_t menu_badge_grayscale;
|
||||
enum msg_hash_enums state_label_idx;
|
||||
} rcheevos_menuitem_t;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
typedef struct rcheevos_locals_t
|
||||
{
|
||||
#ifdef HAVE_RC_CLIENT
|
||||
rc_client_t* client; /* rcheevos client state */
|
||||
#else
|
||||
rc_runtime_t runtime; /* rcheevos runtime state */
|
||||
rcheevos_game_info_t game; /* information about the current game */
|
||||
#endif
|
||||
rc_libretro_memory_regions_t memory;/* achievement addresses to core memory mappings */
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
enum event_command queued_command; /* action queued by background thread to be run on main thread */
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
char displayname[32]; /* name to display in messages */
|
||||
char username[32]; /* case-corrected username */
|
||||
char token[32]; /* user's session token */
|
||||
#endif
|
||||
char user_agent_prefix[128]; /* RetroArch/OS version information */
|
||||
char user_agent_core[256]; /* RetroArch/OS/Core version information */
|
||||
|
||||
@ -186,6 +217,10 @@ typedef struct rcheevos_locals_t
|
||||
unsigned menuitem_count; /* current number of items in the menuitems array */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_RC_CLIENT
|
||||
bool hardcore_allowed; /* prevents enabling hardcore if illegal settings detected */
|
||||
#else
|
||||
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
unsigned active_lboard_trackers; /* bit mask of active leaderboard tracker ids */
|
||||
rcheevos_racheevo_t* tracker_achievement;
|
||||
@ -199,15 +234,20 @@ typedef struct rcheevos_locals_t
|
||||
#ifdef HAVE_GFX_WIDGETS
|
||||
bool assign_new_trackers; /* a new leaderboard was started and needs a tracker assigned */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
bool core_supports; /* false if core explicitly disables achievements */
|
||||
} rcheevos_locals_t;
|
||||
|
||||
rcheevos_locals_t* get_rcheevos_locals(void);
|
||||
|
||||
#ifndef HAVE_RC_CLIENT
|
||||
void rcheevos_begin_load_state(enum rcheevos_load_state state);
|
||||
int rcheevos_end_load_state(void);
|
||||
bool rcheevos_load_aborted(void);
|
||||
|
||||
void rcheevos_show_mastery_placard(void);
|
||||
#endif
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2019-2021 - Brian Weiss
|
||||
* Copyright (C) 2019-2023 - Brian Weiss
|
||||
*
|
||||
* 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-
|
||||
@ -17,6 +17,7 @@
|
||||
#include <retro_assert.h>
|
||||
|
||||
#include "cheevos_locals.h"
|
||||
#include "cheevos_client.h"
|
||||
|
||||
#include "../gfx/gfx_display.h"
|
||||
#include "../file_path_special.h"
|
||||
@ -26,10 +27,487 @@
|
||||
#include "cheevos.h"
|
||||
|
||||
#include "../deps/rcheevos/include/rc_runtime_types.h"
|
||||
#include "../deps/rcheevos/include/rc_api_runtime.h"
|
||||
|
||||
#include "../menu/menu_driver.h"
|
||||
#include "../menu/menu_entries.h"
|
||||
|
||||
#include <features/features_cpu.h>
|
||||
#include <retro_assert.h>
|
||||
|
||||
/* if menu_badge_grayscale is set to a value other than 1 or 0, it's a counter for the number of
|
||||
* frames since the last time we checked for the file. When the counter reaches this value, we'll
|
||||
* check for the file again. */
|
||||
#define MENU_BADGE_RETRY_RELOAD_FRAMES 64
|
||||
|
||||
#ifdef HAVE_RC_CLIENT
|
||||
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char* buffer, size_t buffer_size)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
const rcheevos_menuitem_t* menuitem = &rcheevos_locals->menuitems[menu_offset];
|
||||
const rc_client_achievement_t* cheevo = menuitem->achievement;
|
||||
if (cheevo)
|
||||
{
|
||||
if (cheevo->measured_progress[0])
|
||||
{
|
||||
snprintf(buffer, buffer_size, "%s - %s",
|
||||
msg_hash_to_str(menuitem->state_label_idx), cheevo->measured_progress);
|
||||
}
|
||||
else
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), buffer_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char* buffer, size_t buffer_size)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count && buffer)
|
||||
{
|
||||
const rcheevos_menuitem_t* menuitem = &rcheevos_locals->menuitems[menu_offset];
|
||||
if (menuitem->achievement)
|
||||
{
|
||||
strlcpy(buffer, menuitem->achievement->description, buffer_size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
buffer[0] = '\0';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void rcheevos_menu_reset_badges(void)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_locals->menuitems;
|
||||
rcheevos_menuitem_t* stop = menuitem + rcheevos_locals->menuitem_count;
|
||||
|
||||
while (menuitem < stop)
|
||||
{
|
||||
if (menuitem->menu_badge_texture)
|
||||
{
|
||||
video_driver_texture_unload(&menuitem->menu_badge_texture);
|
||||
menuitem->menu_badge_texture = 0;
|
||||
menuitem->menu_badge_grayscale = MENU_BADGE_RETRY_RELOAD_FRAMES;
|
||||
}
|
||||
++menuitem;
|
||||
}
|
||||
}
|
||||
|
||||
static rcheevos_menuitem_t* rcheevos_menu_allocate(
|
||||
rcheevos_locals_t* rcheevos_locals)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem;
|
||||
|
||||
if (rcheevos_locals->menuitem_count == rcheevos_locals->menuitem_capacity)
|
||||
{
|
||||
if (rcheevos_locals->menuitems)
|
||||
{
|
||||
rcheevos_menuitem_t* new_menuitems;
|
||||
rcheevos_locals->menuitem_capacity += 32;
|
||||
new_menuitems = (rcheevos_menuitem_t*)realloc(rcheevos_locals->menuitems,
|
||||
rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (new_menuitems)
|
||||
rcheevos_locals->menuitems = new_menuitems;
|
||||
else
|
||||
{
|
||||
/* realloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity -= 32;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_locals->menuitem_capacity = 64;
|
||||
rcheevos_locals->menuitems = (rcheevos_menuitem_t*)
|
||||
malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
|
||||
|
||||
if (!rcheevos_locals->menuitems)
|
||||
{
|
||||
/* malloc failed */
|
||||
CHEEVOS_ERR(RCHEEVOS_TAG " could not allocate space for %u menu items\n",
|
||||
rcheevos_locals->menuitem_capacity);
|
||||
rcheevos_locals->menuitem_capacity = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menuitem = &rcheevos_locals->menuitems[rcheevos_locals->menuitem_count++];
|
||||
memset(menuitem, 0, sizeof(*menuitem));
|
||||
return menuitem;
|
||||
}
|
||||
|
||||
static void rcheevos_menu_append_header(rcheevos_locals_t* rcheevos_locals,
|
||||
enum msg_hash_enums label, uint32_t subset_id)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals);
|
||||
if (menuitem)
|
||||
{
|
||||
menuitem->state_label_idx = label;
|
||||
menuitem->subset_id = subset_id;
|
||||
}
|
||||
}
|
||||
|
||||
static void rcheevos_menu_update_badge(rcheevos_menuitem_t* menuitem, bool download_if_missing)
|
||||
{
|
||||
const char* badge_name = "00000";
|
||||
bool badge_grayscale = false;
|
||||
|
||||
if (menuitem->achievement)
|
||||
badge_name = menuitem->achievement->badge_name;
|
||||
|
||||
switch (menuitem->state_label_idx)
|
||||
{
|
||||
case MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY:
|
||||
case MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY:
|
||||
case MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY:
|
||||
case MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY:
|
||||
case MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY:
|
||||
badge_grayscale = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
badge_grayscale = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!menuitem->menu_badge_texture || menuitem->menu_badge_grayscale != badge_grayscale)
|
||||
{
|
||||
uintptr_t new_badge_texture =
|
||||
rcheevos_get_badge_texture(badge_name, badge_grayscale, download_if_missing);
|
||||
|
||||
if (new_badge_texture)
|
||||
{
|
||||
if (menuitem->menu_badge_texture)
|
||||
video_driver_texture_unload(&menuitem->menu_badge_texture);
|
||||
|
||||
menuitem->menu_badge_texture = new_badge_texture;
|
||||
menuitem->menu_badge_grayscale = badge_grayscale;
|
||||
}
|
||||
/* menu_badge_grayscale is overloaded such
|
||||
* that any value greater than 1 indicates
|
||||
* the server default image is being used */
|
||||
else if (menuitem->menu_badge_grayscale < 2)
|
||||
{
|
||||
if (menuitem->menu_badge_texture)
|
||||
video_driver_texture_unload(&menuitem->menu_badge_texture);
|
||||
|
||||
/* requested badge is not available, check for server default */
|
||||
menuitem->menu_badge_texture =
|
||||
rcheevos_get_badge_texture("00000", false, false);
|
||||
|
||||
if (menuitem->menu_badge_texture)
|
||||
menuitem->menu_badge_grayscale = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = &rcheevos_locals->menuitems[menu_offset];
|
||||
|
||||
/* if we're using the placeholder badge, check to see if the real badge
|
||||
* has become available (do this roughly once a second) */
|
||||
if (menuitem->menu_badge_grayscale >= 2)
|
||||
{
|
||||
if (++menuitem->menu_badge_grayscale >= MENU_BADGE_RETRY_RELOAD_FRAMES)
|
||||
{
|
||||
menuitem->menu_badge_grayscale = 2;
|
||||
rcheevos_menu_update_badge(menuitem, false);
|
||||
}
|
||||
}
|
||||
|
||||
return menuitem->menu_badge_texture;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate_hardcore_pause_submenu(void* data)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
const settings_t* settings = config_get_ptr();
|
||||
const bool cheevos_hardcore_mode_enable = settings->bools.cheevos_hardcore_mode_enable;
|
||||
|
||||
if (cheevos_hardcore_mode_enable && rc_client_get_game_info(rcheevos_locals->client))
|
||||
{
|
||||
if (rc_client_get_hardcore_enabled(rcheevos_locals->client))
|
||||
{
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0, NULL);
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
|
||||
MENU_SETTING_ACTION_CLOSE, 0, 0, NULL);
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_RESUME),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_RESUME,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rcheevos_menu_populate(void* data)
|
||||
{
|
||||
menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
|
||||
rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
const rc_client_game_t* game = rc_client_get_game_info(rcheevos_locals->client);
|
||||
const settings_t* settings = config_get_ptr();
|
||||
|
||||
rc_client_achievement_list_t* list = rc_client_create_achievement_list(rcheevos_locals->client,
|
||||
RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
RC_CLIENT_ACHIEVEMENT_LIST_GROUPING_PROGRESS);
|
||||
uint32_t i, j;
|
||||
|
||||
rcheevos_menu_reset_badges();
|
||||
rcheevos_locals->menuitem_count = 0;
|
||||
|
||||
if (game && game->id != 0)
|
||||
{
|
||||
/* first menu item is the Pause/Resume Hardcore option (unless hardcore is completely disabled) */
|
||||
if (settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable)
|
||||
{
|
||||
if (rc_client_get_hardcore_enabled(rcheevos_locals->client))
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0, NULL);
|
||||
else
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU),
|
||||
MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
|
||||
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < list->num_buckets; i++)
|
||||
{
|
||||
if (list->num_buckets > 1)
|
||||
{
|
||||
enum msg_hash_enums label;
|
||||
switch (list->buckets[i].bucket_type)
|
||||
{
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE:
|
||||
label = MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
rcheevos_menu_append_header(rcheevos_locals, label, list->buckets[i].subset_id);
|
||||
}
|
||||
|
||||
for (j = 0; j < list->buckets[i].num_achievements; j++)
|
||||
{
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals);
|
||||
if (!menuitem)
|
||||
break;
|
||||
|
||||
menuitem->achievement = list->buckets[i].achievements[j];
|
||||
|
||||
switch (list->buckets[i].bucket_type)
|
||||
{
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED:
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED:
|
||||
if (menuitem->achievement->unlocked & RC_CLIENT_ACHIEVEMENT_UNLOCKED_HARDCORE)
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY_HARDCORE;
|
||||
else
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED:
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
|
||||
break;
|
||||
case RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL:
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
|
||||
break;
|
||||
default:
|
||||
menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
|
||||
break;
|
||||
}
|
||||
|
||||
rcheevos_menu_update_badge(menuitem, true);
|
||||
}
|
||||
}
|
||||
|
||||
rc_client_destroy_achievement_list(list);
|
||||
|
||||
if (rcheevos_locals->menuitem_count > 0)
|
||||
{
|
||||
char buffer[128];
|
||||
unsigned idx = 0;
|
||||
/* convert to menu entries */
|
||||
rcheevos_menuitem_t* menuitem = rcheevos_locals->menuitems;
|
||||
rcheevos_menuitem_t* stop = menuitem +
|
||||
rcheevos_locals->menuitem_count;
|
||||
|
||||
do
|
||||
{
|
||||
if (menuitem->achievement)
|
||||
menu_entries_append(info->list, menuitem->achievement->title,
|
||||
menuitem->achievement->description,
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0, NULL);
|
||||
else
|
||||
{
|
||||
if (menuitem->subset_id)
|
||||
{
|
||||
const rc_client_subset_t* subset =
|
||||
rc_client_get_subset_info(rcheevos_locals->client, menuitem->subset_id);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "----- %s - %s -----",
|
||||
subset ? subset->title : "Unknown Subset",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buffer, sizeof(buffer), "----- %s -----",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
}
|
||||
|
||||
menu_entries_append(info->list, buffer, "",
|
||||
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
|
||||
MENU_SETTINGS_CHEEVOS_START + idx, 0, 0, NULL);
|
||||
}
|
||||
|
||||
++idx;
|
||||
++menuitem;
|
||||
} while (menuitem != stop);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no achievements found */
|
||||
if (!rcheevos_locals->core_supports)
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE),
|
||||
MENU_ENUM_LABEL_CANNOT_ACTIVATE_ACHIEVEMENTS_WITH_THIS_CORE,
|
||||
FILE_TYPE_NONE, 0, 0, NULL);
|
||||
else if (!rc_client_get_game_info(rcheevos_locals->client))
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN_GAME),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_UNKNOWN_GAME),
|
||||
MENU_ENUM_LABEL_UNKNOWN_GAME,
|
||||
FILE_TYPE_NONE, 0, 0, NULL);
|
||||
else if (!rc_client_get_user_info(rcheevos_locals->client))
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_LOGGED_IN),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NOT_LOGGED_IN),
|
||||
MENU_ENUM_LABEL_NOT_LOGGED_IN,
|
||||
FILE_TYPE_NONE, 0, 0, NULL);
|
||||
else
|
||||
menu_entries_append(info->list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY),
|
||||
MENU_ENUM_LABEL_NO_ACHIEVEMENTS_TO_DISPLAY,
|
||||
FILE_TYPE_NONE, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uintptr_t rcheevos_get_badge_texture(const char* badge, bool locked, bool download_if_missing)
|
||||
{
|
||||
char badge_file[24];
|
||||
char fullpath[PATH_MAX_LENGTH];
|
||||
uintptr_t tex = 0;
|
||||
|
||||
if (!badge || !badge[0])
|
||||
return 0;
|
||||
|
||||
/* OpenGL driver crashes if gfx_display_reset_textures_list is called on a background thread */
|
||||
retro_assert(task_is_on_main_thread());
|
||||
|
||||
snprintf(badge_file, sizeof(badge_file), "%s%s%s", badge,
|
||||
locked ? "_lock" : "", FILE_PATH_PNG_EXTENSION);
|
||||
|
||||
fill_pathname_application_special(fullpath, sizeof(fullpath),
|
||||
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
|
||||
|
||||
if (!gfx_display_reset_textures_list(badge_file, fullpath,
|
||||
&tex, TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL))
|
||||
{
|
||||
if (download_if_missing)
|
||||
{
|
||||
if (badge[0] == 'i')
|
||||
{
|
||||
/* rcheevos_client_download_game_badge expects a rc_client_game_t, not the badge name.
|
||||
* call rc_api_init_fetch_image_request directly */
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
|
||||
memset(&image_request, 0, sizeof(image_request));
|
||||
image_request.image_type = RC_IMAGE_TYPE_GAME;
|
||||
image_request.image_name = &badge[1];
|
||||
result = rc_api_init_fetch_image_request(&request, &image_request);
|
||||
|
||||
if (result == RC_OK)
|
||||
rcheevos_client_download_badge_from_url(request.url, badge);
|
||||
}
|
||||
else
|
||||
{
|
||||
rcheevos_client_download_achievement_badge(badge, locked);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
#else /* !HAVE_RC_CLIENT */
|
||||
|
||||
enum rcheevos_menuitem_bucket
|
||||
{
|
||||
RCHEEVOS_MENUITEM_BUCKET_UNKNOWN = 0,
|
||||
@ -42,11 +520,6 @@ enum rcheevos_menuitem_bucket
|
||||
RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE
|
||||
};
|
||||
|
||||
/* if menu_badge_grayscale is set to a value other than 1 or 0, it's a counter for the number of
|
||||
* frames since the last time we checked for the file. When the counter reaches this value, we'll
|
||||
* check for the file again. */
|
||||
#define MENU_BADGE_RETRY_RELOAD_FRAMES 64
|
||||
|
||||
static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
|
||||
{
|
||||
cheevo->menu_progress = 0;
|
||||
@ -78,7 +551,7 @@ static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
|
||||
trigger = rc_runtime_get_achievement(&rcheevos_locals->runtime, cheevo->id);
|
||||
if (trigger)
|
||||
{
|
||||
if (trigger->measured_value && trigger->measured_target)
|
||||
if (trigger->measured_value && trigger->measured_value != 0xFFFFFFFF && trigger->measured_target)
|
||||
{
|
||||
const unsigned long clamped_value = (unsigned long)
|
||||
MIN(trigger->measured_value, trigger->measured_target);
|
||||
@ -108,7 +581,7 @@ static void rcheevos_menu_update_buckets(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t buffer_size)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
@ -119,14 +592,14 @@ bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
{
|
||||
if (cheevo->menu_progress)
|
||||
{
|
||||
const int written = snprintf(buffer, len, "%s - ",
|
||||
const int written = snprintf(buffer, buffer_size, "%s - ",
|
||||
msg_hash_to_str(menuitem->state_label_idx));
|
||||
if (len - written > 0)
|
||||
if (buffer_size - written > 0)
|
||||
rc_runtime_format_achievement_measured(&rcheevos_locals->runtime,
|
||||
cheevo->id, buffer + written, len - written);
|
||||
cheevo->id, buffer + written, buffer_size - written);
|
||||
}
|
||||
else
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), len);
|
||||
strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), buffer_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -138,7 +611,7 @@ bool rcheevos_menu_get_state(unsigned menu_offset, char *buffer, size_t len)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
|
||||
bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t buffer_size)
|
||||
{
|
||||
const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
|
||||
if (menu_offset < rcheevos_locals->menuitem_count)
|
||||
@ -146,7 +619,7 @@ bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
|
||||
const rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
|
||||
if (cheevo && buffer)
|
||||
{
|
||||
strlcpy(buffer, cheevo->description, len);
|
||||
strlcpy(buffer, cheevo->description, buffer_size);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -251,7 +724,7 @@ static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
|
||||
if (!cheevo->menu_badge_texture || cheevo->menu_badge_grayscale != badge_grayscale)
|
||||
{
|
||||
uintptr_t new_badge_texture =
|
||||
rcheevos_get_badge_texture(cheevo->badge, badge_grayscale);
|
||||
rcheevos_get_badge_texture(cheevo->badge, badge_grayscale, false);
|
||||
|
||||
if (new_badge_texture)
|
||||
{
|
||||
@ -271,7 +744,7 @@ static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
|
||||
|
||||
/* requested badge is not available, check for server default */
|
||||
cheevo->menu_badge_texture =
|
||||
rcheevos_get_badge_texture("00000", false);
|
||||
rcheevos_get_badge_texture("00000", false, false);
|
||||
|
||||
if (cheevo->menu_badge_texture)
|
||||
cheevo->menu_badge_grayscale = 2;
|
||||
@ -654,9 +1127,7 @@ void rcheevos_menu_populate(void* data)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_MENU */
|
||||
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked)
|
||||
uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked, bool download_if_missing)
|
||||
{
|
||||
if (badge)
|
||||
{
|
||||
@ -683,3 +1154,7 @@ uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_RC_CLIENT */
|
||||
|
||||
#endif /* HAVE_MENU */
|
||||
|
8
deps/rcheevos/src/rapi/rc_api_common.c
vendored
8
deps/rcheevos/src/rapi/rc_api_common.c
vendored
@ -18,7 +18,7 @@ static char* g_imagehost = NULL;
|
||||
|
||||
/* --- rc_json --- */
|
||||
|
||||
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_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, uint32_t* fields_seen);
|
||||
static int rc_json_parse_array(rc_json_iterator_t* iterator, rc_json_field_t* field);
|
||||
|
||||
static int rc_json_match_char(rc_json_iterator_t* iterator, char c)
|
||||
@ -182,7 +182,7 @@ static int rc_json_get_next_field(rc_json_iterator_t* iterator, rc_json_field_t*
|
||||
return RC_OK;
|
||||
}
|
||||
|
||||
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_object(rc_json_iterator_t* iterator, rc_json_field_t* fields, size_t field_count, uint32_t* fields_seen) {
|
||||
size_t i;
|
||||
uint32_t num_fields = 0;
|
||||
rc_json_field_t field;
|
||||
@ -437,14 +437,14 @@ int rc_json_get_required_unum_array(uint32_t** entries, uint32_t* num_entries, r
|
||||
rc_json_iterator_t iterator;
|
||||
rc_json_field_t array;
|
||||
rc_json_field_t value;
|
||||
unsigned* entry;
|
||||
uint32_t* entry;
|
||||
|
||||
memset(&array, 0, sizeof(array));
|
||||
if (!rc_json_get_required_array(num_entries, &array, response, field, field_name))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (*num_entries) {
|
||||
*entries = (unsigned*)rc_buffer_alloc(&response->buffer, *num_entries * sizeof(unsigned));
|
||||
*entries = (uint32_t*)rc_buffer_alloc(&response->buffer, *num_entries * sizeof(uint32_t));
|
||||
if (!*entries)
|
||||
return RC_OUT_OF_MEMORY;
|
||||
|
||||
|
2
deps/rcheevos/src/rcheevos/alloc.c
vendored
2
deps/rcheevos/src/rcheevos/alloc.c
vendored
@ -78,7 +78,7 @@ void* rc_alloc(void* pointer, int32_t* offset, uint32_t size, uint32_t alignment
|
||||
void** scratch_object_pointer = (void**)((char*)&scratch->objs + scratch_object_pointer_offset);
|
||||
ptr = *scratch_object_pointer;
|
||||
if (!ptr) {
|
||||
int used;
|
||||
int32_t used;
|
||||
ptr = *scratch_object_pointer = rc_alloc_scratch(NULL, &used, size, alignment, scratch, -1);
|
||||
}
|
||||
}
|
||||
|
2
deps/rcheevos/src/rcheevos/runtime.c
vendored
2
deps/rcheevos/src/rcheevos/runtime.c
vendored
@ -128,7 +128,7 @@ int rc_runtime_activate_achievement(rc_runtime_t* self, uint32_t id, const char*
|
||||
rc_runtime_trigger_t* runtime_trigger;
|
||||
rc_parse_state_t parse;
|
||||
uint8_t md5[16];
|
||||
int size;
|
||||
int32_t size;
|
||||
uint32_t i;
|
||||
|
||||
if (memaddr == NULL)
|
||||
|
6
deps/rcheevos/src/rhash/hash.c
vendored
6
deps/rcheevos/src/rhash/hash.c
vendored
@ -1612,7 +1612,7 @@ static int rc_hash_dreamcast(char hash[33], const char* path)
|
||||
}
|
||||
|
||||
static int rc_hash_find_playstation_executable(void* track_handle, const char* boot_key, const char* cdrom_prefix,
|
||||
char exe_name[], uint32_t exe_name_size, unsigned* exe_size)
|
||||
char exe_name[], uint32_t exe_name_size, uint32_t* exe_size)
|
||||
{
|
||||
uint8_t buffer[2048];
|
||||
uint32_t size;
|
||||
@ -1626,7 +1626,7 @@ static int rc_hash_find_playstation_executable(void* track_handle, const char* b
|
||||
if (!sector)
|
||||
return 0;
|
||||
|
||||
size = (unsigned)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
|
||||
size = (uint32_t)rc_cd_read_sector(track_handle, sector, buffer, sizeof(buffer) - 1);
|
||||
buffer[size] = '\0';
|
||||
|
||||
sector = 0;
|
||||
@ -1653,7 +1653,7 @@ static int rc_hash_find_playstation_executable(void* track_handle, const char* b
|
||||
while (!isspace((unsigned char)*ptr) && *ptr != ';')
|
||||
++ptr;
|
||||
|
||||
size = (unsigned)(ptr - start);
|
||||
size = (uint32_t)(ptr - start);
|
||||
if (size >= exe_name_size)
|
||||
size = exe_name_size - 1;
|
||||
|
||||
|
@ -380,7 +380,9 @@ void gfx_widgets_ai_service_overlay_unload(void);
|
||||
void gfx_widgets_update_cheevos_appearance(void);
|
||||
void gfx_widgets_push_achievement(const char *title, const char* subtitle, const char *badge);
|
||||
void gfx_widgets_set_leaderboard_display(unsigned id, const char* value);
|
||||
void gfx_widgets_clear_leaderboard_displays(void);
|
||||
void gfx_widgets_set_challenge_display(unsigned id, const char* badge);
|
||||
void gfx_widgets_clear_challenge_displays(void);
|
||||
void gfx_widget_set_achievement_progress(const char* badge, const char* progress);
|
||||
#endif
|
||||
|
||||
|
@ -28,7 +28,9 @@ typedef struct cheevo_popup
|
||||
{
|
||||
char* title;
|
||||
char* subtitle;
|
||||
char* badge_name;
|
||||
uintptr_t badge;
|
||||
retro_time_t badge_retry;
|
||||
} cheevo_popup;
|
||||
|
||||
enum
|
||||
@ -211,6 +213,21 @@ static void gfx_widget_achievement_popup_frame(void* data, void* userdata)
|
||||
gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP);
|
||||
gfx_display_set_alpha(pure_white, 1.0f);
|
||||
|
||||
/* badge wasn't ready, periodically see if it's become available */
|
||||
if (!state->queue[state->queue_read_index].badge &&
|
||||
state->queue[state->queue_read_index].badge_name)
|
||||
{
|
||||
const retro_time_t next_try = state->queue[state->queue_read_index].badge_retry;
|
||||
const retro_time_t now = cpu_features_get_time_usec();
|
||||
if (next_try == 0 || now > next_try)
|
||||
{
|
||||
/* try again in 250ms */
|
||||
state->queue[state->queue_read_index].badge_retry = now + 250000;
|
||||
state->queue[state->queue_read_index].badge =
|
||||
rcheevos_get_badge_texture(state->queue[state->queue_read_index].badge_name, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Default Badge */
|
||||
if (!state->queue[state->queue_read_index].badge)
|
||||
{
|
||||
@ -365,6 +382,12 @@ static void gfx_widget_achievement_popup_free_current(
|
||||
state->queue[state->queue_read_index].subtitle = NULL;
|
||||
}
|
||||
|
||||
if (state->queue[state->queue_read_index].badge_name)
|
||||
{
|
||||
free(state->queue[state->queue_read_index].badge_name);
|
||||
state->queue[state->queue_read_index].badge_name = NULL;
|
||||
}
|
||||
|
||||
if (state->queue[state->queue_read_index].badge)
|
||||
{
|
||||
video_driver_texture_unload(&state->queue[state->queue_read_index].badge);
|
||||
@ -565,7 +588,7 @@ void gfx_widgets_push_achievement(const char* title, const char* subtitle, const
|
||||
|
||||
/* important - this must be done outside the lock because it has the potential to need to
|
||||
* lock the video thread, which may be waiting for the popup queue lock to render popups */
|
||||
uintptr_t badge_id = rcheevos_get_badge_texture(badge, 0);
|
||||
uintptr_t badge_id = rcheevos_get_badge_texture(badge, false, true);
|
||||
|
||||
if (state->queue_read_index < 0)
|
||||
{
|
||||
@ -601,6 +624,8 @@ void gfx_widgets_push_achievement(const char* title, const char* subtitle, const
|
||||
state->queue[state->queue_write_index].badge = badge_id;
|
||||
state->queue[state->queue_write_index].title = strdup(title);
|
||||
state->queue[state->queue_write_index].subtitle = strdup(subtitle);
|
||||
state->queue[state->queue_write_index].badge_name = badge_id ? NULL : strdup(badge);
|
||||
state->queue[state->queue_write_index].badge_retry = 0;
|
||||
|
||||
state->queue_write_index = (state->queue_write_index + 1) % ARRAY_SIZE(state->queue);
|
||||
|
||||
|
@ -342,6 +342,21 @@ static void gfx_widget_leaderboard_display_frame(void* data, void* userdata)
|
||||
#endif
|
||||
}
|
||||
|
||||
void gfx_widgets_clear_leaderboard_displays(void)
|
||||
{
|
||||
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_lock(state->array_lock);
|
||||
#endif
|
||||
|
||||
state->tracker_count = 0;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_unlock(state->array_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gfx_widgets_set_leaderboard_display(unsigned id, const char* value)
|
||||
{
|
||||
unsigned i;
|
||||
@ -420,6 +435,21 @@ void gfx_widgets_set_leaderboard_display(unsigned id, const char* value)
|
||||
#endif
|
||||
}
|
||||
|
||||
void gfx_widgets_clear_challenge_displays(void)
|
||||
{
|
||||
gfx_widget_leaderboard_display_state_t* state = &p_w_leaderboard_display_st;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_lock(state->array_lock);
|
||||
#endif
|
||||
|
||||
state->challenge_count = 0;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
slock_unlock(state->array_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gfx_widgets_set_challenge_display(unsigned id, const char* badge)
|
||||
{
|
||||
unsigned i;
|
||||
@ -427,7 +457,7 @@ void gfx_widgets_set_challenge_display(unsigned id, const char* badge)
|
||||
|
||||
/* important - this must be done outside the lock because it has the potential to need to
|
||||
* lock the video thread, which may be waiting for the popup queue lock to render popups */
|
||||
uintptr_t badge_id = badge ? rcheevos_get_badge_texture(badge, 0) : 0;
|
||||
uintptr_t badge_id = badge ? rcheevos_get_badge_texture(badge, false, true) : 0;
|
||||
uintptr_t old_badge_id = 0;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
@ -500,7 +530,7 @@ void gfx_widget_set_achievement_progress(const char* badge, const char* progress
|
||||
{
|
||||
/* show indicator */
|
||||
state->progress_tracker.show_until = cpu_features_get_time_usec() + CHEEVO_PROGRESS_TRACKER_DURATION * 1000;
|
||||
state->progress_tracker.image = rcheevos_get_badge_texture(badge, 1);
|
||||
state->progress_tracker.image = rcheevos_get_badge_texture(badge, true, true);
|
||||
|
||||
snprintf(state->progress_tracker.display, sizeof(state->progress_tracker.display), "%s", progress);
|
||||
state->progress_tracker.width = (uint16_t)font_driver_get_message_width(
|
||||
|
@ -195,6 +195,8 @@ void deinit_netplay(void);
|
||||
|
||||
bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data);
|
||||
|
||||
bool netplay_is_spectating(void);
|
||||
|
||||
#ifdef HAVE_NETPLAYDISCOVERY
|
||||
/** Initialize Netplay discovery */
|
||||
bool init_netplay_discovery(void);
|
||||
|
@ -75,6 +75,10 @@
|
||||
#include "../discord.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#include "netplay_private.h"
|
||||
|
||||
#ifdef TCP_NODELAY
|
||||
@ -1953,6 +1957,11 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay,
|
||||
if (!settings->bools.netplay_start_as_spectator)
|
||||
return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING);
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
/* Not going to be promoted to player - let achievement system know that we're spectating */
|
||||
rcheevos_spectating_changed();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4645,6 +4654,10 @@ static void netplay_announce_play_spectate(netplay_t *netplay,
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
rcheevos_spectating_changed();
|
||||
#endif
|
||||
|
||||
RARCH_LOG("[Netplay] %s\n", dmsg);
|
||||
runloop_msg_queue_push(dmsg, 1, 180, false, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
@ -5754,6 +5767,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
else /* YOU && !PLAYING */
|
||||
{
|
||||
#ifdef HAVE_CHEEVOS
|
||||
rcheevos_spectating_changed(); /* should be a no-op, but synchronize anyway */
|
||||
#endif
|
||||
/* I'm no longer playing, but I should already know this */
|
||||
if (netplay->self_mode != NETPLAY_CONNECTION_SPECTATING)
|
||||
{
|
||||
@ -5858,6 +5874,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
runloop_msg_queue_push(dmsg, 1, 180, false, NULL,
|
||||
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
#ifdef HAVE_CHEEVOS
|
||||
rcheevos_spectating_changed(); /* synchronize mode */
|
||||
#endif
|
||||
break;
|
||||
|
||||
case NETPLAY_CMD_DISCONNECT:
|
||||
@ -9028,6 +9047,14 @@ static bool netplay_have_any_active_connection(netplay_t *netplay)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool netplay_is_spectating(void)
|
||||
{
|
||||
/* helper function to check the spectating flag without being blocked by the netplay_driver_ctl guard */
|
||||
net_driver_state_t* net_st = &networking_driver_st;
|
||||
netplay_t* netplay = net_st->data;
|
||||
return (netplay && (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING));
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_driver_ctl
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user