Created
March 7, 2021 01:59
-
-
Save yearofthewhopper/a0cdb2f8521f1e1c9e05a4798ebbbe1a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
from __future__ import with_statement | |
import sys | |
import struct | |
import StringIO | |
# kcm.bin file format | |
# Source: | |
# build/tools/kcm/kcm.cpp | |
# ***** HEADER ***** | |
# Offset | Description | |
# 0x00-0x07 | The ascii value "keychar" including the null character | |
# 0x08-0x0b | Endian marker (0x12345678) | |
# 0x0c-0x0f | version (2) | |
# 0x10-0x13 | key count (n) | |
# 0x14-0x17 | keyboard type (NUMERIC [1], Q14 [2], QWERTY [3], etc.) | |
# 0x18-0x1f | Padding | |
# 0x20-.... | entries (16 bytes × key count) | |
# ***** ENTRIES ***** | |
# Offset | Description | |
# 0x00-0x03 | KeyEvent name | |
# 0x04-0x05 | Display Label | |
# 0x06-0x07 | Number | |
# 0x08-0x09 | Base | |
# 0x0a-0x0b | Shift | |
# 0x0c-0x0d | Alt | |
# 0x0e-0x0f | Shift+Alt | |
# Keycode Labels | |
# source: | |
# frameworks/base/include/ui/KeycodeLabels.h | |
keycodes={"SOFT_LEFT": 1, "SOFT_RIGHT": 2, "HOME": 3, "BACK": 4, | |
"CALL": 5, "ENDCALL": 6, "0": 7, "1": 8, "2": 9, "3": 10, | |
"4": 11, "5": 12, "6": 13, "7": 14, "8": 15, "9": 16, | |
"STAR": 17, "POUND": 18, "DPAD_UP": 19, "DPAD_DOWN": 20, | |
"DPAD_LEFT": 21, "DPAD_RIGHT": 22, "DPAD_CENTER": 23, | |
"VOLUME_UP": 24, "VOLUME_DOWN": 25, "POWER": 26, | |
"CAMERA": 27, "CLEAR": 28, "A": 29, "B": 30, "C": 31, | |
"D": 32, "E": 33, "F": 34, "G": 35, "H": 36, "I": 37, | |
"J": 38, "K": 39, "L": 40, "M": 41, "N": 42, "O": 43, | |
"P": 44, "Q": 45, "R": 46, "S": 47, "T": 48, "U": 49, | |
"V": 50, "W": 51, "X": 52, "Y": 53, "Z": 54, "COMMA": 55, | |
"PERIOD": 56, "ALT_LEFT": 57, "ALT_RIGHT": 58, | |
"SHIFT_LEFT": 59, "SHIFT_RIGHT": 60, "TAB": 61, "SPACE": 62, | |
"SYM": 63, "EXPLORER": 64, "ENVELOPE": 65, "ENTER": 66, | |
"DEL": 67, "GRAVE": 68, "MINUS": 69, "EQUALS": 70, | |
"LEFT_BRACKET": 71, "RIGHT_BRACKET": 72, "BACKSLASH": 73, | |
"SEMICOLON": 74, "APOSTROPHE": 75, "SLASH": 76, "AT": 77, | |
"NUM": 78, "HEADSETHOOK": 79, "FOCUS": 80, "PLUS": 81, | |
"MENU": 82, "NOTIFICATION": 83, "SEARCH": 84, | |
"MEDIA_PLAY_PAUSE": 85, "MEDIA_STOP": 86, "MEDIA_NEXT": 87, | |
"MEDIA_PREVIOUS": 88, "MEDIA_REWIND": 89, | |
"MEDIA_FAST_FORWARD": 90, "MUTE": 91, "PAGE_UP": 92, | |
"PAGE_DOWN": 93, "PICTSYMBOLS": 94, "SWITCH_CHARSET": 95, } | |
rkeycodes={} | |
for (k,v) in keycodes.items(): rkeycodes[v] = k | |
keyboardTypes = {1: 'NUMERIC', 2: 'Q14', 3: 'QWERTY',} | |
def getKBType(n): | |
if n in keyboardTypes: | |
return keyboardTypes[n] | |
raise ValueError | |
MAGIC = 'keychar\x00' | |
def checkHeader(buf): | |
""" | |
Takes the first 32 bytes of the file, confirms it is a valid keymap header | |
Returns endian marker for struct, entry count, and keyboard type. | |
""" | |
(magic, marker) = struct.unpack('8s4s20x', buf) | |
if magic != MAGIC: | |
raise ValueError | |
endian = None | |
if marker == '\x12\x34\x56\x78': # big | |
endian = '>' | |
elif marker == '\x78\x56\x34\x12': # little | |
endian = '<' | |
else: | |
raise ValueError | |
(version, count, kbtype) = struct.unpack(endian+'12x3i8x', buf) | |
if version != 2: | |
raise ValueError | |
kbtype = getKBType(kbtype) | |
return (endian, count, kbtype) | |
class KR: | |
def __init__(self, values): | |
self.values = values | |
(self.keycode, | |
self.display, | |
self.number, | |
self.base, | |
self.shift, | |
self.alt, | |
self.shiftalt) = values | |
def __repr__(self): | |
return "KR: KeyCode %i; Base: %i" % (self.keycode, self.base) | |
def _fmt(self, val): | |
if 0x20 < val <= 0x7e: | |
return "'%s'" % chr(val) | |
else: | |
return '0x%X' % val | |
def printKR(self): | |
fmt = (rkeycodes[self.keycode],) | |
fmt += tuple([self._fmt(x) for x in self.values[1:]]) | |
return ("%-15s%-10s%-10s%-10s%-10s%-10s%-10s" % fmt).strip() | |
def parseKR(buf, endian): | |
""" | |
Parses a 16-byte key row entry | |
""" | |
return KR(struct.unpack(endian+'I6H', buf)) | |
def decompileKCM(In, Out): | |
header = In.read(32) | |
endian, count, ktype = checkHeader(header) | |
Out.write("[type=%s]\n" % ktype) | |
# 0 1 2 3 4 5 6 7 | |
# 01234567890123456789012345678901234567890123456789012345678901234567890123 | |
Out.write("# keycode Display Number Base Shift Alt Shift+Alt\n") | |
for i in range(count): | |
kr = parseKR(In.read(16), endian) | |
Out.write(kr.printKR()+"\n") | |
if __name__ == '__main__': | |
if len(sys.argv) != 3: | |
print """usage: kcm [INPUT] [OUTPUT] | |
INPUT compiled keycharmap file, - for STDIN | |
OUTPUT keycharmap file, - for STDOUT | |
""" | |
exit(-1) | |
In, Out = sys.argv[1:3] | |
output = StringIO.StringIO() | |
if In == '-': | |
decompileKCM(sys.stdin, output) | |
else: | |
with open(sys.argv[1],'rb') as f: | |
decompileKCM(f, output) | |
if Out == '-': | |
sys.stdout.write(output.getvalue()) | |
else: | |
with open(sys.argv[2],'w') as f: | |
f.write(output.getvalue()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment