/* gameplaySP * * Copyright (C) 2006 Exophase * * 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 */ #include "common.h" typedef struct { bool cheat_active; struct { u32 address; u32 value; } codes[MAX_CHEAT_CODES]; unsigned cheat_count; } cheat_type; cheat_type cheats[MAX_CHEATS]; u32 max_cheat = 0; u32 cheat_master_hook = 0xffffffff; static bool has_encrypted_codebreaker(cheat_type *cheat) { int i; for(i = 0; i < cheat->cheat_count; i++) { u32 code = cheat->codes[i].address; u32 opcode = code >> 28; if (opcode == 9) return true; } return false; } static void update_hook_codebreaker(cheat_type *cheat) { int i; for(i = 0; i < cheat->cheat_count; i++) { u32 code = cheat->codes[i].address; u32 address = code & 0xfffffff; u32 opcode = code >> 28; if (opcode == 1) { u32 pcaddr = 0x08000000 | (address & 0x1ffffff); #ifdef HAVE_DYNAREC if (cheat_master_hook != pcaddr) init_caches(); /* Flush caches to install hook */ #endif cheat_master_hook = pcaddr; return; /* Only support for one hook */ } } } static void process_cheat_codebreaker(cheat_type *cheat, u16 pad) { int i; unsigned j; for(i = 0; i < cheat->cheat_count; i++) { u32 code = cheat->codes[i].address; u16 value = cheat->codes[i].value; u32 address = code & 0xfffffff; u32 opcode = code >> 28; switch (opcode) { case 0: /* Game CRC, ignored for now */ break; case 1: /* Master code function */ break; case 2: /* 16 bit OR */ write_memory16(address, read_memory16(address) | value); break; case 3: /* 8 bit write */ write_memory8(address, value); break; case 4: /* Slide code, writes a buffer with addr/value strides */ if (i + 1 < cheat->cheat_count) { u16 count = cheat->codes[++i].address; u16 vincr = cheat->codes[ i].address >> 16; u16 aincr = cheat->codes[ i].value; for (j = 0; j < count; j++) { write_memory16(address, value); address += aincr; value += vincr; } } break; case 5: /* Super code: copies bytes to a buffer addr */ for (j = 0; j < value * 2 && i < cheat->cheat_count; j++) { u8 bvalue, off = j % 6; switch (off) { case 0: bvalue = cheat->codes[++i].address >> 24; break; case 1 ... 3: bvalue = cheat->codes[i].address >> (24 - off*8); break; case 4 ... 5: bvalue = cheat->codes[i].value >> (40 - off*8); break; }; write_memory8(address, bvalue); address++; } break; case 6: /* 16 bit AND */ write_memory16(address, read_memory16(address) & value); break; case 7: /* Compare mem value and execute next cheat */ if (read_memory16(address) != value) i++; break; case 8: /* 16 bit write */ write_memory16(address, value); break; case 10: /* Compare mem value and skip next cheat */ if (read_memory16(address) == value) i++; break; case 11: /* Compare mem value and skip next cheat */ if (read_memory16(address) <= value) i++; break; case 12: /* Compare mem value and skip next cheat */ if (read_memory16(address) >= value) i++; break; case 13: /* Check button state and execute next cheat */ switch ((address >> 4) & 0xf) { case 0: if (((~pad) & 0x3ff) == value) i++; break; case 1: if ((pad & value) == value) i++; break; case 2: if ((pad & value) == 0) i++; break; }; break; case 14: /* Increase 16/32 bit memory value */ if (address & 1) { u32 value32 = (u32)((s16)value); /* Sign extend to 32 bit */ address &= ~1U; write_memory32(address, read_memory32(address) + value32); } else { write_memory16(address, read_memory16(address) + value); } break; case 15: /* Immediate and check and skip */ if ((read_memory16(address) & value) == 0) i++; break; } } } void process_cheats(void) { u32 i; for(i = 0; i <= max_cheat; i++) { if(!cheats[i].cheat_active) continue; process_cheat_codebreaker(&cheats[i], 0x3ff ^ read_ioreg(REG_P1)); } } void cheat_clear() { int i; for (i = 0; i < MAX_CHEATS; i++) { cheats[i].cheat_count = 0; cheats[i].cheat_active = false; } cheat_master_hook = 0xffffffff; } cheat_error cheat_parse(unsigned index, const char *code) { int pos = 0; int codelen = strlen(code); cheat_type *ch = &cheats[index]; char buf[1024]; if (index >= MAX_CHEATS) return CheatErrorTooMany; if (codelen >= sizeof(buf)) return CheatErrorTooBig; memcpy(buf, code, codelen+1); /* Init to a known good state */ ch->cheat_count = 0; if (index > max_cheat) max_cheat = index; /* Replace all the non-hex chars to spaces */ for (pos = 0; pos < codelen; pos++) if (!((buf[pos] >= '0' && buf[pos] <= '9') || (buf[pos] >= 'a' && buf[pos] <= 'f') || (buf[pos] >= 'A' && buf[pos] <= 'F'))) buf[pos] = ' '; /* Try to parse as Code Breaker */ pos = 0; while (pos < codelen) { u32 op1; u16 op2; if (2 != sscanf(&buf[pos], "%08x %04hx", &op1, &op2)) break; ch->codes[ch->cheat_count].address = op1; ch->codes[ch->cheat_count++].value = op2; pos += 13; while (pos < codelen && buf[pos] == ' ') pos++; if (ch->cheat_count >= MAX_CHEAT_CODES) break; } if (pos >= codelen) { /* Check whether these cheats are readable */ if (has_encrypted_codebreaker(ch)) return CheatErrorEncrypted; /* All codes were parsed! Process hook here */ ch->cheat_active = true; update_hook_codebreaker(ch); return CheatNoError; } /* TODO parse other types here */ return CheatErrorNotSupported; }