From 785d0a4b3ec0e0698cd6c9ad48c541d5861d1f95 Mon Sep 17 00:00:00 2001 From: Martin Donlon Date: Thu, 20 Feb 2025 00:11:28 -0800 Subject: [PATCH] Arcade cheat support (#969) Remove dip switch based cheat support which was unused by any core. MRA files can now specify cheat data directly --- cheats.cpp | 138 +++++++++++++++++++++++----------- cheats.h | 4 + menu.cpp | 69 ++++++----------- support/arcade/mra_loader.cpp | 123 +++++++++++++++++++----------- support/arcade/mra_loader.h | 8 +- 5 files changed, 205 insertions(+), 137 deletions(-) diff --git a/cheats.cpp b/cheats.cpp index f9c42af..12d6e30 100644 --- a/cheats.cpp +++ b/cheats.cpp @@ -10,6 +10,7 @@ #include "hardware.h" #include "file_io.h" +#include "str_util.h" #include "user_io.h" #include "fpga_io.h" #include "miniz.h" @@ -32,6 +33,22 @@ struct cheat_rec_t memset(name, 0, sizeof(name)); } + cheat_rec_t(const cheat_rec_t& other) + { + memcpy(this->name, other.name, sizeof(other.name)); + this->enabled = other.enabled; + this->cheatSize = other.cheatSize; + if (other.cheatData) + { + this->cheatData = new char [this->cheatSize]; + memcpy(this->cheatData, other.cheatData, this->cheatSize); + } + else + { + this->cheatData = nullptr; + } + } + ~cheat_rec_t() { if (this->cheatData) @@ -44,9 +61,13 @@ struct cheat_rec_t typedef std::vector CheatVector; static CheatVector cheats; +#define CHEAT_SIZE (128*16) // 128 codes max + static int iSelectedEntry = 0; static int iFirstEntry = 0; static int loaded = 0; +static int cheat_unit_size = 16; +static int cheat_max_active = 128; struct CheatComp { @@ -170,10 +191,50 @@ bool cheat_init_psx(mz_zip_archive* _z, const char *rom_path) return false; } +void cheats_init_arcade(int unit_size, int max_active) +{ + cheats.clear(); + loaded = 0; + cheat_unit_size = unit_size > 0 ? unit_size : 16; + cheat_max_active = max_active > 0 ? max_active : 128; + if ((cheat_max_active * cheat_unit_size) > CHEAT_SIZE) + { + cheat_max_active = CHEAT_SIZE / cheat_unit_size; + } + + cheat_zip[0] = 0; +} + +void cheats_add_arcade(const char *name, const char *cheatData, int cheatSize) +{ + cheat_rec_t cheat = {}; + + if ((cheatSize % cheat_unit_size) != 0) + { + printf("Arcade cheat \'%s\' has incorrect length %d -> skipping.\n", name, cheatSize); + return; + } + + strcpyz(cheat.name, name); + cheat.cheatSize = cheatSize; + cheat.cheatData = new char [cheatSize]; + memcpy(cheat.cheatData, cheatData, cheatSize); + cheats.push_back(cheat); +} + +void cheats_finalize_arcade() +{ + printf("MRA cheats: %d\n", cheats_available()); + cheats_scan(SCANF_INIT); +} + + void cheats_init(const char *rom_path, uint32_t romcrc) { cheats.clear(); loaded = 0; + cheat_unit_size = 16; + cheat_max_active = 128; cheat_zip[0] = 0; // reset cheats @@ -412,7 +473,6 @@ void cheats_print() } } -#define CHEAT_SIZE (128*16) // 128 codes max static void cheats_send() { @@ -437,7 +497,7 @@ static void cheats_send() } } - loaded = pos / 16; + loaded = pos / cheat_unit_size; printf("Cheat codes: %d\n", loaded); if (is_n64()) @@ -459,15 +519,7 @@ void cheats_toggle() if (cheats[iSelectedEntry].enabled == true) { - /* disabled loaded cheat, free data */ - if (cheats[iSelectedEntry].cheatData) - { - delete[] cheats[iSelectedEntry].cheatData; - cheats[iSelectedEntry].cheatData = NULL; - } - cheats[iSelectedEntry].enabled = false; - cheats[iSelectedEntry].cheatSize = 0; changedCheats = true; } else @@ -476,55 +528,57 @@ void cheats_toggle() static char filename[1024]; fileTYPE f = {}; - if (cheats[iSelectedEntry].cheatData) + /* lazy load cheat data */ + if (cheats[iSelectedEntry].cheatData == NULL) { - printf("Consistency error, memory for cheat already allocated -> cleanup.\n"); - delete[] cheats[iSelectedEntry].cheatData; - cheats[iSelectedEntry].cheatData = NULL; - cheats[iSelectedEntry].cheatSize = 0; - } - - snprintf(filename, sizeof(filename), "%s/%s", cheat_zip, cheats[iSelectedEntry].name); - if (FileOpen(&f, filename)) - { - int len = f.size; - if (!len || (len & 15)) + snprintf(filename, sizeof(filename), "%s/%s", cheat_zip, cheats[iSelectedEntry].name); + if (FileOpen(&f, filename)) { - printf("Cheat file %s has incorrect length %d -> skipping.\n", filename, len); - } - else if ((len + cheats_loaded() * 16) <= CHEAT_SIZE) - { - cheats[iSelectedEntry].cheatData = new char[len]; - if (cheats[iSelectedEntry].cheatData) + int len = f.size; + if (!len || (len % cheat_unit_size)) { - if (FileReadAdv(&f, cheats[iSelectedEntry].cheatData, len) == len) + printf("Cheat file %s has incorrect length %d -> skipping.\n", filename, len); + } + else if (((len / cheat_unit_size) + cheats_loaded()) <= cheat_max_active) + { + cheats[iSelectedEntry].cheatData = new char[len]; + if (cheats[iSelectedEntry].cheatData) { - cheats[iSelectedEntry].cheatSize = len; - cheats[iSelectedEntry].enabled = true; - changedCheats = true; + if (FileReadAdv(&f, cheats[iSelectedEntry].cheatData, len) == len) + { + cheats[iSelectedEntry].cheatSize = len; + cheats[iSelectedEntry].enabled = true; + changedCheats = true; + } + else + { + printf("Cannot read cheat file %s.\n", filename); + delete[] cheats[iSelectedEntry].cheatData; + cheats[iSelectedEntry].cheatData = NULL; + cheats[iSelectedEntry].cheatSize = 0; + } } else { - printf("Cannot read cheat file %s.\n", filename); - delete[] cheats[iSelectedEntry].cheatData; - cheats[iSelectedEntry].cheatData = NULL; - cheats[iSelectedEntry].cheatSize = 0; + printf("Could not allocate required memory (%d) for cheat file %s.\n", len, filename); } } else { - printf("Could not allocate required memory (%d) for cheat file %s.\n", len, filename); + printf("No more room in current selection for cheat file %s.\n", filename); } + FileClose(&f); } else { - printf("No more room in current selection for cheat file %s.\n", filename); + printf("Cannot open cheat file %s.\n", filename); } - FileClose(&f); } - else + + if (cheats[iSelectedEntry].cheatData) { - printf("Cannot open cheat file %s.\n", filename); + cheats[iSelectedEntry].enabled = true; + changedCheats = true; } } @@ -537,4 +591,4 @@ void cheats_toggle() int cheats_loaded() { return loaded; -} \ No newline at end of file +} diff --git a/cheats.h b/cheats.h index 9d8d83f..0404577 100644 --- a/cheats.h +++ b/cheats.h @@ -9,4 +9,8 @@ void cheats_print(); void cheats_toggle(); int cheats_loaded(); +void cheats_init_arcade(int unit_size, int max_active); +void cheats_add_arcade(const char *name, const char *cheatData, int cheatSize); +void cheats_finalize_arcade(); + #endif diff --git a/menu.cpp b/menu.cpp index 9219826..a096b22 100644 --- a/menu.cpp +++ b/menu.cpp @@ -975,7 +975,7 @@ void HandleUI(void) static int has_fb_terminal = 0; static unsigned long flash_timer = 0; static int flash_state = 0; - static uint32_t dip_submenu, dip2_submenu, dipv; + static uint32_t dip_submenu; static int need_reset = 0; static int flat = 0; static int menusub_parent = 0; @@ -1702,7 +1702,6 @@ void HandleUI(void) OsdSetTitle(page ? title : user_io_get_core_name()); dip_submenu = -1; - dip2_submenu = -1; int last_space = 0; @@ -1725,7 +1724,7 @@ void HandleUI(void) else if (!strcmp(p, "DIP")) { h = page; - if (!h && arcade_sw(0)->dip_num) + if (!h && arcade_sw()->dip_num) { dip_submenu = selentry; MenuWrite(entry, " DIP Switches \x16", menusub == selentry, 0); @@ -1735,19 +1734,6 @@ void HandleUI(void) } continue; } - else if (!strcmp(p, "CHEAT")) - { - h = page; - if (!h && arcade_sw(1)->dip_num) - { - dip2_submenu = selentry; - MenuWrite(entry, " Cheats \x16", menusub == selentry, 0); - entry++; - selentry++; - menumask = (menumask << 1) | 1; - } - continue; - } else { //Hide or Disable flag (small letter - opposite action) @@ -2094,9 +2080,8 @@ void HandleUI(void) select = 1; } - if ((dip_submenu == menusub || dip2_submenu == menusub) && select) + if (dip_submenu == menusub && select) { - dipv = (dip_submenu == menusub) ? 0 : 1; menustate = MENU_ARCADE_DIP1; menusub = 0; } @@ -2119,8 +2104,7 @@ void HandleUI(void) d = 0; inpage = !page; - if (!strcmp(p, "DIP")) h = page || !arcade_sw(0)->dip_num; - else if (!strcmp(p, "CHEAT")) h = page || !arcade_sw(1)->dip_num; + if (!strcmp(p, "DIP")) h = page || !arcade_sw()->dip_num; else if (strncmp(p, "DEFMRA,", 7)) { //Hide or Disable flag @@ -3115,7 +3099,7 @@ void HandleUI(void) case MENU_ARCADE_DIP1: helptext_idx = 0; menumask = 0; - OsdSetTitle(dipv ? "Cheats" : "DIP Switches"); + OsdSetTitle("DIP Switches"); menustate = MENU_ARCADE_DIP2; parentstate = MENU_ARCADE_DIP1; @@ -3128,7 +3112,7 @@ void HandleUI(void) uint32_t selentry = 0; menumask = 0; - sw_struct *sw = arcade_sw(dipv); + sw_struct *sw = arcade_sw(); int n = (sw->dip_num < OsdGetSize() - 1) ? (OsdGetSize() - 1 - sw->dip_num) / 2 : 0; for (; entry < n; entry++) MenuWrite(entry); @@ -3169,7 +3153,7 @@ void HandleUI(void) for (; entry < OsdGetSize() - 1; entry++) MenuWrite(entry, "", 0, 0); - MenuWrite(entry, dipv ? STD_BACK : " Reset to apply", menusub == selentry); + MenuWrite(entry, " Reset to apply", menusub == selentry); menusub_last = selentry; menumask = (menumask << 1) | 1; @@ -3182,30 +3166,22 @@ void HandleUI(void) if (menu || left) { menustate = MENU_GENERIC_MAIN1; - menusub = dipv ? dip2_submenu : dip_submenu; - arcade_sw_save(0); + menusub = dip_submenu; + arcade_sw_save(); } if (select) { if (menusub == menusub_last) { - if (!dipv) - { - arcade_sw_save(dipv); - user_io_status_set("[0]", 1); - user_io_status_set("[0]", 0); - menustate = MENU_NONE1; - } - else - { - menusub = dip2_submenu; - menustate = MENU_GENERIC_MAIN1; - } + arcade_sw_save(); + user_io_status_set("[0]", 1); + user_io_status_set("[0]", 0); + menustate = MENU_NONE1; } else { - sw_struct *sw = arcade_sw(dipv); + sw_struct *sw = arcade_sw(); uint64_t status = sw->dip_cur & sw->dip[menusub].mask; int m = 0; for (int n = 0; n < sw->dip[menusub].num; n++) @@ -3220,7 +3196,7 @@ void HandleUI(void) m = (m + 1) % sw->dip[menusub].num; sw->dip_cur = (sw->dip_cur & ~sw->dip[menusub].mask) | sw->dip[menusub].val[m]; menustate = MENU_ARCADE_DIP1; - arcade_sw_send(dipv); + arcade_sw_send(); } } break; @@ -5415,16 +5391,13 @@ void HandleUI(void) printf("Saving config to %s\n", filename); user_io_status_save(filename); menustate = MENU_GENERIC_MAIN1; - for (int n = 0; n < 2; n++) + if (arcade_sw()->dip_num) { - if (arcade_sw(n)->dip_num) - { - arcade_sw(n)->dip_cur = arcade_sw(n)->dip_def; - arcade_sw_send(n); - user_io_status_set("[0]", 1); - user_io_status_set("[0]", 0); - arcade_sw_save(n); - } + arcade_sw()->dip_cur = arcade_sw()->dip_def; + arcade_sw_send(); + user_io_status_set("[0]", 1); + user_io_status_set("[0]", 0); + arcade_sw_save(); } menustate = MENU_NONE1; menusub = 0; diff --git a/support/arcade/mra_loader.cpp b/support/arcade/mra_loader.cpp index 5099669..e967f35 100644 --- a/support/arcade/mra_loader.cpp +++ b/support/arcade/mra_loader.cpp @@ -13,6 +13,8 @@ #include "../../fpga_io.h" #include "../../lib/md5/md5.h" #include "../../shmem.h" +#include "../../str_util.h" +#include "../../cheats.h" #include "buffer.h" #include "mra_loader.h" @@ -25,6 +27,7 @@ struct arc_struct { char partname[kBigTextSize]; char romname[kBigTextSize]; char error_msg[kBigTextSize]; + char cheatname[kBigTextSize]; int romindex; int offset; int length; @@ -35,6 +38,7 @@ struct arc_struct { int validrom0; int insidesw; int insideinterleave; + int insidecheats; int ifrom; int ito; int imap; @@ -51,7 +55,7 @@ static char mame_root[kBigTextSize]; static bool is_vertical = false; -static sw_struct switches[2] = {}; +static sw_struct switches = {}; static int nvram_idx = 0; static int nvram_size = 0; @@ -76,7 +80,7 @@ void arcade_nvm_save() user_io_set_upload(0); FileSave(path, buf, nvram_size); - delete(buf); + delete [] buf; } } } @@ -101,37 +105,35 @@ static void arcade_nvm_load() user_io_set_download(0); } - delete(buf); + delete [] buf; } } } -sw_struct *arcade_sw(int n) +sw_struct *arcade_sw() { - if (n > 1) n = 1; - if (n < 0) n = 0; - return &switches[n]; + return &switches; } -void arcade_sw_send(int n) +void arcade_sw_send() { - sw_struct *sw = arcade_sw(n); + sw_struct *sw = arcade_sw(); if (sw->dip_num) { - user_io_set_index(254 + n); + user_io_set_index(254); user_io_set_download(1); user_io_file_tx_data((uint8_t*)&sw->dip_cur, sizeof(sw->dip_cur)); user_io_set_download(0); } } -void arcade_sw_save(int n) +void arcade_sw_save() { - sw_struct *sw = arcade_sw(n); + sw_struct *sw = arcade_sw(); if (sw->dip_num && sw->dip_saved != sw->dip_cur) { static char path[1024]; - strcpy(path, (n) ? CONFIG_DIR"/cheats/" : CONFIG_DIR"/dips/"); + strcpy(path, CONFIG_DIR"/dips/"); FileCreatePath(path); strcat(path, sw->name); if (FileSave(path, &sw->dip_cur, sizeof(sw->dip_cur))) @@ -141,11 +143,11 @@ void arcade_sw_save(int n) } } -void arcade_sw_load(int n) +void arcade_sw_load() { - sw_struct *sw = arcade_sw(n); + sw_struct *sw = arcade_sw(); static char path[1024]; - strcpy(path, (n) ? "cheats/" : "dips/"); + strcpy(path, "dips/"); strcat(path, sw->name); FileLoadConfig(path, &sw->dip_cur, sizeof(sw->dip_cur)); } @@ -207,7 +209,7 @@ static int rom_checksz(int idx, int chunk) { if ((romlen[idx] + chunk) > romblkl) { - if (romlen[idx] + chunk > romblkl + BLKL) + if (romlen[idx] + chunk > romblkl + BLKL) romblkl += (chunk + BLKL); else romblkl += BLKL; @@ -432,12 +434,16 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons static char message[32]; struct arc_struct *arc_info = (struct arc_struct *)sd->user; + int cheat_size = 0; + int cheat_max = 0; + switch (evt) { case XML_EVENT_START_DOC: message[0] = 0; arc_info->insiderom = 0; arc_info->insidesw = 0; + arc_info->insidecheats = 0; break; case XML_EVENT_START_NODE: @@ -447,6 +453,7 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons buffer_destroy(arc_info->data); arc_info->data = buffer_init(kBigTextSize); arc_info->partname[0] = 0; + arc_info->cheatname[0] = 0; arc_info->offset = 0; arc_info->length = -1; arc_info->repeat = 1; @@ -472,20 +479,12 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons if (!strcasecmp(node->tag, "switches")) { arc_info->insidesw = 1; - switches[0].dip_cur = 0; - switches[0].dip_def = 0; - switches[0].dip_num = 0; - memset(&switches[0].dip, 0, sizeof(switches[0].dip)); + switches.dip_cur = 0; + switches.dip_def = 0; + switches.dip_num = 0; + memset(&switches.dip, 0, sizeof(switches.dip)); } - if (!strcasecmp(node->tag, "cheats")) - { - arc_info->insidesw = 2; - switches[1].dip_cur = 0; - switches[1].dip_def = 0; - switches[1].dip_num = 0; - memset(&switches[1].dip, 0, sizeof(switches[1].dip)); - } if (!strcasecmp(node->tag, "interleave")) { @@ -602,8 +601,8 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons } else if (arc_info->insidesw) { - sw_struct* sw = &switches[arc_info->insidesw - 1]; - if (!strcasecmp(node->tag, "switches") || !strcasecmp(node->tag, "cheats")) + sw_struct* sw = &switches; + if (!strcasecmp(node->tag, "switches")) { if (!strcasecmp(node->attributes[i].name, "default")) { @@ -682,6 +681,16 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons } } } + else if (arc_info->insidecheats) + { + if (!strcasecmp(node->tag, "cheat")) + { + if (!strcasecmp(node->attributes[i].name, "name")) + { + strcpyz(arc_info->cheatname, node->attributes[i].value); + } + } + } else { if (!strcasecmp(node->attributes[i].name, "index") && !strcasecmp(node->tag, "nvram")) @@ -693,6 +702,18 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons { nvram_size = strtoul(node->attributes[i].value, NULL, 0); } + + if (!strcasecmp(node->tag, "cheats")) + { + if (!strcasecmp(node->attributes[i].name, "size")) + { + cheat_size = strtoul(node->attributes[i].value, NULL, 0); + } + if (!strcasecmp(node->attributes[i].name, "max")) + { + cheat_max = strtoul(node->attributes[i].value, NULL, 0); + } + } } } @@ -710,6 +731,12 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons rom_start(arc_info->romindex); } + if (!strcasecmp(node->tag, "cheats")) + { + arc_info->insidecheats = 1; + cheats_init_arcade(cheat_size, cheat_max); + } + if (arc_info->insiderom && !strcasecmp(node->tag, "interleave")) { int valid = 1; @@ -718,7 +745,7 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons if (arc_info->ito < arc_info->ifrom) valid = 0; unitlen = arc_info->ifrom ? arc_info->ito / arc_info->ifrom : 1; - if (unitlen < 0 && unitlen>8) valid = 0; + if (unitlen < 0 || unitlen>8) valid = 0; if (!valid) { @@ -913,7 +940,7 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons if (arc_info->insidesw && !strcasecmp(node->tag, "dip")) { - sw_struct* sw = &switches[arc_info->insidesw - 1]; + sw_struct* sw = &switches; int n = sw->dip_num; for (int i = 0; i < sw->dip[n].num; i++) @@ -931,6 +958,20 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons arc_info->insidesw = 0; } + if (arc_info->insidecheats && !strcasecmp(node->tag, "cheat")) + { + size_t len = 0; + unsigned char* binary = hexstr_to_char(arc_info->data->content, &len); + cheats_add_arcade(arc_info->cheatname, (char *)binary, len); + free(binary); + } + + if (!strcasecmp(node->tag, "cheats")) + { + cheats_finalize_arcade(); + arc_info->insidecheats = 0; + } + if (!strcasecmp(node->tag, "interleave")) { arc_info->ifrom = 0; @@ -1056,10 +1097,9 @@ int arcade_send_rom(const char *xml) { const char *p = strrchr(xml, '/'); p = p ? p + 1 : xml; - snprintf(switches[0].name, sizeof(switches[0].name), "%s", p); - char *ext = strcasestr(switches[0].name, ".mra"); + snprintf(switches.name, sizeof(switches.name), "%s", p); + char *ext = strcasestr(switches.name, ".mra"); if (ext) strcpy(ext, ".dip"); - memcpy(switches[1].name, switches[0].name, sizeof(switches[1].name)); snprintf(nvram_name, sizeof(nvram_name), p); ext = strcasestr(nvram_name, ".mra"); @@ -1090,13 +1130,10 @@ int arcade_send_rom(const char *xml) } buffer_destroy(arc_info.data); - for (int n = 0; n < 2; n++) - { - switches[n].dip_cur = switches[n].dip_def; - arcade_sw_load(n); - switches[n].dip_saved = switches[n].dip_cur; - arcade_sw_send(n); - } + switches.dip_cur = switches.dip_def; + arcade_sw_load(); + switches.dip_saved = switches.dip_cur; + arcade_sw_send(); return 0; } diff --git a/support/arcade/mra_loader.h b/support/arcade/mra_loader.h index f4c56a0..9982174 100644 --- a/support/arcade/mra_loader.h +++ b/support/arcade/mra_loader.h @@ -55,10 +55,10 @@ struct mgl_struct int done; }; -sw_struct *arcade_sw(int n); -void arcade_sw_send(int n); -void arcade_sw_save(int n); -void arcade_sw_load(int n); +sw_struct *arcade_sw(); +void arcade_sw_send(); +void arcade_sw_save(); +void arcade_sw_load(); // Read any mra info necessary for ini processing void arcade_pre_parse(const char *xml);