diff --git a/menu.cpp b/menu.cpp index 20605ce..0d8d775 100644 --- a/menu.cpp +++ b/menu.cpp @@ -164,7 +164,10 @@ enum MENU MENU_8BIT_INFO, MENU_8BIT_INFO2, MENU_8BIT_ABOUT1, - MENU_8BIT_ABOUT2 + MENU_8BIT_ABOUT2, + + MENU_ARCADE_DIP1, + MENU_ARCADE_DIP2 }; static uint32_t menustate = MENU_NONE1; @@ -820,6 +823,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; static char cp_MenuCancel; @@ -1293,6 +1297,7 @@ void HandleUI(void) else OsdCoreNameSet(p); OsdSetTitle(OsdCoreName(), 0); + dip_submenu = -1; // add options as requested by core int i = 2; @@ -1307,15 +1312,29 @@ void HandleUI(void) { int h = 0, d = 0; - //Hide or Disable flag (small letter - opposite action) - while((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p)>2) + if (!strcmp(p, "DIP")) { - int flg = (hdmask & (1<dip_num) + { + dip_submenu = selentry; + MenuWrite(entry, " DIP Switches \x16", menusub == selentry, 0); + entry++; + selentry++; + menumask = (menumask << 1) | 1; + } + } + else + { + //Hide or Disable flag (small letter - opposite action) + while ((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p) > 2) + { + int flg = (hdmask & (1 << getIdx(p))) ? 1 : 0; + if (p[0] == 'H') h |= flg; + if (p[0] == 'h') h |= (flg ^ 1); + if (p[0] == 'D') d |= flg; + if (p[0] == 'd') d |= (flg ^ 1); + p += 2; + } } if (!h) @@ -1505,6 +1524,11 @@ void HandleUI(void) { menustate = MENU_NONE1; } + else if (dip_submenu == menusub) + { + menustate = MENU_ARCADE_DIP1; + menusub = 0; + } else { static char ext[256]; @@ -1513,6 +1537,7 @@ void HandleUI(void) int h = 0, d = 0; uint32_t entry = 0; int i = 1; + while (1) { p = user_io_get_confstr(i++); @@ -1521,15 +1546,18 @@ void HandleUI(void) h = 0; d = 0; - //Hide or Disable flag - while ((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p) > 2) + if (strcmp(p, "DIP")) { - int flg = (hdmask & (1 << getIdx(p))) ? 1 : 0; - if (p[0] == 'H') h |= flg; - if (p[0] == 'h') h |= (flg ^ 1); - if (p[0] == 'D') d |= flg; - if (p[0] == 'd') d |= (flg ^ 1); - p += 2; + //Hide or Disable flag + while ((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p) > 2) + { + int flg = (hdmask & (1 << getIdx(p))) ? 1 : 0; + if (p[0] == 'H') h |= flg; + if (p[0] == 'h') h |= (flg ^ 1); + if (p[0] == 'D') d |= flg; + if (p[0] == 'd') d |= (flg ^ 1); + p += 2; + } } if (h || p[0] < 'A') continue; @@ -1989,6 +2017,111 @@ void HandleUI(void) if(!hold_cnt && reboot_req) fpga_load_rbf("menu.rbf"); break; + case MENU_ARCADE_DIP1: + helptext = 0; + menumask = 0; + OsdSetTitle("DIP Switches"); + menustate = MENU_ARCADE_DIP2; + parentstate = MENU_ARCADE_DIP1; + + while (1) + { + int entry = 0; + if (!menusub) firstmenu = 0; + + adjvisible = 0; + uint32_t selentry = 0; + menumask = 0; + + 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); + + for (int i = 0; i < sw->dip_num; i++) + { + uint64_t status = sw->dip_cur & sw->dip[i].mask; + int m = 0; + for (int n = 0; n < sw->dip[i].num; n++) + { + if (status == sw->dip[i].val[n]) + { + m = n; + break; + } + } + + char l = strlen(sw->dip[i].id[m]); + s[0] = ' '; + strcpy(s + 1, sw->dip[i].name); + + char *end = s + strlen(s) - 1; + while ((end > s + 1) && (*end == ' ')) end--; + *(end + 1) = 0; + + strcat(s, ":"); + l = 28 - l - strlen(s); + while (l--) strcat(s, " "); + + strcat(s, sw->dip[i].id[m]); + + MenuWrite(entry, s, menusub == selentry); + + menumask = (menumask << 1) | 1; + entry++; + selentry++; + }; + + for (; entry < OsdGetSize() - 1; entry++) MenuWrite(entry, "", 0, 0); + + MenuWrite(entry, " Reset to apply", menusub == selentry); + menusub_last = selentry; + menumask = (menumask << 1) | 1; + + if (!adjvisible) break; + firstmenu += adjvisible; + } + break; + + case MENU_ARCADE_DIP2: + if (menu) + { + menustate = MENU_8BIT_MAIN1; + menusub = dip_submenu; + arcade_sw_save(); + } + + if (select) + { + if (menusub == menusub_last) + { + arcade_sw_save(); + user_io_8bit_set_status(UIO_STATUS_RESET, UIO_STATUS_RESET); + user_io_8bit_set_status(0, UIO_STATUS_RESET); + menustate = MENU_NONE1; + } + else + { + 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++) + { + if (status == sw->dip[menusub].val[n]) + { + m = n; + break; + } + } + + 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(); + } + } + break; + case MENU_UART1: { helptext = 0; @@ -3592,6 +3725,15 @@ void HandleUI(void) printf("Saving config to %s\n", filename); FileSaveConfig(filename, status, 8); menustate = MENU_8BIT_MAIN1; + if (arcade_sw()->dip_num) + { + arcade_sw()->dip_cur = arcade_sw()->dip_def; + arcade_sw_send(); + arcade_sw_save(); + user_io_8bit_set_status(UIO_STATUS_RESET, UIO_STATUS_RESET); + user_io_8bit_set_status(0, UIO_STATUS_RESET); + menustate = MENU_NONE1; + } menusub = 0; } } diff --git a/support/arcade/romutils.cpp b/support/arcade/romutils.cpp index 10ffd47..d55cf15 100644 --- a/support/arcade/romutils.cpp +++ b/support/arcade/romutils.cpp @@ -13,6 +13,7 @@ #include "../../lib/md5/md5.h" #include "buffer.h" +#include "romutils.h" #define kBigTextSize 1024 struct arc_struct { @@ -29,6 +30,7 @@ struct arc_struct { int insiderom; int patchaddr; int validrom0; + int insidesw; buffer_data *data; struct MD5Context context; }; @@ -37,6 +39,45 @@ static char arcade_error_msg[kBigTextSize] = {}; static char arcade_root[kBigTextSize]; static char mame_root[kBigTextSize]; +static sw_struct switches = {}; + +sw_struct *arcade_sw() +{ + return &switches; +} + +void arcade_sw_send() +{ + if (switches.dip_num) + { + user_io_set_index(254); + user_io_set_download(1); + user_io_file_tx_write((uint8_t*)&switches.dip_cur, sizeof(switches.dip_cur)); + user_io_set_download(0); + } +} + +void arcade_sw_save() +{ + if (switches.dip_saved != switches.dip_cur) + { + char path[256] = CONFIG_DIR"/dips/"; + FileCreatePath(path); + strcat(path, switches.name); + if (FileSave(path, &switches.dip_cur, sizeof(switches.dip_cur))) + { + switches.dip_saved = switches.dip_cur; + } + } +} + +void arcade_sw_load() +{ + char path[256] = "dips/"; + strcat(path, switches.name); + FileLoadConfig(path, &switches.dip_cur, sizeof(switches.dip_cur)); +} + static void set_arcade_root(const char *path) { strcpy(arcade_root, path); @@ -213,13 +254,13 @@ static void file_finish(int send) unsigned char* hexstr_to_char(const char* hexstr, size_t *out_len) { size_t len = strlen(hexstr); - unsigned char* chrs = (unsigned char*)malloc((len + 1) * sizeof(*chrs)); + unsigned char* chrs = (unsigned char*)malloc(len + 1); int dest = 0; // point to the beginning of the array const char *ptr = hexstr; while (*ptr) { // check to see if we have a space - while (*ptr == '\n' || *ptr == '\r' || *ptr == ' ' || *ptr == '\t' || *ptr == 9 /*horiz tab*/) ptr++; + while (*ptr == '\n' || *ptr == '\r' || *ptr == ' ' || *ptr == ',' || *ptr == '\t' || *ptr == 9 /*horiz tab*/) ptr++; if (*ptr == 0) break; // pull two characters off @@ -256,6 +297,7 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons { case XML_EVENT_START_DOC: arc_info->insiderom = 0; + arc_info->insidesw = 0; break; case XML_EVENT_START_NODE: @@ -279,6 +321,15 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons MD5Init(&arc_info->context); } + if (!strcasecmp(node->tag, "switches")) + { + arc_info->insidesw = 1; + switches.dip_cur = 0; + switches.dip_def = 0; + switches.dip_num = 0; + memset(&switches.dip, 0, sizeof(switches.dip)); + } + // for each part tag, we clear the partzipname since it is optional and may not appear in the part tag if (!strcasecmp(node->tag, "part")) arc_info->partzipname[0] = 0; @@ -335,6 +386,87 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons arc_info->patchaddr = strtoul(node->attributes[i].value, NULL, 0); } } + + if (arc_info->insidesw) + { + if (!strcasecmp(node->tag, "switches")) + { + if (!strcasecmp(node->attributes[i].name, "default")) + { + size_t len = 0; + unsigned char* binary = hexstr_to_char(node->attributes[i].value, &len); + for (size_t i = 0; i < len; i++) switches.dip_def |= binary[i] << (i * 8); + free(binary); + } + } + + if (!strcasecmp(node->tag, "dip")) + { + if (!strcasecmp(node->attributes[i].name, "name")) + { + snprintf(switches.dip[switches.dip_num].name, sizeof(switches.dip[switches.dip_num].name), node->attributes[i].value); + } + + if (!strcasecmp(node->attributes[i].name, "bits")) + { + int b = 0, e = 0; + int num = sscanf(node->attributes[i].value, "%d,%d", &b, &e); + if (num <= 0 || b < 0 || b > 63 || e < 0 || e > 63 || (num == 2 && e < b)) + { + printf("Invalid bits field: ""%s"" (%d, %d, %d)\n", node->attributes[i].value, num, b, e); + } + else + { + uint64_t mask = 1; + if (num == 1) e = b; + switches.dip[switches.dip_num].start = b; + for (int i = 0; i < (e - b); i++) mask = (mask << 1) | 1; + switches.dip[switches.dip_num].mask = mask << b; + switches.dip[switches.dip_num].size = e - b + 1; + } + } + + if (!strcasecmp(node->attributes[i].name, "ids")) + { + int n = 0; + char *val = node->attributes[i].value; + while (*val && n < 32) + { + char *p = strchr(val, ','); + size_t len = p ? p - val : strlen(val); + size_t sz = len + 1; + if (sz > sizeof(switches.dip[0].id[0])) sz = sizeof(switches.dip[0].id[0]); + snprintf(switches.dip[switches.dip_num].id[n], sz, val); + val += len; + if (*val == ',') val++; + n++; + } + switches.dip[switches.dip_num].num = n; + } + + if (!strcasecmp(node->attributes[i].name, "values")) + { + int n = 0; + char *val = node->attributes[i].value; + while (*val && n < 32) + { + char *endp = 0; + uint64_t v = strtoul(val, &endp, 0); + if (endp <= val) + { + printf("Invalid values field: ""%s""\n", node->attributes[i].value); + break; + } + + switches.dip[switches.dip_num].val[n] = v; + val = endp; + while (*val && (*val == ' ' || *val == ',')) val++; + n++; + } + switches.dip[switches.dip_num].has_val = 1; + } + } + } } /* at the beginning of each rom - tell the user_io to start a new message */ @@ -480,6 +612,22 @@ static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons free(binary); } } + + if (!strcasecmp(node->tag, "dip")) + { + int n = switches.dip_num; + for (int i = 0; i < switches.dip[n].num; i++) + { + switches.dip[n].val[i] = ((switches.dip[n].has_val) ? switches.dip[n].val[i] : i) << switches.dip[n].start; + } + + if (switches.dip_num < 63) switches.dip_num++; + } + + if (!strcasecmp(node->tag, "switches")) + { + arc_info->insidesw = 0; + } break; case XML_EVENT_ERROR: @@ -530,8 +678,22 @@ static int xml_scan_rbf(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, cons return true; } +static int arcade_type = 0; +int is_arcade() +{ + return arcade_type; +} + int arcade_send_rom(const char *xml) { + arcade_type = 1; + + const char *p = strrchr(xml, '/'); + p = p ? p + 1 : xml; + snprintf(switches.name, sizeof(switches.name), p); + char *ext = strcasestr(switches.name, ".mra"); + if (ext) strcpy(ext, ".dip"); + SAX_Callbacks sax; SAX_Callbacks_init(&sax); @@ -553,6 +715,11 @@ int arcade_send_rom(const char *xml) printf("arcade_send_rom: pretty error: [%s]\n", arcade_error_msg); } buffer_destroy(arc_info.data); + + 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/romutils.h b/support/arcade/romutils.h index 703e98a..0293961 100644 --- a/support/arcade/romutils.h +++ b/support/arcade/romutils.h @@ -4,5 +4,33 @@ int arcade_send_rom(const char *xml); int arcade_load(const char *xml); void arcade_check_error(); +int is_arcade(); + +struct dip_struct +{ + int start; + int size; + int num; + int has_val; + uint64_t mask; + char name[32]; + char id[32][32]; + uint64_t val[32]; +}; + +struct sw_struct +{ + char name[1024]; + int dip_num; + uint64_t dip_def; + uint64_t dip_cur; + uint64_t dip_saved; + dip_struct dip[64]; +}; + +sw_struct *arcade_sw(); +void arcade_sw_send(); +void arcade_sw_save(); +void arcade_sw_load(); #endif