1
2
3
4
5
6
7
8
9
10
11 """A hex editor widget"""
12
13
14 from __future__ import with_statement
15 import string
16 import struct
17
18
19 import wx
20 from wx.lib import masked
21 from wx.lib import scrolledpanel as scrolled
22
23
24 import common
25 import guihelper
26
27
30 self.name=_name
31 self.fields=[]
32 - def set(self, dict):
45 l=[]
46 for f in self.fields:
47 l.append(f.get())
48 return { self.name: l }
49 - def encode(self, data, buffer_offset=0):
62
63
65 """Represent a data item/component with in a record, which is a list of
66 these things.
67 """
68 offset_from_start='From Start'
69 offset_from_prev='From Last Field'
70 string_type='string'
71 numeric_type='numeric'
72 struct_type='struct'
74 self.name=_name
75 self.offset_type=self.offset_from_start
76 self.offset=0
77 self.size=1
78 self.start=self.len=None
79
80 self.unsigned=True
81 self.LE=True
82
83 self.fixed=True
84 self.null_terminated=False
85 self.type=_type
96 type=property(fget=_get_type, fset=_set_type)
98 return { 'name': self.name, 'offset_type': self.offset_type,
99 'offset': self.offset, 'type': self.type }
101 self.name=d.get('name', '<None>')
102 self.offset_type=d.get('offset_type', None)
103 self.offset=d.get('offset', None)
104 self.type=d.get('type', None)
105 - def encode(self, s, start=None):
106 """Encode the value of this item based on the string s"""
107 raise NotImplementedError
108
109
111 _fmts={
112 True: {
113 True: {
114 1: 'B', 2: '<H', 4: '<I' },
115 False: {
116 1: 'B', 2: '>H', 4: '>I' } },
117 False: {
118 True: {
119 1: 'b', 2: '<h', 4: '<i' },
120 False: {
121 1: 'b', 2: '>h', 4: '>i' } } }
122
126 r=super(NumericDataItem, self).get()
127 r.update(
128 { 'unsigned': self.unsigned,
129 'little_endian': self.LE,
130 'size': self.size })
131 return r
133 super(NumericDataItem, self).set(d)
134 if d.get('type', None)!=self.numeric_type:
135 raise TypeError
136 self.unsigned=d.get('unsigned', True)
137 self.LE=d.get('little_endian', True)
138 self.size=d.get('size', 1)
139 - def encode(self, s, start=None):
149
150
155 r=super(StringDataItem, self).get()
156 r.update({ 'fixed': self.fixed, 'size': self.size,
157 'null_terminated': self.null_terminated })
158 return r
160 super(StringDataItem, self).set(d)
161 if d.get('type', None)!=self.string_type:
162 raise TypeError
163 self.fixed=d.get('fixed', True)
164 self.size=d.get('size', 0)
165 self.null_terminated=d.get('null_terminated', False)
166 - def encode(self, s, start=None):
167 if self.offset_type==self.offset_from_start:
168 self.start=self.offset
169 else:
170 if start is None:
171 raise ValueError
172 self.start=start+self.offset
173 if self.fixed:
174
175 if self.size==-1:
176
177 self.len=len(s)-self.offset
178 s0=s[self.start:]
179 else:
180
181 s0=s[self.start:self.start+self.size]
182 self.len=self.size
183 else:
184
185 self.len=ord(s[self.start])
186 s0=s[self.start+1:self.start+1+self.len]
187 if self.null_terminated:
188 i=s0.find('\x00')
189 if i==-1:
190 return s0
191 else:
192 self.len=i
193 return s0[:i]
194 else:
195 return s0
196
197
200 super(GeneralInfoSizer, self).__init__(-1, 2, 5, 5)
201 self.AddGrowableCol(1)
202 self.Add(wx.StaticText(parent, -1, 'Struct Name:'), 0, wx.EXPAND|wx.ALL, 5)
203 self._struct_name=wx.TextCtrl(parent, -1, '')
204 self.Add(self._struct_name, 0, wx.EXPAND|wx.ALL, 5)
205 self.Add(wx.StaticText(parent, -1, 'Field Name:'), 0, wx.EXPAND|wx.ALL, 5)
206 self._name=wx.TextCtrl(parent, -1, '')
207 self.Add(self._name, 0, wx.EXPAND|wx.ALL, 5)
208 self.Add(wx.StaticText(parent, -1, 'Type:'), 0, wx.EXPAND|wx.ALL, 5)
209 self._type=wx.ComboBox(parent, wx.NewId(),
210 choices=[DataItem.numeric_type,
211 DataItem.string_type],
212 value=DataItem.numeric_type,
213 style=wx.CB_DROPDOWN|wx.CB_READONLY)
214 self.Add(self._type, 0, wx.EXPAND|wx.ALL, 5)
215 self.Add(wx.StaticText(parent, -1, 'Offset Type:'), 0, wx.EXPAND|wx.ALL, 5)
216 self._offset_type=wx.ComboBox(parent, -1,
217 value=DataItem.offset_from_start,
218 choices=[DataItem.offset_from_start,
219 DataItem.offset_from_prev],
220 style=wx.CB_DROPDOWN|wx.CB_READONLY)
221 self.Add(self._offset_type, 0, wx.EXPAND|wx.ALL, 5)
222 self.Add(wx.StaticText(parent, -1, 'Offset Value:'), 0, wx.EXPAND|wx.ALL, 5)
223 self._offset=masked.NumCtrl(parent, wx.NewId(),
224 allowNegative=False,
225 min=0)
226 self.Add(self._offset, 0, wx.ALL, 5)
227 self._fields_group=(self._name, self._type, self._offset_type,
228 self._offset)
229 - def set(self, data):
237 - def get(self, data):
243 - def show(self, show_struct=False, show_field=False):
244 self._struct_name.Enable(show_struct)
245 for w in self._fields_group:
246 w.Enable(show_field)
249 struct_name=property(fget=_get_struct_name)
252 type=property(fget=_get_type)
254 return self._type.GetId()
255 type_id=property(fget=_get_type_id)
256
257
259 _sign_choices=['Unsigned', 'Signed']
260 _endian_choices=['Little Endian', 'Big Endian']
261 _size_choices=['1', '2', '4']
263 super(NumericInfoSizer, self).__init__(-1, 2, 5, 5)
264 self.AddGrowableCol(1)
265 self.Add(wx.StaticText(parent, -1, 'Signed:'), 0, wx.EXPAND|wx.ALL, 5)
266 self._sign=wx.ComboBox(parent, -1, value=self._sign_choices[0],
267 choices=self._sign_choices,
268 style=wx.CB_DROPDOWN|wx.CB_READONLY)
269 self.Add(self._sign, 0, wx.EXPAND|wx.ALL, 5)
270 self.Add(wx.StaticText(parent, -1, 'Endian:'), 0, wx.EXPAND|wx.ALL, 5)
271 self._endian=wx.ComboBox(parent, -1, value=self._endian_choices[0],
272 choices=self._endian_choices,
273 style=wx.CB_DROPDOWN|wx.CB_READONLY)
274 self.Add(self._endian, 0, wx.EXPAND|wx.ALL, 5)
275 self.Add(wx.StaticText(parent, -1, 'Size:'), 0, wx.EXPAND|wx.ALL, 5)
276 self._size=wx.ComboBox(parent, -1, value=self._size_choices[0],
277 choices=self._size_choices,
278 style=wx.CB_DROPDOWN|wx.CB_READONLY)
279 self.Add(self._size, 0, wx.EXPAND|wx.ALL, 5)
280 - def set(self, data):
290 - def get(self, data):
295
296
298 _fixed_choices=['Fixed', 'Pascal']
300 super(StringInfoSizer, self).__init__(-1, 2, 5, 5)
301 self.AddGrowableCol(1)
302 self.Add(wx.StaticText(parent, -1, 'Fixed/Pascal:'), 0, wx.EXPAND|wx.ALL, 5)
303 self._fixed=wx.ComboBox(parent, -1, value=self._fixed_choices[0],
304 choices=self._fixed_choices,
305 style=wx.CB_DROPDOWN|wx.CB_READONLY)
306 self.Add(self._fixed, 0, wx.EXPAND|wx.ALL, 5)
307 self.Add(wx.StaticText(parent, -1, 'Max Length:'), 0, wx.EXPAND|wx.ALL, 5)
308 self._max_len=masked.NumCtrl(parent, -1, value=1, min=-1)
309 self.Add(self._max_len, 0, wx.EXPAND|wx.ALL, 5)
310 self.Add(wx.StaticText(parent, -1, 'Null Terminated:'), 0, wx.EXPAND|wx.ALL, 5)
311 self._null_terminated=wx.CheckBox(parent, -1)
312 self.Add(self._null_terminated, 0, wx.EXPAND|wx.ALL, 5)
313 - def set(self, data):
320 - def get(self, data):
325
326
328 _type_choices=['Numeric', 'String']
329 _struct_type='struct'
330 _field_type='field'
332 super(TemplateDialog, self).__init__(parent, -1,
333 'Hex Template Editor',
334 style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
335 self._data=[]
336 self._item_tree=self._numeric_bs=self._string_bs=None
337 self._general_bs=None
338 self._tree_root=None
339 self._field_info=self._field_info_hbs=None
340 self._info_sizer={ NumericDataItem.numeric_type: self._numeric_bs,
341 StringDataItem.string_type: self._string_bs }
342 main_vbs=wx.BoxSizer(wx.VERTICAL)
343 hbs1=wx.BoxSizer(wx.HORIZONTAL)
344 hbs1.Add(self._create_tree_pane(), 1, wx.EXPAND|wx.ALL, 5)
345 hbs1.Add(self._create_info_pane(), 2, wx.EXPAND|wx.ALL, 5)
346 main_vbs.Add(hbs1, 1, wx.EXPAND|wx.ALL, 5)
347 main_vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5)
348 main_vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
349 self.SetSizer(main_vbs)
350 self.SetAutoLayout(True)
351 main_vbs.Fit(self)
352
354 vbs=wx.BoxSizer(wx.VERTICAL)
355 sw=scrolled.ScrolledPanel(self, -1)
356 self._item_tree=wx.TreeCtrl(sw, wx.NewId(),
357 style=wx.TR_DEFAULT_STYLE|wx.TR_HAS_BUTTONS)
358 wx.EVT_TREE_SEL_CHANGED(self, self._item_tree.GetId(),
359 self._OnTreeSel)
360 self._tree_root=self._item_tree.AddRoot('Data Templates')
361 sw_bs=wx.BoxSizer(wx.VERTICAL)
362 sw_bs.Add(self._item_tree, 1, wx.EXPAND|wx.ALL, 0)
363 sw.SetSizer(sw_bs)
364 sw.SetAutoLayout(True)
365 sw_bs.Fit(sw)
366 sw.SetupScrolling()
367 vbs.Add(sw, 1, wx.EXPAND|wx.ALL, 5)
368 hbs=wx.BoxSizer(wx.HORIZONTAL)
369 hbs.Add(wx.Button(self, wx.ID_ADD, 'Add'), 0, wx.EXPAND|wx.ALL, 5)
370 hbs.Add(wx.Button(self, wx.ID_DELETE, 'Delete'), 0, wx.EXPAND|wx.ALL, 5)
371 wx.EVT_BUTTON(self, wx.ID_ADD, self._OnAdd)
372 wx.EVT_BUTTON(self, wx.ID_DELETE, self._OnDelete)
373 vbs.Add(hbs, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5)
374 return vbs
376
377 vbs=wx.BoxSizer(wx.VERTICAL)
378 hbs=wx.BoxSizer(wx.HORIZONTAL)
379
380 static_bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Type'),
381 wx.VERTICAL)
382 self._general_bs=GeneralInfoSizer(self)
383 wx.EVT_COMBOBOX(self, self._general_bs.type_id, self._OnTypeChanged)
384 static_bs.Add(self._general_bs, 0, wx.EXPAND|wx.ALL, 5)
385 hbs.Add(static_bs, 0, wx.ALL, 5)
386
387 self._field_info=wx.StaticBoxSizer(wx.StaticBox(self, -1, 'Field Info'),
388 wx.VERTICAL)
389
390 self._numeric_bs=NumericInfoSizer(self)
391 self._field_info.Add(self._numeric_bs, 0, wx.EXPAND|wx.ALL, 5)
392 self._string_bs=StringInfoSizer(self)
393 self._field_info.Add(self._string_bs, 0, wx.EXPAND|wx.ALL, 5)
394 hbs.Add(self._field_info, 0, wx.ALL, 5)
395 vbs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
396 hbs1=wx.BoxSizer(wx.HORIZONTAL)
397 hbs1.Add(wx.Button(self, wx.ID_SAVE, 'Set'), 0, wx.EXPAND|wx.ALL, 5)
398 hbs1.Add(wx.Button(self, wx.ID_REVERT, 'Revert'), 0, wx.EXPAND|wx.ALL, 5)
399 wx.EVT_BUTTON(self, wx.ID_SAVE, self._OnSave)
400 wx.EVT_BUTTON(self, wx.ID_REVERT, self._OnRevert)
401 vbs.Add(hbs1, 0, wx.ALIGN_CENTER_HORIZONTAL, 0)
402 self._field_info_hbs=hbs
403 return vbs
404 - def _show_field_info(self, _struct=False, field=False,
405 numeric_field=False, string_field=False):
406
407 self._general_bs.show(_struct, field)
408 self._field_info.Show(self._numeric_bs, numeric_field)
409 self._field_info.Show(self._string_bs, string_field)
410 self._field_info.Layout()
411 self._field_info_hbs.Layout()
413
414 self._item_tree.DeleteChildren(self._tree_root)
415 for i,e in enumerate(self._data):
416 item=self._item_tree.AppendItem(self._tree_root, e.name)
417 self._item_tree.SetPyData(item, { 'type': self._struct_type,
418 'index': i })
419 for i1,e1 in enumerate(e.fields):
420 field_item=self._item_tree.AppendItem(item, e1.name)
421 self._item_tree.SetPyData(field_item, { 'type': self._field_type,
422 'index': i,
423 'field_index': i1 })
424 self.expand()
443 sel_idx=self._item_tree.GetSelection()
444 if not sel_idx.IsOk():
445 return
446 if sel_idx==self._tree_root:
447
448 struct_item=DataStruct('New Struct')
449 self._data.append(struct_item)
450 else:
451
452 data_item=self._item_tree.GetPyData(sel_idx)
453 item=NumericDataItem('New Field')
454 self._data[data_item['index']].fields.append(item)
455 self._populate()
457 sel_idx=self._item_tree.GetSelection()
458 if not sel_idx.IsOk():
459 return
460 node_data=self._item_tree.GetPyData(sel_idx)
461 if node_data is None:
462 return
463 if node_data['type']==self._field_type:
464
465 del self._data[node_data['index']].fields[node_data['field_index']]
466 else:
467
468 del self._data[node_data['index']]
469
470 self._populate()
472 sel_idx=self._item_tree.GetSelection()
473 if not sel_idx.IsOk():
474 return
475 node_data=self._item_tree.GetPyData(sel_idx)
476 if node_data is None:
477 return
478
479 self._data[node_data['index']].name=self._general_bs.struct_name
480 if node_data['type']==self._field_type:
481 data_item=self._data[node_data['index']].\
482 fields[node_data['field_index']]
483 data_item=self._general_bs.get(data_item)
484 if data_item.type==DataItem.numeric_type:
485 data_item=self._numeric_bs.get(data_item)
486 else:
487 data_item=self._string_bs.get(data_item)
488 self._data[node_data['index']].fields[node_data['field_index']]=data_item
489 self._item_tree.SetItemText(self._item_tree.GetItemParent(sel_idx),
490 self._data[node_data['index']].name)
491 self._item_tree.SetItemText(sel_idx, data_item.name)
492 else:
493 self._item_tree.SetItemText(sel_idx, self._data[node_data['index']].name)
494
496 sel_idx=self._item_tree.GetSelection()
497 if not sel_idx.IsOk():
498 return
499 node_data=self._item_tree.GetPyData(sel_idx)
500 if node_data is None:
501 self._show_field_info()
502 else:
503 self._populate_struct(node_data['index'])
504 if node_data['type']==self._field_type:
505 self._populate_each(node_data['index'], node_data['field_index'])
506
508 sel_idx=evt.GetItem()
509 if not sel_idx.IsOk():
510
511 return
512 item_data=self._item_tree.GetPyData(sel_idx)
513 if item_data is None:
514 self._show_field_info()
515 else:
516 self._populate_struct(item_data['index'])
517 if item_data['type']==self._field_type:
518 self._populate_each(item_data['index'], item_data['field_index'])
520
521 self._item_tree.Expand(self._tree_root)
522 (id, cookie)=self._item_tree.GetFirstChild(self._tree_root)
523 while id.IsOk():
524 self._item_tree.Expand(id)
525 (id, cookie)=self._item_tree.GetNextChild(self._tree_root, cookie)
531
532
534
535 _addr_range=xrange(8)
536 _hex_range_start=10
537 _hex_range_start2=33
538 _hex_range=xrange(_hex_range_start, 58)
539 _ascii_range_start=60
540 _ascii_range=xrange(60, 76)
541
542 - def __init__(self, parent, id=-1, style=wx.WANTS_CHARS,
543 _set_pos=None, _set_sel=None, _set_val=None):
544 wx.ScrolledWindow.__init__(self, parent, id, style=style)
545 self.parent=parent
546 self.data=""
547 self.title=""
548 self.buffer=None
549 self.hasfocus=False
550 self.dragging=False
551 self.current_ofs=None
552 self._module=None
553 self._templates=[]
554 self._search_string=None
555
556 self.set_pos=_set_pos or self._set_pos
557 self.set_val=_set_val or self._set_val
558 self.set_sel=_set_sel or self._set_sel
559
560 self.SetBackgroundColour("WHITE")
561 self.SetCursor(wx.StockCursor(wx.CURSOR_IBEAM))
562 self.sethighlight(wx.NamedColour("BLACK"), wx.NamedColour("YELLOW"))
563 self.setnormal(wx.NamedColour("BLACK"), wx.NamedColour("WHITE"))
564 self.setfont(wx.TheFontList.FindOrCreateFont(10, wx.MODERN, wx.NORMAL, wx.NORMAL))
565 self.OnSize(None)
566 self.highlightrange(None, None)
567
568 self._create_context_menu()
569 self._map_events()
570
572 wx.EVT_SCROLLWIN(self, self.OnScrollWin)
573 wx.EVT_PAINT(self, self.OnPaint)
574 wx.EVT_SIZE(self, self.OnSize)
575 wx.EVT_ERASE_BACKGROUND(self, self.OnEraseBackground)
576 wx.EVT_SET_FOCUS(self, self.OnGainFocus)
577 wx.EVT_KILL_FOCUS(self, self.OnLoseFocus)
578 wx.EVT_LEFT_DOWN(self, self.OnStartSelection)
579 wx.EVT_LEFT_UP(self, self.OnEndSelection)
580 wx.EVT_MOTION(self, self.OnMakeSelection)
581 wx.EVT_RIGHT_UP(self, self.OnRightClick)
582
584 self._reload_menu_id=self._apply_menu_id=None
585 menu_items=(
586 ('File', (('Load', self.OnLoadFile),
587 ('Save As', self.OnSaveAs),
588 ('Save Selection As', self.OnSaveSelection),
589 ('Save Hexdump As', self.OnSaveHexdumpAs))),
590 ('Set Selection', (('Start', self.OnStartSelMenu),
591 ('End', self.OnEndSelMenu))),
592 ('Value', self.OnViewValue),
593 ('Search', (('Search', self.OnSearch),
594 ('Search Again', self.OnSearchAgain))),
595 ('Import Python Module', self.OnImportModule),
596 ('Reload Python Module', self.OnReloadModule, '_reload_menu_id'),
597 ('Apply Python Func', self.OnApplyFunc, '_apply_menu_id'),
598 ('Template', (('Load', self.OnTemplateLoad),
599 ('Save As', self.OnTemplateSaveAs),
600 ('Edit', self.OnTemplateEdit),
601 ('Apply', self.OnTemplateApply)))
602 )
603 self._bgmenu=wx.Menu()
604 for menu_item in menu_items:
605 if isinstance(menu_item[1], tuple):
606
607 sub_menu=wx.Menu()
608 for submenu_item in menu_item[1]:
609 id=wx.NewId()
610 sub_menu.Append(id, submenu_item[0])
611 wx.EVT_MENU(self, id, submenu_item[1])
612 self._bgmenu.AppendMenu(wx.NewId(), menu_item[0], sub_menu)
613 else:
614
615 id=wx.NewId()
616 self._bgmenu.Append(id, menu_item[0])
617 wx.EVT_MENU(self, id, menu_item[1])
618 if len(menu_item)>2:
619
620 setattr(self, menu_item[2], id)
621
627
630
635
640 - def _set_sel(self, sel_start, sel_end):
644
646 """Convert an x,y point to (char, line)
647 """
648 return x/self.charwidth, y/self.charheight
649 - def _to_xy(self, char, line):
650 return char*self.charwidth, line*self.charheight
660 c,l=self._to_char_line(evt.GetX(), evt.GetY())
661 self.GetCaret().Move(self._to_xy(c, l))
662 x0, y0=self.GetViewStart()
663 char_x=c+x0
664 line_y=l+y0
665 return self._to_buffer_offset(char_x, line_y)
666 _value_formats=(
667 ('unsigned char', 'B', struct.calcsize('B')),
668 ('signed char', 'b', struct.calcsize('b')),
669 ('LE unsigned short', '<H', struct.calcsize('<H')),
670 ('LE signed short', '<h', struct.calcsize('<h')),
671 ('BE unsigned short', '>H', struct.calcsize('>H')),
672 ('BE signed short', '>h', struct.calcsize('>h')),
673 ('LE unsigned int', '<I', struct.calcsize('<I')),
674 ('LE signed int', '<i', struct.calcsize('<i')),
675 ('BE unsigned int', '>I', struct.calcsize('>I')),
676 ('BE signed int', '>i', struct.calcsize('>i')),
677 )
679 """ Generate the values of various number formats starting at the
680 current offset.
681 """
682 n=_data[_ofs:]
683 len_n=len(n)
684 s='0x%X=%d'%(_ofs, _ofs)
685 res=[{ 'Data Offset': s}, {'':''} ]
686 for i,e in enumerate(self._value_formats):
687 if len_n<e[2]:
688 continue
689 v=struct.unpack(e[1], n[:e[2]])[0]
690 if i%2:
691 s='%d'%v
692 else:
693 fmt='0x%0'+str(e[2]*2)+'X=%d'
694 s=fmt%(v,v)
695 res.append({ e[0]: s })
696 return res
697
699
700 if self.highlightstart is None or self.highlightstart==-1 or \
701 self.highlightend is None or self.highlightend==-1:
702
703 _data=self.data[self.current_ofs:]
704 _ofs=self.current_ofs
705 else:
706 _data=self.data[self.highlightstart:self.highlightend]
707 _ofs=self.highlightstart
708 for f in self._templates:
709 if f.name==template_name:
710 l=[{ 'Template': f.name },
711 { 'Data Offset': '0x%04X=%d'%(_ofs, _ofs) }]
712 return l+f.encode(_data, _ofs)
713 return []
714
716 """ Display the results from applying a Python routine over the data
717 """
718 s=''
719 for d in result:
720 for k,e in d.items():
721 s+=k+':\t'+e+'\n'
722 guihelper.MessageDialog(self, s, 'Results', style=wx.OK)
723
725 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load',
726 style=wx.OPEN|wx.FILE_MUST_EXIST),
727 True) as (dlg, retcode):
728 if retcode==wx.ID_OK:
729 self.SetData(file(dlg.GetPath(), 'rb').read())
731 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
732 style=wx.SAVE|wx.OVERWRITE_PROMPT),
733 True) as (dlg, retcode):
734 if retcode==wx.ID_OK:
735 file(dlg.GetPath(), 'wb').write(self.data)
737 res=""
738 l=len(self.data)
739 if self.title:
740 res += self.title+": "+`l`+" bytes\n"
741 res += "<#! !#>\n"
742 pos=0
743 while pos<l:
744 text="%08X "%(pos)
745 line=self.data[pos:pos+16]
746 for i in range(len(line)):
747 text+="%02X "%(ord(line[i]))
748 text+=" "*(16-len(line))
749 text+=" "
750 for i in range(len(line)):
751 c=line[i]
752 if (ord(c)>=32 and string.printable.find(c)>=0):
753 text+=c
754 else:
755 text+='.'
756 res+=text+"\n"
757 pos+=16
758 return res
759
761 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
762 style=wx.SAVE|wx.OVERWRITE_PROMPT),
763 True) as (dlg, retcode):
764 if retcode==wx.ID_OK:
765 file(dlg.GetPath(), 'wb').write(self.hexdumpdata())
767 if self.highlightstart is None or self.highlightstart==-1 or \
768 self.highlightend is None or self.highlightend==-1:
769
770 return
771 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
772 style=wx.SAVE|wx.OVERWRITE_PROMPT),
773 True) as (dlg, retcode):
774 if retcode==wx.ID_OK:
775 file(dlg.GetPath(), 'wb').write(
776 self.data[self.highlightstart:self.highlightend])
777
779 try:
780 reload(self._module)
781 except:
782 self._module=None
783 guihelper.MessageDialog(self, 'Failed to reload module',
784 'Reload Module Error',
785 style=wx.OK|wx.ICON_ERROR)
786
788 choices=[x for x in dir(self._module) \
789 if callable(getattr(self._module, x))]
790 with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a function to apply:',
791 'Apply Python Func',
792 choices),
793 True) as (dlg, retcode):
794 if retcode==wx.ID_OK:
795 try:
796 res=getattr(self._module, dlg.GetStringSelection())(
797 self, self.data, self.current_ofs)
798 self._display_result(res)
799 except:
800 guihelper.MessageDialog(self, 'Apply Func raised an exception',
801 'Apply Func Error',
802 style=wx.OK|wx.ICON_ERROR)
803
805 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter the name of a Python Module:',
806 'Module Import'),
807 True) as (dlg, retcode):
808 if retcode==wx.ID_OK:
809 try:
810 self._module=common.importas(dlg.GetValue())
811 except ImportError:
812 self._module=None
813 guihelper.MessageDialog(self, 'Failed to import module: '+dlg.GetValue(),
814 'Module Import Error',
815 style=wx.OK|wx.ICON_ERROR)
816
818 ofs=self.current_ofs
819 if ofs is not None:
820 self.highlightstart=ofs
821 self.needsupdate=True
822 self.Refresh()
823 self.set_sel(self.highlightstart, self.highlightend)
824
826 ofs=self.current_ofs
827 if ofs is not None:
828 self.highlightend=ofs+1
829 self.needsupdate=True
830 self.Refresh()
831 self.set_sel(self.highlightstart, self.highlightend)
832
837
839 self.highlightstart=self.highlightend=None
840 ofs=self._set_and_move(evt)
841 if ofs is not None:
842 self.highlightstart=ofs
843 self.dragging=True
844 self.set_val(self.data[ofs:])
845 else:
846 self.set_val(None)
847 self.needsupdate=True
848 self.Refresh()
849 self.set_pos(ofs)
850 self.set_sel(self.highlightstart, self.highlightend)
851
853 if not self.dragging:
854 return
855 ofs=self._set_and_move(evt)
856 if ofs is not None:
857 self.highlightend=ofs+1
858 self.needsupdate=True
859 self.Refresh()
860 self.set_pos(ofs)
861 self.set_sel(self.highlightstart, self.highlightend)
867
869 self.current_ofs=self._set_and_move(evt)
870 if self.current_ofs is None:
871 self.set_val(None)
872 else:
873 self.set_val(self.data[self.current_ofs:])
874 self.set_pos(self.current_ofs)
875 self._bgmenu.Enable(self._apply_menu_id, self._module is not None)
876 self._bgmenu.Enable(self._reload_menu_id, self._module is not None)
877 self.PopupMenu(self._bgmenu, evt.GetPosition())
878
880 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to load',
881 wildcard='*.tmpl',
882 style=wx.OPEN|wx.FILE_MUST_EXIST),
883 True) as (dlg, retcode):
884 if retcode==wx.ID_OK:
885 result={}
886 try:
887 execfile(dlg.GetPath())
888 except UnicodeError:
889 common.unicode_execfile(dlg.GetPath())
890 exist_keys={}
891 for i,e in enumerate(self._templates):
892 exist_keys[e.name]=i
893 for d in result['templates']:
894 data_struct=DataStruct('new struct')
895 data_struct.set(d)
896 if exist_keys.has_key(data_struct.name):
897 self._templates[exist_keys[data_struct.name]]=data_struct
898 else:
899 self._templates.append(data_struct)
900
902 with guihelper.WXDialogWrapper(wx.FileDialog(self, 'Select a file to save',
903 wildcard='*.tmpl',
904 style=wx.SAVE|wx.OVERWRITE_PROMPT),
905 True) as (dlg, retcode):
906 if retcode==wx.ID_OK:
907 r=[x.get() for x in self._templates]
908 common.writeversionindexfile(dlg.GetPath(),
909 { 'templates': r }, 1)
910
912 if not self._templates:
913
914 return
915 choices=[x.name for x in self._templates]
916 with guihelper.WXDialogWrapper(wx.SingleChoiceDialog(self, 'Select a template to apply:',
917 'Apply Data Template',
918 choices),
919 True) as (dlg, retcode):
920 if retcode==wx.ID_OK:
921 try:
922 res=self._apply_template(dlg.GetStringSelection())
923 self._display_result(res)
924 except:
925 guihelper.MessageDialog(self, 'Apply Template raised an exception',
926 'Apply Template Error',
927 style=wx.OK|wx.ICON_ERROR),
928
930 dlg=TemplateDialog(self)
931 dlg.set(self._templates)
932 with guihelper.WXDialogWrapper(dlg, True) as (dlg, retcode):
933 if retcode==wx.ID_OK:
934 self._templates=dlg.get()
935
937 with guihelper.WXDialogWrapper(wx.TextEntryDialog(self, 'Enter data to search (1 0x23 045 ...):',
938 'Search Data'),
939 True) as (dlg, retcode):
940 if retcode==wx.ID_OK:
941 l=dlg.GetValue().split(' ')
942 s=''
943 for e in l:
944 if e[0:2]=='0x':
945 s+=chr(int(e, 16))
946 elif e[0]=='0':
947 s+=chr(int(e, 8))
948 else:
949 s+=chr(int(e))
950 i=self.data[self.current_ofs:].find(s)
951 if i!=-1:
952 self._search_string=s
953 self.highlightstart=i+self.current_ofs
954 self.highlightend=self.highlightstart+len(s)
955 self.needsupdate=True
956 self.Refresh()
957 self.set_sel(self.highlightstart, self.highlightend)
958 else:
959 self._search_string=None
960
962 if self._search_string is not None:
963 i=self.data[self.current_ofs:].find(self._search_string)
964 if i==-1:
965 return
966 self.highlightstart=i+self.current_ofs
967 self.highlightend=self.highlightstart+len(self._search_string)
968 self.needsupdate=True
969 self.Refresh()
970 self.set_sel(self.highlightstart, self.highlightend)
971
973
974
975
976 if evt is None:
977 self.width=(self.widthinchars+3)*self.charwidth
978 self.height=self.charheight*20
979 self.SetClientSize((self.width, self.height))
980 self.SetCaret(wx.Caret(self, (self.charwidth, self.charheight)))
981 self.GetCaret().Show(True)
982 else:
983 self.width,self.height=self.GetClientSizeTuple()
984 self.needsupdate=True
985
987 self.hasfocus=True
988 self.needsupdate=True
989 self.Refresh()
990
992 self.hasfocus=False
993 self.needsupdate=True
994 self.Refresh()
995
997 self.needsupdate=True
998 self.highlightstart=start
999 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
1006 return pos>=self.highlightstart and pos<self.highlightend
1007
1009 self.highlight=foreground,background
1010
1011 - def setnormal(self, foreground, background):
1012 self.normal=foreground,background
1013
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
1031
1033 dc.SetTextForeground(self.normal[0])
1034 dc.SetTextBackground(self.normal[1])
1035
1037 dc.SetTextForeground(self.highlight[0])
1038 dc.SetTextBackground(self.highlight[1])
1039
1041 dc.SetTextForeground(self.normal[1])
1042 dc.SetTextBackground(self.normal[0])
1043 dc.SetBrush(wx.BLACK_BRUSH)
1044
1045
1047 xd,yd=self.GetViewStart()
1048 st=0
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
1054 self._setnormal(dc)
1055 st=0
1056 dc.DrawText("%08X" % (line*16), 0, line*self.charheight)
1057
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
1086
1087
1088
1089
1090
1091 dc.EndDrawing()
1092
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
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
1123
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)
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)
1172 if v:
1173
1174 s='Val: 0x%02X=%d'%(ord(v[0]), ord(v[0]))
1175 if len(v)>1:
1176
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
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):
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