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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 160 additions & 18 deletions cheevos/cheevos.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,74 @@ static void rcheevos_show_subset_completion_placard(const rc_client_subset_t* su
rcheevos_show_completion_placard(subset->title, subset->badge_name);
}

#if defined(HAVE_GFX_WIDGETS)

static void rcheevos_show_achievement_popup(const rc_client_achievement_t* cheevo, float rarity)
{
char title[128], subtitle[96];

if (rarity >= 10.0)
snprintf(title, sizeof(title), "%s - %0.2f%%",
msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), rarity);
else if (rarity > 0.0)
snprintf(title, sizeof(title), "%s - %0.2f%%",
msg_hash_to_str(MSG_RARE_ACHIEVEMENT_UNLOCKED), rarity);
else
strlcpy(title,
msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), sizeof(title));

snprintf(subtitle, sizeof(subtitle), "%s (%lu)", cheevo->title, (unsigned long)cheevo->points);

gfx_widgets_push_achievement(title, subtitle, cheevo->badge_name);

/* if all badges haven't been loaded, preload the next one assuming it will be the next needed */
if (!rcheevos_locals.badges_loaded)
{
const rc_client_achievement_t* next_locked_achievement =
rc_client_get_next_achievement_info(rcheevos_locals.client, cheevo, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
if (next_locked_achievement)
rcheevos_client_download_badge_from_url(next_locked_achievement->badge_url, next_locked_achievement->badge_name);
}
}

struct rcheevos_retry_achievement_info_t
{
uint32_t achievement_id;
float rarity;
};

static void rcheevos_retry_achievement_popup(retro_task_t* task)
{
struct rcheevos_retry_achievement_info_t* info = (struct rcheevos_retry_achievement_info_t*)task->user_data;
const rc_client_achievement_t* cheevo = rc_client_get_achievement_info(rcheevos_locals.client, info->achievement_id);
if (!cheevo)
{
/* achievement not found, assume game unloaded and don't show the popup */
}
else if (task->progress > 4 || rcheevos_is_badge_available(cheevo->badge_name, false))
{
/* badge is available now, or we've reached the retry limit. show the popup */
rcheevos_show_achievement_popup(cheevo, info->rarity);
}
else
{
/* second retry in 200ms, third is 400ms, fourth in 800ms. if not available after 1500ms
* (100+200+400+800), then just show the popup with the placeholder. */
task->progress <<= 1;
task->when = cpu_features_get_time_usec() + 100000 * task->progress; /* first retry in 100ms */
return;
}

/* cleanup the user data */
task->user_data = NULL;
free(info);

/* mark task as complete so it will get cleaned up */
task_set_flags(task, RETRO_TASK_FLG_FINISHED, true);
}

#endif /* HAVE_GFX_WIDGETS */

static void rcheevos_award_achievement(const rc_client_achievement_t* cheevo)
{
const settings_t* settings = config_get_ptr();
Expand All @@ -318,31 +386,37 @@ static void rcheevos_award_achievement(const rc_client_achievement_t* cheevo)
#if defined(HAVE_GFX_WIDGETS)
if (gfx_widgets_ready())
{
char title[128], subtitle[96];
float rarity = rc_client_get_hardcore_enabled(rcheevos_locals.client) ?
cheevo->rarity_hardcore : cheevo->rarity;

if (rarity >= 10.0)
snprintf(title, sizeof(title), "%s - %0.2f%%",
msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), rarity);
else if (rarity > 0.0)
snprintf(title, sizeof(title), "%s - %0.2f%%",
msg_hash_to_str(MSG_RARE_ACHIEVEMENT_UNLOCKED), rarity);
if (rcheevos_locals.badges_loaded || rcheevos_is_badge_available(cheevo->badge_name, false))
{
rcheevos_show_achievement_popup(cheevo, rarity);
}
else
strlcpy(title,
msg_hash_to_str(MSG_ACHIEVEMENT_UNLOCKED), sizeof(title));
{
retro_task_t* task;
rcheevos_client_download_badge_from_url(cheevo->badge_url, cheevo->badge_name);

snprintf(subtitle, sizeof(subtitle), "%s (%lu)", cheevo->title, (unsigned long)cheevo->points);
task = task_init();
if (!task)
{
rcheevos_show_achievement_popup(cheevo, rarity);
}
else
{
struct rcheevos_retry_achievement_info_t* info;
info = (struct rcheevos_retry_achievement_info_t*)malloc(sizeof(*info));
info->achievement_id = cheevo->id;
info->rarity = rarity;

gfx_widgets_push_achievement(title, subtitle, cheevo->badge_name);
task->handler = rcheevos_retry_achievement_popup;
task->user_data = info;
task->progress = 1;
task->when = cpu_features_get_time_usec() + 100000; /* first retry in 100ms */

/* if all badges haven't been loaded, preload the next one assuming it will be the next needed */
if (!rcheevos_locals.badges_loaded)
{
const rc_client_achievement_t* next_locked_achievement =
rc_client_get_next_achievement_info(rcheevos_locals.client, cheevo, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
if (next_locked_achievement)
rcheevos_client_download_badge_from_url(next_locked_achievement->badge_url, next_locked_achievement->badge_name);
task_queue_push(task);
}
}
}
else
Expand Down Expand Up @@ -1432,6 +1506,60 @@ static void rcheevos_client_login_callback(int result,
}
}

#ifdef HAVE_THREADS

void rcheevos_download_next_badge(retro_task_t* task)
{
/* progress: 0 = unlocked images for achievements player hasn't earned
* 1 = locked images for achievements player hasn't earned
* 2 = unlocked images for achievements player has earned
*/
const int bucket = (task->progress == 2) ? RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED : RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED;
const rc_client_achievement_t* first_locked_achievement =
rc_client_get_next_achievement_info(rcheevos_locals.client, task->user_data, bucket);

while (first_locked_achievement)
{
bool result;

if (task->progress == 1)
{
char locked_name[24];
snprintf(locked_name, sizeof(locked_name), "%s_lock", first_locked_achievement->badge_name);
result = rcheevos_client_download_badge_from_url(first_locked_achievement->badge_locked_url, locked_name);
}
else
{
result = rcheevos_client_download_badge_from_url(first_locked_achievement->badge_url, first_locked_achievement->badge_name);
}

if (result)
break;

first_locked_achievement =
rc_client_get_next_achievement_info(rcheevos_locals.client, first_locked_achievement, bucket);
}

if (!first_locked_achievement)
{
if (task->progress == 2 || !rcheevos_is_game_loaded())
{
/* mark task as complete so it will get cleaned up */
task_set_flags(task, RETRO_TASK_FLG_FINISHED, true);
return;
}

task->user_data = NULL; /* restart list */
task->progress++;
}

/* wait 10 seconds, then download the next badge */
task->user_data = (void*)first_locked_achievement;
task->when = cpu_features_get_time_usec() + 10 * 1000000;
}

#endif

static void rcheevos_finalize_game_load(rc_client_t* client)
{
settings_t* settings = config_get_ptr();
Expand All @@ -1448,6 +1576,9 @@ static void rcheevos_finalize_game_load(rc_client_t* client)
const rc_client_achievement_t* first_locked_achievement = NULL;
const rc_client_game_t* game = rc_client_get_game_info(client);
char badge[32];
#ifdef HAVE_THREADS
retro_task_t* task;
#endif

badge[0] = 'i';
strlcpy(&badge[1], game->badge_name, sizeof(badge) - 1);
Expand All @@ -1457,6 +1588,17 @@ static void rcheevos_finalize_game_load(rc_client_t* client)
first_locked_achievement = rc_client_get_next_achievement_info(client, NULL, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED);
if (first_locked_achievement)
rcheevos_client_download_badge_from_url(first_locked_achievement->badge_url, first_locked_achievement->badge_name);

#ifdef HAVE_THREADS
task = task_init();
if (task)
{
task->handler = rcheevos_download_next_badge;
task->user_data = (void*)first_locked_achievement;
task->when = cpu_features_get_time_usec() + 5*60*1000000; /* five minutes */
task_queue_push(task);
}
#endif
}

if (!rc_client_is_processing_required(client))
Expand Down
1 change: 1 addition & 0 deletions cheevos/cheevos.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const char* rcheevos_get_hash(void);
int rcheevos_get_richpresence(char *s, size_t len);
int rcheevos_get_game_badge_url(char *s, size_t len);
uintptr_t rcheevos_get_badge_texture(const char* badge, bool locked, bool download_if_missing);
bool rcheevos_is_badge_available(const char* badge, bool locked);

uint8_t* rcheevos_patch_address(unsigned address);

Expand Down
4 changes: 2 additions & 2 deletions cheevos/cheevos_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,9 @@ bool rcheevos_client_download_badge(rc_client_download_queue_t* queue,
return true;
}

void rcheevos_client_download_badge_from_url(const char* url, const char* badge_name)
bool rcheevos_client_download_badge_from_url(const char* url, const char* badge_name)
{
rcheevos_client_download_badge(NULL, url, badge_name);
return rcheevos_client_download_badge(NULL, url, badge_name);
}

static void rcheevos_client_fetch_next_badge(rc_client_download_queue_t* queue)
Expand Down
2 changes: 1 addition & 1 deletion cheevos/cheevos_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RETRO_BEGIN_DECLS

void rcheevos_client_download_placeholder_badge(void);
void rcheevos_client_download_achievement_badges(rc_client_t* client);
void rcheevos_client_download_badge_from_url(const char* url, const char* badge_name);
bool 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);
Expand Down
27 changes: 23 additions & 4 deletions cheevos/cheevos_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -614,9 +614,30 @@ static void rcheevos_client_download_achievement_badge(const char* badge_name, b
}
}

static void rcheevos_get_local_badge_filename(char badge_file[], size_t badge_file_size, const char* badge, bool locked)
{
size_t _len = strlcpy(badge_file, badge, badge_file_size);
if (locked)
_len += strlcpy(badge_file + _len, "_lock", badge_file_size - _len);
strlcpy(badge_file + _len, FILE_PATH_PNG_EXTENSION, badge_file_size - _len);
}

bool rcheevos_is_badge_available(const char* badge, bool locked)
{
char badge_file[24];
char fullpath[PATH_MAX_LENGTH];

rcheevos_get_local_badge_filename(badge_file, sizeof(badge_file), badge, locked);

fill_pathname_application_special(fullpath, sizeof(fullpath),
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
fill_pathname_join(fullpath, fullpath, badge_file, sizeof(fullpath));

return path_is_valid(fullpath);
}

uintptr_t rcheevos_get_badge_texture(const char* badge, bool locked, bool download_if_missing)
{
size_t _len;
char badge_file[24];
char fullpath[PATH_MAX_LENGTH];
uintptr_t tex = 0;
Expand All @@ -634,9 +655,7 @@ uintptr_t rcheevos_get_badge_texture(const char* badge, bool locked, bool downlo
return 0;
#endif

_len = strlcpy(badge_file, badge, sizeof(badge_file));
_len += strlcpy(badge_file + _len, locked ? "_lock" : "", sizeof(badge_file) - _len);
strlcpy(badge_file + _len, FILE_PATH_PNG_EXTENSION, sizeof(badge_file) - _len);
rcheevos_get_local_badge_filename(badge_file, sizeof(badge_file), badge, locked);

fill_pathname_application_special(fullpath, sizeof(fullpath),
APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
Expand Down
Loading