1 /*
2 * Copyright (C) 2017-2020 Team Kodi (https://kodi.tv)
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 * See LICENSE.md for more information.
6 */
7
8 #include "ControllerTopology.h"
9 #include "InputDefinitions.h"
10 #include "InputTranslator.h"
11 #include "libretro/LibretroEnvironment.h"
12 #include "log/Log.h"
13
14 #include <tinyxml.h>
15
16 #include <algorithm>
17 #include <sstream>
18
19 using namespace LIBRETRO;
20
21 #define TOPOLOGY_XML "topology.xml"
22
23 #define ADDRESS_SEPARATOR '/'
24
GetInstance()25 CControllerTopology& CControllerTopology::GetInstance()
26 {
27 static CControllerTopology instance;
28 return instance;
29 }
30
LoadTopology()31 bool CControllerTopology::LoadTopology()
32 {
33 bool bSuccess = false;
34
35 Clear();
36
37 std::string strFilename = CLibretroEnvironment::Get().GetResourcePath(TOPOLOGY_XML);
38 if (strFilename.empty())
39 {
40 dsyslog("Could not locate controller topology \"%s\"", TOPOLOGY_XML);
41 }
42 else
43 {
44 dsyslog("Loading controller topology \"%s\"", strFilename.c_str());
45
46 TiXmlDocument topologyXml;
47 if (topologyXml.LoadFile(strFilename))
48 {
49 TiXmlElement* pRootElement = topologyXml.RootElement();
50 bSuccess = Deserialize(pRootElement);
51 }
52 else
53 {
54 esyslog("Failed to load controller topology: %s (line %d)", topologyXml.ErrorDesc(), topologyXml.ErrorRow());
55 }
56 }
57
58 return bSuccess;
59 }
60
Clear()61 void CControllerTopology::Clear()
62 {
63 m_ports.clear();
64 }
65
GetTopology()66 game_input_topology *CControllerTopology::GetTopology()
67 {
68 if (!m_ports.empty())
69 {
70 game_input_topology *topology = new game_input_topology;
71
72 int unsigned portCount = 0;
73 topology->ports = GetPorts(m_ports, portCount);
74 topology->port_count = portCount;
75 topology->player_limit = m_playerLimit;
76
77 return topology;
78 }
79
80 return nullptr;
81 }
82
FreeTopology(game_input_topology * topology)83 void CControllerTopology::FreeTopology(game_input_topology *topology)
84 {
85 if (topology != nullptr)
86 FreePorts(topology->ports, topology->port_count);
87
88 delete topology;
89 }
90
FreePorts(game_input_port * ports,unsigned int portCount)91 void CControllerTopology::FreePorts(game_input_port *ports, unsigned int portCount)
92 {
93 if (ports != nullptr)
94 {
95 for (unsigned int i = 0; i < portCount; i++)
96 FreeControllers(ports[i].accepted_devices, ports[i].device_count);
97 }
98
99 delete[] ports;
100 }
101
GetPortIndex(const std::string & address) const102 int CControllerTopology::GetPortIndex(const std::string &address) const
103 {
104 int portIndex = -1;
105 unsigned int playerCount = 0;
106
107 if (m_ports.empty())
108 {
109 // If topology is unknown, use the first port
110 portIndex = 0;
111 }
112 else
113 {
114 for (const auto &port : m_ports)
115 {
116 if (port->type == GAME_PORT_CONTROLLER)
117 {
118 portIndex = GetPortIndex(port, address, playerCount);
119 if (portIndex >= 0)
120 break;
121 }
122 }
123 }
124
125 // Reset port index if it exceeds the player limit
126 if (m_playerLimit >= 0 && portIndex >= m_playerLimit)
127 portIndex = -1;
128
129 return portIndex;
130 }
131
GetPortIndex(const PortPtr & port,const std::string & portAddress,unsigned int & playerCount)132 int CControllerTopology::GetPortIndex(const PortPtr &port, const std::string &portAddress, unsigned int &playerCount)
133 {
134 int portIndex = -1;
135
136 std::string portId;
137 std::string remainingAddress;
138 SplitAddress(portAddress, portId, remainingAddress);
139
140 if (port->portId == portId)
141 {
142 if (remainingAddress.empty())
143 {
144 // Base case
145 portIndex = playerCount;
146 }
147 else if (!port->activeId.empty())
148 {
149 // Visit active controller
150 const auto &accepts = port->accepts;
151
152 auto it = std::find_if(accepts.begin(), accepts.end(),
153 [&port](const ControllerPtr &controller)
154 {
155 return port->activeId == controller->controllerId;
156 });
157
158 if (it != accepts.end())
159 {
160 const ControllerPtr &controller = *it;
161 portIndex = GetPortIndex(controller, portAddress, playerCount);
162 }
163 }
164 }
165
166 playerCount++;
167
168 return portIndex;
169 }
170
GetPortIndex(const ControllerPtr & controller,const std::string & portAddress,unsigned int & playerCount)171 int CControllerTopology::GetPortIndex(const ControllerPtr &controller, const std::string &portAddress, unsigned int &playerCount)
172 {
173 int portIndex = -1;
174
175 std::string portControllerId;
176 std::string remainingAddress;
177 SplitAddress(portAddress, portControllerId, remainingAddress);
178
179 if (controller->controllerId == portControllerId)
180 {
181 const auto &ports = controller->ports;
182
183 for (const auto &port : ports)
184 {
185 portIndex = GetPortIndex(port, portAddress, playerCount);
186 if (portIndex >= 0)
187 break;
188 }
189 }
190
191 if (controller->bProvidesInput)
192 playerCount++;
193
194 return portIndex;
195 }
196
GetAddress(unsigned int portIndex) const197 std::string CControllerTopology::GetAddress(unsigned int portIndex) const
198 {
199 std::string address;
200 unsigned int playerCount = 0;
201
202 if (m_ports.empty())
203 {
204 return DEFAULT_PORT_ID;
205 }
206 else
207 {
208 for (const auto &port : m_ports)
209 {
210 if (port->type == GAME_PORT_CONTROLLER)
211 {
212 address = GetAddress(port, portIndex, playerCount);
213 if (!address.empty())
214 break;
215 }
216 }
217 }
218
219 return address;
220 }
221
GetAddress(const PortPtr & port,unsigned int portIndex,unsigned int & playerCount)222 std::string CControllerTopology::GetAddress(const PortPtr &port, unsigned int portIndex, unsigned int &playerCount)
223 {
224 std::string address;
225
226 if (portIndex == playerCount)
227 {
228 // Base case
229 address = ADDRESS_SEPARATOR + port->portId;
230 }
231 else if (!port->activeId.empty())
232 {
233 // Visit active controller
234 const auto &accepts = port->accepts;
235
236 auto it = std::find_if(accepts.begin(), accepts.end(),
237 [&port](const ControllerPtr &controller)
238 {
239 return port->activeId == controller->controllerId;
240 });
241
242 if (it != accepts.end())
243 {
244 const ControllerPtr &controller = *it;
245 std::string controllerAddress = GetAddress(controller, portIndex, playerCount);
246 if (!controllerAddress.empty())
247 address = ADDRESS_SEPARATOR + port->portId + controllerAddress;
248 }
249 }
250
251 playerCount++;
252
253 return address;
254 }
255
GetAddress(const ControllerPtr & controller,unsigned int portIndex,unsigned int & playerCount)256 std::string CControllerTopology::GetAddress(const ControllerPtr &controller, unsigned int portIndex, unsigned int &playerCount)
257 {
258 std::string address;
259
260 const auto &ports = controller->ports;
261 for (const auto &port : ports)
262 {
263 std::string portAddress = GetAddress(port, portIndex, playerCount);
264 if (!portAddress.empty())
265 {
266 address = ADDRESS_SEPARATOR + controller->controllerId + portAddress;
267 break;
268 }
269 }
270
271 if (controller->bProvidesInput)
272 playerCount++;
273
274 return address;
275 }
276
SetDevice(GAME_PORT_TYPE portType,const std::string & controllerId)277 bool CControllerTopology::SetDevice(GAME_PORT_TYPE portType, const std::string &controllerId)
278 {
279 for (const auto &port : m_ports)
280 {
281 if (port->type == portType)
282 {
283 const auto &accepts = port->accepts;
284
285 auto it = std::find_if(accepts.begin(), accepts.end(),
286 [&controllerId](const ControllerPtr &controller)
287 {
288 return controllerId == controller->controllerId;
289 });
290
291 if (it != accepts.end())
292 {
293 port->activeId = controllerId;
294 return true;
295 }
296 }
297 }
298
299 return false;
300 }
301
RemoveDevice(GAME_PORT_TYPE portType)302 void CControllerTopology::RemoveDevice(GAME_PORT_TYPE portType)
303 {
304 for (const auto &port : m_ports)
305 {
306 if (port->type == portType)
307 port->activeId.clear();
308 }
309 }
310
SetController(const std::string & portAddress,const std::string & controllerId,bool bProvidesInput)311 bool CControllerTopology::SetController(const std::string &portAddress, const std::string &controllerId, bool bProvidesInput)
312 {
313 if (m_ports.empty())
314 {
315 // No topology was specified, create one now
316 m_ports.emplace_back(CreateDefaultPort(controllerId));
317 }
318
319 for (const auto &port : m_ports)
320 {
321 if (port->type == GAME_PORT_CONTROLLER)
322 {
323 if (SetController(port, portAddress, controllerId, bProvidesInput))
324 return true;
325 }
326 }
327
328 return false;
329 }
330
SetController(const PortPtr & port,const std::string & portAddress,const std::string & controllerId,bool bProvidesInput)331 bool CControllerTopology::SetController(const PortPtr &port, const std::string &portAddress, const std::string &controllerId, bool bProvidesInput)
332 {
333 bool bSuccess = false;
334
335 std::string portId;
336 std::string remainingAddress;
337 SplitAddress(portAddress, portId, remainingAddress);
338
339 if (port->portId == portId)
340 {
341 const auto &accepts = port->accepts;
342
343 if (remainingAddress.empty())
344 {
345 // Base case
346 auto it = std::find_if(accepts.begin(), accepts.end(),
347 [&controllerId](const ControllerPtr &controller)
348 {
349 return controllerId == controller->controllerId;
350 });
351
352 if (it != accepts.end())
353 {
354 port->activeId = controllerId;
355 (*it)->bProvidesInput = bProvidesInput;
356 bSuccess = true;
357 }
358 }
359 else if (!port->activeId.empty())
360 {
361 // Visit active controller
362 auto it = std::find_if(accepts.begin(), accepts.end(),
363 [&port](const ControllerPtr &controller)
364 {
365 return port->activeId == controller->controllerId;
366 });
367
368 if (it != accepts.end())
369 {
370 const ControllerPtr &controller = *it;
371 if (SetController(controller, remainingAddress, controllerId, bProvidesInput))
372 bSuccess = true;
373 }
374 }
375 }
376
377 return bSuccess;
378 }
379
SetController(const ControllerPtr & controller,const std::string & portAddress,const std::string & controllerId,bool bProvidesInput)380 bool CControllerTopology::SetController(const ControllerPtr &controller, const std::string &portAddress, const std::string &controllerId, bool bProvidesInput)
381 {
382 std::string portControllerId;
383 std::string remainingAddress;
384 SplitAddress(portAddress, portControllerId, remainingAddress);
385
386 if (controller->controllerId == portControllerId)
387 {
388 const auto &ports = controller->ports;
389 for (const auto &port : ports)
390 {
391 if (SetController(port, remainingAddress, controllerId, bProvidesInput))
392 return true;
393 }
394 }
395
396 return false;
397 }
398
RemoveController(const std::string & portAddress)399 void CControllerTopology::RemoveController(const std::string &portAddress)
400 {
401 for (const auto &port : m_ports)
402 {
403 if (port->type == GAME_PORT_CONTROLLER)
404 RemoveController(port, portAddress);
405 }
406 }
407
RemoveController(const PortPtr & port,const std::string & portAddress)408 void CControllerTopology::RemoveController(const PortPtr &port, const std::string &portAddress)
409 {
410 std::string portId;
411 std::string remainingAddress;
412 SplitAddress(portAddress, portId, remainingAddress);
413
414 if (port->portId == portId)
415 {
416 if (remainingAddress.empty())
417 {
418 // Base case
419 port->activeId.clear();
420 }
421 else if (!port->activeId.empty())
422 {
423 // Visit active controller
424 const auto &accepts = port->accepts;
425
426 auto it = std::find_if(accepts.begin(), accepts.end(),
427 [&port](const ControllerPtr &controller)
428 {
429 return port->activeId == controller->controllerId;
430 });
431
432 if (it != accepts.end())
433 {
434 const ControllerPtr &controller = *it;
435 RemoveController(controller, remainingAddress);
436 }
437 }
438 }
439 }
440
RemoveController(const ControllerPtr & controller,const std::string & portAddress)441 void CControllerTopology::RemoveController(const ControllerPtr &controller, const std::string &portAddress)
442 {
443 std::string portControllerId;
444 std::string remainingAddress;
445 SplitAddress(portAddress, portControllerId, remainingAddress);
446
447 if (controller->controllerId == portControllerId)
448 {
449 const auto &ports = controller->ports;
450 for (const auto &port : ports)
451 RemoveController(port, remainingAddress);
452 }
453 }
454
Deserialize(const TiXmlElement * pElement)455 bool CControllerTopology::Deserialize(const TiXmlElement* pElement)
456 {
457 bool bSuccess = false;
458
459 if (pElement == nullptr ||
460 pElement->ValueStr() != TOPOLOGY_XML_ROOT)
461 {
462 esyslog("Can't find root <%s> tag", TOPOLOGY_XML_ROOT);
463 }
464 else
465 {
466 const char* strPlayerLimit = pElement->Attribute(TOPOLOGY_XML_ATTR_PLAYER_LIMIT);
467 if (strPlayerLimit != nullptr)
468 {
469 std::istringstream ssPlayerLimit(strPlayerLimit);
470 ssPlayerLimit >> m_playerLimit;
471 }
472
473 const TiXmlElement* pChild = pElement->FirstChildElement(TOPOLOGY_XML_ELEM_PORT);
474 if (pChild == nullptr)
475 {
476 esyslog("Can't find <%s> tag", TOPOLOGY_XML_ELEM_PORT);
477 }
478 else
479 {
480 bSuccess = true;
481
482 for ( ; pChild != nullptr; pChild = pChild->NextSiblingElement(TOPOLOGY_XML_ELEM_PORT))
483 {
484 PortPtr port = DeserializePort(pChild);
485
486 if (!port)
487 {
488 bSuccess = false;
489 break;
490 }
491
492 m_ports.emplace_back(std::move(port));
493 }
494
495 if (bSuccess)
496 dsyslog("Loaded controller topology with %u ports", m_ports.size());
497 }
498 }
499
500 return bSuccess;
501 }
502
DeserializePort(const TiXmlElement * pElement)503 CControllerTopology::PortPtr CControllerTopology::DeserializePort(const TiXmlElement* pElement)
504 {
505 PortPtr port;
506
507 const char* strPortType = pElement->Attribute(TOPOLOGY_XML_ATTR_PORT_TYPE);
508
509 GAME_PORT_TYPE portType = CInputTranslator::GetPortType(strPortType != nullptr ? strPortType : "");
510 if (portType == GAME_PORT_UNKNOWN)
511 portType = GAME_PORT_CONTROLLER;
512
513 const char* strPortId = pElement->Attribute(TOPOLOGY_XML_ATTR_PORT_ID);
514 if (portType == GAME_PORT_CONTROLLER && strPortId == nullptr)
515 {
516 esyslog("<%s> tag is missing attribute \"%s\", can't proceed without port ID", TOPOLOGY_XML_ELEM_PORT, TOPOLOGY_XML_ATTR_PORT_ID);
517 }
518 else
519 {
520 port.reset(new Port{ portType, strPortId != nullptr ? strPortId : "" });
521
522 const TiXmlElement* pChild = pElement->FirstChildElement(TOPOLOGY_XML_ELEM_ACCEPTS);
523 if (pChild == nullptr)
524 {
525 dsyslog("<%s> tag with ID \"%s\" is missing <%s> node, port won't accept any controllers", TOPOLOGY_XML_ELEM_PORT, strPortId, TOPOLOGY_XML_ELEM_ACCEPTS);
526 }
527 else
528 {
529 for ( ; pChild != nullptr; pChild = pChild->NextSiblingElement(TOPOLOGY_XML_ELEM_ACCEPTS))
530 {
531 ControllerPtr controller = DeserializeController(pChild);
532
533 if (!controller)
534 {
535 port.reset();
536 break;
537 }
538
539 port->accepts.emplace_back(std::move(controller));
540 }
541 }
542 }
543
544 return port;
545 }
546
DeserializeController(const TiXmlElement * pElement)547 CControllerTopology::ControllerPtr CControllerTopology::DeserializeController(const TiXmlElement* pElement)
548 {
549 ControllerPtr controller;
550
551 const char* strControllerId = pElement->Attribute(TOPOLOGY_XML_ATTR_CONTROLLER_ID);
552 if (strControllerId == nullptr)
553 {
554 esyslog("<%s> tag is missing attribute \"%s\", can't proceed without controller ID", TOPOLOGY_XML_ELEM_ACCEPTS, TOPOLOGY_XML_ATTR_CONTROLLER_ID);
555 }
556 else
557 {
558 controller.reset(new Controller{ strControllerId });
559
560 const TiXmlElement* pChild = pElement->FirstChildElement(TOPOLOGY_XML_ELEM_PORT);
561 for ( ; pChild != nullptr; pChild = pChild->NextSiblingElement(TOPOLOGY_XML_ELEM_PORT))
562 {
563 PortPtr port = DeserializePort(pChild);
564
565 if (!port)
566 {
567 controller.reset();
568 break;
569 }
570
571 controller->ports.emplace_back(std::move(port));
572 }
573 }
574
575 return controller;
576 }
577
GetPorts(const std::vector<PortPtr> & portVec,unsigned int & portCount)578 game_input_port *CControllerTopology::GetPorts(const std::vector<PortPtr> &portVec, unsigned int &portCount)
579 {
580 game_input_port *ports = nullptr;
581
582 portCount = static_cast<unsigned int>(portVec.size());
583 if (portCount > 0)
584 {
585 ports = new game_input_port[portCount];
586
587 for (unsigned int i = 0; i < portCount; i++)
588 {
589 ports[i].type = portVec[i]->type;
590 ports[i].port_id = portVec[i]->portId.c_str();
591
592 unsigned int deviceCount = 0;
593 ports[i].accepted_devices = GetControllers(portVec[i]->accepts, deviceCount);
594 ports[i].device_count = deviceCount;
595 }
596 }
597
598 return ports;
599 }
600
GetControllers(const std::vector<ControllerPtr> & controllerVec,unsigned int & deviceCount)601 game_input_device *CControllerTopology::GetControllers(const std::vector<ControllerPtr> &controllerVec, unsigned int &deviceCount)
602 {
603 game_input_device *devices = nullptr;
604
605 deviceCount = static_cast<unsigned int>(controllerVec.size());
606 if (deviceCount > 0)
607 {
608 devices = new game_input_device[deviceCount];
609
610 for (unsigned int i = 0; i < deviceCount; i++)
611 {
612 devices[i].controller_id = controllerVec[i]->controllerId.c_str();
613
614 unsigned int portCount = 0;
615 devices[i].available_ports = GetPorts(controllerVec[i]->ports, portCount);
616 devices[i].port_count = portCount;
617 }
618 }
619
620 return devices;
621 }
622
FreeControllers(game_input_device * devices,unsigned int deviceCount)623 void CControllerTopology::FreeControllers(game_input_device *devices, unsigned int deviceCount)
624 {
625 for (unsigned int i = 0; i < deviceCount; i++)
626 FreePorts(devices[i].available_ports, devices[i].port_count);
627
628 delete[] devices;
629 }
630
CreateDefaultPort(const std::string & acceptedController)631 CControllerTopology::PortPtr CControllerTopology::CreateDefaultPort(const std::string &acceptedController)
632 {
633 PortPtr port(new Port{GAME_PORT_CONTROLLER, DEFAULT_PORT_ID, {}, {}});
634
635 port->accepts.emplace_back(new Controller{ acceptedController });
636
637 return port;
638 }
639
SplitAddress(const std::string & address,std::string & nodeId,std::string & remainingAddress)640 void CControllerTopology::SplitAddress(const std::string &address, std::string &nodeId, std::string &remainingAddress)
641 {
642 // Start searching after leading /
643 size_t pos = address.find(ADDRESS_SEPARATOR, 1);
644 if (pos == std::string::npos)
645 {
646 // Skip leading / to extract node ID
647 nodeId = address.substr(1);
648 remainingAddress.clear();
649 }
650 else
651 {
652 // Skip leading / to extract node ID
653 nodeId = address.substr(1, pos);
654 remainingAddress = address.substr(pos);
655 }
656 }
657