#!/usr/bin/env python

"""This module is mostly a playground for descriptor-parsing code.

Copyright (c) 2005 Charles Lepple

You may distribute this file under the terms of either the GNU General Public
License or the Artistic License."""

__author__ = "Charles Lepple <clepple+libhid@ghz.cc>"
__date__   = "20 February 2005"

import os, sys, re

def extract_bytes_libhid(f):
   """Extract an array of bytes from a hex dump generated by libhid.
   The returned list contains unsigned integers corresponding to the
   bytes."""
   descriptor_seen = 0
   descriptor_text = []

   for line in f:
       if descriptor_seen:
           if 'parsing the HID tree' in line:
               descriptor_seen = 0
           else:
               # Chop off "TRACE: hid_prepare_parser(): 0xZZZ":
	       descriptor_text += line.split()[3:]
       else:
           if 'raw report descriptor' in line:
               descriptor_seen = 1

   # Map each string element into an integer:
   return [int(var, 16) for var in descriptor_text]

def extract_bytes(f):
   """Extract an array of descriptor bytes from an assembly file, or just a
   list of hex bytes. ('ret' statements, colons, comments and labels are
   removed.) The returned list contains unsigned integers corresponding to the
   bytes."""
   descriptor_text = []

   re_ret = re.compile(r'ret[a-z]+', re.IGNORECASE)
   # Labels begin in the first column, and optionally have a colon afterwards:
   re_label = re.compile(r'^[a-z0-9_]+:?', re.IGNORECASE)
   re_comment = re.compile(r';.*$')

   for line in f:
       line = re_comment.sub('', line)
       line = re_label.sub('', line)
       line = re_ret.sub('', line)
       # print "Line: " + line
       descriptor_text += line.split()

   # Map each string element into an integer:
   return [int(var, 16) for var in descriptor_text]
# string.join([ "%02x" % var for var in descriptor_bytes])

def parse_tag(desc, index = 0):
    """Examine and print information about the item at desc[index].

    Returns the length of the item (including the tag), which can be used to
    advance index to the next item in the descriptor.

    Currently, only short tags are handled.

    bSize:
    0 = 0 bytes
    1 = 1 byte
    2 = 2 bytes
    3 = 4 bytes"""

    bSize = (1 << (desc[index] & 0x3)) / 2
    bType = (desc[index] >> 2) & 0x3
    bTag  = (desc[index] >> 4) & 0xF

    print "[0x%04x]" % index,

    data = 0
    for byte in range(bSize+1):
        print "0x%02x" % desc[index+byte],
	if byte > 0:
	    data = desc[index+byte] << (8*(byte-1)) | data

    # FIXME: sign-extend data if necessary

    if bSize > 0:
        print "(value: 0x%x / %d)" % (data, data),
    print

    print "  bSize = %d byte(s)" % bSize
    print "  bType = 0x%02x (%s)" \
        % (bType, ('Main', 'Global', 'Local', 'Reserved')[bType])
    print "  bTag  = 0x%02x" % bTag,

    if bType == 0:	# Main
    	print "(%s)" % ('Input', 'Output', 'Collection', 'Feature', \
	    'End Collection')[bTag-8],

	if bTag == 0x0a: # Collection type
	    if data <= 6:
	        print "(%s)" % ('Physical', 'Application', 'Logical', 'Report', \
	            'Named Array', 'Usage Switch', 'Usage Modifier')[data],
            else:
	        print "(Vendor defined: 0x%x)" % data,
    elif bType == 1:	# Global
	print "(%s)" % ('Usage page', 'Logical Minimum', 'Logical Maximum', \
	    'Physical Minimum', 'Physical Maximum', 'Unit Exponent', 'Unit', \
	    'Report Size', 'Report ID', 'Report Count', 'Push', 'Pop')[bTag],
    elif bType == 2:	# Local
        print "(%s)" % ('Usage', 'Usage Minimum', 'Usage Maximum', \
	    'Designator Index', 'Designator Minimum', 'Designator Maximum', \
	    'String Index', 'String Minimum', 'String Maximum', \
	    'Delimiter')[bTag],
    print
    print
    return bSize + 1

def tokenize_descriptor(desc):
    byte_index = 0;
    while byte_index < len(desc):
        byte_index += parse_tag(desc, byte_index)

def main():
    if(len(sys.argv) > 1):
        fname = sys.argv[1]
    else:
        fname = '../ref/test_libhid_output/MGE_Pulsar_Evolution_500'

    f = open(fname, 'r')
    # desc_bytes = extract_bytes(f)
    # - or -
    desc_bytes = extract_bytes_libhid(f)
    f.close()

    tokenize_descriptor(desc_bytes)

    print "done"

if __name__ == '__main__':
    main()


syntax highlighted by Code2HTML, v. 0.9.1