mirror of https://git.wuffs.org/MWCC
395 lines
15 KiB
Python
395 lines
15 KiB
Python
|
import json
|
||
|
import sys
|
||
|
|
||
|
def decode_avail(f):
|
||
|
flags = []
|
||
|
if (f & 0x1) != 0: flags.append('Global')
|
||
|
if (f & 0x2) != 0: flags.append('Sticky')
|
||
|
if (f & 0x4) != 0: flags.append('Cased')
|
||
|
if (f & 0x8) != 0: flags.append('Obsolete')
|
||
|
if (f & 0x10) != 0: flags.append('Substituted')
|
||
|
if (f & 0x20) != 0: flags.append('Deprecated')
|
||
|
if (f & 0x40) != 0: flags.append('Link')
|
||
|
if (f & 0x80) != 0: flags.append('Disasm')
|
||
|
if (f & 0x100) != 0: flags.append('Comp')
|
||
|
if (f & 0x200) != 0: flags.append('OF200')
|
||
|
if (f & 0x400) != 0: flags.append('OF400')
|
||
|
if (f & 0x800) != 0: flags.append('Ignored')
|
||
|
if (f & 0x1000) != 0: flags.append('Secret')
|
||
|
if (f & 0x2000) != 0: flags.append('HideDefault')
|
||
|
if (f & 0x4000) != 0: flags.append('Compat')
|
||
|
if (f & 0x8000) != 0: flags.append('Sub')
|
||
|
if (f & 0x10000) != 0: flags.append('SubsOptional')
|
||
|
if (f & 0x20000) != 0: flags.append('OnlyOnce')
|
||
|
if (f & 0x40000) != 0: flags.append('Conflicts')
|
||
|
if (f & 0x80000) != 0: flags.append('Warning')
|
||
|
if (f & 0x100000) != 0: flags.append('CanBeNegated')
|
||
|
if (f & 0x200000) != 0: flags.append('O_SLFlags10')
|
||
|
if (f & 0x400000) != 0: flags.append('O_SLFlags20')
|
||
|
if (f & 0x800000) != 0: flags.append('Meaningless')
|
||
|
if (f & 0x1000000) != 0: flags.append('OF1000000')
|
||
|
if (f & 0x2000000) != 0: flags.append('OF2000000')
|
||
|
if (f & 0x4000000) != 0: flags.append('OF4000000')
|
||
|
if (f & 0x8000000) != 0: flags.append('OF8000000')
|
||
|
if (f & 0x10000000) != 0: flags.append('OF10000000')
|
||
|
if (f & 0x20000000) != 0: flags.append('OF20000000')
|
||
|
return '|'.join(flags)
|
||
|
def decode_paramflags(f):
|
||
|
flags = []
|
||
|
if (f & 0x1) != 0: flags.append('PF01')
|
||
|
if (f & 0x2) != 0: flags.append('PF02')
|
||
|
if (f & 0x4) != 0: flags.append('PF04')
|
||
|
if (f & 0x8) != 0: flags.append('PF08')
|
||
|
if (f & 0x10) != 0: flags.append('PF10')
|
||
|
if (f & 0x20) != 0: flags.append('PF20')
|
||
|
if (f & 0x40) != 0: flags.append('PF40')
|
||
|
if (f & 0x80) != 0: flags.append('PF80')
|
||
|
return '|'.join(flags)
|
||
|
def decode_listflags(f):
|
||
|
flags = []
|
||
|
if (f & 0x1) != 0: flags.append('L_Exclusive')
|
||
|
if (f & 0x2) != 0: flags.append('L_AllowUnknowns')
|
||
|
if (f & 0x4) != 0: flags.append('LF4')
|
||
|
if (f & 0x8) != 0: flags.append('LF8')
|
||
|
if (f & 0x10) != 0: flags.append('LF10')
|
||
|
if (f & 0x20) != 0: flags.append('LF20')
|
||
|
if (f & 0x40) != 0: flags.append('LF40')
|
||
|
if (f & 0x80) != 0: flags.append('LF80')
|
||
|
if (f & 0x100) != 0: flags.append('L_Comp')
|
||
|
if (f & 0x200) != 0: flags.append('L_Link')
|
||
|
if (f & 0x400) != 0: flags.append('L_Disasm')
|
||
|
if (f & 0x800) != 0: flags.append('LF800')
|
||
|
if (f & 0x1000) != 0: flags.append('LF1000')
|
||
|
if (f & 0x2000) != 0: flags.append('LF2000')
|
||
|
if (f & 0x4000) != 0: flags.append('LF4000')
|
||
|
if (f & 0x8000) != 0: flags.append('LF8000')
|
||
|
if (f & 0x10000) != 0: flags.append('LF10000')
|
||
|
if (f & 0x20000) != 0: flags.append('LF20000')
|
||
|
if (f & 0x40000) != 0: flags.append('LF40000')
|
||
|
if (f & 0x80000) != 0: flags.append('LF80000')
|
||
|
if (f & 0x100000) != 0: flags.append('LF100000')
|
||
|
if (f & 0x200000) != 0: flags.append('LF200000')
|
||
|
if (f & 0x400000) != 0: flags.append('LF400000')
|
||
|
if (f & 0x800000) != 0: flags.append('LF800000')
|
||
|
if (f & 0x1000000) != 0: flags.append('LF1000000')
|
||
|
if (f & 0x2000000) != 0: flags.append('LF2000000')
|
||
|
if (f & 0x4000000) != 0: flags.append('LF4000000')
|
||
|
if (f & 0x8000000) != 0: flags.append('LF8000000')
|
||
|
if (f & 0x10000000) != 0: flags.append('LF10000000')
|
||
|
if (f & 0x20000000) != 0: flags.append('LF20000000')
|
||
|
if (f & 0x40000000) != 0: flags.append('LF40000000')
|
||
|
if (f & 0x80000000) != 0: flags.append('LF80000000')
|
||
|
return '|'.join(flags)
|
||
|
|
||
|
navstack = []
|
||
|
indent = ''
|
||
|
def push_indent():
|
||
|
global indent
|
||
|
indent += ' '
|
||
|
def pop_indent():
|
||
|
global indent
|
||
|
indent = indent[:-2]
|
||
|
|
||
|
scan_objs = {}
|
||
|
def register_scan_obj(name, what):
|
||
|
ckname = name.replace('_list', '')
|
||
|
if ckname[-1].isdigit():
|
||
|
last_non_digit = 0
|
||
|
for i,c in enumerate(ckname):
|
||
|
if not c.isdigit():
|
||
|
last_non_digit = i
|
||
|
idx = int(ckname[last_non_digit+1:])
|
||
|
else:
|
||
|
return
|
||
|
|
||
|
stk = ' / '.join(navstack)
|
||
|
if idx in scan_objs:
|
||
|
lst = scan_objs[idx]
|
||
|
for o in lst:
|
||
|
if o[1] == name:
|
||
|
return
|
||
|
lst.append((stk, name, what))
|
||
|
else:
|
||
|
scan_objs[idx] = [(stk, name, what)]
|
||
|
|
||
|
def dump_param(p):
|
||
|
suff = ''
|
||
|
if 'myname' in p:
|
||
|
suff += ' +myname'
|
||
|
if 'help' in p:
|
||
|
suff += ' +help'
|
||
|
register_scan_obj(p['_name'], f"PF:{p['flags']:02X} T:{p['_type']}{suff}")
|
||
|
print(f"{indent}-{p['_name']:37} | {decode_paramflags(p['flags'])} ", end='')
|
||
|
if 'myname' in p:
|
||
|
print(f"<{p['myname']}> ", end='')
|
||
|
if p['_type'] == 'FTypeCreator':
|
||
|
print(f"FTypeCreator(fc={p['fc']} iscreator={p['iscreator']})")
|
||
|
elif p['_type'] == 'FilePath':
|
||
|
print(f"FilePath(flg={p['fflags']:02X} def={p['defaultstr']} fn={p['filename']} max={p['maxlen']})")
|
||
|
elif p['_type'] == 'Number':
|
||
|
print(f"Number(sz={p['size']} fit={p['fit']} lo={p['lo']:08X} hi={p['hi']:08X} num={p['num']})")
|
||
|
elif p['_type'] in ('String','Id','Sym'):
|
||
|
print(f"{p['_type']}(max={p['maxlen']} ps={p['pstring']} s={p['str']})")
|
||
|
elif p['_type'] == 'OnOff':
|
||
|
print(f"OnOff(var={p['var']})")
|
||
|
elif p['_type'] == 'OffOn':
|
||
|
print(f"OffOn(var={p['var']})")
|
||
|
elif p['_type'] == 'Mask':
|
||
|
print(f"Mask(sz={p['size']} or={p['ormask']:08X} and={p['andmask']:08X} num={p['num']})")
|
||
|
elif p['_type'] == 'Toggle':
|
||
|
print(f"Toggle(sz={p['size']} mask={p['mask']:08X} num={p['num']})")
|
||
|
elif p['_type'] == 'Set':
|
||
|
print(f"Set(sz={p['size']} v={p['value']} num={p['num']})")
|
||
|
elif p['_type'] == 'SetString':
|
||
|
print(f"SetString(v={p['value']} ps={p['pstring']} var={p['var']})")
|
||
|
elif p['_type'] == 'Generic':
|
||
|
v = p['var']
|
||
|
if isinstance(v, str):
|
||
|
v = v.replace('\n','').replace('\r','')
|
||
|
print(f"Generic(p={p['parse']} var={v})")
|
||
|
elif p['_type'] == 'IfArg':
|
||
|
print('IfArg')
|
||
|
push_indent()
|
||
|
print(f'{indent}TRUE:')
|
||
|
push_indent()
|
||
|
for i,s in enumerate(p['parg']):
|
||
|
navstack.append(f'ifArgParam{i}')
|
||
|
dump_param(s)
|
||
|
navstack.pop(-1)
|
||
|
pop_indent()
|
||
|
print(f'{indent}FALSE:')
|
||
|
push_indent()
|
||
|
for i,s in enumerate(p['pnone']):
|
||
|
navstack.append(f'ifArgNoneParam{i}')
|
||
|
dump_param(s)
|
||
|
navstack.pop(-1)
|
||
|
pop_indent()
|
||
|
pop_indent()
|
||
|
elif p['_type'] == 'Setting':
|
||
|
print(f"Setting(p={p['parse']} vn={p['valuename']})")
|
||
|
else:
|
||
|
print()
|
||
|
|
||
|
def dump_opt(o):
|
||
|
navstack.append(o['names'])
|
||
|
trunchelp = o['help']
|
||
|
if trunchelp:
|
||
|
if len(trunchelp) > 35:
|
||
|
trunchelp = trunchelp[:20] + '...' + trunchelp[-5:]
|
||
|
trunchelp = trunchelp.replace('\r', ' ')
|
||
|
trunchelp = trunchelp.replace('\n', ' ')
|
||
|
avail = decode_avail(o['avail'])
|
||
|
register_scan_obj(o['_name'], 'OPTION ' + avail)
|
||
|
print(f"{indent}{o['_name']:40} | {avail} {o['names']:40} {trunchelp}")
|
||
|
if 'conflicts' in o:
|
||
|
conflicts = o['conflicts']
|
||
|
print(f"{indent}CONFLICTS: {conflicts['_name']} / {conflicts['_list_name']} / {decode_listflags(conflicts['flags'])}")
|
||
|
print(f"{indent} {conflicts['list']}")
|
||
|
register_scan_obj(conflicts['_name'], f"CONFLICTS LIST")
|
||
|
register_scan_obj(conflicts['_list_name'], f"CONFLICTS LIST NAME")
|
||
|
push_indent()
|
||
|
for i,p in enumerate(o['param']):
|
||
|
navstack.append(f'param{i}')
|
||
|
dump_param(p)
|
||
|
navstack.pop(-1)
|
||
|
pop_indent()
|
||
|
if 'sub' in o:
|
||
|
sub = o['sub']
|
||
|
print(f"{indent}SUB {{ {sub['_name']} / {sub['_list_name']} / {decode_listflags(sub['flags'])}")
|
||
|
register_scan_obj(sub['_name'], f"SUB LIST")
|
||
|
register_scan_obj(sub['_list_name'], f"SUB LIST NAME")
|
||
|
push_indent()
|
||
|
for s in sub['list']:
|
||
|
dump_opt(s)
|
||
|
pop_indent()
|
||
|
print(f"{indent}}}")
|
||
|
navstack.pop(-1)
|
||
|
|
||
|
def dump_optlst(ol):
|
||
|
register_scan_obj(ol['_name'], 'ROOT OBJECT')
|
||
|
register_scan_obj(ol['_list_name'], 'ROOT OBJECT LIST')
|
||
|
print(f"{indent}# FLAGS: {decode_listflags(ol['flags'])} - HELP: {repr(ol['help'])}")
|
||
|
for option in ol['list']:
|
||
|
dump_opt(option)
|
||
|
|
||
|
with open(sys.argv[1], 'r') as f:
|
||
|
optlsts = json.load(f)
|
||
|
|
||
|
for optlst in optlsts:
|
||
|
print(f"{indent}# --- {optlst['_name']} ---")
|
||
|
push_indent()
|
||
|
dump_optlst(optlst)
|
||
|
pop_indent()
|
||
|
print()
|
||
|
|
||
|
lastseen = 0
|
||
|
deltas = {}
|
||
|
for i in range(1220):
|
||
|
if i in scan_objs:
|
||
|
delta = i - lastseen
|
||
|
for stk,name,what in scan_objs[i]:
|
||
|
print(f'{i:04d} {stk:60} | {name:30} | {what}')
|
||
|
deltas[name] = delta
|
||
|
lastseen = i
|
||
|
else:
|
||
|
print(f'{i:04d} -----')
|
||
|
|
||
|
def nice_param(p):
|
||
|
# param names don't matter
|
||
|
np = {'_type': p['_type'], 'flags': p['flags']}
|
||
|
if 'myname' in p and p['myname']:
|
||
|
np['myname'] = p['myname']
|
||
|
|
||
|
delta = deltas[p['_name']]
|
||
|
if p['_name'] == '_optlstCmdLine099':
|
||
|
delta -= 1 # hack to account for the weird 'progress' one
|
||
|
if delta > 1:
|
||
|
np['_idskip'] = delta - 1
|
||
|
|
||
|
if p['_type'] == 'FTypeCreator':
|
||
|
np['target'] = p['fc']
|
||
|
np['is_creator'] = p['iscreator']
|
||
|
elif p['_type'] == 'FilePath':
|
||
|
np['fflags'] = p['fflags']
|
||
|
np['default'] = p['defaultstr']
|
||
|
np['target'] = p['filename']
|
||
|
np['max_length'] = p['maxlen']
|
||
|
elif p['_type'] == 'Number':
|
||
|
np['target'] = p['num']
|
||
|
np['minimum'] = p['lo']
|
||
|
np['maximum'] = p['hi']
|
||
|
np['byte_size'] = p['size']
|
||
|
np['fit'] = p['fit']
|
||
|
elif p['_type'] in ('String','Id','Sym'):
|
||
|
np['max_length'] = p['maxlen']
|
||
|
np['pascal'] = (p['pstring'] != 0)
|
||
|
np['target'] = p['str']
|
||
|
elif p['_type'] == 'OnOff':
|
||
|
np['target'] = p['var']
|
||
|
elif p['_type'] == 'OffOn':
|
||
|
np['target'] = p['var']
|
||
|
elif p['_type'] == 'Mask':
|
||
|
np['target'] = p['num']
|
||
|
np['byte_size'] = p['size']
|
||
|
np['or_mask'] = p['ormask']
|
||
|
np['and_mask'] = p['andmask']
|
||
|
elif p['_type'] == 'Toggle':
|
||
|
np['target'] = p['num']
|
||
|
np['byte_size'] = p['size']
|
||
|
np['mask'] = p['mask']
|
||
|
elif p['_type'] == 'Set':
|
||
|
np['target'] = p['num']
|
||
|
np['value'] = p['value']
|
||
|
np['byte_size'] = p['size']
|
||
|
elif p['_type'] == 'SetString':
|
||
|
np['target'] = p['var']
|
||
|
np['value'] = p['value']
|
||
|
np['pascal'] = (p['pstring'] != 0)
|
||
|
elif p['_type'] == 'Generic':
|
||
|
np['function'] = p['parse']
|
||
|
np['arg'] = p['var']
|
||
|
np['help'] = p['help']
|
||
|
elif p['_type'] == 'IfArg':
|
||
|
np['help_a'] = p['helpa']
|
||
|
np['help_n'] = p['helpn']
|
||
|
np['if_arg'] = [nice_param(sp) for sp in p['parg']]
|
||
|
np['if_no_arg'] = [nice_param(sp) for sp in p['pnone']]
|
||
|
elif p['_type'] == 'Setting':
|
||
|
np['function'] = p['parse']
|
||
|
np['value_name'] = p['valuename']
|
||
|
return np
|
||
|
|
||
|
def nice_option(o, exclusive_parent=False):
|
||
|
no = {'names': o['names'], 'tools': [], 'params': [nice_param(p) for p in o['param']]}
|
||
|
if 'help' in o:
|
||
|
no['help'] = o['help']
|
||
|
if o['_name'] == '_optlstCmdLine_progress':
|
||
|
no['custom_name'] = 'progress' # lol hack
|
||
|
if (o['avail'] & 0x1) != 0: no['global'] = True
|
||
|
if (o['avail'] & 0x2) != 0: no['sticky'] = True
|
||
|
if (o['avail'] & 0x4) != 0: no['cased'] = True
|
||
|
if (o['avail'] & 0x8) != 0: no['obsolete'] = True
|
||
|
if (o['avail'] & 0x10) != 0: no['substituted'] = True
|
||
|
if (o['avail'] & 0x20) != 0: no['deprecated'] = True
|
||
|
if (o['avail'] & 0x100) != 0: no['tools'].append('compiler')
|
||
|
if (o['avail'] & 0x40) != 0: no['tools'].append('linker')
|
||
|
if (o['avail'] & 0x80) != 0: no['tools'].append('disassembler')
|
||
|
if (o['avail'] & 0x200) != 0: raise ValueError('fucked')
|
||
|
if (o['avail'] & 0x400) != 0: raise ValueError('fucked')
|
||
|
if (o['avail'] & 0x800) != 0: no['ignored'] = True
|
||
|
if (o['avail'] & 0x1000) != 0: no['secret'] = True
|
||
|
if (o['avail'] & 0x2000) != 0: no['hide_default'] = True
|
||
|
if (o['avail'] & 0x4000) != 0: no['compatibility'] = True
|
||
|
if (o['avail'] & 0x20000) != 0: no['only_once'] = True
|
||
|
if (o['avail'] & 0x80000) != 0: no['warning'] = True
|
||
|
if (o['avail'] & 0x100000) != 0: no['can_be_negated'] = True
|
||
|
if (o['avail'] & 0x200000) != 0: no['can_be_negated_2'] = True
|
||
|
if (o['avail'] & 0x400000) != 0: no['can_be_negated_3'] = True
|
||
|
if (o['avail'] & 0x800000) != 0: no['meaningless'] = True
|
||
|
|
||
|
if (o['avail'] & 0x8000) != 0:
|
||
|
subflags = o['sub']['flags']
|
||
|
obj_comp = (o['avail'] & 0x100) != 0
|
||
|
obj_link = (o['avail'] & 0x40) != 0
|
||
|
obj_disasm = (o['avail'] & 0x80) != 0
|
||
|
sublist_comp = (subflags & 0x100) != 0
|
||
|
sublist_link = (subflags & 0x200) != 0
|
||
|
sublist_disasm = (subflags & 0x400) != 0
|
||
|
assert(obj_comp == sublist_comp)
|
||
|
assert(obj_link == sublist_link)
|
||
|
assert(obj_disasm == sublist_disasm)
|
||
|
|
||
|
excl = False
|
||
|
if (o['avail'] & 0x10000) != 0:
|
||
|
no['sub_options_optional'] = True
|
||
|
if (subflags & 2) != 0:
|
||
|
no['sub_options_allow_unknowns'] = True
|
||
|
if (subflags & 1) != 0:
|
||
|
no['sub_options_exclusive'] = True
|
||
|
excl = True
|
||
|
|
||
|
assert('help' not in o['sub'])
|
||
|
no['sub_options'] = [nice_option(so, excl) for so in o['sub']['list']]
|
||
|
else:
|
||
|
assert('sub' not in o)
|
||
|
|
||
|
if exclusive_parent:
|
||
|
# conflicts flag should be set
|
||
|
assert((o['avail'] & 0x40000) == 0x40000)
|
||
|
assert('help' not in o['conflicts'])
|
||
|
else:
|
||
|
if (o['avail'] & 0x40000) == 0x40000:
|
||
|
no['conflict_group'] = o['conflicts']['_name'][1:].replace('_conflicts', '')
|
||
|
|
||
|
return no
|
||
|
|
||
|
if len(sys.argv) > 2:
|
||
|
nice_optlsts_map = {}
|
||
|
for optlst in optlsts:
|
||
|
#print('---' + optlst['_name'])
|
||
|
root_list = {'name': optlst['_name'].replace('_optlst', ''), 'tools': [], 'help': optlst['help'], 'options': []}
|
||
|
if (optlst['flags'] & 0x100) != 0: root_list['tools'].append('compiler')
|
||
|
if (optlst['flags'] & 0x200) != 0: root_list['tools'].append('linker')
|
||
|
if (optlst['flags'] & 0x400) != 0: root_list['tools'].append('disassembler')
|
||
|
for opt in optlst['list']:
|
||
|
#print('+' + opt['names'])
|
||
|
root_list['options'].append(nice_option(opt))
|
||
|
nice_optlsts_map[root_list['name']] = root_list
|
||
|
|
||
|
nice_optlsts = [
|
||
|
nice_optlsts_map['CmdLine'],
|
||
|
nice_optlsts_map['CmdLineCompiler'],
|
||
|
nice_optlsts_map['CmdLineLinker'],
|
||
|
nice_optlsts_map['Debugging'],
|
||
|
nice_optlsts_map['FrontEndC'],
|
||
|
nice_optlsts_map['WarningC'],
|
||
|
nice_optlsts_map['Optimizer'],
|
||
|
nice_optlsts_map['BackEnd'],
|
||
|
nice_optlsts_map['Project'],
|
||
|
nice_optlsts_map['Linker'],
|
||
|
nice_optlsts_map['Dumper']
|
||
|
]
|
||
|
|
||
|
with open(sys.argv[2], 'w') as f:
|
||
|
json.dump(nice_optlsts, f, indent=4)
|