diff --git a/cheevos.c b/cheevos.c index 9eba3219d2..0335de628a 100644 --- a/cheevos.c +++ b/cheevos.c @@ -24,6 +24,7 @@ #include "cheevos.h" #include "dynamic.h" #include "libretro.h" +#include "system.h" #include "network/net_http_special.h" #include "configuration.h" #include "performance_counters.h" @@ -44,6 +45,9 @@ /* Define this macro to log URLs (will log the user token). */ #undef CHEEVOS_LOG_URLS +/* Define this macro to dump all cheevos' addresses. */ +#undef CHEEVOS_DUMP_ADDRS + #define JSON_KEY_GAMEID 0xb4960eecU #define JSON_KEY_ACHIEVEMENTS 0x69749ae1U #define JSON_KEY_ID 0x005973f2U @@ -62,6 +66,7 @@ enum { CHEEVOS_CONSOLE_GAMEBOY = 4, + CHEEVOS_CONSOLE_GAMEBOY_ADVANCED = 5, CHEEVOS_CONSOLE_GAMEBOY_COLOR = 6, }; @@ -142,7 +147,7 @@ typedef struct { unsigned size; unsigned type; - unsigned bank_id; + int bank_id; unsigned value; unsigned previous; } cheevos_var_t; @@ -218,7 +223,7 @@ typedef struct typedef struct { int in_cheevos; - int console_id; + int is_console_id; unsigned core_count; unsigned unofficial_count; @@ -236,19 +241,23 @@ typedef struct typedef struct { - int loaded; - int console_id; + int loaded; + int console_id; + bool core_supports; cheevoset_t core; cheevoset_t unofficial; char token[32]; + + retro_ctx_memory_info_t meminfo[4]; } cheevos_locals_t; static cheevos_locals_t cheevos_locals = { 0, 0, + true, {NULL, 0}, {NULL, 0}, {0}, @@ -620,6 +629,69 @@ static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr) var->value = strtol(str, &end, base); *memaddr = end; + + if ( var->type == CHEEVOS_VAR_TYPE_ADDRESS + || var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) + { + rarch_system_info_t *system; + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); + + var->bank_id = -1; + + if (system->mmaps.num_descriptors != 0) + { + const struct retro_memory_descriptor *desc; + const struct retro_memory_descriptor *end; + +#if 0 + if (cheevos_locals.console_id == CHEEVOS_CONSOLE_GAMEBOY_ADVANCED) + { + /* Patch the address to correctly map it to the mmaps */ + if (var->value < 0x8000) + { + /* Internal RAM */ + var->value += 0x3000000; + } + else + { + /* Work RAM */ + var->value += 0x2000000 - 0x8000; + } + } +#endif + + desc = system->mmaps.descriptors; + end = desc + system->mmaps.num_descriptors; + + for (; desc < end; desc++) + { + if (var->value >= desc->start && var->value < (desc->start + desc->len)) + { + var->bank_id = desc - system->mmaps.descriptors; + var->value = var->value - desc->start + desc->offset; + break; + } + } + } + else + { + unsigned i; + + for (i = 0; i < sizeof(cheevos_locals.meminfo) / sizeof(cheevos_locals.meminfo[0]); i++) + { + if (var->value < cheevos_locals.meminfo[i].size) + { + var->bank_id = i; + break; + } + + var->value -= cheevos_locals.meminfo[i].size; + } + } +#ifdef CHEEVOS_DUMP_ADDRS + RARCH_LOG("CHEEVOS var %03d:%08X\n", var->bank_id + 1, var->value); +#endif + } } static void cheevos_parse_cond(cheevos_cond_t *cond, const char **memaddr) @@ -818,7 +890,7 @@ static int cheevos_read__json_key( void *userdata, if (hash == JSON_KEY_ACHIEVEMENTS) ud->in_cheevos = 1; else if (hash == JSON_KEY_CONSOLE_ID) - ud->console_id = 0; + ud->is_console_id = 1; else if (ud->in_cheevos) { switch ( hash ) @@ -883,8 +955,11 @@ static int cheevos_read__json_number(void *userdata, ud->field->string = number; ud->field->length = length; } - else if (ud->console_id == 0) - ud->console_id = strtol(number, NULL, 10); + else if (ud->is_console_id) + { + cheevos_locals.console_id = strtol(number, NULL, 10); + ud->is_console_id = 0; + } return 0; } @@ -963,7 +1038,7 @@ static int cheevos_parse(const char *json) /* Load the achievements. */ ud.in_cheevos = 0; - ud.console_id = -1; + ud.is_console_id = 0; ud.field = NULL; ud.core_count = 0; ud.unofficial_count = 0; @@ -974,7 +1049,6 @@ static int cheevos_parse(const char *json) return -1; } - cheevos_locals.console_id = ud.console_id; return 0; } @@ -982,63 +1056,23 @@ static int cheevos_parse(const char *json) Test all the achievements (call once per frame). *****************************************************************************/ -static const uint8_t *cheevos_get_memory(unsigned offset) +static const uint8_t *cheevos_get_memory(const cheevos_var_t *var) { - retro_ctx_memory_info_t mem_info; - uint8_t *memory = NULL; + if (var->bank_id >= 0) + { + rarch_system_info_t *system; + runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); + + if (system->mmaps.num_descriptors != 0) + { + return (uint8_t *)system->mmaps.descriptors[var->bank_id].ptr + var->value; + } + else + { + return (uint8_t *)cheevos_locals.meminfo[var->bank_id].data + var->value; + } + } - mem_info.id = RETRO_MEMORY_SYSTEM_RAM; - - core_get_memory(&mem_info); - - if (offset < mem_info.size) - { - memory = (uint8_t*)mem_info.data; - return memory + offset; - } - - offset -= mem_info.size; - - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_SAVE_RAM; - - core_get_memory(&mem_info); - - if (offset < mem_info.size) - { - memory = (uint8_t*)mem_info.data; - return memory + offset; - } - - offset -= mem_info.size; - - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_VIDEO_RAM; - - core_get_memory(&mem_info); - - if (offset < mem_info.size) - { - memory = (uint8_t*)mem_info.data; - return memory + offset; - } - - offset -= mem_info.size; - - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_RTC; - - core_get_memory(&mem_info); - - if (offset < mem_info.size) - { - memory = (uint8_t*)mem_info.data; - return memory + offset; - } - return NULL; } @@ -1055,7 +1089,7 @@ static unsigned cheevos_get_var_value(cheevos_var_t *var) || var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) { /* TODO Check with Scott if the bank id is needed */ - memory = cheevos_get_memory(var->value); + memory = cheevos_get_memory(var); if (memory) { @@ -1396,7 +1430,7 @@ static void cheevos_unlocker(void *payload) request[sizeof(request) - 1] = 0; #ifdef CHEEVOS_LOG_URLS - RARCH_LOG("CHEEVOS url to award a cheevo: %s\n", request); + RARCH_LOG("CHEEVOS url to award the cheevo: %s\n", request); #endif if (!cheevos_http_get(&result, NULL, request, NULL)) @@ -1970,8 +2004,6 @@ static unsigned cheevos_find_game_id_nes( bool cheevos_load(const void *data) { - retro_ctx_memory_info_t mem_info; - static const uint32_t genesis_exts[] = { 0x0b888feeU, /* mdx */ @@ -2008,7 +2040,6 @@ bool cheevos_load(const void *data) {cheevos_find_game_id_generic, "Generic (plain content)", NULL}, }; - size_t memory; struct retro_system_info sysinfo; unsigned i; const char *json = NULL; @@ -2019,52 +2050,21 @@ bool cheevos_load(const void *data) cheevos_locals.loaded = 0; - /* Just return OK if cheevos are disabled. */ - if (!settings->cheevos.enable) + /* Just return OK if cheevos are disabled, the core doesn't support cheevos, or info is NULL. */ + if (!settings->cheevos.enable || !cheevos_locals.core_supports || !info) return true; - /* Also return OK if there's no content. */ - if (!info) - return true; + cheevos_locals.meminfo[0].id = RETRO_MEMORY_SYSTEM_RAM; + core_get_memory(&cheevos_locals.meminfo[0]); - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_SYSTEM_RAM; + cheevos_locals.meminfo[1].id = RETRO_MEMORY_SAVE_RAM; + core_get_memory(&cheevos_locals.meminfo[1]); - core_get_memory(&mem_info); - - memory = mem_info.size; + cheevos_locals.meminfo[2].id = RETRO_MEMORY_VIDEO_RAM; + core_get_memory(&cheevos_locals.meminfo[2]); - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_VIDEO_RAM; - - core_get_memory(&mem_info); - - memory += mem_info.size; - - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_RTC; - - core_get_memory(&mem_info); - - memory += mem_info.size; - - mem_info.data = NULL; - mem_info.size = 0; - mem_info.id = RETRO_MEMORY_SAVE_RAM; - - core_get_memory(&mem_info); - - memory += mem_info.size; - - if (!memory) - { - runloop_msg_queue_push("This core doesn't support achievements", 0, 5 * 60, false); - RARCH_ERR("This core doesn't support achievements\n"); - return false; - } + cheevos_locals.meminfo[3].id = RETRO_MEMORY_RTC; + core_get_memory(&cheevos_locals.meminfo[3]); /* The the supported extensions as a hint to what method we should use. */ @@ -2306,3 +2306,8 @@ bool cheevos_set_cheats(void) return true; } + +void cheevos_set_support_cheevos(bool state) +{ + cheevos_locals.core_supports = state; +} diff --git a/cheevos.h b/cheevos.h index 8e75ed8520..02f2df55ac 100644 --- a/cheevos.h +++ b/cheevos.h @@ -42,4 +42,6 @@ bool cheevos_test(void); bool cheevos_set_cheats(void); +void cheevos_set_support_cheevos(bool state); + #endif /* __RARCH_CHEEVOS_H */ diff --git a/dynamic.c b/dynamic.c index fc800b9d06..a435df35c0 100644 --- a/dynamic.c +++ b/dynamic.c @@ -45,6 +45,7 @@ #include "cores/internal_cores.h" #include "frontend/frontend_driver.h" #include "content.h" +#include "cheevos.h" #include "retroarch.h" #include "configuration.h" #include "general.h" @@ -1428,6 +1429,14 @@ bool rarch_environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE: return video_driver_get_hw_render_interface( (const struct retro_hw_render_interface**)data); + + case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS: + { + bool state = *(const bool*)data; + RARCH_LOG("Environ SET_SUPPORT_ACHIEVEMENTS: %s.\n", state ? "yes" : "no"); + cheevos_set_support_cheevos(state); + break; + } /* Private extensions for internal use, not part of libretro API. */ /* None yet. */ diff --git a/libretro.h b/libretro.h index 3d2b2581e8..2959e14501 100644 --- a/libretro.h +++ b/libretro.h @@ -946,6 +946,15 @@ struct retro_hw_render_interface * the contents of the HW_RENDER_INTERFACE are invalidated. */ +#define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* const bool * -- + * If true, the libretro implementation supports achievements + * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS + * or via retro_get_memory_data/retro_get_memory_size. + * + * This must be called before the first call to retro_run. + */ + #define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ #define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ #define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */