Skip to content

Instantly share code, notes, and snippets.

@yearofthewhopper
Created March 7, 2021 01:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yearofthewhopper/a0cdb2f8521f1e1c9e05a4798ebbbe1a to your computer and use it in GitHub Desktop.
Save yearofthewhopper/a0cdb2f8521f1e1c9e05a4798ebbbe1a to your computer and use it in GitHub Desktop.
#!/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