metasm 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'Qt4'
|
|
8
|
+
|
|
9
|
+
module Metasm
|
|
10
|
+
module Gui
|
|
11
|
+
|
|
12
|
+
module Protect
|
|
13
|
+
@@lasterror = Time.now
|
|
14
|
+
def protect
|
|
15
|
+
yield
|
|
16
|
+
rescue Object
|
|
17
|
+
puts $!.message, $!.backtrace # also dump on stdout, for c/c
|
|
18
|
+
delay = Time.now-@@lasterror
|
|
19
|
+
sleep 1-delay if delay < 1 # msgbox flood protection
|
|
20
|
+
@@lasterror = Time.now
|
|
21
|
+
messagebox([$!.message, $!.backtrace].join("\n"), $!.class.name)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module Msgbox
|
|
26
|
+
include Protect
|
|
27
|
+
|
|
28
|
+
# shows a message box (non-modal)
|
|
29
|
+
# args: message, title/optionhash
|
|
30
|
+
def messagebox(text, opts={})
|
|
31
|
+
opts = {:title => opts} if opts.kind_of? String
|
|
32
|
+
mbox = Qt::MessageBox.new#(self)
|
|
33
|
+
mbox.text = text
|
|
34
|
+
mbox.window_title = opts[:title] if opts[:title]
|
|
35
|
+
mbox.window_modality = Qt::NonModal
|
|
36
|
+
mbox.show
|
|
37
|
+
mbox
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# asks for user input, yields the result (unless 'cancel' clicked)
|
|
41
|
+
# args: prompt, :text => default text, :title => title
|
|
42
|
+
def inputbox(prompt, opts={})
|
|
43
|
+
ibox = Qt::InputDialog.new#(self)
|
|
44
|
+
ibox.label_text = prompt
|
|
45
|
+
ibox.window_title = opts[:title] if opts[:title]
|
|
46
|
+
ibox.text_value = opts[:text] if opts[:text]
|
|
47
|
+
connect(ibox, SIGNAL('TextValueSelected(v)')) { |v| protect { yield v } }
|
|
48
|
+
ibox.show
|
|
49
|
+
ibox
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@@dialogfilefolder = nil
|
|
53
|
+
|
|
54
|
+
# asks to chose a file to open, yields filename
|
|
55
|
+
# args: title, :path => path
|
|
56
|
+
def openfile(title, opts={})
|
|
57
|
+
f = Qt::FileDialog.get_open_file_name(nil, title, @@dialogfilefolder)
|
|
58
|
+
if f and f != ''
|
|
59
|
+
@@dialogfilefolder = File.dirname(f)
|
|
60
|
+
protect { yield f }
|
|
61
|
+
end
|
|
62
|
+
f # useless, dialog is modal
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# same as openfile, but for writing a (new) file
|
|
66
|
+
def savefile(title, opts={})
|
|
67
|
+
f = Qt::FileDialog.get_save_file_name(nil, title, @@dialogfilefolder)
|
|
68
|
+
if f and f != ''
|
|
69
|
+
@@dialogfilefolder = File.dirname(f)
|
|
70
|
+
protect { yield f }
|
|
71
|
+
end
|
|
72
|
+
f # useless, dialog is modal
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# shows a window with a list of items
|
|
76
|
+
# the list is an array of arrays, displayed as String
|
|
77
|
+
# the first array is the column names
|
|
78
|
+
# each item clicked yields the block with the selected iterator, double-click also close the popup
|
|
79
|
+
# args: title, [[col0 title, col1 title...], [col0 val0, col1 val0...], [val1], [val2]...]
|
|
80
|
+
def listwindow(title, list, h={})
|
|
81
|
+
l = Qt::TreeWidget.new#(self)
|
|
82
|
+
l.window_title = title
|
|
83
|
+
|
|
84
|
+
cols = list.shift
|
|
85
|
+
#l.column_count = cols.length
|
|
86
|
+
l.header_labels = cols
|
|
87
|
+
list.each { |e|
|
|
88
|
+
i = Qt::TreeWidgetItem.new
|
|
89
|
+
e.length.times { |idx| i.set_text(idx, e[idx].to_s) }
|
|
90
|
+
l.add_top_level_item i
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
connect(l, SIGNAL('itemActivated(QTreeWidgetItem*,int)')) { |item, col|
|
|
94
|
+
next if not item.is_selected
|
|
95
|
+
next if not idx = l.index_of_top_level_item(item)
|
|
96
|
+
protect { yield(list[idx].map { |e| e.to_s }) } #if iter = treeview.selection.selected
|
|
97
|
+
}
|
|
98
|
+
connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)')) { l.close }
|
|
99
|
+
l.resize(cols.length*120, 400)
|
|
100
|
+
l.show if not h[:noshow]
|
|
101
|
+
l
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# a widget that holds many other widgets, and displays only one of them at a time
|
|
106
|
+
class ContainerChoiceWidget < Qt::StackedWidget
|
|
107
|
+
include Msgbox
|
|
108
|
+
|
|
109
|
+
attr_accessor :views, :view_indexes
|
|
110
|
+
def initialize(*a)
|
|
111
|
+
super()
|
|
112
|
+
@views = {}
|
|
113
|
+
@view_indexes = []
|
|
114
|
+
|
|
115
|
+
initialize_widget(*a)
|
|
116
|
+
initialize_visible if respond_to? :initialize_visible
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def view(i)
|
|
120
|
+
@views[i]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def showview(i)
|
|
124
|
+
set_current_index @view_indexes.index(i)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def addview(name, w)
|
|
128
|
+
@view_indexes << name
|
|
129
|
+
@views[name] = w
|
|
130
|
+
add_widget w
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def curview
|
|
134
|
+
@views[curview_index]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def curview_index
|
|
138
|
+
@view_indexes[current_index]
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
class ContainerVBoxWidget < Qt::VBoxLayout
|
|
143
|
+
def initialize(*a)
|
|
144
|
+
super()
|
|
145
|
+
|
|
146
|
+
signal_connect('realize') { initialize_visible } if respond_to? :initialize_visible
|
|
147
|
+
|
|
148
|
+
signal_connect('size_request') { |w, alloc| resize(*alloc) } if respond_to? :resize
|
|
149
|
+
|
|
150
|
+
self.spacing = 2
|
|
151
|
+
|
|
152
|
+
initialize_widget(*a)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
class DrawableWidget < Qt::Widget
|
|
157
|
+
include Msgbox
|
|
158
|
+
|
|
159
|
+
attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word
|
|
160
|
+
# this hash is used to determine the colors of the GUI elements (background, caret, ...)
|
|
161
|
+
# modifications to it are only useful before the widget is first rendered (IE before GUI.main)
|
|
162
|
+
attr_accessor :default_color_association
|
|
163
|
+
|
|
164
|
+
# keypress event keyval traduction table
|
|
165
|
+
# RHA no way to enumerate all Key_* constants, they are handled in Qt.const_missing
|
|
166
|
+
Keyboard_trad = %w[
|
|
167
|
+
Escape Tab Backtab Backspace Return Enter Insert Delete Pause Print SysReq Clear Home End Left Up Right
|
|
168
|
+
Down PageUp PageDown Shift Control Meta Alt AltGr CapsLock NumLock ScrollLock
|
|
169
|
+
F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 F32 F33 F34 F35 Super_L Super_R
|
|
170
|
+
Menu Hyper_L Hyper_R Help Direction_L Direction_R nobreakspace exclamdown cent sterling currency yen brokenbar section diaeresis copyright
|
|
171
|
+
ordfeminine guillemotleft notsign hyphen registered macron degree plusminus twosuperior threesuperior acute mu paragraph periodcentered cedilla onesuperior
|
|
172
|
+
masculine guillemotright onequarter onehalf threequarters questiondown Agrave Aacute Acircumflex Atilde Adiaeresis Aring AE Ccedilla Egrave Eacute
|
|
173
|
+
Ecircumflex Ediaeresis Igrave Iacute Icircumflex Idiaeresis ETH Ntilde Ograve Oacute Ocircumflex Otilde Odiaeresis multiply Ooblique Ugrave
|
|
174
|
+
Uacute Ucircumflex Udiaeresis Yacute THORN ssharp division ydiaeresis Multi_key Codeinput SingleCandidate MultipleCandidate PreviousCandidate Mode_switch Kanji Muhenkan
|
|
175
|
+
Henkan Romaji Hiragana Katakana Hiragana_Katakana Zenkaku Hankaku Zenkaku_Hankaku Touroku Massyo Kana_Lock Kana_Shift Eisu_Shift Eisu_toggle Hangul Hangul_Start
|
|
176
|
+
Hangul_End Hangul_Hanja Hangul_Jamo Hangul_Romaja Hangul_Jeonja Hangul_Banja Hangul_PreHanja Hangul_PostHanja Hangul_Special Dead_Grave Dead_Acute Dead_Circumflex Dead_Tilde Dead_Macron Dead_Breve Dead_Abovedot
|
|
177
|
+
Dead_Diaeresis Dead_Abovering Dead_Doubleacute Dead_Caron Dead_Cedilla Dead_Ogonek Dead_Iota Dead_Voiced_Sound Dead_Semivoiced_Sound Dead_Belowdot Dead_Hook Dead_Horn Back Forward Stop Refresh
|
|
178
|
+
VolumeDown VolumeMute VolumeUp BassBoost BassUp BassDown TrebleUp TrebleDown MediaPlay MediaStop MediaPrevious MediaNext MediaRecord HomePage Favorites Search
|
|
179
|
+
Standby OpenUrl LaunchMail LaunchMedia Launch0 Launch1 Launch2 Launch3 Launch4 Launch5 Launch6 Launch7 Launch8 Launch9 LaunchA LaunchB
|
|
180
|
+
LaunchC LaunchD LaunchE LaunchF MediaLast unknown Call Context1 Context2 Context3 Context4 Flip Hangup No Select Yes
|
|
181
|
+
Execute Printer Play Sleep Zoom Cancel
|
|
182
|
+
].inject({}) { |h, cst|
|
|
183
|
+
v = Qt.const_get("Key_#{cst}").to_i # AONETUHANOTEUHATNOHEU Qt::Enum != Fixnum
|
|
184
|
+
key = cst.downcase.to_sym
|
|
185
|
+
key = { :pageup => :pgup, :pagedown => :pgdown, :escape => :esc, :return => :enter }.fetch(key, key)
|
|
186
|
+
h.update v => key
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
def initialize(*a)
|
|
190
|
+
@parent_widget = nil
|
|
191
|
+
|
|
192
|
+
@caret_x = @caret_y = 0 # text cursor position
|
|
193
|
+
@oldcaret_x = @oldcaret_y = 1
|
|
194
|
+
@hl_word = nil
|
|
195
|
+
|
|
196
|
+
#@layout = Pango::Layout.new Gdk::Pango.context # text rendering
|
|
197
|
+
|
|
198
|
+
@color = {}
|
|
199
|
+
@default_color_association = {:background => :palegrey}
|
|
200
|
+
|
|
201
|
+
if a.last.kind_of? Qt::Widget
|
|
202
|
+
super(a.last)
|
|
203
|
+
else
|
|
204
|
+
super()
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
{ :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
|
|
208
|
+
:red => 'f00', :darkred => '800', :palered => 'fcc',
|
|
209
|
+
:green => '0f0', :darkgreen => '080', :palegreen => 'cfc',
|
|
210
|
+
:blue => '00f', :darkblue => '008', :paleblue => 'ccf',
|
|
211
|
+
:yellow => 'ff0', :darkyellow => '440', :paleyellow => 'ffc',
|
|
212
|
+
}.each { |tag, val| @color[tag] = color(val) }
|
|
213
|
+
|
|
214
|
+
initialize_widget(*a)
|
|
215
|
+
set_auto_fill_background true
|
|
216
|
+
set_color_association @default_color_association
|
|
217
|
+
set_focus_policy Qt::StrongFocus
|
|
218
|
+
|
|
219
|
+
initialize_visible if respond_to? :initialize_visible
|
|
220
|
+
|
|
221
|
+
set_font 'courier 10'
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def keyPressEvent(key)
|
|
225
|
+
val = key.key >= 128 ? Keyboard_trad[key.key] : key.text[0].ord # must use text[0] to differenciate downcase/upcase
|
|
226
|
+
if key.modifiers.to_i & Qt::ControlModifier.to_i > 0 # AONETHUAAAAAAAAAAAAAAA
|
|
227
|
+
protect { keypress_ctrl(val) } if respond_to? :keypress_ctrl
|
|
228
|
+
else
|
|
229
|
+
protect { keypress(val) } if respond_to? :keypress
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def mousePressEvent(ev)
|
|
234
|
+
if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
|
|
235
|
+
protect { click_ctrl(ev.x, ev.y) } if respond_to? :click_ctrl
|
|
236
|
+
else
|
|
237
|
+
if ev.button == Qt::LeftButton
|
|
238
|
+
protect { click(ev.x, ev.y) }
|
|
239
|
+
elsif ev.button == Qt::RightButton
|
|
240
|
+
protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def mouseDoubleClickEvent(ev)
|
|
246
|
+
protect { doubleclick(ev.x, ev.y) } if respond_to? :doubleclick
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def mouseReleaseEvent(ev)
|
|
250
|
+
if ev.button == Qt::LeftButton
|
|
251
|
+
protect { mouserelease(ev.x, ev.y) } if respond_to? :mouserelease
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def mouseMoveEvent(ev)
|
|
256
|
+
if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
|
|
257
|
+
protect { mousemove_ctrl(ev.x, ev.y) } if respond_to? :mousemove_ctrl
|
|
258
|
+
else
|
|
259
|
+
protect { mousemove(ev.x, ev.y) } if respond_to? :mousemove
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def wheelEvent(ev)
|
|
264
|
+
dir = ev.delta > 0 ? :up : :down
|
|
265
|
+
if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
|
|
266
|
+
protect { mouse_wheel_ctrl(dir, ev.x, ev.y) } if respond_to? :mouse_wheel_ctrl
|
|
267
|
+
else
|
|
268
|
+
protect { mouse_wheel(dir, ev.x, ev.y) } if respond_to? :mouse_wheel
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def resizeEvent(ev)
|
|
273
|
+
protect { resized(ev.size.width, ev.size.height) } if respond_to? :resized
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def grab_focus; set_focus end
|
|
277
|
+
|
|
278
|
+
def paintEvent(*a)
|
|
279
|
+
@painter = Qt::Painter.new(self)
|
|
280
|
+
protect { paint }
|
|
281
|
+
@painter.end
|
|
282
|
+
@painter = nil
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def paint
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def gui_update
|
|
289
|
+
redraw
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# create a color from a 'rgb' description
|
|
293
|
+
def color(val)
|
|
294
|
+
@color[val] ||= Qt::Color.new(*val.unpack('CCC').map { |c| (c.chr*2).hex })
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def set_caret_from_click(x, y)
|
|
298
|
+
@caret_x = (x-1).to_i / @font_width
|
|
299
|
+
@caret_y = y.to_i / @font_height
|
|
300
|
+
update_caret
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# change the font of the widget
|
|
304
|
+
# arg is a Gtk Fontdescription string (eg 'courier 10')
|
|
305
|
+
def set_font(descr)
|
|
306
|
+
descr, sz = descr.split
|
|
307
|
+
super(Qt::Font.new(descr, sz.to_i))
|
|
308
|
+
@font_width = font_metrics.width('x')
|
|
309
|
+
@font_height = font_metrics.line_spacing
|
|
310
|
+
@font_descent = font_metrics.descent
|
|
311
|
+
gui_update
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# change the color association
|
|
315
|
+
# arg is a hash function symbol => color symbol
|
|
316
|
+
# color must be allocated
|
|
317
|
+
# check #initialize/sig('realize') for initial function/color list
|
|
318
|
+
def set_color_association(hash)
|
|
319
|
+
hash.each { |k, v| @color[k] = color(v) }
|
|
320
|
+
#set_background_role Qt::Palette::Window(color(:background))
|
|
321
|
+
palette.set_color(Qt::Palette::Window, color(:background))
|
|
322
|
+
gui_update
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# update @hl_word from a line & offset, return nil if unchanged
|
|
326
|
+
def update_hl_word(line, offset)
|
|
327
|
+
return if not line
|
|
328
|
+
word = line[0...offset].to_s[/\w*$/] << line[offset..-1].to_s[/^\w*/]
|
|
329
|
+
word = nil if word == ''
|
|
330
|
+
@hl_word = word if @hl_word != word
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# invalidate the whole widget area
|
|
334
|
+
def redraw
|
|
335
|
+
invalidate(0, 0, 1000000, 1000000)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def invalidate_caret(cx, cy, x=0, y=0)
|
|
339
|
+
invalidate(x + cx*@font_width, y + cy*@font_height, 2, @font_height)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def invalidate(x, y, w, h)
|
|
343
|
+
update x, y, w, h
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def resized(w, h)
|
|
347
|
+
redraw
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def keypress(key)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def keypress_ctrl(key)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def draw_color(col)
|
|
357
|
+
@col = color(col)
|
|
358
|
+
@painter.set_brush Qt::Brush.new(@col)
|
|
359
|
+
@painter.set_pen Qt::Pen.new(@col)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def draw_rectangle(x, y, w, h)
|
|
363
|
+
@painter.fill_rect(x, y, w, h, @col)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def draw_rectangle_color(col, x, y, w, h)
|
|
367
|
+
draw_color(col)
|
|
368
|
+
draw_rectangle(x, y, w, h)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def draw_line(x, y, ex, ey)
|
|
372
|
+
@painter.draw_line(x, y, ex, ey)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def draw_line_color(col, x, y, ex, ey)
|
|
376
|
+
draw_color(col)
|
|
377
|
+
draw_line(x, y, ex, ey)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def draw_string(x, y, str)
|
|
381
|
+
@painter.draw_text(x, y-@font_descent, str)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def draw_string_color(col, x, y, str)
|
|
385
|
+
draw_color(col)
|
|
386
|
+
draw_string(x, y, str)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
class Window < Qt::MainWindow
|
|
391
|
+
include Msgbox
|
|
392
|
+
|
|
393
|
+
attr_accessor :menu
|
|
394
|
+
def initialize(*a)
|
|
395
|
+
super()
|
|
396
|
+
|
|
397
|
+
#connect(self, SIGNAL(:destroy)) { destroy_window }
|
|
398
|
+
|
|
399
|
+
@menu = menu_bar
|
|
400
|
+
|
|
401
|
+
screen = Qt::Application.desktop
|
|
402
|
+
resize screen.width*3/4, screen.height*3/4
|
|
403
|
+
|
|
404
|
+
initialize_window(*a)
|
|
405
|
+
build_menu
|
|
406
|
+
|
|
407
|
+
show
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def build_menu
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def destroy_window
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def widget=(w)
|
|
417
|
+
set_central_widget w
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def title=(t); set_window_title(t) end
|
|
421
|
+
def title; window_title end
|
|
422
|
+
|
|
423
|
+
def new_menu
|
|
424
|
+
Qt::Menu.new
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def addsubmenu(menu, *args)
|
|
428
|
+
accel = args.grep(/^\^?(\w|<\w+>)$/).first
|
|
429
|
+
args.delete accel if accel
|
|
430
|
+
check = args.delete :check
|
|
431
|
+
submenu = args.grep(Qt::Menu).first
|
|
432
|
+
args.delete submenu if submenu
|
|
433
|
+
if label = args.shift
|
|
434
|
+
label = label.capitalize if label == label.upcase # TODO icon on OPEN/CLOSE etc
|
|
435
|
+
label = label.gsub('_', '&')
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
if submenu
|
|
439
|
+
submenu.title = label
|
|
440
|
+
menu.add_menu submenu
|
|
441
|
+
return
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
if check
|
|
445
|
+
# TODO
|
|
446
|
+
#item = Gtk::CheckMenuItem.new(label)
|
|
447
|
+
#item.active = args.shift
|
|
448
|
+
item = Qt::Action.new(label, self)
|
|
449
|
+
menu.add_action item
|
|
450
|
+
elsif label
|
|
451
|
+
item = Qt::Action.new(label, self)
|
|
452
|
+
menu.add_action item
|
|
453
|
+
else
|
|
454
|
+
menu.add_separator
|
|
455
|
+
end
|
|
456
|
+
item.setShortcut accel.sub('^', 'Ctrl+')
|
|
457
|
+
|
|
458
|
+
connect(item, SIGNAL(:triggered)) { protect { yield(item) } } if block_given?
|
|
459
|
+
|
|
460
|
+
item
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
@app = Qt::Application.new ARGV
|
|
465
|
+
|
|
466
|
+
# start the GUI main loop
|
|
467
|
+
def self.main
|
|
468
|
+
@app.exec
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# ends the GUI main loop
|
|
472
|
+
def self.main_quit
|
|
473
|
+
@app.quit # XXX segfault..
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# register a proc to be run whenever the gui loop is idle
|
|
477
|
+
# if the proc returns nil/false, delete it
|
|
478
|
+
def self.idle_add
|
|
479
|
+
t = Qt::Timer.new
|
|
480
|
+
t.connect(t, SIGNAL(:timeout)) { if not yield ; t.stop ; t = nil end }
|
|
481
|
+
t.start
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# run a single iteration of the main_loop
|
|
485
|
+
# e.g. call this from time to time when doing heavy computation, to keep the UI somewhat responsive
|
|
486
|
+
def self.main_iter
|
|
487
|
+
Qt::Application.process_events
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
require 'metasm/gui/dasm_main'
|
|
494
|
+
require 'metasm/gui/debug'
|
|
495
|
+
|