From 613a3e84795ee92295a37b1f7b6f50ef9191e71f Mon Sep 17 00:00:00 2001 From: Andre Leiradella Date: Sun, 2 Sep 2018 14:23:37 +0100 Subject: [PATCH] Integrated rcheevos --- Makefile.common | 53 +- cheevos/badges.h | 19 +- cheevos/cheevos.c | 3136 +++++++++++---------------------------------- cheevos/cheevos.h | 100 +- cheevos/cond.c | 189 --- cheevos/cond.h | 63 - cheevos/coro.h | 6 +- cheevos/fixup.c | 264 ++++ cheevos/fixup.h | 48 + cheevos/hash.c | 12 + cheevos/hash.h | 30 + cheevos/parser.c | 674 ++++++++++ cheevos/parser.h | 69 + cheevos/util.h | 53 + cheevos/var.c | 416 ------ cheevos/var.h | 78 -- command.c | 18 +- 17 files changed, 1991 insertions(+), 3237 deletions(-) delete mode 100644 cheevos/cond.c delete mode 100644 cheevos/cond.h create mode 100644 cheevos/fixup.c create mode 100644 cheevos/fixup.h create mode 100644 cheevos/hash.c create mode 100644 cheevos/hash.h create mode 100644 cheevos/parser.c create mode 100644 cheevos/parser.h create mode 100644 cheevos/util.h delete mode 100644 cheevos/var.c delete mode 100644 cheevos/var.h 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) {