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)