From 714b7f17e9ad95b6d7f507ebb71278767920c3cb Mon Sep 17 00:00:00 2001 From: dinkc64 Date: Thu, 14 Nov 2024 01:41:51 -0500 Subject: [PATCH] add GameGenie / Pro Action Replay(PAR) support to SNES. for .ini files (f.ex, support/cheats/snes_ffight2.ini) --- src/burn/cheat.cpp | 3 +- src/burn/drv/snes/snes.cpp | 120 ++++++++++++++++++++++++++++++++++++- src/burner/conc.cpp | 4 +- 3 files changed, 123 insertions(+), 4 deletions(-) diff --git a/src/burn/cheat.cpp b/src/burn/cheat.cpp index 45228be7d..0685419fc 100644 --- a/src/burn/cheat.cpp +++ b/src/burn/cheat.cpp @@ -5,7 +5,8 @@ #define CHEAT_MAXCPU 8 // enough? -#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) ) +// any system that uses Game Genie/Pro Action Replay codes can be defined as HW_NES... +#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) ) void (*nes_add_cheat)(char *) = NULL; void (*nes_remove_cheat)(char *) = NULL; diff --git a/src/burn/drv/snes/snes.cpp b/src/burn/drv/snes/snes.cpp index 95394e935..1ed725def 100644 --- a/src/burn/drv/snes/snes.cpp +++ b/src/burn/drv/snes/snes.cpp @@ -27,6 +27,7 @@ static uint8_t snes_rread(Snes* snes, uint32_t adr); // wrapped by read, to set static int snes_getAccessTime(Snes* snes, uint32_t adr); static void build_accesstime(Snes* snes); static void free_accesstime(); +static void snes_init_fbn_cheat_subsys(); static uint8_t *access_time; @@ -41,6 +42,8 @@ Snes* snes_init(void) { snes->input2 = input_init(snes, 2); snes->palTiming = false; + snes_init_fbn_cheat_subsys(); + return snes; } @@ -649,9 +652,124 @@ static void free_accesstime() { BurnFree(access_time); } +// game genie/par cheat system +static UINT8 gg_nibble(UINT8 g) +{ + const UINT8 gg_str[] = "DF4709156BC8A23E"; + + for (UINT8 i = 0; i < 0x10; i++) { + if (g == gg_str[i]) { + return i; + } + } + return 0; +} + +static INT32 gg_decode(char *gg_code, UINT32 &address, UINT8 &value) +{ + UINT32 type = strlen(gg_code); + UINT32 temp = 0; + + if (type != 8 && type != 9) { + // bad code! + return 1; + } + + switch (type) { + case 8: { // Pro Action Replay (PAR) + temp = (UINT32)strtoul(gg_code, NULL, 16); + address = (temp >> 8) & 0xffffff; + value = temp & 0xff; + break; + } + case 9: { // Game Genie + for (int i = 0; i < 9; i++) { + if (i != 4) temp = (temp << 4) | gg_nibble(gg_code[i]); + } + + value = (temp >> 24) & 0xff; + address = (temp >> 10) & 0xf; + address = (address << 4) | ((temp >> 2) & 0xf); + address = (address << 4) | ((temp >> 20) & 0xf); + address = (address << 4) | ((temp << 2) & 0xc) | ((temp >> 14) & 0x3); + address = (address << 4) | ((temp >> 16) & 0xf); + address = (address << 4) | ((temp >> 6) & 0xf); + break; + } + } + + return 0; +} + +static const INT32 cheat_MAX = 0x100; +static INT32 cheats_active = 0; + +struct cheat_struct { + char code[0x10]; // gamegenie, par code + UINT32 address; + UINT8 value; +}; + +static cheat_struct cheats[cheat_MAX]; + +static void snes_add_cheat(char *code) +{ + UINT32 address; + UINT8 value; + + if (!gg_decode(code, address, value) && cheats_active < (cheat_MAX-1)) { + strncpy(cheats[cheats_active].code, code, 9); + cheats[cheats_active].address = address; + cheats[cheats_active].value = value; + bprintf(0, _T("cheat #%d (%S) added. (%x, %x)\n"), cheats_active, cheats[cheats_active].code, address, value); + cheats_active++; + } else { + if (cheats_active < (cheat_MAX-1)) { + bprintf(0, _T("snes cheat engine: bad GameGenie code %S\n"), code); + } else { + bprintf(0, _T("snes cheat engine: too many active!\n")); + } + } +} + +static void snes_remove_cheat(char *code) +{ + cheat_struct cheat_temp[cheat_MAX]; + INT32 temp_num = 0; + + for (INT32 i = 0; i < cheats_active; i++) { + if (strcmp(code, cheats[i].code) != 0) { + memcpy(&cheat_temp[temp_num], &cheats[i], sizeof(cheat_struct)); + temp_num++; + } else { + bprintf(0, _T("cheat %S disabled.\n"), cheats[i].code); + } + } + + memcpy(cheats, cheat_temp, sizeof(cheats)); + cheats_active = temp_num; +} + +static void snes_init_fbn_cheat_subsys() +{ + nes_init_cheat_functions(snes_add_cheat, snes_remove_cheat); +} + +static inline UINT8 cheat_check(UINT32 address, UINT8 value) +{ + for (INT32 i = 0; i < cheats_active; i++) { + if (cheats[i].address == address) { + //bprintf(0, _T(".")); + return cheats[i].value; + } + } + + return value; +} + uint8_t snes_read(Snes* snes, uint32_t adr) { snes->adrBus = adr; - uint8_t val = snes_rread(snes, adr); + const uint8_t val = cheat_check(adr, snes_rread(snes, adr)); snes->openBus = val; return val; } diff --git a/src/burner/conc.cpp b/src/burner/conc.cpp index a42b591f4..3c4524d6b 100644 --- a/src/burner/conc.cpp +++ b/src/burner/conc.cpp @@ -1,6 +1,6 @@ #include "burner.h" -#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) ) +#define HW_NES ( ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_NES) || ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_FDS) ) static bool SkipComma(TCHAR** s) { @@ -237,7 +237,7 @@ static INT32 ConfigParseFile(TCHAR* pszFilename) for (INT32 z = 0; z < strlen(t); z++) { #endif char c = toupper((char)*s); - if (c >= 'A' && c <= 'Z' && newlen < 10) + if ( ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '-' || c == ':')) && newlen < 10) pCurrentCheat->pOption[n]->AddressInfo[nCurrentAddress].szGenieCode[newlen++] = c; s++; if (*s == _T(',')) break;