diff options
Diffstat (limited to 'src/emu/info.cpp')
-rw-r--r-- | src/emu/info.cpp | 831 |
1 files changed, 519 insertions, 312 deletions
diff --git a/src/emu/info.cpp b/src/emu/info.cpp index d9e76cd72d9..f427d017795 100644 --- a/src/emu/info.cpp +++ b/src/emu/info.cpp @@ -100,10 +100,11 @@ const char info_xml_creator::s_dtd_string[] = "\t\t\t<!ATTLIST input service (yes|no) \"no\">\n" "\t\t\t<!ATTLIST input tilt (yes|no) \"no\">\n" "\t\t\t<!ATTLIST input players CDATA #REQUIRED>\n" -"\t\t\t<!ATTLIST input buttons CDATA #IMPLIED>\n" "\t\t\t<!ATTLIST input coins CDATA #IMPLIED>\n" "\t\t\t<!ELEMENT control EMPTY>\n" "\t\t\t\t<!ATTLIST control type CDATA #REQUIRED>\n" +"\t\t\t\t<!ATTLIST control player CDATA #IMPLIED>\n" +"\t\t\t\t<!ATTLIST control buttons CDATA #IMPLIED>\n" "\t\t\t\t<!ATTLIST control minimum CDATA #IMPLIED>\n" "\t\t\t\t<!ATTLIST control maximum CDATA #IMPLIED>\n" "\t\t\t\t<!ATTLIST control sensitivity CDATA #IMPLIED>\n" @@ -147,6 +148,7 @@ const char info_xml_creator::s_dtd_string[] = "\t\t<!ELEMENT device (instance*, extension*)>\n" "\t\t\t<!ATTLIST device type CDATA #REQUIRED>\n" "\t\t\t<!ATTLIST device tag CDATA #IMPLIED>\n" +"\t\t\t<!ATTLIST device fixed_image CDATA #IMPLIED>\n" "\t\t\t<!ATTLIST device mandatory CDATA #IMPLIED>\n" "\t\t\t<!ATTLIST device interface CDATA #IMPLIED>\n" "\t\t\t<!ELEMENT instance EMPTY>\n" @@ -248,6 +250,33 @@ void info_xml_creator::output_one() device_iterator iter(config.root_device()); for (device_t *device = iter.first(); device != nullptr; device = iter.next()) 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.first(); device != nullptr; device = iter.next()) + { + int nplayers = 0; + bool new_kbd = FALSE; + for (ioport_port &port : portlist) + if (&port.device() == device) + for (ioport_field &field : port.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); @@ -792,295 +821,466 @@ void info_xml_creator::output_sound(device_t &device) void info_xml_creator::output_input(const ioport_list &portlist) { - // enumerated list of control types - enum - { - ANALOG_TYPE_PADDLE, - ANALOG_TYPE_PEDAL, - ANALOG_TYPE_JOYSTICK, - ANALOG_TYPE_POSITIONAL, - ANALOG_TYPE_LIGHTGUN, - ANALOG_TYPE_DIAL, - ANALOG_TYPE_TRACKBALL, - ANALOG_TYPE_MOUSE, - ANALOG_TYPE_COUNT - }; - - // directions - const UINT8 DIR_UP = 0x01; - const UINT8 DIR_DOWN = 0x02; - const UINT8 DIR_LEFT = 0x04; - const UINT8 DIR_RIGHT = 0x08; - const UINT8 DIR_4WAY = 0x10; - - // initialize the list of control types - struct - { - const char * type; /* general type of input */ - bool analog; - bool keyb; - 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[ANALOG_TYPE_COUNT]; - - memset(&control_info, 0, sizeof(control_info)); - - // tracking info as we iterate - int nplayer = 0; - int nbutton = 0; - int ncoin = 0; - UINT8 joytype[3] = { 0,0,0 }; - bool service = false; - bool tilt = false; - bool keypad = false; - bool keyboard = false; - bool mahjong = false; - bool hanafuda = false; - bool gambling = false; - - // iterate over the ports - for (ioport_port &port : portlist) - for (ioport_field &field : port.fields()) - { - int analogtype = -1; - - // track the highest player number - if (nplayer < field.player() + 1) - nplayer = field.player() + 1; - - // switch off of the type - switch (field.type()) - { - // map which joystick directions are present - case IPT_JOYSTICK_UP: joytype[0] |= DIR_UP | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICK_DOWN: joytype[0] |= DIR_DOWN | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICK_LEFT: joytype[0] |= DIR_LEFT | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICK_RIGHT: joytype[0] |= DIR_RIGHT | ((field.way() == 4) ? DIR_4WAY : 0); break; - - case IPT_JOYSTICKLEFT_UP: joytype[1] |= DIR_UP | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKLEFT_DOWN: joytype[1] |= DIR_DOWN | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKLEFT_LEFT: joytype[1] |= DIR_LEFT | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKLEFT_RIGHT: joytype[1] |= DIR_RIGHT | ((field.way() == 4) ? DIR_4WAY : 0); break; - - case IPT_JOYSTICKRIGHT_UP: joytype[2] |= DIR_UP | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKRIGHT_DOWN: joytype[2] |= DIR_DOWN | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKRIGHT_LEFT: joytype[2] |= DIR_LEFT | ((field.way() == 4) ? DIR_4WAY : 0); break; - case IPT_JOYSTICKRIGHT_RIGHT: joytype[2] |= DIR_RIGHT | ((field.way() == 4) ? DIR_4WAY : 0); break; - - // mark as an analog input, and get analog stats after switch - case IPT_AD_STICK_X: - case IPT_AD_STICK_Y: - case IPT_AD_STICK_Z: - control_info[analogtype = ANALOG_TYPE_JOYSTICK].type = "stick"; - break; - - case IPT_PADDLE: - case IPT_PADDLE_V: - control_info[analogtype = ANALOG_TYPE_PADDLE].type = "paddle"; - break; - - case IPT_PEDAL: - case IPT_PEDAL2: - case IPT_PEDAL3: - control_info[analogtype = ANALOG_TYPE_PEDAL].type = "pedal"; - break; - - case IPT_LIGHTGUN_X: - case IPT_LIGHTGUN_Y: - control_info[analogtype = ANALOG_TYPE_LIGHTGUN].type = "lightgun"; - break; - - case IPT_POSITIONAL: - case IPT_POSITIONAL_V: - control_info[analogtype = ANALOG_TYPE_POSITIONAL].type = "positional"; - break; - - case IPT_DIAL: - case IPT_DIAL_V: - control_info[analogtype = ANALOG_TYPE_DIAL].type = "dial"; - break; - - case IPT_TRACKBALL_X: - case IPT_TRACKBALL_Y: - control_info[analogtype = ANALOG_TYPE_TRACKBALL].type = "trackball"; - break; - - case IPT_MOUSE_X: - case IPT_MOUSE_Y: - control_info[analogtype = ANALOG_TYPE_MOUSE].type = "mouse"; - break; - - // track maximum button index - 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: - nbutton = MAX(nbutton, field.type() - IPT_BUTTON1 + 1); - 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: - ncoin = MAX(ncoin, field.type() - IPT_COIN1 + 1); - break; - - // track presence of these guys - case IPT_KEYPAD: - keypad = true; - break; - - case IPT_KEYBOARD: - keyboard = true; - 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) - mahjong = true; - else if (field.type() > IPT_HANAFUDA_FIRST && field.type() < IPT_HANAFUDA_LAST) - hanafuda = true; - else if (field.type() > IPT_GAMBLING_FIRST && field.type() < IPT_GAMBLING_LAST) - gambling = true; - break; - } - - // get the analog stats - if (analogtype != -1) - { - if (field.minval() != 0) - control_info[analogtype].min = field.minval(); - if (field.maxval() != 0) - control_info[analogtype].max = field.maxval(); - if (field.sensitivity() != 0) - control_info[analogtype].sensitivity = field.sensitivity(); - if (field.delta() != 0) - control_info[analogtype].keydelta = field.delta(); - if (field.analog_reverse() != 0) - control_info[analogtype].reverse = true; - } - } - - // output the basic info - fprintf(m_output, "\t\t<input"); - fprintf(m_output, " players=\"%d\"", nplayer); - if (nbutton != 0) - fprintf(m_output, " buttons=\"%d\"", nbutton); - if (ncoin != 0) - fprintf(m_output, " coins=\"%d\"", ncoin); - if (service) - fprintf(m_output, " service=\"yes\""); - if (tilt) - fprintf(m_output, " tilt=\"yes\""); - fprintf(m_output, ">\n"); - - // output the joystick types - if (joytype[1]==0 && joytype[2]!=0) { joytype[1] = joytype[2]; joytype[2] = 0; } - if (joytype[0]==0 && joytype[1]!=0) { joytype[0] = joytype[1]; joytype[1] = 0; } - if (joytype[1]==0 && joytype[2]!=0) { joytype[1] = joytype[2]; joytype[2] = 0; } - if (joytype[0] != 0) - { - const char *joys = (joytype[2]!=0) ? "triple" : (joytype[1]!=0) ? "double" : ""; - fprintf(m_output, "\t\t\t<control type=\"%sjoy\"", joys); - for (int lp=0; lp<3 && joytype[lp]!=0; lp++) - { - const char *plural = (lp==2) ? "3" : (lp==1) ? "2" : ""; - const char *ways; - switch (joytype[lp] & (DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT)) - { - case DIR_UP | DIR_DOWN | DIR_LEFT | DIR_RIGHT: - ways = ((joytype[lp] & DIR_4WAY) != 0) ? "4" : "8"; - 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 = ((joytype[lp] & DIR_4WAY) != 0) ? "3 (half4)" : "5 (half8)"; - break; - default: - ways = "strange2"; - break; - } - fprintf(m_output, " ways%s=\"%s\"", plural,ways); - } - fprintf(m_output, "/>\n"); - } - - // output analog types - for (auto & elem : control_info) - if (elem.type != nullptr) - { - fprintf(m_output, "\t\t\t<control type=\"%s\"", xml_normalize_string(elem.type)); - 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"); - } - - // output keypad and keyboard - if (keypad) - fprintf(m_output, "\t\t\t<control type=\"keypad\"/>\n"); - if (keyboard) - fprintf(m_output, "\t\t\t<control type=\"keyboard\"/>\n"); - - // misc - if (mahjong) - fprintf(m_output, "\t\t\t<control type=\"mahjong\"/>\n"); - if (hanafuda) - fprintf(m_output, "\t\t\t<control type=\"hanafuda\"/>\n"); - if (gambling) - fprintf(m_output, "\t\t\t<control type=\"gambling\"/>\n"); - - fprintf(m_output, "\t\t</input>\n"); + // 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_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 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 (ioport_port &port : portlist) + { + int ctrl_type = CTRL_DIGITAL_BUTTONS; + bool ctrl_analog = FALSE; + for (ioport_field &field : port.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++; + 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: + 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++; + 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++; + 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++; + } + 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++; + } + 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++; + } + 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].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<input"); + fprintf(m_output, " players=\"%d\"", nplayer); + if (ncoin != 0) + fprintf(m_output, " coins=\"%d\"", ncoin); + if (service) + fprintf(m_output, " service=\"yes\""); + if (tilt) + fprintf(m_output, " tilt=\"yes\""); + fprintf(m_output, ">\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<control type=\"%s\"", xml_normalize_string(elem.type)); + if (nplayer > 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.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<control type=\"%s%s\"", joys, xml_normalize_string(elem.type)); + if (nplayer > 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); + 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</input>\n"); } @@ -1229,6 +1429,7 @@ void info_xml_creator::output_images(device_t &device, const char *root_tag) { 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()); @@ -1239,9 +1440,13 @@ void info_xml_creator::output_images(device_t &device, const char *root_tag) if (imagedev->device().tag()) fprintf(m_output, " tag=\"%s\"", xml_normalize_string(newtag.c_str())); - // is this device mandatory? - if (imagedev->must_be_loaded()) - fprintf(m_output, " mandatory=\"1\""); + // is this device available as media switch? + if (!loadable) + fprintf(m_output, " fixed_image=\"1\""); + + // is this device mandatory? + if (imagedev->must_be_loaded()) + fprintf(m_output, " mandatory=\"1\""); if (imagedev->image_interface() && imagedev->image_interface()[0]) fprintf(m_output, " interface=\"%s\"", xml_normalize_string(imagedev->image_interface())); @@ -1249,25 +1454,27 @@ void info_xml_creator::output_images(device_t &device, const char *root_tag) // close the XML tag fprintf(m_output, ">\n"); - const char *name = imagedev->instance_name(); - const char *shortname = imagedev->brief_instance_name(); - - fprintf(m_output, "\t\t\t<instance"); - fprintf(m_output, " name=\"%s\"", xml_normalize_string(name)); - fprintf(m_output, " briefname=\"%s\"", xml_normalize_string(shortname)); - fprintf(m_output, "/>\n"); - - std::string extensions(imagedev->file_extensions()); - - char *ext = strtok((char *)extensions.c_str(), ","); - while (ext != nullptr) - { - fprintf(m_output, "\t\t\t<extension"); - fprintf(m_output, " name=\"%s\"", xml_normalize_string(ext)); - fprintf(m_output, "/>\n"); - ext = strtok(nullptr, ","); - } - + if (loadable) + { + const char *name = imagedev->instance_name(); + const char *shortname = imagedev->brief_instance_name(); + + fprintf(m_output, "\t\t\t<instance"); + fprintf(m_output, " name=\"%s\"", xml_normalize_string(name)); + fprintf(m_output, " briefname=\"%s\"", xml_normalize_string(shortname)); + fprintf(m_output, "/>\n"); + + std::string extensions(imagedev->file_extensions()); + + char *ext = strtok((char *)extensions.c_str(), ","); + while (ext != nullptr) + { + fprintf(m_output, "\t\t\t<extension"); + fprintf(m_output, " name=\"%s\"", xml_normalize_string(ext)); + fprintf(m_output, "/>\n"); + ext = strtok(nullptr, ","); + } + } fprintf(m_output, "\t\t</device>\n"); } } |