mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-10 21:05:12 +01:00
Implement cheatcode support - implement retro_cheat_reset/retro_cheat_set
This commit is contained in:
parent
41c1f4fe70
commit
8b4b807b8c
@ -81,6 +81,32 @@ static retro_input_state_t input_state_cb;
|
|||||||
static retro_environment_t environ_cb;
|
static retro_environment_t environ_cb;
|
||||||
static retro_audio_sample_batch_t audio_cb;
|
static retro_audio_sample_batch_t audio_cb;
|
||||||
|
|
||||||
|
/* Cheat Support */
|
||||||
|
#define MAX_CHEATS (150)
|
||||||
|
#define MAX_DESC_LENGTH (63)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char code[12];
|
||||||
|
char text[MAX_DESC_LENGTH];
|
||||||
|
uint8_t enable;
|
||||||
|
uint16_t data;
|
||||||
|
uint16_t old;
|
||||||
|
uint32_t address;
|
||||||
|
uint8_t *prev;
|
||||||
|
} CHEATENTRY;
|
||||||
|
|
||||||
|
static int maxcheats = 0;
|
||||||
|
static int maxROMcheats = 0;
|
||||||
|
static int maxRAMcheats = 0;
|
||||||
|
|
||||||
|
static CHEATENTRY cheatlist[MAX_CHEATS];
|
||||||
|
static uint8_t cheatIndexes[MAX_CHEATS];
|
||||||
|
|
||||||
|
static char ggvalidchars[] = "ABCDEFGHJKLMNPRSTVWXYZ0123456789";
|
||||||
|
|
||||||
|
static char arvalidchars[] = "0123456789ABCDEF";
|
||||||
|
|
||||||
/************************************
|
/************************************
|
||||||
* Genesis Plus GX implementation
|
* Genesis Plus GX implementation
|
||||||
************************************/
|
************************************/
|
||||||
@ -169,6 +195,8 @@ int load_archive(char *filename, unsigned char *buffer, int maxsize, char *exten
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RAMCheatUpdate(void);
|
||||||
|
|
||||||
void osd_input_update(void)
|
void osd_input_update(void)
|
||||||
{
|
{
|
||||||
int i, player = 0;
|
int i, player = 0;
|
||||||
@ -176,6 +204,9 @@ void osd_input_update(void)
|
|||||||
|
|
||||||
input_poll_cb();
|
input_poll_cb();
|
||||||
|
|
||||||
|
/* Update RAM patches */
|
||||||
|
RAMCheatUpdate();
|
||||||
|
|
||||||
for (i = 0; i < MAX_INPUTS; i++)
|
for (i = 0; i < MAX_INPUTS; i++)
|
||||||
{
|
{
|
||||||
temp = 0;
|
temp = 0;
|
||||||
@ -941,6 +972,314 @@ static void check_variables(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cheat Support */
|
||||||
|
static uint32_t decode_cheat(char *string, int index)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int i,n;
|
||||||
|
uint32_t len = 0;
|
||||||
|
uint32_t address = 0;
|
||||||
|
uint16_t data = 0;
|
||||||
|
uint8_t ref = 0;
|
||||||
|
/* 16-bit Game Genie code (ABCD-EFGH) */
|
||||||
|
if ((strlen(string) >= 9) && (string[4] == '-'))
|
||||||
|
{
|
||||||
|
/* 16-bit system only */
|
||||||
|
if ((system_hw & SYSTEM_PBC) != SYSTEM_MD)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (i == 4) string++;
|
||||||
|
p = strchr (ggvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = p - ggvalidchars;
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
data |= n << 3;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
data |= n >> 2;
|
||||||
|
address |= (n & 3) << 14;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
address |= n << 9;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
address |= (n & 0xF) << 20 | (n >> 4) << 8;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data |= (n & 1) << 12;
|
||||||
|
address |= (n >> 1) << 16;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
data |= (n & 1) << 15 | (n >> 1) << 8;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
data |= (n >> 3) << 13;
|
||||||
|
address |= (n & 7) << 5;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
address |= n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* code length */
|
||||||
|
len = 9;
|
||||||
|
}
|
||||||
|
/* 8-bit Game Genie code (DDA-AAA-XXX) */
|
||||||
|
else if ((strlen(string) >= 11) && (string[3] == '-') && (string[7] == '-'))
|
||||||
|
{
|
||||||
|
/* 8-bit system only */
|
||||||
|
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* decode 8-bit data */
|
||||||
|
for (i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
data |= (n << ((1 - i) * 4));
|
||||||
|
}
|
||||||
|
/* decode 16-bit address (low 12-bits) */
|
||||||
|
for (i=0; i<3; i++)
|
||||||
|
{
|
||||||
|
if (i==1) string++; /* skip separator */
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
address |= (n << ((2 - i) * 4));
|
||||||
|
}
|
||||||
|
/* decode 16-bit address (high 4-bits) */
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
n ^= 0xF; /* bits inversion */
|
||||||
|
address |= (n << 12);
|
||||||
|
/* RAM address are also supported */
|
||||||
|
if (address >= 0xC000)
|
||||||
|
{
|
||||||
|
/* convert to 24-bit Work RAM address */
|
||||||
|
address = 0xFF0000 | (address & 0x1FFF);
|
||||||
|
}
|
||||||
|
/* decode reference 8-bit data */
|
||||||
|
for (i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
string++; /* skip separator and 2nd digit */
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
ref |= (n << ((1 - i) * 4));
|
||||||
|
}
|
||||||
|
ref = (ref >> 2) | ((ref & 0x03) << 6); /* 2-bit right rotation */
|
||||||
|
ref ^= 0xBA; /* XOR */
|
||||||
|
/* update old data value */
|
||||||
|
cheatlist[index].old = ref;
|
||||||
|
/* code length */
|
||||||
|
len = 11;
|
||||||
|
}
|
||||||
|
/* Action Replay code */
|
||||||
|
else if (string[6] == ':')
|
||||||
|
{
|
||||||
|
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||||
|
{
|
||||||
|
/* 16-bit code (AAAAAA:DDDD) */
|
||||||
|
if (strlen(string) < 11) return 0;
|
||||||
|
/* decode 24-bit address */
|
||||||
|
for (i=0; i<6; i++)
|
||||||
|
{
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
address |= (n << ((5 - i) * 4));
|
||||||
|
}
|
||||||
|
/* decode 16-bit data */
|
||||||
|
string++;
|
||||||
|
for (i=0; i<4; i++)
|
||||||
|
{
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
data |= (n << ((3 - i) * 4));
|
||||||
|
}
|
||||||
|
/* code length */
|
||||||
|
len = 11;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* 8-bit code (xxAAAA:DD) */
|
||||||
|
if (strlen(string) < 9) return 0;
|
||||||
|
/* decode 16-bit address */
|
||||||
|
string+=2;
|
||||||
|
for (i=0; i<4; i++)
|
||||||
|
{
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
address |= (n << ((3 - i) * 4));
|
||||||
|
}
|
||||||
|
/* ROM addresses are not supported */
|
||||||
|
if (address < 0xC000) return 0;
|
||||||
|
/* convert to 24-bit Work RAM address */
|
||||||
|
address = 0xFF0000 | (address & 0x1FFF);
|
||||||
|
/* decode 8-bit data */
|
||||||
|
string++;
|
||||||
|
for (i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
p = strchr (arvalidchars, *string++);
|
||||||
|
if (p == NULL) return 0;
|
||||||
|
n = (p - arvalidchars) & 0xF;
|
||||||
|
data |= (n << ((1 - i) * 4));
|
||||||
|
}
|
||||||
|
/* code length */
|
||||||
|
len = 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Valid code found ? */
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
/* update cheat address & data values */
|
||||||
|
cheatlist[index].address = address;
|
||||||
|
cheatlist[index].data = data;
|
||||||
|
cheatlist[index].enable = 1; // Enable the cheat by default
|
||||||
|
}
|
||||||
|
/* return code length (0 = invalid) */
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_cheats(void)
|
||||||
|
{
|
||||||
|
uint8_t *ptr;
|
||||||
|
/* clear ROM&RAM patches counter */
|
||||||
|
maxROMcheats = maxRAMcheats = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < maxcheats; i++)
|
||||||
|
{
|
||||||
|
if (cheatlist[i].enable)
|
||||||
|
{
|
||||||
|
if (cheatlist[i].address < cart.romsize)
|
||||||
|
{
|
||||||
|
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||||
|
{
|
||||||
|
/* patch ROM data */
|
||||||
|
cheatlist[i].old = *(uint16_t *)(cart.rom + (cheatlist[i].address & 0xFFFFFE));
|
||||||
|
*(uint16_t *)(cart.rom + (cheatlist[i].address & 0xFFFFFE)) = cheatlist[i].data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* add ROM patch */
|
||||||
|
maxROMcheats++;
|
||||||
|
cheatIndexes[MAX_CHEATS - maxROMcheats] = i;
|
||||||
|
/* get current banked ROM address */
|
||||||
|
ptr = &z80_readmap[(cheatlist[i].address) >> 10][cheatlist[i].address & 0x03FF];
|
||||||
|
/* check if reference matches original ROM data */
|
||||||
|
if (((uint8_t)cheatlist[i].old) == *ptr)
|
||||||
|
{
|
||||||
|
/* patch data */
|
||||||
|
*ptr = cheatlist[i].data;
|
||||||
|
/* save patched ROM address */
|
||||||
|
cheatlist[i].prev = ptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* no patched ROM address yet */
|
||||||
|
cheatlist[i].prev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cheatlist[i].address >= 0xFF0000)
|
||||||
|
{
|
||||||
|
/* add RAM patch */
|
||||||
|
cheatIndexes[maxRAMcheats++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_cheats(void)
|
||||||
|
{
|
||||||
|
int i = maxcheats;
|
||||||
|
/* disable cheats in reversed order in case the same address is used by multiple patches */
|
||||||
|
while (i > 0)
|
||||||
|
{
|
||||||
|
if (cheatlist[i-1].enable)
|
||||||
|
{
|
||||||
|
if (cheatlist[i-1].address < cart.romsize)
|
||||||
|
{
|
||||||
|
if ((system_hw & SYSTEM_PBC) == SYSTEM_MD)
|
||||||
|
{
|
||||||
|
/* restore original ROM data */
|
||||||
|
*(uint16_t *)(cart.rom + (cheatlist[i-1].address & 0xFFFFFE)) = cheatlist[i-1].old;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* check if previous banked ROM address has been patched */
|
||||||
|
if (cheatlist[i-1].prev != NULL)
|
||||||
|
{
|
||||||
|
/* restore original data */
|
||||||
|
*cheatlist[i-1].prev = cheatlist[i-1].old;
|
||||||
|
/* no more patched ROM address */
|
||||||
|
cheatlist[i-1].prev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_cheats(void)
|
||||||
|
{
|
||||||
|
int i = maxcheats;
|
||||||
|
while (i > 0)
|
||||||
|
{
|
||||||
|
if (cheatlist[i-1].enable)
|
||||||
|
{
|
||||||
|
cheatlist[i-1].text[0] = 0;
|
||||||
|
cheatlist[i-1].code[0] = 0;
|
||||||
|
cheatlist[i-1].address = 0;
|
||||||
|
cheatlist[i-1].data = 0;
|
||||||
|
cheatlist[i-1].enable = 0;
|
||||||
|
|
||||||
|
maxcheats--;
|
||||||
|
}
|
||||||
|
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* RAMCheatUpdate
|
||||||
|
*
|
||||||
|
* Apply RAM patches (this should be called once per frame)
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
void RAMCheatUpdate(void)
|
||||||
|
{
|
||||||
|
int index, cnt = maxRAMcheats;
|
||||||
|
while (cnt)
|
||||||
|
{
|
||||||
|
/* get cheat index */
|
||||||
|
index = cheatIndexes[--cnt];
|
||||||
|
/* apply RAM patch */
|
||||||
|
//if (cheatlist[index].data & 0xFF00)
|
||||||
|
if (cheatlist[index].data & 0x00FF) // For LSB?
|
||||||
|
{
|
||||||
|
/* word patch */
|
||||||
|
*(uint16_t *)(work_ram + (cheatlist[index].address & 0xFFFE)) = cheatlist[index].data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* byte patch */
|
||||||
|
work_ram[cheatlist[index].address & 0xFFFF] = cheatlist[index].data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************************************
|
/************************************
|
||||||
* libretro implementation
|
* libretro implementation
|
||||||
************************************/
|
************************************/
|
||||||
@ -1173,12 +1512,24 @@ bool retro_unserialize(const void *data, size_t size)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_cheat_reset(void) {}
|
void retro_cheat_reset(void)
|
||||||
|
{
|
||||||
|
/* clear existing ROM patches */
|
||||||
|
clear_cheats();
|
||||||
|
/* remove cheats from the list */
|
||||||
|
remove_cheats();
|
||||||
|
}
|
||||||
|
|
||||||
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||||
{
|
{
|
||||||
(void)index;
|
/* clear existing ROM patches */
|
||||||
(void)enabled;
|
clear_cheats();
|
||||||
(void)code;
|
/* interpret code and give it an index */
|
||||||
|
decode_cheat((char *)code, maxcheats);
|
||||||
|
/* increment cheat count */
|
||||||
|
maxcheats++;
|
||||||
|
/* apply ROM patches */
|
||||||
|
apply_cheats();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retro_load_game(const struct retro_game_info *info)
|
bool retro_load_game(const struct retro_game_info *info)
|
||||||
|
Loading…
Reference in New Issue
Block a user