summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--docs/source/advanced/devicemap.rst81
-rw-r--r--docs/source/advanced/index.rst3
-rw-r--r--src/emu/input.cpp103
-rw-r--r--src/emu/input.h8
-rw-r--r--src/emu/ioport.cpp18
-rw-r--r--src/osd/modules/input/input_rawinput.cpp12
6 files changed, 222 insertions, 3 deletions
diff --git a/docs/source/advanced/devicemap.rst b/docs/source/advanced/devicemap.rst
new file mode 100644
index 00000000000..2b52ef00c78
--- /dev/null
+++ b/docs/source/advanced/devicemap.rst
@@ -0,0 +1,81 @@
+Stable Controller IDs
+===============================
+
+By default, the mapping between devices and controller IDs is not stable. For instance, a gamepad controller may be assigned to "Joy 1" initially, but after a reboot, it may get re-assigned to "Joy 3".
+
+The reason is that MAME enumerates attached devices and assigns controller IDs based on the enumeration order. Factors that can cause controller IDs to change include plugging / unplugging USB devices, changing ports / hubs and even system reboots.
+
+It is quite cumbersome to ensure that controller IDs are always correct.
+
+That's where the "mapdevice" configuration setting comes into the picture. This setting allows you to map a device name to a controller ID, ensuring that the specified device always maps to the same controller ID in MAME.
+
+Usage of mapdevice
+------------------
+The "mapdevice" xml element is specified under the input xml element in the configuration. It requires two attributes, "device" and "controller".
+
+The "device" attribute specifies the name of the device to match. It may also be a substring of the name. To see the list of available devices, enable verbose output and available devices will then be listed to the console at startup (more on this below).
+
+The "controller" attribute specifies the MAME controller ID. It is made up of a controller class (i.e. "JOYCODE", "GUNCODE", "MOUSECODE") and controller index. For example: "JOYCODE_1".
+
+Example
+-------
+Here's an example:
+
+| <mameconfig version="10">
+| <system name="default">
+| <input>
+| **<mapdevice device="VID_D209&amp;PID_1601" controller="GUNCODE_1" />**
+| **<mapdevice device="VID_D209&amp;PID_1602" controller="GUNCODE_2" />**
+| **<mapdevice device="XInput Player 1" controller="JOYCODE_1" />**
+| **<mapdevice device="XInput Player 2" controller="JOYCODE_2" />**
+|
+| <port type="P1_JOYSTICK_UP">
+| <newseq type="standard">
+| JOYCODE_1_YAXIS_UP_SWITCH OR KEYCODE_8PAD
+| </newseq>
+| </port>
+| ...
+|
+
+In the above example, we have four device mappings specified:
+
+The first two mapdevice entries map player 1 and 2 lightguns to Gun 1 and Gun 2, respectively. We use a substring of the full raw device names to match each devices. Note that, since this is XML, we needed to escape the '&' in the name using '&amp;'.
+
+The last two mapdevices entries map player 1 and player 2 gamepad controllers to Joy 1 and Joy 2, respectively. In this case, these are XInput devices.
+
+Listing Available Devices
+-------------------------
+How did we obtain the device names in the above example? Easy!
+
+We simply set verbose to 1 in mame.ini:
+
+| #
+| # CORE DEBUGGING OPTIONS
+| #
+| **verbose 1**
+|
+
+Then, when MAME is started, it will list available devices to the console. For example:
+
+| Input: Adding Gun #0:
+| Input: Adding Gun #1:
+| Input: Adding Gun #2: HID-compliant mouse (\\?\HID#VID_045E&PID_0053#7&18297dcb&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})
+| Input: Adding Gun #3: HID-compliant mouse (\\?\HID#IrDeviceV2&Col08#2&2818a073&0&0007#{378de44c-56ef-11d1-bc8c-00a0c91405dd})
+| Input: Adding Gun #4: HID-compliant mouse (\\?\HID#VID_D209&PID_1602&MI_02#8&389ab7f3&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})
+| Input: Adding Gun #5: HID-compliant mouse (\\?\HID#VID_D209&PID_1601&MI_02#9&375eebb1&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})
+| Input: Adding Gun #6: HID-compliant mouse (\\?\HID#VID_1241&PID_1111#8&198f3adc&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})
+| Skipping DirectInput for XInput compatible joystick Controller (XBOX 360 For Windows).
+| Input: Adding Joy #0: ATRAK Device #1
+| Skipping DirectInput for XInput compatible joystick Controller (XBOX 360 For Windows).
+| Input: Adding Joy #1: ATRAK Device #2
+| Input: Adding Joy #2: XInput Player 1
+| Input: Adding Joy #3: XInput Player 2
+|
+
+Furthermore, when devices are mapped using mapdevice, you'll see that in the verbose logging too, such as:
+
+| Mapped device 'HID-compliant mouse (\\?\HID#VID_D209&PID_1601&MI_02#9&375eebb1&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})' to Gun #0
+| Mapped device 'HID-compliant mouse (\\?\HID#VID_D209&PID_1602&MI_02#8&389ab7f3&0&0000#{378de44c-56ef-11d1-bc8c-00a0c91405dd})' to Gun #1
+| Mapped device 'XInput Player 1' to Joy #0
+| Mapped device 'XInput Player 2' to Joy #1
+|
diff --git a/docs/source/advanced/index.rst b/docs/source/advanced/index.rst
index d40a328fe2f..8ce1dfee49c 100644
--- a/docs/source/advanced/index.rst
+++ b/docs/source/advanced/index.rst
@@ -9,4 +9,5 @@ Advanced configuration
shiftertoggle
bgfx
hlsl
- glsl \ No newline at end of file
+ glsl
+ devicemap \ No newline at end of file
diff --git a/src/emu/input.cpp b/src/emu/input.cpp
index bbae55fcd34..560e9271797 100644
--- a/src/emu/input.cpp
+++ b/src/emu/input.cpp
@@ -941,6 +941,21 @@ void input_device::apply_steadykey() const
}
}
+//-------------------------------------------------
+// match_device_name - match device name via
+// substring search
+//-------------------------------------------------
+
+bool input_device::match_device_name(const char *devicename)
+{
+ std::string devicenameupper(devicename);
+ std::string nameupper(m_name);
+
+ strmakeupper(devicenameupper);
+ strmakeupper(nameupper);
+
+ return std::string::npos == nameupper.find(devicenameupper) ? false : true;
+}
//**************************************************************************
@@ -1020,6 +1035,29 @@ input_item_class input_class::standard_item_class(input_item_id itemid)
//-------------------------------------------------
+// remap_device_index - remaps device index by
+// mapping oldindex to newindex
+//-------------------------------------------------
+
+void input_class::remap_device_index(int oldindex, int newindex)
+{
+ assert(oldindex >= 0 && oldindex < DEVICE_INDEX_MAXIMUM);
+ assert(newindex >= 0 && newindex < DEVICE_INDEX_MAXIMUM);
+
+ // swap indexes in m_device array
+ m_device[oldindex].swap(m_device[newindex]);
+
+ // update device indexes
+ m_device[oldindex]->set_devindex(oldindex);
+ m_device[newindex]->set_devindex(newindex);
+
+ // update the maximum index found, since newindex may
+ // exceed current m_maxindex
+ m_maxindex = std::max(m_maxindex, newindex);
+}
+
+
+//-------------------------------------------------
// frame_callback - per-frame callback for various
// bookkeeping
//-------------------------------------------------
@@ -2058,6 +2096,71 @@ void input_manager::seq_from_tokens(input_seq &seq, const char *string)
}
}
+//-------------------------------------------------
+// map_device_to_controller - map device to
+// controller based on device map table
+//-------------------------------------------------
+
+bool input_manager::map_device_to_controller(const devicemap_table_type *devicemap_table)
+{
+ if (nullptr == devicemap_table)
+ return true;
+
+ for (devicemap_table_type::const_iterator it = devicemap_table->begin(); it != devicemap_table->end(); it++)
+ {
+ const char *devicename = it->first.c_str();
+ const char *controllername = it->second.c_str();
+
+ // tokenize the controller name into device class and index (i.e. controller name should be of the form "GUNCODE_1")
+ std::string token[2];
+ int numtokens;
+ const char *_token = controllername;
+ for (numtokens = 0; numtokens < ARRAY_LENGTH(token); )
+ {
+ // make a token up to the next underscore
+ char *score = (char *)strchr(_token, '_');
+ token[numtokens++].assign(_token, (score == nullptr) ? strlen(_token) : (score - _token));
+
+ // if we hit the end, we're done, else advance our pointer
+ if (score == nullptr)
+ break;
+ _token = score + 1;
+ }
+ if (2 != numtokens)
+ return false;
+
+ // first token should be the devclass
+ input_device_class devclass = input_device_class((*devclass_token_table)[strmakeupper(token[0]).c_str()]);
+ if (devclass == ~input_device_class(0))
+ return false;
+
+ // second token should be the devindex
+ int devindex = 0;
+ if (1 != sscanf(token[1].c_str(), "%d", &devindex))
+ return false;
+ devindex--;
+
+ if (devindex >= DEVICE_INDEX_MAXIMUM)
+ return false;
+
+ // enumerate through devices and look for a match
+ input_class *input_devclass = m_class[devclass];
+ for (int devnum = 0; devnum <= input_devclass->maxindex(); devnum++)
+ {
+ input_device *device = input_devclass->device(devnum);
+ if (device != nullptr && device->match_device_name(devicename))
+ {
+ // remap devindex
+ input_devclass->remap_device_index(device->devindex(), devindex);
+ osd_printf_info("Mapped device '%s' to %s #%d\n", device->name(), (*devclass_string_table)[input_devclass->devclass()], devindex);
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
//-------------------------------------------------
// set_global_joystick_map - set the joystick map
diff --git a/src/emu/input.h b/src/emu/input.h
index d7012c1f779..1c4276fef3d 100644
--- a/src/emu/input.h
+++ b/src/emu/input.h
@@ -352,6 +352,8 @@ class input_manager;
// callback for getting the value of an item on a device
typedef INT32 (*item_get_state_func)(void *device_internal, void *item_internal);
+// controller alias table typedef
+typedef std::map<std::string, std::string> devicemap_table_type;
// ======================> joystick_map
@@ -562,6 +564,9 @@ public:
bool steadykey_enabled() const { return m_steadykey_enabled; }
bool lightgun_reload_button() const { return m_lightgun_reload_button; }
+ // setters
+ void set_devindex(int devindex) { m_devindex = devindex; }
+
// item management
input_item_id add_item(const char *name, input_item_id itemid, item_get_state_func getstate, void *internal = nullptr);
void set_joystick_map(const joystick_map &map) { m_joymap = map; }
@@ -569,6 +574,7 @@ public:
// helpers
INT32 apply_deadzone_and_saturation(INT32 value) const;
void apply_steadykey() const;
+ bool match_device_name(const char * devicename);
private:
// internal state
@@ -616,6 +622,7 @@ public:
// misc helpers
input_item_class standard_item_class(input_item_id itemid);
+ void remap_device_index(int oldindex, int newindex);
private:
// internal helpers
@@ -679,6 +686,7 @@ public:
// misc
bool set_global_joystick_map(const char *mapstring);
+ bool map_device_to_controller(const devicemap_table_type *devicemap_table = nullptr);
private:
// internal helpers
diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp
index 44e758b5752..00dbedb8de8 100644
--- a/src/emu/ioport.cpp
+++ b/src/emu/ioport.cpp
@@ -2904,6 +2904,24 @@ void ioport_manager::load_config(config_type cfg_type, xml_data_node *parentnode
if (cfg_type == config_type::CONFIG_TYPE_CONTROLLER)
load_remap_table(parentnode);
+ // load device map table, if any
+ std::unique_ptr<devicemap_table_type> devicemap_table = std::make_unique<devicemap_table_type>();
+ for (xml_data_node *mapdevice_node = xml_get_sibling(parentnode->child, "mapdevice"); mapdevice_node != nullptr; mapdevice_node = xml_get_sibling(mapdevice_node->next, "mapdevice"))
+ {
+ const char *devicename = xml_get_attribute_string(mapdevice_node, "device", nullptr);
+ const char *controllername = xml_get_attribute_string(mapdevice_node, "controller", nullptr);
+ if (devicename != nullptr && controllername != nullptr)
+ {
+ devicemap_table->insert(std::make_pair(std::string(devicename), std::string(controllername)));
+ }
+ }
+
+ // map device to controller if we have a device map
+ if (!devicemap_table->empty())
+ {
+ machine().input().map_device_to_controller(devicemap_table.get());
+ }
+
// iterate over all the port nodes
for (xml_data_node *portnode = xml_get_sibling(parentnode->child, "port"); portnode; portnode = xml_get_sibling(portnode->next, "port"))
{
diff --git a/src/osd/modules/input/input_rawinput.cpp b/src/osd/modules/input/input_rawinput.cpp
index ee6dbf4a511..44fba696e2c 100644
--- a/src/osd/modules/input/input_rawinput.cpp
+++ b/src/osd/modules/input/input_rawinput.cpp
@@ -572,8 +572,16 @@ protected:
// improve the name and then allocate a device
std::wstring name = rawinput_device_improve_name(tname.get());
- // convert name to utf8
- std::string utf8_name = utf8_from_wstring(name.c_str());
+ // convert name to utf8. Preserve raw name as well (if different than improved name) to allow mapping of device to controller.
+ std::string utf8_name;
+ if (0 == name.compare(tname.get()))
+ {
+ utf8_name = utf8_from_wstring(name.c_str());
+ }
+ else
+ {
+ utf8_name = util::string_format("%s (%s)", utf8_from_wstring(name.c_str()), utf8_from_wstring(tname.get()));
+ }
devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.c_str(), *this);