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