diff --git a/Makefile.common b/Makefile.common
index 0bcd0a2e89..a5b3cfc759 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -2107,7 +2107,7 @@ ifeq ($(HAVE_NETWORKING), 1)
INCLUDE_DIRS += -Ideps/rcheevos/include
OBJ += cheevos/cheevos.o \
- cheevos/badges.o \
+ cheevos/cheevos_menu.o \
cheevos/cheevos_memory.o \
cheevos/cheevos_parser.o \
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
diff --git a/cheevos/badges.c b/cheevos/badges.c
deleted file mode 100644
index 9aa951abc5..0000000000
--- a/cheevos/badges.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2015-2016 - 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 .
- */
-
-#include
-
-#include "../file_path_special.h"
-#include "../configuration.h"
-#include "../gfx/gfx_display.h"
-
-#include "badges.h"
-
-#ifdef HAVE_MENU
-
-#define CHEEVOS_MENU_BADGE_LIMIT 256
-/* TODO/FIXME - public global variables */
-static uintptr_t cheevos_badge_menu_texture_list[CHEEVOS_MENU_BADGE_LIMIT] = { 0 };
-
-void cheevos_reset_menu_badges(void)
-{
- int index;
- for (index = 0; index < CHEEVOS_MENU_BADGE_LIMIT; ++index)
- {
- if (cheevos_badge_menu_texture_list[index])
- video_driver_texture_unload(&cheevos_badge_menu_texture_list[index]);
- }
-
- memset(&cheevos_badge_menu_texture_list, 0,
- sizeof(cheevos_badge_menu_texture_list));
-}
-
-void cheevos_set_menu_badge(int index, const char *badge, bool locked)
-{
- settings_t *settings = config_get_ptr();
-
- if (index >= CHEEVOS_MENU_BADGE_LIMIT)
- return;
-
- if (!settings || !settings->bools.cheevos_badges_enable)
- cheevos_badge_menu_texture_list[index] = 0;
- else
- cheevos_badge_menu_texture_list[index] =
- cheevos_get_badge_texture(badge, locked);
-}
-
-uintptr_t cheevos_get_menu_badge_texture(int index)
-{
- if (index < CHEEVOS_MENU_BADGE_LIMIT)
- return cheevos_badge_menu_texture_list[index];
-
- return 0;
-}
-
-#endif
-
-uintptr_t cheevos_get_badge_texture(const char *badge, bool locked)
-{
- char badge_file[24];
- char fullpath[PATH_MAX_LENGTH];
- uintptr_t tex = 0;
-
- if (!badge)
- return 0;
-
- fullpath[0] = badge_file[0] = '\0';
-
- strlcpy(badge_file, badge, sizeof(badge_file));
- if (locked)
- strlcat(badge_file, "_lock", sizeof(badge_file));
- strlcat(badge_file, FILE_PATH_PNG_EXTENSION, sizeof(badge_file));
-
- 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))
- tex = 0;
-
- return tex;
-}
diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c
index c4180fe0ba..ac9de0344e 100644
--- a/cheevos/cheevos.c
+++ b/cheevos/cheevos.c
@@ -33,11 +33,6 @@
#include "../config.h"
#endif
-#ifdef HAVE_MENU
-#include "../menu/menu_driver.h"
-#include "../menu/menu_entries.h"
-#endif
-
#ifdef HAVE_GFX_WIDGETS
#include "../gfx/gfx_widgets.h"
#endif
@@ -58,11 +53,10 @@
#include "streams/chd_stream.h"
#endif
-#include "badges.h"
#include "cheevos.h"
+#include "cheevos_locals.h"
#include "cheevos_memory.h"
#include "cheevos_parser.h"
-#include "util.h"
#include "../file_path_special.h"
#include "../paths.h"
@@ -80,7 +74,6 @@
#include "../network/net_http_special.h"
#include "../tasks/tasks_internal.h"
-#include "../deps/rcheevos/include/rc_runtime.h"
#include "../deps/rcheevos/include/rc_runtime_types.h"
#include "../deps/rcheevos/include/rc_url.h"
#include "../deps/rcheevos/include/rc_hash.h"
@@ -132,30 +125,6 @@ typedef struct rcheevos_async_io_request
char hardcore;
} rcheevos_async_io_request;
-typedef struct
-{
- rc_runtime_t runtime;
- rcheevos_rapatchdata_t patchdata; /* ptr alignment */
- rcheevos_memory_regions_t memory; /* ptr alignment */
-
- retro_task_t* task;
-#ifdef HAVE_THREADS
- slock_t* task_lock;
- enum event_command queued_command;
-#endif
-
- char token[32];
- char hash[33];
- char user_agent_prefix[128];
-
- bool hardcore_active;
- bool loaded;
- bool core_supports;
- bool leaderboards_enabled;
- bool leaderboard_notifications;
- bool leaderboard_trackers;
-} rcheevos_locals_t;
-
static rcheevos_locals_t rcheevos_locals =
{
{0}, /* runtime */
@@ -169,6 +138,11 @@ static rcheevos_locals_t rcheevos_locals =
{0}, /* token */
"N/A",/* hash */
"", /* user_agent_prefix */
+#ifdef HAVE_MENU
+ NULL, /* menuitems */
+ 0, /* menuitem_capacity */
+ 0, /* menuitem_count */
+#endif
false,/* hardcore_active */
false,/* loaded */
true, /* core_supports */
@@ -177,6 +151,11 @@ static rcheevos_locals_t rcheevos_locals =
false /* leaderboard_trackers */
};
+rcheevos_locals_t* get_rcheevos_locals()
+{
+ return &rcheevos_locals;
+}
+
#ifdef HAVE_THREADS
#define CHEEVOS_LOCK(l) do { slock_lock(l); } while (0)
#define CHEEVOS_UNLOCK(l) do { slock_unlock(l); } while (0)
@@ -986,6 +965,8 @@ static void rcheevos_award_achievement(rcheevos_locals_t *locals,
if (locals->hardcore_active)
cheevo->active &= ~RCHEEVOS_ACTIVE_HARDCORE;
+ cheevo->unlock_time = cpu_features_get_time_usec();
+
/* Show the OSD message. */
{
#if defined(HAVE_GFX_WIDGETS)
@@ -1235,231 +1216,6 @@ void rcheevos_reset_game(bool widgets_ready)
rcheevos_locals.patchdata.console_id);
}
-#ifdef HAVE_MENU
-void rcheevos_get_achievement_state(unsigned index,
- char *buffer, size_t len)
-{
- enum msg_hash_enums enum_idx;
- rcheevos_racheevo_t *cheevo = NULL;
- bool check_measured = false;
-
- if (index < rcheevos_locals.patchdata.core_count)
- {
- enum_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
- if (rcheevos_locals.patchdata.core)
- cheevo = &rcheevos_locals.patchdata.core[index];
- }
- else
- {
- enum_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNOFFICIAL_ENTRY;
- if (rcheevos_locals.patchdata.unofficial)
- cheevo = &rcheevos_locals.patchdata.unofficial[index -
- rcheevos_locals.patchdata.core_count];
- }
-
- if (!cheevo || !cheevo->memaddr)
- enum_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
- else if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
- enum_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY_HARDCORE;
- else if (!(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
- {
- enum_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY;
- /* if in hardcore mode, track progress towards hardcore unlock */
- check_measured = rcheevos_locals.hardcore_active;
- }
- /* Use either "Locked" for core or "Unofficial"
- * for unofficial as set above and track progress */
- else
- check_measured = true;
-
- strlcpy(buffer, msg_hash_to_str(enum_idx), len);
-
- if (check_measured)
- {
- const rc_trigger_t* trigger = rc_runtime_get_achievement(
- &rcheevos_locals.runtime, cheevo->id);
- const unsigned int target = trigger->measured_target;
-
- if (target > 0 && trigger->measured_value > 0)
- {
- char measured_buffer[12];
- const unsigned int value = MIN(trigger->measured_value, target);
- const int percent = (int)(((unsigned long)value) * 100 / target);
-
- snprintf(measured_buffer, sizeof(measured_buffer),
- " - %d%%", percent);
- strlcat(buffer, measured_buffer, len);
- }
- }
-}
-
-static void rcheevos_append_menu_achievement(
- menu_displaylist_info_t* info, size_t idx,
- rcheevos_racheevo_t* cheevo)
-{
- bool badge_grayscale;
-
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description, MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
- (unsigned)(MENU_SETTINGS_CHEEVOS_START + idx), 0, 0);
-
- /* TODO/FIXME - can we refactor this?
- * Make badge_grayscale true by default, then
- * have one conditional (second one here) that sets it
- * to false */
- if (!cheevo->memaddr)
- badge_grayscale = true; /* unsupported */
- else if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE) ||
- !(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
- badge_grayscale = false; /* unlocked */
- else
- badge_grayscale = true; /* locked */
-
- cheevos_set_menu_badge((int)idx, cheevo->badge, badge_grayscale);
-}
-#endif
-
-void rcheevos_populate_hardcore_pause_menu(void* data)
-{
-#ifdef HAVE_MENU
- menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
- settings_t* settings = config_get_ptr();
- bool cheevos_hardcore_mode_enable = settings->bools.cheevos_hardcore_mode_enable;
-
- if (cheevos_hardcore_mode_enable && rcheevos_locals.loaded)
- {
- if (rcheevos_locals.hardcore_active)
- {
- menu_entries_append_enum(info->list,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
- msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE_CANCEL),
- MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
- MENU_SETTING_ACTION_CLOSE, 0, 0);
- menu_entries_append_enum(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);
- }
- else
- {
- menu_entries_append_enum(info->list,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
- msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME_CANCEL),
- MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
- MENU_SETTING_ACTION_CLOSE, 0, 0);
- menu_entries_append_enum(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);
- }
- }
-#endif
-}
-
-void rcheevos_populate_menu(void* data)
-{
-#ifdef HAVE_MENU
- int i = 0;
- int count = 0;
- rcheevos_racheevo_t* cheevo = NULL;
- menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
- settings_t* settings = config_get_ptr();
- bool cheevos_enable = settings->bools.cheevos_enable;
- bool cheevos_hardcore_mode_enable = settings->bools.cheevos_hardcore_mode_enable;
- bool cheevos_test_unofficial = settings->bools.cheevos_test_unofficial;
-
- if ( cheevos_enable
- && cheevos_hardcore_mode_enable
- && rcheevos_locals.loaded)
- {
- if (rcheevos_locals.hardcore_active)
- menu_entries_append_enum(info->list,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
- MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
- MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
- else
- menu_entries_append_enum(info->list,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
- MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
- MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
- }
-
- cheevo = rcheevos_locals.patchdata.core;
- for (count = rcheevos_locals.patchdata.core_count; count > 0; count--)
- rcheevos_append_menu_achievement(info, i++, cheevo++);
-
- if (cheevos_test_unofficial)
- {
- cheevo = rcheevos_locals.patchdata.unofficial;
- for (count = rcheevos_locals.patchdata.unofficial_count; count > 0; count--)
- rcheevos_append_menu_achievement(info, i++, cheevo++);
- }
-
- if (i == 0)
- {
- if (!rcheevos_locals.core_supports)
- {
- menu_entries_append_enum(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);
- }
- else if (!settings->arrays.cheevos_token[0])
- {
- menu_entries_append_enum(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);
- }
- else
- {
- menu_entries_append_enum(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);
- }
- }
-#endif
-}
-
-bool rcheevos_get_description(rcheevos_ctx_desc_t* desc)
-{
- unsigned idx;
- const rcheevos_racheevo_t* cheevo;
-
- if (!desc)
- return false;
-
- *desc->s = 0;
-
- if (rcheevos_locals.loaded)
- {
- idx = desc->idx;
-
- if (idx < rcheevos_locals.patchdata.core_count)
- cheevo = rcheevos_locals.patchdata.core + idx;
- else
- {
- idx -= rcheevos_locals.patchdata.core_count;
-
- if (idx >= rcheevos_locals.patchdata.unofficial_count)
- return true;
- cheevo = rcheevos_locals.patchdata.unofficial + idx;
- }
-
- strlcpy(desc->s, cheevo->description, desc->len);
- }
-
- return true;
-}
-
bool rcheevos_hardcore_active(void)
{
return rcheevos_locals.hardcore_active;
@@ -1500,10 +1256,17 @@ bool rcheevos_unload(void)
if (rcheevos_locals.loaded)
{
- rcheevos_free_patchdata(&rcheevos_locals.patchdata);
#ifdef HAVE_MENU
- cheevos_reset_menu_badges();
+ rcheevos_menu_reset_badges();
+
+ if (rcheevos_locals.menuitems)
+ {
+ CHEEVOS_FREE(rcheevos_locals.menuitems);
+ rcheevos_locals.menuitems = NULL;
+ rcheevos_locals.menuitem_capacity = rcheevos_locals.menuitem_count = 0;
+ }
#endif
+ rcheevos_free_patchdata(&rcheevos_locals.patchdata);
rcheevos_locals.loaded = false;
rcheevos_locals.hardcore_active = false;
@@ -2451,10 +2214,43 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
}
#endif
-#ifdef HAVE_MENU
- cheevos_reset_menu_badges();
-#endif
+ /* make sure the directory exists */
+ coro->badge_fullpath[0] = '\0';
+ fill_pathname_application_special(coro->badge_fullpath,
+ sizeof(coro->badge_fullpath),
+ APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
+ if (!path_is_directory(coro->badge_fullpath))
+ path_mkdir(coro->badge_fullpath);
+
+ /* fetch the placeholder image */
+ strlcpy(coro->badge_name, "00000" FILE_PATH_PNG_EXTENSION,
+ sizeof(coro->badge_name));
+ fill_pathname_join(coro->badge_fullpath, coro->badge_fullpath,
+ coro->badge_name, sizeof(coro->badge_fullpath));
+
+ if (!path_is_valid(coro->badge_fullpath))
+ {
+#ifdef CHEEVOS_LOG_BADGES
+ CHEEVOS_LOG(RCHEEVOS_TAG "downloading badge %s\n",
+ coro->badge_fullpath);
+#endif
+ snprintf(coro->url, sizeof(coro->url),
+ FILE_PATH_RETROACHIEVEMENTS_URL "/Badge/%s", coro->badge_name);
+
+ CORO_GOSUB(RCHEEVOS_HTTP_GET);
+
+ if (coro->json)
+ {
+ if (!filestream_write_file(coro->badge_fullpath, coro->json, coro->k))
+ CHEEVOS_ERR(RCHEEVOS_TAG "Error writing badge %s\n", coro->badge_fullpath);
+
+ CHEEVOS_FREE(coro->json);
+ coro->json = NULL;
+ }
+ }
+
+ /* fetch the game images */
for (coro->i = 0; coro->i < 2; coro->i++)
{
if (coro->i == 0)
@@ -2470,24 +2266,13 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
for (; coro->cheevo < coro->cheevo_end; coro->cheevo++)
{
- if (!coro->cheevo->badge[0])
+ if (!coro->cheevo->badge || !coro->cheevo->badge[0])
continue;
for (coro->j = 0 ; coro->j < 2; coro->j++)
{
- coro->badge_fullpath[0] = '\0';
- fill_pathname_application_special(
- coro->badge_fullpath,
- sizeof(coro->badge_fullpath),
- APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
-
- if (!path_is_directory(coro->badge_fullpath))
- path_mkdir(coro->badge_fullpath);
CORO_YIELD();
- if (!coro->cheevo->badge || !coro->cheevo->badge[0])
- continue;
-
if (coro->j == 0)
snprintf(coro->badge_name,
sizeof(coro->badge_name),
@@ -2499,6 +2284,11 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
"%s_lock" FILE_PATH_PNG_EXTENSION,
coro->cheevo->badge);
+ coro->badge_fullpath[0] = '\0';
+ fill_pathname_application_special(coro->badge_fullpath,
+ sizeof(coro->badge_fullpath),
+ APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
+
fill_pathname_join(
coro->badge_fullpath,
coro->badge_fullpath,
@@ -2524,11 +2314,9 @@ static int rcheevos_iterate(rcheevos_coro_t* coro)
if (!filestream_write_file(coro->badge_fullpath,
coro->json, coro->k))
CHEEVOS_ERR(RCHEEVOS_TAG "Error writing badge %s\n", coro->badge_fullpath);
- else
- {
- CHEEVOS_FREE(coro->json);
- coro->json = NULL;
- }
+
+ CHEEVOS_FREE(coro->json);
+ coro->json = NULL;
}
}
}
diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h
index e168faea63..cfeb29ad35 100644
--- a/cheevos/cheevos.h
+++ b/cheevos/cheevos.h
@@ -21,64 +21,39 @@
#include
-#include "../verbosity.h"
-
#include
RETRO_BEGIN_DECLS
-typedef struct rcheevos_ctx_desc
-{
- unsigned idx;
- char *s;
- size_t len;
-} rcheevos_ctx_desc_t;
-
-enum
-{
- RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
- RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
- RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
-};
-
bool rcheevos_load(const void *data);
size_t rcheevos_get_serialize_size(void);
bool rcheevos_get_serialized_data(void* buffer);
bool rcheevos_set_serialized_data(void* buffer);
-void rcheevos_reset_game(bool widgets_ready);
-
-void rcheevos_populate_menu(void* data);
-void rcheevos_populate_hardcore_pause_menu(void* data);
-void rcheevos_get_achievement_state(unsigned index, char* buffer, size_t buffer_size);
-
-bool rcheevos_get_description(rcheevos_ctx_desc_t *desc);
-
-void rcheevos_pause_hardcore(void);
-
bool rcheevos_unload(void);
+void rcheevos_test(void);
+
+void rcheevos_reset_game(bool widgets_ready);
+
+void rcheevos_pause_hardcore(void);
void rcheevos_hardcore_enabled_changed(void);
void rcheevos_toggle_hardcore_paused(void);
+bool rcheevos_hardcore_active(void);
void rcheevos_validate_config_settings(void);
void rcheevos_leaderboards_enabled_changed(void);
-void rcheevos_test(void);
-
void rcheevos_set_support_cheevos(bool state);
-
bool rcheevos_get_support_cheevos(void);
const char* rcheevos_get_hash(void);
-
int rcheevos_get_richpresence(char buffer[], int buffer_size);
+uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked);
uint8_t* rcheevos_patch_address(unsigned address);
-bool rcheevos_hardcore_active(void);
-
RETRO_END_DECLS
#endif /* __RARCH_CHEEVOS_CHEEVOS_H */
diff --git a/cheevos/cheevos_locals.h b/cheevos/cheevos_locals.h
new file mode 100644
index 0000000000..9431d73913
--- /dev/null
+++ b/cheevos/cheevos_locals.h
@@ -0,0 +1,158 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - Andre Leiradella
+ * Copyright (C) 2019-2021 - 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-
+ * 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 .
+ */
+
+#ifndef __RARCH_CHEEVOS_LOCALS_H
+#define __RARCH_CHEEVOS_LOCALS_H
+
+#include "../deps/rcheevos/include/rc_runtime.h"
+
+#include "cheevos_memory.h"
+
+#include <../command.h>
+#include <../verbosity.h>
+#include
+#include
+
+#ifdef HAVE_THREADS
+#include
+#endif
+
+#include
+
+RETRO_BEGIN_DECLS
+
+/************************************************************************
+ * Logging *
+ ************************************************************************/
+
+/* Define this macro to get extra-verbose log for cheevos. */
+#define CHEEVOS_VERBOSE
+
+#define RCHEEVOS_TAG "[RCHEEVOS]: "
+#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
+
+#ifdef CHEEVOS_VERBOSE
+ #define CHEEVOS_LOG RARCH_LOG
+ #define CHEEVOS_ERR RARCH_ERR
+#else
+ void rcheevos_log(const char *fmt, ...);
+ #define CHEEVOS_LOG rcheevos_log
+ #define CHEEVOS_ERR RARCH_ERR
+#endif
+
+/************************************************************************
+ * State *
+ ************************************************************************/
+
+enum
+{
+ RCHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
+ RCHEEVOS_ACTIVE_HARDCORE = 1 << 1,
+ RCHEEVOS_ACTIVE_UNOFFICIAL = 1 << 2
+};
+
+typedef struct rcheevos_racheevo_t
+{
+ const char* title;
+ const char* description;
+ const char* badge;
+ const char* memaddr;
+ unsigned id;
+ unsigned points;
+
+ retro_time_t unlock_time;
+ uint8_t active;
+
+#ifdef HAVE_MENU
+ uint8_t menu_bucket;
+ uint8_t menu_progress;
+ uint8_t menu_badge_grayscale;
+ uintptr_t menu_badge_texture;
+#endif
+
+} rcheevos_racheevo_t;
+
+typedef struct rcheevos_ralboard_t
+{
+ const char* title;
+ const char* description;
+ const char* mem;
+ unsigned id;
+ 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;
+
+#ifdef HAVE_MENU
+
+typedef struct rcheevos_menuitem_t
+{
+ rcheevos_racheevo_t* cheevo;
+ enum msg_hash_enums state_label_idx;
+} rcheevos_menuitem_t;
+
+void rcheevos_menu_reset_badges(void);
+
+#endif
+
+typedef struct rcheevos_locals_t
+{
+ rc_runtime_t runtime; /* rcheevos runtime state */
+ rcheevos_rapatchdata_t patchdata; /* achievement/leaderboard data from the server */
+ rcheevos_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 token[32]; /* user's session token */
+ char hash[33]; /* retroachievements hash for current content */
+ char user_agent_prefix[128]; /* RetroArch/OS version information */
+
+#ifdef HAVE_MENU
+ rcheevos_menuitem_t* menuitems; /* array of items for the achievements quick menu */
+ unsigned menuitem_capacity; /* maximum number of items in the menuitems array */
+ unsigned menuitem_count; /* current number of items in the menuitems array */
+#endif
+
+ bool hardcore_active; /* hardcore functionality is active */
+ bool loaded; /* load task has completed */
+ bool core_supports; /* false if core explicitly disables achievements */
+ 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();
+
+RETRO_END_DECLS
+
+#endif /* __RARCH_CHEEVOS_LOCALS_H */
diff --git a/cheevos/cheevos_memory.c b/cheevos/cheevos_memory.c
index 5f075f2e5e..2fcb4ce9cd 100644
--- a/cheevos/cheevos_memory.c
+++ b/cheevos/cheevos_memory.c
@@ -15,7 +15,7 @@
#include "cheevos_memory.h"
-#include "util.h"
+#include "cheevos_locals.h"
#include "../retroarch.h"
#include "../verbosity.h"
diff --git a/cheevos/cheevos_menu.c b/cheevos/cheevos_menu.c
new file mode 100644
index 0000000000..afac7afdd1
--- /dev/null
+++ b/cheevos/cheevos_menu.c
@@ -0,0 +1,708 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2019-2021 - 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-
+ * 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 .
+ */
+
+#include "cheevos_locals.h"
+
+#include "../gfx/gfx_display.h"
+
+#ifdef HAVE_MENU
+
+#include "cheevos.h"
+
+#include "../deps/rcheevos/include/rc_runtime_types.h"
+
+#include "../file_path_special.h"
+#include "../menu/menu_driver.h"
+#include "../menu/menu_entries.h"
+
+#include
+
+enum rcheevos_menuitem_bucket
+{
+ RCHEEVOS_MENUITEM_BUCKET_UNKNOWN = 0,
+ RCHEEVOS_MENUITEM_BUCKET_LOCKED,
+ RCHEEVOS_MENUITEM_BUCKET_UNLOCKED,
+ RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED,
+ RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED,
+ RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE,
+ RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE
+};
+
+static void rcheevos_menu_update_bucket(rcheevos_racheevo_t* cheevo)
+{
+ if (!cheevo->memaddr)
+ {
+ /* non-active unsupported achievement */
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED;
+ }
+ else if (!(cheevo->active & RCHEEVOS_ACTIVE_HARDCORE))
+ {
+ /* non-active unlocked in hardcore achievement */
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
+ }
+ else
+ {
+ const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
+ rc_trigger_t* trigger;
+
+ if (!rcheevos_locals->hardcore_active && !(cheevo->active & RCHEEVOS_ACTIVE_SOFTCORE))
+ {
+ /* non-active unlocked in softcore achievement in softcore mode */
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_UNLOCKED;
+ return;
+ }
+
+ /* active achievement */
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_LOCKED;
+ cheevo->menu_progress = 0;
+
+ trigger = rc_runtime_get_achievement(&rcheevos_locals->runtime, cheevo->id);
+ if (trigger)
+ {
+ if (trigger->measured_value && trigger->measured_target)
+ {
+ const unsigned long clamped_value = (unsigned long)
+ MIN(trigger->measured_value, trigger->measured_target);
+ cheevo->menu_progress =
+ (uint8_t)((clamped_value * 100) / trigger->measured_target);
+ }
+
+ if (trigger->state == RC_TRIGGER_STATE_PRIMED)
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE;
+ else if (cheevo->menu_progress >= 80)
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE;
+ }
+ }
+}
+
+static void rcheevos_menu_update_buckets(bool cheevos_test_unofficial)
+{
+ 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)
+ {
+ 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)
+{
+ 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 rcheevos_racheevo_t* cheevo = menuitem->cheevo;
+ if (cheevo)
+ {
+ if (cheevo->menu_progress)
+ {
+ snprintf(buffer, len, "%s - %d%%",
+ msg_hash_to_str(menuitem->state_label_idx),
+ cheevo->menu_progress);
+ }
+ else
+ {
+ strlcpy(buffer, msg_hash_to_str(menuitem->state_label_idx), len);
+ }
+
+ return true;
+ }
+ }
+
+ if (buffer)
+ buffer[0] = '\0';
+
+ return false;
+}
+
+bool rcheevos_menu_get_sublabel(unsigned menu_offset, char *buffer, size_t len)
+{
+ const rcheevos_locals_t* rcheevos_locals = get_rcheevos_locals();
+ if (menu_offset < rcheevos_locals->menuitem_count)
+ {
+ const rcheevos_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
+ if (cheevo && buffer)
+ {
+ strlcpy(buffer, cheevo->description, len);
+ 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_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;
+
+ while (cheevo < stop)
+ {
+ if (cheevo->menu_badge_texture)
+ video_driver_texture_unload(&cheevo->menu_badge_texture);
+ ++cheevo;
+ }
+}
+
+static rcheevos_menuitem_t* rcheevos_menu_allocate(
+ rcheevos_locals_t* rcheevos_locals, rcheevos_racheevo_t* cheevo)
+{
+ 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++];
+ menuitem->cheevo = cheevo;
+ menuitem->state_label_idx = MSG_UNKNOWN;
+ return menuitem;
+}
+
+static void rcheevos_menu_append_header(rcheevos_locals_t* rcheevos_locals,
+ enum msg_hash_enums label)
+{
+ rcheevos_menuitem_t* menuitem = rcheevos_menu_allocate(rcheevos_locals, NULL);
+ if (menuitem)
+ menuitem->state_label_idx = label;
+}
+
+static void rcheevos_menu_update_badge(rcheevos_racheevo_t* cheevo)
+{
+ bool badge_grayscale = false;
+ switch (cheevo->menu_bucket)
+ {
+ case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
+ case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
+ case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
+ case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
+ badge_grayscale = true;
+ break;
+
+ default:
+ badge_grayscale = false;
+ break;
+ }
+
+ if (!cheevo->menu_badge_texture || cheevo->menu_badge_grayscale != badge_grayscale)
+ {
+ uintptr_t new_badge_texture =
+ rcheevos_get_badge_texture(cheevo->badge, badge_grayscale);
+
+ if (new_badge_texture)
+ {
+ if (cheevo->menu_badge_texture)
+ video_driver_texture_unload(&cheevo->menu_badge_texture);
+
+ cheevo->menu_badge_texture = new_badge_texture;
+ cheevo->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 (cheevo->menu_badge_grayscale < 2)
+ {
+ if (cheevo->menu_badge_texture)
+ video_driver_texture_unload(&cheevo->menu_badge_texture);
+
+ /* requested badge is not available, check for server default */
+ cheevo->menu_badge_texture =
+ rcheevos_get_badge_texture("00000", false);
+
+ if (cheevo->menu_badge_texture)
+ cheevo->menu_badge_grayscale = 2;
+ }
+ }
+}
+
+static void rcheevos_menu_append_items(rcheevos_locals_t* rcheevos_locals,
+ bool cheevos_test_unofficial, 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;
+ const unsigned first_index = rcheevos_locals->menuitem_count;
+
+ do
+ {
+ 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);
+ if (!menuitem)
+ return;
+
+ switch (cheevo->menu_bucket)
+ {
+ case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
+ menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY;
+ break;
+
+ case RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED:
+ {
+ /* insert the item such that the unlock times are descending */
+ unsigned entry_index = rcheevos_locals->menuitem_count - 1;
+ while (entry_index > first_index)
+ {
+ rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
+ if (prev_menuitem->cheevo->unlock_time >= cheevo->unlock_time)
+ break;
+
+ memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
+ menuitem = prev_menuitem;
+ --entry_index;
+ }
+
+ menuitem->cheevo = cheevo;
+ }
+ /* fallthrough to RCHEEVOS_MENUITEM_BUCKET_UNLOCKED */
+
+ case RCHEEVOS_MENUITEM_BUCKET_UNLOCKED:
+ if (!(cheevo->active & RCHEEVOS_ACTIVE_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 RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
+ {
+ /* insert the item such that the progresses are descending */
+ unsigned entry_index = rcheevos_locals->menuitem_count - 1;
+ while (entry_index > first_index)
+ {
+ rcheevos_menuitem_t* prev_menuitem = menuitem - 1;
+ if (prev_menuitem->cheevo->menu_progress >= cheevo->menu_progress)
+ break;
+
+ memcpy(menuitem, prev_menuitem, sizeof(rcheevos_menuitem_t));
+ menuitem = prev_menuitem;
+ --entry_index;
+ }
+
+ menuitem->cheevo = cheevo;
+ }
+ /* fallthrough to default */
+
+ default:
+ if (processing_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;
+ else
+ menuitem->state_label_idx = MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY;
+ break;
+ }
+
+ if (cheevo->badge && cheevo->badge[0] && settings &&
+ settings->bools.cheevos_badges_enable)
+ {
+ rcheevos_menu_update_badge(cheevo);
+ }
+ }
+
+ ++cheevo;
+ } while (true);
+}
+
+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_racheevo_t* cheevo = rcheevos_locals->menuitems[menu_offset].cheevo;
+ if (cheevo)
+ {
+ /* if we're using the placeholder badge, check to see if the real badge
+ * has become available (do this roughly once a second) */
+ if (cheevo->menu_badge_grayscale >= 2)
+ {
+ if (++cheevo->menu_badge_grayscale == 64)
+ {
+ cheevo->menu_badge_grayscale = 2;
+ rcheevos_menu_update_badge(cheevo);
+ }
+ }
+
+ return cheevo->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 && rcheevos_locals->loaded)
+ {
+ if (rcheevos_locals->hardcore_active)
+ {
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_CANCEL),
+ msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE_CANCEL),
+ MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_CANCEL,
+ MENU_SETTING_ACTION_CLOSE, 0, 0);
+ menu_entries_append_enum(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);
+ }
+ else
+ {
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME_CANCEL),
+ msg_hash_to_str(MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME_CANCEL),
+ MENU_ENUM_LABEL_ACHIEVEMENT_RESUME_CANCEL,
+ MENU_SETTING_ACTION_CLOSE, 0, 0);
+ menu_entries_append_enum(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);
+ }
+ }
+}
+
+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 settings_t* settings = config_get_ptr();
+ const bool cheevos_test_unofficial = settings->bools.cheevos_test_unofficial;
+ unsigned num_locked = 0;
+ unsigned num_unlocked = 0;
+ unsigned num_recently_unlocked = 0;
+ unsigned num_unsupported = 0;
+ unsigned num_active_challenges = 0;
+ unsigned num_almost_there = 0;
+
+ if (rcheevos_locals->loaded)
+ {
+ 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;
+
+ /* first menu item is the Pause/Resume Hardcore option (unless hardcore is disabled) */
+ if (settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable)
+ {
+ if (rcheevos_locals->hardcore_active)
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE),
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
+ MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
+ MENU_SETTING_ACTION_PAUSE_ACHIEVEMENTS, 0, 0);
+ else
+ menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_RESUME),
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_PAUSE_MENU),
+ MENU_ENUM_LABEL_ACHIEVEMENT_PAUSE_MENU,
+ MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
+ }
+
+ /* update the bucket for each achievement */
+ rcheevos_menu_update_buckets(cheevos_test_unofficial);
+
+ /* count items in each bucket */
+ cheevo = rcheevos_locals->patchdata.core;
+ stop = cheevo + rcheevos_locals->patchdata.core_count;
+
+ do
+ {
+ 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:
+ if (cheevo->unlock_time && cheevo->unlock_time >= recent_unlock_time)
+ {
+ cheevo->menu_bucket = RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED;
+ ++num_recently_unlocked;
+ }
+ else
+ {
+ ++num_unlocked;
+ }
+ break;
+
+ case RCHEEVOS_MENUITEM_BUCKET_LOCKED:
+ ++num_locked;
+ break;
+
+ case RCHEEVOS_MENUITEM_BUCKET_UNSUPPORTED:
+ ++num_unsupported;
+ break;
+
+ case RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE:
+ ++num_active_challenges;
+ break;
+
+ case RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE:
+ ++num_almost_there;
+ break;
+ }
+
+ ++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->menuitems = (rcheevos_menuitem_t*)
+ malloc(rcheevos_locals->menuitem_capacity * sizeof(rcheevos_menuitem_t));
+ if (!rcheevos_locals->menuitems)
+ rcheevos_locals->menuitem_capacity = 0;
+ }
+ }
+
+ /* reset menu */
+ rcheevos_locals->menuitem_count = 0;
+
+ /* active challenges */
+ if (num_active_challenges)
+ {
+ rcheevos_menu_append_header(rcheevos_locals,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY);
+
+ rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
+ RCHEEVOS_MENUITEM_BUCKET_ACTIVE_CHALLENGE);
+ }
+
+ /* recently unlocked */
+ if (num_recently_unlocked)
+ {
+ rcheevos_menu_append_header(rcheevos_locals,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY);
+
+ rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
+ RCHEEVOS_MENUITEM_BUCKET_RECENTLY_UNLOCKED);
+ }
+
+ /* almost there */
+ if (num_almost_there)
+ {
+ rcheevos_menu_append_header(rcheevos_locals,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY);
+
+ rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
+ RCHEEVOS_MENUITEM_BUCKET_ALMOST_THERE);
+ }
+
+ /* locked */
+ if (num_locked)
+ {
+ if (rcheevos_locals->menuitem_count > 0)
+ {
+ rcheevos_menu_append_header(rcheevos_locals,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY);
+ }
+
+ rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
+ RCHEEVOS_MENUITEM_BUCKET_LOCKED);
+ }
+
+ /* unlocked */
+ if (num_unlocked)
+ {
+ if (rcheevos_locals->menuitem_count > 0)
+ {
+ rcheevos_menu_append_header(rcheevos_locals,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY);
+ }
+
+ rcheevos_menu_append_items(rcheevos_locals, cheevos_test_unofficial,
+ RCHEEVOS_MENUITEM_BUCKET_UNLOCKED);
+ }
+
+ if (rcheevos_locals->menuitem_count > 0)
+ {
+ /* convert to menu entries */
+ rcheevos_menuitem_t* menuitem = rcheevos_locals->menuitems;
+ rcheevos_menuitem_t* stop = menuitem + rcheevos_locals->menuitem_count;
+ char buffer[128];
+ unsigned idx = 0;
+
+ do
+ {
+ if (menuitem->cheevo)
+ {
+ menu_entries_append_enum(info->list, menuitem->cheevo->title,
+ menuitem->cheevo->description,
+ MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
+ MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
+ }
+ else
+ {
+ snprintf(buffer, sizeof(buffer), "----- %s -----",
+ msg_hash_to_str(menuitem->state_label_idx));
+
+ menu_entries_append_enum(info->list, buffer, "",
+ MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
+ MENU_SETTINGS_CHEEVOS_START + idx, 0, 0);
+ }
+
+ ++idx;
+ ++menuitem;
+ } while (menuitem != stop);
+ }
+ else
+ {
+ /* no achievements found */
+ if (!rcheevos_locals->core_supports)
+ {
+ menu_entries_append_enum(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);
+ }
+ else if (!rcheevos_locals->token[0])
+ {
+ menu_entries_append_enum(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);
+ }
+ else
+ {
+ menu_entries_append_enum(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);
+ }
+ }
+}
+
+#endif /* HAVE_MENU */
+
+uintptr_t rcheevos_get_badge_texture(const char *badge, bool locked)
+{
+ char badge_file[24];
+ char fullpath[PATH_MAX_LENGTH];
+ uintptr_t tex = 0;
+
+ if (!badge)
+ return 0;
+
+ 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))
+ {
+ tex = 0;
+ }
+
+ return tex;
+}
+
diff --git a/cheevos/badges.h b/cheevos/cheevos_menu.h
similarity index 61%
rename from cheevos/badges.h
rename to cheevos/cheevos_menu.h
index cdbb4375a4..eea43fb2d5 100644
--- a/cheevos/badges.h
+++ b/cheevos/cheevos_menu.h
@@ -1,5 +1,6 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2015-2018 - Andre Leiradella
+ * Copyright (C) 2019-2021 - 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-
@@ -13,23 +14,28 @@
* If not, see .
*/
-#ifndef __RARCH_CHEEVOS_BADGE_H
-#define __RARCH_CHEEVOS_BADGE_H
+#ifndef __RARCH_CHEEVOS_MENU_H
+#define __RARCH_CHEEVOS_MENU_H
+
+#ifdef HAVE_MENU
#include
-#include
+#include
+
#include
+#include
+
RETRO_BEGIN_DECLS
-#ifdef HAVE_MENU
-void cheevos_reset_menu_badges(void);
-void cheevos_set_menu_badge(int index, const char *badge, bool locked);
-uintptr_t cheevos_get_menu_badge_texture(int index);
-#endif
-
-uintptr_t cheevos_get_badge_texture(const char* badge, bool locked);
+void rcheevos_menu_populate(void* data);
+void rcheevos_menu_populate_hardcore_pause_submenu(void* data);
+bool rcheevos_menu_get_state(unsigned menu_offset, char* buffer, size_t buffer_size);
+bool rcheevos_menu_get_sublabel(unsigned menu_offset, char* buffer, size_t buffer_size);
+uintptr_t rcheevos_menu_get_badge_texture(unsigned menu_offset);
RETRO_END_DECLS
-#endif
+#endif /* HAVE_MENU */
+
+#endif /* __RARCH_CHEEVOS_MENU_H */
diff --git a/cheevos/cheevos_parser.c b/cheevos/cheevos_parser.c
index 163a4634c1..cc8374485c 100644
--- a/cheevos/cheevos_parser.c
+++ b/cheevos/cheevos_parser.c
@@ -1,6 +1,6 @@
#include "cheevos_parser.h"
-#include "util.h"
+#include "cheevos_locals.h"
#include
#include
diff --git a/cheevos/cheevos_parser.h b/cheevos/cheevos_parser.h
index 8f27aeb811..6bec0063f2 100644
--- a/cheevos/cheevos_parser.h
+++ b/cheevos/cheevos_parser.h
@@ -16,49 +16,10 @@
#ifndef __RARCH_CHEEVOS_PARSER_H
#define __RARCH_CHEEVOS_PARSER_H
-#include
-#include
-#include
-
-#include
+#include "cheevos_locals.h"
RETRO_BEGIN_DECLS
-typedef struct
-{
- const char* title;
- const char* description;
- const char* badge;
- const char* memaddr;
- unsigned points;
- unsigned id;
- unsigned active;
-} rcheevos_racheevo_t;
-
-typedef struct
-{
- const char* title;
- const char* description;
- const char* mem;
- unsigned id;
- unsigned format;
-} rcheevos_ralboard_t;
-
-typedef struct
-{
- 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;
-
typedef void (*rcheevos_unlock_cb_t)(unsigned id, void* userdata);
int rcheevos_get_json_error(const char* json, char* token, size_t length);
diff --git a/cheevos/util.h b/cheevos/util.h
deleted file mode 100644
index f94be8d506..0000000000
--- a/cheevos/util.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2015-2016 - 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 .
- */
-
-#ifndef __RARCH_CHEEVOS_UTIL_H
-#define __RARCH_CHEEVOS_UTIL_H
-
-#include
-
-RETRO_BEGIN_DECLS
-
-/*****************************************************************************
-Setup - mainly for debugging
-*****************************************************************************/
-
-/* Define this macro to get extra-verbose log for cheevos. */
-#define CHEEVOS_VERBOSE
-
-/*****************************************************************************
-End of setup
-*****************************************************************************/
-
-#define RCHEEVOS_TAG "[RCHEEVOS]: "
-#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q) free(q); } while (0)
-
-#ifdef CHEEVOS_VERBOSE
-
-#define CHEEVOS_LOG RARCH_LOG
-#define CHEEVOS_ERR RARCH_ERR
-
-#else
-
-#define CHEEVOS_LOG rcheevos_log
-#define CHEEVOS_ERR RARCH_ERR
-
-void rcheevos_log(const char *fmt, ...);
-
-#endif
-
-RETRO_END_DECLS
-
-#endif /* __RARCH_CHEEVOS_UTIL_H */
diff --git a/gfx/widgets/gfx_widget_achievement_popup.c b/gfx/widgets/gfx_widget_achievement_popup.c
index decf158216..5553e24091 100644
--- a/gfx/widgets/gfx_widget_achievement_popup.c
+++ b/gfx/widgets/gfx_widget_achievement_popup.c
@@ -18,7 +18,7 @@
#include "../gfx_display.h"
#include "../gfx_widgets.h"
-#include "../cheevos/badges.h"
+#include "../cheevos/cheevos.h"
#ifdef HAVE_THREADS
#define SLOCK_LOCK(x) slock_lock(x)
@@ -391,7 +391,7 @@ void gfx_widgets_push_achievement(const char *title, 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 = cheevos_get_badge_texture(badge, 0);
+ uintptr_t badge_id = rcheevos_get_badge_texture(badge, 0);
if (state->queue_read_index < 0)
{
diff --git a/griffin/griffin.c b/griffin/griffin.c
index ec0204448f..d9bc61da3a 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -191,7 +191,7 @@ ACHIEVEMENTS
#include "../network/net_http_special.c"
#include "../cheevos/cheevos.c"
-#include "../cheevos/badges.c"
+#include "../cheevos/cheevos_menu.c"
#include "../cheevos/cheevos_memory.c"
#include "../cheevos/cheevos_parser.c"
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index d7d5deec7f..6fa127a0ac 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -7979,6 +7979,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNSUPPORTED_ENTRY,
"Unsupported"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY,
+ "Recently Unlocked"
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY,
+ "Almost There"
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY,
+ "Active Challenges"
+ )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CHEEVOS_TRACKERS_ONLY,
"Trackers Only"
diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c
index f5befe000c..8952e1d830 100644
--- a/menu/cbs/menu_cbs_get_value.c
+++ b/menu/cbs/menu_cbs_get_value.c
@@ -53,7 +53,7 @@
#endif
#ifdef HAVE_CHEEVOS
-#include "../../cheevos/cheevos.h"
+#include "../../cheevos/cheevos_menu.h"
#endif
#ifndef BIND_ACTION_GET_VALUE
@@ -129,7 +129,7 @@ static void menu_action_setting_disp_set_label_cheevos_entry(
*w = 19;
strlcpy(s2, path, len2);
- rcheevos_get_achievement_state(type - MENU_SETTINGS_CHEEVOS_START, s, len);
+ rcheevos_menu_get_state(type - MENU_SETTINGS_CHEEVOS_START, s, len);
}
#endif
diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c
index beaf60aef9..f0103d59fb 100644
--- a/menu/cbs/menu_cbs_sublabel.c
+++ b/menu/cbs/menu_cbs_sublabel.c
@@ -26,7 +26,7 @@
#include "../../core_option_manager.h"
#ifdef HAVE_CHEEVOS
-#include "../../cheevos/cheevos.h"
+#include "../../cheevos/cheevos_menu.h"
#endif
#include "../../core_info.h"
#include "../../verbosity.h"
@@ -1052,20 +1052,8 @@ static int action_bind_sublabel_cheevos_entry(
const char *label, const char *path,
char *s, size_t len)
{
- rcheevos_ctx_desc_t desc_info;
- unsigned new_id;
- char fetched_sublabel[MENU_SUBLABEL_MAX_LENGTH];
-
- fetched_sublabel[0] = '\0';
-
- new_id = type - MENU_SETTINGS_CHEEVOS_START;
- desc_info.idx = new_id;
- desc_info.s = fetched_sublabel;
- desc_info.len = len;
-
- rcheevos_get_description((rcheevos_ctx_desc_t*) &desc_info);
-
- strlcpy(s, desc_info.s, len);
+ unsigned offset = type - MENU_SETTINGS_CHEEVOS_START;
+ rcheevos_menu_get_sublabel(offset, s, len);
return 0;
}
#endif
diff --git a/menu/drivers/ozone/ozone_texture.c b/menu/drivers/ozone/ozone_texture.c
index 19db500552..425d04351d 100644
--- a/menu/drivers/ozone/ozone_texture.c
+++ b/menu/drivers/ozone/ozone_texture.c
@@ -24,7 +24,7 @@
#include
#ifdef HAVE_CHEEVOS
-#include "../../../cheevos/badges.h"
+#include "../../../cheevos/cheevos_menu.h"
#endif
#include "../../../file_path_special.h"
@@ -464,11 +464,17 @@ uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
(type < MENU_SETTINGS_NETPLAY_ROOMS_START)
)
{
+ char buffer[64];
int index = type - MENU_SETTINGS_CHEEVOS_START;
- uintptr_t badge_texture = cheevos_get_menu_badge_texture(index);
+ uintptr_t badge_texture = rcheevos_menu_get_badge_texture(index);
if (badge_texture)
return badge_texture;
- /* Should be replaced with placeholder badge icon. */
+
+ /* no state means its a header - show the info icon */
+ if (!rcheevos_menu_get_state(index, buffer, sizeof(buffer)))
+ return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_INFO];
+
+ /* placeholder badge image was not found, show generic menu icon */
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_ACHIEVEMENTS];
}
#endif
diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c
index c8725bbc39..94d14143af 100644
--- a/menu/drivers/xmb.c
+++ b/menu/drivers/xmb.c
@@ -59,7 +59,7 @@
#include "../../tasks/tasks_internal.h"
#ifdef HAVE_CHEEVOS
-#include "../../cheevos/badges.h"
+#include "../../cheevos/cheevos_menu.h"
#endif
#include "../../content.h"
@@ -2905,11 +2905,17 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
(type < MENU_SETTINGS_NETPLAY_ROOMS_START)
)
{
+ char buffer[64];
int index = type - MENU_SETTINGS_CHEEVOS_START;
- uintptr_t badge_texture = cheevos_get_menu_badge_texture(index);
+ uintptr_t badge_texture = rcheevos_menu_get_badge_texture(index);
if (badge_texture)
return badge_texture;
- /* Should be replaced with placeholder badge icon. */
+
+ /* no state means its a header - show the info icon */
+ if (!rcheevos_menu_get_state(index, buffer, sizeof(buffer)))
+ return xmb->textures.list[XMB_TEXTURE_INFO];
+
+ /* placeholder badge image was not found, show generic menu icon */
return xmb->textures.list[XMB_TEXTURE_ACHIEVEMENTS];
}
#endif
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index 57e022f335..0c5a62ce8c 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -41,6 +41,7 @@
#ifdef HAVE_CHEEVOS
#include "../cheevos/cheevos.h"
+#include "../cheevos/cheevos_menu.h"
#endif
#ifdef HAVE_NETWORKING
@@ -10982,7 +10983,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
case DISPLAYLIST_ACHIEVEMENT_PAUSE_MENU:
#ifdef HAVE_CHEEVOS
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- rcheevos_populate_hardcore_pause_menu(info);
+ rcheevos_menu_populate_hardcore_pause_submenu(info);
#endif
info->need_push = true;
info->need_refresh = true;
@@ -10990,7 +10991,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
case DISPLAYLIST_ACHIEVEMENT_LIST:
#ifdef HAVE_CHEEVOS
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
- rcheevos_populate_menu(info);
+ rcheevos_menu_populate(info);
#endif
info->need_push = true;
info->need_refresh = true;
diff --git a/msg_hash.h b/msg_hash.h
index e57bfcf90e..8a9ec5d700 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -704,6 +704,9 @@ enum msg_hash_enums
MENU_LABEL(CHEEVOS_LOCKED_ENTRY),
MENU_LABEL(CHEEVOS_UNSUPPORTED_ENTRY),
MENU_LABEL(CHEEVOS_UNOFFICIAL_ENTRY),
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_RECENTLY_UNLOCKED_ENTRY,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ALMOST_THERE_ENTRY,
+ MENU_ENUM_LABEL_VALUE_CHEEVOS_ACTIVE_CHALLENGES_ENTRY,
MENU_ENUM_LABEL_VALUE_CHEEVOS_TRACKERS_ONLY,
MENU_ENUM_LABEL_VALUE_CHEEVOS_NOTIFICATIONS_ONLY,
diff --git a/retroarch.c b/retroarch.c
index 354ebc1a3e..5ab1ba30a6 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -174,6 +174,7 @@
#ifdef HAVE_CHEEVOS
#include "cheevos/cheevos.h"
+#include "cheevos/cheevos_menu.h"
#endif
#ifdef HAVE_TRANSLATE
@@ -789,13 +790,8 @@ static int menu_dialog_iterate(
#ifdef HAVE_CHEEVOS
case MENU_DIALOG_HELP_CHEEVOS_DESCRIPTION:
- {
- rcheevos_ctx_desc_t desc_info;
- desc_info.idx = p_dialog->current_id;
- desc_info.s = s;
- desc_info.len = len;
- rcheevos_get_description((rcheevos_ctx_desc_t*) &desc_info);
- }
+ if (!rcheevos_menu_get_sublabel(p_dialog->current_id, s, len))
+ return 1;
break;
#endif