mirror of https://git.wuffs.org/MWCC
387 lines
11 KiB
Python
387 lines
11 KiB
Python
|
import json
|
||
|
|
||
|
'''
|
||
|
HOW TO GENERATE:
|
||
|
|
||
|
|
||
|
'''
|
||
|
|
||
|
def play_with_addr(ea):
|
||
|
if ea == 0:
|
||
|
return None
|
||
|
|
||
|
e = ida_name.get_name_expr(0, 0, ea, BADADDR)
|
||
|
e = e[13:-2]
|
||
|
return e
|
||
|
|
||
|
class String:
|
||
|
name: str
|
||
|
value: str
|
||
|
|
||
|
@staticmethod
|
||
|
def from_ea(ea):
|
||
|
if ea == 0:
|
||
|
return None
|
||
|
else:
|
||
|
s = String()
|
||
|
s.name = get_name(ea)
|
||
|
v = get_bytes(ea, 1024)
|
||
|
v = v[:v.find(b'\0')]
|
||
|
s.value = v.decode('ascii')
|
||
|
return s
|
||
|
|
||
|
def encode(self):
|
||
|
if self.name.startswith('a'):
|
||
|
return self.value
|
||
|
else:
|
||
|
return dict(name=self.name, value=self.value)
|
||
|
|
||
|
class Param:
|
||
|
name: str
|
||
|
flags: int
|
||
|
myname: String
|
||
|
|
||
|
@staticmethod
|
||
|
def from_ea(ea):
|
||
|
print('reading param %08x' % ea)
|
||
|
which = get_wide_byte(ea)
|
||
|
next_ea = get_wide_dword(ea + 6)
|
||
|
p = PARAM_CLASSES[which]()
|
||
|
p.populate_from_ea(ea)
|
||
|
return p, next_ea
|
||
|
|
||
|
def populate_from_ea(self, ea):
|
||
|
self.name = get_name(ea)
|
||
|
self.flags = get_wide_byte(ea + 1)
|
||
|
self.myname = String.from_ea(get_wide_dword(ea + 2))
|
||
|
|
||
|
def encode(self):
|
||
|
d = dict(_name=self.name, flags=self.flags)
|
||
|
if self.myname is not None:
|
||
|
d['myname'] = self.myname
|
||
|
return d
|
||
|
|
||
|
@staticmethod
|
||
|
def list_from_ea(ea):
|
||
|
lst = []
|
||
|
while ea != 0:
|
||
|
param, next_ea = Param.from_ea(ea)
|
||
|
lst.append(param)
|
||
|
ea = next_ea
|
||
|
return lst
|
||
|
|
||
|
class FTypeCreatorParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(FTypeCreatorParam, self).populate_from_ea(ea)
|
||
|
self.fc = play_with_addr(get_wide_dword(ea + 10))
|
||
|
self.iscreator = get_wide_byte(ea + 14)
|
||
|
def encode(self):
|
||
|
d = super(FTypeCreatorParam, self).encode()
|
||
|
d['_type'] = 'FTypeCreator'
|
||
|
d['fc'] = self.fc
|
||
|
d['iscreator'] = self.iscreator
|
||
|
return d
|
||
|
|
||
|
class FilePathParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(FilePathParam, self).populate_from_ea(ea)
|
||
|
self.fflags = get_wide_byte(ea + 10)
|
||
|
self.defaultstr = String.from_ea(get_wide_dword(ea + 11))
|
||
|
self.filename = play_with_addr(get_wide_dword(ea + 15))
|
||
|
self.maxlen = get_wide_dword(ea + 19)
|
||
|
def encode(self):
|
||
|
d = super(FilePathParam, self).encode()
|
||
|
d['_type'] = 'FilePath'
|
||
|
d['fflags'] = self.fflags
|
||
|
d['defaultstr'] = self.defaultstr
|
||
|
d['filename'] = self.filename
|
||
|
d['maxlen'] = self.maxlen
|
||
|
return d
|
||
|
|
||
|
class NumberParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(NumberParam, self).populate_from_ea(ea)
|
||
|
self.size = get_wide_byte(ea + 10)
|
||
|
self.fit = get_wide_byte(ea + 11)
|
||
|
self.lo = get_wide_dword(ea + 12)
|
||
|
self.hi = get_wide_dword(ea + 16)
|
||
|
self.num = play_with_addr(get_wide_dword(ea + 20))
|
||
|
def encode(self):
|
||
|
d = super(NumberParam, self).encode()
|
||
|
d['_type'] = 'Number'
|
||
|
d['size'] = self.size
|
||
|
d['fit'] = self.fit
|
||
|
d['lo'] = self.lo
|
||
|
d['hi'] = self.hi
|
||
|
d['num'] = self.num
|
||
|
return d
|
||
|
|
||
|
class StringParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(StringParam, self).populate_from_ea(ea)
|
||
|
self.maxlen = get_wide_word(ea + 10)
|
||
|
self.pstring = get_wide_byte(ea + 12)
|
||
|
self.str = play_with_addr(get_wide_dword(ea + 13))
|
||
|
def encode(self):
|
||
|
d = super(StringParam, self).encode()
|
||
|
d['_type'] = 'String'
|
||
|
d['maxlen'] = self.maxlen
|
||
|
d['pstring'] = self.pstring
|
||
|
d['str'] = self.str
|
||
|
return d
|
||
|
|
||
|
class IdParam(StringParam):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(IdParam, self).populate_from_ea(ea)
|
||
|
def encode(self):
|
||
|
d = super(IdParam, self).encode()
|
||
|
d['_type'] = 'Id'
|
||
|
return d
|
||
|
|
||
|
class SymParam(StringParam):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(SymParam, self).populate_from_ea(ea)
|
||
|
def encode(self):
|
||
|
d = super(SymParam, self).encode()
|
||
|
d['_type'] = 'Sym'
|
||
|
return d
|
||
|
|
||
|
class OnOffParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(OnOffParam, self).populate_from_ea(ea)
|
||
|
self.var = play_with_addr(get_wide_dword(ea + 10))
|
||
|
def encode(self):
|
||
|
d = super(OnOffParam, self).encode()
|
||
|
d['_type'] = 'OnOff'
|
||
|
d['var'] = self.var
|
||
|
return d
|
||
|
|
||
|
class OffOnParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(OffOnParam, self).populate_from_ea(ea)
|
||
|
self.var = play_with_addr(get_wide_dword(ea + 10))
|
||
|
def encode(self):
|
||
|
d = super(OffOnParam, self).encode()
|
||
|
d['_type'] = 'OffOn'
|
||
|
d['var'] = self.var
|
||
|
return d
|
||
|
|
||
|
class MaskParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(MaskParam, self).populate_from_ea(ea)
|
||
|
self.size = get_wide_byte(ea + 10)
|
||
|
self.ormask = get_wide_dword(ea + 11)
|
||
|
self.andmask = get_wide_dword(ea + 15)
|
||
|
self.num = play_with_addr(get_wide_dword(ea + 19))
|
||
|
def encode(self):
|
||
|
d = super(MaskParam, self).encode()
|
||
|
d['_type'] = 'Mask'
|
||
|
d['size'] = self.size
|
||
|
d['ormask'] = self.ormask
|
||
|
d['andmask'] = self.andmask
|
||
|
d['num'] = self.num
|
||
|
return d
|
||
|
|
||
|
class ToggleParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(ToggleParam, self).populate_from_ea(ea)
|
||
|
self.size = get_wide_byte(ea + 10)
|
||
|
self.mask = get_wide_dword(ea + 11)
|
||
|
self.num = play_with_addr(get_wide_dword(ea + 15))
|
||
|
def encode(self):
|
||
|
d = super(ToggleParam, self).encode()
|
||
|
d['_type'] = 'Toggle'
|
||
|
d['size'] = self.size
|
||
|
d['mask'] = self.mask
|
||
|
d['num'] = self.num
|
||
|
return d
|
||
|
|
||
|
class SetParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(SetParam, self).populate_from_ea(ea)
|
||
|
self.size = get_wide_byte(ea + 10)
|
||
|
self.value = get_wide_dword(ea + 11)
|
||
|
self.num = play_with_addr(get_wide_dword(ea + 15))
|
||
|
def encode(self):
|
||
|
d = super(SetParam, self).encode()
|
||
|
d['_type'] = 'Set'
|
||
|
d['size'] = self.size
|
||
|
d['value'] = self.value
|
||
|
d['num'] = self.num
|
||
|
return d
|
||
|
|
||
|
class SetStringParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(SetStringParam, self).populate_from_ea(ea)
|
||
|
self.value = get_wide_dword(ea + 10)
|
||
|
self.pstring = get_wide_byte(ea + 14)
|
||
|
self.var = play_with_addr(get_wide_dword(ea + 15))
|
||
|
def encode(self):
|
||
|
d = super(SetStringParam, self).encode()
|
||
|
d['_type'] = 'SetString'
|
||
|
d['value'] = self.value
|
||
|
d['pstring'] = self.pstring
|
||
|
d['var'] = self.var
|
||
|
return d
|
||
|
|
||
|
class GenericParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(GenericParam, self).populate_from_ea(ea)
|
||
|
self.parse = play_with_addr(get_wide_dword(ea + 10))
|
||
|
# self.var = play_with_addr(get_wide_dword(ea + 14))
|
||
|
var = get_wide_dword(ea + 14)
|
||
|
if get_name(var).startswith('a'):
|
||
|
self.var = String.from_ea(var)
|
||
|
else:
|
||
|
self.var = var
|
||
|
self.help = String.from_ea(get_wide_dword(ea + 18))
|
||
|
def encode(self):
|
||
|
d = super(GenericParam, self).encode()
|
||
|
d['_type'] = 'Generic'
|
||
|
d['parse'] = self.parse
|
||
|
d['var'] = self.var
|
||
|
d['help'] = self.help
|
||
|
return d
|
||
|
|
||
|
class IfArgParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(IfArgParam, self).populate_from_ea(ea)
|
||
|
self.parg = Param.list_from_ea(get_wide_dword(ea + 10))
|
||
|
self.helpa = String.from_ea(get_wide_dword(ea + 14))
|
||
|
self.pnone = Param.list_from_ea(get_wide_dword(ea + 18))
|
||
|
self.helpn = String.from_ea(get_wide_dword(ea + 22))
|
||
|
def encode(self):
|
||
|
d = super(IfArgParam, self).encode()
|
||
|
d['_type'] = 'IfArg'
|
||
|
d['parg'] = self.parg
|
||
|
d['helpa'] = self.helpa
|
||
|
d['pnone'] = self.pnone
|
||
|
d['helpn'] = self.helpn
|
||
|
return d
|
||
|
|
||
|
class SettingParam(Param):
|
||
|
def populate_from_ea(self, ea):
|
||
|
super(SettingParam, self).populate_from_ea(ea)
|
||
|
self.parse = play_with_addr(get_wide_dword(ea + 10))
|
||
|
self.valuename = String.from_ea(get_wide_dword(ea + 14))
|
||
|
def encode(self):
|
||
|
d = super(SettingParam, self).encode()
|
||
|
d['_type'] = 'Setting'
|
||
|
d['parse'] = self.parse
|
||
|
d['valuename'] = self.valuename
|
||
|
return d
|
||
|
|
||
|
PARAM_CLASSES = [
|
||
|
Param,
|
||
|
FTypeCreatorParam,
|
||
|
FilePathParam,
|
||
|
NumberParam,
|
||
|
StringParam,
|
||
|
IdParam,
|
||
|
SymParam,
|
||
|
OnOffParam,
|
||
|
OffOnParam,
|
||
|
MaskParam,
|
||
|
ToggleParam,
|
||
|
SetParam,
|
||
|
SetStringParam,
|
||
|
GenericParam,
|
||
|
IfArgParam,
|
||
|
SettingParam
|
||
|
]
|
||
|
|
||
|
class Option:
|
||
|
name: str
|
||
|
names: String
|
||
|
avail: int
|
||
|
sub: 'OptionList'
|
||
|
conflicts: 'OptionList'
|
||
|
help: String
|
||
|
|
||
|
@staticmethod
|
||
|
def from_ea(ea):
|
||
|
if ea == 0:
|
||
|
return None
|
||
|
else:
|
||
|
o = Option()
|
||
|
o.name = get_name(ea)
|
||
|
o.names = String.from_ea(get_wide_dword(ea))
|
||
|
o.avail = get_wide_dword(ea + 4)
|
||
|
o.param = Param.list_from_ea(get_wide_dword(ea + 8))
|
||
|
o.sub = OptionList.from_ea(get_wide_dword(ea + 12))
|
||
|
o.conflicts = OptionList.from_ea(get_wide_dword(ea + 16), shallow=True)
|
||
|
o.help = String.from_ea(get_wide_dword(ea + 20))
|
||
|
return o
|
||
|
|
||
|
@staticmethod
|
||
|
def read_list(list_ea, shallow=False):
|
||
|
lst = []
|
||
|
while True:
|
||
|
ea = get_wide_dword(list_ea)
|
||
|
if ea == 0:
|
||
|
break
|
||
|
if shallow:
|
||
|
lst.append(get_name(ea))
|
||
|
else:
|
||
|
lst.append(Option.from_ea(ea))
|
||
|
list_ea += 4
|
||
|
return lst
|
||
|
|
||
|
def encode(self):
|
||
|
d = dict(_name=self.name, names=self.names, avail=self.avail, param=self.param, help=self.help)
|
||
|
if self.sub is not None:
|
||
|
d['sub'] = self.sub
|
||
|
if self.conflicts is not None:
|
||
|
d['conflicts'] = self.conflicts
|
||
|
return d
|
||
|
|
||
|
class OptionList:
|
||
|
@staticmethod
|
||
|
def from_ea(ea, shallow=False):
|
||
|
if ea == 0:
|
||
|
return None
|
||
|
else:
|
||
|
ol = OptionList()
|
||
|
ol.name = get_name(ea)
|
||
|
ol.help = String.from_ea(get_wide_dword(ea))
|
||
|
ol.flags = get_wide_dword(ea + 4)
|
||
|
list_ea = get_wide_dword(ea + 8)
|
||
|
if list_ea != 0:
|
||
|
ol.list_name = get_name(list_ea)
|
||
|
ol.list = Option.read_list(list_ea, shallow)
|
||
|
return ol
|
||
|
|
||
|
def dump(self):
|
||
|
print(f'OptionList({self.name})')
|
||
|
|
||
|
def encode(self):
|
||
|
d = dict(_name=self.name, flags=self.flags, _list_name=self.list_name, list=self.list)
|
||
|
if self.help is not None:
|
||
|
d['help'] = self.help
|
||
|
return d
|
||
|
|
||
|
def get_optlist_eas():
|
||
|
ea = get_name_ea_simple('_optLists')
|
||
|
lists = []
|
||
|
while True:
|
||
|
list_ea = get_wide_dword(ea)
|
||
|
if list_ea == 0:
|
||
|
break
|
||
|
# list_name = get_name(list_ea)
|
||
|
lists.append(list_ea)
|
||
|
ea += 4
|
||
|
return lists
|
||
|
|
||
|
optlists = []
|
||
|
for ea in get_optlist_eas():
|
||
|
optlists.append(OptionList.from_ea(ea))
|
||
|
|
||
|
def enc(obj):
|
||
|
if hasattr(obj, 'encode'):
|
||
|
return obj.encode()
|
||
|
else:
|
||
|
raise TypeError
|
||
|
|
||
|
with open('/Users/ash/src/mwcc/opts.json', 'w') as f:
|
||
|
json.dump(optlists, f, sort_keys=True, indent=4, default=enc)
|