2016-07-06 04:17:47 +05:30
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2017-10-07 20:56:20 -04:00
|
|
|
#import fileinput
|
2016-07-06 04:17:47 +05:30
|
|
|
import string
|
|
|
|
import sys
|
2017-10-07 16:34:07 -04:00
|
|
|
from collections import defaultdict
|
|
|
|
import collections
|
2017-10-07 20:12:35 -04:00
|
|
|
import shutil
|
2017-10-07 20:56:20 -04:00
|
|
|
import argparse
|
2016-07-06 04:17:47 +05:30
|
|
|
|
|
|
|
success = True
|
2017-10-07 19:04:18 -04:00
|
|
|
current_line = ""
|
2017-10-07 20:56:20 -04:00
|
|
|
current_lineno = 0
|
2017-10-07 19:04:18 -04:00
|
|
|
entry_dict = defaultdict(tuple)
|
|
|
|
dupe_dict = defaultdict(list)
|
|
|
|
|
|
|
|
def get_current_line():
|
|
|
|
return current_line
|
2016-07-06 04:17:47 +05:30
|
|
|
|
2017-10-07 20:56:20 -04:00
|
|
|
def get_current_lineno():
|
|
|
|
return current_lineno
|
|
|
|
|
2016-07-06 04:17:47 +05:30
|
|
|
def error (message):
|
2017-10-07 19:04:18 -04:00
|
|
|
global success
|
2016-07-06 04:17:47 +05:30
|
|
|
success = False
|
2017-10-07 20:56:20 -04:00
|
|
|
print("Error at line #" + str(get_current_lineno()), ":", message)
|
2017-10-07 19:04:18 -04:00
|
|
|
print(get_current_line())
|
2016-07-06 04:17:47 +05:30
|
|
|
|
|
|
|
def check_guid (guid):
|
|
|
|
if len (guid) != 32:
|
|
|
|
error ("The length of the guid string must be equal to 32")
|
|
|
|
for c in guid:
|
|
|
|
if not c in string.hexdigits:
|
2017-10-07 13:29:01 -04:00
|
|
|
error ("Each character in guid string must be a hex character "
|
|
|
|
+ string.hexdigits)
|
2016-07-06 04:17:47 +05:30
|
|
|
|
|
|
|
def check_mapping (mappingstring):
|
2017-10-07 13:29:01 -04:00
|
|
|
keys = ["platform", "leftx", "lefty", "rightx", "righty", "a", "b", \
|
|
|
|
"back", "dpdown", \
|
2016-07-06 04:17:47 +05:30
|
|
|
"dpleft", "dpright", "dpup", "guide", "leftshoulder", "leftstick", \
|
2017-10-07 13:29:01 -04:00
|
|
|
"lefttrigger", "rightshoulder", "rightstick", "righttrigger", \
|
|
|
|
"start", "x", "y"]
|
2016-07-06 04:17:47 +05:30
|
|
|
platforms = ["Linux", "Mac OS X", "Windows"]
|
|
|
|
mappings = mappingstring.split (',')
|
|
|
|
for mapping in mappings:
|
|
|
|
if not mapping:
|
|
|
|
continue
|
|
|
|
if len (mapping.split(':')) != 2:
|
|
|
|
error ("Invalid mapping : " + mapping)
|
|
|
|
continue
|
|
|
|
key = mapping.split (':')[0]
|
|
|
|
value = mapping.split (':')[1]
|
|
|
|
if not key in keys:
|
|
|
|
error ("Invalid key \"" + key + "\" in mapping string")
|
|
|
|
|
|
|
|
# Check values
|
|
|
|
if key == "platform":
|
|
|
|
if value not in platforms:
|
|
|
|
error ("Invalid platform \"" + value + "\" in mapping string")
|
|
|
|
else:
|
|
|
|
if not value:
|
|
|
|
continue
|
2017-10-14 13:09:29 -04:00
|
|
|
if value[0] in ['-', '+', '~']:
|
|
|
|
if not value[1] == 'a':
|
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
|
|
|
"\". Inversion and range modifiers only valid for " +
|
|
|
|
"axis (a).")
|
|
|
|
if not value[2:].isnumeric():
|
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
|
|
|
"\". Should be followed by a number after 'a'")
|
|
|
|
elif not value[0] in ['a', 'h', 'b']:
|
2016-07-06 04:17:47 +05:30
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
|
|
|
"\". Should start with a, b, or h")
|
|
|
|
elif value[0] in ['a', 'b']:
|
|
|
|
if not value[1:].isnumeric():
|
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
2017-10-07 13:29:01 -04:00
|
|
|
"\". Should be followed by a number after 'a' or " +
|
|
|
|
"'b'")
|
2016-07-06 04:17:47 +05:30
|
|
|
else:
|
|
|
|
dpad_positions = map(str, [0, 1, 2, 4, 8, 1|2, 2|4, 4|8, 8|1])
|
|
|
|
dpad_index = value[1:].split ('.')[0]
|
|
|
|
dpad_position = value[1:].split ('.')[1]
|
|
|
|
if not dpad_index.isnumeric():
|
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
2017-10-07 13:29:01 -04:00
|
|
|
"\". Dpad index \"" + dpad_index + "\" should be " +
|
|
|
|
"a number")
|
2016-07-06 04:17:47 +05:30
|
|
|
if not dpad_position in dpad_positions:
|
|
|
|
error ("Invalid value \"" + value + "\" for key \"" + key +
|
2017-10-07 13:29:01 -04:00
|
|
|
"\". Dpad position \"" + dpad_position + "\" " +
|
|
|
|
"should be one of" + ', '.join(dpad_positions))
|
2016-07-06 04:17:47 +05:30
|
|
|
|
2017-10-07 13:29:58 -04:00
|
|
|
def get_platform (mappingstring):
|
|
|
|
mappings = mappingstring.split (',')
|
|
|
|
for mapping in mappings:
|
|
|
|
if not mapping:
|
|
|
|
continue
|
2017-10-14 13:14:05 -04:00
|
|
|
if len (mapping.split(':')) != 2:
|
|
|
|
continue
|
2017-10-07 13:29:58 -04:00
|
|
|
key = mapping.split(':')[0]
|
|
|
|
value = mapping.split(':')[1]
|
|
|
|
if key == "platform":
|
|
|
|
return value
|
|
|
|
error ("No platform specified " + mapping)
|
|
|
|
|
2017-10-07 16:34:07 -04:00
|
|
|
def has_duplicate(guids):
|
|
|
|
seen = set()
|
|
|
|
seen_add = seen.add
|
|
|
|
seen_twice = set( x for x in guids if x in seen or seen_add(x) )
|
|
|
|
return len(seen_twice) != 0
|
|
|
|
|
2017-10-07 19:04:18 -04:00
|
|
|
def check_duplicates(guid, platform):
|
2017-10-07 16:34:07 -04:00
|
|
|
guids = list(dupe_dict[platform])
|
|
|
|
guids.append(guid)
|
|
|
|
if has_duplicate(guids):
|
2017-10-07 19:04:18 -04:00
|
|
|
error("\nDuplicate entry :")
|
|
|
|
print("Original at line #" + entry_dict[guid][0] +
|
|
|
|
":\n" + entry_dict[guid][1])
|
2017-10-07 16:34:07 -04:00
|
|
|
else:
|
|
|
|
dupe_dict[platform].append(guid)
|
2017-10-07 20:56:20 -04:00
|
|
|
entry_dict[guid] = (str(get_current_lineno()), get_current_line())
|
2017-10-07 16:34:07 -04:00
|
|
|
|
2017-10-14 13:14:05 -04:00
|
|
|
def is_duplicate(guid, platform):
|
|
|
|
guids = list(dupe_dict[platform])
|
|
|
|
guids.append(guid)
|
|
|
|
if has_duplicate(guids):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
dupe_dict[platform].append(guid)
|
|
|
|
return False
|
|
|
|
|
2017-10-07 20:56:20 -04:00
|
|
|
def do_tests(filename):
|
2017-10-07 19:04:18 -04:00
|
|
|
global current_line
|
2017-10-07 20:56:20 -04:00
|
|
|
global current_lineno
|
|
|
|
input_file = open(filename, 'r')
|
|
|
|
for lineno, line in enumerate(input_file):
|
2017-10-07 19:04:18 -04:00
|
|
|
current_line = line
|
2017-10-07 20:56:20 -04:00
|
|
|
current_lineno = lineno + 1
|
2017-10-07 19:04:18 -04:00
|
|
|
if line.startswith('#') or line == '\n':
|
|
|
|
continue
|
|
|
|
splitted = line[:-1].split(',', 2)
|
|
|
|
if len(splitted) < 3 or not splitted[0] or not splitted[1] \
|
|
|
|
or not splitted[2]:
|
|
|
|
error ("Either GUID/Name/Mappingstring is missing or empty")
|
|
|
|
check_guid(splitted[0])
|
|
|
|
check_mapping(splitted[2])
|
2017-10-07 23:43:37 -04:00
|
|
|
check_duplicates(splitted[0].lower(), get_platform(splitted[2]))
|
2017-10-07 20:56:20 -04:00
|
|
|
input_file.close()
|
2017-10-07 19:04:18 -04:00
|
|
|
|
2017-10-07 20:56:20 -04:00
|
|
|
def sort_by_name(filename):
|
2017-10-14 13:14:05 -04:00
|
|
|
global current_line
|
|
|
|
global current_lineno
|
2017-10-07 20:56:20 -04:00
|
|
|
input_file = open(filename, 'r')
|
2017-10-07 20:12:35 -04:00
|
|
|
sorted_dict = dict({"Windows": list(tuple()), "Mac OS X": list(tuple()), \
|
2017-10-14 13:14:05 -04:00
|
|
|
"Linux": list(tuple())})
|
2017-10-07 20:12:35 -04:00
|
|
|
|
2017-10-14 13:14:05 -04:00
|
|
|
for lineno, line in enumerate(input_file):
|
|
|
|
current_line = line
|
|
|
|
current_lineno = lineno + 1
|
2017-10-07 20:12:35 -04:00
|
|
|
if line.startswith('#') or line == '\n':
|
|
|
|
continue
|
|
|
|
splitted = line[:-1].split(',', 2)
|
|
|
|
if len(splitted) < 3 or not splitted[0] or not splitted[1] \
|
|
|
|
or not splitted[2]:
|
|
|
|
continue
|
|
|
|
platform = get_platform(splitted[2])
|
|
|
|
sorted_dict[platform].append((splitted[1], line))
|
|
|
|
|
|
|
|
out_file = open("gamecontrollerdb_sorted.txt", 'w')
|
|
|
|
for platform, name_tuples in sorted_dict.items():
|
|
|
|
if platform != "Windows":
|
|
|
|
out_file.write("\n")
|
|
|
|
out_file.write("# " + platform + "\n")
|
|
|
|
for tuples in sorted(name_tuples, key=lambda tup: tup[0].lower()):
|
|
|
|
out_file.write(tuples[1])
|
|
|
|
out_file.close()
|
2017-10-07 20:56:20 -04:00
|
|
|
input_file.close()
|
|
|
|
shutil.copyfile(input_file.name, ".bak." + input_file.name)
|
2017-10-07 20:12:35 -04:00
|
|
|
shutil.move("gamecontrollerdb_sorted.txt", "gamecontrollerdb.txt")
|
|
|
|
|
2017-10-07 23:43:37 -04:00
|
|
|
# https://hg.libsdl.org/SDL/rev/20855a38e048
|
|
|
|
def convert_guids(filename):
|
2017-10-14 13:14:05 -04:00
|
|
|
global current_line
|
|
|
|
global current_lineno
|
2017-10-07 23:43:37 -04:00
|
|
|
input_file = open(filename, 'r')
|
|
|
|
out_file = open("gamecontrollerdb_converted.txt", 'w')
|
|
|
|
for lineno, line in enumerate(input_file):
|
2017-10-14 13:14:05 -04:00
|
|
|
current_line = line
|
|
|
|
current_lineno = lineno + 1
|
2017-10-07 23:43:37 -04:00
|
|
|
if line.startswith('#') or line == '\n':
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
splitted = line[:-1].split(',', 2)
|
|
|
|
guid = splitted[0]
|
|
|
|
if get_platform(splitted[2]) == "Windows":
|
|
|
|
if guid[20:32] != "504944564944":
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
guid = guid[:20] + "000000000000"
|
|
|
|
guid = guid[:16] + guid[4:8] + guid[20:]
|
|
|
|
guid = guid[:8] + guid[:4] + guid[12:]
|
|
|
|
guid = "03000000" + guid[8:]
|
|
|
|
guid = guid.lower()
|
|
|
|
elif get_platform(splitted[2]) == "Mac OS X":
|
|
|
|
if guid[4:16] != "000000000000" or guid[20:32] != "000000000000":
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
guid = guid[:20] + "000000000000"
|
|
|
|
guid = guid[:8] + guid[:4] + guid[12:]
|
|
|
|
guid = "03000000" + guid[8:]
|
|
|
|
guid = guid.lower()
|
|
|
|
|
|
|
|
out = line.replace(splitted[0], guid)
|
|
|
|
out_file.write(out)
|
|
|
|
out_file.close()
|
|
|
|
input_file.close()
|
|
|
|
shutil.copyfile(input_file.name, ".bak." + input_file.name)
|
|
|
|
shutil.move("gamecontrollerdb_converted.txt", "gamecontrollerdb.txt")
|
|
|
|
|
2017-10-14 13:14:05 -04:00
|
|
|
def remove_dupes(filename):
|
|
|
|
global current_line
|
|
|
|
global current_lineno
|
|
|
|
global dupe_dict
|
|
|
|
dupe_dict = defaultdict(list)
|
|
|
|
input_file = open(filename, 'r')
|
|
|
|
out_file = open("gamecontrollerdb_dupes_removed.txt", 'w')
|
|
|
|
for lineno, line in enumerate(input_file):
|
|
|
|
current_line = line
|
|
|
|
current_lineno = lineno + 1
|
|
|
|
if line.startswith('#') or line == '\n':
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
splitted = line[:-1].split(',', 2)
|
|
|
|
guid = splitted[0].lower()
|
|
|
|
if is_duplicate(guid, get_platform(splitted[2])):
|
|
|
|
print("Duplicate detected, removing :\n" + line)
|
|
|
|
continue
|
|
|
|
out_file.write(line)
|
|
|
|
out_file.close()
|
|
|
|
input_file.close()
|
|
|
|
shutil.copyfile(input_file.name, ".bak." + input_file.name)
|
|
|
|
shutil.move("gamecontrollerdb_dupes_removed.txt", "gamecontrollerdb.txt")
|
|
|
|
|
|
|
|
def add_missing_platforms(filename):
|
|
|
|
global current_line
|
|
|
|
global current_lineno
|
|
|
|
input_file = open(filename, 'r')
|
|
|
|
out_file = open("gamecontrollerdb_platforms.txt", 'w')
|
|
|
|
for lineno, line in enumerate(input_file):
|
|
|
|
current_line = line
|
|
|
|
current_lineno = lineno + 1
|
|
|
|
if line.startswith('#') or line == '\n':
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
splitted = line[:-1].split(',', 2)
|
|
|
|
guid = splitted[0]
|
|
|
|
platform = get_platform(splitted[2])
|
|
|
|
if platform:
|
|
|
|
out_file.write(line)
|
|
|
|
continue
|
|
|
|
|
|
|
|
out = line[0:-1]
|
|
|
|
if guid[20:32] == "504944564944":
|
|
|
|
print("Adding Windows platform to #" + str(lineno) + " :\n" + line)
|
|
|
|
out += "platform:Windows,"
|
|
|
|
elif guid[4:16] == "000000000000" and guid[20:32] == "000000000000":
|
|
|
|
print("Adding Mac OS X platform to #" + str(lineno) + " :\n" + line)
|
|
|
|
out += "platform:Mac OS X,"
|
|
|
|
else:
|
|
|
|
print("Adding Linux platform to #" + str(lineno) + " :\n" + line)
|
|
|
|
out += "platform:Linux,"
|
|
|
|
out += "\n"
|
|
|
|
out_file.write(out)
|
|
|
|
out_file.close()
|
|
|
|
input_file.close()
|
|
|
|
shutil.copyfile(input_file.name, ".bak." + input_file.name)
|
|
|
|
shutil.move("gamecontrollerdb_platforms.txt", "gamecontrollerdb.txt")
|
|
|
|
|
2017-10-07 19:04:18 -04:00
|
|
|
def main():
|
2017-10-07 20:56:20 -04:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("input_file", help="database file to check \
|
|
|
|
(gamecontrollerdb.txt)")
|
2017-10-14 13:14:05 -04:00
|
|
|
parser.add_argument("--sort", help="sort the database on success",
|
2017-10-07 20:56:20 -04:00
|
|
|
action="store_true")
|
2017-10-07 23:43:37 -04:00
|
|
|
parser.add_argument("--guid_convert", help="convert Windows and macOS \
|
|
|
|
GUIDs to the newer SDL 2.0.5 format", action="store_true")
|
2017-10-14 13:14:05 -04:00
|
|
|
parser.add_argument("--remove_dupes", help="automatically remove \
|
|
|
|
duplicates", action="store_true")
|
|
|
|
parser.add_argument("--add_missing_platform", help="adds a platform \
|
|
|
|
field if it is missing (on older pre 2.0.5 entries). Skips checks!",
|
|
|
|
action="store_true")
|
2017-10-07 20:56:20 -04:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
2017-10-14 13:14:05 -04:00
|
|
|
if args.add_missing_platform:
|
|
|
|
print("Adding missing platforms.")
|
|
|
|
add_missing_platforms(args.input_file)
|
|
|
|
return
|
|
|
|
|
|
|
|
print("Applying checks.")
|
2017-10-07 20:56:20 -04:00
|
|
|
do_tests(args.input_file)
|
2017-10-14 13:14:05 -04:00
|
|
|
|
|
|
|
if args.remove_dupes:
|
|
|
|
print("Removing duplicates.")
|
|
|
|
remove_dupes(args.input_file)
|
|
|
|
|
2017-10-07 19:04:18 -04:00
|
|
|
if success:
|
2017-10-07 20:56:20 -04:00
|
|
|
print("No mapping errors found.")
|
2017-10-14 13:14:05 -04:00
|
|
|
if args.sort:
|
2017-10-07 20:56:20 -04:00
|
|
|
print("Sorting by human readable name.")
|
|
|
|
sort_by_name(args.input_file)
|
2017-10-07 23:43:37 -04:00
|
|
|
if args.guid_convert:
|
|
|
|
print("Converting GUIDs to SDL 2.0.5 format.")
|
|
|
|
convert_guids(args.input_file)
|
2017-10-14 13:14:05 -04:00
|
|
|
if args.remove_dupes:
|
|
|
|
print("Removing duplicates again.")
|
|
|
|
remove_dupes(args.input_file)
|
2017-10-07 19:04:18 -04:00
|
|
|
else:
|
|
|
|
sys.exit(1)
|
2017-10-07 13:29:58 -04:00
|
|
|
|
2017-10-07 19:04:18 -04:00
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|