]> asedeno.scripts.mit.edu Git - unkcm.git/blob - unkcm
realign comment with output string
[unkcm.git] / unkcm
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # Copyright (c) 2010 Alejandro R. Sedeño <asedeno@mit.edu>
5
6 # Permission is hereby granted, free of charge, to any person
7 # obtaining a copy of this software and associated documentation files
8 # (the "Software"), to deal in the Software without restriction,
9 # including without limitation the rights to use, copy, modify, merge,
10 # publish, distribute, sublicense, and/or sell copies of the Software,
11 # and to permit persons to whom the Software is furnished to do so,
12 # subject to the following conditions:
13
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
16
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 # SOFTWARE.
25
26 from __future__ import with_statement
27 import sys
28 import struct
29 import StringIO
30
31 # All specified sources are from Google's Android SDK.
32 # For more information, see:
33 #  http://developer.android.com/sdk/index.html
34
35 # kcm.bin file format
36 # Source:
37 #  build/tools/kcm/kcm.cpp
38
39 # ***** HEADER *****
40 # Offset    | Description
41 # 0x00-0x07 | The ascii value "keychar" including the null character
42 # 0x08-0x0b | Endian marker (0x12345678)
43 # 0x0c-0x0f | version (2)
44 # 0x10-0x13 | key count (n)
45 # 0x14-0x17 | keyboard type (NUMERIC [1], Q14 [2], QWERTY [3], etc.)
46 # 0x18-0x1f | Padding
47 # 0x20-.... | entries (16 bytes × key count)
48
49 # ***** ENTRIES *****
50 # Offset    | Description
51 # 0x00-0x03 | KeyEvent name
52 # 0x04-0x05 | Display Label
53 # 0x06-0x07 | Number
54 # 0x08-0x09 | Base
55 # 0x0a-0x0b | Shift
56 # 0x0c-0x0d | Alt
57 # 0x0e-0x0f | Shift+Alt
58
59 # Keycode Labels
60 # source:
61 #  frameworks/base/include/ui/KeycodeLabels.h
62 keycodes={"SOFT_LEFT": 1, "SOFT_RIGHT": 2, "HOME": 3, "BACK": 4,
63           "CALL": 5, "ENDCALL": 6, "0": 7, "1": 8, "2": 9, "3": 10,
64           "4": 11, "5": 12, "6": 13, "7": 14, "8": 15, "9": 16,
65           "STAR": 17, "POUND": 18, "DPAD_UP": 19, "DPAD_DOWN": 20,
66           "DPAD_LEFT": 21, "DPAD_RIGHT": 22, "DPAD_CENTER": 23,
67           "VOLUME_UP": 24, "VOLUME_DOWN": 25, "POWER": 26,
68           "CAMERA": 27, "CLEAR": 28, "A": 29, "B": 30, "C": 31,
69           "D": 32, "E": 33, "F": 34, "G": 35, "H": 36, "I": 37,
70           "J": 38, "K": 39, "L": 40, "M": 41, "N": 42, "O": 43,
71           "P": 44, "Q": 45, "R": 46, "S": 47, "T": 48, "U": 49,
72           "V": 50, "W": 51, "X": 52, "Y": 53, "Z": 54, "COMMA": 55,
73           "PERIOD": 56, "ALT_LEFT": 57, "ALT_RIGHT": 58,
74           "SHIFT_LEFT": 59, "SHIFT_RIGHT": 60, "TAB": 61, "SPACE": 62,
75           "SYM": 63, "EXPLORER": 64, "ENVELOPE": 65, "ENTER": 66,
76           "DEL": 67, "GRAVE": 68, "MINUS": 69, "EQUALS": 70,
77           "LEFT_BRACKET": 71, "RIGHT_BRACKET": 72, "BACKSLASH": 73,
78           "SEMICOLON": 74, "APOSTROPHE": 75, "SLASH": 76, "AT": 77,
79           "NUM": 78, "HEADSETHOOK": 79, "FOCUS": 80, "PLUS": 81,
80           "MENU": 82, "NOTIFICATION": 83, "SEARCH": 84,
81           "MEDIA_PLAY_PAUSE": 85, "MEDIA_STOP": 86, "MEDIA_NEXT": 87,
82           "MEDIA_PREVIOUS": 88, "MEDIA_REWIND": 89,
83           "MEDIA_FAST_FORWARD": 90, "MUTE": 91, "PAGE_UP": 92,
84           "PAGE_DOWN": 93, "PICTSYMBOLS": 94, "SWITCH_CHARSET": 95, }
85
86 rkeycodes={}
87 for (k,v) in keycodes.items(): rkeycodes[v] = k
88
89 keyboardTypes = {1: 'NUMERIC', 2: 'Q14', 3: 'QWERTY',}
90 def getKBType(n):
91     if n in keyboardTypes:
92         return keyboardTypes[n]
93     raise ValueError
94
95 MAGIC = 'keychar\x00'
96
97 def checkHeader(buf):
98     """
99     Takes the first 32 bytes of the file, confirms it is a valid keymap header
100     Returns endian marker for struct, entry count, and keyboard type.
101     """
102     (magic, marker) = struct.unpack('8s4s20x', buf)
103     if magic != MAGIC:
104         raise ValueError
105     endian = None
106     if marker == '\x12\x34\x56\x78': # big
107         endian = '>'
108     elif marker == '\x78\x56\x34\x12': # little
109         endian = '<'
110     else:
111         raise ValueError
112
113     (version, count, kbtype) = struct.unpack(endian+'12x3i8x', buf)
114     if version != 2:
115         raise ValueError
116     kbtype = getKBType(kbtype)
117     return (endian, count, kbtype)
118
119 class KR:
120     def __init__(self, values):
121         self.values = values
122         (self.keycode,
123          self.display,
124          self.number,
125          self.base,
126          self.shift,
127          self.alt,
128          self.shiftalt) = values
129
130     def __repr__(self):
131         return "KR: KeyCode %i; Base: %i" % (self.keycode, self.base)
132
133     def _fmt(self, val):
134         if 0x20 < val <= 0x7e:
135             return "'%s'" % chr(val)
136         else:
137             return '0x%X' % val
138
139     def printKR(self):
140         fmt = (rkeycodes[self.keycode],)
141         fmt += tuple([self._fmt(x) for x in self.values[1:]])
142         return ("%-15s%-10s%-10s%-10s%-10s%-10s%-10s" % fmt).strip()
143
144 def parseKR(buf, endian):
145     """
146     Parses a 16-byte key row entry
147     """
148     return KR(struct.unpack(endian+'I6H', buf))
149
150 def decompileKCM(In, Out):
151     header = In.read(32)
152     endian, count, ktype = checkHeader(header)
153     Out.write("[type=%s]\n" % ktype)
154     #          0         1         2         3         4         5         6         7
155     #          01234567890123456789012345678901234567890123456789012345678901234567890123
156     Out.write("# keycode      Display   Number    Base      Shift     Alt       Shift+Alt\n")
157     for i in range(count):
158         kr = parseKR(In.read(16), endian)
159         Out.write(kr.printKR()+"\n")
160
161 if __name__ == '__main__':
162     if len(sys.argv) != 3:
163         print """usage: kcm [INPUT] [OUTPUT]
164
165         INPUT   compiled keycharmap file, - for STDIN
166         OUTPUT  keycharmap file, - for STDOUT
167         """
168         exit(-1)
169     In, Out = sys.argv[1:3]
170
171     output = StringIO.StringIO()
172     if In == '-':
173         decompileKCM(sys.stdin, output)
174     else:
175         with open(sys.argv[1],'rb') as f:
176             decompileKCM(f, output)
177
178     if Out == '-':
179         sys.stdout.write(output.getvalue())
180     else:
181         with open(sys.argv[2],'w') as f:
182             f.write(output.getvalue())