From cd918390bb9f3c444c102af672f99e61a5d59283 Mon Sep 17 00:00:00 2001 From: Andre Leiradella Date: Sun, 22 Oct 2017 17:11:23 +0100 Subject: [PATCH] Moved all cheevos_var_t related stuff to their own files --- Makefile.common | 1 + cheevos/cheevos.c | 414 ++-------------------------------------------- cheevos/cheevos.h | 41 +++-- cheevos/var.c | 410 +++++++++++++++++++++++++++++++++++++++++++++ cheevos/var.h | 77 +++++++++ command.c | 11 +- 6 files changed, 543 insertions(+), 411 deletions(-) create mode 100644 cheevos/var.c create mode 100644 cheevos/var.h diff --git a/Makefile.common b/Makefile.common index 218a6e89f5..44d05c3be6 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1474,6 +1474,7 @@ ifeq ($(HAVE_NETWORKING), 1) ifeq ($(HAVE_CHEEVOS), 1) DEFINES += -DHAVE_CHEEVOS OBJ += cheevos/cheevos.o \ + cheevos/var.o \ $(LIBRETRO_COMM_DIR)/utils/md5.o endif diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c index ff5bdd5e48..231385665a 100644 --- a/cheevos/cheevos.c +++ b/cheevos/cheevos.c @@ -36,6 +36,7 @@ #endif #include "cheevos.h" +#include "var.h" #include "../command.h" #include "../dynamic.h" @@ -62,9 +63,6 @@ /* Define this macro to remove HTTP timeouts. */ #undef CHEEVOS_NO_TIMEOUT -/* Define this macro to get extra-verbose log for cheevos. */ -#undef CHEEVOS_VERBOSE - /* Define this macro to load a JSON file from disk instead of downloading * from retroachievements.org. */ #undef CHEEVOS_JSON_OVERRIDE @@ -100,60 +98,6 @@ #define CHEEVOS_EIGHT_MB ( 8 * 1024 * 1024) #define CHEEVOS_SIZE_LIMIT (64 * 1024 * 1024) -enum -{ - /* Don't change those, the values match the console IDs - * at retroachievements.org. */ - CHEEVOS_CONSOLE_MEGA_DRIVE = 1, - CHEEVOS_CONSOLE_NINTENDO_64 = 2, - CHEEVOS_CONSOLE_SUPER_NINTENDO = 3, - CHEEVOS_CONSOLE_GAMEBOY = 4, - CHEEVOS_CONSOLE_GAMEBOY_ADVANCE = 5, - CHEEVOS_CONSOLE_GAMEBOY_COLOR = 6, - CHEEVOS_CONSOLE_NINTENDO = 7, - CHEEVOS_CONSOLE_PC_ENGINE = 8, - CHEEVOS_CONSOLE_SEGA_CD = 9, - CHEEVOS_CONSOLE_SEGA_32X = 10, - CHEEVOS_CONSOLE_MASTER_SYSTEM = 11 -}; - -enum -{ - CHEEVOS_VAR_SIZE_BIT_0 = 0, - CHEEVOS_VAR_SIZE_BIT_1, - CHEEVOS_VAR_SIZE_BIT_2, - CHEEVOS_VAR_SIZE_BIT_3, - CHEEVOS_VAR_SIZE_BIT_4, - CHEEVOS_VAR_SIZE_BIT_5, - CHEEVOS_VAR_SIZE_BIT_6, - CHEEVOS_VAR_SIZE_BIT_7, - CHEEVOS_VAR_SIZE_NIBBLE_LOWER, - CHEEVOS_VAR_SIZE_NIBBLE_UPPER, - /* Byte, */ - CHEEVOS_VAR_SIZE_EIGHT_BITS, /* =Byte, */ - CHEEVOS_VAR_SIZE_SIXTEEN_BITS, - CHEEVOS_VAR_SIZE_THIRTYTWO_BITS, - - CHEEVOS_VAR_SIZE_LAST -}; /* cheevos_var_t.size */ - -enum -{ - /* compare to the value of a live address in RAM */ - CHEEVOS_VAR_TYPE_ADDRESS = 0, - - /* a number. assume 32 bit */ - CHEEVOS_VAR_TYPE_VALUE_COMP, - - /* the value last known at this address. */ - CHEEVOS_VAR_TYPE_DELTA_MEM, - - /* a custom user-set variable */ - CHEEVOS_VAR_TYPE_DYNAMIC_VAR, - - CHEEVOS_VAR_TYPE_LAST -}; /* cheevos_var_t.type */ - enum { CHEEVOS_COND_OP_EQUALS = 0, @@ -918,44 +862,6 @@ static int cheevos_count_cheevos(const char *json, Parse the MemAddr field. *****************************************************************************/ -static unsigned cheevos_prefix_to_comp_size(char prefix) -{ - /* Careful not to use ABCDEF here, this denotes part of an actual variable! */ - - switch( toupper( (unsigned char)prefix ) ) - { - case 'M': - return CHEEVOS_VAR_SIZE_BIT_0; - case 'N': - return CHEEVOS_VAR_SIZE_BIT_1; - case 'O': - return CHEEVOS_VAR_SIZE_BIT_2; - case 'P': - return CHEEVOS_VAR_SIZE_BIT_3; - case 'Q': - return CHEEVOS_VAR_SIZE_BIT_4; - case 'R': - return CHEEVOS_VAR_SIZE_BIT_5; - case 'S': - return CHEEVOS_VAR_SIZE_BIT_6; - case 'T': - return CHEEVOS_VAR_SIZE_BIT_7; - case 'L': - return CHEEVOS_VAR_SIZE_NIBBLE_LOWER; - case 'U': - return CHEEVOS_VAR_SIZE_NIBBLE_UPPER; - case 'H': - return CHEEVOS_VAR_SIZE_EIGHT_BITS; - case 'X': - return CHEEVOS_VAR_SIZE_THIRTYTWO_BITS; - default: - case ' ': - break; - } - - return CHEEVOS_VAR_SIZE_SIXTEEN_BITS; -} - static unsigned cheevos_read_hits(const char **memaddr) { char *end = NULL; @@ -1022,203 +928,6 @@ static unsigned cheevos_parse_operator(const char **memaddr) return op; } -static size_t cheevos_reduce(size_t addr, size_t mask) -{ - while (mask) - { - size_t tmp = (mask - 1) & ~mask; - addr = (addr & tmp) | ((addr >> 1) & ~tmp); - mask = (mask & (mask - 1)) >> 1; - } - - return addr; -} - -static size_t cheevos_highest_bit(size_t n) -{ - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - - return n ^ (n >> 1); -} - -void cheevos_parse_guest_addr(cheevos_var_t *var, unsigned value) -{ - rarch_system_info_t *system = runloop_get_system_info(); - - var->bank_id = -1; - var->value = value; - - switch (cheevos_locals.console_id) - { - case CHEEVOS_CONSOLE_NINTENDO: - if (var->value >= 0x0800 && var->value < 0x2000) - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: NES memory address in mirrorred RAM %X, adjusted to %X\n", var->value, var->value & 0x07ff); -#endif - var->value &= 0x07ff; - } - break; - - case CHEEVOS_CONSOLE_GAMEBOY_COLOR: - if (var->value >= 0xe000 && var->value <= 0xfdff) - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: GBC memory address in echo RAM %X, adjusted to %X\n", var->value, var->value - 0x2000); -#endif - var->value -= 0x2000; - } - break; - } - - if (system->mmaps.num_descriptors != 0) - { - const rarch_memory_descriptor_t *desc = NULL; - const rarch_memory_descriptor_t *end = NULL; - - switch (cheevos_locals.console_id) - { - /* Patch the address to correctly map it to the mmaps */ - case CHEEVOS_CONSOLE_GAMEBOY_ADVANCE: - if (var->value < 0x8000) /* Internal RAM */ - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: GBA memory address %X adjusted to %X\n", var->value, var->value + 0x3000000); -#endif - var->value += 0x3000000; - } - else /* Work RAM */ - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: GBA memory address %X adjusted to %X\n", var->value, var->value + 0x2000000 - 0x8000); -#endif - var->value += 0x2000000 - 0x8000; - } - break; - - case CHEEVOS_CONSOLE_PC_ENGINE: -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: PCE memory address %X adjusted to %X\n", var->value, var->value + 0x1f0000); -#endif - var->value += 0x1f0000; - break; - - case CHEEVOS_CONSOLE_SUPER_NINTENDO: - if (var->value < 0x020000) /* Work RAM */ - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: SNES memory address %X adjusted to %X\n", var->value, var->value + 0x7e0000); -#endif - var->value += 0x7e0000; - } - else /* Save RAM */ - { -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: SNES memory address %X adjusted to %X\n", var->value, var->value + 0x006000 - 0x020000); -#endif - var->value += 0x006000 - 0x020000; - } - break; - - default: - break; - } - - desc = system->mmaps.descriptors; - end = desc + system->mmaps.num_descriptors; - - for (; desc < end; desc++) - { - if (((desc->core.start ^ var->value) & desc->core.select) == 0) - { - unsigned addr = var->value; - var->bank_id = (int)(desc - system->mmaps.descriptors); - var->value = (unsigned)cheevos_reduce( - (var->value - desc->core.start) & desc->disconnect_mask, - desc->core.disconnect); - - if (var->value >= desc->core.len) - var->value -= cheevos_highest_bit(var->value); - - var->value += desc->core.offset; - -#ifdef CHEEVOS_VERBOSE - RARCH_LOG("[CHEEVOS]: Address %X set to descriptor %d at offset %X\n", addr, var->bank_id + 1, var->value); -#endif - break; - } - } - } - else - { - unsigned i; - - for (i = 0; i < ARRAY_SIZE(cheevos_locals.meminfo); i++) - { - if (var->value < cheevos_locals.meminfo[i].size) - { - var->bank_id = i; - break; - } - - /* HACK subtract the correct amount of bytes to reach the save RAM */ - if (i == 0 && cheevos_locals.console_id == CHEEVOS_CONSOLE_NINTENDO) - var->value -= 0x6000; - else - var->value -= cheevos_locals.meminfo[i].size; - } - } -} - -static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr) -{ - char *end = NULL; - const char *str = *memaddr; - unsigned base = 16; - - if (toupper((unsigned char)*str) == 'D' && str[1] == '0' && toupper((unsigned char)str[2]) == 'X') - { - /* d0x + 4 hex digits */ - str += 3; - var->type = CHEEVOS_VAR_TYPE_DELTA_MEM; - } - else if (*str == '0' && toupper((unsigned char)str[1]) == 'X') - { - /* 0x + 4 hex digits */ - str += 2; - var->type = CHEEVOS_VAR_TYPE_ADDRESS; - } - else - { - var->type = CHEEVOS_VAR_TYPE_VALUE_COMP; - - if (toupper((unsigned char)*str) == 'H') - str++; - else - { - if (toupper((unsigned char)*str) == 'V') - str++; - - base = 10; - } - } - - if (var->type != CHEEVOS_VAR_TYPE_VALUE_COMP) - { - var->size = cheevos_prefix_to_comp_size(*str); - - if (var->size != CHEEVOS_VAR_SIZE_SIXTEEN_BITS) - str++; - } - - var->value = (unsigned)strtol(str, &end, base); - *memaddr = end; -} - static void cheevos_parse_cond(cheevos_cond_t *cond, const char **memaddr) { const char* str = *memaddr; @@ -1253,9 +962,9 @@ static void cheevos_parse_cond(cheevos_cond_t *cond, const char **memaddr) str += skip; } - cheevos_parse_var(&cond->source, &str); + cheevos_var_parse(&cond->source, &str); cond->op = cheevos_parse_operator(&str); - cheevos_parse_var(&cond->target, &str); + cheevos_var_parse(&cond->target, &str); cond->curr_hits = 0; cond->req_hits = cheevos_read_hits(&str); @@ -1450,7 +1159,7 @@ static int cheevos_parse_expression(cheevos_expr_t *expr, const char* mem) for (i = 0, aux = mem; i < expr->count; i++) { - cheevos_parse_var(&expr->terms[i].var, &aux); + cheevos_var_parse(&expr->terms[i].var, &aux); if (*aux != '*') { @@ -1816,104 +1525,10 @@ error: Test all the achievements (call once per frame). *****************************************************************************/ -uint8_t *cheevos_get_memory(const cheevos_var_t *var) -{ - uint8_t *memory = NULL; - - if (var->bank_id >= 0) - { - rarch_system_info_t *system = runloop_get_system_info(); - - if (system->mmaps.num_descriptors != 0) - memory = (uint8_t *)system->mmaps.descriptors[var->bank_id].core.ptr; - else - memory = (uint8_t *)cheevos_locals.meminfo[var->bank_id].data; - - if (memory) - memory += var->value; - } - - return memory; -} - -static unsigned cheevos_get_var_value(cheevos_var_t *var) -{ - if (var->type == CHEEVOS_VAR_TYPE_VALUE_COMP) - return var->value; - - if ( var->type == CHEEVOS_VAR_TYPE_ADDRESS - || var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) - { - const uint8_t *memory = cheevos_get_memory(var); - unsigned live_val = 0; - - if (memory) - { - live_val = memory[0]; - - switch (var->size) - { - case CHEEVOS_VAR_SIZE_BIT_0: - live_val &= 1; - break; - case CHEEVOS_VAR_SIZE_BIT_1: - live_val = (live_val >> 1) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_2: - live_val = (live_val >> 2) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_3: - live_val = (live_val >> 3) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_4: - live_val = (live_val >> 4) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_5: - live_val = (live_val >> 5) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_6: - live_val = (live_val >> 6) & 1; - break; - case CHEEVOS_VAR_SIZE_BIT_7: - live_val = (live_val >> 7) & 1; - break; - case CHEEVOS_VAR_SIZE_NIBBLE_LOWER: - live_val &= 0x0f; - break; - case CHEEVOS_VAR_SIZE_NIBBLE_UPPER: - live_val = (live_val >> 4) & 0x0f; - break; - case CHEEVOS_VAR_SIZE_EIGHT_BITS: - break; - case CHEEVOS_VAR_SIZE_SIXTEEN_BITS: - live_val |= memory[1] << 8; - break; - case CHEEVOS_VAR_SIZE_THIRTYTWO_BITS: - live_val |= memory[1] << 8; - live_val |= memory[2] << 16; - live_val |= memory[3] << 24; - break; - } - } - - if (var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) - { - unsigned previous = var->previous; - var->previous = live_val; - return previous; - } - - return live_val; - } - - /* We shouldn't get here... */ - return 0; -} - static int cheevos_test_condition(cheevos_cond_t *cond) { - unsigned sval = cheevos_get_var_value(&cond->source) + cheevos_locals.add_buffer; - unsigned tval = cheevos_get_var_value(&cond->target); + unsigned sval = cheevos_var_get_value(&cond->source) + cheevos_locals.add_buffer; + unsigned tval = cheevos_var_get_value(&cond->target); switch (cond->op) { @@ -1975,14 +1590,14 @@ static int cheevos_test_cond_set(const cheevos_condset_t *condset, if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE) { - cheevos_locals.add_buffer += cheevos_get_var_value(&cond->source); + cheevos_locals.add_buffer += cheevos_var_get_value(&cond->source); set_valid &= 1; continue; } if (cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE) { - cheevos_locals.add_buffer -= cheevos_get_var_value(&cond->source); + cheevos_locals.add_buffer -= cheevos_var_get_value(&cond->source); set_valid &= 1; continue; } @@ -2276,7 +1891,7 @@ static int cheevos_expr_value(cheevos_expr_t* expr) for (i = expr->count; i != 0; i--, term++) { - value += cheevos_get_var_value(&term->var) * term->multiplier; + value += cheevos_var_get_value(&term->var) * term->multiplier; } return value; @@ -2721,7 +2336,7 @@ static void cheevos_patch_addresses(cheevoset_t* set) { case CHEEVOS_VAR_TYPE_ADDRESS: case CHEEVOS_VAR_TYPE_DELTA_MEM: - cheevos_parse_guest_addr(&cond->source, cond->source.value); + cheevos_var_patch_addr(&cond->source, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS RARCH_LOG("[CHEEVOS]: var %03d:%08X\n", cond->source.bank_id + 1, cond->source.value); #endif @@ -2735,7 +2350,7 @@ static void cheevos_patch_addresses(cheevoset_t* set) { case CHEEVOS_VAR_TYPE_ADDRESS: case CHEEVOS_VAR_TYPE_DELTA_MEM: - cheevos_parse_guest_addr(&cond->target, cond->target.value); + cheevos_var_patch_addr(&cond->target, cheevos_locals.console_id); #ifdef CHEEVOS_DUMP_ADDRS RARCH_LOG("[CHEEVOS]: var %03d:%08X\n", cond->target.bank_id + 1, cond->target.value); #endif @@ -2788,6 +2403,11 @@ bool cheevos_get_support_cheevos(void) return cheevos_locals.core_supports; } +cheevos_console_t cheevos_get_console(void) +{ + return cheevos_locals.console_id; +} + typedef struct { uint8_t id[4]; /* NES^Z */ @@ -2861,7 +2481,7 @@ static int cheevos_iterate(coro_t* coro) size_t to_read = 4096; uint8_t *buffer = NULL; const char *end = NULL; - + enum { /* Negative values because CORO_SUB generates positive values */ diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h index 6594105ac3..5b14489cca 100644 --- a/cheevos/cheevos.h +++ b/cheevos/cheevos.h @@ -23,6 +23,19 @@ RETRO_BEGIN_DECLS +/***************************************************************************** +Setup - mainly for debugging +*****************************************************************************/ + +/* Define this macro to get extra-verbose log for cheevos. */ +#undef CHEEVOS_VERBOSE + +/***************************************************************************** +End of setup +*****************************************************************************/ + +#define CHEEVOS_TAG "[CHEEVOS]: " + typedef struct cheevos_ctx_desc { unsigned idx; @@ -30,14 +43,24 @@ typedef struct cheevos_ctx_desc size_t len; } cheevos_ctx_desc_t; -typedef struct +typedef enum { - unsigned size; - unsigned type; - int bank_id; - unsigned value; - unsigned previous; -} cheevos_var_t; + /* Don't change those, the values match the console IDs + * at retroachievements.org. */ + CHEEVOS_CONSOLE_MEGA_DRIVE = 1, + CHEEVOS_CONSOLE_NINTENDO_64 = 2, + CHEEVOS_CONSOLE_SUPER_NINTENDO = 3, + CHEEVOS_CONSOLE_GAMEBOY = 4, + CHEEVOS_CONSOLE_GAMEBOY_ADVANCE = 5, + CHEEVOS_CONSOLE_GAMEBOY_COLOR = 6, + CHEEVOS_CONSOLE_NINTENDO = 7, + CHEEVOS_CONSOLE_PC_ENGINE = 8, + CHEEVOS_CONSOLE_SEGA_CD = 9, + CHEEVOS_CONSOLE_SEGA_32X = 10, + CHEEVOS_CONSOLE_MASTER_SYSTEM = 11, + CHEEVOS_CONSOLE_XBOX_360 = 12, + CHEEVOS_CONSOLE_ATARI_LYNX = 13 +} cheevos_console_t; bool cheevos_load(const void *data); @@ -61,9 +84,7 @@ void cheevos_set_support_cheevos(bool state); bool cheevos_get_support_cheevos(void); -void cheevos_parse_guest_addr(cheevos_var_t *var, unsigned value); - -uint8_t *cheevos_get_memory(const cheevos_var_t *var); +cheevos_console_t cheevos_get_console(void); extern bool cheevos_loaded; extern int cheats_are_enabled; diff --git a/cheevos/var.c b/cheevos/var.c new file mode 100644 index 0000000000..1f11707354 --- /dev/null +++ b/cheevos/var.c @@ -0,0 +1,410 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2015-2017 - 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 + +#include + +#include "var.h" + +#include "../retroarch.h" +#include "../core.h" +#include "../verbosity.h" + +#ifdef CHEEVOS_VERBOSE +#define CHEEVOS_LOG RARCH_LOG +#else +#define CHEEVOS_LOG(...) +#endif + +/***************************************************************************** +Parsing +*****************************************************************************/ + +static cheevos_var_size_t cheevos_var_parse_prefix(const char** memaddr) +{ + /* Careful not to use ABCDEF here, this denotes part of an actual variable! */ + const char* str = *memaddr; + cheevos_var_size_t size; + + switch (toupper((unsigned char)*str++)) + { + case 'M': + size = CHEEVOS_VAR_SIZE_BIT_0; + break; + case 'N': + size = CHEEVOS_VAR_SIZE_BIT_1; + break; + case 'O': + size = CHEEVOS_VAR_SIZE_BIT_2; + break; + case 'P': + size = CHEEVOS_VAR_SIZE_BIT_3; + break; + case 'Q': + size = CHEEVOS_VAR_SIZE_BIT_4; + break; + case 'R': + size = CHEEVOS_VAR_SIZE_BIT_5; + break; + case 'S': + size = CHEEVOS_VAR_SIZE_BIT_6; + break; + case 'T': + size = CHEEVOS_VAR_SIZE_BIT_7; + break; + case 'L': + size = CHEEVOS_VAR_SIZE_NIBBLE_LOWER; + break; + case 'U': + size = CHEEVOS_VAR_SIZE_NIBBLE_UPPER; + break; + case 'H': + size = CHEEVOS_VAR_SIZE_EIGHT_BITS; + break; + case 'X': + size = CHEEVOS_VAR_SIZE_THIRTYTWO_BITS; + break; + default: + str--; + /* fall through */ + case ' ': + size = CHEEVOS_VAR_SIZE_SIXTEEN_BITS; + break; + } + + *memaddr = str; + return size; +} + +static size_t cheevos_var_reduce(size_t addr, size_t mask) +{ + while (mask) + { + size_t tmp = (mask - 1) & ~mask; + addr = (addr & tmp) | ((addr >> 1) & ~tmp); + mask = (mask & (mask - 1)) >> 1; + } + + return addr; +} + +static size_t cheevos_var_highest_bit(size_t n) +{ + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + + return n ^ (n >> 1); +} + +void cheevos_var_parse(cheevos_var_t* var, const char** memaddr) +{ + char *end = NULL; + const char *str = *memaddr; + unsigned base = 16; + + if (toupper((unsigned char)*str) == 'D' && str[1] == '0' && toupper((unsigned char)str[2]) == 'X') + { + /* d0x + 4 hex digits */ + str += 3; + var->type = CHEEVOS_VAR_TYPE_DELTA_MEM; + } + else if (*str == '0' && toupper((unsigned char)str[1]) == 'X') + { + /* 0x + 4 hex digits */ + str += 2; + var->type = CHEEVOS_VAR_TYPE_ADDRESS; + } + else + { + var->type = CHEEVOS_VAR_TYPE_VALUE_COMP; + + if (toupper((unsigned char)*str) == 'H') + str++; + else + { + if (toupper((unsigned char)*str) == 'V') + str++; + + base = 10; + } + } + + if (var->type != CHEEVOS_VAR_TYPE_VALUE_COMP) + { + var->size = cheevos_var_parse_prefix(&str); + } + + var->value = (unsigned)strtol(str, &end, base); + *memaddr = end; +} + +void cheevos_var_patch_addr(cheevos_var_t* var, cheevos_console_t console) +{ + rarch_system_info_t *system = runloop_get_system_info(); + + var->bank_id = -1; + + if (console == CHEEVOS_CONSOLE_NINTENDO) + { + if (var->value >= 0x0800 && var->value < 0x2000) + { + CHEEVOS_LOG(CHEEVOS_TAG "NES memory address in mirrorred RAM %X, adjusted to %X\n", var->value, var->value & 0x07ff); + var->value &= 0x07ff; + } + } + else if (console == CHEEVOS_CONSOLE_GAMEBOY_COLOR) + { + if (var->value >= 0xe000 && var->value <= 0xfdff) + { + CHEEVOS_LOG(CHEEVOS_TAG "GBC memory address in echo RAM %X, adjusted to %X\n", var->value, var->value - 0x2000); + var->value -= 0x2000; + } + } + + if (system->mmaps.num_descriptors != 0) + { + const rarch_memory_descriptor_t *desc = NULL; + const rarch_memory_descriptor_t *end = NULL; + + /* Patch the address to correctly map it to the mmaps */ + if (console == CHEEVOS_CONSOLE_GAMEBOY_ADVANCE) + { + if (var->value < 0x8000) /* Internal RAM */ + { + CHEEVOS_LOG(CHEEVOS_TAG "GBA memory address %X adjusted to %X\n", var->value, var->value + 0x3000000); + var->value += 0x3000000; + } + else /* Work RAM */ + { + CHEEVOS_LOG(CHEEVOS_TAG "GBA memory address %X adjusted to %X\n", var->value, var->value + 0x2000000 - 0x8000); + var->value += 0x2000000 - 0x8000; + } + } + else if (console == CHEEVOS_CONSOLE_PC_ENGINE) + { + CHEEVOS_LOG(CHEEVOS_TAG "PCE memory address %X adjusted to %X\n", var->value, var->value + 0x1f0000); + var->value += 0x1f0000; + } + else if (console == CHEEVOS_CONSOLE_SUPER_NINTENDO) + { + if (var->value < 0x020000) /* Work RAM */ + { + CHEEVOS_LOG(CHEEVOS_TAG "SNES memory address %X adjusted to %X\n", var->value, var->value + 0x7e0000); + var->value += 0x7e0000; + } + else /* Save RAM */ + { + CHEEVOS_LOG(CHEEVOS_TAG "SNES memory address %X adjusted to %X\n", var->value, var->value + 0x006000 - 0x020000); + var->value += 0x006000 - 0x020000; + } + } + + desc = system->mmaps.descriptors; + end = desc + system->mmaps.num_descriptors; + + for (; desc < end; desc++) + { + if (((desc->core.start ^ var->value) & desc->core.select) == 0) + { + unsigned addr = var->value; + var->bank_id = (int)(desc - system->mmaps.descriptors); + var->value = (unsigned)cheevos_var_reduce( + (var->value - desc->core.start) & desc->disconnect_mask, + desc->core.disconnect); + + if (var->value >= desc->core.len) + var->value -= cheevos_var_highest_bit(var->value); + + var->value += desc->core.offset; + + CHEEVOS_LOG(CHEEVOS_TAG "address %X set to descriptor %d at offset %X\n", addr, var->bank_id + 1, var->value); + break; + } + } + } + else + { + unsigned i; + + for (i = 0; i < 4; i++) + { + retro_ctx_memory_info_t meminfo; + + switch (i) + { + case 0: + meminfo.id = RETRO_MEMORY_SYSTEM_RAM; + break; + case 1: + meminfo.id = RETRO_MEMORY_SAVE_RAM; + break; + case 2: + meminfo.id = RETRO_MEMORY_VIDEO_RAM; + break; + case 3: + meminfo.id = RETRO_MEMORY_RTC; + break; + } + + core_get_memory(&meminfo); + + if (var->value < meminfo.size) + { + var->bank_id = i; + break; + } + + /* HACK subtract the correct amount of bytes to reach the save RAM */ + if (i == 0 && console == CHEEVOS_CONSOLE_NINTENDO) + var->value -= 0x6000; + else + var->value -= meminfo.size; + } + } +} + +/***************************************************************************** +Testing +*****************************************************************************/ + +uint8_t* cheevos_var_get_memory(const cheevos_var_t* var) +{ + uint8_t* memory = NULL; + + if (var->bank_id >= 0) + { + rarch_system_info_t* system = runloop_get_system_info(); + + if (system->mmaps.num_descriptors != 0) + memory = (uint8_t*)system->mmaps.descriptors[var->bank_id].core.ptr; + else + { + retro_ctx_memory_info_t meminfo; + + switch (var->bank_id) + { + case 0: + meminfo.id = RETRO_MEMORY_SYSTEM_RAM; + break; + case 1: + meminfo.id = RETRO_MEMORY_SAVE_RAM; + break; + case 2: + meminfo.id = RETRO_MEMORY_VIDEO_RAM; + break; + case 3: + meminfo.id = RETRO_MEMORY_RTC; + break; + default: + RARCH_ERR(CHEEVOS_TAG "invalid bank id: %s\n", var->bank_id); + break; + } + + core_get_memory(&meminfo); + memory = (uint8_t*)meminfo.data; + } + + if (memory) + memory += var->value; + } + + return memory; +} + +unsigned cheevos_var_get_value(cheevos_var_t* var) +{ + const uint8_t* memory = NULL; + unsigned value = 0; + + switch (var->type) + { + case CHEEVOS_VAR_TYPE_VALUE_COMP: + value = var->value; + break; + + case CHEEVOS_VAR_TYPE_ADDRESS: + case CHEEVOS_VAR_TYPE_DELTA_MEM: + memory = cheevos_var_get_memory(var); + + if (memory) + { + value = memory[0]; + + switch (var->size) + { + case CHEEVOS_VAR_SIZE_BIT_0: + value &= 1; + break; + case CHEEVOS_VAR_SIZE_BIT_1: + value = (value >> 1) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_2: + value = (value >> 2) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_3: + value = (value >> 3) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_4: + value = (value >> 4) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_5: + value = (value >> 5) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_6: + value = (value >> 6) & 1; + break; + case CHEEVOS_VAR_SIZE_BIT_7: + value = (value >> 7) & 1; + break; + case CHEEVOS_VAR_SIZE_NIBBLE_LOWER: + value &= 0x0f; + break; + case CHEEVOS_VAR_SIZE_NIBBLE_UPPER: + value = (value >> 4) & 0x0f; + break; + case CHEEVOS_VAR_SIZE_EIGHT_BITS: + break; + case CHEEVOS_VAR_SIZE_SIXTEEN_BITS: + value |= memory[1] << 8; + break; + case CHEEVOS_VAR_SIZE_THIRTYTWO_BITS: + value |= memory[1] << 8; + value |= memory[2] << 16; + value |= memory[3] << 24; + break; + } + } + + if (var->type == CHEEVOS_VAR_TYPE_DELTA_MEM) + { + unsigned previous = var->previous; + var->previous = value; + value = previous; + } + + break; + + case CHEEVOS_VAR_TYPE_DYNAMIC_VAR: + /* We shouldn't get here... */ + break; + } + + return value; +} diff --git a/cheevos/var.h b/cheevos/var.h new file mode 100644 index 0000000000..2e9959288e --- /dev/null +++ b/cheevos/var.h @@ -0,0 +1,77 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2015-2017 - 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_VAR_H +#define __RARCH_CHEEVOS_VAR_H + +#include + +#include "cheevos.h" + +#include + +RETRO_BEGIN_DECLS + +typedef enum +{ + CHEEVOS_VAR_SIZE_BIT_0 = 0, + CHEEVOS_VAR_SIZE_BIT_1, + CHEEVOS_VAR_SIZE_BIT_2, + CHEEVOS_VAR_SIZE_BIT_3, + CHEEVOS_VAR_SIZE_BIT_4, + CHEEVOS_VAR_SIZE_BIT_5, + CHEEVOS_VAR_SIZE_BIT_6, + CHEEVOS_VAR_SIZE_BIT_7, + CHEEVOS_VAR_SIZE_NIBBLE_LOWER, + CHEEVOS_VAR_SIZE_NIBBLE_UPPER, + /* Byte, */ + CHEEVOS_VAR_SIZE_EIGHT_BITS, /* =Byte, */ + CHEEVOS_VAR_SIZE_SIXTEEN_BITS, + CHEEVOS_VAR_SIZE_THIRTYTWO_BITS +} cheevos_var_size_t; + +typedef enum +{ + /* compare to the value of a live address in RAM */ + CHEEVOS_VAR_TYPE_ADDRESS = 0, + + /* a number. assume 32 bit */ + CHEEVOS_VAR_TYPE_VALUE_COMP, + + /* the value last known at this address. */ + CHEEVOS_VAR_TYPE_DELTA_MEM, + + /* a custom user-set variable */ + CHEEVOS_VAR_TYPE_DYNAMIC_VAR +} cheevos_var_type_t; + +typedef struct +{ + cheevos_var_size_t size; + cheevos_var_type_t type; + int bank_id; + unsigned value; + unsigned previous; +} cheevos_var_t; + +void cheevos_var_parse(cheevos_var_t* var, const char** memaddr); +void cheevos_var_patch_addr(cheevos_var_t* var, cheevos_console_t console); + +uint8_t* cheevos_var_get_memory(const cheevos_var_t* var); +unsigned cheevos_var_get_value(cheevos_var_t* var); + +RETRO_END_DECLS + +#endif /* __RARCH_CHEEVOS_VAR_H */ diff --git a/command.c b/command.c index ea2e9e15a4..dd22a40c67 100644 --- a/command.c +++ b/command.c @@ -40,6 +40,7 @@ #ifdef HAVE_CHEEVOS #include "cheevos/cheevos.h" +#include "cheevos/var.h" #endif #ifdef HAVE_MENU @@ -261,8 +262,9 @@ static bool command_read_ram(const char *arg) reply_at = reply + strlen("READ_CORE_RAM "); strlcpy(reply_at, arg, sizeof(reply)-strlen(reply)); - cheevos_parse_guest_addr(&var, strtoul(reply_at, (char**)&reply_at, 16)); - data = cheevos_get_memory(&var); + var.value = strtoul(reply_at, (char**)&reply_at, 16); + cheevos_var_patch_addr(&var, cheevos_get_console()); + data = cheevos_var_get_memory(&var); if (data) { @@ -293,9 +295,10 @@ static bool command_write_ram(const char *arg) unsigned nbytes = 0; uint8_t *data = NULL; - cheevos_parse_guest_addr(&var, strtoul(arg, (char**)&arg, 16)); + var.value = strtoul(arg, (char**)&arg, 16); + cheevos_var_patch_addr(&var, cheevos_get_console()); - data = cheevos_get_memory(&var); + data = cheevos_var_get_memory(&var); if (data) {