src/emu/uiswlist.c
| r0 | r11097 | |
| 1 | /********************************************************************* |
| 2 | |
| 3 | uiswlist.c |
| 4 | |
| 5 | Internal MAME user interface for software list. |
| 6 | |
| 7 | Copyright Nicola Salmoria and the MAME Team. |
| 8 | Visit http://mamedev.org for licensing and usage restrictions. |
| 9 | |
| 10 | *********************************************************************/ |
| 11 | |
| 12 | #include "emu.h" |
| 13 | #include "ui.h" |
| 14 | #include "uimenu.h" |
| 15 | #include "softlist.h" |
| 16 | |
| 17 | |
| 18 | /*************************************************************************** |
| 19 | CONSTANTS |
| 20 | ***************************************************************************/ |
| 21 | |
| 22 | /* time (in seconds) to display errors */ |
| 23 | #define ERROR_MESSAGE_TIME 5 |
| 24 | |
| 25 | |
| 26 | /*************************************************************************** |
| 27 | TYPE DEFINITIONS |
| 28 | ***************************************************************************/ |
| 29 | |
| 30 | /* state of a software entry */ |
| 31 | typedef struct _software_entry_state software_entry_state; |
| 32 | struct _software_entry_state |
| 33 | { |
| 34 | software_entry_state *next; |
| 35 | |
| 36 | const char *short_name; |
| 37 | const char *long_name; |
| 38 | const char *interface; |
| 39 | char *list_name; |
| 40 | device_image_interface* image; |
| 41 | }; |
| 42 | |
| 43 | /* state of the software menu */ |
| 44 | typedef struct _software_menu_state software_menu_state; |
| 45 | struct _software_menu_state |
| 46 | { |
| 47 | char *list_name; /* currently selected list */ |
| 48 | device_image_interface* image; |
| 49 | software_entry_state *entrylist; |
| 50 | char filename_buffer[1024]; |
| 51 | }; |
| 52 | |
| 53 | /* state of a software part */ |
| 54 | typedef struct _software_part_state software_part_state; |
| 55 | struct _software_part_state |
| 56 | { |
| 57 | const char *part_name; |
| 58 | const char *interface; |
| 59 | }; |
| 60 | |
| 61 | |
| 62 | static void ui_mess_menu_populate_software_parts(running_machine &machine, ui_menu *menu, const char *swlist, const char *swinfo, const char *interface) |
| 63 | { |
| 64 | software_list *list = software_list_open(machine.options(), swlist, FALSE, NULL); |
| 65 | |
| 66 | if (list) |
| 67 | { |
| 68 | software_info *info = software_list_find(list, swinfo, NULL); |
| 69 | |
| 70 | if (info) |
| 71 | { |
| 72 | for (software_part *swpart = software_find_part(info, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 73 | { |
| 74 | if (strcmp(interface, swpart->interface_) == 0) |
| 75 | { |
| 76 | software_part_state *entry = (software_part_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); |
| 77 | // check if the available parts have specific part_id to be displayed (e.g. "Map Disc", "Bonus Disc", etc.) |
| 78 | // if not, we simply display "part_name"; if yes we display "part_name (part_id)" |
| 79 | astring menu_part_name(swpart->name); |
| 80 | if (software_part_get_feature(swpart, "part_id") != NULL) |
| 81 | { |
| 82 | menu_part_name.cat(" ("); |
| 83 | menu_part_name.cat(software_part_get_feature(swpart, "part_id")); |
| 84 | menu_part_name.cat(")"); |
| 85 | } |
| 86 | entry->part_name = ui_menu_pool_strdup(menu, swpart->name); // part_name is later used to build up the filename to load, so we use swpart->name! |
| 87 | entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); |
| 88 | ui_menu_item_append(menu, info->shortname, menu_part_name.cstr(), 0, entry); |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | software_list_close(list); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | void ui_mess_menu_software_parts(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 98 | { |
| 99 | const ui_menu_event *event; |
| 100 | software_entry_state *sw_state = (software_entry_state *)state; |
| 101 | const char *swlist = sw_state->list_name; |
| 102 | const char *swinfo = sw_state->short_name; |
| 103 | const char *interface = sw_state->interface; |
| 104 | |
| 105 | // generate list of available parts |
| 106 | if (!ui_menu_populated(menu)) |
| 107 | { |
| 108 | if (sw_state->list_name) |
| 109 | { |
| 110 | ui_mess_menu_populate_software_parts(machine, menu, swlist, swinfo, interface); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | /* process the menu */ |
| 115 | event = ui_menu_process(machine, menu, 0); |
| 116 | |
| 117 | if (event != NULL && event->iptkey == IPT_UI_SELECT && event->itemref != NULL) |
| 118 | { |
| 119 | software_part_state *entry = (software_part_state *) event->itemref; |
| 120 | |
| 121 | // build the name for the part to be loaded |
| 122 | astring temp_name(sw_state->short_name); |
| 123 | temp_name.cat(":"); |
| 124 | temp_name.cat(entry->part_name); |
| 125 | //printf("%s\n", temp_name.cstr()); |
| 126 | |
| 127 | sw_state->image->load(temp_name.cstr()); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | static int compare_software_entries(const software_entry_state *e1, const software_entry_state *e2) |
| 132 | { |
| 133 | int result; |
| 134 | const char *e1_basename = (e1->short_name != NULL) ? e1->short_name : ""; |
| 135 | const char *e2_basename = (e2->short_name != NULL) ? e2->short_name : ""; |
| 136 | |
| 137 | result = mame_stricmp(e1_basename, e2_basename); |
| 138 | if (result == 0) |
| 139 | { |
| 140 | result = strcmp(e1_basename, e2_basename); |
| 141 | if (result == 0) |
| 142 | { |
| 143 | if (e1 < e2) |
| 144 | result = -1; |
| 145 | else if (e1 > e2) |
| 146 | result = 1; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | return result; |
| 151 | } |
| 152 | |
| 153 | /* populate a specific list */ |
| 154 | |
| 155 | static software_entry_state *append_software_entry(ui_menu *menu, software_menu_state *menustate, |
| 156 | software_info *swinfo, char *list_name, device_image_interface* image) |
| 157 | { |
| 158 | software_entry_state *entry = NULL; |
| 159 | software_entry_state **entryptr; |
| 160 | const char *interface = image->image_config().image_interface(); |
| 161 | |
| 162 | // check if at least one of the parts has the correct interface and add a menu entry only in this case |
| 163 | for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 164 | { |
| 165 | if (strcmp(interface, swpart->interface_) == 0) |
| 166 | { |
| 167 | // allocate a new entry |
| 168 | entry = (software_entry_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); |
| 169 | memset(entry, 0, sizeof(*entry)); |
| 170 | |
| 171 | entry->short_name = ui_menu_pool_strdup(menu, swinfo->shortname); |
| 172 | entry->long_name = ui_menu_pool_strdup(menu, swinfo->longname); |
| 173 | entry->list_name = list_name; |
| 174 | entry->image = image; |
| 175 | entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); |
| 176 | break; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | // find the end of the list |
| 181 | entryptr = &menustate->entrylist; |
| 182 | while ((*entryptr != NULL) && (compare_software_entries(entry, *entryptr) >= 0)) |
| 183 | entryptr = &(*entryptr)->next; |
| 184 | |
| 185 | // insert the entry |
| 186 | entry->next = *entryptr; |
| 187 | *entryptr = entry; |
| 188 | |
| 189 | return entry; |
| 190 | } |
| 191 | |
| 192 | |
| 193 | static void ui_mess_menu_populate_software_entries(running_machine &machine, ui_menu *menu, software_menu_state *menustate) |
| 194 | { |
| 195 | software_list *list = software_list_open(machine.options(), menustate->list_name, FALSE, NULL); |
| 196 | |
| 197 | // build up the list of entries for the menu |
| 198 | if (list) |
| 199 | { |
| 200 | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 201 | append_software_entry(menu, menustate, swinfo, menustate->list_name, menustate->image); |
| 202 | |
| 203 | software_list_close(list); |
| 204 | } |
| 205 | |
| 206 | // append all of the menu entries |
| 207 | for (software_entry_state *entry = menustate->entrylist; entry != NULL; entry = entry->next) |
| 208 | ui_menu_item_append(menu, entry->short_name, entry->long_name, 0, entry); |
| 209 | } |
| 210 | |
| 211 | bool swinfo_has_multiple_parts(software_info *swinfo, const char *interface) |
| 212 | { |
| 213 | int count = 0; |
| 214 | |
| 215 | for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 216 | { |
| 217 | if (strcmp(interface, swpart->interface_) == 0) |
| 218 | count++; |
| 219 | } |
| 220 | return (count > 1) ? TRUE : FALSE; |
| 221 | } |
| 222 | |
| 223 | void ui_mess_menu_software_list(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 224 | { |
| 225 | const ui_menu_event *event; |
| 226 | software_menu_state *sw_state = (software_menu_state *)state; |
| 227 | const software_entry_state *entry; |
| 228 | const software_entry_state *selected_entry = NULL; |
| 229 | int bestmatch = 0; |
| 230 | |
| 231 | if (!ui_menu_populated(menu)) |
| 232 | { |
| 233 | if (sw_state->list_name) |
| 234 | { |
| 235 | ui_mess_menu_populate_software_entries(machine, menu, sw_state); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | /* process the menu */ |
| 240 | event = ui_menu_process(machine, menu, 0); |
| 241 | |
| 242 | if (event != NULL && event->itemref != NULL) |
| 243 | { |
| 244 | /* handle selections */ |
| 245 | if (event->iptkey == IPT_UI_SELECT) |
| 246 | { |
| 247 | device_image_interface *image = sw_state->image; |
| 248 | software_entry_state *entry = (software_entry_state *) event->itemref; |
| 249 | software_list *tmp_list = software_list_open(machine.options(), sw_state->list_name, FALSE, NULL); |
| 250 | software_info *tmp_info = software_list_find(tmp_list, entry->short_name, NULL); |
| 251 | |
| 252 | // if the selected software has multiple parts that can be loaded, open the submenu |
| 253 | if (swinfo_has_multiple_parts(tmp_info, image->image_config().image_interface())) |
| 254 | { |
| 255 | ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_parts, entry); |
| 256 | software_entry_state *child_menustate = (software_entry_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); |
| 257 | child_menustate->short_name = entry->short_name; |
| 258 | child_menustate->interface = image->image_config().image_interface(); |
| 259 | child_menustate->list_name = sw_state->list_name; |
| 260 | child_menustate->image = image; |
| 261 | ui_menu_stack_push(child_menu); |
| 262 | } |
| 263 | else |
| 264 | { |
| 265 | // otherwise, load the file |
| 266 | if (image != NULL) |
| 267 | image->load(entry->short_name); |
| 268 | else |
| 269 | popmessage("No matching device found for interface '%s'!", entry->interface); |
| 270 | } |
| 271 | software_list_close(tmp_list); |
| 272 | |
| 273 | // reset the char buffer when pressing IPT_UI_SELECT |
| 274 | if (sw_state->filename_buffer[0] != '\0') |
| 275 | memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); |
| 276 | } |
| 277 | else if (event->iptkey == IPT_SPECIAL) |
| 278 | { |
| 279 | int buflen = strlen(sw_state->filename_buffer); |
| 280 | bool update_selected = FALSE; |
| 281 | |
| 282 | /* if it's a backspace and we can handle it, do so */ |
| 283 | if ((event->unichar == 8 || event->unichar == 0x7f) && buflen > 0) |
| 284 | { |
| 285 | *(char *)utf8_previous_char(&sw_state->filename_buffer[buflen]) = 0; |
| 286 | update_selected = TRUE; |
| 287 | |
| 288 | if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) |
| 289 | ui_popup_time(ERROR_MESSAGE_TIME, "%s", sw_state->filename_buffer); |
| 290 | } |
| 291 | /* if it's any other key and we're not maxed out, update */ |
| 292 | else if (event->unichar >= ' ' && event->unichar < 0x7f) |
| 293 | { |
| 294 | buflen += utf8_from_uchar(&sw_state->filename_buffer[buflen], ARRAY_LENGTH(sw_state->filename_buffer) - buflen, event->unichar); |
| 295 | sw_state->filename_buffer[buflen] = 0; |
| 296 | update_selected = TRUE; |
| 297 | |
| 298 | if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) |
| 299 | ui_popup_time(ERROR_MESSAGE_TIME, "%s", sw_state->filename_buffer); |
| 300 | } |
| 301 | |
| 302 | if (update_selected) |
| 303 | { |
| 304 | const software_entry_state *cur_selected = (const software_entry_state *)ui_menu_get_selection(menu); |
| 305 | |
| 306 | // check for entries which matches our filename_buffer: |
| 307 | // from current entry to the end |
| 308 | for (entry = cur_selected; entry != NULL; entry = entry->next) |
| 309 | { |
| 310 | if (entry->short_name != NULL && sw_state->filename_buffer != NULL) |
| 311 | { |
| 312 | int match = 0; |
| 313 | for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) |
| 314 | { |
| 315 | if (mame_strnicmp(entry->short_name, sw_state->filename_buffer, i) == 0) |
| 316 | match = i; |
| 317 | } |
| 318 | |
| 319 | if (match > bestmatch) |
| 320 | { |
| 321 | bestmatch = match; |
| 322 | selected_entry = entry; |
| 323 | } |
| 324 | } |
| 325 | } |
| 326 | // and from the first entry to current one |
| 327 | for (entry = sw_state->entrylist; entry != cur_selected; entry = entry->next) |
| 328 | { |
| 329 | if (entry->short_name != NULL && sw_state->filename_buffer != NULL) |
| 330 | { |
| 331 | int match = 0; |
| 332 | for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) |
| 333 | { |
| 334 | if (mame_strnicmp(entry->short_name, sw_state->filename_buffer, i) == 0) |
| 335 | match = i; |
| 336 | } |
| 337 | |
| 338 | if (match > bestmatch) |
| 339 | { |
| 340 | bestmatch = match; |
| 341 | selected_entry = entry; |
| 342 | } |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | if (selected_entry != NULL && selected_entry != cur_selected) |
| 347 | ui_menu_set_selection(menu, (void *) selected_entry); |
| 348 | } |
| 349 | } |
| 350 | else if (event->iptkey == IPT_UI_CANCEL) |
| 351 | { |
| 352 | // reset the char buffer also in this case |
| 353 | if (sw_state->filename_buffer[0] != '\0') |
| 354 | memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); |
| 355 | } |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | /* list of available software lists - i.e. cartridges, floppies */ |
| 360 | static void ui_mess_menu_populate_software_list(running_machine &machine, ui_menu *menu, device_image_interface* image) |
| 361 | { |
| 362 | bool haveCompatible = FALSE; |
| 363 | const char *interface = image->image_config().image_interface(); |
| 364 | |
| 365 | // Add original software lists for this system |
| 366 | for (const device_config *dev = machine.config().m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) |
| 367 | { |
| 368 | software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); |
| 369 | |
| 370 | for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) |
| 371 | { |
| 372 | if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)) |
| 373 | { |
| 374 | software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); |
| 375 | |
| 376 | if (list) |
| 377 | { |
| 378 | bool found = FALSE; |
| 379 | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 380 | { |
| 381 | software_part *part = software_find_part(swinfo, NULL, NULL); |
| 382 | if (strcmp(interface,part->interface_)==0) { |
| 383 | found = TRUE; |
| 384 | } |
| 385 | } |
| 386 | if (found) { |
| 387 | ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); |
| 388 | } |
| 389 | |
| 390 | software_list_close(list); |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | // Add compatible software lists for this system |
| 397 | for (const device_config *dev = machine.config().m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) |
| 398 | { |
| 399 | software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); |
| 400 | |
| 401 | for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) |
| 402 | { |
| 403 | if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_COMPATIBLE_SYSTEM)) |
| 404 | { |
| 405 | software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); |
| 406 | |
| 407 | if (list) |
| 408 | { |
| 409 | bool found = FALSE; |
| 410 | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 411 | { |
| 412 | software_part *part = software_find_part(swinfo, NULL, NULL); |
| 413 | if (strcmp(interface,part->interface_)==0) { |
| 414 | found = TRUE; |
| 415 | } |
| 416 | } |
| 417 | if (found) { |
| 418 | if (!haveCompatible) { |
| 419 | ui_menu_item_append(menu, "[compatible lists]", NULL, 0, NULL); |
| 420 | } |
| 421 | ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); |
| 422 | } |
| 423 | |
| 424 | haveCompatible = TRUE; |
| 425 | software_list_close(list); |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | } |
| 432 | |
| 433 | void ui_image_menu_software(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 434 | { |
| 435 | const ui_menu_event *event; |
| 436 | device_image_interface* image = (device_image_interface*)parameter; |
| 437 | if (!ui_menu_populated(menu)) |
| 438 | ui_mess_menu_populate_software_list(machine, menu, image); |
| 439 | |
| 440 | /* process the menu */ |
| 441 | event = ui_menu_process(machine, menu, 0); |
| 442 | |
| 443 | if (event != NULL && event->iptkey == IPT_UI_SELECT) |
| 444 | { |
| 445 | ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_list, NULL); |
| 446 | software_menu_state *child_menustate = (software_menu_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); |
| 447 | child_menustate->list_name = (char *)event->itemref; |
| 448 | child_menustate->image = image; |
| 449 | child_menustate->entrylist = NULL; |
| 450 | ui_menu_stack_push(child_menu); |
| 451 | } |
| 452 | } |
src/emu/softlist.c
| r11096 | r11097 | |
| 9 | 9 | |
| 10 | 10 | #include "emu.h" |
| 11 | 11 | #include "pool.h" |
| 12 | | #include "expat.h" |
| 13 | 12 | #include "emuopts.h" |
| 14 | 13 | #include "hash.h" |
| 15 | 14 | #include "softlist.h" |
| 16 | | #include "ui.h" |
| 17 | 15 | |
| 18 | 16 | #include <ctype.h> |
| 19 | 17 | |
| 20 | | enum softlist_parse_position |
| 21 | | { |
| 22 | | POS_ROOT, |
| 23 | | POS_MAIN, |
| 24 | | POS_SOFT, |
| 25 | | POS_PART, |
| 26 | | POS_DATA |
| 27 | | }; |
| 28 | | |
| 29 | | |
| 30 | | typedef struct _parse_state |
| 31 | | { |
| 32 | | XML_Parser parser; |
| 33 | | int done; |
| 34 | | |
| 35 | | void (*error_proc)(const char *message); |
| 36 | | void *param; |
| 37 | | |
| 38 | | enum softlist_parse_position pos; |
| 39 | | char **text_dest; |
| 40 | | } parse_state; |
| 41 | | |
| 42 | | |
| 43 | | struct _software_list |
| 44 | | { |
| 45 | | emu_file *file; |
| 46 | | object_pool *pool; |
| 47 | | parse_state state; |
| 48 | | const char *description; |
| 49 | | struct software_info *software_info_list; |
| 50 | | struct software_info *current_software_info; |
| 51 | | software_info *softinfo; |
| 52 | | const char *look_for; |
| 53 | | int part_entries; |
| 54 | | int current_part_entry; |
| 55 | | int rom_entries; |
| 56 | | int current_rom_entry; |
| 57 | | void (*error_proc)(const char *message); |
| 58 | | int list_entries; |
| 59 | | }; |
| 60 | | |
| 61 | | |
| 62 | 18 | typedef tagmap_t<software_info *> softlist_map; |
| 63 | 19 | |
| 64 | 20 | /*************************************************************************** |
| r11096 | r11097 | |
| 1842 | 1798 | } |
| 1843 | 1799 | |
| 1844 | 1800 | |
| 1845 | | /*************************************************************************** |
| 1846 | | MENU SUPPORT |
| 1847 | | ***************************************************************************/ |
| 1848 | | |
| 1849 | | /* state of a software entry */ |
| 1850 | | typedef struct _software_entry_state software_entry_state; |
| 1851 | | struct _software_entry_state |
| 1852 | | { |
| 1853 | | software_entry_state *next; |
| 1854 | | |
| 1855 | | const char *short_name; |
| 1856 | | const char *long_name; |
| 1857 | | const char *interface; |
| 1858 | | char *list_name; |
| 1859 | | device_image_interface* image; |
| 1860 | | }; |
| 1861 | | |
| 1862 | | /* state of the software menu */ |
| 1863 | | typedef struct _software_menu_state software_menu_state; |
| 1864 | | struct _software_menu_state |
| 1865 | | { |
| 1866 | | char *list_name; /* currently selected list */ |
| 1867 | | device_image_interface* image; |
| 1868 | | software_entry_state *entrylist; |
| 1869 | | char filename_buffer[1024]; |
| 1870 | | }; |
| 1871 | | |
| 1872 | | /* state of a software part */ |
| 1873 | | typedef struct _software_part_state software_part_state; |
| 1874 | | struct _software_part_state |
| 1875 | | { |
| 1876 | | const char *part_name; |
| 1877 | | const char *interface; |
| 1878 | | }; |
| 1879 | | |
| 1880 | | |
| 1881 | | static void ui_mess_menu_populate_software_parts(running_machine &machine, ui_menu *menu, const char *swlist, const char *swinfo, const char *interface) |
| 1882 | | { |
| 1883 | | software_list *list = software_list_open(machine.options(), swlist, FALSE, NULL); |
| 1884 | | |
| 1885 | | if (list) |
| 1886 | | { |
| 1887 | | software_info *info = software_list_find(list, swinfo, NULL); |
| 1888 | | |
| 1889 | | if (info) |
| 1890 | | { |
| 1891 | | for (software_part *swpart = software_find_part(info, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 1892 | | { |
| 1893 | | if (strcmp(interface, swpart->interface_) == 0) |
| 1894 | | { |
| 1895 | | software_part_state *entry = (software_part_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); |
| 1896 | | // check if the available parts have specific part_id to be displayed (e.g. "Map Disc", "Bonus Disc", etc.) |
| 1897 | | // if not, we simply display "part_name"; if yes we display "part_name (part_id)" |
| 1898 | | astring menu_part_name(swpart->name); |
| 1899 | | if (software_part_get_feature(swpart, "part_id") != NULL) |
| 1900 | | { |
| 1901 | | menu_part_name.cat(" ("); |
| 1902 | | menu_part_name.cat(software_part_get_feature(swpart, "part_id")); |
| 1903 | | menu_part_name.cat(")"); |
| 1904 | | } |
| 1905 | | entry->part_name = ui_menu_pool_strdup(menu, swpart->name); // part_name is later used to build up the filename to load, so we use swpart->name! |
| 1906 | | entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); |
| 1907 | | ui_menu_item_append(menu, info->shortname, menu_part_name.cstr(), 0, entry); |
| 1908 | | } |
| 1909 | | } |
| 1910 | | } |
| 1911 | | |
| 1912 | | software_list_close(list); |
| 1913 | | } |
| 1914 | | } |
| 1915 | | |
| 1916 | | void ui_mess_menu_software_parts(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 1917 | | { |
| 1918 | | const ui_menu_event *event; |
| 1919 | | software_entry_state *sw_state = (software_entry_state *)state; |
| 1920 | | const char *swlist = sw_state->list_name; |
| 1921 | | const char *swinfo = sw_state->short_name; |
| 1922 | | const char *interface = sw_state->interface; |
| 1923 | | |
| 1924 | | // generate list of available parts |
| 1925 | | if (!ui_menu_populated(menu)) |
| 1926 | | { |
| 1927 | | if (sw_state->list_name) |
| 1928 | | { |
| 1929 | | ui_mess_menu_populate_software_parts(machine, menu, swlist, swinfo, interface); |
| 1930 | | } |
| 1931 | | } |
| 1932 | | |
| 1933 | | /* process the menu */ |
| 1934 | | event = ui_menu_process(machine, menu, 0); |
| 1935 | | |
| 1936 | | if (event != NULL && event->iptkey == IPT_UI_SELECT && event->itemref != NULL) |
| 1937 | | { |
| 1938 | | software_part_state *entry = (software_part_state *) event->itemref; |
| 1939 | | |
| 1940 | | // build the name for the part to be loaded |
| 1941 | | astring temp_name(sw_state->short_name); |
| 1942 | | temp_name.cat(":"); |
| 1943 | | temp_name.cat(entry->part_name); |
| 1944 | | //printf("%s\n", temp_name.cstr()); |
| 1945 | | |
| 1946 | | sw_state->image->load(temp_name.cstr()); |
| 1947 | | } |
| 1948 | | } |
| 1949 | | |
| 1950 | | static int compare_software_entries(const software_entry_state *e1, const software_entry_state *e2) |
| 1951 | | { |
| 1952 | | int result; |
| 1953 | | const char *e1_basename = (e1->short_name != NULL) ? e1->short_name : ""; |
| 1954 | | const char *e2_basename = (e2->short_name != NULL) ? e2->short_name : ""; |
| 1955 | | |
| 1956 | | result = mame_stricmp(e1_basename, e2_basename); |
| 1957 | | if (result == 0) |
| 1958 | | { |
| 1959 | | result = strcmp(e1_basename, e2_basename); |
| 1960 | | if (result == 0) |
| 1961 | | { |
| 1962 | | if (e1 < e2) |
| 1963 | | result = -1; |
| 1964 | | else if (e1 > e2) |
| 1965 | | result = 1; |
| 1966 | | } |
| 1967 | | } |
| 1968 | | |
| 1969 | | return result; |
| 1970 | | } |
| 1971 | | |
| 1972 | | /* populate a specific list */ |
| 1973 | | |
| 1974 | | static software_entry_state *append_software_entry(ui_menu *menu, software_menu_state *menustate, |
| 1975 | | software_info *swinfo, char *list_name, device_image_interface* image) |
| 1976 | | { |
| 1977 | | software_entry_state *entry = NULL; |
| 1978 | | software_entry_state **entryptr; |
| 1979 | | const char *interface = image->image_config().image_interface(); |
| 1980 | | |
| 1981 | | // check if at least one of the parts has the correct interface and add a menu entry only in this case |
| 1982 | | for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 1983 | | { |
| 1984 | | if (strcmp(interface, swpart->interface_) == 0) |
| 1985 | | { |
| 1986 | | // allocate a new entry |
| 1987 | | entry = (software_entry_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); |
| 1988 | | memset(entry, 0, sizeof(*entry)); |
| 1989 | | |
| 1990 | | entry->short_name = ui_menu_pool_strdup(menu, swinfo->shortname); |
| 1991 | | entry->long_name = ui_menu_pool_strdup(menu, swinfo->longname); |
| 1992 | | entry->list_name = list_name; |
| 1993 | | entry->image = image; |
| 1994 | | entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); |
| 1995 | | break; |
| 1996 | | } |
| 1997 | | } |
| 1998 | | |
| 1999 | | // find the end of the list |
| 2000 | | entryptr = &menustate->entrylist; |
| 2001 | | while ((*entryptr != NULL) && (compare_software_entries(entry, *entryptr) >= 0)) |
| 2002 | | entryptr = &(*entryptr)->next; |
| 2003 | | |
| 2004 | | // insert the entry |
| 2005 | | entry->next = *entryptr; |
| 2006 | | *entryptr = entry; |
| 2007 | | |
| 2008 | | return entry; |
| 2009 | | } |
| 2010 | | |
| 2011 | | |
| 2012 | | static void ui_mess_menu_populate_software_entries(running_machine &machine, ui_menu *menu, software_menu_state *menustate) |
| 2013 | | { |
| 2014 | | software_list *list = software_list_open(machine.options(), menustate->list_name, FALSE, NULL); |
| 2015 | | |
| 2016 | | // build up the list of entries for the menu |
| 2017 | | if (list) |
| 2018 | | { |
| 2019 | | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 2020 | | append_software_entry(menu, menustate, swinfo, menustate->list_name, menustate->image); |
| 2021 | | |
| 2022 | | software_list_close(list); |
| 2023 | | } |
| 2024 | | |
| 2025 | | // append all of the menu entries |
| 2026 | | for (software_entry_state *entry = menustate->entrylist; entry != NULL; entry = entry->next) |
| 2027 | | ui_menu_item_append(menu, entry->short_name, entry->long_name, 0, entry); |
| 2028 | | } |
| 2029 | | |
| 2030 | | bool swinfo_has_multiple_parts(software_info *swinfo, const char *interface) |
| 2031 | | { |
| 2032 | | int count = 0; |
| 2033 | | |
| 2034 | | for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) |
| 2035 | | { |
| 2036 | | if (strcmp(interface, swpart->interface_) == 0) |
| 2037 | | count++; |
| 2038 | | } |
| 2039 | | return (count > 1) ? TRUE : FALSE; |
| 2040 | | } |
| 2041 | | |
| 2042 | | void ui_mess_menu_software_list(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 2043 | | { |
| 2044 | | const ui_menu_event *event; |
| 2045 | | software_menu_state *sw_state = (software_menu_state *)state; |
| 2046 | | const software_entry_state *entry; |
| 2047 | | const software_entry_state *selected_entry = NULL; |
| 2048 | | int bestmatch = 0; |
| 2049 | | |
| 2050 | | if (!ui_menu_populated(menu)) |
| 2051 | | { |
| 2052 | | if (sw_state->list_name) |
| 2053 | | { |
| 2054 | | ui_mess_menu_populate_software_entries(machine, menu, sw_state); |
| 2055 | | } |
| 2056 | | } |
| 2057 | | |
| 2058 | | /* process the menu */ |
| 2059 | | event = ui_menu_process(machine, menu, 0); |
| 2060 | | |
| 2061 | | if (event != NULL && event->itemref != NULL) |
| 2062 | | { |
| 2063 | | /* handle selections */ |
| 2064 | | if (event->iptkey == IPT_UI_SELECT) |
| 2065 | | { |
| 2066 | | device_image_interface *image = sw_state->image; |
| 2067 | | software_entry_state *entry = (software_entry_state *) event->itemref; |
| 2068 | | software_list *tmp_list = software_list_open(machine.options(), sw_state->list_name, FALSE, NULL); |
| 2069 | | software_info *tmp_info = software_list_find(tmp_list, entry->short_name, NULL); |
| 2070 | | |
| 2071 | | // if the selected software has multiple parts that can be loaded, open the submenu |
| 2072 | | if (swinfo_has_multiple_parts(tmp_info, image->image_config().image_interface())) |
| 2073 | | { |
| 2074 | | ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_parts, entry); |
| 2075 | | software_entry_state *child_menustate = (software_entry_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); |
| 2076 | | child_menustate->short_name = entry->short_name; |
| 2077 | | child_menustate->interface = image->image_config().image_interface(); |
| 2078 | | child_menustate->list_name = sw_state->list_name; |
| 2079 | | child_menustate->image = image; |
| 2080 | | ui_menu_stack_push(child_menu); |
| 2081 | | } |
| 2082 | | else |
| 2083 | | { |
| 2084 | | // otherwise, load the file |
| 2085 | | if (image != NULL) |
| 2086 | | image->load(entry->short_name); |
| 2087 | | else |
| 2088 | | popmessage("No matching device found for interface '%s'!", entry->interface); |
| 2089 | | } |
| 2090 | | software_list_close(tmp_list); |
| 2091 | | |
| 2092 | | // reset the char buffer when pressing IPT_UI_SELECT |
| 2093 | | if (sw_state->filename_buffer[0] != '\0') |
| 2094 | | memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); |
| 2095 | | } |
| 2096 | | else if (event->iptkey == IPT_SPECIAL) |
| 2097 | | { |
| 2098 | | int buflen = strlen(sw_state->filename_buffer); |
| 2099 | | bool update_selected = FALSE; |
| 2100 | | |
| 2101 | | /* if it's a backspace and we can handle it, do so */ |
| 2102 | | if ((event->unichar == 8 || event->unichar == 0x7f) && buflen > 0) |
| 2103 | | { |
| 2104 | | *(char *)utf8_previous_char(&sw_state->filename_buffer[buflen]) = 0; |
| 2105 | | update_selected = TRUE; |
| 2106 | | |
| 2107 | | if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) |
| 2108 | | ui_popup_time(5, "%s", sw_state->filename_buffer); |
| 2109 | | } |
| 2110 | | /* if it's any other key and we're not maxed out, update */ |
| 2111 | | else if (event->unichar >= ' ' && event->unichar < 0x7f) |
| 2112 | | { |
| 2113 | | buflen += utf8_from_uchar(&sw_state->filename_buffer[buflen], ARRAY_LENGTH(sw_state->filename_buffer) - buflen, event->unichar); |
| 2114 | | sw_state->filename_buffer[buflen] = 0; |
| 2115 | | update_selected = TRUE; |
| 2116 | | |
| 2117 | | if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) |
| 2118 | | ui_popup_time(5, "%s", sw_state->filename_buffer); |
| 2119 | | } |
| 2120 | | |
| 2121 | | if (update_selected) |
| 2122 | | { |
| 2123 | | const software_entry_state *cur_selected = (const software_entry_state *)ui_menu_get_selection(menu); |
| 2124 | | |
| 2125 | | // check for entries which matches our filename_buffer: |
| 2126 | | // from current entry to the end |
| 2127 | | for (entry = cur_selected; entry != NULL; entry = entry->next) |
| 2128 | | { |
| 2129 | | if (entry->short_name != NULL && sw_state->filename_buffer != NULL) |
| 2130 | | { |
| 2131 | | int match = 0; |
| 2132 | | for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) |
| 2133 | | { |
| 2134 | | if (mame_strnicmp(entry->short_name, sw_state->filename_buffer, i) == 0) |
| 2135 | | match = i; |
| 2136 | | } |
| 2137 | | |
| 2138 | | if (match > bestmatch) |
| 2139 | | { |
| 2140 | | bestmatch = match; |
| 2141 | | selected_entry = entry; |
| 2142 | | } |
| 2143 | | } |
| 2144 | | } |
| 2145 | | // and from the first entry to current one |
| 2146 | | for (entry = sw_state->entrylist; entry != cur_selected; entry = entry->next) |
| 2147 | | { |
| 2148 | | if (entry->short_name != NULL && sw_state->filename_buffer != NULL) |
| 2149 | | { |
| 2150 | | int match = 0; |
| 2151 | | for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) |
| 2152 | | { |
| 2153 | | if (mame_strnicmp(entry->short_name, sw_state->filename_buffer, i) == 0) |
| 2154 | | match = i; |
| 2155 | | } |
| 2156 | | |
| 2157 | | if (match > bestmatch) |
| 2158 | | { |
| 2159 | | bestmatch = match; |
| 2160 | | selected_entry = entry; |
| 2161 | | } |
| 2162 | | } |
| 2163 | | } |
| 2164 | | |
| 2165 | | if (selected_entry != NULL && selected_entry != cur_selected) |
| 2166 | | ui_menu_set_selection(menu, (void *) selected_entry); |
| 2167 | | } |
| 2168 | | } |
| 2169 | | else if (event->iptkey == IPT_UI_CANCEL) |
| 2170 | | { |
| 2171 | | // reset the char buffer also in this case |
| 2172 | | if (sw_state->filename_buffer[0] != '\0') |
| 2173 | | memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); |
| 2174 | | } |
| 2175 | | } |
| 2176 | | } |
| 2177 | | |
| 2178 | | /* list of available software lists - i.e. cartridges, floppies */ |
| 2179 | | static void ui_mess_menu_populate_software_list(running_machine &machine, ui_menu *menu, device_image_interface* image) |
| 2180 | | { |
| 2181 | | bool haveCompatible = FALSE; |
| 2182 | | const char *interface = image->image_config().image_interface(); |
| 2183 | | |
| 2184 | | // Add original software lists for this system |
| 2185 | | for (const device_config *dev = machine.config().m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) |
| 2186 | | { |
| 2187 | | software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); |
| 2188 | | |
| 2189 | | for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) |
| 2190 | | { |
| 2191 | | if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)) |
| 2192 | | { |
| 2193 | | software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); |
| 2194 | | |
| 2195 | | if (list) |
| 2196 | | { |
| 2197 | | bool found = FALSE; |
| 2198 | | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 2199 | | { |
| 2200 | | software_part *part = software_find_part(swinfo, NULL, NULL); |
| 2201 | | if (strcmp(interface,part->interface_)==0) { |
| 2202 | | found = TRUE; |
| 2203 | | } |
| 2204 | | } |
| 2205 | | if (found) { |
| 2206 | | ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); |
| 2207 | | } |
| 2208 | | |
| 2209 | | software_list_close(list); |
| 2210 | | } |
| 2211 | | } |
| 2212 | | } |
| 2213 | | } |
| 2214 | | |
| 2215 | | // Add compatible software lists for this system |
| 2216 | | for (const device_config *dev = machine.config().m_devicelist.first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) |
| 2217 | | { |
| 2218 | | software_list_config *swlist = (software_list_config *)downcast<const legacy_device_config_base *>(dev)->inline_config(); |
| 2219 | | |
| 2220 | | for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) |
| 2221 | | { |
| 2222 | | if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_COMPATIBLE_SYSTEM)) |
| 2223 | | { |
| 2224 | | software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); |
| 2225 | | |
| 2226 | | if (list) |
| 2227 | | { |
| 2228 | | bool found = FALSE; |
| 2229 | | for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) |
| 2230 | | { |
| 2231 | | software_part *part = software_find_part(swinfo, NULL, NULL); |
| 2232 | | if (strcmp(interface,part->interface_)==0) { |
| 2233 | | found = TRUE; |
| 2234 | | } |
| 2235 | | } |
| 2236 | | if (found) { |
| 2237 | | if (!haveCompatible) { |
| 2238 | | ui_menu_item_append(menu, "[compatible lists]", NULL, 0, NULL); |
| 2239 | | } |
| 2240 | | ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); |
| 2241 | | } |
| 2242 | | |
| 2243 | | haveCompatible = TRUE; |
| 2244 | | software_list_close(list); |
| 2245 | | } |
| 2246 | | } |
| 2247 | | } |
| 2248 | | } |
| 2249 | | |
| 2250 | | } |
| 2251 | | |
| 2252 | | void ui_image_menu_software(running_machine &machine, ui_menu *menu, void *parameter, void *state) |
| 2253 | | { |
| 2254 | | const ui_menu_event *event; |
| 2255 | | device_image_interface* image = (device_image_interface*)parameter; |
| 2256 | | if (!ui_menu_populated(menu)) |
| 2257 | | ui_mess_menu_populate_software_list(machine, menu, image); |
| 2258 | | |
| 2259 | | /* process the menu */ |
| 2260 | | event = ui_menu_process(machine, menu, 0); |
| 2261 | | |
| 2262 | | if (event != NULL && event->iptkey == IPT_UI_SELECT) |
| 2263 | | { |
| 2264 | | ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_list, NULL); |
| 2265 | | software_menu_state *child_menustate = (software_menu_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); |
| 2266 | | child_menustate->list_name = (char *)event->itemref; |
| 2267 | | child_menustate->image = image; |
| 2268 | | child_menustate->entrylist = NULL; |
| 2269 | | ui_menu_stack_push(child_menu); |
| 2270 | | } |
| 2271 | | } |
| 2272 | | |
| 2273 | 1801 | DEFINE_LEGACY_DEVICE(SOFTWARE_LIST, software_list); |