// license:BSD-3-Clause // copyright-holders:Aaron Giles,Paul Priest /*************************************************************************** info.c Dumps the MAME internal data as an XML file. ***************************************************************************/ #include "emu.h" #include "mameopts.h" #include "machine/ram.h" #include "sound/samples.h" #include "info.h" #include "xmlfile.h" #include "config.h" #include "drivenum.h" #include "softlist.h" #include #define XML_ROOT "mame" #define XML_TOP "machine" //************************************************************************** // GLOBAL VARIABLES //************************************************************************** // DTD string describing the data const char info_xml_creator::s_dtd_string[] = "\n" "\t\n" "\t\n" "\t\n" "\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\t\t\n" "\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\t\n" "\t\t\n" "\t\t\t\n" "]>"; //************************************************************************** // INFO XML CREATOR //************************************************************************** //------------------------------------------------- // info_xml_creator - constructor //------------------------------------------------- info_xml_creator::info_xml_creator(driver_enumerator &drivlist) : m_output(nullptr), m_drivlist(drivlist), m_lookup_options(m_drivlist.options()) { mame_options::remove_device_options(m_lookup_options); } //------------------------------------------------- // output_mame_xml - print the XML information // for all known games //------------------------------------------------- void info_xml_creator::output(FILE *out, bool nodevices) { m_output = out; // output the DTD fprintf(m_output, "\n"); std::string dtd(s_dtd_string); strreplace(dtd, "__XML_ROOT__", XML_ROOT); strreplace(dtd, "__XML_TOP__", XML_TOP); fprintf(m_output, "%s\n\n", dtd.c_str()); // top-level tag fprintf(m_output, "<%s build=\"%s\" debug=\"" #ifdef MAME_DEBUG "yes" #else "no" #endif "\" mameconfig=\"%d\">\n", XML_ROOT, xml_normalize_string(emulator_info::get_build_version()), CONFIG_VERSION ); // iterate through the drivers, outputting one at a time while (m_drivlist.next()) output_one(); // output devices (both devices with roms and slot devices) if (!nodevices) output_devices(); // close the top level tag fprintf(m_output, "\n",XML_ROOT); } //------------------------------------------------- // output_one - print the XML information // for one particular game driver //------------------------------------------------- void info_xml_creator::output_one() { // no action if not a game const game_driver &driver = m_drivlist.driver(); if (driver.flags & MACHINE_NO_STANDALONE) return; // allocate input ports machine_config &config = m_drivlist.config(); ioport_list portlist; std::string errors; device_iterator iter(config.root_device()); for (device_t &device : iter) portlist.append(device, errors); // renumber player numbers for controller ports int player_offset = 0; // but treat keyboard count separately from players' number int kbd_offset = 0; for (device_t &device : iter) { int nplayers = 0; bool new_kbd = false; for (auto &port : portlist) if (&port.second->device() == &device) for (ioport_field &field : port.second->fields()) if (field.type() >= IPT_START && field.type() < IPT_ANALOG_LAST) { if (field.type() == IPT_KEYBOARD) { if (!new_kbd) new_kbd = TRUE; field.set_player(field.player() + kbd_offset); } else { nplayers = MAX(nplayers, field.player() + 1); field.set_player(field.player() + player_offset); } } player_offset += nplayers; if (new_kbd) kbd_offset++; } // print the header and the game name fprintf(m_output, "\t<%s",XML_TOP); fprintf(m_output, " name=\"%s\"", xml_normalize_string(driver.name)); // strip away any path information from the source_file and output it const char *start = strrchr(driver.source_file, '/'); if (start == nullptr) start = strrchr(driver.source_file, '\\'); if (start == nullptr) start = driver.source_file - 1; fprintf(m_output, " sourcefile=\"%s\"", xml_normalize_string(start + 1)); // append bios and runnable flags if (driver.flags & MACHINE_IS_BIOS_ROOT) fprintf(m_output, " isbios=\"yes\""); if (driver.flags & MACHINE_NO_STANDALONE) fprintf(m_output, " runnable=\"no\""); if (driver.flags & MACHINE_MECHANICAL) fprintf(m_output, " ismechanical=\"yes\""); // display clone information int clone_of = m_drivlist.find(driver.parent); if (clone_of != -1 && !(m_drivlist.driver(clone_of).flags & MACHINE_IS_BIOS_ROOT)) fprintf(m_output, " cloneof=\"%s\"", xml_normalize_string(m_drivlist.driver(clone_of).name)); if (clone_of != -1) fprintf(m_output, " romof=\"%s\"", xml_normalize_string(m_drivlist.driver(clone_of).name)); // display sample information and close the game tag output_sampleof(); fprintf(m_output, ">\n"); // output game description if (driver.description != nullptr) fprintf(m_output, "\t\t%s\n", xml_normalize_string(driver.description)); // print the year only if is a number or another allowed character (? or +) if (driver.year != nullptr && strspn(driver.year, "0123456789?+") == strlen(driver.year)) fprintf(m_output, "\t\t%s\n", xml_normalize_string(driver.year)); // print the manufacturer information if (driver.manufacturer != nullptr) fprintf(m_output, "\t\t%s\n", xml_normalize_string(driver.manufacturer)); // now print various additional information output_bios(); output_rom(m_drivlist.config().root_device()); output_device_roms(); output_sample(m_drivlist.config().root_device()); output_chips(m_drivlist.config().root_device(), ""); output_display(m_drivlist.config().root_device(), ""); output_sound(m_drivlist.config().root_device()); output_input(portlist); output_switches(portlist, "", IPT_DIPSWITCH, "dipswitch", "dipvalue"); output_switches(portlist, "", IPT_CONFIG, "configuration", "confsetting"); output_ports(portlist); output_adjusters(portlist); output_driver(); output_images(m_drivlist.config().root_device(), ""); output_slots(m_drivlist.config().root_device(), ""); output_software_list(); output_ramoptions(); // close the topmost tag fprintf(m_output, "\t\n",XML_TOP); } //------------------------------------------------- // output_one_device - print the XML info for // a single device //------------------------------------------------- void info_xml_creator::output_one_device(device_t &device, const char *devtag) { bool has_speaker = FALSE, has_input = FALSE; // check if the device adds speakers to the system sound_interface_iterator snditer(device); if (snditer.first() != nullptr) has_speaker = TRUE; // generate input list ioport_list portlist; std::string errors; for (device_t &dev : device_iterator(device)) portlist.append(dev, errors); // check if the device adds player inputs (other than dsw and configs) to the system for (auto &port : portlist) for (ioport_field &field : port.second->fields()) if (field.type() >= IPT_START1 && field.type() < IPT_UI_FIRST) { has_input = TRUE; break; } // start to output info fprintf(m_output, "\t<%s", XML_TOP); fprintf(m_output, " name=\"%s\"", xml_normalize_string(device.shortname())); std::string src(device.source()); strreplace(src,"../", ""); fprintf(m_output, " sourcefile=\"%s\"", xml_normalize_string(src.c_str())); fprintf(m_output, " isdevice=\"yes\""); fprintf(m_output, " runnable=\"no\""); output_sampleof(); fprintf(m_output, ">\n"); fprintf(m_output, "\t\t%s\n", xml_normalize_string(device.name())); output_rom(device); samples_device *samples = dynamic_cast(&device); if (samples==nullptr) output_sample(device); // ignore samples_device itself output_chips(device, devtag); output_display(device, devtag); if (has_speaker) output_sound(device); if (has_input) output_input(portlist); output_switches(portlist, devtag, IPT_DIPSWITCH, "dipswitch", "dipvalue"); output_switches(portlist, devtag, IPT_CONFIG, "configuration", "confsetting"); output_adjusters(portlist); output_images(device, devtag); output_slots(device, devtag); fprintf(m_output, "\t\n", XML_TOP); } //------------------------------------------------- // output_devices - print the XML info for devices // with roms and for devices that can be mounted // in slots // The current solution works to some extent, but // it is limited by the fact that devices are only // acknowledged when attached to a driver (so that // for instance sub-sub-devices could never appear // in the xml input if they are not also attached // directly to a driver as device or sub-device) //------------------------------------------------- void info_xml_creator::output_devices() { m_drivlist.reset(); std::unordered_set shortnames; while (m_drivlist.next()) { // first, run through devices with roms which belongs to the default configuration for (device_t &device : device_iterator(m_drivlist.config().root_device())) { if (device.owner() != nullptr && device.shortname() != nullptr && device.shortname()[0]!='\0') { if (shortnames.insert(device.shortname()).second) output_one_device(device, device.tag()); } } // then, run through slot devices for (const device_slot_interface &slot : slot_interface_iterator(m_drivlist.config().root_device())) { for (auto &option : slot.option_list()) { std::string temptag("_"); temptag.append(option.second->name()); device_t *dev = const_cast(m_drivlist.config()).device_add(&m_drivlist.config().root_device(), temptag.c_str(), option.second->devtype(), 0); // notify this device and all its subdevices that they are now configured for (device_t &device : device_iterator(*dev)) if (!device.configured()) device.config_complete(); if (shortnames.insert(dev->shortname()).second) output_one_device(*dev, temptag.c_str()); // also, check for subdevices with ROMs (a few devices are missed otherwise, e.g. MPU401) for (device_t &device : device_iterator(*dev)) { if (device.owner() == dev && device.shortname() != nullptr && device.shortname()[0]!='\0') { if (shortnames.insert(device.shortname()).second) output_one_device(device, device.tag()); } } const_cast(m_drivlist.config()).device_remove(&m_drivlist.config().root_device(), temptag.c_str()); } } } } //------------------------------------------------ // output_device_roms - when a driver uses roms // included in a device set, print a reference //------------------------------------------------- void info_xml_creator::output_device_roms() { for (device_t &device : device_iterator(m_drivlist.config().root_device())) if (device.owner() != nullptr && device.shortname() != nullptr && device.shortname()[0] != '\0') fprintf(m_output, "\t\t\n", xml_normalize_string(device.shortname())); } //------------------------------------------------ // output_sampleof - print the 'sampleof' // attribute, if appropriate //------------------------------------------------- void info_xml_creator::output_sampleof() { // iterate over sample devices for (samples_device &device : samples_device_iterator(m_drivlist.config().root_device())) { samples_iterator sampiter(device); if (sampiter.altbasename() != nullptr) { fprintf(m_output, " sampleof=\"%s\"", xml_normalize_string(sampiter.altbasename())); // must stop here, as there can only be one attribute of the same name return; } } } //------------------------------------------------- // output_bios - print the BIOS set for a // game //------------------------------------------------- void info_xml_creator::output_bios() { // skip if no ROMs if (m_drivlist.driver().rom == nullptr) return; // first determine the default BIOS name std::string defaultname; for (const rom_entry *rom = m_drivlist.driver().rom; !ROMENTRY_ISEND(rom); rom++) if (ROMENTRY_ISDEFAULT_BIOS(rom)) defaultname = ROM_GETNAME(rom); // iterate over ROM entries and look for BIOSes for (const rom_entry *rom = m_drivlist.driver().rom; !ROMENTRY_ISEND(rom); rom++) if (ROMENTRY_ISSYSTEM_BIOS(rom)) { // output extracted name and descriptions fprintf(m_output, "\t\t\n"); } } //------------------------------------------------- // output_rom - print the roms section of // the XML output //------------------------------------------------- void info_xml_creator::output_rom(device_t &device) { // iterate over 3 different ROM "types": BIOS, ROMs, DISKs for (int rom_type = 0; rom_type < 3; rom_type++) for (const rom_entry *region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) { bool is_disk = ROMREGION_ISDISKDATA(region); // disk regions only work for disks if ((is_disk && rom_type != 2) || (!is_disk && rom_type == 2)) continue; // iterate through ROM entries for (const rom_entry *rom = rom_first_file(region); rom != nullptr; rom = rom_next_file(rom)) { bool is_bios = ROM_GETBIOSFLAGS(rom); const char *name = ROM_GETNAME(rom); int offset = ROM_GETOFFSET(rom); const char *merge_name = nullptr; char bios_name[100]; // BIOS ROMs only apply to bioses if ((is_bios && rom_type != 0) || (!is_bios && rom_type == 0)) continue; // if we have a valid ROM and we are a clone, see if we can find the parent ROM hash_collection hashes(ROM_GETHASHDATA(rom)); if (!hashes.flag(hash_collection::FLAG_NO_DUMP)) merge_name = get_merge_name(hashes); if (&device != &m_drivlist.config().root_device()) merge_name = nullptr; // scan for a BIOS name bios_name[0] = 0; if (!is_disk && is_bios) { // scan backwards through the ROM entries for (const rom_entry *brom = rom - 1; brom != m_drivlist.driver().rom; brom--) if (ROMENTRY_ISSYSTEM_BIOS(brom)) { strcpy(bios_name, ROM_GETNAME(brom)); break; } } std::ostringstream output; // opening tag if (!is_disk) output << "\t\t\n"; fprintf(m_output, "%s", output.str().c_str()); } } } //------------------------------------------------- // output_sample - print a list of all // samples referenced by a game_driver //------------------------------------------------- void info_xml_creator::output_sample(device_t &device) { // iterate over sample devices for (samples_device &samples : samples_device_iterator(device)) { samples_iterator iter(samples); std::unordered_set already_printed; for (const char *samplename = iter.first(); samplename != nullptr; samplename = iter.next()) { // filter out duplicates if (!already_printed.insert(samplename).second) continue; // output the sample name fprintf(m_output, "\t\t\n", xml_normalize_string(samplename)); } } } /*------------------------------------------------- output_chips - print a list of CPU and sound chips used by a game -------------------------------------------------*/ void info_xml_creator::output_chips(device_t &device, const char *root_tag) { // iterate over executable devices for (device_execute_interface &exec : execute_interface_iterator(device)) { if (strcmp(exec.device().tag(), device.tag())) { std::string newtag(exec.device().tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); fprintf(m_output, "\t\t\n"); } } // iterate over sound devices for (device_sound_interface &sound : sound_interface_iterator(device)) { if (strcmp(sound.device().tag(), device.tag())) { std::string newtag(sound.device().tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); fprintf(m_output, "\t\t\n"); } } } //------------------------------------------------- // output_display - print a list of all the // displays //------------------------------------------------- void info_xml_creator::output_display(device_t &device, const char *root_tag) { // iterate over screens for (const screen_device &screendev : screen_device_iterator(device)) { if (strcmp(screendev.tag(), device.tag())) { std::string newtag(screendev.tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); fprintf(m_output, "\t\t\n"); } } } //------------------------------------------------- // output_sound - print a list of all the // speakers //------------------------------------------------ void info_xml_creator::output_sound(device_t &device) { speaker_device_iterator spkiter(device); int speakers = spkiter.count(); // if we have no sound, zero m_output the speaker count sound_interface_iterator snditer(device); if (snditer.first() == nullptr) speakers = 0; fprintf(m_output, "\t\t\n", speakers); } //------------------------------------------------- // output_input - print a summary of a game's // input //------------------------------------------------- void info_xml_creator::output_input(const ioport_list &portlist) { // enumerated list of control types // NOTE: the order is chosen so that 'spare' button inputs are assigned to the // most-likely-correct input device when info is output (you can think of it as // a sort of likelihood order of having buttons) enum { CTRL_DIGITAL_BUTTONS, CTRL_DIGITAL_JOYSTICK, CTRL_ANALOG_JOYSTICK, CTRL_ANALOG_LIGHTGUN, CTRL_ANALOG_DIAL, CTRL_ANALOG_POSITIONAL, CTRL_ANALOG_TRACKBALL, CTRL_ANALOG_MOUSE, CTRL_ANALOG_PADDLE, CTRL_ANALOG_PEDAL, CTRL_DIGITAL_KEYPAD, CTRL_DIGITAL_KEYBOARD, CTRL_DIGITAL_MAHJONG, CTRL_DIGITAL_HANAFUDA, CTRL_DIGITAL_GAMBLING, CTRL_COUNT }; enum { CTRL_P1, CTRL_P2, CTRL_P3, CTRL_P4, CTRL_P5, CTRL_P6, CTRL_P7, CTRL_P8, CTRL_P9, CTRL_P10, CTRL_PCOUNT }; // directions const UINT8 DIR_UP = 0x01; const UINT8 DIR_DOWN = 0x02; const UINT8 DIR_LEFT = 0x04; const UINT8 DIR_RIGHT = 0x08; // initialize the list of control types struct { const char * type; // general type of input int player; // player which the input belongs to int nbuttons; // total number of buttons int reqbuttons; // total number of non-optional buttons int maxbuttons; // max index of buttons (using IPT_BUTTONn) [probably to be removed soonish] int ways; // directions for joystick bool analog; // is analog input? UINT8 helper[3]; // for dual joysticks [possibly to be removed soonish] INT32 min; // analog minimum value INT32 max; // analog maximum value INT32 sensitivity; // default analog sensitivity INT32 keydelta; // default analog keydelta bool reverse; // default analog reverse setting } control_info[CTRL_COUNT * CTRL_PCOUNT]; memset(&control_info, 0, sizeof(control_info)); // tracking info as we iterate int nplayer = 0; int ncoin = 0; bool service = false; bool tilt = false; // iterate over the ports for (auto &port : portlist) { int ctrl_type = CTRL_DIGITAL_BUTTONS; bool ctrl_analog = FALSE; for (ioport_field &field : port.second->fields()) { // track the highest player number if (nplayer < field.player() + 1) nplayer = field.player() + 1; // switch off of the type switch (field.type()) { // map joysticks case IPT_JOYSTICK_UP: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_UP; break; case IPT_JOYSTICK_DOWN: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_DOWN; break; case IPT_JOYSTICK_LEFT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_LEFT; break; case IPT_JOYSTICK_RIGHT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[0] |= DIR_RIGHT; break; case IPT_JOYSTICKLEFT_UP: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_UP; break; case IPT_JOYSTICKLEFT_DOWN: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_DOWN; break; case IPT_JOYSTICKLEFT_LEFT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_LEFT; break; case IPT_JOYSTICKLEFT_RIGHT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[1] |= DIR_RIGHT; break; case IPT_JOYSTICKRIGHT_UP: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_UP; break; case IPT_JOYSTICKRIGHT_DOWN: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_DOWN; break; case IPT_JOYSTICKRIGHT_LEFT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_LEFT; break; case IPT_JOYSTICKRIGHT_RIGHT: ctrl_type = CTRL_DIGITAL_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "joy"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].ways = field.way(); control_info[field.player() * CTRL_COUNT + ctrl_type].helper[2] |= DIR_RIGHT; break; // map analog inputs case IPT_AD_STICK_X: case IPT_AD_STICK_Y: case IPT_AD_STICK_Z: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_JOYSTICK; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "stick"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_PADDLE: case IPT_PADDLE_V: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_PADDLE; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "paddle"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_PEDAL: case IPT_PEDAL2: case IPT_PEDAL3: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_PEDAL; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "pedal"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_LIGHTGUN_X: case IPT_LIGHTGUN_Y: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_LIGHTGUN; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "lightgun"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_POSITIONAL: case IPT_POSITIONAL_V: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_POSITIONAL; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "positional"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_DIAL: case IPT_DIAL_V: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_DIAL; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "dial"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_TRACKBALL_X: case IPT_TRACKBALL_Y: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_TRACKBALL; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "trackball"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; case IPT_MOUSE_X: case IPT_MOUSE_Y: ctrl_analog = TRUE; ctrl_type = CTRL_ANALOG_MOUSE; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "mouse"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = TRUE; break; // map buttons case IPT_BUTTON1: case IPT_BUTTON2: case IPT_BUTTON3: case IPT_BUTTON4: case IPT_BUTTON5: case IPT_BUTTON6: case IPT_BUTTON7: case IPT_BUTTON8: case IPT_BUTTON9: case IPT_BUTTON10: case IPT_BUTTON11: case IPT_BUTTON12: case IPT_BUTTON13: case IPT_BUTTON14: case IPT_BUTTON15: case IPT_BUTTON16: ctrl_analog = FALSE; if (control_info[field.player() * CTRL_COUNT + ctrl_type].type == nullptr) { control_info[field.player() * CTRL_COUNT + ctrl_type].type = "only_buttons"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].analog = FALSE; } control_info[field.player() * CTRL_COUNT + ctrl_type].maxbuttons = MAX(control_info[field.player() * CTRL_COUNT + ctrl_type].maxbuttons, field.type() - IPT_BUTTON1 + 1); control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; break; // track maximum coin index case IPT_COIN1: case IPT_COIN2: case IPT_COIN3: case IPT_COIN4: case IPT_COIN5: case IPT_COIN6: case IPT_COIN7: case IPT_COIN8: case IPT_COIN9: case IPT_COIN10: case IPT_COIN11: case IPT_COIN12: ncoin = MAX(ncoin, field.type() - IPT_COIN1 + 1); break; // track presence of keypads and keyboards case IPT_KEYPAD: ctrl_type = CTRL_DIGITAL_KEYPAD; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "keypad"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; break; case IPT_KEYBOARD: ctrl_type = CTRL_DIGITAL_KEYBOARD; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "keyboard"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; break; // additional types case IPT_SERVICE: service = true; break; case IPT_TILT: tilt = true; break; default: if (field.type() > IPT_MAHJONG_FIRST && field.type() < IPT_MAHJONG_LAST) { ctrl_type = CTRL_DIGITAL_MAHJONG; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "mahjong"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; } else if (field.type() > IPT_HANAFUDA_FIRST && field.type() < IPT_HANAFUDA_LAST) { ctrl_type = CTRL_DIGITAL_HANAFUDA; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "hanafuda"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; } else if (field.type() > IPT_GAMBLING_FIRST && field.type() < IPT_GAMBLING_LAST) { ctrl_type = CTRL_DIGITAL_GAMBLING; control_info[field.player() * CTRL_COUNT + ctrl_type].type = "gambling"; control_info[field.player() * CTRL_COUNT + ctrl_type].player = field.player() + 1; control_info[field.player() * CTRL_COUNT + ctrl_type].nbuttons++; if (!field.optional()) control_info[field.player() * CTRL_COUNT + ctrl_type].reqbuttons++; } break; } if (ctrl_analog) { // get the analog stats if (field.minval() != 0) control_info[field.player() * CTRL_COUNT + ctrl_type].min = field.minval(); if (field.maxval() != 0) control_info[field.player() * CTRL_COUNT + ctrl_type].max = field.maxval(); if (field.sensitivity() != 0) control_info[field.player() * CTRL_COUNT + ctrl_type].sensitivity = field.sensitivity(); if (field.delta() != 0) control_info[field.player() * CTRL_COUNT + ctrl_type].keydelta = field.delta(); if (field.analog_reverse() != 0) control_info[field.player() * CTRL_COUNT + ctrl_type].reverse = TRUE; } } } // Clean-up those entries, if any, where buttons were defined in a separate port than the actual controller they belong to. // This is quite often the case, especially for arcades where controls can be easily mapped to separate input ports on PCB. // If such situation would only happen for joystick, it would be possible to work it around by initializing differently // ctrl_type above, but it is quite common among analog inputs as well (for instance, this is the tipical situation // for lightguns) and therefore we really need this separate loop. for (int i = 0; i < CTRL_PCOUNT; i++) { bool fix_done = FALSE; for (int j = 1; j < CTRL_COUNT; j++) if (control_info[i * CTRL_COUNT].type != nullptr && control_info[i * CTRL_COUNT + j].type != nullptr && !fix_done) { control_info[i * CTRL_COUNT + j].nbuttons += control_info[i * CTRL_COUNT].nbuttons; control_info[i * CTRL_COUNT + j].reqbuttons += control_info[i * CTRL_COUNT].reqbuttons; control_info[i * CTRL_COUNT + j].maxbuttons = MAX(control_info[i * CTRL_COUNT + j].maxbuttons, control_info[i * CTRL_COUNT].maxbuttons); memset(&control_info[i * CTRL_COUNT], 0, sizeof(control_info[0])); fix_done = TRUE; } } // Output the input info // First basic info fprintf(m_output, "\t\t\n"); // Then controller specific ones for (auto & elem : control_info) if (elem.type != nullptr) { //printf("type %s - player %d - buttons %d\n", elem.type, elem.player, elem.nbuttons); if (elem.analog) { fprintf(m_output, "\t\t\t 1) fprintf(m_output, " player=\"%d\"", elem.player); if (elem.nbuttons > 0) { fprintf(m_output, " buttons=\"%d\"", strcmp(elem.type, "stick") ? elem.nbuttons : elem.maxbuttons); if (elem.reqbuttons < elem.nbuttons) fprintf(m_output, " reqbuttons=\"%d\"", elem.reqbuttons); } if (elem.min != 0 || elem.max != 0) { fprintf(m_output, " minimum=\"%d\"", elem.min); fprintf(m_output, " maximum=\"%d\"", elem.max); } if (elem.sensitivity != 0) fprintf(m_output, " sensitivity=\"%d\"", elem.sensitivity); if (elem.keydelta != 0) fprintf(m_output, " keydelta=\"%d\"", elem.keydelta); if (elem.reverse) fprintf(m_output, " reverse=\"yes\""); fprintf(m_output, "/>\n"); } else { if (elem.helper[1] == 0 && elem.helper[2] != 0) { elem.helper[1] = elem.helper[2]; elem.helper[2] = 0; } if (elem.helper[0] == 0 && elem.helper[1] != 0) { elem.helper[0] = elem.helper[1]; elem.helper[1] = 0; } if (elem.helper[1] == 0 && elem.helper[2] != 0) { elem.helper[1] = elem.helper[2]; elem.helper[2] = 0; } const char *joys = (elem.helper[2] != 0) ? "triple" : (elem.helper[1] != 0) ? "double" : ""; fprintf(m_output, "\t\t\t 1) fprintf(m_output, " player=\"%d\"", elem.player); if (elem.nbuttons > 0) { fprintf(m_output, " buttons=\"%d\"", strcmp(elem.type, "joy") ? elem.nbuttons : elem.maxbuttons); if (elem.reqbuttons < elem.nbuttons) fprintf(m_output, " reqbuttons=\"%d\"", elem.reqbuttons); } for (int lp = 0; lp < 3 && elem.helper[lp] != 0; lp++) { const char *plural = (lp==2) ? "3" : (lp==1) ? "2" : ""; const char *ways; std::string helper; switch (elem.helper[lp] & (DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT)) { case DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT: helper = string_format("%d", (elem.ways == 0) ? 8 : elem.ways); ways = helper.c_str(); break; case DIR_LEFT | DIR_RIGHT: ways = "2"; break; case DIR_UP | DIR_DOWN: ways = "vertical2"; break; case DIR_UP: case DIR_DOWN: case DIR_LEFT: case DIR_RIGHT: ways = "1"; break; case DIR_UP | DIR_DOWN | DIR_LEFT: case DIR_UP | DIR_DOWN | DIR_RIGHT: case DIR_UP | DIR_LEFT | DIR_RIGHT: case DIR_DOWN | DIR_LEFT | DIR_RIGHT: ways = (elem.ways == 4) ? "3 (half4)" : "5 (half8)"; break; default: ways = "strange2"; break; } fprintf(m_output, " ways%s=\"%s\"", plural, ways); } fprintf(m_output, "/>\n"); } } fprintf(m_output, "\t\t\n"); } //------------------------------------------------- // output_switches - print the configurations or // DIP switch settings //------------------------------------------------- void info_xml_creator::output_switches(const ioport_list &portlist, const char *root_tag, int type, const char *outertag, const char *innertag) { // iterate looking for DIP switches for (auto &port : portlist) for (ioport_field &field : port.second->fields()) if (field.type() == type) { std::ostringstream output; std::string newtag(port.second->tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); // output the switch name information std::string normalized_field_name(xml_normalize_string(field.name())); std::string normalized_newtag(xml_normalize_string(newtag.c_str())); util::stream_format(output,"\t\t<%s name=\"%s\" tag=\"%s\" mask=\"%u\">\n", outertag, normalized_field_name.c_str(), normalized_newtag.c_str(), field.mask()); // loop over settings for (ioport_setting &setting : field.settings()) { util::stream_format(output,"\t\t\t<%s name=\"%s\" value=\"%u\"%s/>\n", innertag, xml_normalize_string(setting.name()), setting.value(), setting.value() == field.defvalue() ? " default=\"yes\"" : ""); } // terminate the switch entry util::stream_format(output,"\t\t\n", outertag); fprintf(m_output, "%s", output.str().c_str()); } } //------------------------------------------------- // output_ports - print the structure of input // ports in the driver //------------------------------------------------- void info_xml_creator::output_ports(const ioport_list &portlist) { // cycle through ports for (auto &port : portlist) { fprintf(m_output,"\t\t\n", xml_normalize_string(port.second->tag())); for (ioport_field &field : port.second->fields()) { if(field.is_analog()) fprintf(m_output,"\t\t\t\n", field.mask()); } // close element fprintf(m_output,"\t\t\n"); } } //------------------------------------------------- // output_adjusters - print the Analog // Adjusters for a game //------------------------------------------------- void info_xml_creator::output_adjusters(const ioport_list &portlist) { // iterate looking for Adjusters for (auto &port : portlist) for (ioport_field &field : port.second->fields()) if (field.type() == IPT_ADJUSTER) fprintf(m_output, "\t\t\n", xml_normalize_string(field.name()), field.defvalue()); } //------------------------------------------------- // output_driver - print driver status //------------------------------------------------- void info_xml_creator::output_driver() { fprintf(m_output, "\t\t\n"); } //------------------------------------------------- // output_images - prints m_output all info on // image devices //------------------------------------------------- void info_xml_creator::output_images(device_t &device, const char *root_tag) { for (const device_image_interface &imagedev : image_interface_iterator(device)) { if (strcmp(imagedev.device().tag(), device.tag())) { bool loadable = imagedev.user_loadable(); std::string newtag(imagedev.device().tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); // print m_output device type fprintf(m_output, "\t\t\n"); if (loadable) { const char *name = imagedev.instance_name(); const char *shortname = imagedev.brief_instance_name(); fprintf(m_output, "\t\t\t\n"); std::string extensions(imagedev.file_extensions()); char *ext = strtok((char *)extensions.c_str(), ","); while (ext != nullptr) { fprintf(m_output, "\t\t\t\n"); ext = strtok(nullptr, ","); } } fprintf(m_output, "\t\t\n"); } } } //------------------------------------------------- // output_images - prints all info about slots //------------------------------------------------- void info_xml_creator::output_slots(device_t &device, const char *root_tag) { for (const device_slot_interface &slot : slot_interface_iterator(device)) { if (slot.fixed()) continue; // or shall we list these as non-configurable? if (strcmp(slot.device().tag(), device.tag())) { std::string newtag(slot.device().tag()), oldtag(":"); newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); // print m_output device type fprintf(m_output, "\t\t\n", xml_normalize_string(newtag.c_str())); /* if (slot.slot_interface()[0]) fprintf(m_output, " interface=\"%s\"", xml_normalize_string(slot.slot_interface())); */ for (auto &option : slot.option_list()) { if (option.second->selectable()) { device_t *dev = const_cast(m_drivlist.config()).device_add(&m_drivlist.config().root_device(), "dummy", option.second->devtype(), 0); if (!dev->configured()) dev->config_complete(); fprintf(m_output, "\t\t\tname())); fprintf(m_output, " devname=\"%s\"", xml_normalize_string(dev->shortname())); if (slot.default_option() != nullptr && strcmp(slot.default_option(), option.second->name())==0) fprintf(m_output, " default=\"yes\""); fprintf(m_output, "/>\n"); const_cast(m_drivlist.config()).device_remove(&m_drivlist.config().root_device(), "dummy"); } } fprintf(m_output, "\t\t\n"); } } } //------------------------------------------------- // output_software_list - print the information // for all known software lists for this system //------------------------------------------------- void info_xml_creator::output_software_list() { for (const software_list_device &swlist : software_list_device_iterator(m_drivlist.config().root_device())) { fprintf(m_output, "\t\t\n"); } } //------------------------------------------------- // output_ramoptions - prints m_output all RAM // options for this system //------------------------------------------------- void info_xml_creator::output_ramoptions() { for (const ram_device &ram : ram_device_iterator(m_drivlist.config().root_device())) { fprintf(m_output, "\t\t%u\n", ram.default_size()); if (ram.extra_options() != nullptr) { std::string options(ram.extra_options()); for (int start = 0, end = options.find_first_of(',');; start = end + 1, end = options.find_first_of(',', start)) { std::string option; option.assign(options.substr(start, (end == -1) ? -1 : end - start)); fprintf(m_output, "\t\t%u\n", ram_device::parse_string(option.c_str())); if (end == -1) break; } } } } //------------------------------------------------- // get_merge_name - get the rom name from a // parent set //------------------------------------------------- const char *info_xml_creator::get_merge_name(const hash_collection &romhashes) { // walk the parent chain const char *merge_name = nullptr; for (int clone_of = m_drivlist.find(m_drivlist.driver().parent); clone_of != -1; clone_of = m_drivlist.find(m_drivlist.driver(clone_of).parent)) { // look in the parent's ROMs device_t *device = &m_drivlist.config(clone_of, m_lookup_options).root_device(); for (const rom_entry *pregion = rom_first_region(*device); pregion != nullptr; pregion = rom_next_region(pregion)) for (const rom_entry *prom = rom_first_file(pregion); prom != nullptr; prom = rom_next_file(prom)) { hash_collection phashes(ROM_GETHASHDATA(prom)); if (!phashes.flag(hash_collection::FLAG_NO_DUMP) && romhashes == phashes) { // stop when we find a match merge_name = ROM_GETNAME(prom); break; } } } return merge_name; }