#!/usr/bin/env python3
import os
import sys

if len(sys.argv) != 2:
    print(f'Usage: {sys.argv[0]} [path to wine source tree]', file=sys.stderr)
    sys.exit(1)

# String table. Starts with one string, an empty string.
string_table = bytearray(b'\x00')

# String dictionary. Maps strings to offsets within the string table.
string_dict = {"": 0}

modules = {}

for ent in sorted(os.scandir(os.path.join(sys.argv[1], 'dlls')), key=lambda x: x.name):
    if not ent.is_dir():
        continue
    if ent.name[-6:] not in ['.exe16', '.dll16', '.drv16']:
        continue

    module_name = ent.name[:-6].upper()
    if module_name == 'KRNL386':
        module_name = 'KERNEL'
    if len(module_name) > 8:
        print(f'WARNING: module name {module_name} is longer than 8 characters', file=sys.stderr)

    spec_name = os.path.join(ent.path, ent.name+'.spec')
    if not os.path.exists(spec_name):
        print(f'{spec_name} doesn\'t exist', file=sys.stderr)
        continue

    print(f'processing {module_name} @ {ent.name}...', file=sys.stderr)
    ordinals = {}
    if module_name in modules:
        print('duplicate module name', file=sys.stderr)
        continue
    modules[module_name] = ordinals

    with open(spec_name) as f:
        for line in f.read().splitlines():
            line = line.split('#')[0].split()
            if len(line) == 0:
                continue
            if len(line) < 3:
                print(f'couldn\'t find symbol name in:\n\t{line}', file=sys.stderr)
                continue

            if line[0] == '@':
                continue
            try:
                ordinal = int(line[0])
            except ValueError:
                print(f'ordinal is not @ or integer in:\n\t{line}', file=sys.stderr)
                continue

            if line[1] not in ['cdecl', 'equate', 'pascal', 'stub', 'varargs', 'variable']:
                print(f'unknown functype type {line[1]} in:\n\t{line}', file=sys.stderr)
                continue

            for tok in line[2:]:
                if not tok.startswith('-'):
                    break
            else:
                print(f'couldn\'t find symbol name in:\n\t{line}', file=sys.stderr)
                continue
            tok = tok.split('(')[0]

            if ordinal in ordinals:
                print(f'ordinal {ordinal} already points to {ordinals[ordinal]} instead of {tok} in:\n\t{line}', file=sys.stderr)
                continue

            # look up the name in the string table
            name_idx = 0
            if tok in string_dict:
                name_idx = string_dict[tok]
            else:
                name_idx = len(string_table)
                string_table.extend(tok.encode('UTF-8'))
                string_table.append(0)
                string_dict[tok] = name_idx

            ordinals[ordinal] = name_idx

print("// generated by EXENEEntries_data.py from wine sources\n")

# print the string table
print("#include <stdint.h>\n")
print("static const char EXENEEntries_strtbl[] =")

# Print up to 64 characters per line, including NULL bytes.
# Control codes and non-ASCII characters will be escaped.
# NOTE: Control characters may cause it to be slightly more
# than 64 characters per line, depending on where they show up.
string_table_len = len(string_table)
i = 0
print("\t\"", end='')
last_was_hex = False
for c in string_table:
	if i >= 64:
		print("\"\n\t\"", end='')
		last_was_hex = False
		i = 0

	if c < 32 or c >= 128:
		if i != 0 and not last_was_hex:
			print("\" \"", end='')
			i += 3
		last_was_hex = True
		print("\\x{0:0{1}x}".format(c, 2), end='')
		i += 4
	else:
		if last_was_hex:
			print("\" \"", end='')
			i += 3
		last_was_hex = False
		print(chr(c), end='')
		i += 1
print("\";\n")

# module ordinals
for module in sorted(modules.items(), key=lambda kv: kv[0]):
    print(f'static const OrdinalName {module[0]}_entries[] = {{')
    for kv in sorted(module[1].items(), key=lambda kv: kv[0]):
        print(f'\t{{ {kv[0]}, {kv[1]} }},')
    print("};\n")

# array of module name mappings
print('static const OrdinalNameTable entries[] = {')
for module in sorted(modules.items(), key=lambda kv: kv[0]):
    namearr = "{'" + "','".join(module[0]) + "'}"
    print(f'\t{{ {namearr}, {module[0]}_entries, ARRAY_SIZE({module[0]}_entries) }},')
print('};')
