0001 #!/usr/bin/env python 0002 ### BITPIM 0003 ### 0004 ### Copyright (C) 2003-2004 Roger Binns <rogerb@rogerbinns.com> 0005 ### 0006 ### This program is free software; you can redistribute it and/or modify 0007 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0008 ### 0009 ### $Id: hexeditor.py 4377 2007-08-27 04:58:33Z djpham $ 0010 0011 """A hex editor widget""" 0012 0013 # system modules 0014 from __future__ import with_statement 0015 import string 0016 import struct 0017 0018 # wx modules 0019 import wx 0020 from wx.lib import masked 0021 from wx.lib import scrolledpanel as scrolled 0022 0023 # bitpim modules 0024 import common 0025 import guihelper 0026 0027 #------------------------------------------------------------------------------- 0028 class DataStruct(object): 0029 def __init__(self, _name): 0030 self.name=_name 0031 self.fields=[] 0032 def set(self, dict): 0033 self.name=dict.keys()[0] 0034 self.fields=[] 0035 for f in dict[self.name]: 0036 if f['type']==DataItem.numeric_type: 0037 item=NumericDataItem(f['name']) 0038 elif f['type']==DataItem.string_type: 0039 item=StringDataItem(f['name']) 0040 else: 0041 item=DataItem(f['name'], DataItem.struct_type) 0042 item.set(f) 0043 self.fields.append(item) 0044 def get(self): 0045 l=[] 0046 for f in self.fields: 0047 l.append(f.get()) 0048 return { self.name: l } 0049 def encode(self, data, buffer_offset=0): 0050 # encode data to each & every fields and return the results 0051 l=[] 0052 start=0 0053 data_len=len(data) 0054 for f in self.fields: 0055 s=f.encode(data, start) 0056 start=f.start+f.len 0057 l.append( { '[0x%04X=%d]%s'%(f.start+buffer_offset, 0058 f.start+buffer_offset, f.name): `s` }) 0059 if start>=data_len: 0060 break 0061 return l 0062 0063 #------------------------------------------------------------------------------- 0064 class DataItem(object): 0065 """Represent a data item/component with in a record, which is a list of 0066 these things. 0067 """ 0068 offset_from_start='From Start' 0069 offset_from_prev='From Last Field' 0070 string_type='string' 0071 numeric_type='numeric' 0072 struct_type='struct' 0073 def __init__(self, _name, _type=numeric_type): 0074 self.name=_name 0075 self.offset_type=self.offset_from_start 0076 self.offset=0 0077 self.size=1 0078 self.start=self.len=None # start & length/size of actual data encoded 0079 # numeric fields 0080 self.unsigned=True 0081 self.LE=True 0082 # string fields 0083 self.fixed=True 0084 self.null_terminated=False 0085 self.type=_type 0086 def _get_type(self): 0087 return self._type 0088 def _set_type(self, _type): 0089 if _type not in (self.numeric_type, self.string_type, self.struct_type): 0090 raise TypeError 0091 self._type=_type 0092 if _type==self.numeric_type: 0093 self.__class__=NumericDataItem 0094 elif _type==self.string_type: 0095 self.__class__=StringDataItem 0096 type=property(fget=_get_type, fset=_set_type) 0097 def get(self): 0098 return { 'name': self.name, 'offset_type': self.offset_type, 0099 'offset': self.offset, 'type': self.type } 0100 def set(self, d): 0101 self.name=d.get('name', '<None>') 0102 self.offset_type=d.get('offset_type', None) 0103 self.offset=d.get('offset', None) 0104 self.type=d.get('type', None) 0105 def encode(self, s, start=None): 0106 """Encode the value of this item based on the string s""" 0107 raise NotImplementedError 0108 0109 #------------------------------------------------------------------------------- 0110 class NumericDataItem(DataItem): 0111 _fmts={ # struct pack/unpack formats 0112 True: { # unsigned 0113 True: { # little endian 0114 1: 'B', 2: '<H', 4: '<I' }, # size 0115 False: { # big endian 0116 1: 'B', 2: '>H', 4: '>I' } }, # size 0117 False: { # signed 0118 True: { # little endian 0119 1: 'b', 2: '<h', 4: '<i' }, # size 0120 False: { # big endian 0121 1: 'b', 2: '>h', 4: '>i' } } } # size 0122 0123 def __init__(self, name): 0124 super(NumericDataItem, self).__init__(name, self.numeric_type) 0125 def get(self): 0126 r=super(NumericDataItem, self).get() 0127 r.update( 0128 { 'unsigned': self.unsigned, 0129 'little_endian': self.LE, 0130 'size': self.size }) 0131 return r 0132 def set(self, d): 0133 super(NumericDataItem, self).set(d) 0134 if d.get('type', None)!=self.numeric_type: 0135 raise TypeError 0136 self.unsigned=d.get('unsigned', True) 0137 self.LE=d.get('little_endian', True) 0138 self.size=d.get('size', 1) 0139 def encode(self, s, start=None): 0140 fmt=self._fmts[self.unsigned][self.LE][self.size] 0141 self.len=struct.calcsize(fmt) 0142 if self.offset_type==self.offset_from_start: 0143 self.start=self.offset 0144 else: 0145 if start is None: 0146 raise ValueError 0147 self.start=start+self.offset 0148 return struct.unpack(fmt, s[self.start:self.start+self.len])[0] 0149 0150 #------------------------------------------------------------------------------- 0151 class StringDataItem(DataItem): 0152 def __init__(self, name): 0153 super(StringDataItem, self).__init__(name, self.string_type) 0154 def get(self): 0155 r=super(StringDataItem, self).get() 0156 r.update({ 'fixed': self.fixed, 'size': self.size, 0157 'null_terminated': self.null_terminated }) 0158 return r 0159 def set(self, d): 0160 super(StringDataItem, self).set(d) 0161 if d.get('type', None)!=self.string_type: 0162 raise TypeError 0163 self.fixed=d.get('fixed', True) 0164 self.size=d.get('size', 0) 0165 self.null_terminated=d.get('null_terminated', False) 0166 def encode(self, s, start=None): 0167 if self.offset_type==self.offset_from_start: 0168 self.start=self.offset 0169 else: 0170 if start is None: 0171 raise ValueError 0172 self.start=start+self.offset 0173 if self.fixed: 0174 # fixed length string 0175 if self.size==-1: 0176 # take all available space 0177 self.len=len(s)-self.offset 0178 s0=s[self.start:] 0179 else: 0180 # fixed size 0181 s0=s[self.start:self.start+self.size] 0182 self.len=self.size 0183 else: 0184 # pascal style variable string 0185 self.len=ord(s[self.start]) 0186 s0=s[self.start+1:self.start+1+self.len] 0187 if self.null_terminated: 0188 i=s0.find('\x00') 0189 if i==-1: 0190 return s0 0191 else: 0192 self.len=i 0193 return s0[:i] 0194 else: 0195 return s0 0196 0197 #------------------------------------------------------------------------------- 0198 class GeneralInfoSizer(wx.FlexGridSizer): 0199 def __init__(self, parent): 0200 super(GeneralInfoSizer, self).__init__(-1, 2, 5, 5) 0201 self.AddGrowableCol(1) 0202 self.Add(wx.StaticText(parent, -1, 'Struct Name:'), 0, wx.EXPAND|wx.ALL, 5) 0203 self._struct_name=wx.TextCtrl(parent, -1, '') 0204 self.Add(self._struct_name, 0, wx.EXPAND|wx.ALL, 5) 0205 self.Add(wx.StaticText(parent, -1, 'Field Name:'), 0, wx.EXPAND|wx.ALL, 5) 0206 self._name=wx.TextCtrl(parent, -1, '') 0207 self.Add(self._name, 0, wx.EXPAND|wx.ALL, 5) 0208 self.Add(wx.StaticText(parent, -1, 'Type:'), 0, wx.EXPAND|wx.ALL, 5) 0209 self._type=wx.ComboBox(parent, wx.NewId(), 0210 choices=[DataItem.numeric_type, 0211 DataItem.string_type], 0212 value=DataItem.numeric_type, 0213 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0214 self.Add(self._type, 0, wx.EXPAND|wx.ALL, 5) 0215 self.Add(wx.StaticText(parent, -1, 'Offset Type:'), 0, wx.EXPAND|wx.ALL, 5) 0216 self._offset_type=wx.ComboBox(parent, -1, 0217 value=DataItem.offset_from_start, 0218 choices=[DataItem.offset_from_start, 0219 DataItem.offset_from_prev], 0220 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0221 self.Add(self._offset_type, 0, wx.EXPAND|wx.ALL, 5) 0222 self.Add(wx.StaticText(parent, -1, 'Offset Value:'), 0, wx.EXPAND|wx.ALL, 5) 0223 self._offset=masked.NumCtrl(parent, wx.NewId(), 0224 allowNegative=False, 0225 min=0) 0226 self.Add(self._offset, 0, wx.ALL, 5) 0227 self._fields_group=(self._name, self._type, self._offset_type, 0228 self._offset) 0229 def set(self, data): 0230 if isinstance(data, DataStruct): 0231 self._struct_name.SetValue(data.name) 0232 elif isinstance(data, DataItem): 0233 self._name.SetValue(data.name) 0234 self._type.SetValue(data.type) 0235 self._offset_type.SetValue(data.offset_type) 0236 self._offset.SetValue(data.offset) 0237 def get(self, data): 0238 data.name=self._name.GetValue() 0239 data.type=self._type.GetValue() 0240 data.offset_type=self._offset_type.GetValue() 0241 data.offset=int(self._offset.GetValue()) 0242 return data 0243 def show(self, show_struct=False, show_field=False): 0244 self._struct_name.Enable(show_struct) 0245 for w in self._fields_group: 0246 w.Enable(show_field) 0247 def _get_struct_name(self): 0248 return self._struct_name.GetValue() 0249 struct_name=property(fget=_get_struct_name) 0250 def _get_type(self): 0251 return self._type.GetValue() 0252 type=property(fget=_get_type) 0253 def _get_type_id(self): 0254 return self._type.GetId() 0255 type_id=property(fget=_get_type_id) 0256 0257 #------------------------------------------------------------------------------- 0258 class NumericInfoSizer(wx.FlexGridSizer): 0259 _sign_choices=['Unsigned', 'Signed'] 0260 _endian_choices=['Little Endian', 'Big Endian'] 0261 _size_choices=['1', '2', '4'] 0262 def __init__(self, parent): 0263 super(NumericInfoSizer, self).__init__(-1, 2, 5, 5) 0264 self.AddGrowableCol(1) 0265 self.Add(wx.StaticText(parent, -1, 'Signed:'), 0, wx.EXPAND|wx.ALL, 5) 0266 self._sign=wx.ComboBox(parent, -1, value=self._sign_choices[0], 0267 choices=self._sign_choices, 0268 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0269 self.Add(self._sign, 0, wx.EXPAND|wx.ALL, 5) 0270 self.Add(wx.StaticText(parent, -1, 'Endian:'), 0, wx.EXPAND|wx.ALL, 5) 0271 self._endian=wx.ComboBox(parent, -1, value=self._endian_choices[0], 0272 choices=self._endian_choices, 0273 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0274 self.Add(self._endian, 0, wx.EXPAND|wx.ALL, 5) 0275 self.Add(wx.StaticText(parent, -1, 'Size:'), 0, wx.EXPAND|wx.ALL, 5) 0276 self._size=wx.ComboBox(parent, -1, value=self._size_choices[0], 0277 choices=self._size_choices, 0278 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0279 self.Add(self._size, 0, wx.EXPAND|wx.ALL, 5) 0280 def set(self, data): 0281 if data.unsigned: 0282 self._sign.SetValue(self._sign_choices[0]) 0283 else: 0284 self._sign.SetValue(self._sign_choices[1]) 0285 if data.LE: 0286 self._endian.SetValue(self._endian_choices[0]) 0287 else: 0288 self._endian.SetValue(self._endian_choices[1]) 0289 self._size.SetValue(`data.size`) 0290 def get(self, data): 0291 data.unsigned=self._sign.GetValue()==self._sign_choices[0] 0292 data.LE=self._endian.GetValue()==self._endian_choices[0] 0293 data.size=int(self._size.GetValue()) 0294 return data 0295 0296 #------------------------------------------------------------------------------- 0297 class StringInfoSizer(wx.FlexGridSizer): 0298 _fixed_choices=['Fixed', 'Pascal'] 0299 def __init__(self, parent): 0300 super(StringInfoSizer, self).__init__(-1, 2, 5, 5) 0301 self.AddGrowableCol(1) 0302 self.Add(wx.StaticText(parent, -1, 'Fixed/Pascal:'), 0, wx.EXPAND|wx.ALL, 5) 0303 self._fixed=wx.ComboBox(parent, -1, value=self._fixed_choices[0], 0304 choices=self._fixed_choices, 0305 style=wx.CB_DROPDOWN|wx.CB_READONLY) 0306 self.Add(self._fixed, 0, wx.EXPAND|wx.ALL, 5) 0307 self.Add(wx.StaticText(parent, -1, 'Max Length:'), 0, wx.EXPAND|wx.ALL, 5) 0308 self._max_len=masked.NumCtrl(parent, -1, value=1, min=-1) 0309 self.Add(self._max_len, 0, wx.EXPAND|wx.ALL, 5) 0310 self.Add(wx.StaticText(parent, -1, 'Null Terminated:'), 0, wx.EXPAND|wx.ALL, 5) 0311 self._null_terminated=wx.CheckBox(parent, -1) 0312 self.Add(self._null_terminated, 0, wx.EXPAND|wx.ALL, 5) 0313 def set(self, data): 0314 if data.fixed: 0315 self._fixed.SetValue(self._fixed_choices[0]) 0316 else: 0317 self._fixed.SetValue(self._fixed_choices[1]) 0318 self._max_len.SetValue(`data.size`) 0319 self._null_terminated.SetValue(data.null_terminated) 0320 def get(self, data): 0321 data.fixed=self._fixed.GetValue()==self._fixed_choices[0] 0322 data.size=int(self._max_len.GetValue()) 0323 data.null_terminated=self._null_terminated.GetValue() 0324 return data 0325 0326 #------------------------------------------------------------------------------- 0327 class TemplateDialog(wx.Dialog): 0328 _type_choices=['Numeric', 'String'] 0329 _struct_type='struct' 0330 _field_type='field' 0331 def __init__(self, parent): 0332 super(TemplateDialog, self).__init__(parent, -1, 0333 'Hex Template Editor', 0334 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 0335 self._data=[] 0336 self._item_tree=self._numeric_bs=self._string_bs=None 0337 self._general_bs=None 0338 self._tree_root=None 0339 self._field_info=self._field_info_hbs=None 0340 self._info_sizer={ NumericDataItem.numeric_type: self._numeric_bs, 0341 StringDataItem.string_type: self._string_bs } 0342 main_vbs=wx.BoxSizer(wx.VERTICAL) 0343 hbs1=wx.BoxSizer(wx.HORIZONTAL) 0344 hbs1.Add(self._create_tree_pane(), 1, wx.EXPAND|wx.ALL, 5) 0345 hbs1.Add(self._create_info_pane(), 2, wx.EXPAND|wx.ALL, 5) 0346 main_vbs.Add(hbs1, 1, wx.EXPAND|wx.ALL, 5) 0347 main_vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 0348 main_vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0349 self.SetSizer(main_vbs) 0350 self.SetAutoLayout(True) 0351 main_vbs.Fit(self) 0352 0353 def _create_tree_pane(self): 0354 vbs=wx.BoxSizer(wx.VERTICAL) 0355 sw=scrolled.ScrolledPanel(self, -1) 0356 self._item_tree=wx.TreeCtrl(sw, wx.NewId(), 0357 style=wx.TR_DEFAULT_STYLE|wx.TR_HAS_BUTTONS) 0358 wx.EVT_TREE_SEL_CHANGED(self, self._item_tree.GetId(), 0359 self._OnTreeSel) 0360 self._tree_root=self._item_tree.AddRoot('Data Templates') 0361 sw_bs=wx.BoxSizer(wx.VERTICAL) 0362 sw_bs.Add(self._item_tree, 1, wx.EXPAND|wx.ALL, 0) 0363 sw.SetSizer(sw_bs) 0364 sw.SetAutoLayout(True) 0365 sw_bs.Fit(sw) 0366 sw.SetupScrolling() 0367 vbs.Add(sw, 1, wx.EXPAND|wx.ALL, 5) 0368 hbs=wx.BoxSizer(wx.HORIZONTAL) 0369 hbs.Add(wx.Button(self, wx.ID_ADD, 'Add'), 0, wx.EXPAND|wx.ALL, 5) 0370 hbs.Add(wx.Button(self, wx.ID_DELETE, 'Delete'), 0, wx.EXPAND|wx.ALL, 5) 0371 wx.EVT_BUTTON(self, wx.ID_ADD, self._OnAdd) 0372 wx.EVT_BUTTON(self, wx.ID_DELETE, self._OnDelete) 0373 vbs.Add(hbs, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5) 0374 return vbs 0375 def _create_info_pane(self): 0376 # main boxsize 0377 vbs=wx.BoxSizer(wx.VERTICAL) 0378 hbs=wx.BoxSizer(wx.HORIZONTAL) 0379 # Type & offset 0380 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Type'), 0381 wx.VERTICAL) 0382 self._general_bs=GeneralInfoSizer(self) 0383 wx.EVT_COMBOBOX(self, self._general_bs.type_id, self._OnTypeChanged) 0384 static_bs.Add(self._general_bs, 0, wx.EXPAND|wx.ALL, 5) 0385 hbs.Add(static_bs, 0, wx.ALL, 5) 0386 # all info 0387 self._field_info=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Info'), 0388 wx.VERTICAL) 0389 # numeric info box 0390 self._numeric_bs=NumericInfoSizer(self) 0391 self._field_info.Add(self._numeric_bs, 0, wx.EXPAND|wx.ALL, 5) 0392 self._string_bs=StringInfoSizer(self) 0393 self._field_info.Add(self._string_bs, 0, wx.EXPAND|wx.ALL, 5) 0394 hbs.Add(self._field_info, 0, wx.ALL, 5) 0395 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5) 0396 hbs1=wx.BoxSizer(wx.HORIZONTAL) 0397 hbs1.Add(wx.Button(self, wx.ID_SAVE, 'Set'), 0, wx.EXPAND|wx.ALL, 5) 0398 hbs1.Add(wx.Button(self, wx.ID_REVERT, 'Revert'), 0, wx.EXPAND|wx.ALL, 5) 0399 wx.EVT_BUTTON(self, wx.ID_SAVE, self._OnSave) 0400 wx.EVT_BUTTON(self, wx.ID_REVERT, self._OnRevert) 0401 vbs.Add(hbs1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0) 0402 self._field_info_hbs=hbs 0403 return vbs 0404 def _show_field_info(self, _struct=False, field=False, 0405 numeric_field=False, string_field=False): 0406 # show/hide individual fields 0407 self._general_bs.show(_struct, field) 0408 self._field_info.Show(self._numeric_bs, numeric_field) 0409 self._field_info.Show(self._string_bs, string_field) 0410 self._field_info.Layout() 0411 self._field_info_hbs.Layout() 0412 def _populate(self): 0413 # clear the tree and repopulate 0414 self._item_tree.DeleteChildren(self._tree_root) 0415 for i,e in enumerate(self._data): 0416 item=self._item_tree.AppendItem(self._tree_root, e.name) 0417 self._item_tree.SetPyData(item, { 'type': self._struct_type, 0418 'index': i }) 0419 for i1,e1 in enumerate(e.fields): 0420 field_item=self._item_tree.AppendItem(item, e1.name) 0421 self._item_tree.SetPyData(field_item, { 'type': self._field_type, 0422 'index': i, 0423 'field_index': i1 }) 0424 self.expand() 0425 def _populate_struct(self, _item_index): 0426 self._general_bs.set(self._data[_item_index]) 0427 self._show_field_info(True) 0428 def _populate_each(self, _struct_index, _item_index): 0429 _struct=self._data[_struct_index] 0430 _item=_struct.fields[_item_index] 0431 self._general_bs.set(_item) 0432 if _item.type==DataItem.numeric_type: 0433 self._show_field_info(True, True, True) 0434 self._numeric_bs.set(_item) 0435 else: 0436 self._show_field_info(True, True, False, True) 0437 self._string_bs.set(_item) 0438 def _OnTypeChanged(self, _): 0439 new_type=self._general_bs.type 0440 self._show_field_info(True, True, new_type==DataItem.numeric_type, 0441 new_type==DataItem.string_type) 0442 def _OnAdd(self, _): 0443 sel_idx=self._item_tree.GetSelection() 0444 if not sel_idx.IsOk(): 0445 return 0446 if sel_idx==self._tree_root: 0447 # add a new structure 0448 struct_item=DataStruct('New Struct') 0449 self._data.append(struct_item) 0450 else: 0451 # add a new field to the existing structure 0452 data_item=self._item_tree.GetPyData(sel_idx) 0453 item=NumericDataItem('New Field') 0454 self._data[data_item['index']].fields.append(item) 0455 self._populate() 0456 def _OnDelete(self, _): 0457 sel_idx=self._item_tree.GetSelection() 0458 if not sel_idx.IsOk(): 0459 return 0460 node_data=self._item_tree.GetPyData(sel_idx) 0461 if node_data is None: 0462 return 0463 if node_data['type']==self._field_type: 0464 # del this field 0465 del self._data[node_data['index']].fields[node_data['field_index']] 0466 else: 0467 # del this struct and its fields 0468 del self._data[node_data['index']] 0469 # and re-populate the tree 0470 self._populate() 0471 def _OnSave(self, _): 0472 sel_idx=self._item_tree.GetSelection() 0473 if not sel_idx.IsOk(): 0474 return 0475 node_data=self._item_tree.GetPyData(sel_idx) 0476 if node_data is None: 0477 return 0478 # update the struct name 0479 self._data[node_data['index']].name=self._general_bs.struct_name 0480 if node_data['type']==self._field_type: 0481 data_item=self._data[node_data['index']].\ 0482 fields[node_data['field_index']] 0483 data_item=self._general_bs.get(data_item) 0484 if data_item.type==DataItem.numeric_type: 0485 data_item=self._numeric_bs.get(data_item) 0486 else: 0487 data_item=self._string_bs.get(data_item) 0488 self._data[node_data['index']].fields[node_data['field_index']]=data_item 0489 self._item_tree.SetItemText(self._item_tree.GetItemParent(sel_idx), 0490 self._data[node_data['index']].name) 0491 self._item_tree.SetItemText(sel_idx, data_item.name) 0492 else: 0493 self._item_tree.SetItemText(sel_idx, self._data[node_data['index']].name) 0494 0495 def _OnRevert(self, _): 0496 sel_idx=self._item_tree.GetSelection() 0497 if not sel_idx.IsOk(): 0498 return 0499 node_data=self._item_tree.GetPyData(sel_idx) 0500 if node_data is None: 0501 self._show_field_info() 0502 else: 0503 self._populate_struct(node_data['index']) 0504 if node_data['type']==self._field_type: 0505 self._populate_each(node_data['index'], node_data['field_index']) 0506 0507 def _OnTreeSel(self, evt): 0508 sel_idx=evt.GetItem() 0509 if not sel_idx.IsOk(): 0510 # invalid selection 0511 return 0512 item_data=self._item_tree.GetPyData(sel_idx) 0513 if item_data is None: 0514 self._show_field_info() 0515 else: 0516 self._populate_struct(item_data['index']) 0517 if item_data['type']==self._field_type: 0518 self._populate_each(item_data['index'], item_data['field_index']) 0519 def expand(self): 0520 # expand the tree 0521 self._item_tree.Expand(self._tree_root) 0522 (id, cookie)=self._item_tree.GetFirstChild(self._tree_root) 0523 while id.IsOk(): 0524 self._item_tree.Expand(id) 0525 (id, cookie)=self._item_tree.GetNextChild(self._tree_root, cookie) 0526 def set(self, l): 0527 self._data=l 0528 self._populate() 0529 def get(self): 0530 return self._data 0531 0532 #------------------------------------------------------------------------------- 0533 class HexEditor(wx.ScrolledWindow): 0534 0535 _addr_range=xrange(8) 0536 _hex_range_start=10 0537 _hex_range_start2=33 0538 _hex_range=xrange(_hex_range_start, 58) 0539 _ascii_range_start=60 0540 _ascii_range=xrange(60, 76) 0541 0542 def __init__(self, parent, id=-1, style=wx.WANTS_CHARS, 0543 _set_pos=None, _set_sel=None, _set_val=None): 0544 wx.ScrolledWindow.__init__(self, parent, id, style=style) 0545 self.parent=parent 0546 self.data="" 0547 self.title="" 0548 self.buffer=None 0549 self.hasfocus=False 0550 self.dragging=False 0551 self.current_ofs=None 0552 self._module=None 0553 self._templates=[] 0554 self._search_string=None 0555 # ways of displaying status 0556 self.set_pos=_set_pos or self._set_pos 0557 self.set_val=_set_val or self._set_val 0558 self.set_sel=_set_sel or self._set_sel 0559 # some GUI setup 0560 self.SetBackgroundColour("WHITE") 0561 self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM)) 0562 self.sethighlight(wx.NamedColour("BLACK"), wx.NamedColour("YELLOW")) 0563 self.setnormal(wx.NamedColour("BLACK"), wx.NamedColour("WHITE")) 0564 self.setfont(wx.TheFontList.FindOrCreateFont(10, wx.MODERN, wx.NORMAL, wx.NORMAL)) 0565 self.OnSize(None) 0566 self.highlightrange(None, None) 0567 # other stuff 0568 self._create_context_menu() 0569 self._map_events() 0570 0571 def _map_events(self): 0572 wx.EVT_SCROLLWIN(self, self.OnScrollWin) 0573 wx.EVT_PAINT(self, self.OnPaint) 0574 wx.EVT_SIZE(self, self.OnSize) 0575 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground) 0576 wx.EVT_SET_FOCUS(self, self.OnGainFocus) 0577 wx.EVT_KILL_FOCUS(self, self.OnLoseFocus) 0578 wx.EVT_LEFT_DOWN(self, self.OnStartSelection) 0579 wx.EVT_LEFT_UP(self, self.OnEndSelection) 0580 wx.EVT_MOTION(self, self.OnMakeSelection) 0581 wx.EVT_RIGHT_UP(self, self.OnRightClick) 0582 0583 def _create_context_menu(self): 0584 self._reload_menu_id=self._apply_menu_id=None 0585 menu_items=( 0586 ('File', (('Load', self.OnLoadFile), 0587 ('Save As', self.OnSaveAs), 0588 ('Save Selection As', self.OnSaveSelection), 0589 ('Save Hexdump As', self.OnSaveHexdumpAs))), 0590 ('Set Selection', (('Start', self.OnStartSelMenu), 0591 ('End', self.OnEndSelMenu))), 0592 ('Value', self.OnViewValue), 0593 ('Search', (('Search', self.OnSearch), 0594 ('Search Again', self.OnSearchAgain))), 0595 ('Import Python Module', self.OnImportModule), 0596 ('Reload Python Module', self.OnReloadModule, '_reload_menu_id'), 0597 ('Apply Python Func', self.OnApplyFunc, '_apply_menu_id'), 0598 ('Template', (('Load', self.OnTemplateLoad), 0599 ('Save As', self.OnTemplateSaveAs), 0600 ('Edit', self.OnTemplateEdit), 0601 ('Apply', self.OnTemplateApply))) 0602 ) 0603 self._bgmenu=wx.Menu() 0604 for menu_item in menu_items: 0605 if isinstance(menu_item[1], tuple): 0606 # submenu 0607 sub_menu=wx.Menu() 0608 for submenu_item in menu_item[1]: 0609 id=wx.NewId() 0610 sub_menu.Append(id, submenu_item[0]) 0611 wx.EVT_MENU(self, id, submenu_item[1]) 0612 self._bgmenu.AppendMenu(wx.NewId(), menu_item[0], sub_menu) 0613 else: 0614 # regular menu item 0615 id=wx.NewId() 0616 self._bgmenu.Append(id, menu_item[0]) 0617 wx.EVT_MENU(self, id, menu_item[1]) 0618 if len(menu_item)>2: 0619 # need to save menu ID 0620 setattr(self, menu_item[2], id) 0621 0622 def SetData(self, data): 0623 self.data=data 0624 self.needsupdate=True 0625 self.updatescrollbars() 0626 self.Refresh() 0627 0628 def SetTitle(self, title): 0629 self.title=title 0630 0631 def SetStatusDisplay(self, _set_pos=None, _set_sel=None, _set_val=None): 0632 self.set_pos=_set_pos or self._set_pos 0633 self.set_sel=_set_sel or self._set_sel 0634 self.set_val=_set_val or self._set_val 0635 0636 def OnEraseBackground(self, _): 0637 pass 0638 def _set_pos(self, pos): 0639 pass 0640 def _set_sel(self, sel_start, sel_end): 0641 pass 0642 def _set_val(self, v): 0643 pass 0644 0645 def _to_char_line(self, x, y): 0646 """Convert an x,y point to (char, line) 0647 """ 0648 return x/self.charwidth, y/self.charheight 0649 def _to_xy(self, char, line): 0650 return char*self.charwidth, line*self.charheight 0651 def _to_buffer_offset(self, char, line): 0652 if char in self._hex_range: 0653 if char>self._hex_range_start2: 0654 char-=1 0655 if ((char-self._hex_range_start)%3)<2: 0656 return line*16+(char-self._hex_range_start)/3 0657 elif char in self._ascii_range: 0658 return line*16+char-self._ascii_range_start 0659 def _set_and_move(self, evt): 0660 c,l=self._to_char_line(evt.GetX(), evt.GetY()) 0661 self.GetCaret().Move(self._to_xy(c, l)) 0662 x0, y0=self.GetViewStart() 0663 char_x=c+x0 0664 line_y=l+y0 0665 return self._to_buffer_offset(char_x, line_y) 0666 _value_formats=( 0667 ('unsigned char', 'B', struct.calcsize('B')), 0668 ('signed char', 'b', struct.calcsize('b')), 0669 ('LE unsigned short', '<H', struct.calcsize('<H')), 0670 ('LE signed short', '<h', struct.calcsize('<h')), 0671 ('BE unsigned short', '>H', struct.calcsize('>H')), 0672 ('BE signed short', '>h', struct.calcsize('>h')), 0673 ('LE unsigned int', '<I', struct.calcsize('<I')), 0674 ('LE signed int', '<i', struct.calcsize('<i')), 0675 ('BE unsigned int', '>I', struct.calcsize('>I')), 0676 ('BE signed int', '>i', struct.calcsize('>i')), 0677 ) 0678 def _gen_values(self, _data, _ofs): 0679 """ Generate the values of various number formats starting at the 0680 current offset. 0681 """ 0682 n=_data[_ofs:] 0683 len_n=len(n) 0684 s='0x%X=%d'%(_ofs, _ofs) 0685 res=[{ 'Data Offset': s}, {'':''} ] 0686 for i,e in enumerate(self._value_formats): 0687 if len_n<e[2]: 0688 continue 0689 v=struct.unpack(e[1], n[:e[2]])[0] 0690 if i%2: 0691 s='%d'%v 0692 else: 0693 fmt='0x%0'+str(e[2]*2)+'X=%d' 0694 s=fmt%(v,v) 0695 res.append({ e[0]: s }) 0696 return res 0697 0698 def _apply_template(self, template_name): 0699 # if user specifies a block, encode that, 0700 if self.highlightstart is None or self.highlightstart==-1 or \ 0701 self.highlightend is None or self.highlightend==-1: 0702 # no selection 0703 _data=self.data[self.current_ofs:] 0704 _ofs=self.current_ofs 0705 else: 0706 _data=self.data[self.highlightstart:self.highlightend] 0707 _ofs=self.highlightstart 0708 for f in self._templates: 0709 if f.name==template_name: 0710 l=[{ 'Template': f.name }, 0711 { 'Data Offset': '0x%04X=%d'%(_ofs, _ofs) }] 0712 return l+f.encode(_data, _ofs) 0713 return [] 0714 0715 def _display_result(self, result): 0716 """ Display the results from applying a Python routine over the data 0717 """ 0718 s='' 0719 for d in result: 0720 for k,e in d.items(): 0721 s+=k+':\t'+e+'\n' 0722 guihelper.MessageDialog(self, s, 'Results', style=wx.OK) 0723 0724 def OnLoadFile(self, _): 0725 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load', 0726 style=wx.OPEN|wx.FILE_MUST_EXIST), 0727 True) as (dlg, retcode): 0728 if retcode==wx.ID_OK: 0729 self.SetData(file(dlg.GetPath(), 'rb').read()) 0730 def OnSaveAs(self, _): 0731 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save', 0732 style=wx.SAVE|wx.OVERWRITE_PROMPT), 0733 True) as (dlg, retcode): 0734 if retcode==wx.ID_OK: 0735 file(dlg.GetPath(), 'wb').write(self.data) 0736 def hexdumpdata(self): 0737 res="" 0738 l=len(self.data) 0739 if self.title: 0740 res += self.title+": "+`l`+" bytes\n" 0741 res += "<#! !#>\n" 0742 pos=0 0743 while pos<l: 0744 text="%08X "%(pos) 0745 line=self.data[pos:pos+16] 0746 for i in range(len(line)): 0747 text+="%02X "%(ord(line[i])) 0748 text+=" "*(16-len(line)) 0749 text+=" " 0750 for i in range(len(line)): 0751 c=line[i] 0752 if (ord(c)>=32 and string.printable.find(c)>=0): 0753 text+=c 0754 else: 0755 text+='.' 0756 res+=text+"\n" 0757 pos+=16 0758 return res 0759 0760 def OnSaveHexdumpAs(self, _): 0761 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save', 0762 style=wx.SAVE|wx.OVERWRITE_PROMPT), 0763 True) as (dlg, retcode): 0764 if retcode==wx.ID_OK: 0765 file(dlg.GetPath(), 'wb').write(self.hexdumpdata()) 0766 def OnSaveSelection(self, _): 0767 if self.highlightstart is None or self.highlightstart==-1 or \ 0768 self.highlightend is None or self.highlightend==-1: 0769 # no selection 0770 return 0771 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save', 0772 style=wx.SAVE|wx.OVERWRITE_PROMPT), 0773 True) as (dlg, retcode): 0774 if retcode==wx.ID_OK: 0775 file(dlg.GetPath(), 'wb').write( 0776 self.data[self.highlightstart:self.highlightend]) 0777 0778 def OnReloadModule(self, _): 0779 try: 0780 reload(self._module) 0781 except: 0782 self._module=None 0783 guihelper.MessageDialog(self, 'Failed to reload module', 0784 'Reload Module Error', 0785 style=wx.OK|wx.ICON_ERROR) 0786 0787 def OnApplyFunc(self, _): 0788 choices=[x for x in dir(self._module) \ 0789 if callable(getattr(self._module, x))] 0790 with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a function to apply:', 0791 'Apply Python Func', 0792 choices), 0793 True) as (dlg, retcode): 0794 if retcode==wx.ID_OK: 0795 try: 0796 res=getattr(self._module, dlg.GetStringSelection())( 0797 self, self.data, self.current_ofs) 0798 self._display_result(res) 0799 except: 0800 guihelper.MessageDialog(self, 'Apply Func raised an exception', 0801 'Apply Func Error', 0802 style=wx.OK|wx.ICON_ERROR) 0803 0804 def OnImportModule(self, _): 0805 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter the name of a Python Module:', 0806 'Module Import'), 0807 True) as (dlg, retcode): 0808 if retcode==wx.ID_OK: 0809 try: 0810 self._module=common.importas(dlg.GetValue()) 0811 except ImportError: 0812 self._module=None 0813 guihelper.MessageDialog(self, 'Failed to import module: '+dlg.GetValue(), 0814 'Module Import Error', 0815 style=wx.OK|wx.ICON_ERROR) 0816 0817 def OnStartSelMenu(self, evt): 0818 ofs=self.current_ofs 0819 if ofs is not None: 0820 self.highlightstart=ofs 0821 self.needsupdate=True 0822 self.Refresh() 0823 self.set_sel(self.highlightstart, self.highlightend) 0824 0825 def OnEndSelMenu(self, _): 0826 ofs=self.current_ofs 0827 if ofs is not None: 0828 self.highlightend=ofs+1 0829 self.needsupdate=True 0830 self.Refresh() 0831 self.set_sel(self.highlightstart, self.highlightend) 0832 0833 def OnViewValue(self, _): 0834 ofs=self.current_ofs 0835 if ofs is not None: 0836 self._display_result(self._gen_values(self.data, ofs)) 0837 0838 def OnStartSelection(self, evt): 0839 self.highlightstart=self.highlightend=None 0840 ofs=self._set_and_move(evt) 0841 if ofs is not None: 0842 self.highlightstart=ofs 0843 self.dragging=True 0844 self.set_val(self.data[ofs:]) 0845 else: 0846 self.set_val(None) 0847 self.needsupdate=True 0848 self.Refresh() 0849 self.set_pos(ofs) 0850 self.set_sel(self.highlightstart, self.highlightend) 0851 0852 def OnMakeSelection(self, evt): 0853 if not self.dragging: 0854 return 0855 ofs=self._set_and_move(evt) 0856 if ofs is not None: 0857 self.highlightend=ofs+1 0858 self.needsupdate=True 0859 self.Refresh() 0860 self.set_pos(ofs) 0861 self.set_sel(self.highlightstart, self.highlightend) 0862 def OnEndSelection(self, evt): 0863 self.dragging=False 0864 ofs=self._set_and_move(evt) 0865 self.set_pos(ofs) 0866 self.set_sel(self.highlightstart, self.highlightend) 0867 0868 def OnRightClick(self, evt): 0869 self.current_ofs=self._set_and_move(evt) 0870 if self.current_ofs is None: 0871 self.set_val(None) 0872 else: 0873 self.set_val(self.data[self.current_ofs:]) 0874 self.set_pos(self.current_ofs) 0875 self._bgmenu.Enable(self._apply_menu_id, self._module is not None) 0876 self._bgmenu.Enable(self._reload_menu_id, self._module is not None) 0877 self.PopupMenu(self._bgmenu, evt.GetPosition()) 0878 0879 def OnTemplateLoad(self, _): 0880 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load', 0881 wildcard='*.tmpl', 0882 style=wx.OPEN|wx.FILE_MUST_EXIST), 0883 True) as (dlg, retcode): 0884 if retcode==wx.ID_OK: 0885 result={} 0886 try: 0887 execfile(dlg.GetPath()) 0888 except UnicodeError: 0889 common.unicode_execfile(dlg.GetPath()) 0890 exist_keys={} 0891 for i,e in enumerate(self._templates): 0892 exist_keys[e.name]=i 0893 for d in result['templates']: 0894 data_struct=DataStruct('new struct') 0895 data_struct.set(d) 0896 if exist_keys.has_key(data_struct.name): 0897 self._templates[exist_keys[data_struct.name]]=data_struct 0898 else: 0899 self._templates.append(data_struct) 0900 0901 def OnTemplateSaveAs(self, _): 0902 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save', 0903 wildcard='*.tmpl', 0904 style=wx.SAVE|wx.OVERWRITE_PROMPT), 0905 True) as (dlg, retcode): 0906 if retcode==wx.ID_OK: 0907 r=[x.get() for x in self._templates] 0908 common.writeversionindexfile(dlg.GetPath(), 0909 { 'templates': r }, 1) 0910 0911 def OnTemplateApply(self, _): 0912 if not self._templates: 0913 # no templates to apply 0914 return 0915 choices=[x.name for x in self._templates] 0916 with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a template to apply:', 0917 'Apply Data Template', 0918 choices), 0919 True) as (dlg, retcode): 0920 if retcode==wx.ID_OK: 0921 try: 0922 res=self._apply_template(dlg.GetStringSelection()) 0923 self._display_result(res) 0924 except: 0925 guihelper.MessageDialog(self, 'Apply Template raised an exception', 0926 'Apply Template Error', 0927 style=wx.OK|wx.ICON_ERROR), 0928 0929 def OnTemplateEdit(self, _): 0930 dlg=TemplateDialog(self) 0931 dlg.set(self._templates) 0932 with guihelper.WXDialogWrapper(dlg, True) as (dlg, retcode): 0933 if retcode==wx.ID_OK: 0934 self._templates=dlg.get() 0935 0936 def OnSearch(self, evt): 0937 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter data to search (1 0x23 045 ...):', 0938 'Search Data'), 0939 True) as (dlg, retcode): 0940 if retcode==wx.ID_OK: 0941 l=dlg.GetValue().split(' ') 0942 s='' 0943 for e in l: 0944 if e[0:2]=='0x': 0945 s+=chr(int(e, 16)) 0946 elif e[0]=='0': 0947 s+=chr(int(e, 8)) 0948 else: 0949 s+=chr(int(e)) 0950 i=self.data[self.current_ofs:].find(s) 0951 if i!=-1: 0952 self._search_string=s 0953 self.highlightstart=i+self.current_ofs 0954 self.highlightend=self.highlightstart+len(s) 0955 self.needsupdate=True 0956 self.Refresh() 0957 self.set_sel(self.highlightstart, self.highlightend) 0958 else: 0959 self._search_string=None 0960 0961 def OnSearchAgain(self, evt): 0962 if self._search_string is not None: 0963 i=self.data[self.current_ofs:].find(self._search_string) 0964 if i==-1: 0965 return 0966 self.highlightstart=i+self.current_ofs 0967 self.highlightend=self.highlightstart+len(self._search_string) 0968 self.needsupdate=True 0969 self.Refresh() 0970 self.set_sel(self.highlightstart, self.highlightend) 0971 0972 def OnSize(self, evt): 0973 # uncomment these lines to prevent going wider than is needed 0974 # if self.width>self.widthinchars*self.charwidth: 0975 # self.SetClientSize( (self.widthinchars*self.charwidth, self.height) ) 0976 if evt is None: 0977 self.width=(self.widthinchars+3)*self.charwidth 0978 self.height=self.charheight*20 0979 self.SetClientSize((self.width, self.height)) 0980 self.SetCaret(wx.Caret(self, (self.charwidth, self.charheight))) 0981 self.GetCaret().Show(True) 0982 else: 0983 self.width,self.height=self.GetClientSizeTuple() 0984 self.needsupdate=True 0985 0986 def OnGainFocus(self,_): 0987 self.hasfocus=True 0988 self.needsupdate=True 0989 self.Refresh() 0990 0991 def OnLoseFocus(self,_): 0992 self.hasfocus=False 0993 self.needsupdate=True 0994 self.Refresh() 0995 0996 def highlightrange(self, start, end): 0997 self.needsupdate=True 0998 self.highlightstart=start 0999 self.highlightend=end 1000 self.Refresh() 1001 self.set_pos(None) 1002 self.set_sel(self.highlightstart, self.highlightend) 1003 self.set_val(None) 1004 1005 def _ishighlighted(self, pos): 1006 return pos>=self.highlightstart and pos<self.highlightend 1007 1008 def sethighlight(self, foreground, background): 1009 self.highlight=foreground,background 1010 1011 def setnormal(self, foreground, background): 1012 self.normal=foreground,background 1013 1014 def setfont(self, font): 1015 dc=wx.ClientDC(self) 1016 dc.SetFont(font) 1017 self.charwidth, self.charheight=dc.GetTextExtent("M") 1018 self.font=font 1019 self.updatescrollbars() 1020 1021 def updatescrollbars(self): 1022 # how many lines are we? 1023 lines=len(self.data)/16 1024 if lines==0 or len(self.data)%16: 1025 lines+=1 1026 self.datalines=lines 1027 ## lines+=1 # status line 1028 # fixed width 1029 self.widthinchars=8+2+3*16+1+2+16 1030 self.SetScrollbars(self.charwidth, self.charheight, self.widthinchars, lines, self.GetViewStart()[0], self.GetViewStart()[1]) 1031 1032 def _setnormal(self,dc): 1033 dc.SetTextForeground(self.normal[0]) 1034 dc.SetTextBackground(self.normal[1]) 1035 1036 def _sethighlight(self,dc): 1037 dc.SetTextForeground(self.highlight[0]) 1038 dc.SetTextBackground(self.highlight[1]) 1039 1040 def _setstatus(self,dc): 1041 dc.SetTextForeground(self.normal[1]) 1042 dc.SetTextBackground(self.normal[0]) 1043 dc.SetBrush(wx.BLACK_BRUSH) 1044 1045 1046 def OnDraw(self, dc): 1047 xd,yd=self.GetViewStart() 1048 st=0 # 0=normal, 1=highlight 1049 dc.BeginDrawing() 1050 dc.SetBackgroundMode(wx.SOLID) 1051 dc.SetFont(self.font) 1052 for line in range(yd, min(self.datalines, yd+self.height/self.charheight+1)): 1053 # address 1054 self._setnormal(dc) 1055 st=0 1056 dc.DrawText("%08X" % (line*16), 0, line*self.charheight) 1057 # bytes 1058 for i in range(16): 1059 pos=line*16+i 1060 if pos>=len(self.data): 1061 break 1062 hl=self._ishighlighted(pos) 1063 if hl!=st: 1064 if hl: 1065 st=1 1066 self._sethighlight(dc) 1067 else: 1068 st=0 1069 self._setnormal(dc) 1070 if hl: 1071 space="" 1072 if i<15: 1073 if self._ishighlighted(pos+1): 1074 space=" " 1075 if i==7: 1076 space=" " 1077 else: 1078 space="" 1079 c=self.data[pos] 1080 dc.DrawText("%02X%s" % (ord(c),space), (10+(3*i)+(i>=8))*self.charwidth, line*self.charheight) 1081 if not (ord(c)>=32 and string.printable.find(c)>=0): 1082 c='.' 1083 dc.DrawText(c, (10+(3*16)+2+i)*self.charwidth, line*self.charheight) 1084 1085 ## if self.hasfocus: 1086 ## self._setstatus(dc) 1087 ## w,h=self.GetClientSizeTuple() 1088 ## dc.DrawRectangle(0,h-self.charheight+yd*self.charheight,self.widthinchars*self.charwidth,self.charheight) 1089 ## dc.DrawText("A test of stuff "+`yd`, 0, h-self.charheight+yd*self.charheight) 1090 1091 dc.EndDrawing() 1092 1093 def updatebuffer(self): 1094 if self.buffer is None or \ 1095 self.buffer.GetWidth()!=self.width or \ 1096 self.buffer.GetHeight()!=self.height: 1097 if self.buffer is not None: 1098 del self.buffer 1099 self.buffer=wx.EmptyBitmap(self.width, self.height) 1100 1101 mdc=wx.MemoryDC() 1102 mdc.SelectObject(self.buffer) 1103 mdc.SetBackground(wx.TheBrushList.FindOrCreateBrush(self.GetBackgroundColour(), wx.SOLID)) 1104 mdc.Clear() 1105 self.PrepareDC(mdc) 1106 self.OnDraw(mdc) 1107 mdc.SelectObject(wx.NullBitmap) 1108 del mdc 1109 1110 def OnPaint(self, event): 1111 if self.needsupdate: 1112 self.needsupdate=False 1113 self.updatebuffer() 1114 dc=wx.PaintDC(self) 1115 dc.BeginDrawing() 1116 dc.DrawBitmap(self.buffer, 0, 0, False) 1117 dc.EndDrawing() 1118 1119 def OnScrollWin(self, event): 1120 self.needsupdate=True 1121 self.Refresh() # clear whole widget 1122 event.Skip() # default event handlers now do scrolling etc 1123 1124 class HexEditorDialog(wx.Dialog): 1125 _pane_widths=[-2, -3, -4] 1126 _pos_pane_index=0 1127 _sel_pane_index=1 1128 _val_pane_index=2 1129 def __init__(self, parent, data='', title='BitPim Hex Editor', helpd_id=-1): 1130 super(HexEditorDialog, self).__init__(parent, -1, title, 1131 size=(500, 500), 1132 style=wx.DEFAULT_DIALOG_STYLE|\ 1133 wx.RESIZE_BORDER) 1134 self._status_bar=wx.StatusBar(self, -1) 1135 self._status_bar.SetFieldsCount(len(self._pane_widths)) 1136 self._status_bar.SetStatusWidths(self._pane_widths) 1137 vbs=wx.BoxSizer(wx.VERTICAL) 1138 self._hex_editor=HexEditor(self, _set_pos=self.set_pos, 1139 _set_val=self.set_val, 1140 _set_sel=self.set_sel) 1141 self._hex_editor.SetData(data) 1142 self._hex_editor.SetTitle(title) 1143 vbs.Add(self._hex_editor, 1, wx.EXPAND|wx.ALL, 5) 1144 vbs.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5) 1145 ok_btn=wx.Button(self, wx.ID_OK, 'OK') 1146 vbs.Add(ok_btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5) 1147 vbs.Add(self._status_bar, 0, wx.EXPAND|wx.ALL, 0) 1148 self.SetSizer(vbs) 1149 self.SetAutoLayout(True) 1150 vbs.Fit(self) 1151 def set_pos(self, pos): 1152 """Display the current buffer offset in the format of 1153 Pos: 0x12=18 1154 """ 1155 if pos is None: 1156 s='' 1157 else: 1158 s='Pos: 0x%X=%d'%(pos, pos) 1159 self._status_bar.SetStatusText(s, self._pos_pane_index) 1160 def set_sel(self, sel_start, sel_end): 1161 if sel_start is None or sel_start==-1 or\ 1162 sel_end is None or sel_end ==-1: 1163 s='' 1164 else: 1165 sel_len=sel_end-sel_start 1166 sel_end-=1 1167 s='Sel: 0x%X=%d to 0x%X=%d (0x%X=%d bytes)'%( 1168 sel_start, sel_start, sel_end, sel_end, 1169 sel_len, sel_len) 1170 self._status_bar.SetStatusText(s, self._sel_pane_index) 1171 def set_val(self, v): 1172 if v: 1173 # char 1174 s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0])) 1175 if len(v)>1: 1176 # short 1177 u_s=struct.unpack('<H', v[:struct.calcsize('<H')])[0] 1178 s+=' 0x%04X=%d'%(u_s, u_s) 1179 if len(v)>3: 1180 # int/long 1181 u_i=struct.unpack('<I', v[:struct.calcsize('<I')])[0] 1182 s+=' 0x%08X=%d'%(u_i, u_i) 1183 else: 1184 s='' 1185 self._status_bar.SetStatusText(s, self._val_pane_index) 1186 1187 def set(self, data): 1188 self._hex_editor.SetData(data) 1189 1190 1191 if __name__=='__main__': 1192 import sys 1193 1194 if len(sys.argv)!=2: 1195 print 'Usage:',sys.argv[0],'<File Name>' 1196 sys.exit(1) 1197 app=wx.PySimpleApp() 1198 with guihelper.WXDialogWrapper(HexEditorDialog(None, file(sys.argv[1], 'rb').read(), 1199 sys.argv[1]), 1200 True): 1201 pass 1202 sys.exit(0) 1203
Generated by PyXR 0.9.4