X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fmupen64plus-core%2Fsrc%2Fmain%2Fcheat.c;fp=source%2Fmupen64plus-core%2Fsrc%2Fmain%2Fcheat.c;h=52551ce0a0185b4c165402b3ba0f49563e0073f0;hb=451ab91e3827a6384981b3300e2a7000d2eaba58;hp=0000000000000000000000000000000000000000;hpb=a2ab25365b5b0dddbee476d695d8a31151407581;p=mupen64plus-pandora.git diff --git a/source/mupen64plus-core/src/main/cheat.c b/source/mupen64plus-core/src/main/cheat.c new file mode 100644 index 0000000..52551ce --- /dev/null +++ b/source/mupen64plus-core/src/main/cheat.c @@ -0,0 +1,457 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - cheat.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2008 Okaygo * + * * + * This program 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 Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program 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 this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// gameshark and xploder64 reference: http://doc.kodewerx.net/hacking_n64.html + +#include +#include + +#include "api/m64p_types.h" +#include "api/callbacks.h" +#include "api/config.h" + +#include "memory/memory.h" +#include "osal/preproc.h" +#include "cheat.h" +#include "main.h" +#include "rom.h" +#include "eventloop.h" +#include "list.h" + +#include +#include + +// local definitions +#define CHEAT_CODE_MAGIC_VALUE 0xDEAD0000 + +typedef struct cheat_code { + unsigned int address; + int value; + int old_value; + struct list_head list; +} cheat_code_t; + +typedef struct cheat { + char *name; + int enabled; + int was_enabled; + struct list_head cheat_codes; + struct list_head list; +} cheat_t; + +// local variables +static LIST_HEAD(active_cheats); +static SDL_mutex *cheat_mutex = NULL; + +// private functions +static unsigned short read_address_16bit(unsigned int address) +{ + return *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))); +} + +static unsigned char read_address_8bit(unsigned int address) +{ + return *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))); +} + +static void update_address_16bit(unsigned int address, unsigned short new_value) +{ + *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))) = new_value; +} + +static void update_address_8bit(unsigned int address, unsigned char new_value) +{ + *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))) = new_value; +} + +static int address_equal_to_8bit(unsigned int address, unsigned char value) +{ + unsigned char value_read; + value_read = *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))); + return value_read == value; +} + +static int address_equal_to_16bit(unsigned int address, unsigned short value) +{ + unsigned short value_read; + value_read = *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))); + return value_read == value; +} + +// individual application - returns 0 if we are supposed to skip the next cheat +// (only really used on conditional codes) +static int execute_cheat(unsigned int address, unsigned short value, int *old_value) +{ + switch (address & 0xFF000000) + { + case 0x80000000: + case 0x88000000: + case 0xA0000000: + case 0xA8000000: + case 0xF0000000: + // if pointer to old value is valid and uninitialized, write current value to it + if(old_value && (*old_value == CHEAT_CODE_MAGIC_VALUE)) + *old_value = (int) read_address_8bit(address); + update_address_8bit(address,(unsigned char) value); + return 1; + case 0x81000000: + case 0x89000000: + case 0xA1000000: + case 0xA9000000: + case 0xF1000000: + // if pointer to old value is valid and uninitialized, write current value to it + if(old_value && (*old_value == CHEAT_CODE_MAGIC_VALUE)) + *old_value = (int) read_address_16bit(address); + update_address_16bit(address,value); + return 1; + case 0xD0000000: + case 0xD8000000: + return address_equal_to_8bit(address,(unsigned char) value); + case 0xD1000000: + case 0xD9000000: + return address_equal_to_16bit(address,value); + case 0xD2000000: + case 0xDB000000: + return !(address_equal_to_8bit(address,(unsigned char) value)); + case 0xD3000000: + case 0xDA000000: + return !(address_equal_to_16bit(address,value)); + case 0xEE000000: + // most likely, this doesnt do anything. + execute_cheat(0xF1000318, 0x0040, NULL); + execute_cheat(0xF100031A, 0x0000, NULL); + return 1; + default: + return 1; + } +} + +static cheat_t *find_or_create_cheat(const char *name) +{ + cheat_t *cheat; + int found = 0; + + list_for_each_entry(cheat, &active_cheats, cheat_t, list) { + if (strcmp(cheat->name, name) == 0) { + found = 1; + break; + } + } + + if (found) + { + /* delete any pre-existing cheat codes */ + cheat_code_t *code, *safe; + + list_for_each_entry_safe(code, safe, &cheat->cheat_codes, cheat_code_t, list) { + list_del(&code->list); + free(code); + } + + cheat->enabled = 0; + cheat->was_enabled = 0; + } + else + { + cheat = malloc(sizeof(*cheat)); + cheat->name = strdup(name); + cheat->enabled = 0; + cheat->was_enabled = 0; + INIT_LIST_HEAD(&cheat->cheat_codes); + list_add_tail(&cheat->list, &active_cheats); + } + + return cheat; +} + + +// public functions +void cheat_init(void) +{ + cheat_mutex = SDL_CreateMutex(); +} + +void cheat_uninit(void) +{ + if (cheat_mutex != NULL) + SDL_DestroyMutex(cheat_mutex); + cheat_mutex = NULL; +} + +void cheat_apply_cheats(int entry) +{ + cheat_t *cheat; + cheat_code_t *code; + int skip; + int execute_next; + + // If game is Zelda OOT, apply subscreen delay fix + if (strncmp((char *)ROM_HEADER.Name, "THE LEGEND OF ZELDA", 19) == 0 && entry == ENTRY_VI) { + if (sl(ROM_HEADER.CRC1) == 0xEC7011B7 && sl(ROM_HEADER.CRC2) == 0x7616D72B) { + // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.0) + execute_cheat(0x801DA5CB, 0x0002, NULL); + } else if (sl(ROM_HEADER.CRC1) == 0xD43DA81F && sl(ROM_HEADER.CRC2) == 0x021E1E19) { + // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.1) + execute_cheat(0x801DA78B, 0x0002, NULL); + } else if (sl(ROM_HEADER.CRC1) == 0x693BA2AE && sl(ROM_HEADER.CRC2) == 0xB7F14E9F) { + // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.2) + execute_cheat(0x801DAE8B, 0x0002, NULL); + } else if (sl(ROM_HEADER.CRC1) == 0xB044B569 && sl(ROM_HEADER.CRC2) == 0x373C1985) { + // Legend of Zelda, The - Ocarina of Time (E) (V1.0) + execute_cheat(0x801D860B, 0x0002, NULL); + } else if (sl(ROM_HEADER.CRC1) == 0xB2055FBD && sl(ROM_HEADER.CRC2) == 0x0BAB4E0C) { + // Legend of Zelda, The - Ocarina of Time (E) (V1.1) + execute_cheat(0x801D864B, 0x0002, NULL); + } else { + // Legend of Zelda, The - Ocarina of Time Master Quest + execute_cheat(0x801D8F4B, 0x0002, NULL); + } + } + + if (list_empty(&active_cheats)) + return; + + if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) + { + DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_apply_cheats()"); + return; + } + + list_for_each_entry(cheat, &active_cheats, cheat_t, list) { + if (cheat->enabled) + { + cheat->was_enabled = 1; + switch(entry) + { + case ENTRY_BOOT: + list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { + // code should only be written once at boot time + if((code->address & 0xF0000000) == 0xF0000000) + execute_cheat(code->address, code->value, &code->old_value); + } + break; + case ENTRY_VI: + skip = 0; + execute_next = 0; + list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { + if (skip) { + skip = 0; + continue; + } + if (execute_next) { + execute_next = 0; + + // if code needs GS button pressed, don't save old value + if(((code->address & 0xFF000000) == 0xD8000000 || + (code->address & 0xFF000000) == 0xD9000000 || + (code->address & 0xFF000000) == 0xDA000000 || + (code->address & 0xFF000000) == 0xDB000000)) + execute_cheat(code->address, code->value, NULL); + else + execute_cheat(code->address, code->value, &code->old_value); + + continue; + } + // conditional cheat codes + if((code->address & 0xF0000000) == 0xD0000000) + { + // if code needs GS button pressed and it's not, skip it + if(((code->address & 0xFF000000) == 0xD8000000 || + (code->address & 0xFF000000) == 0xD9000000 || + (code->address & 0xFF000000) == 0xDA000000 || + (code->address & 0xFF000000) == 0xDB000000) && + !event_gameshark_active()) + { + // skip next code + skip = 1; + continue; + } + + if (execute_cheat(code->address, code->value, NULL)) { + // if condition true, execute next cheat code + execute_next = 1; + } else { + // if condition false, skip next code + skip = 1; + continue; + } + } + // GS button triggers cheat code + else if((code->address & 0xFF000000) == 0x88000000 || + (code->address & 0xFF000000) == 0x89000000 || + (code->address & 0xFF000000) == 0xA8000000 || + (code->address & 0xFF000000) == 0xA9000000) + { + if(event_gameshark_active()) + execute_cheat(code->address, code->value, NULL); + } + // normal cheat code + else + { + // exclude boot-time cheat codes + if((code->address & 0xF0000000) != 0xF0000000) + execute_cheat(code->address, code->value, &code->old_value); + } + } + break; + default: + break; + } + } + // if cheat was enabled, but is now disabled, restore old memory values + else if (cheat->was_enabled) + { + cheat->was_enabled = 0; + switch(entry) + { + case ENTRY_VI: + list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { + // set memory back to old value and clear saved copy of old value + if(code->old_value != CHEAT_CODE_MAGIC_VALUE) + { + execute_cheat(code->address, code->old_value, NULL); + code->old_value = CHEAT_CODE_MAGIC_VALUE; + } + } + break; + default: + break; + } + } + } + + SDL_UnlockMutex(cheat_mutex); +} + + +void cheat_delete_all(void) +{ + cheat_t *cheat, *safe_cheat; + cheat_code_t *code, *safe_code; + + if (list_empty(&active_cheats)) + return; + + if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) + { + DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_delete_all()"); + return; + } + + list_for_each_entry_safe(cheat, safe_cheat, &active_cheats, cheat_t, list) { + free(cheat->name); + + list_for_each_entry_safe(code, safe_code, &cheat->cheat_codes, cheat_code_t, list) { + list_del(&code->list); + free(code); + } + list_del(&cheat->list); + free(cheat); + } + + SDL_UnlockMutex(cheat_mutex); +} + +int cheat_set_enabled(const char *name, int enabled) +{ + cheat_t *cheat = NULL; + + if (list_empty(&active_cheats)) + return 0; + + if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) + { + DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_set_enabled()"); + return 0; + } + + list_for_each_entry(cheat, &active_cheats, cheat_t, list) { + if (strcmp(name, cheat->name) == 0) + { + cheat->enabled = enabled; + SDL_UnlockMutex(cheat_mutex); + return 1; + } + } + + SDL_UnlockMutex(cheat_mutex); + return 0; +} + +int cheat_add_new(const char *name, m64p_cheat_code *code_list, int num_codes) +{ + cheat_t *cheat; + int i, j; + + if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) + { + DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_add_new()"); + return 0; + } + + /* create a new cheat function or erase the codes in an existing cheat function */ + cheat = find_or_create_cheat(name); + if (cheat == NULL) + { + SDL_UnlockMutex(cheat_mutex); + return 0; + } + + cheat->enabled = 1; /* default for new cheats is enabled */ + + for (i = 0; i < num_codes; i++) + { + /* if this is a 'patch' code, convert it and dump out all of the individual codes */ + if ((code_list[i].address & 0xFFFF0000) == 0x50000000 && i < num_codes - 1) + { + int code_count = ((code_list[i].address & 0xFF00) >> 8); + int incr_addr = code_list[i].address & 0xFF; + int incr_value = code_list[i].value; + int cur_addr = code_list[i+1].address; + int cur_value = code_list[i+1].value; + i += 1; + for (j = 0; j < code_count; j++) + { + cheat_code_t *code = malloc(sizeof(*code)); + code->address = cur_addr; + code->value = cur_value; + code->old_value = CHEAT_CODE_MAGIC_VALUE; + list_add_tail(&code->list, &cheat->cheat_codes); + cur_addr += incr_addr; + cur_value += incr_value; + } + } + else + { /* just a normal code */ + cheat_code_t *code = malloc(sizeof(*code)); + code->address = code_list[i].address; + code->value = code_list[i].value; + code->old_value = CHEAT_CODE_MAGIC_VALUE; + list_add_tail(&code->list, &cheat->cheat_codes); + } + } + + SDL_UnlockMutex(cheat_mutex); + return 1; +} + +