#!/usr/bin/env python3
&#34;&#34;&#34;Decode a HID report descriptor hex blob (short items only).

Input: hex string on stdin or argv[1]; whitespace / &#39;&lt;&#39; &#39;&gt;&#39; / &#39;0x&#39; / commas tolerated,
so you can paste the raw ioreg value like &lt;05010906a1...&gt; directly.
Output: (a) comma-separated 0xNN list for https://eleccelerator.com/usbdescreqparser/
        (b) an indented item-level decode (Usage Page / Usage / Collection / Report ID / Input...).
&#34;&#34;&#34;
import re
import sys

MAIN = {0x8: &#34;Input&#34;, 0x9: &#34;Output&#34;, 0xA: &#34;Collection&#34;, 0xB: &#34;Feature&#34;, 0xC: &#34;End Collection&#34;}
GLOBAL = {0x0: &#34;Usage Page&#34;, 0x1: &#34;Logical Min&#34;, 0x2: &#34;Logical Max&#34;, 0x3: &#34;Physical Min&#34;,
          0x4: &#34;Physical Max&#34;, 0x5: &#34;Unit Exponent&#34;, 0x6: &#34;Unit&#34;, 0x7: &#34;Report Size&#34;,
          0x8: &#34;Report ID&#34;, 0x9: &#34;Report Count&#34;, 0xA: &#34;Push&#34;, 0xB: &#34;Pop&#34;}
LOCAL = {0x0: &#34;Usage&#34;, 0x1: &#34;Usage Min&#34;, 0x2: &#34;Usage Max&#34;, 0x3: &#34;Designator Index&#34;,
         0x4: &#34;Designator Min&#34;, 0x5: &#34;Designator Max&#34;, 0x7: &#34;String Index&#34;,
         0x8: &#34;String Min&#34;, 0x9: &#34;String Max&#34;, 0xA: &#34;Delimiter&#34;}
PAGES = {0x01: &#34;Generic Desktop&#34;, 0x07: &#34;Keyboard/Keypad&#34;, 0x08: &#34;LED&#34;, 0x09: &#34;Button&#34;,
         0x0C: &#34;Consumer&#34;, 0x0D: &#34;Digitizer&#34;, 0xFF00: &#34;Vendor(FF00)&#34;}
GD_USAGE = {0x01: &#34;Pointer&#34;, 0x02: &#34;Mouse&#34;, 0x06: &#34;Keyboard&#34;, 0x30: &#34;X&#34;, 0x31: &#34;Y&#34;, 0x38: &#34;Wheel&#34;}
COLLECTION = {0x00: &#34;Physical&#34;, 0x01: &#34;Application&#34;, 0x02: &#34;Logical&#34;}

raw = sys.argv[1] if len(sys.argv) &gt; 1 else sys.stdin.read()
hexstr = re.sub(r&#34;(?i)0x|[^0-9a-f]&#34;, &#34;&#34;, raw)
data = bytes.fromhex(hexstr)

print(&#34;eleccelerator paste format:&#34;)
print(&#34;, &#34;.join(f&#34;0x{b:02X}&#34; for b in data))
print(&#34;\ndecode:&#34;)
i, depth, page = 0, 0, 0
report_ids = set()
while i &lt; len(data):
    prefix = data[i]
    size = [0, 1, 2, 4][prefix &amp; 0x3]
    typ = (prefix &gt;&gt; 2) &amp; 0x3
    tag = (prefix &gt;&gt; 4) &amp; 0xF
    val = int.from_bytes(data[i + 1:i + 1 + size], &#34;little&#34;)
    name = (MAIN if typ == 0 else GLOBAL if typ == 1 else LOCAL if typ == 2 else {}).get(tag, f&#34;tag{tag:#x}/type{typ}&#34;)
    note = &#34;&#34;
    if name == &#34;Report ID&#34;:
        report_ids.add(val)
    if name == &#34;Usage Page&#34;:
        page = val
        note = f&#34;  ; {PAGES.get(val, hex(val))}&#34;
    elif name == &#34;Usage&#34; and page == 0x01:
        note = f&#34;  ; {GD_USAGE.get(val, hex(val))}&#34;
    elif name == &#34;Collection&#34;:
        note = f&#34;  ; {COLLECTION.get(val, hex(val))}&#34;
    elif name == &#34;Input&#34;:
        bits = []
        bits.append(&#34;Const&#34; if val &amp; 1 else &#34;Data&#34;)
        bits.append(&#34;Var&#34; if val &amp; 2 else &#34;Array&#34;)
        bits.append(&#34;Rel&#34; if val &amp; 4 else &#34;Abs&#34;)
        note = &#34;  ; &#34; + &#34;,&#34;.join(bits)
    if name == &#34;End Collection&#34;:
        depth -= 1
    print(f&#34;{&#39;  &#39; * max(depth, 0)}{name} ({val:#x}){note}&#34;)
    if name == &#34;Collection&#34;:
        depth += 1
    i += 1 + size
print(f&#34;\nreport IDs seen: {sorted(report_ids) or &#39;NONE (single unnumbered report)&#39;}&#34;)
