| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- #!/usr/bin/env python3
- #
- # Script to sort the game controller database entries in SDL_gamepad.c
- import re
- filename = "SDL_gamepad_db.h"
- input = open(filename)
- output = open(f"{filename}.new", "w")
- parsing_controllers = False
- controllers = []
- controller_guids = {}
- conditionals = []
- split_pattern = re.compile(r'([^"]*")([^,]*,)([^,]*,)([^"]*)(".*)')
- # BUS (1) CRC (3,2) VID (5,4) (6) PID (8,7) (9) VERSION (11,10) MISC (12)
- standard_guid_pattern = re.compile(r'^([0-9a-fA-F]{4})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})(0000)([0-9a-fA-F]{2})([0-9a-fA-F]{2})(0000)([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{4},)$')
- # These chipsets are used in multiple controllers with different mappings,
- # without enough unique information to differentiate them. e.g.
- # https://github.com/gabomdq/SDL_GameControllerDB/issues/202
- invalid_controllers = (
- ('0079', '0006', '0000'), # DragonRise Inc. Generic USB Joystick
- ('0079', '0006', '6120'), # DragonRise Inc. Generic USB Joystick
- ('04b4', '2412', 'c529'), # Flydigi Vader 2, Vader 2 Pro, Apex 2, Apex 3
- ('16c0', '05e1', '0000'), # Xinmotek Controller
- )
- def find_element(prefix, bindings):
- i=0
- for element in bindings:
- if element.startswith(prefix):
- return i
- i=(i + 1)
- return -1
- def get_crc_from_entry(entry):
- crc = ""
- line = "".join(entry)
- bindings = line.split(",")
- pos = find_element("crc:", bindings)
- if pos >= 0:
- crc = bindings[pos][4:]
- return crc
- def save_controller(line):
- global controllers
- match = split_pattern.match(line)
- entry = [ match.group(1), match.group(2), match.group(3) ]
- bindings = sorted(match.group(4).split(","))
- if (bindings[0] == ""):
- bindings.pop(0)
- name = entry[2].rstrip(',')
- crc = ""
- pos = find_element("crc:", bindings)
- if pos >= 0:
- crc = bindings[pos] + ","
- bindings.pop(pos)
- guid_match = standard_guid_pattern.match(entry[1])
- if guid_match:
- groups = guid_match.groups()
- crc_value = groups[2] + groups[1]
- vid_value = groups[4] + groups[3]
- pid_value = groups[7] + groups[6]
- version_value = groups[10] + groups[9]
- #print("CRC: %s, VID: %s, PID: %s, VERSION: %s" % (crc_value, vid_value, pid_value, version_value))
- if crc_value == "0000":
- if crc != "":
- crc_value = crc[4:-1]
- else:
- print("Extracting CRC from GUID of " + name)
- entry[1] = groups[0] + "0000" + "".join(groups[3:])
- crc = "crc:" + crc_value + ","
- if (vid_value, pid_value, crc_value) in invalid_controllers:
- print("Controller '%s' not unique, skipping" % name)
- return
- pos = find_element("type", bindings)
- if pos >= 0:
- bindings.insert(0, bindings.pop(pos))
- pos = find_element("platform", bindings)
- if pos >= 0:
- bindings.insert(0, bindings.pop(pos))
- pos = find_element("sdk", bindings)
- if pos >= 0:
- bindings.append(bindings.pop(pos))
- pos = find_element("hint:", bindings)
- if pos >= 0:
- bindings.append(bindings.pop(pos))
- entry.extend(crc)
- entry.extend(",".join(bindings) + ",")
- entry.append(match.group(5))
- controllers.append(entry)
- entry_id = entry[1] + get_crc_from_entry(entry)
- if ',sdk' in line or ',hint:' in line:
- conditionals.append(entry_id)
- def write_controllers():
- global controllers
- global controller_guids
- # Check for duplicates
- for entry in controllers:
- entry_id = entry[1] + get_crc_from_entry(entry)
- if (entry_id in controller_guids and entry_id not in conditionals):
- current_name = entry[2]
- existing_name = controller_guids[entry_id][2]
- print("Warning: entry '%s' is duplicate of entry '%s'" % (current_name, existing_name))
- if (not current_name.startswith("(DUPE)")):
- entry[2] = f"(DUPE) {current_name}"
- if (not existing_name.startswith("(DUPE)")):
- controller_guids[entry_id][2] = f"(DUPE) {existing_name}"
- controller_guids[entry_id] = entry
- for entry in sorted(controllers, key=lambda entry: f"{entry[2]}-{entry[1]}"):
- line = "".join(entry) + "\n"
- line = line.replace("\t", " ")
- if not line.endswith(",\n") and not line.endswith("*/\n") and not line.endswith(",\r\n") and not line.endswith("*/\r\n"):
- print("Warning: '%s' is missing a comma at the end of the line" % (line))
- output.write(line)
- controllers = []
- controller_guids = {}
- for line in input:
- if parsing_controllers:
- if (line.startswith("{")):
- output.write(line)
- elif (line.startswith(" NULL")):
- parsing_controllers = False
- write_controllers()
- output.write(line)
- elif (line.startswith("#if")):
- print(f"Parsing {line.strip()}")
- output.write(line)
- elif (line.startswith("#endif")):
- write_controllers()
- output.write(line)
- else:
- save_controller(line)
- else:
- if (line.startswith("static const char *")):
- parsing_controllers = True
- output.write(line)
- output.close()
- print(f"Finished writing {filename}.new")
|