| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
| 2 | * Mupen64plus - cheat.c * |
| 3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
| 4 | * Copyright (C) 2009 Richard Goedeken * |
| 5 | * Copyright (C) 2008 Okaygo * |
| 6 | * * |
| 7 | * This program is free software; you can redistribute it and/or modify * |
| 8 | * it under the terms of the GNU General Public License as published by * |
| 9 | * the Free Software Foundation; either version 2 of the License, or * |
| 10 | * (at your option) any later version. * |
| 11 | * * |
| 12 | * This program is distributed in the hope that it will be useful, * |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 15 | * GNU General Public License for more details. * |
| 16 | * * |
| 17 | * You should have received a copy of the GNU General Public License * |
| 18 | * along with this program; if not, write to the * |
| 19 | * Free Software Foundation, Inc., * |
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
| 21 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 22 | |
| 23 | // gameshark and xploder64 reference: http://doc.kodewerx.net/hacking_n64.html |
| 24 | |
| 25 | #include <SDL.h> |
| 26 | #include <SDL_thread.h> |
| 27 | |
| 28 | #include "api/m64p_types.h" |
| 29 | #include "api/callbacks.h" |
| 30 | #include "api/config.h" |
| 31 | |
| 32 | #include "memory/memory.h" |
| 33 | #include "osal/preproc.h" |
| 34 | #include "cheat.h" |
| 35 | #include "main.h" |
| 36 | #include "rom.h" |
| 37 | #include "eventloop.h" |
| 38 | #include "list.h" |
| 39 | |
| 40 | #include <stdio.h> |
| 41 | #include <string.h> |
| 42 | |
| 43 | // local definitions |
| 44 | #define CHEAT_CODE_MAGIC_VALUE 0xDEAD0000 |
| 45 | |
| 46 | typedef struct cheat_code { |
| 47 | unsigned int address; |
| 48 | int value; |
| 49 | int old_value; |
| 50 | struct list_head list; |
| 51 | } cheat_code_t; |
| 52 | |
| 53 | typedef struct cheat { |
| 54 | char *name; |
| 55 | int enabled; |
| 56 | int was_enabled; |
| 57 | struct list_head cheat_codes; |
| 58 | struct list_head list; |
| 59 | } cheat_t; |
| 60 | |
| 61 | // local variables |
| 62 | static LIST_HEAD(active_cheats); |
| 63 | static SDL_mutex *cheat_mutex = NULL; |
| 64 | |
| 65 | // private functions |
| 66 | static unsigned short read_address_16bit(unsigned int address) |
| 67 | { |
| 68 | return *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))); |
| 69 | } |
| 70 | |
| 71 | static unsigned char read_address_8bit(unsigned int address) |
| 72 | { |
| 73 | return *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))); |
| 74 | } |
| 75 | |
| 76 | static void update_address_16bit(unsigned int address, unsigned short new_value) |
| 77 | { |
| 78 | *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))) = new_value; |
| 79 | } |
| 80 | |
| 81 | static void update_address_8bit(unsigned int address, unsigned char new_value) |
| 82 | { |
| 83 | *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))) = new_value; |
| 84 | } |
| 85 | |
| 86 | static int address_equal_to_8bit(unsigned int address, unsigned char value) |
| 87 | { |
| 88 | unsigned char value_read; |
| 89 | value_read = *(unsigned char *)((rdramb + ((address & 0xFFFFFF)^S8))); |
| 90 | return value_read == value; |
| 91 | } |
| 92 | |
| 93 | static int address_equal_to_16bit(unsigned int address, unsigned short value) |
| 94 | { |
| 95 | unsigned short value_read; |
| 96 | value_read = *(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))); |
| 97 | return value_read == value; |
| 98 | } |
| 99 | |
| 100 | // individual application - returns 0 if we are supposed to skip the next cheat |
| 101 | // (only really used on conditional codes) |
| 102 | static int execute_cheat(unsigned int address, unsigned short value, int *old_value) |
| 103 | { |
| 104 | switch (address & 0xFF000000) |
| 105 | { |
| 106 | case 0x80000000: |
| 107 | case 0x88000000: |
| 108 | case 0xA0000000: |
| 109 | case 0xA8000000: |
| 110 | case 0xF0000000: |
| 111 | // if pointer to old value is valid and uninitialized, write current value to it |
| 112 | if(old_value && (*old_value == CHEAT_CODE_MAGIC_VALUE)) |
| 113 | *old_value = (int) read_address_8bit(address); |
| 114 | update_address_8bit(address,(unsigned char) value); |
| 115 | return 1; |
| 116 | case 0x81000000: |
| 117 | case 0x89000000: |
| 118 | case 0xA1000000: |
| 119 | case 0xA9000000: |
| 120 | case 0xF1000000: |
| 121 | // if pointer to old value is valid and uninitialized, write current value to it |
| 122 | if(old_value && (*old_value == CHEAT_CODE_MAGIC_VALUE)) |
| 123 | *old_value = (int) read_address_16bit(address); |
| 124 | update_address_16bit(address,value); |
| 125 | return 1; |
| 126 | case 0xD0000000: |
| 127 | case 0xD8000000: |
| 128 | return address_equal_to_8bit(address,(unsigned char) value); |
| 129 | case 0xD1000000: |
| 130 | case 0xD9000000: |
| 131 | return address_equal_to_16bit(address,value); |
| 132 | case 0xD2000000: |
| 133 | case 0xDB000000: |
| 134 | return !(address_equal_to_8bit(address,(unsigned char) value)); |
| 135 | case 0xD3000000: |
| 136 | case 0xDA000000: |
| 137 | return !(address_equal_to_16bit(address,value)); |
| 138 | case 0xEE000000: |
| 139 | // most likely, this doesnt do anything. |
| 140 | execute_cheat(0xF1000318, 0x0040, NULL); |
| 141 | execute_cheat(0xF100031A, 0x0000, NULL); |
| 142 | return 1; |
| 143 | default: |
| 144 | return 1; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | static cheat_t *find_or_create_cheat(const char *name) |
| 149 | { |
| 150 | cheat_t *cheat; |
| 151 | int found = 0; |
| 152 | |
| 153 | list_for_each_entry(cheat, &active_cheats, cheat_t, list) { |
| 154 | if (strcmp(cheat->name, name) == 0) { |
| 155 | found = 1; |
| 156 | break; |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | if (found) |
| 161 | { |
| 162 | /* delete any pre-existing cheat codes */ |
| 163 | cheat_code_t *code, *safe; |
| 164 | |
| 165 | list_for_each_entry_safe(code, safe, &cheat->cheat_codes, cheat_code_t, list) { |
| 166 | list_del(&code->list); |
| 167 | free(code); |
| 168 | } |
| 169 | |
| 170 | cheat->enabled = 0; |
| 171 | cheat->was_enabled = 0; |
| 172 | } |
| 173 | else |
| 174 | { |
| 175 | cheat = malloc(sizeof(*cheat)); |
| 176 | cheat->name = strdup(name); |
| 177 | cheat->enabled = 0; |
| 178 | cheat->was_enabled = 0; |
| 179 | INIT_LIST_HEAD(&cheat->cheat_codes); |
| 180 | list_add_tail(&cheat->list, &active_cheats); |
| 181 | } |
| 182 | |
| 183 | return cheat; |
| 184 | } |
| 185 | |
| 186 | |
| 187 | // public functions |
| 188 | void cheat_init(void) |
| 189 | { |
| 190 | cheat_mutex = SDL_CreateMutex(); |
| 191 | } |
| 192 | |
| 193 | void cheat_uninit(void) |
| 194 | { |
| 195 | if (cheat_mutex != NULL) |
| 196 | SDL_DestroyMutex(cheat_mutex); |
| 197 | cheat_mutex = NULL; |
| 198 | } |
| 199 | |
| 200 | void cheat_apply_cheats(int entry) |
| 201 | { |
| 202 | cheat_t *cheat; |
| 203 | cheat_code_t *code; |
| 204 | int skip; |
| 205 | int execute_next; |
| 206 | |
| 207 | // If game is Zelda OOT, apply subscreen delay fix |
| 208 | if (strncmp((char *)ROM_HEADER.Name, "THE LEGEND OF ZELDA", 19) == 0 && entry == ENTRY_VI) { |
| 209 | if (sl(ROM_HEADER.CRC1) == 0xEC7011B7 && sl(ROM_HEADER.CRC2) == 0x7616D72B) { |
| 210 | // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.0) |
| 211 | execute_cheat(0x801DA5CB, 0x0002, NULL); |
| 212 | } else if (sl(ROM_HEADER.CRC1) == 0xD43DA81F && sl(ROM_HEADER.CRC2) == 0x021E1E19) { |
| 213 | // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.1) |
| 214 | execute_cheat(0x801DA78B, 0x0002, NULL); |
| 215 | } else if (sl(ROM_HEADER.CRC1) == 0x693BA2AE && sl(ROM_HEADER.CRC2) == 0xB7F14E9F) { |
| 216 | // Legend of Zelda, The - Ocarina of Time (U) + (J) (V1.2) |
| 217 | execute_cheat(0x801DAE8B, 0x0002, NULL); |
| 218 | } else if (sl(ROM_HEADER.CRC1) == 0xB044B569 && sl(ROM_HEADER.CRC2) == 0x373C1985) { |
| 219 | // Legend of Zelda, The - Ocarina of Time (E) (V1.0) |
| 220 | execute_cheat(0x801D860B, 0x0002, NULL); |
| 221 | } else if (sl(ROM_HEADER.CRC1) == 0xB2055FBD && sl(ROM_HEADER.CRC2) == 0x0BAB4E0C) { |
| 222 | // Legend of Zelda, The - Ocarina of Time (E) (V1.1) |
| 223 | execute_cheat(0x801D864B, 0x0002, NULL); |
| 224 | } else { |
| 225 | // Legend of Zelda, The - Ocarina of Time Master Quest |
| 226 | execute_cheat(0x801D8F4B, 0x0002, NULL); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | if (list_empty(&active_cheats)) |
| 231 | return; |
| 232 | |
| 233 | if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) |
| 234 | { |
| 235 | DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_apply_cheats()"); |
| 236 | return; |
| 237 | } |
| 238 | |
| 239 | list_for_each_entry(cheat, &active_cheats, cheat_t, list) { |
| 240 | if (cheat->enabled) |
| 241 | { |
| 242 | cheat->was_enabled = 1; |
| 243 | switch(entry) |
| 244 | { |
| 245 | case ENTRY_BOOT: |
| 246 | list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { |
| 247 | // code should only be written once at boot time |
| 248 | if((code->address & 0xF0000000) == 0xF0000000) |
| 249 | execute_cheat(code->address, code->value, &code->old_value); |
| 250 | } |
| 251 | break; |
| 252 | case ENTRY_VI: |
| 253 | skip = 0; |
| 254 | execute_next = 0; |
| 255 | list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { |
| 256 | if (skip) { |
| 257 | skip = 0; |
| 258 | continue; |
| 259 | } |
| 260 | if (execute_next) { |
| 261 | execute_next = 0; |
| 262 | |
| 263 | // if code needs GS button pressed, don't save old value |
| 264 | if(((code->address & 0xFF000000) == 0xD8000000 || |
| 265 | (code->address & 0xFF000000) == 0xD9000000 || |
| 266 | (code->address & 0xFF000000) == 0xDA000000 || |
| 267 | (code->address & 0xFF000000) == 0xDB000000)) |
| 268 | execute_cheat(code->address, code->value, NULL); |
| 269 | else |
| 270 | execute_cheat(code->address, code->value, &code->old_value); |
| 271 | |
| 272 | continue; |
| 273 | } |
| 274 | // conditional cheat codes |
| 275 | if((code->address & 0xF0000000) == 0xD0000000) |
| 276 | { |
| 277 | // if code needs GS button pressed and it's not, skip it |
| 278 | if(((code->address & 0xFF000000) == 0xD8000000 || |
| 279 | (code->address & 0xFF000000) == 0xD9000000 || |
| 280 | (code->address & 0xFF000000) == 0xDA000000 || |
| 281 | (code->address & 0xFF000000) == 0xDB000000) && |
| 282 | !event_gameshark_active()) |
| 283 | { |
| 284 | // skip next code |
| 285 | skip = 1; |
| 286 | continue; |
| 287 | } |
| 288 | |
| 289 | if (execute_cheat(code->address, code->value, NULL)) { |
| 290 | // if condition true, execute next cheat code |
| 291 | execute_next = 1; |
| 292 | } else { |
| 293 | // if condition false, skip next code |
| 294 | skip = 1; |
| 295 | continue; |
| 296 | } |
| 297 | } |
| 298 | // GS button triggers cheat code |
| 299 | else if((code->address & 0xFF000000) == 0x88000000 || |
| 300 | (code->address & 0xFF000000) == 0x89000000 || |
| 301 | (code->address & 0xFF000000) == 0xA8000000 || |
| 302 | (code->address & 0xFF000000) == 0xA9000000) |
| 303 | { |
| 304 | if(event_gameshark_active()) |
| 305 | execute_cheat(code->address, code->value, NULL); |
| 306 | } |
| 307 | // normal cheat code |
| 308 | else |
| 309 | { |
| 310 | // exclude boot-time cheat codes |
| 311 | if((code->address & 0xF0000000) != 0xF0000000) |
| 312 | execute_cheat(code->address, code->value, &code->old_value); |
| 313 | } |
| 314 | } |
| 315 | break; |
| 316 | default: |
| 317 | break; |
| 318 | } |
| 319 | } |
| 320 | // if cheat was enabled, but is now disabled, restore old memory values |
| 321 | else if (cheat->was_enabled) |
| 322 | { |
| 323 | cheat->was_enabled = 0; |
| 324 | switch(entry) |
| 325 | { |
| 326 | case ENTRY_VI: |
| 327 | list_for_each_entry(code, &cheat->cheat_codes, cheat_code_t, list) { |
| 328 | // set memory back to old value and clear saved copy of old value |
| 329 | if(code->old_value != CHEAT_CODE_MAGIC_VALUE) |
| 330 | { |
| 331 | execute_cheat(code->address, code->old_value, NULL); |
| 332 | code->old_value = CHEAT_CODE_MAGIC_VALUE; |
| 333 | } |
| 334 | } |
| 335 | break; |
| 336 | default: |
| 337 | break; |
| 338 | } |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | SDL_UnlockMutex(cheat_mutex); |
| 343 | } |
| 344 | |
| 345 | |
| 346 | void cheat_delete_all(void) |
| 347 | { |
| 348 | cheat_t *cheat, *safe_cheat; |
| 349 | cheat_code_t *code, *safe_code; |
| 350 | |
| 351 | if (list_empty(&active_cheats)) |
| 352 | return; |
| 353 | |
| 354 | if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) |
| 355 | { |
| 356 | DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_delete_all()"); |
| 357 | return; |
| 358 | } |
| 359 | |
| 360 | list_for_each_entry_safe(cheat, safe_cheat, &active_cheats, cheat_t, list) { |
| 361 | free(cheat->name); |
| 362 | |
| 363 | list_for_each_entry_safe(code, safe_code, &cheat->cheat_codes, cheat_code_t, list) { |
| 364 | list_del(&code->list); |
| 365 | free(code); |
| 366 | } |
| 367 | list_del(&cheat->list); |
| 368 | free(cheat); |
| 369 | } |
| 370 | |
| 371 | SDL_UnlockMutex(cheat_mutex); |
| 372 | } |
| 373 | |
| 374 | int cheat_set_enabled(const char *name, int enabled) |
| 375 | { |
| 376 | cheat_t *cheat = NULL; |
| 377 | |
| 378 | if (list_empty(&active_cheats)) |
| 379 | return 0; |
| 380 | |
| 381 | if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) |
| 382 | { |
| 383 | DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_set_enabled()"); |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | list_for_each_entry(cheat, &active_cheats, cheat_t, list) { |
| 388 | if (strcmp(name, cheat->name) == 0) |
| 389 | { |
| 390 | cheat->enabled = enabled; |
| 391 | SDL_UnlockMutex(cheat_mutex); |
| 392 | return 1; |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | SDL_UnlockMutex(cheat_mutex); |
| 397 | return 0; |
| 398 | } |
| 399 | |
| 400 | int cheat_add_new(const char *name, m64p_cheat_code *code_list, int num_codes) |
| 401 | { |
| 402 | cheat_t *cheat; |
| 403 | int i, j; |
| 404 | |
| 405 | if (cheat_mutex == NULL || SDL_LockMutex(cheat_mutex) != 0) |
| 406 | { |
| 407 | DebugMessage(M64MSG_ERROR, "Internal error: failed to lock mutex in cheat_add_new()"); |
| 408 | return 0; |
| 409 | } |
| 410 | |
| 411 | /* create a new cheat function or erase the codes in an existing cheat function */ |
| 412 | cheat = find_or_create_cheat(name); |
| 413 | if (cheat == NULL) |
| 414 | { |
| 415 | SDL_UnlockMutex(cheat_mutex); |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | cheat->enabled = 1; /* default for new cheats is enabled */ |
| 420 | |
| 421 | for (i = 0; i < num_codes; i++) |
| 422 | { |
| 423 | /* if this is a 'patch' code, convert it and dump out all of the individual codes */ |
| 424 | if ((code_list[i].address & 0xFFFF0000) == 0x50000000 && i < num_codes - 1) |
| 425 | { |
| 426 | int code_count = ((code_list[i].address & 0xFF00) >> 8); |
| 427 | int incr_addr = code_list[i].address & 0xFF; |
| 428 | int incr_value = code_list[i].value; |
| 429 | int cur_addr = code_list[i+1].address; |
| 430 | int cur_value = code_list[i+1].value; |
| 431 | i += 1; |
| 432 | for (j = 0; j < code_count; j++) |
| 433 | { |
| 434 | cheat_code_t *code = malloc(sizeof(*code)); |
| 435 | code->address = cur_addr; |
| 436 | code->value = cur_value; |
| 437 | code->old_value = CHEAT_CODE_MAGIC_VALUE; |
| 438 | list_add_tail(&code->list, &cheat->cheat_codes); |
| 439 | cur_addr += incr_addr; |
| 440 | cur_value += incr_value; |
| 441 | } |
| 442 | } |
| 443 | else |
| 444 | { /* just a normal code */ |
| 445 | cheat_code_t *code = malloc(sizeof(*code)); |
| 446 | code->address = code_list[i].address; |
| 447 | code->value = code_list[i].value; |
| 448 | code->old_value = CHEAT_CODE_MAGIC_VALUE; |
| 449 | list_add_tail(&code->list, &cheat->cheat_codes); |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | SDL_UnlockMutex(cheat_mutex); |
| 454 | return 1; |
| 455 | } |
| 456 | |
| 457 | |