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)
{