summaryrefslogtreecommitdiff
path: root/opt_decomp.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--opt_decomp.py394
1 files changed, 394 insertions, 0 deletions
diff --git a/opt_decomp.py b/opt_decomp.py
new file mode 100644
index 0000000..a6e612b
--- /dev/null
+++ b/opt_decomp.py
@@ -0,0 +1,394 @@
+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)