#!/usr/bin/python # Character map generator import sys import re import inspect autogen_header = """ /* * Autogenerated character map file for Saitek X52 Pro * Generated from %s */ #include "x52_char_map.h" """ class MapTable(): """ Defines a MapTable entry, with each entry storing the value seen so far, the type of the entry, and the value, if it's a value node. """ # Empty list root = [None] * 256 def __init__(self, value_so_far, map_value = None): self.next_level = [None] * 256 self.value_so_far = value_so_far self.map_value = map_value def output_nodes(self): output_lines = [] output_count = 0 for node in self.next_level: if node is not None: output_lines.extend(node.output_nodes()) output_count += 1 if output_count != 0: struct_header = 'static struct map_entry table_%x[64] = {' % self.value_so_far output_lines.append(struct_header) for node_index in range(0,256): node = self.next_level[node_index] if node is not None: output_lines.append(self.dump_entry_line(0x80, node_index, node.value_so_far, node.map_value)) output_lines.extend(['};', '']) return output_lines @staticmethod def dump_entry_line(offset, node_index, value_so_far, map_value): if map_value is None: node_entry_line = '\t[0x%02x] = { table_%x, TYPE_POINTER, 0 },' % \ (node_index - offset, value_so_far) else: node_entry_line = '\t[0x%02x] = { NULL, TYPE_ENTRY, 0x%02x },' % \ (node_index - offset, map_value) return node_entry_line @classmethod def add_to_table(cls, input_val, map_val): utf8_str = unichr(input_val).encode('utf-8') utf8_vals = [ord(c) for c in utf8_str] value_so_far = 0 level = cls.root for i in range(len(utf8_vals)): c = utf8_vals[i] value_so_far = (value_so_far << 8) | c if i < (len(utf8_vals) - 1): node = level[c] if node is None: node = cls(value_so_far) level[c] = node level = level[c].next_level else: node = cls(value_so_far, map_val) level[c] = node @classmethod def output_table_as_list(cls): output_lines = [] for node in cls.root: if node is not None: output_lines.extend(node.output_nodes()) output_lines.append('struct map_entry map_root[256] = {') for node_index in range(0,256): node = cls.root[node_index] if node is not None: output_lines.append(cls.dump_entry_line(0x0, node_index, node.value_so_far, node.map_value)) output_lines.extend(['};', '']) return output_lines class LineFormatError(ValueError): pass def parse_line(line): """ Parse a line containing a mapping descriptor. The mapping descriptor must start with a hexadecimal unicode code point, followed by either a single character, or a hexadecimal integer that corresponds to the map value. """ # Strip off comments line = re.sub(re.compile('#.*$'), '', line) # Strip off leading and trailing whitespace line = line.strip() # If the line is empty, it is a comment line if len(line) == 0: return None, None # Find the code point and the target value try: code_point, target = line.strip().split() except ValueError: # Raised when there are either too many, or not enough values in # the string raise LineFormatError('Invalid descriptor format "%s"' % line) # Convert the string to its equivalent numeric value try: code_point = int(code_point, 0) except ValueError: raise LineFormatError('Invalid code point "%s"' % code_point) # Check if the target is a single character if len(target) == 1: target = ord(target) else: # Try parsing the target as an integer try: target = int(target, 0) except ValueError: raise LineFormatError('Invalid map value "%s"' % target) return code_point, target if __name__ == "__main__": if len(sys.argv) != 3: sys.stderr.write('Usage: %s \n' % sys.argv[0]) sys.exit(1) with open(sys.argv[1], 'r') as infile: for line in infile: src, dst = parse_line(line) if src is not None: MapTable.add_to_table(src,dst) with open(sys.argv[2], 'w') as outfile: outfile.write(autogen_header % sys.argv[1]) for line in MapTable.output_table_as_list(): outfile.write(line + '\n')