Atlas - sort_controllers.py

Home / ext / SDL / src / joystick Lines: 6 | Size: 5605 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1#!/usr/bin/env python3 2# 3# Script to sort the game controller database entries in SDL_gamepad.c 4 5import re 6 7 8filename = "SDL_gamepad_db.h" 9input = open(filename) 10output = open(f"{filename}.new", "w") 11parsing_controllers = False 12controllers = [] 13controller_guids = {} 14conditionals = [] 15split_pattern = re.compile(r'([^"]*")([^,]*,)([^,]*,)([^"]*)(".*)') 16# BUS (1) CRC (3,2) VID (5,4) (6) PID (8,7) (9) VERSION (11,10) MISC (12) 17standard_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},)$') 18 19# These chipsets are used in multiple controllers with different mappings, 20# without enough unique information to differentiate them. e.g. 21# https://github.com/gabomdq/SDL_GameControllerDB/issues/202 22invalid_controllers = ( 23 ('0079', '0006', '0000'), # DragonRise Inc. Generic USB Joystick 24 ('0079', '0006', '6120'), # DragonRise Inc. Generic USB Joystick 25 ('04b4', '2412', 'c529'), # Flydigi Vader 2, Vader 2 Pro, Apex 2, Apex 3, Apex 4 26 ('16c0', '05e1', '0000'), # Xinmotek Controller 27) 28 29def find_element(prefix, bindings): 30 i=0 31 for element in bindings: 32 if element.startswith(prefix): 33 return i 34 i=(i + 1) 35 36 return -1 37 38def get_crc_from_entry(entry): 39 crc = "" 40 line = "".join(entry) 41 bindings = line.split(",") 42 pos = find_element("crc:", bindings) 43 if pos >= 0: 44 crc = bindings[pos][4:] 45 return crc 46 47def save_controller(line): 48 global controllers 49 match = split_pattern.match(line) 50 entry = [ match.group(1), match.group(2), match.group(3) ] 51 bindings = sorted(match.group(4).split(",")) 52 if (bindings[0] == ""): 53 bindings.pop(0) 54 55 name = entry[2].rstrip(',') 56 57 crc = "" 58 pos = find_element("crc:", bindings) 59 if pos >= 0: 60 crc = bindings[pos] + "," 61 bindings.pop(pos) 62 63 guid_match = standard_guid_pattern.match(entry[1]) 64 if guid_match: 65 groups = guid_match.groups() 66 crc_value = groups[2] + groups[1] 67 vid_value = groups[4] + groups[3] 68 pid_value = groups[7] + groups[6] 69 version_value = groups[10] + groups[9] 70 #print("CRC: %s, VID: %s, PID: %s, VERSION: %s" % (crc_value, vid_value, pid_value, version_value)) 71 72 if crc_value == "0000": 73 if crc != "": 74 crc_value = crc[4:-1] 75 else: 76 print("Extracting CRC from GUID of " + name) 77 entry[1] = groups[0] + "0000" + "".join(groups[3:]) 78 crc = "crc:" + crc_value + "," 79 80 if (vid_value, pid_value, crc_value) in invalid_controllers: 81 print("Controller '%s' not unique, skipping" % name) 82 return 83 84 pos = find_element("type", bindings) 85 if pos >= 0: 86 bindings.insert(0, bindings.pop(pos)) 87 88 pos = find_element("platform", bindings) 89 if pos >= 0: 90 bindings.insert(0, bindings.pop(pos)) 91 92 pos = find_element("sdk", bindings) 93 if pos >= 0: 94 bindings.append(bindings.pop(pos)) 95 96 pos = find_element("hint:", bindings) 97 if pos >= 0: 98 bindings.append(bindings.pop(pos)) 99 100 entry.extend(crc) 101 entry.extend(",".join(bindings) + ",") 102 entry.append(match.group(5)) 103 controllers.append(entry) 104 105 entry_id = entry[1] + get_crc_from_entry(entry) 106 if ',sdk' in line or ',hint:' in line: 107 conditionals.append(entry_id) 108 109def write_controllers(): 110 global controllers 111 global controller_guids 112 # Check for duplicates 113 for entry in controllers: 114 entry_id = entry[1] + get_crc_from_entry(entry) 115 if (entry_id in controller_guids and entry_id not in conditionals): 116 current_name = entry[2] 117 existing_name = controller_guids[entry_id][2] 118 print("Warning: entry '%s' is duplicate of entry '%s'" % (current_name, existing_name)) 119 120 if (not current_name.startswith("(DUPE)")): 121 entry[2] = f"(DUPE) {current_name}" 122 123 if (not existing_name.startswith("(DUPE)")): 124 controller_guids[entry_id][2] = f"(DUPE) {existing_name}" 125 126 controller_guids[entry_id] = entry 127 128 for entry in sorted(controllers, key=lambda entry: f"{entry[2]}-{entry[1]}"): 129 line = "".join(entry) + "\n" 130 line = line.replace("\t", " ") 131 if not line.endswith(",\n") and not line.endswith("*/\n") and not line.endswith(",\r\n") and not line.endswith("*/\r\n"): 132 print("Warning: '%s' is missing a comma at the end of the line" % (line)) 133 output.write(line) 134 135 controllers = [] 136 controller_guids = {} 137 138for line in input: 139 if parsing_controllers: 140 if (line.startswith("{")): 141 output.write(line) 142 elif (line.startswith(" NULL")): 143 parsing_controllers = False 144 write_controllers() 145 output.write(line) 146 elif (line.startswith("#if")): 147 print(f"Parsing {line.strip()}") 148 output.write(line) 149 elif ("SDL_PRIVATE_GAMEPAD_DEFINITIONS" in line): 150 write_controllers() 151 output.write(line) 152 elif (line.startswith("#endif")): 153 write_controllers() 154 output.write(line) 155 else: 156 save_controller(line) 157 else: 158 if (line.startswith("static const char *")): 159 parsing_controllers = True 160 161 output.write(line) 162 163output.close() 164print(f"Finished writing {filename}.new") 165
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.