metasm 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.hgtags +3 -0
- data/Gemfile +1 -0
- data/INSTALL +61 -0
- data/LICENCE +458 -0
- data/README +29 -21
- data/Rakefile +10 -0
- data/TODO +10 -12
- data/doc/code_organisation.txt +2 -0
- data/doc/core/DynLdr.txt +247 -0
- data/doc/core/ExeFormat.txt +43 -0
- data/doc/core/Expression.txt +220 -0
- data/doc/core/GNUExports.txt +27 -0
- data/doc/core/Ia32.txt +236 -0
- data/doc/core/SerialStruct.txt +108 -0
- data/doc/core/VirtualString.txt +145 -0
- data/doc/core/WindowsExports.txt +61 -0
- data/doc/core/index.txt +1 -0
- data/doc/style.css +6 -3
- data/doc/usage/debugger.txt +327 -0
- data/doc/usage/index.txt +1 -0
- data/doc/use_cases.txt +2 -2
- data/metasm.gemspec +22 -0
- data/{lib/metasm.rb → metasm.rb} +11 -3
- data/{lib/metasm → metasm}/compile_c.rb +13 -7
- data/metasm/cpu/arc.rb +8 -0
- data/metasm/cpu/arc/decode.rb +425 -0
- data/metasm/cpu/arc/main.rb +191 -0
- data/metasm/cpu/arc/opcodes.rb +588 -0
- data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
- data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
- data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
- data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
- data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
- data/metasm/cpu/arm/opcodes.rb +324 -0
- data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
- data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
- data/metasm/cpu/arm64.rb +15 -0
- data/metasm/cpu/arm64/debug.rb +38 -0
- data/metasm/cpu/arm64/decode.rb +289 -0
- data/metasm/cpu/arm64/encode.rb +41 -0
- data/metasm/cpu/arm64/main.rb +105 -0
- data/metasm/cpu/arm64/opcodes.rb +232 -0
- data/metasm/cpu/arm64/parse.rb +20 -0
- data/metasm/cpu/arm64/render.rb +95 -0
- data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
- data/metasm/cpu/bpf/decode.rb +142 -0
- data/metasm/cpu/bpf/main.rb +60 -0
- data/metasm/cpu/bpf/opcodes.rb +81 -0
- data/metasm/cpu/bpf/render.rb +41 -0
- data/metasm/cpu/cy16.rb +9 -0
- data/metasm/cpu/cy16/decode.rb +253 -0
- data/metasm/cpu/cy16/main.rb +63 -0
- data/metasm/cpu/cy16/opcodes.rb +78 -0
- data/metasm/cpu/cy16/render.rb +41 -0
- data/metasm/cpu/dalvik.rb +11 -0
- data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
- data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
- data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
- data/metasm/cpu/ia32.rb +17 -0
- data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
- data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
- data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
- data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
- data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
- data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
- data/metasm/cpu/ia32/opcodes.rb +1424 -0
- data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
- data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
- data/metasm/cpu/mips.rb +14 -0
- data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
- data/metasm/cpu/mips/debug.rb +42 -0
- data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
- data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
- data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
- data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
- data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
- data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
- data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
- data/metasm/cpu/msp430/decode.rb +247 -0
- data/metasm/cpu/msp430/main.rb +62 -0
- data/metasm/cpu/msp430/opcodes.rb +101 -0
- data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
- data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
- data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
- data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
- data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
- data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
- data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
- data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
- data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
- data/metasm/cpu/ppc/parse.rb +55 -0
- data/metasm/cpu/python.rb +8 -0
- data/metasm/cpu/python/decode.rb +136 -0
- data/metasm/cpu/python/main.rb +36 -0
- data/metasm/cpu/python/opcodes.rb +180 -0
- data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
- data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
- data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
- data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
- data/metasm/cpu/x86_64.rb +15 -0
- data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
- data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
- data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
- data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
- data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
- data/metasm/cpu/x86_64/opcodes.rb +136 -0
- data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
- data/metasm/cpu/x86_64/render.rb +35 -0
- data/metasm/cpu/z80.rb +9 -0
- data/metasm/cpu/z80/decode.rb +313 -0
- data/metasm/cpu/z80/main.rb +67 -0
- data/metasm/cpu/z80/opcodes.rb +224 -0
- data/metasm/cpu/z80/render.rb +59 -0
- data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
- data/{lib/metasm → metasm}/decode.rb +35 -4
- data/{lib/metasm → metasm}/decompile.rb +15 -16
- data/{lib/metasm → metasm}/disassemble.rb +201 -45
- data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
- data/{lib/metasm → metasm}/dynldr.rb +220 -133
- data/{lib/metasm → metasm}/encode.rb +10 -1
- data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
- data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
- data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
- data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
- data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
- data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
- data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
- data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
- data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
- data/metasm/exe_format/gb.rb +65 -0
- data/metasm/exe_format/javaclass.rb +424 -0
- data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
- data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
- data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
- data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
- data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
- data/metasm/exe_format/pyc.rb +167 -0
- data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
- data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
- data/metasm/exe_format/shellcode_rwx.rb +114 -0
- data/metasm/exe_format/swf.rb +205 -0
- data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
- data/metasm/exe_format/zip.rb +335 -0
- data/metasm/gui.rb +13 -0
- data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
- data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
- data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
- data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
- data/metasm/gui/dasm_graph.rb +1695 -0
- data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
- data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
- data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
- data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
- data/{lib/metasm → metasm}/gui/debug.rb +93 -27
- data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
- data/{lib/metasm → metasm}/gui/qt.rb +12 -2
- data/{lib/metasm → metasm}/gui/win32.rb +179 -42
- data/{lib/metasm → metasm}/gui/x11.rb +59 -59
- data/{lib/metasm → metasm}/main.rb +389 -264
- data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
- data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
- data/{lib/metasm → metasm}/os/linux.rb +628 -151
- data/metasm/os/main.rb +330 -0
- data/{lib/metasm → metasm}/os/windows.rb +132 -42
- data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
- data/{lib/metasm → metasm}/parse.rb +26 -24
- data/{lib/metasm → metasm}/parse_c.rb +221 -116
- data/{lib/metasm → metasm}/preprocessor.rb +55 -40
- data/{lib/metasm → metasm}/render.rb +14 -38
- data/misc/hexdump.rb +2 -1
- data/misc/lint.rb +58 -0
- data/misc/txt2html.rb +9 -7
- data/samples/bindiff.rb +3 -4
- data/samples/dasm-plugins/bindiff.rb +15 -0
- data/samples/dasm-plugins/bookmark.rb +133 -0
- data/samples/dasm-plugins/c_constants.rb +57 -0
- data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
- data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
- data/samples/dasm-plugins/dasm_all.rb +70 -0
- data/samples/dasm-plugins/demangle_cpp.rb +31 -0
- data/samples/dasm-plugins/deobfuscate.rb +251 -0
- data/samples/dasm-plugins/dump_text.rb +35 -0
- data/samples/dasm-plugins/export_graph_svg.rb +86 -0
- data/samples/dasm-plugins/findgadget.rb +75 -0
- data/samples/dasm-plugins/hl_opcode.rb +32 -0
- data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
- data/samples/dasm-plugins/imm2off.rb +34 -0
- data/samples/dasm-plugins/match_libsigs.rb +93 -0
- data/samples/dasm-plugins/patch_file.rb +95 -0
- data/samples/dasm-plugins/scanfuncstart.rb +36 -0
- data/samples/dasm-plugins/scanxrefs.rb +26 -0
- data/samples/dasm-plugins/selfmodify.rb +197 -0
- data/samples/dasm-plugins/stringsxrefs.rb +28 -0
- data/samples/dasmnavig.rb +1 -1
- data/samples/dbg-apihook.rb +24 -9
- data/samples/dbg-plugins/heapscan.rb +283 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
- data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
- data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
- data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
- data/samples/dbg-plugins/heapscan/winheap.h +174 -0
- data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
- data/samples/dbg-plugins/trace_func.rb +214 -0
- data/samples/disassemble-gui.rb +35 -5
- data/samples/disassemble.rb +31 -6
- data/samples/dump_upx.rb +24 -12
- data/samples/dynamic_ruby.rb +12 -3
- data/samples/exeencode.rb +6 -5
- data/samples/factorize-headers-peimports.rb +1 -1
- data/samples/lindebug.rb +175 -381
- data/samples/metasm-shell.rb +1 -2
- data/samples/peldr.rb +2 -2
- data/tests/all.rb +1 -1
- data/tests/arc.rb +26 -0
- data/tests/dynldr.rb +22 -4
- data/tests/expression.rb +55 -0
- data/tests/graph_layout.rb +285 -0
- data/tests/ia32.rb +79 -26
- data/tests/mips.rb +9 -2
- data/tests/x86_64.rb +66 -18
- metadata +330 -218
- data/lib/metasm/arm/opcodes.rb +0 -177
- data/lib/metasm/gui.rb +0 -23
- data/lib/metasm/gui/dasm_graph.rb +0 -1354
- data/lib/metasm/ia32.rb +0 -14
- data/lib/metasm/ia32/opcodes.rb +0 -873
- data/lib/metasm/ppc/parse.rb +0 -52
- data/lib/metasm/x86_64.rb +0 -12
- data/lib/metasm/x86_64/opcodes.rb +0 -118
- data/samples/gdbclient.rb +0 -583
- data/samples/rubstop.rb +0 -399
data/lib/metasm/arm/opcodes.rb
DELETED
@@ -1,177 +0,0 @@
|
|
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 'metasm/arm/main'
|
8
|
-
|
9
|
-
module Metasm
|
10
|
-
class ARM
|
11
|
-
private
|
12
|
-
def addop(name, bin, *args)
|
13
|
-
args << :cond if not args.delete :uncond
|
14
|
-
|
15
|
-
o = Opcode.new name, bin
|
16
|
-
o.args.concat(args & @valid_args)
|
17
|
-
(args & @valid_props).each { |p| o.props[p] = true }
|
18
|
-
args.grep(Hash).each { |h| o.props.update h }
|
19
|
-
|
20
|
-
# special args -> multiple fields
|
21
|
-
case (o.args & [:i8_r, :rm_is, :rm_rs, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]).first
|
22
|
-
when :i8_r; args << :i8 << :rotate
|
23
|
-
when :rm_is; args << :rm << :stype << :shifti
|
24
|
-
when :rm_rs; args << :rm << :stype << :rs
|
25
|
-
when :mem_rn_rm; args << :rn << :rm << :rsx << :u
|
26
|
-
when :mem_rn_i8_12; args << :rn << :i8_12 << :u
|
27
|
-
when :mem_rn_rms; args << :rn << :rm << :stype << :shifti << :u
|
28
|
-
when :mem_rn_i12; args << :rn << :i12 << :u
|
29
|
-
end
|
30
|
-
|
31
|
-
(args & @fields_mask.keys).each { |f|
|
32
|
-
o.fields[f] = [@fields_mask[f], @fields_shift[f]]
|
33
|
-
}
|
34
|
-
|
35
|
-
@opcode_list << o
|
36
|
-
end
|
37
|
-
|
38
|
-
def addop_data_s(name, op, a1, a2, *h)
|
39
|
-
addop name, op | (1 << 25), a1, a2, :i8_r, :rotate, *h
|
40
|
-
addop name, op, a1, a2, :rm_is, *h
|
41
|
-
addop name, op | (1 << 4), a1, a2, :rm_rs, *h
|
42
|
-
end
|
43
|
-
def addop_data(name, op, a1, a2)
|
44
|
-
addop_data_s name, op << 21, a1, a2
|
45
|
-
addop_data_s name+'s', (op << 21) | (1 << 20), a1, a2, :cond_name_off => name.length
|
46
|
-
end
|
47
|
-
|
48
|
-
def addop_load_puw(name, op, *a)
|
49
|
-
addop name, op, {:baseincr => :post}, :rd, :u, *a
|
50
|
-
addop name, op | (1 << 24), :rd, :u, *a
|
51
|
-
addop name, op | (1 << 24) | (1 << 21), {:baseincr => :pre}, :rd, :u, *a
|
52
|
-
end
|
53
|
-
def addop_load_lsh_o(name, op)
|
54
|
-
addop_load_puw name, op, :rsz, :mem_rn_rm, {:cond_name_off => 3}
|
55
|
-
addop_load_puw name, op | (1 << 22), :mem_rn_i8_12, {:cond_name_off => 3}
|
56
|
-
end
|
57
|
-
def addop_load_lsh
|
58
|
-
op = 9 << 4
|
59
|
-
addop_load_lsh_o 'strh', op | (1 << 5)
|
60
|
-
addop_load_lsh_o 'ldrd', op | (1 << 6)
|
61
|
-
addop_load_lsh_o 'strd', op | (1 << 6) | (1 << 5)
|
62
|
-
addop_load_lsh_o 'ldrh', op | (1 << 20) | (1 << 5)
|
63
|
-
addop_load_lsh_o 'ldrsb', op | (1 << 20) | (1 << 6)
|
64
|
-
addop_load_lsh_o 'ldrsh', op | (1 << 20) | (1 << 6) | (1 << 5)
|
65
|
-
end
|
66
|
-
|
67
|
-
def addop_load_puwt(name, op, *a)
|
68
|
-
addop_load_puw name, op, *a
|
69
|
-
addop name+'t', op | (1 << 21), {:baseincr => :post, :cond_name_off => name.length}, :rd, :u, *a
|
70
|
-
end
|
71
|
-
def addop_load_o(name, op, *a)
|
72
|
-
addop_load_puwt name, op, :mem_rn_i12, *a
|
73
|
-
addop_load_puwt name, op | (1 << 25), :mem_rn_rms, *a
|
74
|
-
end
|
75
|
-
def addop_load(name, op)
|
76
|
-
addop_load_o name, op
|
77
|
-
addop_load_o name+'b', op | (1 << 22), :cond_name_off => name.length
|
78
|
-
end
|
79
|
-
|
80
|
-
def addop_ldm_go(name, op, *a)
|
81
|
-
addop name, op, :rn, :reglist, {:cond_name_off => 3}, *a
|
82
|
-
end
|
83
|
-
def addop_ldm_w(name, op, *a)
|
84
|
-
addop_ldm_go name, op, *a # base reg untouched
|
85
|
-
addop_ldm_go name, op | (1 << 21), {:baseincr => :post}, *a # base updated
|
86
|
-
end
|
87
|
-
def addop_ldm_s(name, op)
|
88
|
-
addop_ldm_w name, op # transfer regs
|
89
|
-
addop_ldm_w name, op | (1 << 22), :usermoderegs # transfer usermode regs
|
90
|
-
end
|
91
|
-
def addop_ldm_p(name, op)
|
92
|
-
addop_ldm_s name+'a', op # target memory included
|
93
|
-
addop_ldm_s name+'b', op | (1 << 24) # target memory excluded, transfer starts at next addr
|
94
|
-
end
|
95
|
-
def addop_ldm_u(name, op)
|
96
|
-
addop_ldm_p name+'d', op # transfer made downward
|
97
|
-
addop_ldm_p name+'i', op | (1 << 23) # transfer made upward
|
98
|
-
end
|
99
|
-
def addop_ldm(name, op)
|
100
|
-
addop_ldm_u name, op
|
101
|
-
end
|
102
|
-
|
103
|
-
# ARMv6 instruction set, aka arm7/arm9
|
104
|
-
def init_arm_v6
|
105
|
-
@opcode_list = []
|
106
|
-
@valid_props << :baseincr << :cond << :cond_name_off << :usermoderegs <<
|
107
|
-
:tothumb << :tojazelle
|
108
|
-
@valid_args.concat [:rn, :rd, :rm, :crn, :crd, :crm, :cpn, :reglist, :i24,
|
109
|
-
:rm_rs, :rm_is, :i8_r, :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12]
|
110
|
-
@fields_mask.update :rn => 0xf, :rd => 0xf, :rs => 0xf, :rm => 0xf,
|
111
|
-
:crn => 0xf, :crd => 0xf, :crm => 0xf, :cpn => 0xf,
|
112
|
-
:rnx => 0xf, :rdx => 0xf, :rsx => 0xf,
|
113
|
-
:shifti => 0x1f, :stype => 3, :rotate => 0xf, :reglist => 0xffff,
|
114
|
-
:i8 => 0xff, :i12 => 0xfff, :i24 => 0xff_ffff, :i8_12 => 0xf0f,
|
115
|
-
:u => 1, :mask => 0xf, :sbo => 0xf, :cond => 0xf
|
116
|
-
|
117
|
-
@fields_shift.update :rn => 16, :rd => 12, :rs => 8, :rm => 0,
|
118
|
-
:crn => 16, :crd => 12, :crm => 0, :cpn => 8,
|
119
|
-
:rnx => 16, :rdx => 12, :rsx => 8,
|
120
|
-
:shifti => 7, :stype => 5, :rotate => 8, :reglist => 0,
|
121
|
-
:i8 => 0, :i12 => 0, :i24 => 0, :i8_12 => 0,
|
122
|
-
:u => 23, :mask => 16, :sbo => 12, :cond => 28
|
123
|
-
|
124
|
-
addop_data 'and', 0, :rd, :rn
|
125
|
-
addop_data 'eor', 1, :rd, :rn
|
126
|
-
addop_data 'xor', 1, :rd, :rn
|
127
|
-
addop_data 'sub', 2, :rd, :rn
|
128
|
-
addop_data 'rsb', 3, :rd, :rn
|
129
|
-
addop_data 'add', 4, :rd, :rn
|
130
|
-
addop_data 'adc', 5, :rd, :rn
|
131
|
-
addop_data 'sbc', 6, :rd, :rn
|
132
|
-
addop_data 'rsc', 7, :rd, :rn
|
133
|
-
addop_data 'tst', 8, :rdx, :rn
|
134
|
-
addop_data 'teq', 9, :rdx, :rn
|
135
|
-
addop_data 'cmp', 10, :rdx, :rn
|
136
|
-
addop_data 'cmn', 11, :rdx, :rn
|
137
|
-
addop_data 'orr', 12, :rd, :rn
|
138
|
-
addop_data 'or', 12, :rd, :rn
|
139
|
-
addop_data 'mov', 13, :rd, :rnx
|
140
|
-
addop_data 'bic', 14, :rd, :rn
|
141
|
-
addop_data 'mvn', 15, :rd, :rnx
|
142
|
-
|
143
|
-
addop 'b', 0b1010 << 24, :setip, :stopexec, :i24
|
144
|
-
addop 'bl', 0b1011 << 24, :setip, :stopexec, :i24, :saveip
|
145
|
-
addop 'bkpt', (0b00010010 << 20) | (0b0111 << 4) # other fields are available&unused, also cnd != AL is undef
|
146
|
-
addop 'blx', 0b1111101 << 25, :setip, :stopexec, :saveip, :tothumb, :h, :nocond, :i24
|
147
|
-
addop 'blx', (0b00010010 << 20) | (0b0011 << 4), :setip, :stopexec, :saveip, :tothumb, :rm
|
148
|
-
addop 'bx', (0b00010010 << 20) | (0b0001 << 4), :setip, :stopexec, :rm
|
149
|
-
addop 'bxj', (0b00010010 << 20) | (0b0010 << 4), :setip, :stopexec, :rm, :tojazelle
|
150
|
-
|
151
|
-
addop_load 'str', (1 << 26)
|
152
|
-
addop_load 'ldr', (1 << 26) | (1 << 20)
|
153
|
-
addop_load_lsh
|
154
|
-
addop_ldm 'stm', (1 << 27)
|
155
|
-
addop_ldm 'ldm', (1 << 27) | (1 << 20)
|
156
|
-
end
|
157
|
-
alias init_latest init_arm_v6
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
__END__
|
162
|
-
addop_cond 'mrs', 0b0001000011110000000000000000, :rd
|
163
|
-
addop_cond 'msr', 0b0001001010011111000000000000, :rd
|
164
|
-
addop_cond 'msrf', 0b0001001010001111000000000000, :rd
|
165
|
-
|
166
|
-
addop_cond 'mul', 0b000000000000001001 << 4, :rd, :rn, :rs, :rm
|
167
|
-
addop_cond 'mla', 0b100000000000001001 << 4, :rd, :rn, :rs, :rm
|
168
|
-
|
169
|
-
addop_cond 'swp', 0b0001000000000000000010010000, :rd, :rn, :rs, :rm
|
170
|
-
addop_cond 'swpb', 0b0001010000000000000010010000, :rd, :rn, :rs, :rm
|
171
|
-
|
172
|
-
addop_cond 'undef', 0b00000110000000000000000000010000
|
173
|
-
|
174
|
-
addop_cond 'swi', 0b00001111 << 24
|
175
|
-
|
176
|
-
addop_cond 'bkpt', 0b1001000000000000001110000
|
177
|
-
addop_cond 'movw', 0b0011 << 24, :movwimm
|
data/lib/metasm/gui.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
backend = case ENV['METASM_GUI']
|
2
|
-
when 'gtk'; 'gtk'
|
3
|
-
when 'qt'; 'qt'
|
4
|
-
when 'win32'; 'win32'
|
5
|
-
else
|
6
|
-
puts "Unsupported METASM_GUI #{ENV['METASM_GUI'].inspect}" if $VERBOSE and ENV['METASM_GUI']
|
7
|
-
if RUBY_PLATFORM =~ /(i.86|x(86_)?64)-(mswin|mingw|cygwin)/i
|
8
|
-
'win32'
|
9
|
-
else
|
10
|
-
begin
|
11
|
-
require 'gtk2'
|
12
|
-
'gtk'
|
13
|
-
rescue LoadError
|
14
|
-
#begin
|
15
|
-
# require 'Qt4'
|
16
|
-
# 'qt'
|
17
|
-
#rescue LoadError
|
18
|
-
raise LoadError, 'No GUI ruby binding installed - please install libgtk2-ruby'
|
19
|
-
#end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
require "metasm/gui/#{backend}"
|
@@ -1,1354 +0,0 @@
|
|
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
|
-
module Metasm
|
7
|
-
module Gui
|
8
|
-
class Graph
|
9
|
-
# one box, has a text, an id, and a list of other boxes to/from
|
10
|
-
class Box
|
11
|
-
attr_accessor :id, :x, :y, :w, :h
|
12
|
-
attr_accessor :to, :from # other boxes linked (arrays)
|
13
|
-
attr_accessor :content
|
14
|
-
attr_accessor :direct_to
|
15
|
-
def initialize(id, content=nil)
|
16
|
-
@id = id
|
17
|
-
@x = @y = @w = @h = 0
|
18
|
-
@to, @from = [], []
|
19
|
-
@content = content
|
20
|
-
end
|
21
|
-
def [](a) @content[a] end
|
22
|
-
#def inspect ; puts caller ; "#{Expression[@id] rescue @id.inspect}" end
|
23
|
-
end
|
24
|
-
|
25
|
-
# TODO
|
26
|
-
class MergedBox
|
27
|
-
attr_accessor :id, :text, :x, :y, :w, :h
|
28
|
-
attr_accessor :to, :from
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_accessor :id, :box, :root_addrs, :view_x, :view_y, :keep_split
|
32
|
-
def initialize(id)
|
33
|
-
@id = id
|
34
|
-
@root_addrs = []
|
35
|
-
@view_x = @view_y = -0xfff_ffff
|
36
|
-
clear
|
37
|
-
end
|
38
|
-
|
39
|
-
# empty @box
|
40
|
-
def clear
|
41
|
-
@box = []
|
42
|
-
end
|
43
|
-
|
44
|
-
# link the two boxes (by id)
|
45
|
-
def link_boxes(id1, id2)
|
46
|
-
raise "unknown index 1 #{id1}" if not b1 = @box.find { |b| b.id == id1 }
|
47
|
-
raise "unknown index 2 #{id2}" if not b2 = @box.find { |b| b.id == id2 }
|
48
|
-
b1.to |= [b2]
|
49
|
-
b2.from |= [b1]
|
50
|
-
end
|
51
|
-
|
52
|
-
# creates a new box, ensures id is not already taken
|
53
|
-
def new_box(id, content=nil)
|
54
|
-
raise "duplicate id #{id}" if @box.find { |b| b.id == id }
|
55
|
-
b = Box.new(id, content)
|
56
|
-
@box << b
|
57
|
-
b
|
58
|
-
end
|
59
|
-
|
60
|
-
# place boxes in a good-looking layout
|
61
|
-
def auto_arrange_init(list=@box)
|
62
|
-
# groups is an array of box groups
|
63
|
-
# all groups are centered on the origin
|
64
|
-
@groups = list.map { |b|
|
65
|
-
b.x = -b.w/2
|
66
|
-
b.y = -b.h/2
|
67
|
-
g = Box.new(nil, [b])
|
68
|
-
g.x = b.x - 8
|
69
|
-
g.y = b.y - 9
|
70
|
-
g.w = b.w + 16
|
71
|
-
g.h = b.h + 18
|
72
|
-
g
|
73
|
-
}
|
74
|
-
|
75
|
-
# init group.to/from
|
76
|
-
# must always point to something that is in the 'groups' array
|
77
|
-
# no self references
|
78
|
-
# a box is in one and only one group in 'groups'
|
79
|
-
@groups.each { |g|
|
80
|
-
g.to = g.content.first.to.map { |t| next if not t = list.index(t) ; @groups[t] }.compact - [g]
|
81
|
-
g.from = g.content.first.from.map { |f| next if not f = list.index(f) ; @groups[f] }.compact - [g]
|
82
|
-
}
|
83
|
-
|
84
|
-
# walk from a box, fork at each multiple to, chop links to a previous box (loops etc)
|
85
|
-
@madetree = false
|
86
|
-
end
|
87
|
-
|
88
|
-
# gives a text representation of the current graph state
|
89
|
-
def dump_layout(groups=@groups)
|
90
|
-
groups.map { |g| "#{groups.index(g)} -> #{g.to.map { |t| groups.index(t) }.sort.inspect}" }
|
91
|
-
end
|
92
|
-
|
93
|
-
def auto_arrange_step
|
94
|
-
# TODO fix
|
95
|
-
# 0->[1, 2] 1->[3] 2->[3, 4] 3->[] 4->[1]
|
96
|
-
# push 0 jz l3 push 1 jz l4 push 2 l3: push 3 l4: hlt
|
97
|
-
# and more generally all non-looping graphs where this algo creates backward links
|
98
|
-
|
99
|
-
groups = @groups
|
100
|
-
return if groups.length <= 1
|
101
|
-
|
102
|
-
maketree = lambda { |roots|
|
103
|
-
next if @madetree
|
104
|
-
@madetree = true
|
105
|
-
|
106
|
-
maxdepth = {} # max arc count to reach this box from graph start (excl loop)
|
107
|
-
|
108
|
-
trim = lambda { |g, from|
|
109
|
-
# unlink g from (part of) its from
|
110
|
-
from.each { |gg| gg.to.delete g }
|
111
|
-
g.from -= from
|
112
|
-
}
|
113
|
-
|
114
|
-
walk = lambda { |g|
|
115
|
-
# score
|
116
|
-
parentdepth = g.from.map { |gg| maxdepth[gg] }
|
117
|
-
if parentdepth.empty?
|
118
|
-
# root
|
119
|
-
maxdepth[g] = 0
|
120
|
-
elsif parentdepth.include? nil
|
121
|
-
# not farthest parent found / loop
|
122
|
-
next
|
123
|
-
# elsif maxdepth[g] => ?
|
124
|
-
else
|
125
|
-
maxdepth[g] = parentdepth.max + 1
|
126
|
-
end
|
127
|
-
g.to.each { |gg| walk[gg] }
|
128
|
-
}
|
129
|
-
|
130
|
-
roots.each { |g| trim[g, g.from] unless g.from.empty? }
|
131
|
-
roots.each { |g| walk[g] }
|
132
|
-
|
133
|
-
# handle loops now (unmarked nodes)
|
134
|
-
while unmarked = groups - maxdepth.keys and not unmarked.empty?
|
135
|
-
if g = unmarked.find { |g_| g_.from.find { |gg| maxdepth[gg] } }
|
136
|
-
# loop head
|
137
|
-
trim[g, g.from.find_all { |gg| not maxdepth[gg] }] # XXX not quite sure for this
|
138
|
-
walk[g]
|
139
|
-
else
|
140
|
-
# disconnected subgraph
|
141
|
-
g = unmarked.find { |g_| g_.from.empty? } || unmarked.first
|
142
|
-
trim[g, g.from]
|
143
|
-
maxdepth[g] = 0
|
144
|
-
walk[g]
|
145
|
-
end
|
146
|
-
end
|
147
|
-
}
|
148
|
-
|
149
|
-
# concat all ary boxes into its 1st element, remove trailing groups from 'groups'
|
150
|
-
# updates from/to
|
151
|
-
merge_groups = lambda { |ary|
|
152
|
-
bg = Box.new(nil, [])
|
153
|
-
bg.x, bg.y = ary.map { |g| g.x }.min, ary.map { |g| g.y }.min
|
154
|
-
bg.w, bg.h = ary.map { |g| g.x+g.w }.max - bg.x, ary.map { |g| g.y+g.h }.max - bg.y
|
155
|
-
ary.each { |g|
|
156
|
-
bg.content.concat g.content
|
157
|
-
bg.to |= g.to
|
158
|
-
bg.from |= g.from
|
159
|
-
}
|
160
|
-
bg.to -= ary
|
161
|
-
bg.to.each { |t| t.from = t.from - ary + [bg] }
|
162
|
-
bg.from -= ary
|
163
|
-
bg.from.each { |f| f.to = f.to - ary + [bg] }
|
164
|
-
idx = ary.map { |g| groups.index(g) }.min
|
165
|
-
groups = @groups = groups - ary
|
166
|
-
groups.insert(idx, bg)
|
167
|
-
bg
|
168
|
-
}
|
169
|
-
|
170
|
-
# move all boxes within group of dx, dy
|
171
|
-
move_group = lambda { |g, dx, dy|
|
172
|
-
g.content.each { |b| b.x += dx ; b.y += dy }
|
173
|
-
g.x += dx ; g.y += dy
|
174
|
-
}
|
175
|
-
|
176
|
-
align_hz = lambda { |ary|
|
177
|
-
# if we have one of the block much bigger than the others, put it on the far right
|
178
|
-
big = ary.sort_by { |g| g.h }.last
|
179
|
-
if (ary-[big]).all? { |g| g.h < big.h/3 }
|
180
|
-
ary -= [big]
|
181
|
-
else
|
182
|
-
big = nil
|
183
|
-
end
|
184
|
-
nx = ary.map { |g| g.w }.inject(0) { |a, b| a+b } / -2
|
185
|
-
nx *= 2 if big and ary.length == 1 # just put the parent on the separation of the 2 child
|
186
|
-
ary.each { |g|
|
187
|
-
move_group[g, nx-g.x, 0]
|
188
|
-
nx += g.w
|
189
|
-
}
|
190
|
-
move_group[big, nx-big.x, 0] if big
|
191
|
-
}
|
192
|
-
align_vt = lambda { |ary|
|
193
|
-
ny = ary.map { |g| g.h }.inject(0) { |a, b| a+b } / -2
|
194
|
-
ary.each { |g|
|
195
|
-
move_group[g, 0, ny-g.y]
|
196
|
-
ny += g.h
|
197
|
-
}
|
198
|
-
}
|
199
|
-
|
200
|
-
# scan groups for a column pattern (head has 1 'to' which from == [head])
|
201
|
-
group_columns = lambda {
|
202
|
-
groups.find { |g|
|
203
|
-
next if g.from.length == 1 and g.from.first.to.length == 1
|
204
|
-
ary = [g]
|
205
|
-
ary << (g = g.to.first) while g.to.length == 1 and g.to.first.from.length == 1
|
206
|
-
next if ary.length <= 1
|
207
|
-
align_vt[ary]
|
208
|
-
merge_groups[ary]
|
209
|
-
true
|
210
|
-
}
|
211
|
-
}
|
212
|
-
|
213
|
-
# scan groups for a line pattern (multiple groups with same to & same from)
|
214
|
-
group_lines = lambda { |strict|
|
215
|
-
if groups.all? { |g1| g1.from.empty? and g1.to.empty? }
|
216
|
-
# disjoint subgraphs
|
217
|
-
align_hz[groups]
|
218
|
-
merge_groups[groups]
|
219
|
-
next true
|
220
|
-
end
|
221
|
-
|
222
|
-
groups.find { |g1|
|
223
|
-
ary = g1.from.map { |gg| gg.to }.flatten.uniq.find_all { |gg|
|
224
|
-
gg != g1 and
|
225
|
-
(gg.from - g1.from).empty? and (g1.from - gg.from).empty? and
|
226
|
-
(strict ? ((gg.to - g1.to).empty? and (g1.to - gg.to).empty?) : (g1.to & gg.to).first)
|
227
|
-
}
|
228
|
-
ary = g1.to.map { |gg| gg.from }.flatten.uniq.find_all { |gg|
|
229
|
-
gg != g1 and
|
230
|
-
(gg.to - g1.to).empty? and (g1.to - gg.to).empty? and
|
231
|
-
(strict ? ((gg.from - g1.from).empty? and (g1.from - gg.from).empty?) : (g1.from & gg.from).first)
|
232
|
-
} if ary.empty?
|
233
|
-
next if ary.empty?
|
234
|
-
ary << g1
|
235
|
-
dy = 16*ary.map { |g| g.to.length + g.from.length }.inject { |a, b| a+b }
|
236
|
-
ary.each { |g| g.h += dy ; g.y -= dy/2 }
|
237
|
-
align_hz[ary]
|
238
|
-
if ary.first.to.empty? # shrink graph if highly dissymetric and to.empty?
|
239
|
-
ah = ary.map { |g| g.h }.max
|
240
|
-
ary.each { |g|
|
241
|
-
move_group[g, 0, (g.h-ah)/2] # move up
|
242
|
-
next if not p = ary[ary.index(g)-1]
|
243
|
-
y = [g.y, p.y].min # shrink width
|
244
|
-
h = [g.h, p.h].min
|
245
|
-
xp = p.content.map { |b| b.x+b.w if b.y+b.h+8 >= y and b.y-8 <= y+h }.compact.max || p.x+p.w/2
|
246
|
-
xg = g.content.map { |b| b.x if b.y+b.h+8 >= y and b.y-8 <= y+h }.compact.min || g.x+g.w/2
|
247
|
-
dx = xg-xp-24
|
248
|
-
next if dx <= 0
|
249
|
-
ary.each { |gg|
|
250
|
-
dx = -dx if gg == g
|
251
|
-
move_group[gg, dx/2, 0]
|
252
|
-
}
|
253
|
-
if p.x+p.w > ary.last.x+ary.last.w or ary.first.x > g.x # fix broken centerism
|
254
|
-
x = [g.x, ary.first.x].min
|
255
|
-
xm = [p.x+p.w, ary.last.x+ary.last.w].max
|
256
|
-
ary.each { |gg| move_group[gg, (x+xm)/-2, 0] }
|
257
|
-
end
|
258
|
-
}
|
259
|
-
end
|
260
|
-
merge_groups[ary]
|
261
|
-
true
|
262
|
-
}
|
263
|
-
}
|
264
|
-
|
265
|
-
group_inv_if = {}
|
266
|
-
|
267
|
-
# scan groups for a if/then pattern (1 -> 2 -> 3 & 1 -> 3)
|
268
|
-
group_ifthen = lambda { |strict|
|
269
|
-
groups.reverse.find { |g|
|
270
|
-
next if not g2 = g.to.find { |g2_| (g2_.to.length == 1 and g.to.include?(g2_.to.first)) or
|
271
|
-
(not strict and g2_.to.empty?) }
|
272
|
-
next if strict and g2.from != [g] or g.to.length != 2
|
273
|
-
g2.h += 16 ; g2.y -= 8
|
274
|
-
align_vt[[g, g2]]
|
275
|
-
dx = -g2.x+8
|
276
|
-
dx -= g2.w+16 if group_inv_if[g]
|
277
|
-
move_group[g2, dx, 0]
|
278
|
-
merge_groups[[g, g2]]
|
279
|
-
true
|
280
|
-
}
|
281
|
-
}
|
282
|
-
|
283
|
-
# if (a || b) c;
|
284
|
-
# the 'else' case handles '&& else', and && is two if/then nested
|
285
|
-
group_or = lambda { |strict|
|
286
|
-
groups.find { |g|
|
287
|
-
next if g.to.length != 2
|
288
|
-
g2 = g.to[0]
|
289
|
-
g2 = g.to[1] if not g2.to.include? g.to[1]
|
290
|
-
thn = (g.to & g2.to).first
|
291
|
-
next if g2.to.length != 2 or not thn or thn.to.length != 1
|
292
|
-
els = (g2.to - [thn]).first
|
293
|
-
if thn.to == [els]
|
294
|
-
els = nil
|
295
|
-
elsif els.to != thn.to
|
296
|
-
next if strict
|
297
|
-
align_vt[[g, g2]]
|
298
|
-
merge_groups[[g, g2]]
|
299
|
-
break true
|
300
|
-
else
|
301
|
-
align_hz[[thn, els]]
|
302
|
-
thn = merge_groups[[thn, els]]
|
303
|
-
end
|
304
|
-
thn.h += 16 ; thn.y -= 8
|
305
|
-
align_vt[[g, g2, thn]]
|
306
|
-
move_group[g2, -g2.x, 0]
|
307
|
-
move_group[thn, thn.x-8, 0] if not els
|
308
|
-
merge_groups[[g, g2, thn]]
|
309
|
-
true
|
310
|
-
}
|
311
|
-
}
|
312
|
-
|
313
|
-
|
314
|
-
# loop with exit 1 -> 2, 3 & 2 -> 1
|
315
|
-
group_loop = lambda {
|
316
|
-
groups.find { |g|
|
317
|
-
next if not g2 = g.to.sort_by { |g2_| g2_.h }.find { |g2_| g2_.to == [g] or (g2_.to.empty? and g2_.from == [g]) }
|
318
|
-
g2.h += 16
|
319
|
-
align_vt[[g, g2]]
|
320
|
-
move_group[g2, g2.x-8, 0]
|
321
|
-
merge_groups[[g, g2]]
|
322
|
-
true
|
323
|
-
}
|
324
|
-
}
|
325
|
-
|
326
|
-
# same single from or to
|
327
|
-
group_halflines = lambda {
|
328
|
-
ary = nil
|
329
|
-
if groups.find { |g| ary = g.from.find_all { |gg| gg.to == [g] } and ary.length > 1 } or
|
330
|
-
groups.find { |g| ary = g.to.find_all { |gg| gg.from == [g] } and ary.length > 1 }
|
331
|
-
align_hz[ary]
|
332
|
-
merge_groups[ary]
|
333
|
-
true
|
334
|
-
end
|
335
|
-
}
|
336
|
-
|
337
|
-
|
338
|
-
# unknown pattern, group as we can..
|
339
|
-
group_other = lambda {
|
340
|
-
puts 'graph arrange: unknown configuration', dump_layout
|
341
|
-
g1 = groups.find_all { |g| g.from.empty? }
|
342
|
-
g1 << groups[rand(groups.length)] if g1.empty?
|
343
|
-
g2 = g1.map { |g| g.to }.flatten.uniq - g1
|
344
|
-
align_vt[g1]
|
345
|
-
g1 = merge_groups[g1]
|
346
|
-
g1.w += 128 ; g1.x -= 64
|
347
|
-
next if g2.empty?
|
348
|
-
align_vt[g2]
|
349
|
-
g2 = merge_groups[g2]
|
350
|
-
g2.w += 128 ; g2.x -= 64
|
351
|
-
|
352
|
-
align_hz[[g1, g2]]
|
353
|
-
merge_groups[[g1, g2]]
|
354
|
-
true
|
355
|
-
}
|
356
|
-
|
357
|
-
# check constructs with multiple blocks with to to end block (a la break;)
|
358
|
-
ign_break = lambda {
|
359
|
-
can_reach = lambda { |b1, b2, term|
|
360
|
-
next if b1 == term
|
361
|
-
done = [term]
|
362
|
-
todo = b1.to.dup
|
363
|
-
while t = todo.pop
|
364
|
-
next if done.include? t
|
365
|
-
done << t
|
366
|
-
break true if t == b2
|
367
|
-
todo.concat t.to
|
368
|
-
end
|
369
|
-
}
|
370
|
-
can_reach_unidir = lambda { |b1, b2, term| can_reach[b1, b2, term] and not can_reach[b2, b1, term] }
|
371
|
-
groups.find { |g|
|
372
|
-
f2 = nil
|
373
|
-
if (g.from.length > 2 and f3 = g.from.find { |f| f.to == [g] } and f1 = g.from.find { |f|
|
374
|
-
f2 = g.from.find { |ff| can_reach_unidir[ff, f3, g] and can_reach_unidir[f, ff, g] }}) or
|
375
|
-
(g.to.length > 2 and f3 = g.to.find { |f| f.from == [g] } and f1 = g.to.find { |f|
|
376
|
-
f2 = g.to.find { |ff| can_reach_unidir[f3, ff, g] and can_reach_unidir[ff, f, g] }})
|
377
|
-
group_inv_if[f1] = true
|
378
|
-
if f3.to == [g]
|
379
|
-
g.from.delete f2
|
380
|
-
f2.to.delete g
|
381
|
-
else
|
382
|
-
g.to.delete f2
|
383
|
-
f2.from.delete g
|
384
|
-
end
|
385
|
-
true
|
386
|
-
end
|
387
|
-
}
|
388
|
-
}
|
389
|
-
|
390
|
-
# walk graph from roots, cut backward links
|
391
|
-
trim_graph = lambda {
|
392
|
-
next true if ign_break[]
|
393
|
-
g1 = groups.find_all { |g| g.from.empty? }
|
394
|
-
g1 << groups.first if g1.empty?
|
395
|
-
cntpre = groups.inject(0) { |cntpre_, g| cntpre_ + g.to.length }
|
396
|
-
g1.each { |g| maketree[[g]] }
|
397
|
-
cntpost = groups.inject(0) { |cntpre_, g| cntpre_ + g.to.length }
|
398
|
-
true if cntpre != cntpost
|
399
|
-
}
|
400
|
-
|
401
|
-
# known, clean patterns
|
402
|
-
group_clean = lambda {
|
403
|
-
group_columns[] or group_lines[true] or group_ifthen[true] or group_loop[] or group_or[true]
|
404
|
-
}
|
405
|
-
# approximations
|
406
|
-
group_unclean = lambda {
|
407
|
-
group_lines[false] or group_or[false] or group_halflines[] or group_ifthen[false] or group_other[]
|
408
|
-
}
|
409
|
-
|
410
|
-
group_clean[] or trim_graph[] or group_unclean[]
|
411
|
-
end
|
412
|
-
|
413
|
-
# the boxes have been almost put in place, here we soften a little the result & arrange some qwirks
|
414
|
-
def auto_arrange_post
|
415
|
-
# entrypoint should be above other boxes, same for exitpoints
|
416
|
-
@box.each { |b|
|
417
|
-
if b.from == []
|
418
|
-
chld = b.to
|
419
|
-
chld = @box - [b] if not @box.find { |bb| bb != b and bb.from == [] }
|
420
|
-
chld.each { |t| b.y = t.y - b.h - 16 if t.y < b.y+b.h }
|
421
|
-
end
|
422
|
-
if b.to == []
|
423
|
-
chld = b.from
|
424
|
-
chld = @box - [b] if not @box.find { |bb| bb != b and bb.to == [] }
|
425
|
-
chld.each { |f| b.y = f.y + f.h + 16 if f.y+f.h > b.y }
|
426
|
-
end
|
427
|
-
}
|
428
|
-
|
429
|
-
boxxy = @box.sort_by { |bb| bb.y }
|
430
|
-
# fill gaps that we created
|
431
|
-
@box.each { |b|
|
432
|
-
bottom = b.y+b.h
|
433
|
-
next if not follower = boxxy.find { |bb| bb.y+bb.h > bottom }
|
434
|
-
|
435
|
-
# preserve line[] constructs margins
|
436
|
-
gap = follower.y-16*follower.from.length - (bottom+16*b.to.length)
|
437
|
-
next if gap <= 0
|
438
|
-
|
439
|
-
@box.each { |bb|
|
440
|
-
if bb.y+bb.h <= bottom
|
441
|
-
bb.y += gap/2
|
442
|
-
else
|
443
|
-
bb.y -= gap/2
|
444
|
-
end
|
445
|
-
}
|
446
|
-
boxxy = @box.sort_by { |bb| bb.y }
|
447
|
-
}
|
448
|
-
|
449
|
-
@box[0,0].each { |b|
|
450
|
-
# TODO elastic positionning (ignore up arrows ?) & collision detection (box/box + box/arrow)
|
451
|
-
f = b.from[0]
|
452
|
-
t = b.to[0]
|
453
|
-
if b.to.length == 1 and b.from.length == 1 and b.y+b.h<t.y and b.y>f.y+f.h
|
454
|
-
wx = (t.x+t.w/2 + f.x+f.w/2)/2 - b.w/2
|
455
|
-
wy = (t.y + f.y+f.h)/2 - b.h/2
|
456
|
-
b.x += (wx-b.x)/5
|
457
|
-
b.y += (wy-b.y)/5
|
458
|
-
end
|
459
|
-
}
|
460
|
-
|
461
|
-
end
|
462
|
-
|
463
|
-
def auto_arrange_boxes
|
464
|
-
auto_arrange_init
|
465
|
-
nil while @groups.length > 1 and auto_arrange_step
|
466
|
-
@groups = []
|
467
|
-
auto_arrange_post
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
class GraphViewWidget < DrawableWidget
|
476
|
-
attr_accessor :dasm, :caret_box, :curcontext, :zoom, :margin
|
477
|
-
# bool, specifies if we should display addresses before instrs
|
478
|
-
attr_accessor :show_addresses
|
479
|
-
|
480
|
-
def initialize_widget(dasm, parent_widget)
|
481
|
-
@dasm = dasm
|
482
|
-
@parent_widget = parent_widget
|
483
|
-
|
484
|
-
@show_addresses = false
|
485
|
-
|
486
|
-
@caret_box = nil
|
487
|
-
@selected_boxes = []
|
488
|
-
@shown_boxes = []
|
489
|
-
@mousemove_origin = @mousemove_origin_ctrl = nil
|
490
|
-
@curcontext = Graph.new(nil)
|
491
|
-
@margin = 8
|
492
|
-
@zoom = 1.0
|
493
|
-
@default_color_association = { :background => :paleblue, :hlbox_bg => :palegrey, :box_bg => :white,
|
494
|
-
:text => :black, :arrow_hl => :red, :comment => :darkblue, :address => :darkblue,
|
495
|
-
:instruction => :black, :label => :darkgreen, :caret => :black, :hl_word => :palered,
|
496
|
-
:cursorline_bg => :paleyellow, :arrow_cond => :darkgreen, :arrow_uncond => :darkblue,
|
497
|
-
:arrow_direct => :darkred }
|
498
|
-
# @othergraphs = ? (to keep user-specified formatting)
|
499
|
-
end
|
500
|
-
|
501
|
-
def resized(w, h)
|
502
|
-
redraw
|
503
|
-
end
|
504
|
-
|
505
|
-
def find_box_xy(x, y)
|
506
|
-
x = @curcontext.view_x+x/@zoom
|
507
|
-
y = @curcontext.view_y+y/@zoom
|
508
|
-
@shown_boxes.to_a.reverse.find { |b| b.x <= x and b.x+b.w > x and b.y <= y-1 and b.y+b.h > y+1 }
|
509
|
-
end
|
510
|
-
|
511
|
-
def mouse_wheel_ctrl(dir, x, y)
|
512
|
-
case dir
|
513
|
-
when :up
|
514
|
-
if @zoom < 100
|
515
|
-
oldzoom = @zoom
|
516
|
-
@zoom *= 1.1
|
517
|
-
@zoom = 1.0 if (@zoom-1.0).abs < 0.05
|
518
|
-
@curcontext.view_x += (x / oldzoom - x / @zoom)
|
519
|
-
@curcontext.view_y += (y / oldzoom - y / @zoom)
|
520
|
-
end
|
521
|
-
when :down
|
522
|
-
if @zoom > 1.0/100
|
523
|
-
oldzoom = @zoom
|
524
|
-
@zoom /= 1.1
|
525
|
-
@zoom = 1.0 if (@zoom-1.0).abs < 0.05
|
526
|
-
@curcontext.view_x += (x / oldzoom - x / @zoom)
|
527
|
-
@curcontext.view_y += (y / oldzoom - y / @zoom)
|
528
|
-
end
|
529
|
-
end
|
530
|
-
redraw
|
531
|
-
end
|
532
|
-
|
533
|
-
def mouse_wheel(dir, x, y)
|
534
|
-
case dir
|
535
|
-
when :up; @curcontext.view_y -= height/4 / @zoom
|
536
|
-
when :down; @curcontext.view_y += height/4 / @zoom
|
537
|
-
end
|
538
|
-
redraw
|
539
|
-
end
|
540
|
-
|
541
|
-
def mousemove(x, y)
|
542
|
-
return if not @mousemove_origin
|
543
|
-
|
544
|
-
dx = (x - @mousemove_origin[0])/@zoom
|
545
|
-
dy = (y - @mousemove_origin[1])/@zoom
|
546
|
-
@mousemove_origin = [x, y]
|
547
|
-
if @selected_boxes.empty?
|
548
|
-
@curcontext.view_x -= dx ; @curcontext.view_y -= dy
|
549
|
-
else
|
550
|
-
@selected_boxes.each { |b| b.x += dx ; b.y += dy }
|
551
|
-
end
|
552
|
-
redraw
|
553
|
-
end
|
554
|
-
|
555
|
-
def mouserelease(x, y)
|
556
|
-
mousemove(x, y)
|
557
|
-
@mousemove_origin = nil
|
558
|
-
|
559
|
-
if @mousemove_origin_ctrl
|
560
|
-
x1 = @curcontext.view_x + @mousemove_origin_ctrl[0]/@zoom
|
561
|
-
x2 = x1 + (x - @mousemove_origin_ctrl[0])/@zoom
|
562
|
-
x1, x2 = x2, x1 if x1 > x2
|
563
|
-
y1 = @curcontext.view_y + @mousemove_origin_ctrl[1]/@zoom
|
564
|
-
y2 = y1 + (y - @mousemove_origin_ctrl[1])/@zoom
|
565
|
-
y1, y2 = y2, y1 if y1 > y2
|
566
|
-
@selected_boxes |= @curcontext.box.find_all { |b| b.x >= x1 and b.x + b.w <= x2 and b.y >= y1 and b.y + b.h <= y2 }
|
567
|
-
redraw
|
568
|
-
@mousemove_origin_ctrl = nil
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
def click_ctrl(x, y)
|
573
|
-
if b = find_box_xy(x, y)
|
574
|
-
if @selected_boxes.include? b
|
575
|
-
@selected_boxes.delete b
|
576
|
-
else
|
577
|
-
@selected_boxes << b
|
578
|
-
end
|
579
|
-
redraw
|
580
|
-
else
|
581
|
-
@mousemove_origin_ctrl = [x, y]
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
def click(x, y)
|
586
|
-
@mousemove_origin = [x, y]
|
587
|
-
if b = find_box_xy(x, y)
|
588
|
-
@selected_boxes = [b] if not @selected_boxes.include? b
|
589
|
-
@caret_box = b
|
590
|
-
@caret_x = (@curcontext.view_x+x/@zoom-b.x-1).to_i / @font_width
|
591
|
-
@caret_y = (@curcontext.view_y+y/@zoom-b.y-1).to_i / @font_height
|
592
|
-
update_caret
|
593
|
-
else
|
594
|
-
@selected_boxes = []
|
595
|
-
@caret_box = nil
|
596
|
-
end
|
597
|
-
redraw
|
598
|
-
end
|
599
|
-
|
600
|
-
# if the target is a call to a subfunction, open a new window with the graph of this function (popup)
|
601
|
-
def rightclick(x, y)
|
602
|
-
if b = find_box_xy(x, y) and @zoom >= 0.90 and @zoom <= 1.1
|
603
|
-
click(x, y)
|
604
|
-
@mousemove_origin = nil
|
605
|
-
@parent_widget.clone_window(@hl_word, :graph)
|
606
|
-
end
|
607
|
-
end
|
608
|
-
|
609
|
-
def doubleclick(x, y)
|
610
|
-
if b = find_box_xy(x, y)
|
611
|
-
@mousemove_origin = nil
|
612
|
-
if @hl_word and @zoom >= 0.90 and @zoom <= 1.1
|
613
|
-
@parent_widget.focus_addr(@hl_word)
|
614
|
-
else
|
615
|
-
@parent_widget.focus_addr b[:addresses].first
|
616
|
-
end
|
617
|
-
elsif doubleclick_check_arrow(x, y)
|
618
|
-
elsif @zoom == 1.0
|
619
|
-
zoom_all
|
620
|
-
else
|
621
|
-
@curcontext.view_x += (x/@zoom - x)
|
622
|
-
@curcontext.view_y += (y/@zoom - y)
|
623
|
-
@zoom = 1.0
|
624
|
-
end
|
625
|
-
redraw
|
626
|
-
end
|
627
|
-
|
628
|
-
# check if the user clicked on the beginning/end of an arrow, if so focus on the other end
|
629
|
-
def doubleclick_check_arrow(x, y)
|
630
|
-
return if @margin*@zoom < 2
|
631
|
-
x = @curcontext.view_x+x/@zoom
|
632
|
-
y = @curcontext.view_y+y/@zoom
|
633
|
-
sx = nil
|
634
|
-
if bt = @shown_boxes.to_a.reverse.find { |b|
|
635
|
-
y >= b.y+b.h-1 and y <= b.y+b.h-1+@margin+2 and
|
636
|
-
sx = b.x+b.w/2 - b.to.length/2 * @margin/2 and
|
637
|
-
x >= sx-@margin/2 and x <= sx+b.to.length*@margin/2 # should be margin/4, but add a little comfort margin
|
638
|
-
}
|
639
|
-
idx = (x-sx+@margin/4).to_i / (@margin/2)
|
640
|
-
idx = 0 if idx < 0
|
641
|
-
idx = bt.to.length-1 if idx >= bt.to.length
|
642
|
-
if bt.to[idx]
|
643
|
-
if @parent_widget
|
644
|
-
@parent_widget.focus_addr bt.to[idx][:line_address][0]
|
645
|
-
else
|
646
|
-
focus_xy(bt.to[idx].x, bt.to[idx].y)
|
647
|
-
end
|
648
|
-
end
|
649
|
-
true
|
650
|
-
elsif bf = @shown_boxes.to_a.reverse.find { |b|
|
651
|
-
y >= b.y-@margin-2 and y <= b.y and
|
652
|
-
sx = b.x+b.w/2 - b.from.length/2 * @margin/2 and
|
653
|
-
x >= sx-@margin/2 and x <= sx+b.from.length*@margin/2
|
654
|
-
}
|
655
|
-
idx = (x-sx+@margin/4).to_i / (@margin/2)
|
656
|
-
idx = 0 if idx < 0
|
657
|
-
idx = bf.from.length-1 if idx >= bf.from.length
|
658
|
-
if bf.from[idx]
|
659
|
-
if @parent_widget
|
660
|
-
@parent_widget.focus_addr bf.from[idx][:line_address][-1]
|
661
|
-
else
|
662
|
-
focus_xy(bt.from[idx].x, bt.from[idx].y)
|
663
|
-
end
|
664
|
-
end
|
665
|
-
true
|
666
|
-
end
|
667
|
-
end
|
668
|
-
|
669
|
-
# update the zoom & view_xy to show the whole graph in the window
|
670
|
-
def zoom_all
|
671
|
-
minx = @curcontext.box.map { |b| b.x }.min.to_i - 10
|
672
|
-
miny = @curcontext.box.map { |b| b.y }.min.to_i - 10
|
673
|
-
maxx = @curcontext.box.map { |b| b.x + b.w }.max.to_i + 10
|
674
|
-
maxy = @curcontext.box.map { |b| b.y + b.h }.max.to_i + 10
|
675
|
-
@zoom = [width.to_f/(maxx-minx), height.to_f/(maxy-miny)].min
|
676
|
-
@zoom = 1.0 if @zoom > 1.0 or (@zoom-1.0).abs < 0.1
|
677
|
-
@curcontext.view_x = minx + (maxx-minx-width/@zoom)/2
|
678
|
-
@curcontext.view_y = miny + (maxy-miny-height/@zoom)/2
|
679
|
-
redraw
|
680
|
-
end
|
681
|
-
|
682
|
-
def paint
|
683
|
-
update_graph if @want_update_graph
|
684
|
-
if @want_focus_addr and @curcontext.box.find { |b_| b_[:line_address].index(@want_focus_addr) }
|
685
|
-
focus_addr(@want_focus_addr, false)
|
686
|
-
@want_focus_addr = nil
|
687
|
-
#zoom_all
|
688
|
-
end
|
689
|
-
|
690
|
-
@curcontext.box.each { |b|
|
691
|
-
# reorder arrows so that endings do not overlap
|
692
|
-
b.to = b.to.sort_by { |bt| bt.x+bt.w/2 }
|
693
|
-
b.from = b.from.sort_by { |bt| bt.x+bt.w/2 }
|
694
|
-
}
|
695
|
-
# arrows drawn first to stay under the boxes
|
696
|
-
# XXX precalc ?
|
697
|
-
@curcontext.box.each { |b|
|
698
|
-
b.to.each { |bt| paint_arrow(b, bt) }
|
699
|
-
}
|
700
|
-
|
701
|
-
@shown_boxes = []
|
702
|
-
w_w, w_h = width, height
|
703
|
-
@curcontext.box.each { |b|
|
704
|
-
next if b.x >= @curcontext.view_x+w_w/@zoom or b.y >= @curcontext.view_y+w_h/@zoom or b.x+b.w <= @curcontext.view_x or b.y+b.h <= @curcontext.view_y
|
705
|
-
@shown_boxes << b
|
706
|
-
paint_box(b)
|
707
|
-
}
|
708
|
-
end
|
709
|
-
|
710
|
-
def set_color_arrow(b1, b2)
|
711
|
-
if b1 == @caret_box or b2 == @caret_box
|
712
|
-
draw_color :arrow_hl
|
713
|
-
elsif b1.to.length == 1
|
714
|
-
draw_color :arrow_uncond
|
715
|
-
elsif b1.direct_to == b2.id
|
716
|
-
draw_color :arrow_direct
|
717
|
-
else
|
718
|
-
draw_color :arrow_cond
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
|
-
def paint_arrow(b1, b2)
|
723
|
-
x1, y1 = b1.x+b1.w/2-@curcontext.view_x, b1.y+b1.h-@curcontext.view_y
|
724
|
-
x2, y2 = b2.x+b2.w/2-@curcontext.view_x, b2.y-1-@curcontext.view_y
|
725
|
-
x1o, x2o = x1, x2
|
726
|
-
margin = @margin
|
727
|
-
x1 += (-(b1.to.length-1)/2 + b1.to.index(b2)) * margin/2
|
728
|
-
x2 += (-(b2.from.length-1)/2 + b2.from.index(b1)) * margin/2
|
729
|
-
return if (y1+margin < 0 and y2 < 0) or (y1 > height/@zoom and y2-margin > height/@zoom) # just clip on y
|
730
|
-
margin, x1, y1, x2, y2, b1w, b2w, x1o, x2o = [margin, x1, y1, x2, y2, b1.w, b2.w, x1o, x2o].map { |v| v*@zoom }
|
731
|
-
|
732
|
-
|
733
|
-
# XXX gtk wraps coords around 0x8000
|
734
|
-
if x1.abs > 0x7000 ; y1 /= x1.abs/0x7000 ; x1 /= x1.abs/0x7000 ; end
|
735
|
-
if y1.abs > 0x7000 ; x1 /= y1.abs/0x7000 ; y1 /= y1.abs/0x7000 ; end
|
736
|
-
if x2.abs > 0x7000 ; y2 /= x2.abs/0x7000 ; x2 /= x2.abs/0x7000 ; end
|
737
|
-
if y2.abs > 0x7000 ; x2 /= y2.abs/0x7000 ; y2 /= y2.abs/0x7000 ; end
|
738
|
-
|
739
|
-
# straighten vertical arrows if possible
|
740
|
-
if y2 > y1 and (x1-x2).abs <= margin
|
741
|
-
if b1.to.length == 1
|
742
|
-
x1 = x2
|
743
|
-
elsif b2.from.length == 1
|
744
|
-
x2 = x1
|
745
|
-
end
|
746
|
-
end
|
747
|
-
|
748
|
-
set_color_arrow(b1, b2)
|
749
|
-
if margin > 1
|
750
|
-
# draw arrow tip
|
751
|
-
draw_line(x1, y1, x1, y1+margin)
|
752
|
-
draw_line(x2, y2-margin+1, x2, y2)
|
753
|
-
draw_line(x2-margin/2, y2-margin/2, x2, y2)
|
754
|
-
draw_line(x2+margin/2, y2-margin/2, x2, y2)
|
755
|
-
y1 += margin
|
756
|
-
y2 -= margin-1
|
757
|
-
end
|
758
|
-
if y2+margin >= y1-margin-1
|
759
|
-
# straight vertical down arrow
|
760
|
-
draw_line(x1, y1, x2, y2) if x1 != y1 or x2 != y2
|
761
|
-
|
762
|
-
# else arrow up, need to sneak around boxes
|
763
|
-
elsif x1o-b1w/2-margin >= x2o+b2w/2+margin # z
|
764
|
-
draw_line(x1, y1, x1o-b1w/2-margin, y1)
|
765
|
-
draw_line(x1o-b1w/2-margin, y1, x2o+b2w/2+margin, y2)
|
766
|
-
draw_line(x2o+b2w/2+margin, y2, x2, y2)
|
767
|
-
draw_line(x1, y1+1, x1o-b1w/2-margin, y1+1) # double
|
768
|
-
draw_line(x1o-b1w/2-margin+1, y1, x2o+b2w/2+margin+1, y2)
|
769
|
-
draw_line(x2o+b2w/2+margin, y2+1, x2, y2+1)
|
770
|
-
elsif x1+b1w/2+margin <= x2-b2w/2-margin # invert z
|
771
|
-
draw_line(x1, y1, x1o+b1w/2+margin, y1)
|
772
|
-
draw_line(x1o+b1w/2+margin, y1, x2o-b2w/2-margin, y2)
|
773
|
-
draw_line(x2o-b2w/2-margin, y2, x2, y2)
|
774
|
-
draw_line(x1, y1+1, x1+b1w/2+margin, y1+1) # double
|
775
|
-
draw_line(x1o+b1w/2+margin+1, y1, x2o-b2w/2-margin+1, y2)
|
776
|
-
draw_line(x2o-b2w/2-margin, y2+1, x2, y2+1)
|
777
|
-
else # turn around
|
778
|
-
x = (x1 <= x2 ? [x1o-b1w/2-margin, x2o-b2w/2-margin].min : [x1o+b1w/2+margin, x2o+b2w/2+margin].max)
|
779
|
-
draw_line(x1, y1, x, y1)
|
780
|
-
draw_line(x, y1, x, y2)
|
781
|
-
draw_line(x, y2, x2, y2)
|
782
|
-
draw_line(x1, y1+1, x, y1+1) # double
|
783
|
-
draw_line(x+1, y1, x+1, y2)
|
784
|
-
draw_line(x, y2+1, x2, y2+1)
|
785
|
-
end
|
786
|
-
end
|
787
|
-
|
788
|
-
def set_color_boxshadow(b)
|
789
|
-
draw_color :black
|
790
|
-
end
|
791
|
-
|
792
|
-
def set_color_box(b)
|
793
|
-
if @selected_boxes.include? b
|
794
|
-
draw_color :hlbox_bg
|
795
|
-
else
|
796
|
-
draw_color :box_bg
|
797
|
-
end
|
798
|
-
end
|
799
|
-
|
800
|
-
def paint_box(b)
|
801
|
-
set_color_boxshadow(b)
|
802
|
-
draw_rectangle((b.x-@curcontext.view_x+3)*@zoom, (b.y-@curcontext.view_y+4)*@zoom, b.w*@zoom, b.h*@zoom)
|
803
|
-
set_color_box(b)
|
804
|
-
draw_rectangle((b.x-@curcontext.view_x)*@zoom, (b.y-@curcontext.view_y+1)*@zoom, b.w*@zoom, b.h*@zoom)
|
805
|
-
|
806
|
-
# current text position
|
807
|
-
x = (b.x - @curcontext.view_x + 1)*@zoom
|
808
|
-
y = (b.y - @curcontext.view_y + 1)*@zoom
|
809
|
-
w_w = (b.x - @curcontext.view_x + b.w - @font_width)*@zoom
|
810
|
-
w_h = (b.y - @curcontext.view_y + b.h - @font_height)*@zoom
|
811
|
-
|
812
|
-
if @parent_widget and @parent_widget.bg_color_callback
|
813
|
-
ly = 0
|
814
|
-
b[:line_address].each { |a|
|
815
|
-
if c = @parent_widget.bg_color_callback[a]
|
816
|
-
draw_rectangle_color(c, (b.x-@curcontext.view_x)*@zoom, (1+b.y-@curcontext.view_y+ly*@font_height)*@zoom, b.w*@zoom, (@font_height*@zoom).ceil)
|
817
|
-
end
|
818
|
-
ly += 1
|
819
|
-
}
|
820
|
-
end
|
821
|
-
|
822
|
-
if @caret_box == b
|
823
|
-
draw_rectangle_color(:cursorline_bg, (b.x-@curcontext.view_x)*@zoom, (1+b.y-@curcontext.view_y+@caret_y*@font_height)*@zoom, b.w*@zoom, @font_height*@zoom)
|
824
|
-
end
|
825
|
-
|
826
|
-
return if @zoom < 0.99 or @zoom > 1.1
|
827
|
-
# TODO dynamic font size ?
|
828
|
-
|
829
|
-
# renders a string at current cursor position with a color
|
830
|
-
# must not include newline
|
831
|
-
render = lambda { |str, color|
|
832
|
-
# function ends when we write under the bottom of the listing
|
833
|
-
next if y >= w_h+2 or x >= w_w
|
834
|
-
if @hl_word
|
835
|
-
stmp = str
|
836
|
-
pre_x = 0
|
837
|
-
while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
|
838
|
-
s1, s2 = $1, $2
|
839
|
-
pre_x += s1.length * @font_width
|
840
|
-
hl_x = s2.length * @font_width
|
841
|
-
draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height*@zoom)
|
842
|
-
pre_x += hl_x
|
843
|
-
stmp = stmp[s1.length+s2.length..-1]
|
844
|
-
end
|
845
|
-
end
|
846
|
-
draw_string_color(color, x, y, str)
|
847
|
-
x += str.length * @font_width
|
848
|
-
}
|
849
|
-
|
850
|
-
b[:line_text_col].each { |list|
|
851
|
-
list.each { |s, c| render[s, c] }
|
852
|
-
x = (b.x - @curcontext.view_x + 1)*@zoom
|
853
|
-
y += @font_height*@zoom
|
854
|
-
}
|
855
|
-
|
856
|
-
if b == @caret_box and focus?
|
857
|
-
cx = (b.x - @curcontext.view_x + 1 + @caret_x*@font_width)*@zoom
|
858
|
-
cy = (b.y - @curcontext.view_y + 1 + @caret_y*@font_height)*@zoom
|
859
|
-
draw_line_color(:caret, cx, cy, cx, cy+(@font_height-1)*@zoom)
|
860
|
-
end
|
861
|
-
end
|
862
|
-
|
863
|
-
def gui_update
|
864
|
-
@want_update_graph = true
|
865
|
-
redraw
|
866
|
-
end
|
867
|
-
|
868
|
-
#
|
869
|
-
# rebuild the code flow graph from @curcontext.roots
|
870
|
-
# recalc the boxes w/h
|
871
|
-
#
|
872
|
-
def update_graph
|
873
|
-
@want_update_graph = false
|
874
|
-
|
875
|
-
ctx = @curcontext
|
876
|
-
|
877
|
-
boxcnt = ctx.box.length
|
878
|
-
arrcnt = ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length }
|
879
|
-
ctx.clear
|
880
|
-
|
881
|
-
build_ctx(ctx)
|
882
|
-
|
883
|
-
ctx.auto_arrange_boxes
|
884
|
-
|
885
|
-
return if ctx != @curcontext
|
886
|
-
|
887
|
-
if boxcnt != ctx.box.length or arrcnt != ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length }
|
888
|
-
zoom_all
|
889
|
-
elsif @caret_box # update @caret_box with a box at the same place
|
890
|
-
bx = @caret_box.x + @caret_box.w/2
|
891
|
-
by = @caret_box.y + @caret_box.h/2
|
892
|
-
@caret_box = ctx.box.find { |cb| cb.x < bx and cb.x+cb.w > bx and cb.y < by and cb.y+cb.h > by }
|
893
|
-
end
|
894
|
-
end
|
895
|
-
|
896
|
-
def load_dotfile(path)
|
897
|
-
@want_update_graph = false
|
898
|
-
@curcontext.clear
|
899
|
-
boxes = {}
|
900
|
-
new_box = lambda { |text|
|
901
|
-
b = @curcontext.new_box(text, :line_text_col => [[[text, :text]]])
|
902
|
-
b.w = text.length * @font_width
|
903
|
-
b.h = @font_height
|
904
|
-
b
|
905
|
-
}
|
906
|
-
max = File.size(path)
|
907
|
-
i = 0
|
908
|
-
File.open(path) { |fd|
|
909
|
-
while l = fd.gets
|
910
|
-
case l.strip
|
911
|
-
when /^"?(\w+)"?\s*->\s*"?(\w+)"?;?$/
|
912
|
-
b1 = boxes[$1] ||= new_box[$1]
|
913
|
-
b2 = boxes[$2] ||= new_box[$2]
|
914
|
-
b1.to |= [b2]
|
915
|
-
b2.from |= [b1]
|
916
|
-
end
|
917
|
-
$stderr.printf("%.02f\r" % (fd.pos*100.0/max)) if (i += 1) & 0xff == 0
|
918
|
-
end
|
919
|
-
}
|
920
|
-
p boxes.length
|
921
|
-
redraw
|
922
|
-
rescue Interrupt
|
923
|
-
p boxes.length
|
924
|
-
end
|
925
|
-
|
926
|
-
# create the graph objects in ctx
|
927
|
-
def build_ctx(ctx)
|
928
|
-
# graph : block -> following blocks in same function
|
929
|
-
block_rel = {}
|
930
|
-
|
931
|
-
todo = ctx.root_addrs.dup
|
932
|
-
done = [:default, Expression::Unknown]
|
933
|
-
while a = todo.shift
|
934
|
-
a = @dasm.normalize a
|
935
|
-
next if done.include? a
|
936
|
-
done << a
|
937
|
-
next if not di = @dasm.di_at(a)
|
938
|
-
if not di.block_head?
|
939
|
-
block_rel[di.block.address] = [a]
|
940
|
-
@dasm.split_block(a)
|
941
|
-
end
|
942
|
-
block_rel[a] = []
|
943
|
-
di.block.each_to_samefunc(@dasm) { |t|
|
944
|
-
t = @dasm.normalize t
|
945
|
-
next if not @dasm.di_at(t)
|
946
|
-
todo << t
|
947
|
-
block_rel[a] << t
|
948
|
-
}
|
949
|
-
block_rel[a].uniq!
|
950
|
-
end
|
951
|
-
|
952
|
-
# populate boxes
|
953
|
-
addr2box = {}
|
954
|
-
todo = ctx.root_addrs.dup
|
955
|
-
todo.delete_if { |t| not @dasm.di_at(t) } # undefined func start
|
956
|
-
done = []
|
957
|
-
while a = todo.shift
|
958
|
-
next if done.include? a
|
959
|
-
done << a
|
960
|
-
if not ctx.keep_split.to_a.include?(a) and from = block_rel.keys.find_all { |ba| block_rel[ba].include? a } and
|
961
|
-
from.length == 1 and block_rel[from.first].length == 1 and
|
962
|
-
addr2box[from.first] and lst = @dasm.decoded[from.first].block.list.last and
|
963
|
-
lst.next_addr == a and (not lst.opcode.props[:saveip] or lst.block.to_subfuncret)
|
964
|
-
box = addr2box[from.first]
|
965
|
-
else
|
966
|
-
box = ctx.new_box a, :addresses => [], :line_text_col => [], :line_address => []
|
967
|
-
end
|
968
|
-
@dasm.decoded[a].block.list.each { |di_|
|
969
|
-
box[:addresses] << di_.address
|
970
|
-
addr2box[di_.address] = box
|
971
|
-
}
|
972
|
-
todo.concat block_rel[a]
|
973
|
-
end
|
974
|
-
|
975
|
-
# link boxes
|
976
|
-
ctx.box.each { |b|
|
977
|
-
next if not di = @dasm.decoded[b[:addresses].last]
|
978
|
-
a = di.block.address
|
979
|
-
next if not block_rel[a]
|
980
|
-
block_rel[a].each { |t|
|
981
|
-
ctx.link_boxes(b.id, t)
|
982
|
-
b.direct_to = t if t == di.next_addr
|
983
|
-
}
|
984
|
-
}
|
985
|
-
|
986
|
-
# calc box dimensions/text
|
987
|
-
ctx.box.each { |b|
|
988
|
-
colstr = []
|
989
|
-
curaddr = nil
|
990
|
-
line = 0
|
991
|
-
render = lambda { |str, col| colstr << [str, col] }
|
992
|
-
nl = lambda {
|
993
|
-
b[:line_address][line] = curaddr
|
994
|
-
b[:line_text_col][line] = colstr
|
995
|
-
colstr = []
|
996
|
-
line += 1
|
997
|
-
}
|
998
|
-
b[:addresses].each { |addr|
|
999
|
-
curaddr = addr
|
1000
|
-
if di = @dasm.di_at(curaddr)
|
1001
|
-
if di.block_head?
|
1002
|
-
# render dump_block_header, add a few colors
|
1003
|
-
b_header = '' ; @dasm.dump_block_header(di.block) { |l| b_header << l ; b_header << ?\n if b_header[-1] != ?\n }
|
1004
|
-
b_header.strip.each_line { |l| l.chomp!
|
1005
|
-
col = :comment
|
1006
|
-
col = :label if l[0, 2] != '//' and l[-1] == ?:
|
1007
|
-
render[l, col]
|
1008
|
-
nl[]
|
1009
|
-
}
|
1010
|
-
end
|
1011
|
-
render["#{Expression[curaddr]} ", :address] if @show_addresses
|
1012
|
-
render[di.instruction.to_s.ljust(di.comment ? 24 : 0), :instruction]
|
1013
|
-
render[' ; ' + di.comment.join(' ')[0, 64], :comment] if di.comment
|
1014
|
-
nl[]
|
1015
|
-
else
|
1016
|
-
# TODO real data display (dwords, xrefs, strings..)
|
1017
|
-
if label = @dasm.get_label_at(curaddr)
|
1018
|
-
render[label + ' ', :label]
|
1019
|
-
end
|
1020
|
-
s = @dasm.get_section_at(curaddr)
|
1021
|
-
render['db '+((s and s[0].data.length > s[0].ptr) ? Expression[s[0].read(1)[0]].to_s : '?'), :text]
|
1022
|
-
nl[]
|
1023
|
-
end
|
1024
|
-
}
|
1025
|
-
b.w = b[:line_text_col].map { |strc| strc.map { |s, c| s }.join.length }.max.to_i * @font_width + 2
|
1026
|
-
b.w += 1 if b.w % 2 == 0 # ensure boxes have odd width -> vertical arrows are straight
|
1027
|
-
b.h = line * @font_height
|
1028
|
-
}
|
1029
|
-
end
|
1030
|
-
|
1031
|
-
def keypress_ctrl(key)
|
1032
|
-
case key
|
1033
|
-
when ?F
|
1034
|
-
@parent_widget.inputbox('text to search in curview (regex)', :text => @hl_word) { |pat|
|
1035
|
-
re = /#{pat}/i
|
1036
|
-
list = [['addr', 'instr']]
|
1037
|
-
@curcontext.box.each { |b|
|
1038
|
-
b[:line_text_col].zip(b[:line_address]) { |l, a|
|
1039
|
-
str = l.map { |s, c| s }.join
|
1040
|
-
list << [Expression[a], str] if str =~ re
|
1041
|
-
}
|
1042
|
-
}
|
1043
|
-
@parent_widget.list_bghilight("search result for /#{pat}/i", list) { |i| @parent_widget.focus_addr i[0] }
|
1044
|
-
}
|
1045
|
-
else return false
|
1046
|
-
end
|
1047
|
-
true
|
1048
|
-
end
|
1049
|
-
|
1050
|
-
def keypress(key)
|
1051
|
-
case key
|
1052
|
-
when :left
|
1053
|
-
if @caret_box
|
1054
|
-
if @caret_x > 0
|
1055
|
-
@caret_x -= 1
|
1056
|
-
update_caret
|
1057
|
-
elsif b = @curcontext.box.sort_by { |b_| -b_.x }.find { |b_| b_.x < @caret_box.x and
|
1058
|
-
b_.y < @caret_box.y+@caret_y*@font_height and
|
1059
|
-
b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height }
|
1060
|
-
@caret_x = (b.w/@font_width).to_i
|
1061
|
-
@caret_y += ((@caret_box.y-b.y)/@font_height).to_i
|
1062
|
-
@caret_box = b
|
1063
|
-
update_caret
|
1064
|
-
redraw
|
1065
|
-
else
|
1066
|
-
@curcontext.view_x -= 20/@zoom
|
1067
|
-
redraw
|
1068
|
-
end
|
1069
|
-
else
|
1070
|
-
@curcontext.view_x -= 20/@zoom
|
1071
|
-
redraw
|
1072
|
-
end
|
1073
|
-
when :up
|
1074
|
-
if @caret_box
|
1075
|
-
if @caret_y > 0
|
1076
|
-
@caret_y -= 1
|
1077
|
-
update_caret
|
1078
|
-
elsif b = @curcontext.box.sort_by { |b_| -b_.y }.find { |b_| b_.y < @caret_box.y and
|
1079
|
-
b_.x < @caret_box.x+@caret_x*@font_width and
|
1080
|
-
b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width }
|
1081
|
-
@caret_x += ((@caret_box.x-b.x)/@font_width).to_i
|
1082
|
-
@caret_y = b[:line_address].length-1
|
1083
|
-
@caret_box = b
|
1084
|
-
update_caret
|
1085
|
-
redraw
|
1086
|
-
else
|
1087
|
-
@curcontext.view_y -= 20/@zoom
|
1088
|
-
redraw
|
1089
|
-
end
|
1090
|
-
else
|
1091
|
-
@curcontext.view_y -= 20/@zoom
|
1092
|
-
redraw
|
1093
|
-
end
|
1094
|
-
when :right
|
1095
|
-
if @caret_box
|
1096
|
-
if @caret_x <= @caret_box[:line_text_col].map { |s| s.map { |ss, cc| ss }.join.length }.max
|
1097
|
-
@caret_x += 1
|
1098
|
-
update_caret
|
1099
|
-
elsif b = @curcontext.box.sort_by { |b_| b_.x }.find { |b_| b_.x > @caret_box.x and
|
1100
|
-
b_.y < @caret_box.y+@caret_y*@font_height and
|
1101
|
-
b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height }
|
1102
|
-
@caret_x = 0
|
1103
|
-
@caret_y += ((@caret_box.y-b.y)/@font_height).to_i
|
1104
|
-
@caret_box = b
|
1105
|
-
update_caret
|
1106
|
-
redraw
|
1107
|
-
else
|
1108
|
-
@curcontext.view_x += 20/@zoom
|
1109
|
-
redraw
|
1110
|
-
end
|
1111
|
-
else
|
1112
|
-
@curcontext.view_x += 20/@zoom
|
1113
|
-
redraw
|
1114
|
-
end
|
1115
|
-
when :down
|
1116
|
-
if @caret_box
|
1117
|
-
if @caret_y < @caret_box[:line_address].length-1
|
1118
|
-
@caret_y += 1
|
1119
|
-
update_caret
|
1120
|
-
elsif b = @curcontext.box.sort_by { |b_| b_.y }.find { |b_| b_.y > @caret_box.y and
|
1121
|
-
b_.x < @caret_box.x+@caret_x*@font_width and
|
1122
|
-
b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width }
|
1123
|
-
@caret_x += ((@caret_box.x-b.x)/@font_width).to_i
|
1124
|
-
@caret_y = 0
|
1125
|
-
@caret_box = b
|
1126
|
-
update_caret
|
1127
|
-
redraw
|
1128
|
-
else
|
1129
|
-
@curcontext.view_y += 20/@zoom
|
1130
|
-
redraw
|
1131
|
-
end
|
1132
|
-
else
|
1133
|
-
@curcontext.view_y += 20/@zoom
|
1134
|
-
redraw
|
1135
|
-
end
|
1136
|
-
when :pgup
|
1137
|
-
if @caret_box
|
1138
|
-
@caret_y = 0
|
1139
|
-
update_caret
|
1140
|
-
else
|
1141
|
-
@curcontext.view_y -= height/4/@zoom
|
1142
|
-
redraw
|
1143
|
-
end
|
1144
|
-
when :pgdown
|
1145
|
-
if @caret_box
|
1146
|
-
@caret_y = @caret_box[:line_address].length-1
|
1147
|
-
update_caret
|
1148
|
-
else
|
1149
|
-
@curcontext.view_y += height/4/@zoom
|
1150
|
-
redraw
|
1151
|
-
end
|
1152
|
-
when :home
|
1153
|
-
if @caret_box
|
1154
|
-
@caret_x = 0
|
1155
|
-
update_caret
|
1156
|
-
else
|
1157
|
-
@curcontext.view_x = @curcontext.box.map { |b_| b_.x }.min-10
|
1158
|
-
@curcontext.view_y = @curcontext.box.map { |b_| b_.y }.min-10
|
1159
|
-
redraw
|
1160
|
-
end
|
1161
|
-
when :end
|
1162
|
-
if @caret_box
|
1163
|
-
@caret_x = @caret_box[:line_text_col][@caret_y].to_a.map { |ss, cc| ss }.join.length
|
1164
|
-
update_caret
|
1165
|
-
else
|
1166
|
-
@curcontext.view_x = [@curcontext.box.map { |b_| b_.x+b_.w }.max-width/@zoom+10, @curcontext.box.map { |b_| b_.x }.min-10].max
|
1167
|
-
@curcontext.view_y = [@curcontext.box.map { |b_| b_.y+b_.h }.max-height/@zoom+10, @curcontext.box.map { |b_| b_.y }.min-10].max
|
1168
|
-
redraw
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
when :delete
|
1172
|
-
@selected_boxes.each { |b_|
|
1173
|
-
@curcontext.box.delete b_
|
1174
|
-
b_.from.each { |bb| bb.to.delete b_ }
|
1175
|
-
b_.to.each { |bb| bb.from.delete b_ }
|
1176
|
-
}
|
1177
|
-
redraw
|
1178
|
-
|
1179
|
-
when ?a
|
1180
|
-
puts 'autoarrange'
|
1181
|
-
@curcontext.auto_arrange_boxes
|
1182
|
-
redraw
|
1183
|
-
puts 'autoarrange done'
|
1184
|
-
when ?u
|
1185
|
-
gui_update
|
1186
|
-
|
1187
|
-
when ?R
|
1188
|
-
load __FILE__
|
1189
|
-
when ?S # reset
|
1190
|
-
@curcontext.auto_arrange_init(@selected_boxes.empty? ? @curcontext.box : @selected_boxes)
|
1191
|
-
puts 'reset', @curcontext.dump_layout, ''
|
1192
|
-
zoom_all
|
1193
|
-
redraw
|
1194
|
-
when ?T # step auto_arrange
|
1195
|
-
@curcontext.auto_arrange_step
|
1196
|
-
puts @curcontext.dump_layout, ''
|
1197
|
-
zoom_all
|
1198
|
-
redraw
|
1199
|
-
when ?L # post auto_arrange
|
1200
|
-
@curcontext.auto_arrange_post
|
1201
|
-
zoom_all
|
1202
|
-
redraw
|
1203
|
-
when ?V # shrink
|
1204
|
-
@selected_boxes.each { |b_|
|
1205
|
-
dx = (b_.from + b_.to).map { |bb| bb.x+bb.w/2 - b_.x-b_.w/2 }
|
1206
|
-
dx = dx.inject(0) { |s, xx| s+xx }/dx.length
|
1207
|
-
b_.x += dx
|
1208
|
-
}
|
1209
|
-
redraw
|
1210
|
-
when ?I # create arbitrary boxes/links
|
1211
|
-
if @selected_boxes.empty?
|
1212
|
-
@fakebox ||= 0
|
1213
|
-
b = @curcontext.new_box "id_#@fakebox",
|
1214
|
-
:addresses => [], :line_address => [],
|
1215
|
-
:line_text_col => [[[" blublu #@fakebox", :text]]]
|
1216
|
-
b.w = @font_width * 15
|
1217
|
-
b.h = @font_height * 2
|
1218
|
-
b.x = rand(200) - 100
|
1219
|
-
b.y = rand(200) - 100
|
1220
|
-
|
1221
|
-
@fakebox += 1
|
1222
|
-
else
|
1223
|
-
b1, *bl = @selected_boxes
|
1224
|
-
bl = [b1] if bl.empty? # loop
|
1225
|
-
bl.each { |b2|
|
1226
|
-
if b1.to.include? b2
|
1227
|
-
b1.to.delete b2
|
1228
|
-
b2.from.delete b1
|
1229
|
-
else
|
1230
|
-
b1.to << b2
|
1231
|
-
b2.from << b1
|
1232
|
-
end
|
1233
|
-
}
|
1234
|
-
end
|
1235
|
-
redraw
|
1236
|
-
|
1237
|
-
when ?1 # (numeric) zoom to 1:1
|
1238
|
-
if @zoom == 1.0
|
1239
|
-
zoom_all
|
1240
|
-
else
|
1241
|
-
@curcontext.view_x += (width/2 / @zoom - width/2)
|
1242
|
-
@curcontext.view_y += (height/2 / @zoom - height/2)
|
1243
|
-
@zoom = 1.0
|
1244
|
-
end
|
1245
|
-
redraw
|
1246
|
-
when :insert # split curbox at @caret_y
|
1247
|
-
if @caret_box and a = @caret_box[:line_address][@caret_y] and @dasm.decoded[a]
|
1248
|
-
@dasm.split_block(a)
|
1249
|
-
@curcontext.keep_split ||= []
|
1250
|
-
@curcontext.keep_split |= [a]
|
1251
|
-
gui_update
|
1252
|
-
focus_addr a
|
1253
|
-
end
|
1254
|
-
else return false
|
1255
|
-
end
|
1256
|
-
true
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
# find a suitable array of graph roots, walking up from a block (function start/entrypoint)
|
1260
|
-
def dasm_find_roots(addr)
|
1261
|
-
todo = [addr]
|
1262
|
-
done = []
|
1263
|
-
roots = []
|
1264
|
-
default_root = nil
|
1265
|
-
while a = todo.shift
|
1266
|
-
next if not di = @dasm.di_at(a)
|
1267
|
-
b = di.block
|
1268
|
-
a = b.address
|
1269
|
-
if done.include? a
|
1270
|
-
default_root ||= a
|
1271
|
-
next
|
1272
|
-
end
|
1273
|
-
done << a
|
1274
|
-
newf = []
|
1275
|
-
b.each_from_samefunc(@dasm) { |f| newf << f }
|
1276
|
-
if newf.empty?
|
1277
|
-
roots << b.address
|
1278
|
-
else
|
1279
|
-
todo.concat newf
|
1280
|
-
end
|
1281
|
-
end
|
1282
|
-
roots << default_root if roots.empty? and default_root
|
1283
|
-
|
1284
|
-
roots
|
1285
|
-
end
|
1286
|
-
|
1287
|
-
def set_cursor_pos(p)
|
1288
|
-
addr, x = p
|
1289
|
-
focus_addr(addr)
|
1290
|
-
@caret_x = x
|
1291
|
-
update_caret
|
1292
|
-
end
|
1293
|
-
|
1294
|
-
def get_cursor_pos
|
1295
|
-
[current_address, @caret_x]
|
1296
|
-
end
|
1297
|
-
|
1298
|
-
# focus on addr
|
1299
|
-
# addr may be a dasm label, dasm address, dasm address in string form (eg "0DEADBEEFh")
|
1300
|
-
# addr must point to a decodedinstruction
|
1301
|
-
# if the addr is not found in curcontext, the code flow is walked up until a function
|
1302
|
-
# start or an entrypoint is found, then the graph is created from there
|
1303
|
-
# will call gui_update then
|
1304
|
-
def focus_addr(addr, can_update_context=true)
|
1305
|
-
return if @parent_widget and not addr = @parent_widget.normalize(addr)
|
1306
|
-
return if not @dasm.di_at(addr)
|
1307
|
-
|
1308
|
-
# move window / change curcontext
|
1309
|
-
if b = @curcontext.box.find { |b_| b_[:line_address].index(addr) }
|
1310
|
-
@caret_box, @caret_x, @caret_y = b, 0, b[:line_address].rindex(addr)
|
1311
|
-
@curcontext.view_x += (width/2 / @zoom - width/2)
|
1312
|
-
@curcontext.view_y += (height/2 / @zoom - height/2)
|
1313
|
-
@zoom = 1.0
|
1314
|
-
|
1315
|
-
focus_xy(b.x, b.y + @caret_y*@font_height)
|
1316
|
-
update_caret
|
1317
|
-
elsif can_update_context
|
1318
|
-
@curcontext = Graph.new 'testic'
|
1319
|
-
@curcontext.root_addrs = dasm_find_roots(addr)
|
1320
|
-
@want_focus_addr = addr
|
1321
|
-
gui_update
|
1322
|
-
else
|
1323
|
-
return
|
1324
|
-
end
|
1325
|
-
true
|
1326
|
-
end
|
1327
|
-
|
1328
|
-
def focus_xy(x, y)
|
1329
|
-
if not @curcontext.view_x or @curcontext.view_x*@zoom + width*3/4 < x or @curcontext.view_x*@zoom > x
|
1330
|
-
@curcontext.view_x = (x - width/5)/@zoom
|
1331
|
-
redraw
|
1332
|
-
end
|
1333
|
-
if not @curcontext.view_y or @curcontext.view_y*@zoom + height*3/4 < y or @curcontext.view_y*@zoom > y
|
1334
|
-
@curcontext.view_y = (y - height/5)/@zoom
|
1335
|
-
redraw
|
1336
|
-
end
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
# hint that the caret moved
|
1340
|
-
# redraw, change the hilighted word
|
1341
|
-
def update_caret
|
1342
|
-
return if not @caret_box or not @caret_x or not l = @caret_box[:line_text_col][@caret_y]
|
1343
|
-
l = l.map { |s, c| s }.join
|
1344
|
-
@parent_widget.focus_changed_callback[] if @parent_widget and @parent_widget.focus_changed_callback and @oldcaret_y != @caret_y
|
1345
|
-
update_hl_word(l, @caret_x)
|
1346
|
-
redraw
|
1347
|
-
end
|
1348
|
-
|
1349
|
-
def current_address
|
1350
|
-
@caret_box ? @caret_box[:line_address][@caret_y] : @curcontext.root_addrs.first
|
1351
|
-
end
|
1352
|
-
end
|
1353
|
-
end
|
1354
|
-
end
|