266 lines
6.8 KiB
C
266 lines
6.8 KiB
C
/* gameplaySP
|
|
*
|
|
* Copyright (C) 2006 Exophase <exophase@gmail.com>
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
|