sort_controllers.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env python3
  2. #
  3. # Script to sort the game controller database entries in SDL_gamepad.c
  4. import re
  5. filename = "SDL_gamepad_db.h"
  6. input = open(filename)
  7. output = open(f"{filename}.new", "w")
  8. parsing_controllers = False
  9. controllers = []
  10. controller_guids = {}
  11. conditionals = []
  12. split_pattern = re.compile(r'([^"]*")([^,]*,)([^,]*,)([^"]*)(".*)')
  13. guid_crc_pattern = re.compile(r'^([0-9a-zA-Z]{4})([0-9a-zA-Z]{2})([0-9a-zA-Z]{2})([0-9a-zA-Z]{24},)$')
  14. def find_element(prefix, bindings):
  15. i=0
  16. for element in bindings:
  17. if element.startswith(prefix):
  18. return i
  19. i=(i + 1)
  20. return -1
  21. def save_controller(line):
  22. global controllers
  23. match = split_pattern.match(line)
  24. entry = [ match.group(1), match.group(2), match.group(3) ]
  25. bindings = sorted(match.group(4).split(","))
  26. if (bindings[0] == ""):
  27. bindings.pop(0)
  28. crc = ""
  29. pos = find_element("crc:", bindings)
  30. if pos >= 0:
  31. crc = bindings[pos] + ","
  32. bindings.pop(pos)
  33. # Look for CRC embedded in the GUID and convert to crc element
  34. crc_match = guid_crc_pattern.match(entry[1])
  35. if crc_match and crc_match.group(2) != '00' and crc_match.group(3) != '00':
  36. print("Extracting CRC from GUID of " + entry[2])
  37. entry[1] = crc_match.group(1) + '0000' + crc_match.group(4)
  38. crc = "crc:" + crc_match.group(3) + crc_match.group(2) + ","
  39. pos = find_element("sdk", bindings)
  40. if pos >= 0:
  41. bindings.append(bindings.pop(pos))
  42. pos = find_element("hint:", bindings)
  43. if pos >= 0:
  44. bindings.append(bindings.pop(pos))
  45. entry.extend(crc)
  46. entry.extend(",".join(bindings) + ",")
  47. entry.append(match.group(5))
  48. controllers.append(entry)
  49. entry_id = entry[1] + entry[3]
  50. if ',sdk' in line or ',hint:' in line:
  51. conditionals.append(entry_id)
  52. def write_controllers():
  53. global controllers
  54. global controller_guids
  55. # Check for duplicates
  56. for entry in controllers:
  57. entry_id = entry[1] + entry[3]
  58. if (entry_id in controller_guids and entry_id not in conditionals):
  59. current_name = entry[2]
  60. existing_name = controller_guids[entry_id][2]
  61. print("Warning: entry '%s' is duplicate of entry '%s'" % (current_name, existing_name))
  62. if (not current_name.startswith("(DUPE)")):
  63. entry[2] = f"(DUPE) {current_name}"
  64. if (not existing_name.startswith("(DUPE)")):
  65. controller_guids[entry_id][2] = f"(DUPE) {existing_name}"
  66. controller_guids[entry_id] = entry
  67. for entry in sorted(controllers, key=lambda entry: f"{entry[2]}-{entry[1]}"):
  68. line = "".join(entry) + "\n"
  69. line = line.replace("\t", " ")
  70. if not line.endswith(",\n") and not line.endswith("*/\n") and not line.endswith(",\r\n") and not line.endswith("*/\r\n"):
  71. print("Warning: '%s' is missing a comma at the end of the line" % (line))
  72. output.write(line)
  73. controllers = []
  74. controller_guids = {}
  75. for line in input:
  76. if parsing_controllers:
  77. if (line.startswith("{")):
  78. output.write(line)
  79. elif (line.startswith(" NULL")):
  80. parsing_controllers = False
  81. write_controllers()
  82. output.write(line)
  83. elif (line.startswith("#if")):
  84. print(f"Parsing {line.strip()}")
  85. output.write(line)
  86. elif (line.startswith("#endif")):
  87. write_controllers()
  88. output.write(line)
  89. else:
  90. save_controller(line)
  91. else:
  92. if (line.startswith("static const char *s_ControllerMappings")):
  93. parsing_controllers = True
  94. output.write(line)
  95. output.close()
  96. print(f"Finished writing {filename}.new")