diff --git a/Makefile.common b/Makefile.common
index 289328dac2..74d1a6c625 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1676,11 +1676,58 @@ ifeq ($(HAVE_NETWORKING), 1)
# Retro Achievements
ifeq ($(HAVE_CHEEVOS), 1)
- DEFINES += -DHAVE_CHEEVOS
+ DEFINES += -DHAVE_CHEEVOS \
+ -Ideps/rcheevos/include \
+ -Ideps/lua/src
OBJ += cheevos/cheevos.o \
- cheevos/var.o \
- cheevos/cond.o \
+ cheevos/fixup.o \
+ cheevos/parser.o \
cheevos/badges.o \
+ cheevos/hash.o \
+ deps/rcheevos/src/rcheevos/trigger.o \
+ deps/rcheevos/src/rcheevos/condset.o \
+ deps/rcheevos/src/rcheevos/condition.o \
+ deps/rcheevos/src/rcheevos/operand.o \
+ deps/rcheevos/src/rcheevos/term.o \
+ deps/rcheevos/src/rcheevos/expression.o \
+ deps/rcheevos/src/rcheevos/value.o \
+ deps/rcheevos/src/rcheevos/lboard.o \
+ deps/rcheevos/src/rcheevos/alloc.o \
+ deps/rcheevos/src/rcheevos/format.o \
+ deps/rcheevos/src/rurl/url.o \
+ deps/lua/src/lapi.o \
+ deps/lua/src/lcode.o \
+ deps/lua/src/lctype.o \
+ deps/lua/src/ldebug.o \
+ deps/lua/src/ldo.o \
+ deps/lua/src/ldump.o \
+ deps/lua/src/lfunc.o \
+ deps/lua/src/lgc.o \
+ deps/lua/src/llex.o \
+ deps/lua/src/lmem.o \
+ deps/lua/src/lobject.o \
+ deps/lua/src/lopcodes.o \
+ deps/lua/src/lparser.o \
+ deps/lua/src/lstate.o \
+ deps/lua/src/lstring.o \
+ deps/lua/src/ltable.o \
+ deps/lua/src/ltm.o \
+ deps/lua/src/lundump.o \
+ deps/lua/src/lvm.o \
+ deps/lua/src/lzio.o \
+ deps/lua/src/lauxlib.o \
+ deps/lua/src/lbaselib.o \
+ deps/lua/src/lbitlib.o \
+ deps/lua/src/lcorolib.o \
+ deps/lua/src/ldblib.o \
+ deps/lua/src/liolib.o \
+ deps/lua/src/lmathlib.o \
+ deps/lua/src/loslib.o \
+ deps/lua/src/lstrlib.o \
+ deps/lua/src/ltablib.o \
+ deps/lua/src/lutf8lib.o \
+ deps/lua/src/loadlib.o \
+ deps/lua/src/linit.o \
$(LIBRETRO_COMM_DIR)/utils/md5.o
endif
diff --git a/cheevos/badges.h b/cheevos/badges.h
index 05fe875a2a..f2bee1993a 100644
--- a/cheevos/badges.h
+++ b/cheevos/badges.h
@@ -1,5 +1,20 @@
-#ifndef __RARCH_BADGE_H
-#define __RARCH_BADGE_H
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - 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_BADGE_H
+#define __RARCH_CHEEVOS_BADGE_H
#include "../menu/menu_driver.h"
diff --git a/cheevos/cheevos.c b/cheevos/cheevos.c
index c542783d29..c892817a5d 100644
--- a/cheevos/cheevos.c
+++ b/cheevos/cheevos.c
@@ -18,7 +18,6 @@
#include
#include
-#include
#include
#include
#include
@@ -44,8 +43,10 @@
#include "badges.h"
#include "cheevos.h"
-#include "var.h"
-#include "cond.h"
+#include "fixup.h"
+#include "parser.h"
+#include "hash.h"
+#include "util.h"
#include "../file_path_special.h"
#include "../paths.h"
@@ -60,7 +61,8 @@
#include "../network/net_http_special.h"
#include "../tasks/tasks_internal.h"
-#include "../verbosity.h"
+#include
+#include
/* Define this macro to prevent cheevos from being deactivated. */
#undef CHEEVOS_DONT_DEACTIVATE
@@ -89,164 +91,50 @@
/* Define this macro to log downloaded badge images. */
#undef CHEEVOS_LOG_BADGES
-/* C89 wants only int values in enums. */
-#define CHEEVOS_JSON_KEY_GAMEID 0xb4960eecU
-#define CHEEVOS_JSON_KEY_ACHIEVEMENTS 0x69749ae1U
-#define CHEEVOS_JSON_KEY_ID 0x005973f2U
-#define CHEEVOS_JSON_KEY_MEMADDR 0x1e76b53fU
-#define CHEEVOS_JSON_KEY_TITLE 0x0e2a9a07U
-#define CHEEVOS_JSON_KEY_DESCRIPTION 0xe61a1f69U
-#define CHEEVOS_JSON_KEY_POINTS 0xca8fce22U
-#define CHEEVOS_JSON_KEY_AUTHOR 0xa804edb8U
-#define CHEEVOS_JSON_KEY_MODIFIED 0xdcea4fe6U
-#define CHEEVOS_JSON_KEY_CREATED 0x3a84721dU
-#define CHEEVOS_JSON_KEY_BADGENAME 0x887685d9U
-#define CHEEVOS_JSON_KEY_CONSOLE_ID 0x071656e5U
-#define CHEEVOS_JSON_KEY_TOKEN 0x0e2dbd26U
-#define CHEEVOS_JSON_KEY_FLAGS 0x0d2e96b2U
-#define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU
-#define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U
-#define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU
-#define CHEEVOS_JSON_KEY_SUCCESS 0x110461deU
-#define CHEEVOS_JSON_KEY_ERROR 0x0d2011cfU
+typedef struct
+{
+ rc_trigger_t* trigger;
+ const cheevos_racheevo_t* info;
+ int active;
+ int last;
+} cheevos_cheevo_t;
typedef struct
{
- cheevos_cond_t *conds;
- unsigned count;
-} cheevos_condset_t;
-
-typedef struct
-{
- cheevos_condset_t *condsets;
- unsigned count;
-} cheevos_condition_t;
-
-typedef struct
-{
- unsigned id;
- const char *title;
- const char *description;
- const char *author;
- const char *badge;
- unsigned points;
- unsigned dirty;
- int active;
- int last;
- int modified;
-
- cheevos_condition_t condition;
-} cheevo_t;
-
-typedef struct
-{
- cheevo_t *cheevos;
- unsigned count;
-} cheevoset_t;
-
-typedef struct
-{
- int is_element;
- int mode;
-} cheevos_deactivate_t;
-
-typedef struct
-{
- unsigned key_hash;
- int is_key;
- const char *value;
- size_t length;
-} cheevos_getvalueud_t;
-
-typedef struct
-{
- int in_cheevos;
- int in_lboards;
- uint32_t field_hash;
- unsigned core_count;
- unsigned unofficial_count;
- unsigned lboard_count;
-} cheevos_countud_t;
-
-typedef struct
-{
- const char *string;
- size_t length;
-} cheevos_field_t;
-
-typedef struct
-{
- int in_cheevos;
- int in_lboards;
- int is_console_id;
- unsigned core_count;
- unsigned unofficial_count;
- unsigned lboard_count;
-
- cheevos_field_t *field;
- cheevos_field_t id, memaddr, title, desc, points, author;
- cheevos_field_t modified, created, badge, flags, format;
-} cheevos_readud_t;
-
-typedef struct
-{
- int label;
- const char *name;
- const uint32_t *ext_hashes;
-} cheevos_finder_t;
-
-typedef struct
-{
- cheevos_var_t var;
- double multiplier;
- bool compare_next;
-} cheevos_term_t;
-
-typedef struct
-{
- cheevos_term_t *terms;
- unsigned count;
- unsigned compare_count;
-} cheevos_expr_t;
-
-typedef struct
-{
- unsigned id;
- unsigned format;
- const char *title;
- const char *description;
- int active;
- int last_value;
-
- cheevos_condition_t start;
- cheevos_condition_t cancel;
- cheevos_condition_t submit;
- cheevos_expr_t value;
-} cheevos_leaderboard_t;
+ rc_lboard_t* lboard;
+ const cheevos_ralboard_t* info;
+ bool active;
+ unsigned last_value;
+ int format;
+} cheevos_lboard_t;
typedef struct
{
retro_task_t* task;
#ifdef HAVE_THREADS
- slock_t* task_lock;
+ slock_t* task_lock;
#endif
- cheevos_console_t console_id;
+ int console_id;
bool core_supports;
- bool addrs_patched;
- int add_buffer;
- int add_hits;
+
+ cheevos_rapatchdata_t patchdata;
+ cheevos_cheevo_t* core;
+ cheevos_cheevo_t* unofficial;
+ cheevos_lboard_t* lboards;
- cheevoset_t core;
- cheevoset_t unofficial;
- cheevos_leaderboard_t *leaderboards;
- unsigned lboard_count;
+ cheevos_fixups_t fixups;
char token[32];
-
- retro_ctx_memory_info_t meminfo[4];
} cheevos_locals_t;
+typedef struct
+{
+ int label;
+ const char* name;
+ const uint32_t* ext_hashes;
+} cheevos_finder_t;
+
typedef struct
{
uint8_t id[4]; /* NES^Z */
@@ -257,33 +145,7 @@ typedef struct
uint8_t reserve[8];
} cheevos_nes_header_t;
-static cheevos_locals_t cheevos_locals =
-{
- /* task */ NULL,
-#ifdef HAVE_THREADS
- /* task_lock */ NULL,
-#endif
-
- /* console_id */ CHEEVOS_CONSOLE_NONE,
- /* core_supports */ true,
- /* addrs_patched */ false,
- /* add_buffer */ 0,
- /* add_hits */ 0,
-
- /* core */ {NULL, 0},
- /* unofficial */ {NULL, 0},
- /* leaderboards */ NULL,
- /* lboard_count */ 0,
-
- /* token */ {0},
-
- {
- /* meminfo[0] */ {NULL, 0, 0},
- /* meminfo[1] */ {NULL, 0, 0},
- /* meminfo[2] */ {NULL, 0, 0},
- /* meminfo[3] */ {NULL, 0, 0}
- }
-};
+static cheevos_locals_t cheevos_locals;
bool cheevos_loaded = false;
bool cheevos_hardcore_active = false;
@@ -299,6 +161,8 @@ int cheats_were_enabled = 0;
#define CHEEVOS_UNLOCK(l)
#endif
+#define CHEEVOS_MB(x) ((x) * 1024 * 1024)
+
/*****************************************************************************
Supporting functions.
*****************************************************************************/
@@ -312,14 +176,9 @@ void cheevos_log(const char *fmt, ...)
#endif
-static unsigned size_in_megabytes(unsigned val)
-{
- return (val * 1024 * 1024);
-}
-
-#ifdef CHEEVOS_LOG_URLS
static void cheevos_log_url(const char* format, const char* url)
{
+#ifdef CHEEVOS_LOG_URLS
#ifdef CHEEVOS_LOG_PASSWORD
CHEEVOS_LOG(format, url);
#else
@@ -345,7 +204,7 @@ static void cheevos_log_url(const char* format, const char* url)
do
{
*aux++ = *next++;
- }while (next[-1] != 0);
+ } while (next[-1] != 0);
}
else
*aux = 0;
@@ -366,7 +225,7 @@ static void cheevos_log_url(const char* format, const char* url)
do
{
*aux++ = *next++;
- }while (next[-1] != 0);
+ } while (next[-1] != 0);
}
else
*aux = 0;
@@ -374,881 +233,167 @@ static void cheevos_log_url(const char* format, const char* url)
CHEEVOS_LOG(format, copy);
#endif
-}
+#else
+ (void)format;
+ (void)url;
#endif
-
-static uint32_t cheevos_djb2(const char* str, size_t length)
-{
- const unsigned char *aux = (const unsigned char*)str;
- const unsigned char *end = aux + length;
- uint32_t hash = 5381;
-
- while (aux < end)
- hash = (hash << 5) + hash + *aux++;
-
- return hash;
}
-static int cheevos_getvalue__json_key(void *userdata,
- const char *name, size_t length)
+static const char* cheevos_rc_error(int ret)
{
- cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
-
- if (ud)
- ud->is_key = cheevos_djb2(name, length) == ud->key_hash;
- return 0;
-}
-
-static int cheevos_getvalue__json_string(void *userdata,
- const char *string, size_t length)
-{
- cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
-
- if (ud && ud->is_key)
+ switch (ret)
{
- ud->value = string;
- ud->length = length;
- ud->is_key = 0;
+ case RC_OK: return "Ok";
+ case RC_INVALID_LUA_OPERAND: return "Invalid Lua operand";
+ case RC_INVALID_MEMORY_OPERAND: return "Invalid memory operand";
+ case RC_INVALID_CONST_OPERAND: return "Invalid constant operand";
+ case RC_INVALID_FP_OPERAND: return "Invalid floating-point operand";
+ case RC_INVALID_CONDITION_TYPE: return "Invalid condition type";
+ case RC_INVALID_OPERATOR: return "Invalid operator";
+ case RC_INVALID_REQUIRED_HITS: return "Invalid required hits";
+ case RC_DUPLICATED_START: return "Duplicated start condition";
+ case RC_DUPLICATED_CANCEL: return "Duplicated cancel condition";
+ case RC_DUPLICATED_SUBMIT: return "Duplicated submit condition";
+ case RC_DUPLICATED_VALUE: return "Duplicated value expression";
+ case RC_DUPLICATED_PROGRESS: return "Duplicated progress expression";
+ case RC_MISSING_START: return "Missing start condition";
+ case RC_MISSING_CANCEL: return "Missing cancel condition";
+ case RC_MISSING_SUBMIT: return "Missing submit condition";
+ case RC_MISSING_VALUE: return "Missing value expression";
+ case RC_INVALID_LBOARD_FIELD: return "Invalid field in leaderboard";
+ default: return "Unknown error";
+ }
+}
+
+static int cheevos_parse(const char* json)
+{
+ int res = 0;
+ int i = 0;
+ unsigned j = 0;
+ unsigned count = 0;
+ cheevos_cheevo_t* cheevo = NULL;
+ cheevos_lboard_t* lboard = NULL;
+ cheevos_racheevo_t* rac = NULL;
+
+ cheevos_fixup_init(&cheevos_locals.fixups);
+
+ res = cheevos_get_patchdata(json, &cheevos_locals.patchdata);
+
+ if (res != 0)
+ {
+ RARCH_ERR(CHEEVOS_TAG "Error parsing cheevos");
+ return -1;
}
- return 0;
-}
-
-static int cheevos_getvalue__json_boolean(void *userdata, int istrue)
-{
- cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
-
- if (ud && ud->is_key)
+ if ( cheevos_locals.patchdata.core_count == 0
+ && !cheevos_locals.patchdata.unofficial_count == 0
+ && !cheevos_locals.patchdata.lboard_count == 0)
{
- if (istrue)
+ cheevos_locals.core = NULL;
+ cheevos_locals.unofficial = NULL;
+ cheevos_locals.lboards = NULL;
+ cheevos_free_patchdata(&cheevos_locals.patchdata);
+ return 0;
+ }
+
+ /* Allocate memory. */
+ cheevos_locals.core = (cheevos_cheevo_t*)
+ calloc(cheevos_locals.patchdata.core_count, sizeof(cheevos_cheevo_t));
+
+ cheevos_locals.unofficial = (cheevos_cheevo_t*)
+ calloc(cheevos_locals.patchdata.unofficial_count, sizeof(cheevos_cheevo_t));
+
+ cheevos_locals.lboards = (cheevos_lboard_t*)
+ calloc(cheevos_locals.patchdata.lboard_count, sizeof(cheevos_lboard_t));
+
+ if ( !cheevos_locals.core
+ || !cheevos_locals.unofficial
+ || !cheevos_locals.lboards)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Error allocating memory for cheevos");
+ goto error;
+ }
+
+ /* Initialize. */
+ for (i = 0; i < 2; i++)
+ {
+ if (i == 0)
{
- ud->value = "true";
- ud->length = 4;
+ cheevo = cheevos_locals.core;
+ rac = cheevos_locals.patchdata.core;
+ count = cheevos_locals.patchdata.core_count;
}
else
{
- ud->value = "false";
- ud->length = 5;
- }
- ud->is_key = 0;
- }
-
- return 0;
-}
-
-static int cheevos_getvalue__json_null(void *userdata)
-{
- cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
-
- if (ud && ud->is_key )
- {
- ud->value = "null";
- ud->length = 4;
- ud->is_key = 0;
- }
-
- return 0;
-}
-
-static int cheevos_get_value(const char *json, unsigned key_hash,
- char *value, size_t length)
-{
- static const jsonsax_handlers_t handlers =
- {
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- cheevos_getvalue__json_key,
- NULL,
- cheevos_getvalue__json_string,
- cheevos_getvalue__json_string, /* number */
- cheevos_getvalue__json_boolean,
- cheevos_getvalue__json_null
- };
-
- cheevos_getvalueud_t ud;
-
- ud.key_hash = key_hash;
- ud.is_key = 0;
- ud.value = NULL;
- ud.length = 0;
- *value = 0;
-
- if ((jsonsax_parse(json, &handlers, (void*)&ud) == JSONSAX_OK)
- && ud.value && ud.length < length)
- {
- if (!string_is_empty(ud.value))
- strlcpy(value, ud.value, ud.length + 1);
- return 0;
- }
-
- return -1;
-}
-
-/*****************************************************************************
-Count number of achievements in a JSON file.
-*****************************************************************************/
-
-static int cheevos_count__json_end_array(void *userdata)
-{
- cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
-
- if (ud)
- {
- ud->in_cheevos = 0;
- ud->in_lboards = 0;
- }
-
- return 0;
-}
-
-static int cheevos_count__json_key(void *userdata,
- const char *name, size_t length)
-{
- cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
-
- if (ud)
- {
- ud->field_hash = cheevos_djb2(name, length);
- if (ud->field_hash == CHEEVOS_JSON_KEY_ACHIEVEMENTS)
- ud->in_cheevos = 1;
- else if (ud->field_hash == CHEEVOS_JSON_KEY_LEADERBOARDS)
- ud->in_lboards = 1;
- }
-
- return 0;
-}
-
-static int cheevos_count__json_number(void *userdata,
- const char *number, size_t length)
-{
- cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
-
- if (ud)
- {
- if (ud->in_cheevos && ud->field_hash == CHEEVOS_JSON_KEY_FLAGS)
- {
- long flags = strtol(number, NULL, 10);
-
- if (flags == 3)
- ud->core_count++; /* Core achievements */
- else if (flags == 5)
- ud->unofficial_count++; /* Unofficial achievements */
- }
- else if (ud->in_lboards && ud->field_hash == CHEEVOS_JSON_KEY_ID)
- ud->lboard_count++;
- }
-
- return 0;
-}
-
-static int cheevos_count_cheevos(const char *json,
- unsigned *core_count, unsigned *unofficial_count,
- unsigned *lboard_count)
-{
- static const jsonsax_handlers_t handlers =
- {
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- cheevos_count__json_end_array,
- cheevos_count__json_key,
- NULL,
- NULL,
- cheevos_count__json_number,
- NULL,
- NULL
- };
-
- int res;
- cheevos_countud_t ud;
- ud.in_cheevos = 0;
- ud.in_lboards = 0;
- ud.core_count = 0;
- ud.unofficial_count = 0;
- ud.lboard_count = 0;
-
- res = jsonsax_parse(json, &handlers, (void*)&ud);
-
- *core_count = ud.core_count;
- *unofficial_count = ud.unofficial_count;
- *lboard_count = ud.lboard_count;
-
- return res;
-}
-
-/*****************************************************************************
-Parse the MemAddr field.
-*****************************************************************************/
-
-static unsigned cheevos_count_cond_sets(const char *memaddr)
-{
- cheevos_cond_t cond;
- unsigned count = 0;
-
- for (;;)
- {
- count++;
-
- for (;;)
- {
- cheevos_cond_parse(&cond, &memaddr);
-
- if (*memaddr != '_')
- break;
-
- memaddr++;
+ cheevo = cheevos_locals.unofficial;
+ rac = cheevos_locals.patchdata.unofficial;
+ count = cheevos_locals.patchdata.unofficial_count;
}
- if (*memaddr != 'S')
- break;
-
- memaddr++;
- }
-
- return count;
-}
-
-static int cheevos_parse_condition(
- cheevos_condition_t *condition,
- const char* memaddr)
-{
- if (!condition)
- return 0;
-
- condition->count = cheevos_count_cond_sets(memaddr);
-
- if (condition->count)
- {
- unsigned set = 0;
- const cheevos_condset_t* end = NULL;
- cheevos_condset_t *conds = NULL;
- cheevos_condset_t *condset = NULL;
- cheevos_condset_t *condsets = (cheevos_condset_t*)
- calloc(condition->count, sizeof(cheevos_condset_t));
-
- (void)conds;
-
- if (!condsets)
- return -1;
-
- condition->condsets = condsets;
- end = condition->condsets + condition->count;
-
- for (condset = condition->condsets; condset < end; condset++, set++)
+ for (j = 0; j < count; j++, cheevo++, rac++)
{
- condset->count =
- cheevos_cond_count_in_set(memaddr, set);
- condset->conds = NULL;
+ cheevo->info = rac;
+ res = rc_trigger_size(cheevo->info->memaddr);
- CHEEVOS_LOG("[CHEEVOS]: set %p (index=%u)\n", condset, set);
- CHEEVOS_LOG("[CHEEVOS]: conds: %u\n", condset->count);
-
- if (condset->count)
+ if (res < 0)
{
- cheevos_cond_t *conds = (cheevos_cond_t*)
- calloc(condset->count, sizeof(cheevos_cond_t));
-
- if (!conds)
- {
- while (--condset >= condition->condsets)
- {
- if ((void*)condset->conds)
- free((void*)condset->conds);
- }
-
- return -1;
- }
-
- condset->conds = conds;
- cheevos_cond_parse_in_set(condset->conds, memaddr, set);
- }
- }
- }
-
- return 0;
-}
-
-static void cheevos_free_condition(cheevos_condition_t* condition)
-{
- unsigned i;
-
- if (!condition)
- return;
-
- if (condition->condsets)
- {
- for (i = 0; i < condition->count; i++)
- {
- if (condition->condsets[i].conds)
- {
- free(condition->condsets[i].conds);
- condition->condsets[i].conds = NULL;
- }
- }
-
- if (condition->condsets)
- {
- free(condition->condsets);
- condition->condsets = NULL;
- }
- }
-}
-
-/*****************************************************************************
-Parse the Mem field of leaderboards.
-*****************************************************************************/
-
-static int cheevos_parse_expression(cheevos_expr_t *expr, const char* mem)
-{
- unsigned i;
- const char *aux;
- cheevos_term_t *terms = NULL;
- char *end = NULL;
-
- if (!expr)
- return -1;
-
- expr->count = 1;
- expr->compare_count = 1;
-
- for (aux = mem;; aux++)
- {
- if (*aux == '"' || *aux == ':')
- break;
- expr->count += *aux == '_';
- }
-
- if (expr->count > 0)
- terms = (cheevos_term_t*)
- calloc(expr->count, sizeof(cheevos_term_t));
-
- if (!terms)
- return -1;
-
- expr->terms = terms;
-
- for (i = 0; i < expr->count; i++)
- {
- expr->terms[i].compare_next = false;
- expr->terms[i].multiplier = 1;
- }
-
- for (i = 0, aux = mem; i < expr->count;)
- {
- cheevos_var_parse(&expr->terms[i].var, &aux);
-
- if (*aux != '*')
- {
- /* expression has no multiplier */
- if (*aux == '_')
- {
- aux++;
- i++;
- }
- else if (*aux == '$')
- {
- expr->terms[i].compare_next = true;
- expr->compare_count++;
- aux++;
- i++;
- }
-
- /* no multiplier at end of string */
- else if (*aux == '\0' || *aux == '"' || *aux == ',')
- return 0;
-
- /* invalid character in expression */
- else
- {
- if (expr->terms)
- {
- free(expr->terms);
- expr->terms = NULL;
- }
- return -1;
- }
- }
- else
- {
- if (aux[1] == 'h' || aux[1] == 'H')
- expr->terms[i].multiplier = (double)strtol(aux + 2, &end, 16);
- else
- expr->terms[i].multiplier = strtod(aux + 1, &end);
- aux = end;
-
- if (*aux == '$')
- {
- aux++;
- expr->terms[i].compare_next = true;
- expr->compare_count++;
- }
- else
- expr->terms[i].compare_next = false;
-
- aux++;
- i++;
- }
- }
- return 0;
-}
-
-static int cheevos_parse_mem(cheevos_leaderboard_t *lb, const char* mem)
-{
- lb->start.condsets = NULL;
- lb->cancel.condsets = NULL;
- lb->submit.condsets = NULL;
- lb->value.terms = NULL;
-
- for (;;)
- {
- if (*mem == 'S' && mem[1] == 'T' && mem[2] == 'A' && mem[3] == ':')
- {
- if (cheevos_parse_condition(&lb->start, mem + 4))
+ CHEEVOS_ERR(CHEEVOS_TAG "Error in cheevo memaddr %s: %s",
+ cheevo->info->memaddr, cheevos_rc_error(res));
goto error;
- }
- else if (*mem == 'C' && mem[1] == 'A' && mem[2] == 'N' && mem[3] == ':')
- {
- if (cheevos_parse_condition(&lb->cancel, mem + 4))
- goto error;
- }
- else if (*mem == 'S' && mem[1] == 'U' && mem[2] == 'B' && mem[3] == ':')
- {
- if (cheevos_parse_condition(&lb->submit, mem + 4))
- goto error;
- }
- else if (*mem == 'V' && mem[1] == 'A' && mem[2] == 'L' && mem[3] == ':')
- {
- if (cheevos_parse_expression(&lb->value, mem + 4))
- goto error;
- }
-
- for (mem += 4;; mem++)
- {
- if (*mem == ':' && mem[1] == ':')
- {
- mem += 2;
- break;
}
- else if (*mem == '"')
- return 0;
+
+ cheevo->trigger = (rc_trigger_t*)calloc(1, res);
+
+ if (!cheevo->trigger)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Error allocating memory for cheevos");
+ goto error;
+ }
+
+ rc_parse_trigger(cheevo->trigger, cheevo->info->memaddr, NULL, 0);
+ cheevo->active = CHEEVOS_ACTIVE_SOFTCORE | CHEEVOS_ACTIVE_HARDCORE;
+ cheevo->last = 1;
}
}
-error:
- cheevos_free_condition(&lb->start);
- cheevos_free_condition(&lb->cancel);
- cheevos_free_condition(&lb->submit);
- if (lb->value.terms)
+ lboard = cheevos_locals.lboards;
+ count = cheevos_locals.patchdata.lboard_count;
+
+ for (j = 0; j < count; j++, lboard++)
{
- free((void*)lb->value.terms);
- lb->value.terms = NULL;
+ lboard->info = cheevos_locals.patchdata.lboards + j;
+ res = rc_lboard_size(lboard->info->mem);
+
+ if (res < 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Error in leaderboard mem %s: %s",
+ lboard->info->mem, cheevos_rc_error(res));
+ goto error;
+ }
+
+ lboard->lboard = (rc_lboard_t*)calloc(1, res);
+
+ if (!lboard->lboard)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Error allocating memory for cheevos");
+ goto error;
+ }
+
+ rc_parse_lboard(lboard->lboard,
+ lboard->info->mem, NULL, 0);
+ lboard->active = false;
+ lboard->last_value = 0;
+ lboard->format = rc_parse_format(lboard->info->format);
}
- return -1;
-}
-
-/*****************************************************************************
-Load achievements from a JSON string.
-*****************************************************************************/
-
-static INLINE const char *cheevos_dupstr(const cheevos_field_t *field)
-{
- char *string = (char*)malloc(field->length + 1);
-
- if (!string)
- return NULL;
-
- memcpy ((void*)string, (void*)field->string, field->length);
- string[field->length] = 0;
-
- return string;
-}
-
-static int cheevos_new_cheevo(cheevos_readud_t *ud)
-{
- cheevo_t *cheevo = NULL;
- long flags = strtol(ud->flags.string, NULL, 10);
-
- if (flags == 3)
- cheevo = cheevos_locals.core.cheevos + ud->core_count++;
- else if (flags == 5)
- cheevo = cheevos_locals.unofficial.cheevos + ud->unofficial_count++;
- else
- return 0;
-
- cheevo->id = (unsigned)strtol(ud->id.string, NULL, 10);
- cheevo->title = cheevos_dupstr(&ud->title);
- cheevo->description = cheevos_dupstr(&ud->desc);
- cheevo->author = cheevos_dupstr(&ud->author);
- cheevo->badge = cheevos_dupstr(&ud->badge);
- cheevo->points = (unsigned)strtol(ud->points.string, NULL, 10);
- cheevo->dirty = 0;
- cheevo->active = CHEEVOS_ACTIVE_SOFTCORE | CHEEVOS_ACTIVE_HARDCORE;
- cheevo->last = 1;
- cheevo->modified = 0;
-
- if ( !cheevo->title ||
- !cheevo->description ||
- !cheevo->author ||
- !cheevo->badge)
- goto error;
-
- if (cheevos_parse_condition(&cheevo->condition, ud->memaddr.string))
- goto error;
return 0;
error:
- if (cheevo->title)
- {
- free((void*)cheevo->title);
- cheevo->title = NULL;
- }
- if (cheevo->description)
- {
- free((void*)cheevo->description);
- cheevo->description = NULL;
- }
- if (cheevo->author)
- {
- free((void*)cheevo->author);
- cheevo->author = NULL;
- }
- if (cheevo->badge)
- {
- free((void*)cheevo->badge);
- cheevo->badge = NULL;
- }
- return -1;
-}
-
-/*****************************************************************************
-Helper functions for displaying leaderboard values.
-*****************************************************************************/
-
-static void cheevos_format_value(const unsigned value, const unsigned type,
- char* formatted_value, size_t formatted_size)
-{
- unsigned mins, secs, millis;
-
- switch(type)
- {
- case CHEEVOS_FORMAT_VALUE:
- snprintf(formatted_value, formatted_size, "%u", value);
- break;
-
- case CHEEVOS_FORMAT_SCORE:
- snprintf(formatted_value, formatted_size,
- "%06upts", value);
- break;
-
- case CHEEVOS_FORMAT_FRAMES:
- mins = value / 3600;
- secs = (value % 3600) / 60;
- millis = (int) (value % 60) * (10.00 / 6.00);
- snprintf(formatted_value, formatted_size,
- "%02u:%02u.%02u", mins, secs, millis);
- break;
-
- case CHEEVOS_FORMAT_MILLIS:
- mins = value / 6000;
- secs = (value % 6000) / 100;
- millis = (int) (value % 100);
- snprintf(formatted_value, formatted_size,
- "%02u:%02u.%02u", mins, secs, millis);
- break;
-
- case CHEEVOS_FORMAT_SECS:
- mins = value / 60;
- secs = value % 60;
- snprintf(formatted_value, formatted_size,
- "%02u:%02u", mins, secs);
- break;
-
- default:
- snprintf(formatted_value, formatted_size,
- "%u (?)", value);
- }
-}
-
-unsigned cheevos_parse_format(cheevos_field_t* format)
-{
- /* Most likely */
- if (strncmp(format->string, "VALUE", format->length) == 0)
- return CHEEVOS_FORMAT_VALUE;
- else if (strncmp(format->string, "TIME", format->length) == 0)
- return CHEEVOS_FORMAT_FRAMES;
- else if (strncmp(format->string, "SCORE", format->length) == 0)
- return CHEEVOS_FORMAT_SCORE;
-
- /* Less likely */
- else if (strncmp(format->string, "MILLISECS", format->length) == 0)
- return CHEEVOS_FORMAT_MILLIS;
- else if (strncmp(format->string, "TIMESECS", format->length) == 0)
- return CHEEVOS_FORMAT_SECS;
-
- /* Rare (RPS only) */
- else if (strncmp(format->string, "POINTS", format->length) == 0)
- return CHEEVOS_FORMAT_SCORE;
- else if (strncmp(format->string, "FRAMES", format->length) == 0)
- return CHEEVOS_FORMAT_FRAMES;
- else
- return CHEEVOS_FORMAT_OTHER;
-}
-
-static int cheevos_new_lboard(cheevos_readud_t *ud)
-{
- cheevos_leaderboard_t *lboard = NULL;
- cheevos_leaderboard_t *ldb = cheevos_locals.leaderboards;
-
- if (!ldb || !ud)
- return -1;
-
- lboard = ldb + ud->lboard_count++;
-
- lboard->id = (unsigned)strtol(ud->id.string, NULL, 10);
- lboard->format = cheevos_parse_format(&ud->format);
- lboard->title = cheevos_dupstr(&ud->title);
- lboard->description = cheevos_dupstr(&ud->desc);
-
- if (!lboard->title || !lboard->description)
- goto error;
-
- if (cheevos_parse_mem(lboard, ud->memaddr.string))
- goto error;
-
- return 0;
-
-error:
- if ((void*)lboard->title)
- free((void*)lboard->title);
- if ((void*)lboard->description)
- free((void*)lboard->description);
- return -1;
-}
-
-static int cheevos_read__json_key( void *userdata,
- const char *name, size_t length)
-{
- cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
-
- if (ud)
- {
- int common = ud->in_cheevos || ud->in_lboards;
- uint32_t hash = cheevos_djb2(name, length);
- ud->field = NULL;
-
- switch (hash)
- {
- case CHEEVOS_JSON_KEY_ACHIEVEMENTS:
- ud->in_cheevos = 1;
- break;
- case CHEEVOS_JSON_KEY_LEADERBOARDS:
- ud->in_lboards = 1;
- break;
- case CHEEVOS_JSON_KEY_CONSOLE_ID:
- ud->is_console_id = 1;
- break;
- case CHEEVOS_JSON_KEY_ID:
- if (common)
- ud->field = &ud->id;
- break;
- case CHEEVOS_JSON_KEY_MEMADDR:
- if (ud->in_cheevos)
- ud->field = &ud->memaddr;
- break;
- case CHEEVOS_JSON_KEY_MEM:
- if (ud->in_lboards)
- ud->field = &ud->memaddr;
- break;
- case CHEEVOS_JSON_KEY_TITLE:
- if (common)
- ud->field = &ud->title;
- break;
- case CHEEVOS_JSON_KEY_DESCRIPTION:
- if (common)
- ud->field = &ud->desc;
- break;
- case CHEEVOS_JSON_KEY_POINTS:
- if (ud->in_cheevos)
- ud->field = &ud->points;
- break;
- case CHEEVOS_JSON_KEY_AUTHOR:
- if (ud->in_cheevos)
- ud->field = &ud->author;
- break;
- case CHEEVOS_JSON_KEY_MODIFIED:
- if (ud->in_cheevos)
- ud->field = &ud->modified;
- break;
- case CHEEVOS_JSON_KEY_CREATED:
- if (ud->in_cheevos)
- ud->field = &ud->created;
- break;
- case CHEEVOS_JSON_KEY_BADGENAME:
- if (ud->in_cheevos)
- ud->field = &ud->badge;
- break;
- case CHEEVOS_JSON_KEY_FLAGS:
- if (ud->in_cheevos)
- ud->field = &ud->flags;
- break;
- case CHEEVOS_JSON_KEY_FORMAT:
- if (ud->in_lboards)
- ud->field = &ud->format;
- break;
- default:
- break;
- }
- }
-
- return 0;
-}
-
-static int cheevos_read__json_string(void *userdata,
- const char *string, size_t length)
-{
- cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
-
- if (ud && ud->field)
- {
- ud->field->string = string;
- ud->field->length = length;
- }
-
- return 0;
-}
-
-static int cheevos_read__json_number(void *userdata,
- const char *number, size_t length)
-{
- cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
-
- if (ud)
- {
- if (ud->field)
- {
- ud->field->string = number;
- ud->field->length = length;
- }
- else if (ud->is_console_id)
- {
- cheevos_locals.console_id = (cheevos_console_t)
- strtol(number, NULL, 10);
- ud->is_console_id = 0;
- }
- }
-
- return 0;
-}
-
-static int cheevos_read__json_end_object(void *userdata)
-{
- cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
-
- if (ud)
- {
- if (ud->in_cheevos)
- return cheevos_new_cheevo(ud);
- if (ud->in_lboards)
- return cheevos_new_lboard(ud);
- }
-
- return 0;
-}
-
-static int cheevos_read__json_end_array(void *userdata)
-{
- cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
-
- if (ud)
- {
- ud->in_cheevos = 0;
- ud->in_lboards = 0;
- }
-
- return 0;
-}
-
-static int cheevos_parse(const char *json)
-{
- static const jsonsax_handlers_t handlers =
- {
- NULL,
- NULL,
- NULL,
- cheevos_read__json_end_object,
- NULL,
- cheevos_read__json_end_array,
- cheevos_read__json_key,
- NULL,
- cheevos_read__json_string,
- cheevos_read__json_number,
- NULL,
- NULL
- };
-
- cheevos_readud_t ud;
- unsigned core_count, unofficial_count, lboard_count;
- /* Count the number of achievements in the JSON file. */
- int res = cheevos_count_cheevos(json, &core_count, &unofficial_count,
- &lboard_count);
-
- if (res != JSONSAX_OK)
- return -1;
-
- /* Allocate the achievements. */
-
- cheevos_locals.core.cheevos = (cheevo_t*)
- calloc(core_count, sizeof(cheevo_t));
- cheevos_locals.core.count = core_count;
-
- cheevos_locals.unofficial.cheevos = (cheevo_t*)
- calloc(unofficial_count, sizeof(cheevo_t));
- cheevos_locals.unofficial.count = unofficial_count;
-
- cheevos_locals.leaderboards = (cheevos_leaderboard_t*)
- calloc(lboard_count, sizeof(cheevos_leaderboard_t));
- cheevos_locals.lboard_count = lboard_count;
-
- if ( !cheevos_locals.core.cheevos ||
- !cheevos_locals.unofficial.cheevos ||
- !cheevos_locals.leaderboards)
- {
- if ((void*)cheevos_locals.core.cheevos)
- free((void*)cheevos_locals.core.cheevos);
- if ((void*)cheevos_locals.unofficial.cheevos)
- free((void*)cheevos_locals.unofficial.cheevos);
- if ((void*)cheevos_locals.leaderboards)
- free((void*)cheevos_locals.leaderboards);
- cheevos_locals.core.count = cheevos_locals.unofficial.count =
- cheevos_locals.lboard_count = 0;
-
- return -1;
- }
-
- /* Load the achievements. */
- ud.in_cheevos = 0;
- ud.in_lboards = 0;
- ud.is_console_id = 0;
- ud.field = NULL;
- ud.core_count = 0;
- ud.unofficial_count = 0;
- ud.lboard_count = 0;
-
- if (jsonsax_parse(json, &handlers, (void*)&ud) != JSONSAX_OK)
- goto error;
-
- return 0;
-
-error:
- cheevos_unload();
-
+ CHEEVOS_FREE(cheevos_locals.core);
+ CHEEVOS_FREE(cheevos_locals.unofficial);
+ CHEEVOS_FREE(cheevos_locals.lboards);
+ cheevos_free_patchdata(&cheevos_locals.patchdata);
+ cheevos_fixup_destroy(&cheevos_locals.fixups);
return -1;
}
@@ -1256,640 +401,261 @@ error:
Test all the achievements (call once per frame).
*****************************************************************************/
-static int cheevos_test_condition(cheevos_cond_t *cond)
-{
- unsigned sval = 0;
- unsigned tval = 0;
-
- if (!cond)
- return 0;
-
- sval = cheevos_var_get_value(&cond->source) +
- cheevos_locals.add_buffer;
- tval = cheevos_var_get_value(&cond->target);
-
- switch (cond->op)
- {
- case CHEEVOS_COND_OP_EQUALS:
- return (sval == tval);
- case CHEEVOS_COND_OP_LESS_THAN:
- return (sval < tval);
- case CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL:
- return (sval <= tval);
- case CHEEVOS_COND_OP_GREATER_THAN:
- return (sval > tval);
- case CHEEVOS_COND_OP_GREATER_THAN_OR_EQUAL:
- return (sval >= tval);
- case CHEEVOS_COND_OP_NOT_EQUAL_TO:
- return (sval != tval);
- default:
- break;
- }
-
- return 1;
-}
-
-static int cheevos_test_pause_cond_set(const cheevos_condset_t *condset,
- int *dirty_conds, int *reset_conds, int process_pause)
-{
- int cond_valid = 0;
- int set_valid = 1; /* must start true so AND logic works */
- cheevos_cond_t *cond = NULL;
- const cheevos_cond_t *end = condset->conds + condset->count;
-
- cheevos_locals.add_buffer = 0;
- cheevos_locals.add_hits = 0;
-
- for (cond = condset->conds; cond < end; cond++)
- {
- if (cond->pause != process_pause)
- continue;
-
- if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE)
- {
- cheevos_locals.add_buffer += cheevos_var_get_value(&cond->source);
- continue;
- }
-
- if (cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE)
- {
- cheevos_locals.add_buffer -= cheevos_var_get_value(&cond->source);
- continue;
- }
-
- if (cond->type == CHEEVOS_COND_TYPE_ADD_HITS)
- {
- if (cheevos_test_condition(cond))
- {
- cond->curr_hits++;
- *dirty_conds = 1;
- }
-
- cheevos_locals.add_hits += cond->curr_hits;
- continue;
- }
-
- /* always evaluate the condition to ensure delta values get tracked correctly */
- cond_valid = cheevos_test_condition(cond);
-
- /* if the condition has a target hit count that has already been met,
- * it's automatically true, even if not currently true. */
- if ( (cond->req_hits != 0) &&
- (cond->curr_hits + cheevos_locals.add_hits) >= cond->req_hits)
- {
- cond_valid = 1;
- }
- else if (cond_valid)
- {
- cond->curr_hits++;
- *dirty_conds = 1;
-
- /* Process this logic, if this condition is true: */
- if (cond->req_hits == 0)
- ; /* Not a hit-based requirement: ignore any additional logic! */
- else if ((cond->curr_hits + cheevos_locals.add_hits) < cond->req_hits)
- cond_valid = 0; /* HitCount target has not yet been met, condition is not yet valid. */
- }
-
- cheevos_locals.add_buffer = 0;
- cheevos_locals.add_hits = 0;
-
- if (cond->type == CHEEVOS_COND_TYPE_PAUSE_IF)
- {
- /* as soon as we find a PauseIf that evaluates to true,
- * stop processing the rest of the group. */
- if (cond_valid)
- return 1;
-
- /* if we make it to the end of the function, make sure we are
- * indicating nothing matched. if we do find a later PauseIf match,
- * it'll automatically return true via the previous condition. */
- set_valid = 0;
-
- if (cond->req_hits == 0)
- {
- /* PauseIf didn't evaluate true, and doesn't have a HitCount,
- * reset the HitCount to indicate the condition didn't match. */
- if (cond->curr_hits != 0)
- {
- cond->curr_hits = 0;
- *dirty_conds = 1;
- }
- }
- else
- {
- /* PauseIf has a HitCount that hasn't been met, ignore it for now. */
- }
- }
- else if (cond->type == CHEEVOS_COND_TYPE_RESET_IF)
- {
- if (cond_valid)
- {
- *reset_conds = 1; /* Resets all hits found so far */
- set_valid = 0; /* Cannot be valid if we've hit a reset condition. */
- }
- }
- else /* Sequential or non-sequential? */
- set_valid &= cond_valid;
- }
-
- return set_valid;
-}
-
-static int cheevos_test_cond_set(const cheevos_condset_t *condset,
- int *dirty_conds, int *reset_conds)
-{
- int in_pause = 0;
- int has_pause = 0;
- cheevos_cond_t *cond = NULL;
-
- if (!condset)
- return 1; /* important: empty group must evaluate true */
-
- /* the ints below are used for Pause conditions and their dependent AddSource/AddHits. */
-
- /* this loop needs to go backwards to check AddSource/AddHits */
- cond = condset->conds + condset->count - 1;
- for (; cond >= condset->conds; cond--)
- {
- if (cond->type == CHEEVOS_COND_TYPE_PAUSE_IF)
- {
- has_pause = 1;
- in_pause = 1;
- cond->pause = 1;
- }
- else if (cond->type == CHEEVOS_COND_TYPE_ADD_SOURCE ||
- cond->type == CHEEVOS_COND_TYPE_SUB_SOURCE ||
- cond->type == CHEEVOS_COND_TYPE_ADD_HITS)
- {
- cond->pause = in_pause;
- }
- else
- {
- in_pause = 0;
- cond->pause = 0;
- }
- }
-
- if (has_pause)
- { /* one or more Pause conditions exists, if any of them are true,
- * stop processing this group. */
- if (cheevos_test_pause_cond_set(condset, dirty_conds, reset_conds, 1))
- return 0;
- }
-
- /* process the non-Pause conditions to see if the group is true */
- return cheevos_test_pause_cond_set(condset, dirty_conds, reset_conds, 0);
-}
-
-static int cheevos_reset_cond_set(cheevos_condset_t *condset, int deltas)
-{
- int dirty = 0;
- const cheevos_cond_t *end = NULL;
-
- if (!condset)
- return 0;
-
- end = condset->conds + condset->count;
-
- if (deltas)
- {
- cheevos_cond_t *cond = NULL;
- for (cond = condset->conds; cond < end; cond++)
- {
- dirty |= cond->curr_hits != 0;
-
- cond->curr_hits = 0;
-
- cond->source.previous = cond->source.value;
- cond->target.previous = cond->target.value;
- }
- }
- else
- {
- cheevos_cond_t *cond = NULL;
- for (cond = condset->conds; cond < end; cond++)
- {
- dirty |= cond->curr_hits != 0;
- cond->curr_hits = 0;
- }
- }
-
- return dirty;
-}
-
-static int cheevos_test_cheevo(cheevo_t *cheevo)
-{
- int dirty_conds = 0;
- int reset_conds = 0;
- int ret_val = 0;
- int ret_val_sub_cond = 0;
- cheevos_condset_t *condset = NULL;
- cheevos_condset_t *end = NULL;
-
- if (!cheevo)
- return 0;
-
- ret_val_sub_cond = cheevo->condition.count == 1;
- condset = cheevo->condition.condsets;
-
- if (!condset)
- return 0;
-
- end = condset + cheevo->condition.count;
-
- if (condset < end)
- {
- ret_val = cheevos_test_cond_set(condset, &dirty_conds, &reset_conds);
- condset++;
- }
-
- while (condset < end)
- {
- ret_val_sub_cond |= cheevos_test_cond_set(
- condset, &dirty_conds, &reset_conds);
- condset++;
- }
-
- if (dirty_conds)
- cheevo->dirty |= CHEEVOS_DIRTY_CONDITIONS;
-
- if (reset_conds)
- {
- int dirty = 0;
-
- for (condset = cheevo->condition.condsets; condset < end; condset++)
- dirty |= cheevos_reset_cond_set(condset, 0);
-
- if (dirty)
- cheevo->dirty |= CHEEVOS_DIRTY_CONDITIONS;
- }
-
- return (ret_val && ret_val_sub_cond);
-}
-
-static void cheevos_url_encode(const char *str, char *encoded, size_t len)
-{
- if (!str)
- return;
-
- while (*str)
- {
- if ( isalnum((unsigned char)*str) || *str == '-'
- || *str == '_' || *str == '.'
- || *str == '~')
- {
- if (len >= 2)
- {
- *encoded++ = *str++;
- len--;
- }
- else
- break;
- }
- else
- {
- if (len >= 4)
- {
- snprintf(encoded, len, "%%%02x", (uint8_t)*str);
- encoded += 3;
- str++;
- len -= 3;
- }
- else
- break;
- }
- }
-
- *encoded = 0;
-}
-
-static void cheevos_make_unlock_url(const cheevo_t *cheevo,
- char* url, size_t url_size)
+static void cheevos_award_task_softcore(void* task_data, void* user_data,
+ const char* error)
{
settings_t *settings = config_get_ptr();
+ const cheevos_cheevo_t* cheevo = (const cheevos_cheevo_t*)user_data;
+ char buffer[256];
+ int ret;
+ buffer[0] = 0;
- if (!settings)
+ if (error == NULL)
+ {
+ CHEEVOS_LOG(CHEEVOS_TAG "Awarded achievement %u\n", cheevo->info->id);
return;
+ }
- snprintf(
- url, url_size,
- "http://retroachievements.org/dorequest.php?r=awardachievement&u=%s&t=%s&a=%u&h=%d",
- settings->arrays.cheevos_username,
- cheevos_locals.token,
- cheevo->id,
- settings->bools.cheevos_hardcore_mode_enable && !cheevos_hardcore_paused ? 1 : 0
- );
+ if (*error)
+ CHEEVOS_ERR(CHEEVOS_TAG "Error awarding achievement %u: %s\n", cheevo->info->id, error);
- url[url_size - 1] = 0;
+ /* Try again. */
+ ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, cheevos_locals.token, cheevo->info->id, 0);
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to award the cheevo: %s\n", url);
-#endif
+ if (ret != 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Buffer to small to create URL");
+ return;
+ }
+
+ cheevos_log_url(CHEEVOS_TAG "rc_url_award_cheevo: %s\n", buffer);
+ task_push_http_transfer(buffer, true, NULL, cheevos_award_task_softcore, user_data);
}
-static void cheevos_unlocked(void *task_data, void *user_data,
- const char *error)
-{
- cheevo_t *cheevo = (cheevo_t *)user_data;
-
- if (!error)
- {
- CHEEVOS_LOG("[CHEEVOS]: awarded achievement %u.\n", cheevo->id);
- }
- else
- {
- char url[256];
- url[0] = '\0';
-
- CHEEVOS_ERR("[CHEEVOS]: error awarding achievement %u, retrying...\n", cheevo->id);
-
- cheevos_make_unlock_url(cheevo, url, sizeof(url));
- task_push_http_transfer(url, true, NULL, cheevos_unlocked, cheevo);
- }
-}
-
-static void cheevos_test_cheevo_set(const cheevoset_t *set)
+static void cheevos_award_task_hardcore(void* task_data, void* user_data,
+ const char* error)
{
settings_t *settings = config_get_ptr();
- int mode = CHEEVOS_ACTIVE_SOFTCORE;
- cheevo_t *cheevo = NULL;
- const cheevo_t *end = NULL;
+ const cheevos_cheevo_t* cheevo = (const cheevos_cheevo_t*)user_data;
+ char buffer[256];
+ int ret;
+ buffer[0] = 0;
- if (!set)
+ if (error == NULL)
+ {
+ CHEEVOS_LOG(CHEEVOS_TAG "Awarded achievement %u\n", cheevo->info->id);
return;
+ }
- end = set->cheevos + set->count;
+ if (*error)
+ CHEEVOS_ERR(CHEEVOS_TAG "Error awarding achievement %u: %s\n", cheevo->info->id, error);
+
+ /* Try again. */
+ ret = rc_url_award_cheevo(buffer, sizeof(buffer), settings->arrays.cheevos_username, cheevos_locals.token, cheevo->info->id, 1);
+
+ if (ret != 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Buffer to small to create URL\n");
+ return;
+ }
+
+ cheevos_log_url(CHEEVOS_TAG "rc_url_award_cheevo: %s\n", buffer);
+ task_push_http_transfer(buffer, true, NULL, cheevos_award_task_hardcore, user_data);
+}
+
+static void cheevos_award(cheevos_cheevo_t* cheevo, int mode)
+{
+ settings_t *settings = config_get_ptr();
+ int ret;
+ char buffer[256];
+ buffer[0] = 0;
+
+ CHEEVOS_LOG(CHEEVOS_TAG "awarding cheevo %u: %s (%s)\n",
+ cheevo->info->id, cheevo->info->title, cheevo->info->description);
+
+ /* Deactivates the cheevo. */
+ cheevo->active &= ~mode;
+
+ if (mode == CHEEVOS_ACTIVE_HARDCORE)
+ cheevo->active &= ~CHEEVOS_ACTIVE_SOFTCORE;
+
+ /* Show the OSD message. */
+ snprintf(buffer, sizeof(buffer), "Achievement Unlocked: %s", cheevo->info->title);
+ runloop_msg_queue_push(buffer, 0, 2 * 60, false);
+ runloop_msg_queue_push(cheevo->info->description, 0, 3 * 60, false);
+
+ /* Start the award task. */
+ if ((mode & CHEEVOS_ACTIVE_HARDCORE) != 0)
+ cheevos_award_task_hardcore(NULL, cheevo, "");
+ else
+ cheevos_award_task_softcore(NULL, cheevo, "");
+
+ /* Take a screenshot of the achievement. */
+ if (settings && settings->bools.cheevos_auto_screenshot)
+ {
+ snprintf(buffer, sizeof(buffer), "%s/%s-cheevo-%u",
+ settings->paths.directory_screenshot,
+ path_basename(path_get(RARCH_PATH_BASENAME)),
+ cheevo->info->id);
+
+ if (take_screenshot(buffer, true,
+ video_driver_cached_frame_has_valid_framebuffer()))
+ CHEEVOS_LOG(CHEEVOS_TAG "Took a screenshot for cheevo %u\n", cheevo->info->id);
+ else
+ CHEEVOS_LOG(CHEEVOS_TAG "Failed to take screenshot for cheevo %u\n", cheevo->info->id);
+ }
+}
+
+static unsigned cheevos_peek(unsigned address, unsigned num_bytes, void* ud)
+{
+ const uint8_t* data = cheevos_fixup_find(&cheevos_locals.fixups, address, cheevos_locals.console_id);
+ unsigned value = 0;
+
+ switch (num_bytes)
+ {
+ case 4: value |= data[2] << 16 | data[3] << 24;
+ case 2: value |= data[1] << 8;
+ case 1: value |= data[0];
+ }
+
+ return value;
+}
+
+static void cheevos_test_cheevo_set(bool official)
+{
+ settings_t *settings = config_get_ptr();
+ int mode = CHEEVOS_ACTIVE_SOFTCORE;
+ cheevos_cheevo_t* cheevo;
+ int i, count, valid;
if (settings && settings->bools.cheevos_hardcore_mode_enable && !cheevos_hardcore_paused)
mode = CHEEVOS_ACTIVE_HARDCORE;
- for (cheevo = set->cheevos; cheevo < end; cheevo++)
+ if (official)
{
+ cheevo = cheevos_locals.core;
+ count = cheevos_locals.patchdata.core_count;
+ }
+ else
+ {
+ cheevo = cheevos_locals.unofficial;
+ count = cheevos_locals.patchdata.unofficial_count;
+ }
+
+ for (i = 0; i < count; i++, cheevo++)
+ {
+ /* Check if the achievement is active for the current mode. */
+ if ((cheevo->active & mode) == 0)
+ continue;
+
if (cheevo->active & mode)
{
- int valid = cheevos_test_cheevo(cheevo);
+ int valid = rc_test_trigger(cheevo->trigger, cheevos_peek, NULL, NULL);
if (cheevo->last)
- {
- cheevos_condset_t* condset = cheevo->condition.condsets;
- const cheevos_condset_t* end = cheevo->condition.condsets
- + cheevo->condition.count;
-
- for (; condset < end; condset++)
- cheevos_reset_cond_set(condset, 0);
- }
+ rc_reset_trigger(cheevo->trigger);
else if (valid)
- {
- char msg[256];
- char url[256];
- msg[0] = url[0] = '\0';
-
- cheevo->active &= ~mode;
-
- if (mode == CHEEVOS_ACTIVE_HARDCORE)
- cheevo->active &= ~CHEEVOS_ACTIVE_SOFTCORE;
-
- CHEEVOS_LOG("[CHEEVOS]: awarding cheevo %u: %s (%s).\n",
- cheevo->id, cheevo->title, cheevo->description);
-
- snprintf(msg, sizeof(msg), "Achievement Unlocked: %s",
- cheevo->title);
- msg[sizeof(msg) - 1] = 0;
- runloop_msg_queue_push(msg, 0, 2 * 60, false);
- runloop_msg_queue_push(cheevo->description, 0, 3 * 60, false);
-
- cheevos_make_unlock_url(cheevo, url, sizeof(url));
- task_push_http_transfer(url, true, NULL,
- cheevos_unlocked, cheevo);
-
- if (settings && settings->bools.cheevos_auto_screenshot)
- {
- char shotname[256];
-
- snprintf(shotname, sizeof(shotname), "%s/%s-cheevo-%u",
- settings->paths.directory_screenshot,
- path_basename(path_get(RARCH_PATH_BASENAME)),
- cheevo->id);
- shotname[sizeof(shotname) - 1] = '\0';
-
- if (take_screenshot(shotname, true,
- video_driver_cached_frame_has_valid_framebuffer()))
- CHEEVOS_LOG("[CHEEVOS]: got a screenshot for cheevo %u\n", cheevo->id);
- else
- CHEEVOS_LOG("[CHEEVOS]: failed to get screenshot for cheevo %u\n", cheevo->id);
- }
- }
+ cheevos_award(cheevo, mode);
cheevo->last = valid;
}
}
}
-static int cheevos_test_lboard_condition(const cheevos_condition_t* condition)
-{
- int dirty_conds = 0;
- int reset_conds = 0;
- int ret_val = 0;
- int ret_val_sub_cond = 0;
- cheevos_condset_t *condset = NULL;
- const cheevos_condset_t *end = NULL;
-
- if (!condition)
- return 0;
-
- ret_val_sub_cond = condition->count == 1;
- condset = condition->condsets;
- end = condset + condition->count;
-
- if (condset < end)
- {
- ret_val = cheevos_test_cond_set(
- condset, &dirty_conds, &reset_conds);
- condset++;
- }
-
- while (condset < end)
- {
- ret_val_sub_cond |= cheevos_test_cond_set(
- condset, &dirty_conds, &reset_conds);
- condset++;
- }
-
- if (reset_conds)
- {
- for (condset = condition->condsets; condset < end; condset++)
- cheevos_reset_cond_set(condset, 0);
- }
-
- return (ret_val && ret_val_sub_cond);
-}
-
-static int cheevos_expr_value(cheevos_expr_t* expr)
-{
- unsigned i;
- int values[16];
- /* Separate possible values with '$' operator, submit the largest */
- unsigned current_value = 0;
- cheevos_term_t* term = NULL;
-
- if (!expr)
- return 0;
-
- term = expr->terms;
-
- if (!term)
- return 0;
-
- if (expr->compare_count >= ARRAY_SIZE(values))
- {
- CHEEVOS_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", expr->compare_count);
- return 0;
- }
-
- memset(values, 0, sizeof values);
-
- for (i = expr->count; i != 0; i--, term++)
- {
- if (current_value >= ARRAY_SIZE(values))
- {
- CHEEVOS_ERR("[CHEEVOS]: too many values in the leaderboard expression: %u\n", current_value);
- return 0;
- }
-
- values[current_value] +=
- cheevos_var_get_value(&term->var) * term->multiplier;
-
- if (term->compare_next)
- current_value++;
- }
-
- if (expr->compare_count > 1)
- {
- unsigned j;
- int maximum = values[0];
-
- for (j = 1; j < expr->compare_count; j++)
- maximum = values[j] > maximum ? values[j] : maximum;
-
- return maximum;
- }
- else
- return values[0];
-}
-
-static void cheevos_make_lboard_url(const cheevos_leaderboard_t *lboard,
- char* url, size_t url_size)
+static void cheevos_lboard_submit_task(void* task_data, void* user_data,
+ const char* error)
{
+ settings_t *settings = config_get_ptr();
+ const cheevos_lboard_t* lboard = (const cheevos_lboard_t*)user_data;
MD5_CTX ctx;
uint8_t hash[16];
char signature[64];
- settings_t *settings = config_get_ptr();
+ char buffer[256];
+ int ret;
- hash[0] = '\0';
+ if (!error)
+ {
+ CHEEVOS_LOG(CHEEVOS_TAG "Submitted leaderboard %u\n", lboard->info->id);
+ return;
+ }
- snprintf(signature, sizeof(signature), "%u%s%u", lboard->id,
+ CHEEVOS_ERR(CHEEVOS_TAG "Error submitting leaderboard %u: %s\n", lboard->info->id, error);
+
+ /* Try again. */
+
+ /* Evaluate the signature. */
+ snprintf(signature, sizeof(signature), "%u%s%u", lboard->info->id,
settings->arrays.cheevos_username,
- lboard->id);
+ lboard->info->id);
MD5_Init(&ctx);
MD5_Update(&ctx, (void*)signature, strlen(signature));
MD5_Final(hash, &ctx);
- snprintf(
- url, url_size,
- "http://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d"
- "&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- settings->arrays.cheevos_username,
- cheevos_locals.token,
- lboard->id,
- lboard->last_value,
- hash[ 0], hash[ 1], hash[ 2], hash[ 3],
- hash[ 4], hash[ 5], hash[ 6], hash[ 7],
- hash[ 8], hash[ 9], hash[10], hash[11],
- hash[12], hash[13], hash[14], hash[15]
- );
+ /* Start the request. */
+ ret = rc_url_submit_lboard(buffer, sizeof(buffer), settings->arrays.cheevos_username, cheevos_locals.token, lboard->info->id, lboard->last_value, hash);
- url[url_size - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to submit the leaderboard: %s\n", url);
-#endif
-}
-
-static void cheevos_lboard_submit(void *task_data, void *user_data,
- const char *error)
-{
- cheevos_leaderboard_t *lboard = (cheevos_leaderboard_t *)user_data;
-
- if (!lboard)
- return;
-
- if (!error)
+ if (ret != 0)
{
- CHEEVOS_ERR("[CHEEVOS]: error submitting leaderboard %u\n", lboard->id);
+ CHEEVOS_ERR(CHEEVOS_TAG "Buffer to small to create URL\n");
return;
}
- CHEEVOS_LOG("[CHEEVOS]: submitted leaderboard %u.\n", lboard->id);
+ cheevos_log_url(CHEEVOS_TAG "rc_url_submit_lboard: %s\n", buffer);
+ task_push_http_transfer(buffer, true, NULL, cheevos_lboard_submit_task, user_data);
+}
+
+static void cheevos_lboard_submit(cheevos_lboard_t* lboard)
+{
+ char buffer[256];
+ char value[16];
+
+ /* Deactivate the leaderboard. */
+ lboard->active = 0;
+
+ /* Failsafe for improper leaderboards. */
+ if (lboard->last_value == 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "Leaderboard %s tried to submit 0\n", lboard->info->title);
+ runloop_msg_queue_push("Leaderboard attempt cancelled!", 0, 2 * 60, false);
+ return;
+ }
+
+ /* Show the OSD message. */
+ rc_format_value(value, sizeof(value), lboard->last_value, lboard->format);
+
+ snprintf(buffer, sizeof(buffer), "Submitted %s for %s",
+ value, lboard->info->title);
+ runloop_msg_queue_push(buffer, 0, 2 * 60, false);
+
+ /* Start the submit task. */
+ cheevos_lboard_submit_task(NULL, lboard, "no error, first try");
}
static void cheevos_test_leaderboards(void)
{
- unsigned i;
- cheevos_leaderboard_t* lboard = cheevos_locals.leaderboards;
+ cheevos_lboard_t* lboard = cheevos_locals.lboards;
+ int i;
+ unsigned value;
- if (!lboard)
- return;
-
- for (i = cheevos_locals.lboard_count; i != 0; i--, lboard++)
+ for (i = 0; i < cheevos_locals.patchdata.lboard_count; i++, lboard++)
{
if (lboard->active)
{
- int value = cheevos_expr_value(&lboard->value);
+ value = rc_evaluate_value(&lboard->lboard->value, cheevos_peek, NULL, NULL);
if (value != lboard->last_value)
{
- CHEEVOS_LOG("[CHEEVOS]: value lboard %s %u\n",
- lboard->title, value);
+ CHEEVOS_LOG(CHEEVOS_TAG "Value lboard %s %u\n", lboard->info->title, value);
lboard->last_value = value;
}
- if (cheevos_test_lboard_condition(&lboard->submit))
+ if (rc_test_trigger(&lboard->lboard->submit, cheevos_peek, NULL, NULL))
+ cheevos_lboard_submit(lboard);
+
+ if (rc_test_trigger(&lboard->lboard->cancel, cheevos_peek, NULL, NULL))
{
- lboard->active = 0;
-
- /* failsafe for improper LBs */
- if (value == 0)
- {
- CHEEVOS_LOG("[CHEEVOS]: error: lboard %s tried to submit 0\n",
- lboard->title);
- runloop_msg_queue_push("Leaderboard attempt cancelled!",
- 0, 2 * 60, false);
- }
- else
- {
- char url[256];
- char msg[256];
- char formatted_value[16];
-
- cheevos_make_lboard_url(lboard, url, sizeof(url));
- task_push_http_transfer(url, true, NULL,
- cheevos_lboard_submit, lboard);
- CHEEVOS_LOG("[CHEEVOS]: submit lboard %s\n", lboard->title);
-
- cheevos_format_value(value, lboard->format,
- formatted_value, sizeof(formatted_value));
- snprintf(msg, sizeof(msg), "Submitted %s for %s",
- formatted_value, lboard->title);
- msg[sizeof(msg) - 1] = 0;
- runloop_msg_queue_push(msg, 0, 2 * 60, false);
- }
- }
-
- if (cheevos_test_lboard_condition(&lboard->cancel))
- {
- CHEEVOS_LOG("[CHEEVOS]: cancel lboard %s\n", lboard->title);
+ CHEEVOS_LOG(CHEEVOS_TAG "Cancel leaderboard %s\n", lboard->info->title);
lboard->active = 0;
runloop_msg_queue_push("Leaderboard attempt cancelled!",
0, 2 * 60, false);
@@ -1897,191 +663,54 @@ static void cheevos_test_leaderboards(void)
}
else
{
- if (cheevos_test_lboard_condition(&lboard->start))
+ if (rc_test_trigger(&lboard->lboard->start, cheevos_peek, NULL, NULL))
{
- char msg[256];
+ char buffer[256];
- CHEEVOS_LOG("[CHEEVOS]: start lboard %s\n", lboard->title);
+ CHEEVOS_LOG(CHEEVOS_TAG "Leaderboard started: %s\n", lboard->info->title);
lboard->active = 1;
- lboard->last_value = -1;
+ lboard->last_value = 0;
- snprintf(msg, sizeof(msg),
- "Leaderboard Active: %s", lboard->title);
- msg[sizeof(msg) - 1] = 0;
- runloop_msg_queue_push(msg, 0, 2 * 60, false);
- runloop_msg_queue_push(lboard->description, 0, 3*60, false);
+ snprintf(buffer, sizeof(buffer),
+ "Leaderboard Active: %s", lboard->info->title);
+ runloop_msg_queue_push(buffer, 0, 2 * 60, false);
+ runloop_msg_queue_push(lboard->info->description, 0, 3 * 60, false);
}
}
}
}
-/*****************************************************************************
-Free the loaded achievements.
-*****************************************************************************/
-
-static void cheevos_free_condset(const cheevos_condset_t *set)
-{
- if (set && set->conds)
- free((void*)set->conds);
-}
-
-static void cheevos_free_cheevo(const cheevo_t *cheevo)
-{
- if (!cheevo)
- return;
-
- if (cheevo->title)
- free((void*)cheevo->title);
- if (cheevo->description)
- free((void*)cheevo->description);
- if (cheevo->author)
- free((void*)cheevo->author);
- if (cheevo->badge)
- free((void*)cheevo->badge);
- cheevos_free_condset(cheevo->condition.condsets);
-}
-
-static void cheevos_free_cheevo_set(const cheevoset_t *set)
-{
- const cheevo_t *cheevo = NULL;
- const cheevo_t *end = NULL;
-
- if (!set)
- return;
-
- cheevo = set->cheevos;
- end = cheevo + set->count;
-
- while (cheevo < end)
- cheevos_free_cheevo(cheevo++);
-
- if (set->cheevos)
- free((void*)set->cheevos);
-}
-
-#ifndef CHEEVOS_DONT_DEACTIVATE
-static int cheevos_deactivate__json_index(void *userdata, unsigned int index)
-{
- cheevos_deactivate_t *ud = (cheevos_deactivate_t*)userdata;
-
- if (ud)
- ud->is_element = 1;
-
- return 0;
-}
-
-static int cheevos_deactivate__json_number(void *userdata,
- const char *number, size_t length)
-{
- long id;
- int found;
- cheevo_t* cheevo = NULL;
- const cheevo_t* end = NULL;
- cheevos_deactivate_t *ud = (cheevos_deactivate_t*)userdata;
-
- if (ud && ud->is_element)
- {
- ud->is_element = 0;
- id = strtol(number, NULL, 10);
- found = 0;
- cheevo = cheevos_locals.core.cheevos;
- end = cheevo + cheevos_locals.core.count;
-
- for (; cheevo < end; cheevo++)
- {
- if (cheevo->id == (unsigned)id)
- {
- cheevo->active &= ~ud->mode;
- found = 1;
- break;
- }
- }
-
- if (!found)
- {
- cheevo = cheevos_locals.unofficial.cheevos;
- end = cheevo + cheevos_locals.unofficial.count;
-
- for (; cheevo < end; cheevo++)
- {
- if (cheevo->id == (unsigned)id)
- {
- cheevo->active &= ~ud->mode;
- found = 1;
- break;
- }
- }
- }
-
- if (found)
- CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked cheevo %u (%s).\n",
- cheevo->id, cheevo->title);
- else
- CHEEVOS_ERR("[CHEEVOS]: unknown cheevo to deactivate: %u.\n", id);
- }
-
- return 0;
-}
-
-static int cheevos_deactivate_unlocks(const char* json, unsigned mode)
-{
- static const jsonsax_handlers_t handlers =
- {
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- cheevos_deactivate__json_index,
- NULL,
- cheevos_deactivate__json_number,
- NULL,
- NULL
- };
-
- cheevos_deactivate_t ud;
-
- ud.is_element = 0;
- ud.mode = mode;
- return jsonsax_parse(json, &handlers, (void*)&ud) != JSONSAX_OK;
-}
-#endif
-
void cheevos_reset_game(void)
{
- cheevo_t *end = NULL;
- cheevo_t *cheevo = cheevos_locals.core.cheevos;
+ cheevos_cheevo_t* cheevo = NULL;
+ int i, count;
- if (!cheevo)
- return;
+ cheevo = cheevos_locals.core;
- end = cheevo + cheevos_locals.core.count;
-
- for (; cheevo < end; cheevo++)
- cheevo->last = 1;
-
- cheevo = cheevos_locals.unofficial.cheevos;
- end = cheevo + cheevos_locals.unofficial.count;
-
- for (; cheevo < end; cheevo++)
+ for (i = 0, count = cheevos_locals.patchdata.core_count; i < count; i++, cheevo++)
+ {
cheevo->last = 1;
+ }
+
+ cheevo = cheevos_locals.unofficial;
+
+ for (i = 0, count = cheevos_locals.patchdata.unofficial_count; i < count; i++, cheevo++)
+ {
+ cheevo->last = 1;
+ }
}
-void cheevos_populate_menu(void *data)
+void cheevos_populate_menu(void* data)
{
#ifdef HAVE_MENU
- unsigned i = 0;
- unsigned items_found = 0;
- settings_t *settings = config_get_ptr();
- menu_displaylist_info_t *info = (menu_displaylist_info_t*)data;
- cheevo_t *end = NULL;
- cheevo_t *cheevo = cheevos_locals.core.cheevos;
- end = cheevo + cheevos_locals.core.count;
+ int i = 0;
+ int count = 0;
+ settings_t* settings = config_get_ptr();
+ menu_displaylist_info_t* info = (menu_displaylist_info_t*)data;
+ cheevos_cheevo_t* cheevo = NULL;
- if(settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable
- && cheevos_loaded)
+ if ( settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable
+ && cheevos_loaded)
{
if (!cheevos_hardcore_paused)
menu_entries_append_enum(info->list,
@@ -2097,81 +726,87 @@ void cheevos_populate_menu(void *data)
MENU_SETTING_ACTION_RESUME_ACHIEVEMENTS, 0, 0);
}
- if (cheevo)
+ cheevo = cheevos_locals.core;
+
+ for (i = 0, count = cheevos_locals.patchdata.core_count; i < count; i++, cheevo++)
{
- for (i = 0; cheevo < end; i++, cheevo++)
+ if (!(cheevo->active & CHEEVOS_ACTIVE_HARDCORE))
+ {
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
+ MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY_HARDCORE,
+ MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
+ (cheevo->active & CHEEVOS_ACTIVE_HARDCORE));
+ }
+ else if (!(cheevo->active & CHEEVOS_ACTIVE_SOFTCORE))
+ {
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
+ MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY,
+ MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
+ (cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
+ }
+ else
+ {
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
+ MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
+ MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
+ (cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
+ }
+ }
+
+ if (settings->bools.cheevos_test_unofficial)
+ {
+ cheevo = cheevos_locals.unofficial;
+
+ for (i = 0, count = cheevos_locals.patchdata.unofficial_count; i < count; i++, cheevo++)
{
if (!(cheevo->active & CHEEVOS_ACTIVE_HARDCORE))
{
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY_HARDCORE,
MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
(cheevo->active & CHEEVOS_ACTIVE_HARDCORE));
}
else if (!(cheevo->active & CHEEVOS_ACTIVE_SOFTCORE))
{
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY,
MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
(cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
}
else
{
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
+ menu_entries_append_enum(info->list, cheevo->info->title,
+ cheevo->info->description,
MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
+
+ set_badge_info(&badges_ctx, i, cheevo->info->badge,
(cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
}
- items_found++;
}
}
- cheevo = cheevos_locals.unofficial.cheevos;
+ count = cheevos_locals.patchdata.core_count;
- if (cheevo && settings->bools.cheevos_test_unofficial)
- {
- end = cheevo + cheevos_locals.unofficial.count;
+ if (settings->bools.cheevos_test_unofficial)
+ count += cheevos_locals.patchdata.unofficial_count;
- for (i = items_found; cheevo < end; i++, cheevo++)
- {
- if (!(cheevo->active & CHEEVOS_ACTIVE_HARDCORE))
- {
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
- MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY_HARDCORE,
- MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
- (cheevo->active & CHEEVOS_ACTIVE_HARDCORE));
- }
- else if (!(cheevo->active & CHEEVOS_ACTIVE_SOFTCORE))
- {
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
- MENU_ENUM_LABEL_CHEEVOS_UNLOCKED_ENTRY,
- MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
- (cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
- }
- else
- {
- menu_entries_append_enum(info->list, cheevo->title,
- cheevo->description,
- MENU_ENUM_LABEL_CHEEVOS_LOCKED_ENTRY,
- MENU_SETTINGS_CHEEVOS_START + i, 0, 0);
- set_badge_info(&badges_ctx, i, cheevo->badge,
- (cheevo->active & CHEEVOS_ACTIVE_SOFTCORE));
- }
- items_found++;
- }
- }
-
- if (items_found == 0)
+ if (count == 0)
{
menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_ACHIEVEMENTS_TO_DISPLAY),
@@ -2182,34 +817,39 @@ void cheevos_populate_menu(void *data)
#endif
}
-bool cheevos_get_description(cheevos_ctx_desc_t *desc)
+bool cheevos_get_description(cheevos_ctx_desc_t* desc)
{
+ unsigned idx;
+ const cheevos_cheevo_t* cheevo;
+
if (!desc)
return false;
+ *desc->s = 0;
+
if (cheevos_loaded)
{
- cheevo_t *cheevos = cheevos_locals.core.cheevos;
+ idx = desc->idx;
- if (!cheevos)
- return false;
-
- if (desc->idx >= cheevos_locals.core.count)
+ if (idx < cheevos_locals.patchdata.core_count)
+ cheevo = cheevos_locals.core + idx;
+ else
{
- cheevos = cheevos_locals.unofficial.cheevos;
- desc->idx -= cheevos_locals.core.count;
+ idx -= cheevos_locals.patchdata.core_count;
+
+ if (idx < cheevos_locals.patchdata.unofficial_count)
+ cheevo = cheevos_locals.unofficial + idx;
+ else
+ return true;
}
- if (!string_is_empty(cheevos[desc->idx].description))
- strlcpy(desc->s, cheevos[desc->idx].description, desc->len);
+ strlcpy(desc->s, cheevo->info->description, desc->len);
}
- else
- *desc->s = 0;
return true;
}
-bool cheevos_apply_cheats(bool *data_bool)
+bool cheevos_apply_cheats(bool* data_bool)
{
cheats_are_enabled = *data_bool;
cheats_were_enabled |= cheats_are_enabled;
@@ -2219,14 +859,16 @@ bool cheevos_apply_cheats(bool *data_bool)
bool cheevos_unload(void)
{
- bool running;
+ bool running = false;
+ unsigned i = 0, count = 0;
+
CHEEVOS_LOCK(cheevos_locals.task_lock);
running = cheevos_locals.task != NULL;
CHEEVOS_UNLOCK(cheevos_locals.task_lock);
if (running)
{
- CHEEVOS_LOG("[CHEEVOS]: Asked the load thread to terminate\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "Asked the load thread to terminate\n");
task_queue_cancel_task(cheevos_locals.task);
#ifdef HAVE_THREADS
@@ -2242,17 +884,34 @@ bool cheevos_unload(void)
if (cheevos_loaded)
{
- cheevos_free_cheevo_set(&cheevos_locals.core);
- cheevos_free_cheevo_set(&cheevos_locals.unofficial);
+ for (i = 0, count = cheevos_locals.patchdata.core_count; i < count; i++)
+ {
+ CHEEVOS_FREE(cheevos_locals.core[i].trigger);
+ }
+
+ for (i = 0, count = cheevos_locals.patchdata.unofficial_count; i < count; i++)
+ {
+ CHEEVOS_FREE(cheevos_locals.unofficial[i].trigger);
+ }
+
+ for (i = 0, count = cheevos_locals.patchdata.lboard_count; i < count; i++)
+ {
+ CHEEVOS_FREE(cheevos_locals.lboards[i].lboard);
+ }
+
+ CHEEVOS_FREE(cheevos_locals.core);
+ CHEEVOS_FREE(cheevos_locals.unofficial);
+ CHEEVOS_FREE(cheevos_locals.lboards);
+ cheevos_free_patchdata(&cheevos_locals.patchdata);
+ cheevos_fixup_destroy(&cheevos_locals.fixups);
}
- cheevos_locals.core.cheevos = NULL;
- cheevos_locals.unofficial.cheevos = NULL;
- cheevos_locals.core.count = 0;
- cheevos_locals.unofficial.count = 0;
+ cheevos_locals.core = NULL;
+ cheevos_locals.unofficial = NULL;
+ cheevos_locals.lboards = NULL;
- cheevos_loaded = false;
- cheevos_hardcore_paused = false;
+ cheevos_loaded = false;
+ cheevos_hardcore_paused = false;
return true;
}
@@ -2289,187 +948,16 @@ bool cheevos_toggle_hardcore_mode(void)
return true;
}
-static void cheevos_patch_addresses(cheevoset_t* set)
-{
- unsigned i;
- cheevo_t* cheevo = NULL;
-
- if (!set)
- return;
-
- cheevo = set->cheevos;
-
- if (!cheevo)
- return;
-
- for (i = set->count; i != 0; i--, cheevo++)
- {
- unsigned j;
- cheevos_condset_t* condset = cheevo->condition.condsets;
-
- for (j = cheevo->condition.count; j != 0; j--, condset++)
- {
- unsigned k;
- cheevos_cond_t* cond = condset->conds;
-
- for (k = condset->count; k != 0; k--, cond++)
- {
- switch (cond->source.type)
- {
- case CHEEVOS_VAR_TYPE_ADDRESS:
- case CHEEVOS_VAR_TYPE_DELTA_MEM:
- cheevos_var_patch_addr(&cond->source,
- cheevos_locals.console_id);
-#ifdef CHEEVOS_DUMP_ADDRS
- CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n",
- cond->source.bank_id + 1, cond->source.value);
-#endif
- break;
-
- default:
- break;
- }
-
- switch (cond->target.type)
- {
- case CHEEVOS_VAR_TYPE_ADDRESS:
- case CHEEVOS_VAR_TYPE_DELTA_MEM:
- cheevos_var_patch_addr(&cond->target,
- cheevos_locals.console_id);
-#ifdef CHEEVOS_DUMP_ADDRS
- CHEEVOS_LOG("[CHEEVOS]: t-var %03d:%08X\n",
- cond->target.bank_id + 1, cond->target.value);
-#endif
- break;
-
- default:
- break;
- }
- }
- }
- }
-}
-
-static void cheevos_patch_lb_conditions(cheevos_condition_t* condition)
-{
- unsigned i;
- cheevos_condset_t* condset = NULL;
-
- if (!condition)
- return;
-
- condset = condition->condsets;
-
- for (i = condition->count; i != 0; i--, condset++)
- {
- unsigned j;
- cheevos_cond_t* cond = condset->conds;
-
- for (j = condset->count; j != 0; j--, cond++)
- {
- switch (cond->source.type)
- {
- case CHEEVOS_VAR_TYPE_ADDRESS:
- case CHEEVOS_VAR_TYPE_DELTA_MEM:
- cheevos_var_patch_addr(&cond->source,
- cheevos_locals.console_id);
-#ifdef CHEEVOS_DUMP_ADDRS
- CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n",
- cond->source.bank_id + 1, cond->source.value);
-#endif
- break;
- default:
- break;
- }
- switch (cond->target.type)
- {
- case CHEEVOS_VAR_TYPE_ADDRESS:
- case CHEEVOS_VAR_TYPE_DELTA_MEM:
- cheevos_var_patch_addr(&cond->target,
- cheevos_locals.console_id);
-#ifdef CHEEVOS_DUMP_ADDRS
- CHEEVOS_LOG("[CHEEVOS]: t-var %03d:%08X\n",
- cond->target.bank_id + 1, cond->target.value);
-#endif
- break;
- default:
- break;
- }
- }
- }
-}
-
-static void cheevos_patch_lb_expressions(cheevos_expr_t* expression)
-{
- unsigned i;
- cheevos_term_t* term = NULL;
-
- if (!expression)
- return;
-
- term = expression->terms;
-
- for (i = expression->count; i != 0; i--, term++)
- {
- switch (term->var.type)
- {
- case CHEEVOS_VAR_TYPE_ADDRESS:
- case CHEEVOS_VAR_TYPE_DELTA_MEM:
- cheevos_var_patch_addr(&term->var, cheevos_locals.console_id);
-#ifdef CHEEVOS_DUMP_ADDRS
- CHEEVOS_LOG("[CHEEVOS]: s-var %03d:%08X\n",
- term->var.bank_id + 1, term->var.value);
-#endif
- break;
- default:
- break;
- }
- }
-}
-
-static void cheevos_patch_lbs(cheevos_leaderboard_t *leaderboard)
-{
- unsigned i;
-
- for (i = 0; i < cheevos_locals.lboard_count; i++)
- {
- cheevos_condition_t *start = &leaderboard[i].start;
- cheevos_condition_t *cancel = &leaderboard[i].cancel;
- cheevos_condition_t *submit = &leaderboard[i].submit;
- cheevos_expr_t *value = &leaderboard[i].value;
-
- cheevos_patch_lb_conditions(start);
- cheevos_patch_lb_conditions(cancel);
- cheevos_patch_lb_conditions(submit);
- cheevos_patch_lb_expressions(value);
- }
-}
-
void cheevos_test(void)
{
settings_t *settings = config_get_ptr();
- if (!cheevos_locals.addrs_patched)
- {
- cheevos_patch_addresses(&cheevos_locals.core);
- cheevos_patch_addresses(&cheevos_locals.unofficial);
- cheevos_patch_lbs(cheevos_locals.leaderboards);
+ cheevos_test_cheevo_set(true);
- cheevos_locals.addrs_patched = true;
- }
-
- cheevos_test_cheevo_set(&cheevos_locals.core);
-
- if (settings)
- {
- if (settings->bools.cheevos_test_unofficial)
- cheevos_test_cheevo_set(&cheevos_locals.unofficial);
-
- if (settings->bools.cheevos_hardcore_mode_enable &&
- settings->bools.cheevos_leaderboards_enable &&
- !cheevos_hardcore_paused)
- cheevos_test_leaderboards();
- }
+ if (settings->bools.cheevos_test_unofficial)
+ cheevos_test_cheevo_set(false);
+
+ cheevos_test_leaderboards();
}
bool cheevos_set_cheats(void)
@@ -2488,11 +976,44 @@ bool cheevos_get_support_cheevos(void)
return cheevos_locals.core_supports;
}
-cheevos_console_t cheevos_get_console(void)
+int cheevos_get_console(void)
{
return cheevos_locals.console_id;
}
+static void cheevos_unlock_cb(unsigned id, void* userdata)
+{
+ cheevos_cheevo_t* cheevo = NULL;
+ int i = 0;
+ unsigned j = 0, count = 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (i == 0)
+ {
+ cheevo = cheevos_locals.core;
+ count = cheevos_locals.patchdata.core_count;
+ }
+ else
+ {
+ cheevo = cheevos_locals.unofficial;
+ count = cheevos_locals.patchdata.unofficial_count;
+ }
+
+ for (j = 0; j < count; j++, cheevo++)
+ {
+ if (cheevo->info->id == id)
+ {
+#ifndef CHEEVOS_DONT_DEACTIVATE
+ cheevo->active &= ~*(unsigned*)userdata;
+#endif
+ CHEEVOS_LOG(CHEEVOS_TAG "cheevo %u deactivated: %s\n", id, cheevo->info->title);
+ return;
+ }
+ }
+ }
+}
+
#include "coro.h"
/* Uncomment the following two lines to debug cheevos_iterate, this will
@@ -2507,8 +1028,8 @@ cheevos_console_t cheevos_get_console(void)
*
* https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
*/
-/*#undef CORO_YIELD
-#define CORO_YIELD()*/
+#undef CORO_YIELD
+#define CORO_YIELD()
typedef struct
{
@@ -2537,11 +1058,11 @@ typedef struct
const char *path;
const char *ext;
intfstream_t *stream;
- cheevo_t *cheevo;
+ cheevos_cheevo_t *cheevo;
settings_t *settings;
struct http_connection_t *conn;
struct http_t *http;
- const cheevo_t *cheevo_end;
+ const cheevos_cheevo_t *cheevo_end;
/* co-routine required fields */
CORO_FIELDS
@@ -2568,7 +1089,7 @@ enum
DELAY = -16
};
-static int cheevos_iterate(coro_t *coro)
+static int cheevos_iterate(coro_t* coro)
{
ssize_t num_read = 0;
size_t to_read = 4096;
@@ -2621,36 +1142,7 @@ static int cheevos_iterate(coro_t *coro)
CORO_ENTER();
-
-
- cheevos_locals.addrs_patched = false;
-
- coro->settings = config_get_ptr();
-
- cheevos_locals.meminfo[0].id = RETRO_MEMORY_SYSTEM_RAM;
- core_get_memory(&cheevos_locals.meminfo[0]);
-
- cheevos_locals.meminfo[1].id = RETRO_MEMORY_SAVE_RAM;
- core_get_memory(&cheevos_locals.meminfo[1]);
-
- cheevos_locals.meminfo[2].id = RETRO_MEMORY_VIDEO_RAM;
- core_get_memory(&cheevos_locals.meminfo[2]);
-
- cheevos_locals.meminfo[3].id = RETRO_MEMORY_RTC;
- core_get_memory(&cheevos_locals.meminfo[3]);
-
- CHEEVOS_LOG("[CHEEVOS]: system RAM: %p %u\n",
- cheevos_locals.meminfo[0].data,
- cheevos_locals.meminfo[0].size);
- CHEEVOS_LOG("[CHEEVOS]: save RAM: %p %u\n",
- cheevos_locals.meminfo[1].data,
- cheevos_locals.meminfo[1].size);
- CHEEVOS_LOG("[CHEEVOS]: video RAM: %p %u\n",
- cheevos_locals.meminfo[2].data,
- cheevos_locals.meminfo[2].size);
- CHEEVOS_LOG("[CHEEVOS]: RTC: %p %u\n",
- cheevos_locals.meminfo[3].data,
- cheevos_locals.meminfo[3].size);
+ coro->settings = config_get_ptr();
/* Bail out if cheevos are disabled.
* But set the above anyways,
@@ -2675,15 +1167,15 @@ static int cheevos_iterate(coro_t *coro)
coro->count = intfstream_get_size(coro->stream);
/* size limit */
- if (coro->count > size_in_megabytes(64))
- coro->count = size_in_megabytes(64);
+ if (coro->count > CHEEVOS_MB(64))
+ coro->count = CHEEVOS_MB(64);
coro->data = malloc(coro->count);
if (!coro->data)
{
intfstream_close(coro->stream);
- free(coro->stream);
+ CHEEVOS_FREE(coro->stream);
CORO_STOP();
}
@@ -2711,7 +1203,7 @@ static int cheevos_iterate(coro_t *coro)
}
intfstream_close(coro->stream);
- free(coro->stream);
+ CHEEVOS_FREE(coro->stream);
}
/* Use the supported extensions as a hint
@@ -2731,14 +1223,12 @@ static int cheevos_iterate(coro_t *coro)
if (end)
{
- hash = cheevos_djb2(
- coro->ext, end - coro->ext);
+ hash = cheevos_djb2(coro->ext, end - coro->ext);
coro->ext = end + 1;
}
else
{
- hash = cheevos_djb2(
- coro->ext, strlen(coro->ext));
+ hash = cheevos_djb2(coro->ext, strlen(coro->ext));
coro->ext = NULL;
}
@@ -2746,7 +1236,7 @@ static int cheevos_iterate(coro_t *coro)
{
if (finders[coro->i].ext_hashes[coro->j] == hash)
{
- CHEEVOS_LOG("[CHEEVOS]: testing %s.\n",
+ CHEEVOS_LOG(CHEEVOS_TAG "testing %s\n",
finders[coro->i].name);
/*
@@ -2771,7 +1261,7 @@ static int cheevos_iterate(coro_t *coro)
if (finders[coro->i].ext_hashes)
continue;
- CHEEVOS_LOG("[CHEEVOS]: testing %s.\n",
+ CHEEVOS_LOG(CHEEVOS_TAG "testing %s\n",
finders[coro->i].name);
/*
@@ -2784,7 +1274,7 @@ static int cheevos_iterate(coro_t *coro)
goto found;
}
- CHEEVOS_LOG("[CHEEVOS]: this game doesn't feature achievements.\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "this game doesn't feature achievements\n");
CORO_STOP();
found:
@@ -2810,7 +1300,7 @@ found:
if (!coro->json)
{
runloop_msg_queue_push("Error loading achievements.", 0, 5 * 60, false);
- CHEEVOS_ERR("[CHEEVOS]: error loading achievements.\n");
+ CHEEVOS_ERR(CHEEVOS_TAG "error loading achievements\n");
CORO_STOP();
}
#endif
@@ -2824,32 +1314,20 @@ found:
#endif
if (cheevos_parse(coro->json))
{
- if ((void*)coro->json)
- free((void*)coro->json);
+ CHEEVOS_FREE(coro->json);
CORO_STOP();
}
- if ((void*)coro->json)
- free((void*)coro->json);
+ CHEEVOS_FREE(coro->json);
- if ( cheevos_locals.core.count == 0
- && cheevos_locals.unofficial.count == 0
- && cheevos_locals.lboard_count == 0)
+ if ( cheevos_locals.patchdata.core_count == 0
+ && cheevos_locals.patchdata.unofficial_count == 0
+ && cheevos_locals.patchdata.lboard_count == 0)
{
runloop_msg_queue_push(
"This game has no achievements.",
0, 5 * 60, false);
- cheevos_free_cheevo_set(&cheevos_locals.core);
- cheevos_free_cheevo_set(&cheevos_locals.unofficial);
-
- cheevos_locals.core.cheevos = NULL;
- cheevos_locals.unofficial.cheevos = NULL;
- cheevos_locals.core.count = 0;
- cheevos_locals.unofficial.count = 0;
-
- cheevos_loaded = false;
- cheevos_hardcore_paused = false;
CORO_STOP();
}
@@ -2867,13 +1345,13 @@ found:
*/
CORO_GOSUB(PLAYING);
- if (coro->settings->bools.cheevos_verbose_enable && cheevos_locals.core.count > 0)
+ if (coro->settings->bools.cheevos_verbose_enable && cheevos_locals.patchdata.core_count > 0)
{
char msg[256];
- int mode = CHEEVOS_ACTIVE_SOFTCORE;
- const cheevo_t* cheevo = cheevos_locals.core.cheevos;
- const cheevo_t* end = cheevo + cheevos_locals.core.count;
- int number_of_unlocked = cheevos_locals.core.count;
+ int mode = CHEEVOS_ACTIVE_SOFTCORE;
+ const cheevos_cheevo_t* cheevo = cheevos_locals.core;
+ const cheevos_cheevo_t* end = cheevo + cheevos_locals.patchdata.core_count;
+ int number_of_unlocked = cheevos_locals.patchdata.core_count;
if (coro->settings->bools.cheevos_hardcore_mode_enable && !cheevos_hardcore_paused)
mode = CHEEVOS_ACTIVE_HARDCORE;
@@ -2884,7 +1362,7 @@ found:
snprintf(msg, sizeof(msg),
"You have %d of %d achievements unlocked.",
- number_of_unlocked, cheevos_locals.core.count);
+ number_of_unlocked, cheevos_locals.patchdata.core_count);
msg[sizeof(msg) - 1] = 0;
runloop_msg_queue_push(msg, 0, 6 * 60, false);
}
@@ -2913,14 +1391,14 @@ found:
CORO_RET();
}
- if (coro->count < size_in_megabytes(8))
+ if (coro->count < CHEEVOS_MB(8))
{
/*
* Inputs: CHEEVOS_VAR_MD5, CHEEVOS_VAR_OFFSET, CHEEVOS_VAR_COUNT
* Outputs: CHEEVOS_VAR_MD5
*/
coro->offset = 0;
- coro->count = size_in_megabytes(8) - coro->count;
+ coro->count = CHEEVOS_MB(8) - coro->count;
CORO_GOSUB(FILL_MD5);
}
@@ -2947,10 +1425,10 @@ found:
CORO_RET();
}
- if (coro->count < size_in_megabytes(6))
+ if (coro->count < CHEEVOS_MB(6))
{
coro->offset = 0;
- coro->count = size_in_megabytes(6) - coro->count;
+ coro->count = CHEEVOS_MB(6) - coro->count;
CORO_GOSUB(FILL_MD5);
}
@@ -3098,8 +1576,8 @@ found:
coro->count = coro->len - coro->offset;
/* size limit */
- if (coro->count > size_in_megabytes(64))
- coro->count = size_in_megabytes(64);
+ if (coro->count > CHEEVOS_MB(64))
+ coro->count = CHEEVOS_MB(64);
MD5_Update(&coro->md5,
(void*)((uint8_t*)coro->data + coro->offset),
@@ -3139,49 +1617,24 @@ found:
CORO_SUB(GET_GAMEID)
{
- char gameid[16];
+ int size = rc_url_get_gameid(coro->url, sizeof(coro->url), coro->hash);
- CHEEVOS_LOG(
- "[CHEEVOS]: getting game id for hash %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
- coro->hash[ 0], coro->hash[ 1], coro->hash[ 2], coro->hash[ 3],
- coro->hash[ 4], coro->hash[ 5], coro->hash[ 6], coro->hash[ 7],
- coro->hash[ 8], coro->hash[ 9], coro->hash[10], coro->hash[11],
- coro->hash[12], coro->hash[13], coro->hash[14], coro->hash[15]
- );
-
- snprintf(
- coro->url, sizeof(coro->url),
- "http://retroachievements.org/dorequest.php?r=gameid&m=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
- coro->hash[ 0], coro->hash[ 1], coro->hash[ 2], coro->hash[ 3],
- coro->hash[ 4], coro->hash[ 5], coro->hash[ 6], coro->hash[ 7],
- coro->hash[ 8], coro->hash[ 9], coro->hash[10], coro->hash[11],
- coro->hash[12], coro->hash[13], coro->hash[14], coro->hash[15]
- );
-
- coro->url[sizeof(coro->url) - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to get the game's id: %s\n", coro->url);
-#endif
+ if (size < 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "buffer too small to create URL\n");
+ CORO_RET();
+ }
+ cheevos_log_url(CHEEVOS_TAG "rc_url_get_gameid: %s\n", coro->url);
CORO_GOSUB(HTTP_GET);
if (!coro->json)
CORO_RET();
- if (cheevos_get_value(coro->json,
- CHEEVOS_JSON_KEY_GAMEID, gameid, sizeof(gameid)))
- {
- if ((void*)coro->json)
- free((void*)coro->json);
- CHEEVOS_ERR("[CHEEVOS]: error getting game_id.\n");
- CORO_RET();
- }
+ coro->gameid = chevos_get_gameid(coro->json);
- if ((void*)coro->json)
- free((void*)coro->json);
- CHEEVOS_LOG("[CHEEVOS]: got game id %s.\n", gameid);
- coro->gameid = (unsigned)strtol(gameid, NULL, 10);
+ CHEEVOS_FREE(coro->json);
+ CHEEVOS_LOG(CHEEVOS_TAG "got game id %u\n", coro->gameid);
CORO_RET();
}
@@ -3191,31 +1644,31 @@ found:
* Outputs CHEEVOS_VAR_JSON
*************************************************************************/
CORO_SUB(GET_CHEEVOS)
+ {
+ int ret;
CORO_GOSUB(LOGIN);
- snprintf(coro->url, sizeof(coro->url),
- "http://retroachievements.org/dorequest.php?r=patch&g=%u&u=%s&t=%s",
- coro->gameid,
- coro->settings->arrays.cheevos_username,
- cheevos_locals.token);
+ ret = rc_url_get_patch(coro->url, sizeof(coro->url), coro->settings->arrays.cheevos_username, cheevos_locals.token, coro->gameid);
- coro->url[sizeof(coro->url) - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to get the list of cheevos: %s\n", coro->url);
-#endif
+ if (ret < 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "buffer too small to create URL\n");
+ CORO_STOP();
+ }
+ cheevos_log_url(CHEEVOS_TAG "rc_url_get_patch: %s\n", coro->url);
CORO_GOSUB(HTTP_GET);
if (!coro->json)
{
- CHEEVOS_ERR("[CHEEVOS]: error getting achievements for game id %u.\n", coro->gameid);
+ CHEEVOS_ERR(CHEEVOS_TAG "error getting achievements for game id %u\n", coro->gameid);
CORO_STOP();
}
- CHEEVOS_LOG("[CHEEVOS]: got achievements for game id %u.\n", coro->gameid);
+ CHEEVOS_LOG(CHEEVOS_TAG "got achievements for game id %u\n", coro->gameid);
CORO_RET();
+ }
/**************************************************************************
* Info Gets the achievements from Retro Achievements
@@ -3233,58 +1686,69 @@ found:
CORO_RET();
}
- coro->cheevo = cheevos_locals.core.cheevos;
- coro->cheevo_end = cheevos_locals.core.cheevos + cheevos_locals.core.count;
-
- for (; coro->cheevo < coro->cheevo_end; coro->cheevo++)
+ for (coro->i = 0; coro->i < 2; coro->i++)
{
- for (coro->j = 0 ; coro->j < 2; coro->j++)
+ if (coro->i == 0)
{
- coro->badge_fullpath[0] = '\0';
- fill_pathname_application_special(
- coro->badge_fullpath,
- sizeof(coro->badge_fullpath),
- APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
+ coro->cheevo = cheevos_locals.core;
+ coro->cheevo_end = coro->cheevo + cheevos_locals.patchdata.core_count;
+ }
+ else
+ {
+ coro->cheevo = cheevos_locals.unofficial;
+ coro->cheevo_end = coro->cheevo + cheevos_locals.patchdata.unofficial_count;
+ }
- if (!path_is_directory(coro->badge_fullpath))
- path_mkdir(coro->badge_fullpath);
- CORO_YIELD();
- if (coro->j == 0)
- snprintf(coro->badge_name,
- sizeof(coro->badge_name),
- "%s.png", coro->cheevo->badge);
- else
- snprintf(coro->badge_name,
- sizeof(coro->badge_name),
- "%s_lock.png", coro->cheevo->badge);
-
- fill_pathname_join(
- coro->badge_fullpath,
- coro->badge_fullpath,
- coro->badge_name,
- sizeof(coro->badge_fullpath));
-
- if (!badge_exists(coro->badge_fullpath))
+ for (; coro->cheevo < coro->cheevo_end; coro->cheevo++)
+ {
+ for (coro->j = 0 ; coro->j < 2; coro->j++)
{
-#ifdef CHEEVOS_LOG_BADGES
- CHEEVOS_LOG(
- "[CHEEVOS]: downloading badge %s\n",
- coro->badge_fullpath);
-#endif
- snprintf(coro->url,
- sizeof(coro->url),
- "http://i.retroachievements.org/Badge/%s",
- coro->badge_name);
+ coro->badge_fullpath[0] = '\0';
+ fill_pathname_application_special(
+ coro->badge_fullpath,
+ sizeof(coro->badge_fullpath),
+ APPLICATION_SPECIAL_DIRECTORY_THUMBNAILS_CHEEVOS_BADGES);
- CORO_GOSUB(HTTP_GET);
+ if (!path_is_directory(coro->badge_fullpath))
+ path_mkdir(coro->badge_fullpath);
+ CORO_YIELD();
+ if (coro->j == 0)
+ snprintf(coro->badge_name,
+ sizeof(coro->badge_name),
+ "%s.png", coro->cheevo->info->badge);
+ else
+ snprintf(coro->badge_name,
+ sizeof(coro->badge_name),
+ "%s_lock.png", coro->cheevo->info->badge);
- if (coro->json)
+ fill_pathname_join(
+ coro->badge_fullpath,
+ coro->badge_fullpath,
+ coro->badge_name,
+ sizeof(coro->badge_fullpath));
+
+ if (!badge_exists(coro->badge_fullpath))
{
- if (!filestream_write_file(coro->badge_fullpath,
- coro->json, coro->k))
- CHEEVOS_ERR("[CHEEVOS]: error writing badge %s\n", coro->badge_fullpath);
- else
- free(coro->json);
+#ifdef CHEEVOS_LOG_BADGES
+ CHEEVOS_LOG(
+ CHEEVOS_TAG "downloading badge %s\n",
+ coro->badge_fullpath);
+#endif
+ snprintf(coro->url,
+ sizeof(coro->url),
+ "http://i.retroachievements.org/Badge/%s",
+ coro->badge_name);
+
+ CORO_GOSUB(HTTP_GET);
+
+ if (coro->json)
+ {
+ if (!filestream_write_file(coro->badge_fullpath,
+ coro->json, coro->k))
+ CHEEVOS_ERR(CHEEVOS_TAG "error writing badge %s\n", coro->badge_fullpath);
+ else
+ CHEEVOS_FREE(coro->json);
+ }
}
}
}
@@ -3296,125 +1760,89 @@ found:
* Info Logs in the user at Retro Achievements
*************************************************************************/
CORO_SUB(LOGIN)
+ {
+ const char* username = coro->settings->arrays.cheevos_username;
+ const char* password = coro->settings->arrays.cheevos_password;
+ const char* token = coro->settings->arrays.cheevos_token;
+ int ret;
+ char tok[256];
if (cheevos_locals.token[0])
CORO_RET();
+ if (string_is_empty(username))
{
- char urle_user[64];
- char urle_login[64];
- const char *username = coro ? coro->settings->arrays.cheevos_username : NULL;
- const char *login = NULL;
- bool via_token = false;
-
- if (coro)
- {
- if (string_is_empty(coro->settings->arrays.cheevos_password))
- {
- via_token = true;
- login = coro->settings->arrays.cheevos_token;
- }
- else
- login = coro->settings->arrays.cheevos_password;
- }
- else
- login = NULL;
-
- if (string_is_empty(username) || string_is_empty(login))
- {
- runloop_msg_queue_push(
- "Missing RetroAchievements account information.",
- 0, 5 * 60, false);
- runloop_msg_queue_push(
- "Please fill in your account information in Settings.",
- 0, 5 * 60, false);
- CHEEVOS_ERR("[CHEEVOS]: login info not informed.\n");
- CORO_STOP();
- }
-
- cheevos_url_encode(username, urle_user, sizeof(urle_user));
- cheevos_url_encode(login, urle_login, sizeof(urle_login));
-
- snprintf(
- coro->url, sizeof(coro->url),
- "http://retroachievements.org/dorequest.php?r=login&u=%s&%c=%s",
- urle_user, via_token ? 't' : 'p', urle_login
- );
-
- coro->url[sizeof(coro->url) - 1] = 0;
+ runloop_msg_queue_push(
+ "Missing RetroAchievements account information.",
+ 0, 5 * 60, false);
+ runloop_msg_queue_push(
+ "Please fill in your account information in Settings.",
+ 0, 5 * 60, false);
+ CHEEVOS_ERR(CHEEVOS_TAG "login info not informed\n");
+ CORO_STOP();
}
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to login: %s\n",
- coro->url);
-#endif
+ if (string_is_empty(token))
+ ret = rc_url_login_with_password(coro->url, sizeof(coro->url),
+ username, password);
+ else
+ ret = rc_url_login_with_token(coro->url, sizeof(coro->url),
+ username, token);
+ if (ret < 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "buffer too small to create URL\n");
+ CORO_STOP();
+ }
+
+ cheevos_log_url(CHEEVOS_TAG "rc_url_login_with_password: %s\n", coro->url);
CORO_GOSUB(HTTP_GET);
- if (coro->json)
+ if (!coro->json)
{
- char error_response[64];
- char error_message[256];
-
- cheevos_get_value(
- coro->json,
- CHEEVOS_JSON_KEY_ERROR,
- error_response,
- sizeof(error_response)
- );
-
- /* No error, continue with login */
- if (string_is_empty(error_response))
- {
- int res = cheevos_get_value(
- coro->json,
- CHEEVOS_JSON_KEY_TOKEN,
- cheevos_locals.token,
- sizeof(cheevos_locals.token));
-
- if ((void*)coro->json)
- free((void*)coro->json);
-
- if (!res)
- {
- if (coro->settings->bools.cheevos_verbose_enable)
- {
- char msg[256];
- snprintf(msg, sizeof(msg),
- "RetroAchievements: Logged in as \"%s\".",
- coro->settings->arrays.cheevos_username);
- msg[sizeof(msg) - 1] = 0;
- runloop_msg_queue_push(msg, 0, 3 * 60, false);
- }
-
- /* Save token to config and clear pass on success */
- *coro->settings->arrays.cheevos_password = '\0';
- strncpy(
- coro->settings->arrays.cheevos_token,
- cheevos_locals.token, sizeof(cheevos_locals.token)
- );
- CORO_RET();
- }
- }
-
- if ((void*)coro->json)
- free((void*)coro->json);
-
- /* Site returned error, display it */
- snprintf(error_message, sizeof(error_message),
- "RetroAchievements: %s",
- error_response);
- error_message[sizeof(error_message) - 1] = 0;
- runloop_msg_queue_push(error_message, 0, 5 * 60, false);
- *coro->settings->arrays.cheevos_token = '\0';
+ runloop_msg_queue_push("RetroAchievements: Error contacting server.", 0, 5 * 60, false);
+ CHEEVOS_ERR(CHEEVOS_TAG "error getting user token\n");
CORO_STOP();
}
- runloop_msg_queue_push("RetroAchievements: Error contacting server.", 0, 5 * 60, false);
- CHEEVOS_ERR("[CHEEVOS]: error getting user token.\n");
+ ret = cheevos_get_token(coro->json, tok, sizeof(tok));
- CORO_STOP();
+ if (ret != 0)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg),
+ "RetroAchievements: %s",
+ tok);
+ runloop_msg_queue_push(msg, 0, 5 * 60, false);
+ *coro->settings->arrays.cheevos_token = 0;
+
+ CHEEVOS_FREE(coro->json);
+ CORO_STOP();
+ }
+
+ CHEEVOS_FREE(coro->json);
+
+ if (coro->settings->bools.cheevos_verbose_enable)
+ {
+ char msg[256];
+ snprintf(msg, sizeof(msg),
+ "RetroAchievements: Logged in as \"%s\".",
+ coro->settings->arrays.cheevos_username);
+ msg[sizeof(msg) - 1] = 0;
+ runloop_msg_queue_push(msg, 0, 3 * 60, false);
+ }
+
+ strlcpy(cheevos_locals.token, tok,
+ sizeof(cheevos_locals.token));
+
+ /* Save token to config and clear pass on success */
+ strlcpy(coro->settings->arrays.cheevos_token, tok,
+ sizeof(coro->settings->arrays.cheevos_token));
+
+ *coro->settings->arrays.cheevos_password = 0;
+ CORO_RET();
+ }
/**************************************************************************
* Info Pauses execution for five seconds
@@ -3444,7 +1872,7 @@ found:
for (coro->k = 0; coro->k < 5; coro->k++)
{
if (coro->k != 0)
- CHEEVOS_LOG("[CHEEVOS]: Retrying HTTP request: %u of 5\n", coro->k + 1);
+ CHEEVOS_LOG(CHEEVOS_TAG "Retrying HTTP request: %u of 5\n", coro->k + 1);
coro->json = NULL;
coro->conn = net_http_connection_new(
@@ -3491,7 +1919,7 @@ found:
if (coro->json)
{
memcpy((void*)coro->json, (void*)data, length);
- free(data);
+ CHEEVOS_FREE(data);
coro->json[length] = 0;
}
@@ -3506,78 +1934,45 @@ found:
net_http_connection_free(coro->conn);
}
- CHEEVOS_LOG("[CHEEVOS]: Couldn't connect to server after 5 tries\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "Couldn't connect to server after 5 tries\n");
CORO_RET();
/**************************************************************************
* Info Deactivates the achievements already awarded
- * Inputs CHEEVOS_VAR_GAMEID
- * Outputs
- *************************************************************************/
+ * Inputs CHEEVOS_VAR_GAMEID
+ * Outputs
+ *************************************************************************/
CORO_SUB(DEACTIVATE)
-#ifndef CHEEVOS_DONT_DEACTIVATE
CORO_GOSUB(LOGIN);
-
- /* Deactivate achievements in softcore mode. */
- snprintf(
- coro->url, sizeof(coro->url),
- "http://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=0",
- coro->settings->arrays.cheevos_username,
- cheevos_locals.token, coro->gameid
- );
-
- coro->url[sizeof(coro->url) - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to get the list of unlocked cheevos in softcore: %s\n", coro->url);
-#endif
-
- CORO_GOSUB(HTTP_GET);
-
- if (coro->json)
{
- if (!cheevos_deactivate_unlocks(coro->json, CHEEVOS_ACTIVE_SOFTCORE))
- CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked achievements in softcore mode.\n");
- else
- CHEEVOS_ERR("[CHEEVOS]: error deactivating unlocked achievements in softcore mode.\n");
+ int i, ret;
+ unsigned mode;
- if ((void*)coro->json)
- free((void*)coro->json);
+ for (i = 0; i < 2; i++)
+ {
+ ret = rc_url_get_unlock_list(coro->url, sizeof(coro->url), coro->settings->arrays.cheevos_username, cheevos_locals.token, coro->gameid, i);
+
+ if (ret < 0)
+ {
+ CHEEVOS_ERR(CHEEVOS_TAG "buffer too small to create URL\n");
+ CORO_STOP();
+ }
+
+ cheevos_log_url(CHEEVOS_TAG "rc_url_get_unlock_list: %s\n", coro->url);
+ CORO_GOSUB(HTTP_GET);
+
+ if (coro->json)
+ {
+ mode = i == 0 ? CHEEVOS_ACTIVE_SOFTCORE : CHEEVOS_ACTIVE_HARDCORE;
+ cheevos_deactivate_unlocks(coro->json, cheevos_unlock_cb, &mode);
+ CHEEVOS_FREE(coro->json);
+ }
+ else
+ CHEEVOS_ERR(CHEEVOS_TAG "error retrieving list of unlocked achievements in softcore mode\n");
+ }
}
- else
- CHEEVOS_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in softcore mode.\n");
- /* Deactivate achievements in hardcore mode. */
- snprintf(
- coro->url, sizeof(coro->url),
- "http://retroachievements.org/dorequest.php?r=unlocks&u=%s&t=%s&g=%u&h=1",
- coro->settings->arrays.cheevos_username,
- cheevos_locals.token, coro->gameid
- );
-
- coro->url[sizeof(coro->url) - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to get the list of unlocked cheevos in hardcore: %s\n", coro->url);
-#endif
-
- CORO_GOSUB(HTTP_GET);
-
- if (coro->json)
- {
- if (!cheevos_deactivate_unlocks(coro->json, CHEEVOS_ACTIVE_HARDCORE))
- CHEEVOS_LOG("[CHEEVOS]: deactivated unlocked achievements in hardcore mode.\n");
- else
- CHEEVOS_ERR("[CHEEVOS]: error deactivating unlocked achievements in hardcore mode.\n");
-
- if ((void*)coro->json)
- free((void*)coro->json);
- }
- else
- CHEEVOS_ERR("[CHEEVOS]: error retrieving list of unlocked achievements in hardcore mode.\n");
-
-#endif
CORO_RET();
/**************************************************************************
@@ -3595,23 +1990,19 @@ found:
);
coro->url[sizeof(coro->url) - 1] = 0;
-
-#ifdef CHEEVOS_LOG_URLS
- cheevos_log_url("[CHEEVOS]: url to post the 'playing' activity: %s\n", coro->url);
-#endif
+ cheevos_log_url(CHEEVOS_TAG "url to post the 'playing' activity: %s\n", coro->url);
CORO_GOSUB(HTTP_GET);
if (coro->json)
{
- CHEEVOS_LOG("[CHEEVOS]: posted playing activity.\n");
- if ((void*)coro->json)
- free((void*)coro->json);
+ CHEEVOS_LOG(CHEEVOS_TAG "posted playing activity\n");
+ CHEEVOS_FREE(coro->json);
}
else
- CHEEVOS_ERR("[CHEEVOS]: error posting playing activity.\n");
+ CHEEVOS_ERR(CHEEVOS_TAG "error posting playing activity\n");
- CHEEVOS_LOG("[CHEEVOS]: posted playing activity.\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "posted playing activity\n");
CORO_RET();
CORO_LEAVE();
@@ -3634,20 +2025,16 @@ static void cheevos_task_handler(retro_task_t *task)
if (task_get_cancelled(task))
{
- CHEEVOS_LOG("[CHEEVOS]: Load task cancelled\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "Load task cancelled\n");
}
else
{
- CHEEVOS_LOG("[CHEEVOS]: Load task finished\n");
+ CHEEVOS_LOG(CHEEVOS_TAG "Load task finished\n");
}
- if (coro->data)
- free(coro->data);
-
- if ((void*)coro->path)
- free((void*)coro->path);
-
- free((void*)coro);
+ CHEEVOS_FREE(coro->data);
+ CHEEVOS_FREE(coro->path);
+ CHEEVOS_FREE(coro);
}
}
@@ -3672,8 +2059,7 @@ bool cheevos_load(const void *data)
if (!task)
{
- if ((void*)coro)
- free((void*)coro);
+ CHEEVOS_FREE(coro);
return false;
}
@@ -3686,17 +2072,15 @@ bool cheevos_load(const void *data)
coro->len = info->size;
/* size limit */
- if (coro->len > size_in_megabytes(64))
- coro->len = size_in_megabytes(64);
+ if (coro->len > CHEEVOS_MB(64))
+ coro->len = CHEEVOS_MB(64);
coro->data = malloc(coro->len);
if (!coro->data)
{
- if ((void*)task)
- free((void*)task);
- if ((void*)coro)
- free((void*)coro);
+ CHEEVOS_FREE(task);
+ CHEEVOS_FREE(coro);
return false;
}
diff --git a/cheevos/cheevos.h b/cheevos/cheevos.h
index 45c799dfe3..cfca893388 100644
--- a/cheevos/cheevos.h
+++ b/cheevos/cheevos.h
@@ -13,45 +13,20 @@
* If not, see .
*/
-#ifndef __RARCH_CHEEVOS_H
-#define __RARCH_CHEEVOS_H
+#ifndef __RARCH_CHEEVOS_CHEEVOS_H
+#define __RARCH_CHEEVOS_CHEEVOS_H
#include
#include
#include
+#include "../verbosity.h"
+
#include
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]: "
-
-#ifdef CHEEVOS_VERBOSE
-
-#define CHEEVOS_LOG RARCH_LOG
-#define CHEEVOS_ERR RARCH_ERR
-
-#else
-
-void cheevos_log(const char *fmt, ...);
-
-#define CHEEVOS_LOG cheevos_log
-#define CHEEVOS_ERR cheevos_log
-
-#endif
-
typedef struct cheevos_ctx_desc
{
unsigned idx;
@@ -59,75 +34,12 @@ typedef struct cheevos_ctx_desc
size_t len;
} cheevos_ctx_desc_t;
-typedef enum
-{
- CHEEVOS_CONSOLE_NONE = 0,
- /* 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_PLAYSTATION = 12,
- CHEEVOS_CONSOLE_ATARI_LYNX = 13,
- CHEEVOS_CONSOLE_NEOGEO_POCKET = 14,
- CHEEVOS_CONSOLE_GAME_GEAR = 15,
- CHEEVOS_CONSOLE_GAMECUBE = 16,
- CHEEVOS_CONSOLE_ATARI_JAGUAR = 17,
- CHEEVOS_CONSOLE_NINTENDO_DS = 18,
- CHEEVOS_CONSOLE_WII = 19,
- CHEEVOS_CONSOLE_WII_U = 20,
- CHEEVOS_CONSOLE_PLAYSTATION_2 = 21,
- CHEEVOS_CONSOLE_XBOX = 22,
- CHEEVOS_CONSOLE_SKYNET = 23,
- CHEEVOS_CONSOLE_XBOX_ONE = 24,
- CHEEVOS_CONSOLE_ATARI_2600 = 25,
- CHEEVOS_CONSOLE_MS_DOS = 26,
- CHEEVOS_CONSOLE_ARCADE = 27,
- CHEEVOS_CONSOLE_VIRTUAL_BOY = 28,
- CHEEVOS_CONSOLE_MSX = 29,
- CHEEVOS_CONSOLE_COMMODORE_64 = 30,
- CHEEVOS_CONSOLE_ZX81 = 31
-} cheevos_console_t;
-
-enum
-{
- CHEEVOS_DIRTY_TITLE = 1 << 0,
- CHEEVOS_DIRTY_DESC = 1 << 1,
- CHEEVOS_DIRTY_POINTS = 1 << 2,
- CHEEVOS_DIRTY_AUTHOR = 1 << 3,
- CHEEVOS_DIRTY_ID = 1 << 4,
- CHEEVOS_DIRTY_BADGE = 1 << 5,
- CHEEVOS_DIRTY_CONDITIONS = 1 << 6,
- CHEEVOS_DIRTY_VOTES = 1 << 7,
- CHEEVOS_DIRTY_DESCRIPTION = 1 << 8,
-
- CHEEVOS_DIRTY_ALL = (1 << 9) - 1
-};
-
enum
{
CHEEVOS_ACTIVE_SOFTCORE = 1 << 0,
CHEEVOS_ACTIVE_HARDCORE = 1 << 1
};
-enum
-{
- CHEEVOS_FORMAT_FRAMES = 0,
- CHEEVOS_FORMAT_SECS,
- CHEEVOS_FORMAT_MILLIS,
- CHEEVOS_FORMAT_SCORE,
- CHEEVOS_FORMAT_VALUE,
- CHEEVOS_FORMAT_OTHER
-};
-
bool cheevos_load(const void *data);
void cheevos_reset_game(void);
@@ -150,7 +62,7 @@ void cheevos_set_support_cheevos(bool state);
bool cheevos_get_support_cheevos(void);
-cheevos_console_t cheevos_get_console(void);
+int cheevos_get_console(void);
extern bool cheevos_loaded;
extern bool cheevos_hardcore_active;
@@ -160,4 +72,4 @@ extern int cheats_were_enabled;
RETRO_END_DECLS
-#endif /* __RARCH_CHEEVOS_H */
+#endif /* __RARCH_CHEEVOS_CHEEVOS_H */
diff --git a/cheevos/cond.c b/cheevos/cond.c
deleted file mode 100644
index 9f665900c9..0000000000
--- a/cheevos/cond.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/* 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 "cond.h"
-#include "var.h"
-
-#include "../retroarch.h"
-#include "../verbosity.h"
-
-/*****************************************************************************
-Parsing
-*****************************************************************************/
-
-static cheevos_cond_op_t cheevos_cond_parse_operator(const char** memaddr)
-{
- const char *str = *memaddr;
- cheevos_cond_op_t op;
-
- if (*str == '=' && str[1] == '=')
- {
- op = CHEEVOS_COND_OP_EQUALS;
- str += 2;
- }
- else if (*str == '=')
- {
- op = CHEEVOS_COND_OP_EQUALS;
- str++;
- }
- else if (*str == '!' && str[1] == '=')
- {
- op = CHEEVOS_COND_OP_NOT_EQUAL_TO;
- str += 2;
- }
- else if (*str == '<' && str[1] == '=')
- {
- op = CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL;
- str += 2;
- }
- else if (*str == '<')
- {
- op = CHEEVOS_COND_OP_LESS_THAN;
- str++;
- }
- else if (*str == '>' && str[1] == '=')
- {
- op = CHEEVOS_COND_OP_GREATER_THAN_OR_EQUAL;
- str += 2;
- }
- else if (*str == '>')
- {
- op = CHEEVOS_COND_OP_GREATER_THAN;
- str++;
- }
- else
- {
- CHEEVOS_ERR(CHEEVOS_TAG "unknown operator %c\n.", *str);
- op = CHEEVOS_COND_OP_EQUALS;
- }
-
- *memaddr = str;
- return op;
-}
-
-void cheevos_cond_parse(cheevos_cond_t* cond, const char** memaddr)
-{
- const char* str = *memaddr;
- cond->type = CHEEVOS_COND_TYPE_STANDARD;
-
- if (*str != 0 && str[1] == ':')
- {
- int skip = 2;
-
- switch (*str)
- {
- case 'R':
- cond->type = CHEEVOS_COND_TYPE_RESET_IF;
- break;
- case 'P':
- cond->type = CHEEVOS_COND_TYPE_PAUSE_IF;
- break;
- case 'A':
- cond->type = CHEEVOS_COND_TYPE_ADD_SOURCE;
- break;
- case 'B':
- cond->type = CHEEVOS_COND_TYPE_SUB_SOURCE;
- break;
- case 'C':
- cond->type = CHEEVOS_COND_TYPE_ADD_HITS;
- break;
- default:
- skip = 0;
- break;
- }
-
- str += skip;
- }
-
- cheevos_var_parse(&cond->source, &str);
- cond->op = cheevos_cond_parse_operator(&str);
- cheevos_var_parse(&cond->target, &str);
- cond->curr_hits = 0;
-
- if (*str == '(' || *str == '.')
- {
- char* end;
- cond->req_hits = (unsigned)strtol(str + 1, &end, 10);
- str = end + (*end == ')' || *end == '.');
- }
- else
- cond->req_hits = 0;
-
- *memaddr = str;
-}
-
-unsigned cheevos_cond_count_in_set(const char* memaddr, unsigned which)
-{
- cheevos_cond_t dummy;
- unsigned index = 0;
- unsigned count = 0;
-
- for (;;)
- {
- for (;;)
- {
- cheevos_cond_parse(&dummy, &memaddr);
-
- if (index == which)
- count++;
-
- if (*memaddr != '_')
- break;
-
- memaddr++;
- }
-
- index++;
-
- if (*memaddr != 'S')
- break;
-
- memaddr++;
- }
-
- return count;
-}
-
-void cheevos_cond_parse_in_set(cheevos_cond_t* cond, const char* memaddr, unsigned which)
-{
- cheevos_cond_t dummy;
- unsigned index = 0;
-
- for (;;)
- {
- for (;;)
- {
- if (index == which)
- {
- cheevos_cond_parse(cond, &memaddr);
- cond++;
- }
- else
- cheevos_cond_parse(&dummy, &memaddr);
-
- if (*memaddr != '_')
- break;
-
- memaddr++;
- }
-
- index++;
-
- if (*memaddr != 'S')
- break;
-
- memaddr++;
- }
-}
diff --git a/cheevos/cond.h b/cheevos/cond.h
deleted file mode 100644
index ee7cb668a1..0000000000
--- a/cheevos/cond.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* 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_COND_H
-#define __RARCH_CHEEVOS_COND_H
-
-#include "var.h"
-
-#include
-
-RETRO_BEGIN_DECLS
-
-typedef enum
-{
- CHEEVOS_COND_TYPE_STANDARD,
- CHEEVOS_COND_TYPE_PAUSE_IF,
- CHEEVOS_COND_TYPE_RESET_IF,
- CHEEVOS_COND_TYPE_ADD_SOURCE,
- CHEEVOS_COND_TYPE_SUB_SOURCE,
- CHEEVOS_COND_TYPE_ADD_HITS
-} cheevos_cond_type_t;
-
-typedef enum
-{
- CHEEVOS_COND_OP_EQUALS,
- CHEEVOS_COND_OP_LESS_THAN,
- CHEEVOS_COND_OP_LESS_THAN_OR_EQUAL,
- CHEEVOS_COND_OP_GREATER_THAN,
- CHEEVOS_COND_OP_GREATER_THAN_OR_EQUAL,
- CHEEVOS_COND_OP_NOT_EQUAL_TO
-} cheevos_cond_op_t;
-
-typedef struct
-{
- cheevos_cond_type_t type;
- unsigned req_hits;
- unsigned curr_hits;
- char pause;
-
- cheevos_var_t source;
- cheevos_cond_op_t op;
- cheevos_var_t target;
-} cheevos_cond_t;
-
-void cheevos_cond_parse(cheevos_cond_t* cond, const char** memaddr);
-unsigned cheevos_cond_count_in_set(const char* memaddr, unsigned which);
-void cheevos_cond_parse_in_set(cheevos_cond_t* cond, const char* memaddr, unsigned which);
-
-RETRO_END_DECLS
-
-#endif /* __RARCH_CHEEVOS_COND_H */
diff --git a/cheevos/coro.h b/cheevos/coro.h
index e30616b5f2..a9eb7a4c8b 100644
--- a/cheevos/coro.h
+++ b/cheevos/coro.h
@@ -1,5 +1,5 @@
-#ifndef CORO_H
-#define CORO_H
+#ifndef __RARCH_CHEEVOS_CORO_H
+#define __RARCH_CHEEVOS_CORO_H
/*
Released under the CC0: https://creativecommons.org/publicdomain/zero/1.0/
@@ -72,4 +72,4 @@ Released under the CC0: https://creativecommons.org/publicdomain/zero/1.0/
int step, sp; \
int stack[ 8 ];
-#endif /* CORO_H */
+#endif /* __RARCH_CHEEVOS_CORO_H */
diff --git a/cheevos/fixup.c b/cheevos/fixup.c
new file mode 100644
index 0000000000..ea1109adbc
--- /dev/null
+++ b/cheevos/fixup.c
@@ -0,0 +1,264 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - 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 "fixup.h"
+#include "cheevos.h"
+#include "util.h"
+
+#include "../retroarch.h"
+#include "../core.h"
+
+#include
+
+static int cheevos_cmpaddr(const void* e1, const void* e2)
+{
+ const cheevos_fixup_t* f1 = (const cheevos_fixup_t*)e1;
+ const cheevos_fixup_t* f2 = (const cheevos_fixup_t*)e2;
+
+ if (f1->address < f2->address)
+ {
+ return -1;
+ }
+ else if (f1->address > f2->address)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+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_fixup_init(cheevos_fixups_t* fixups)
+{
+ fixups->elements = NULL;
+ fixups->capacity = fixups->count = 0;
+ fixups->dirty = false;
+}
+
+void cheevos_fixup_destroy(cheevos_fixups_t* fixups)
+{
+ CHEEVOS_FREE(fixups->elements);
+ cheevos_fixup_init(fixups);
+}
+
+const uint8_t* cheevos_fixup_find(cheevos_fixups_t* fixups, unsigned address, int console)
+{
+ cheevos_fixup_t key;
+ cheevos_fixup_t* found;
+ const uint8_t* location;
+
+ if (fixups->dirty)
+ {
+ qsort(fixups->elements, fixups->count, sizeof(cheevos_fixup_t), cheevos_cmpaddr);
+ fixups->dirty = false;
+ }
+
+ key.address = address;
+ found = (cheevos_fixup_t*)bsearch(&key, fixups->elements, fixups->count, sizeof(cheevos_fixup_t), cheevos_cmpaddr);
+
+ if (found != NULL)
+ {
+ return found->location;
+ }
+
+ if (fixups->count == fixups->capacity)
+ {
+ unsigned new_capacity = fixups->capacity == 0 ? 16 : fixups->capacity * 2;
+ cheevos_fixup_t* new_elements = (cheevos_fixup_t*)
+ realloc(fixups->elements, new_capacity * sizeof(cheevos_fixup_t));
+
+ if (new_elements == NULL)
+ {
+ return NULL;
+ }
+
+ fixups->elements = new_elements;
+ fixups->capacity = new_capacity;
+ }
+
+ fixups->elements[fixups->count].address = address;
+ fixups->elements[fixups->count++].location = location =
+ cheevos_patch_address(address, console);
+ fixups->dirty = true;
+
+ return location;
+}
+
+const uint8_t* cheevos_patch_address(unsigned address, int console)
+{
+ rarch_system_info_t* system = runloop_get_system_info();
+ const void* pointer = NULL;
+
+ if (console == RC_CONSOLE_NINTENDO)
+ {
+ if (address >= 0x0800 && address < 0x2000)
+ {
+ /* Address in the mirrorred RAM, adjust to real RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "NES memory address in mirrorred RAM %X, adjusted to %X\n", address, address & 0x07ff);
+ address &= 0x07ff;
+ }
+ }
+ else if (console == RC_CONSOLE_GAMEBOY_COLOR)
+ {
+ if (address >= 0xe000 && address <= 0xfdff)
+ {
+ /* Address in the echo RAM, adjust to real RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "GBC memory address in echo RAM %X, adjusted to %X\n", address, address - 0x2000);
+ address -= 0x2000;
+ }
+ }
+
+ if (system->mmaps.num_descriptors != 0)
+ {
+ /* We have memory descriptors, use it. */
+ 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 == RC_CONSOLE_GAMEBOY_ADVANCE)
+ {
+ if (address < 0x8000)
+ {
+ /* Internal RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "GBA memory address %X adjusted to %X\n", address, address + 0x3000000);
+ address += 0x3000000;
+ }
+ else
+ {
+ /* Work RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "GBA memory address %X adjusted to %X\n", address, address + 0x2000000 - 0x8000);
+ address += 0x2000000 - 0x8000;
+ }
+ }
+ else if (console == RC_CONSOLE_PC_ENGINE)
+ {
+ /* RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "PCE memory address %X adjusted to %X\n", address, address + 0x1f0000);
+ address += 0x1f0000;
+ }
+ else if (console == RC_CONSOLE_SUPER_NINTENDO)
+ {
+ if (address < 0x020000)
+ {
+ /* Work RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "SNES memory address %X adjusted to %X\n", address, address + 0x7e0000);
+ address += 0x7e0000;
+ }
+ else
+ {
+ /* Save RAM. */
+ CHEEVOS_LOG(CHEEVOS_TAG "SNES memory address %X adjusted to %X\n", address, address + 0x006000 - 0x020000);
+ address += 0x006000 - 0x020000;
+ }
+ }
+
+ desc = system->mmaps.descriptors;
+ end = desc + system->mmaps.num_descriptors;
+
+ for (; desc < end; desc++)
+ {
+ if (((desc->core.start ^ address) & desc->core.select) == 0)
+ {
+ unsigned addr = address;
+ pointer = desc->core.ptr;
+
+ address = (unsigned)cheevos_var_reduce(
+ (addr - desc->core.start) & desc->disconnect_mask,
+ desc->core.disconnect);
+
+ if (address >= desc->core.len)
+ address -= cheevos_var_highest_bit(address);
+
+ address += desc->core.offset;
+
+ CHEEVOS_LOG(CHEEVOS_TAG "address %X set to descriptor %d at offset %X\n", addr, (int)((desc - system->mmaps.descriptors) + 1), address);
+ 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 (address < meminfo.size)
+ {
+ pointer = meminfo.data;
+ break;
+ }
+
+ /**
+ * HACK Subtract the correct amount of bytes to reach the save RAM as
+ * it's size is not always set correctly in the core.
+ */
+ if (i == 0 && console == RC_CONSOLE_NINTENDO)
+ address -= 0x6000;
+ else
+ address -= meminfo.size;
+ }
+ }
+
+ if (pointer == NULL)
+ {
+ return NULL;
+ }
+
+ return (const uint8_t*)pointer + address;
+}
diff --git a/cheevos/fixup.h b/cheevos/fixup.h
new file mode 100644
index 0000000000..41893a0ba8
--- /dev/null
+++ b/cheevos/fixup.h
@@ -0,0 +1,48 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - 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_FIXUP_H
+#define __RARCH_CHEEVOS_FIXUP_H
+
+#include
+#include
+
+#include
+
+RETRO_BEGIN_DECLS
+
+typedef struct
+{
+ unsigned address;
+ const uint8_t* location;
+} cheevos_fixup_t;
+
+typedef struct
+{
+ cheevos_fixup_t* elements;
+ unsigned capacity, count;
+ bool dirty;
+} cheevos_fixups_t;
+
+void cheevos_fixup_init(cheevos_fixups_t* fixups);
+void cheevos_fixup_destroy(cheevos_fixups_t* fixups);
+
+const uint8_t* cheevos_fixup_find(cheevos_fixups_t* fixups, unsigned address, int console);
+
+const uint8_t* cheevos_patch_address(unsigned address, int console);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/cheevos/hash.c b/cheevos/hash.c
new file mode 100644
index 0000000000..7b84bbbc62
--- /dev/null
+++ b/cheevos/hash.c
@@ -0,0 +1,12 @@
+#include "hash.h"
+
+uint32_t cheevos_djb2(const char* str, size_t length)
+{
+ const unsigned char* aux = (const unsigned char*)str;
+ uint32_t hash = 5381;
+
+ while (length--)
+ hash = ( hash << 5 ) + hash + *aux++;
+
+ return hash;
+}
diff --git a/cheevos/hash.h b/cheevos/hash.h
new file mode 100644
index 0000000000..ec6e802947
--- /dev/null
+++ b/cheevos/hash.h
@@ -0,0 +1,30 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - 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_HASH_H
+#define __RARCH_CHEEVOS_HASH_H
+
+#include
+#include
+
+#include
+
+RETRO_BEGIN_DECLS
+
+uint32_t cheevos_djb2(const char* str, size_t length);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/cheevos/parser.c b/cheevos/parser.c
new file mode 100644
index 0000000000..cca2e2281a
--- /dev/null
+++ b/cheevos/parser.c
@@ -0,0 +1,674 @@
+#include "parser.h"
+
+#include "hash.h"
+#include "util.h"
+
+#include
+#include
+#include
+
+/* C89 wants only int values in enums. */
+#define CHEEVOS_JSON_KEY_GAMEID 0xb4960eecU
+#define CHEEVOS_JSON_KEY_ACHIEVEMENTS 0x69749ae1U
+#define CHEEVOS_JSON_KEY_ID 0x005973f2U
+#define CHEEVOS_JSON_KEY_MEMADDR 0x1e76b53fU
+#define CHEEVOS_JSON_KEY_TITLE 0x0e2a9a07U
+#define CHEEVOS_JSON_KEY_DESCRIPTION 0xe61a1f69U
+#define CHEEVOS_JSON_KEY_POINTS 0xca8fce22U
+#define CHEEVOS_JSON_KEY_AUTHOR 0xa804edb8U
+#define CHEEVOS_JSON_KEY_MODIFIED 0xdcea4fe6U
+#define CHEEVOS_JSON_KEY_CREATED 0x3a84721dU
+#define CHEEVOS_JSON_KEY_BADGENAME 0x887685d9U
+#define CHEEVOS_JSON_KEY_CONSOLE_ID 0x071656e5U
+#define CHEEVOS_JSON_KEY_TOKEN 0x0e2dbd26U
+#define CHEEVOS_JSON_KEY_FLAGS 0x0d2e96b2U
+#define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU
+#define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U
+#define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU
+#define CHEEVOS_JSON_KEY_SUCCESS 0x110461deU
+#define CHEEVOS_JSON_KEY_ERROR 0x0d2011cfU
+
+/*****************************************************************************
+Gets a value in a JSON
+*****************************************************************************/
+
+typedef struct
+{
+ unsigned key_hash;
+ int is_key;
+ const char* value;
+ size_t length;
+} cheevos_getvalueud_t;
+
+static int cheevos_getvalue_key(void* userdata,
+ const char* name, size_t length)
+{
+ cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
+
+ ud->is_key = cheevos_djb2(name, length) == ud->key_hash;
+ return 0;
+}
+
+static int cheevos_getvalue_string(void* userdata,
+ const char* string, size_t length)
+{
+ cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
+
+ if (ud->is_key)
+ {
+ ud->value = string;
+ ud->length = length;
+ ud->is_key = 0;
+ }
+
+ return 0;
+}
+
+static int cheevos_getvalue_boolean(void* userdata, int istrue)
+{
+ cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
+
+ if (ud->is_key)
+ {
+ if (istrue)
+ {
+ ud->value = "true";
+ ud->length = 4;
+ }
+ else
+ {
+ ud->value = "false";
+ ud->length = 5;
+ }
+
+ ud->is_key = 0;
+ }
+
+ return 0;
+}
+
+static int cheevos_getvalue_null(void* userdata)
+{
+ cheevos_getvalueud_t* ud = (cheevos_getvalueud_t*)userdata;
+
+ if (ud->is_key )
+ {
+ ud->value = "null";
+ ud->length = 4;
+ ud->is_key = 0;
+ }
+
+ return 0;
+}
+
+static int cheevos_get_value(const char* json, unsigned key_hash,
+ char* value, size_t length)
+{
+ static const jsonsax_handlers_t handlers =
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cheevos_getvalue_key,
+ NULL,
+ cheevos_getvalue_string,
+ cheevos_getvalue_string, /* number */
+ cheevos_getvalue_boolean,
+ cheevos_getvalue_null
+ };
+
+ cheevos_getvalueud_t ud;
+
+ ud.key_hash = key_hash;
+ ud.is_key = 0;
+ ud.value = NULL;
+ ud.length = 0;
+ *value = 0;
+
+ if ((jsonsax_parse(json, &handlers, (void*)&ud) == JSONSAX_OK)
+ && ud.value && ud.length < length)
+ {
+ strlcpy(value, ud.value, ud.length + 1);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*****************************************************************************
+Returns the token of the error message
+*****************************************************************************/
+
+int cheevos_get_token(const char* json, char* token, size_t length)
+{
+ cheevos_get_value(json, CHEEVOS_JSON_KEY_ERROR, token, length);
+
+ if (!string_is_empty(token))
+ return -1;
+
+ return cheevos_get_value(json, CHEEVOS_JSON_KEY_TOKEN, token, length);
+}
+
+/*****************************************************************************
+Count number of achievements in a JSON file
+*****************************************************************************/
+
+typedef struct
+{
+ int in_cheevos;
+ int in_lboards;
+ uint32_t field_hash;
+ unsigned core_count;
+ unsigned unofficial_count;
+ unsigned lboard_count;
+} cheevos_countud_t;
+
+static int cheevos_count_end_array(void* userdata)
+{
+ cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
+
+ ud->in_cheevos = 0;
+ ud->in_lboards = 0;
+ return 0;
+}
+
+static int cheevos_count_key(void* userdata,
+ const char* name, size_t length)
+{
+ cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
+
+ ud->field_hash = cheevos_djb2(name, length);
+
+ if (ud->field_hash == CHEEVOS_JSON_KEY_ACHIEVEMENTS)
+ ud->in_cheevos = 1;
+ else if (ud->field_hash == CHEEVOS_JSON_KEY_LEADERBOARDS)
+ ud->in_lboards = 1;
+
+ return 0;
+}
+
+static int cheevos_count_number(void* userdata,
+ const char* number, size_t length)
+{
+ cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
+
+ if (ud->in_cheevos && ud->field_hash == CHEEVOS_JSON_KEY_FLAGS)
+ {
+ long flags = strtol(number, NULL, 10);
+
+ if (flags == 3)
+ ud->core_count++; /* Core achievements */
+ else if (flags == 5)
+ ud->unofficial_count++; /* Unofficial achievements */
+ }
+ else if (ud->in_lboards && ud->field_hash == CHEEVOS_JSON_KEY_ID)
+ ud->lboard_count++;
+
+ return 0;
+}
+
+static int cheevos_count_cheevos(const char* json,
+ unsigned* core_count, unsigned* unofficial_count,
+ unsigned* lboard_count)
+{
+ static const jsonsax_handlers_t handlers =
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cheevos_count_end_array,
+ cheevos_count_key,
+ NULL,
+ NULL,
+ cheevos_count_number,
+ NULL,
+ NULL
+ };
+
+ int res;
+ cheevos_countud_t ud;
+ ud.in_cheevos = 0;
+ ud.in_lboards = 0;
+ ud.core_count = 0;
+ ud.unofficial_count = 0;
+ ud.lboard_count = 0;
+
+ res = jsonsax_parse(json, &handlers, (void*)&ud);
+
+ *core_count = ud.core_count;
+ *unofficial_count = ud.unofficial_count;
+ *lboard_count = ud.lboard_count;
+
+ return res;
+}
+
+/*****************************************************************************
+Parses the cheevos in the JSON
+*****************************************************************************/
+
+typedef struct
+{
+ const char* string;
+ size_t length;
+} cheevos_field_t;
+
+typedef struct
+{
+ int in_cheevos;
+ int in_lboards;
+ int is_console_id;
+ unsigned core_count;
+ unsigned unofficial_count;
+ unsigned lboard_count;
+
+ cheevos_field_t* field;
+ cheevos_field_t id, memaddr, title, desc, points, author;
+ cheevos_field_t modified, created, badge, flags, format;
+
+ cheevos_rapatchdata_t* patchdata;
+} cheevos_readud_t;
+
+static const char* cheevos_dupstr(const cheevos_field_t* field)
+{
+ char* string = (char*)malloc(field->length + 1);
+
+ if (!string)
+ return NULL;
+
+ memcpy((void*)string, (void*)field->string, field->length);
+ string[field->length] = 0;
+ return string;
+}
+
+static int cheevos_new_cheevo(cheevos_readud_t* ud)
+{
+ cheevos_racheevo_t* cheevo = NULL;
+ unsigned flags = (unsigned)strtol(ud->flags.string, NULL, 10);
+
+ if (flags == 3)
+ cheevo = ud->patchdata->core + ud->core_count++;
+ else if (flags == 5)
+ cheevo = ud->patchdata->unofficial + ud->unofficial_count++;
+ else
+ return 0;
+
+ cheevo->title = cheevos_dupstr(&ud->title);
+ cheevo->description = cheevos_dupstr(&ud->desc);
+ cheevo->badge = cheevos_dupstr(&ud->badge);
+ cheevo->memaddr = cheevos_dupstr(&ud->memaddr);
+ cheevo->points = (unsigned)strtol(ud->points.string, NULL, 10);
+ cheevo->id = (unsigned)strtol(ud->id.string, NULL, 10);
+
+ if ( !cheevo->title
+ || !cheevo->description
+ || !cheevo->badge
+ || !cheevo->memaddr)
+ {
+ CHEEVOS_FREE(cheevo->title);
+ CHEEVOS_FREE(cheevo->description);
+ CHEEVOS_FREE(cheevo->badge);
+ CHEEVOS_FREE(cheevo->memaddr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cheevos_new_lboard(cheevos_readud_t* ud)
+{
+ cheevos_ralboard_t* lboard = ud->patchdata->lboards + ud->lboard_count++;
+
+ lboard->title = cheevos_dupstr(&ud->title);
+ lboard->description = cheevos_dupstr(&ud->desc);
+ lboard->format = cheevos_dupstr(&ud->format);
+ lboard->mem = cheevos_dupstr(&ud->memaddr);
+ lboard->id = (unsigned)strtol(ud->id.string, NULL, 10);
+
+ if ( !lboard->title
+ || !lboard->description
+ || !lboard->format
+ || !lboard->mem)
+ {
+ CHEEVOS_FREE(lboard->title);
+ CHEEVOS_FREE(lboard->description);
+ CHEEVOS_FREE(lboard->format);
+ CHEEVOS_FREE(lboard->mem);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cheevos_read_end_object(void* userdata)
+{
+ cheevos_readud_t* ud = (cheevos_readud_t*)userdata;
+
+ if (ud->in_cheevos)
+ return cheevos_new_cheevo(ud);
+
+ if (ud->in_lboards)
+ return cheevos_new_lboard(ud);
+
+ return 0;
+}
+
+static int cheevos_read_end_array(void* userdata)
+{
+ cheevos_readud_t* ud = (cheevos_readud_t*)userdata;
+
+ ud->in_cheevos = 0;
+ ud->in_lboards = 0;
+ return 0;
+}
+
+static int cheevos_read_key(void* userdata,
+ const char* name, size_t length)
+{
+ cheevos_readud_t* ud = (cheevos_readud_t*)userdata;
+
+ int common = ud->in_cheevos || ud->in_lboards;
+ uint32_t hash = cheevos_djb2(name, length);
+ ud->field = NULL;
+
+ switch (hash)
+ {
+ case CHEEVOS_JSON_KEY_ACHIEVEMENTS:
+ ud->in_cheevos = 1;
+ break;
+ case CHEEVOS_JSON_KEY_LEADERBOARDS:
+ ud->in_lboards = 1;
+ break;
+ case CHEEVOS_JSON_KEY_CONSOLE_ID:
+ ud->is_console_id = 1;
+ break;
+ case CHEEVOS_JSON_KEY_ID:
+ if (common)
+ ud->field = &ud->id;
+ break;
+ case CHEEVOS_JSON_KEY_MEMADDR:
+ if (ud->in_cheevos)
+ ud->field = &ud->memaddr;
+ break;
+ case CHEEVOS_JSON_KEY_MEM:
+ if (ud->in_lboards)
+ ud->field = &ud->memaddr;
+ break;
+ case CHEEVOS_JSON_KEY_TITLE:
+ if (common)
+ ud->field = &ud->title;
+ break;
+ case CHEEVOS_JSON_KEY_DESCRIPTION:
+ if (common)
+ ud->field = &ud->desc;
+ break;
+ case CHEEVOS_JSON_KEY_POINTS:
+ if (ud->in_cheevos)
+ ud->field = &ud->points;
+ break;
+ case CHEEVOS_JSON_KEY_AUTHOR:
+ if (ud->in_cheevos)
+ ud->field = &ud->author;
+ break;
+ case CHEEVOS_JSON_KEY_MODIFIED:
+ if (ud->in_cheevos)
+ ud->field = &ud->modified;
+ break;
+ case CHEEVOS_JSON_KEY_CREATED:
+ if (ud->in_cheevos)
+ ud->field = &ud->created;
+ break;
+ case CHEEVOS_JSON_KEY_BADGENAME:
+ if (ud->in_cheevos)
+ ud->field = &ud->badge;
+ break;
+ case CHEEVOS_JSON_KEY_FLAGS:
+ if (ud->in_cheevos)
+ ud->field = &ud->flags;
+ break;
+ case CHEEVOS_JSON_KEY_FORMAT:
+ if (ud->in_lboards)
+ ud->field = &ud->format;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cheevos_read_string(void* userdata,
+ const char* string, size_t length)
+{
+ cheevos_readud_t* ud = (cheevos_readud_t*)userdata;
+
+ if (ud->field)
+ {
+ ud->field->string = string;
+ ud->field->length = length;
+ }
+
+ return 0;
+}
+
+static int cheevos_read_number(void* userdata,
+ const char* number, size_t length)
+{
+ cheevos_readud_t* ud = (cheevos_readud_t*)userdata;
+
+ if (ud->field)
+ {
+ ud->field->string = number;
+ ud->field->length = length;
+ }
+ else if (ud->is_console_id)
+ {
+ ud->patchdata->console_id = strtol(number, NULL, 10);
+ ud->is_console_id = 0;
+ }
+
+ return 0;
+}
+
+int cheevos_get_patchdata(const char* json, cheevos_rapatchdata_t* patchdata)
+{
+ static const jsonsax_handlers_t handlers =
+ {
+ NULL,
+ NULL,
+ NULL,
+ cheevos_read_end_object,
+ NULL,
+ cheevos_read_end_array,
+ cheevos_read_key,
+ NULL,
+ cheevos_read_string,
+ cheevos_read_number,
+ NULL,
+ NULL
+ };
+
+ cheevos_readud_t ud;
+ int res;
+
+ /* Count the number of achievements in the JSON file. */
+ res = cheevos_count_cheevos(json, &patchdata->core_count,
+ &patchdata->unofficial_count, &patchdata->lboard_count);
+
+ if (res != JSONSAX_OK)
+ return -1;
+
+ /* Allocate the achievements. */
+
+ patchdata->core = (cheevos_racheevo_t*)
+ calloc(patchdata->core_count, sizeof(cheevos_racheevo_t));
+
+ patchdata->unofficial = (cheevos_racheevo_t*)
+ calloc(patchdata->unofficial_count, sizeof(cheevos_racheevo_t));
+
+ patchdata->lboards = (cheevos_ralboard_t*)
+ calloc(patchdata->lboard_count, sizeof(cheevos_ralboard_t));
+
+ if (!patchdata->core ||
+ !patchdata->unofficial ||
+ !patchdata->lboards)
+ {
+ CHEEVOS_FREE(patchdata->core);
+ CHEEVOS_FREE(patchdata->unofficial);
+ CHEEVOS_FREE(patchdata->lboards);
+
+ return -1;
+ }
+
+ /* Load the achievements. */
+ ud.in_cheevos = 0;
+ ud.in_lboards = 0;
+ ud.is_console_id = 0;
+ ud.field = NULL;
+ ud.core_count = 0;
+ ud.unofficial_count = 0;
+ ud.lboard_count = 0;
+ ud.patchdata = patchdata;
+
+ if (jsonsax_parse(json, &handlers, (void*)&ud) != JSONSAX_OK)
+ {
+ cheevos_free_patchdata(patchdata);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+Frees the patchdata
+*****************************************************************************/
+
+void cheevos_free_patchdata(cheevos_rapatchdata_t* patchdata)
+{
+ unsigned i = 0, count = 0;
+ const cheevos_racheevo_t* cheevo = NULL;
+ const cheevos_ralboard_t* lboard = NULL;
+
+ cheevo = patchdata->core;
+
+ for (i = 0, count = patchdata->core_count; i < count; i++, cheevo++)
+ {
+ CHEEVOS_FREE(cheevo->title);
+ CHEEVOS_FREE(cheevo->description);
+ CHEEVOS_FREE(cheevo->badge);
+ CHEEVOS_FREE(cheevo->memaddr);
+ }
+
+ cheevo = patchdata->unofficial;
+
+ for (i = 0, count = patchdata->unofficial_count; i < count; i++, cheevo++)
+ {
+ CHEEVOS_FREE(cheevo->title);
+ CHEEVOS_FREE(cheevo->description);
+ CHEEVOS_FREE(cheevo->badge);
+ CHEEVOS_FREE(cheevo->memaddr);
+ }
+
+ lboard = patchdata->lboards;
+
+ for (i = 0, count = patchdata->lboard_count; i < count; i++, lboard++)
+ {
+ CHEEVOS_FREE(lboard->title);
+ CHEEVOS_FREE(lboard->description);
+ CHEEVOS_FREE(lboard->format);
+ CHEEVOS_FREE(lboard->mem);
+ }
+
+ CHEEVOS_FREE(patchdata->core);
+ CHEEVOS_FREE(patchdata->unofficial);
+ CHEEVOS_FREE(patchdata->lboards);
+
+ patchdata->console_id = 0;
+ patchdata->core = NULL;
+ patchdata->unofficial = NULL;
+ patchdata->lboards = NULL;
+ patchdata->core_count = 0;
+ patchdata->unofficial_count = 0;
+ patchdata->lboard_count = 0;
+}
+
+/*****************************************************************************
+Deactivates unlocked cheevos
+*****************************************************************************/
+
+typedef struct
+{
+ int is_element;
+ cheevos_unlock_cb_t unlock_cb;
+ void* userdata;
+} cheevos_deactivate_t;
+
+static int cheevos_deactivate_index(void* userdata, unsigned int index)
+{
+ cheevos_deactivate_t* ud = (cheevos_deactivate_t*)userdata;
+
+ ud->is_element = 1;
+ return 0;
+}
+
+static int cheevos_deactivate_number(void* userdata,
+ const char* number, size_t length)
+{
+ cheevos_deactivate_t* ud = (cheevos_deactivate_t*)userdata;
+ unsigned id = 0;
+
+ if (ud->is_element)
+ {
+ ud->is_element = 0;
+ id = (unsigned)strtol(number, NULL, 10);
+
+ ud->unlock_cb(id, ud->userdata);
+ }
+
+ return 0;
+}
+
+void cheevos_deactivate_unlocks(const char* json, cheevos_unlock_cb_t unlock_cb, void* userdata)
+{
+ static const jsonsax_handlers_t handlers =
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ cheevos_deactivate_index,
+ NULL,
+ cheevos_deactivate_number,
+ NULL,
+ NULL
+ };
+
+ cheevos_deactivate_t ud;
+
+ ud.is_element = 0;
+ ud.unlock_cb = unlock_cb;
+ ud.userdata = userdata;
+
+ jsonsax_parse(json, &handlers, (void*)&ud);
+}
+
+/*****************************************************************************
+Returns the game ID
+*****************************************************************************/
+
+unsigned chevos_get_gameid(const char* json)
+{
+ char gameid[32];
+
+ if (cheevos_get_value(json, CHEEVOS_JSON_KEY_GAMEID, gameid, sizeof(gameid)) != 0)
+ return 0;
+
+ return (unsigned)strtol(gameid, NULL, 10);
+}
diff --git a/cheevos/parser.h b/cheevos/parser.h
new file mode 100644
index 0000000000..5b10de6236
--- /dev/null
+++ b/cheevos/parser.h
@@ -0,0 +1,69 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2015-2018 - 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_PARSER_H
+#define __RARCH_CHEEVOS_PARSER_H
+
+#include
+#include
+#include
+
+#include
+
+RETRO_BEGIN_DECLS
+
+typedef struct {
+ const char* title;
+ const char* description;
+ const char* badge;
+ const char* memaddr;
+ unsigned points;
+ unsigned id;
+} cheevos_racheevo_t;
+
+typedef struct {
+ const char* title;
+ const char* description;
+ const char* format;
+ const char* mem;
+ unsigned id;
+} cheevos_ralboard_t;
+
+typedef struct {
+ unsigned console_id;
+
+ cheevos_racheevo_t* core;
+ cheevos_racheevo_t* unofficial;
+ cheevos_ralboard_t* lboards;
+
+ unsigned core_count;
+ unsigned unofficial_count;
+ unsigned lboard_count;
+} cheevos_rapatchdata_t;
+
+typedef void (*cheevos_unlock_cb_t)(unsigned id, void* userdata);
+
+int cheevos_get_token(const char* json, char* token, size_t length);
+
+int cheevos_get_patchdata(const char* json, cheevos_rapatchdata_t* patchdata);
+void cheevos_free_patchdata(cheevos_rapatchdata_t* patchdata);
+
+void cheevos_deactivate_unlocks(const char* json, cheevos_unlock_cb_t unlock_cb, void* userdata);
+
+unsigned chevos_get_gameid(const char* json);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/cheevos/util.h b/cheevos/util.h
new file mode 100644
index 0000000000..4e389cf404
--- /dev/null
+++ b/cheevos/util.h
@@ -0,0 +1,53 @@
+/* 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. */
+#undef CHEEVOS_VERBOSE
+
+/*****************************************************************************
+End of setup
+*****************************************************************************/
+
+#define CHEEVOS_TAG "[CHEEVOS]: "
+#define CHEEVOS_FREE(p) do { void* q = (void*)p; if (q != NULL) free(q); } while (0)
+
+#ifdef CHEEVOS_VERBOSE
+
+#define CHEEVOS_LOG RARCH_LOG
+#define CHEEVOS_ERR RARCH_ERR
+
+#else
+
+#define CHEEVOS_LOG cheevos_log
+#define CHEEVOS_ERR RARCH_ERR
+
+void cheevos_log(const char *fmt, ...);
+
+#endif
+
+RETRO_END_DECLS
+
+#endif /* __RARCH_CHEEVOS_UTIL_H */
diff --git a/cheevos/var.c b/cheevos/var.c
deleted file mode 100644
index 5f13c2b8b6..0000000000
--- a/cheevos/var.c
+++ /dev/null
@@ -1,416 +0,0 @@
-/* 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"
-
-/*****************************************************************************
-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;
-
- var->is_bcd = false;
-
- 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 (toupper((unsigned char)*str) == 'B' && str[1] == '0' && toupper((unsigned char)str[2]) == 'X')
- {
- /* b0x (binary-coded decimal) */
- str += 3;
- var->is_bcd = true;
- var->type = CHEEVOS_VAR_TYPE_ADDRESS;
- }
- 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(
- (addr - 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 = {NULL, 0, 0};
-
- 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:
- CHEEVOS_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;
- }
-
- if(var->is_bcd)
- return (((value >> 4) & 0xf) * 10) + (value & 0xf);
- else
- return value;
-}
diff --git a/cheevos/var.h b/cheevos/var.h
deleted file mode 100644
index 336b65d4ed..0000000000
--- a/cheevos/var.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* 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;
- bool is_bcd;
- 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 46a3e51a9c..d551f502ae 100644
--- a/command.c
+++ b/command.c
@@ -41,7 +41,7 @@
#ifdef HAVE_CHEEVOS
#include "cheevos/cheevos.h"
-#include "cheevos/var.h"
+#include "cheevos/fixup.h"
#endif
#ifdef HAVE_DISCORD
@@ -262,14 +262,13 @@ static bool command_version(const char* arg)
#define SMY_CMD_STR "READ_CORE_RAM"
static bool command_read_ram(const char *arg)
{
- cheevos_var_t var;
unsigned i;
char *reply = NULL;
const uint8_t * data = NULL;
char *reply_at = NULL;
unsigned int nbytes = 0;
unsigned int alloc_size = 0;
- int addr = -1;
+ unsigned int addr = -1;
if (sscanf(arg, "%x %d", &addr, &nbytes) != 2)
return true;
@@ -278,9 +277,7 @@ static bool command_read_ram(const char *arg)
reply[0] = '\0';
reply_at = reply + sprintf(reply, SMY_CMD_STR " %x", addr);
- var.value = addr;
- cheevos_var_patch_addr(&var, cheevos_get_console());
- data = cheevos_var_get_memory(&var);
+ data = cheevos_patch_address(addr, cheevos_get_console());
if (data)
{
@@ -302,14 +299,9 @@ static bool command_read_ram(const char *arg)
static bool command_write_ram(const char *arg)
{
- cheevos_var_t var;
unsigned nbytes = 0;
- uint8_t *data = NULL;
-
- var.value = strtoul(arg, (char**)&arg, 16);
- cheevos_var_patch_addr(&var, cheevos_get_console());
-
- data = cheevos_var_get_memory(&var);
+ unsigned int addr = strtoul(arg, (char**)&arg, 16);
+ uint8_t *data = (uint8_t *)cheevos_patch_address(addr, cheevos_get_console());
if (data)
{