#!/usr/bin/env python
# -*- coding: UTF-8 -*-

table = [
    0x80, 0x84, 0x80, 0x84, 0x80, 0x84, 0x80, 0x84,
    0x80, 0x88, 0x80, 0x88, 0x80, 0x88, 0x80, 0x88,
    0x8c, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b,
    0x90, 0x94, 0x98, 0x8b, 0x9c, 0x9c, 0x9c, 0x9c,
    0xa0, 0x80, 0x80, 0x80, 0x8b, 0x8b, 0xa4, 0x8b,
    0xa8, 0x8b, 0x84, 0x8b, 0xac, 0xac, 0xa8, 0xa8,
    0xb0, 0xb4, 0xb8, 0xbc, 0x80, 0xc0, 0x80, 0x80,
    0x9c, 0xac, 0xc4, 0x8b, 0xc8, 0x90, 0x8b, 0x90,
    0x80, 0x8b, 0x8b, 0xcc, 0x80, 0x80, 0xd0, 0x8b,
    0x80, 0xd4, 0x80, 0x80, 0x8b, 0x8b, 0x8b, 0x8b,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0xd8, 0xdc, 0x8b, 0x80,
    0xe0, 0xe0, 0xe0, 0xe0, 0x80, 0x80, 0x80, 0x80,
    0x8f, 0xcf, 0x8f, 0xdb, 0x80, 0x80, 0xe4, 0x80,
    0xe8, 0xd9, 0x8b, 0x8b, 0x80, 0x80, 0x80, 0x80,
    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xdc,
    0x08, 0x08, 0x08, 0x08, 0x01, 0x10, 0x00, 0x00,
    0x01, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x08, 0x08, 0x20, 0x20, 0x20, 0x20,
    0x10, 0x18, 0x01, 0x09, 0x81, 0x81, 0x81, 0x81,
    0x09, 0x18, 0x09, 0x09, 0x00, 0x00, 0x12, 0x00,
    0x10, 0x10, 0x10, 0x10, 0x01, 0x01, 0x01, 0x01,
    0x09, 0x09, 0x02, 0x00, 0x08, 0x08, 0x09, 0x18,
    0x03, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x01, 0x01, 0x00, 0x00, 0x50, 0x50, 0x12, 0x81,
    0x20, 0x00, 0x20, 0x20, 0x00, 0x08, 0x00, 0x09,
    0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
    0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x00,
    0x50, 0x50, 0x50, 0x50, 0x00, 0x00, 0x09, 0x08,
    0x08, 0x08, 0x09, 0x08,
]

def print_unpacked_table(t):
    feature_names = ['imm8', 'imm16', 'imm32', 'modrm', '66-67', 'is_prefix', 'rel32', 'rel8']
    for opcode, flags in enumerate(t):
        print('Opcode {}{:02x}: '.format('0f ' if opcode >= 0x100 else '', opcode & 0xff), end='')
        features = [name for i, name in enumerate(feature_names) if flags & (1 << i)]
        print(', '.join(features) if len(features) else '<none>')

full_table = []

for opcode in range(0x200):
    index = table[opcode >> 2] + (opcode & 3)
    flags = table[index]
    full_table.append(flags)

print_unpacked_table(full_table)

print('Repacking table...')

indices = []
flagdata = bytearray()

for index, group in enumerate(zip(*([iter(full_table)] * 4))):
    group = bytes(group)
    pos = flagdata.find(group)
    if pos == -1:
        pos = len(flagdata)
        flagdata.extend(group)
    indices.append(pos)

# Fix indices and concat flags.
result = bytes(map(lambda x: x + len(indices), indices)) + bytes(flagdata)

print('Built the same table? {}'.format(bytes(table) == result))
