class Metasm::Gui::CStructWidget

Attributes

dasm[RW]
view_x[RW]
view_y[RW]

Public Instance Methods

click(x, y) click to toggle source
# File metasm/gui/cstruct.rb, line 29
def click(x, y)
        @caret_x = (x-1).to_i / @font_width + @view_x
        @caret_y = y.to_i / @font_height + @view_y
        update_caret
end
current_address() click to toggle source

returns the address of the data under the cursor

# File metasm/gui/cstruct.rb, line 259
def current_address
        @curaddr
end
doubleclick(x, y) click to toggle source
# File metasm/gui/cstruct.rb, line 40
def doubleclick(x, y)
        click(x, y)
        keypress(:enter)
end
focus_addr(addr, struct=@curstruct) click to toggle source

focus on addr returns true on success

# File metasm/gui/cstruct.rb, line 244
def focus_addr(addr, struct=@curstruct)
        return if @parent_widget and not addr = @parent_widget.normalize(addr)
        @curaddr = addr
        @caret_x = @caret_y = 0
        if struct.kind_of? String
                @curstruct = nil
                focus_struct_byname(struct)
        else
                @curstruct = struct
                gui_update
        end
        true
end
focus_struct_byname(n, addr=@curaddr) click to toggle source

display the struct or pop a list of matching struct names if ambiguous

# File metasm/gui/cstruct.rb, line 176
def focus_struct_byname(n, addr=@curaddr)
        lst = @dasm.c_parser.toplevel.struct.keys.grep(String)
        if fn = lst.find { |ln| ln == n } || lst.find { |ln| ln.downcase == n.downcase }
                focus_addr(addr, @dasm.c_parser.toplevel.struct[fn])
        else
                lst = @dasm.c_parser.toplevel.symbol.keys.grep(String).find_all { |ln|
                        s = @dasm.c_parser.toplevel.symbol[ln]
                        s.kind_of?(C::TypeDef) and s.untypedef.kind_of?(C::Union)
                }
                if fn = lst.find { |ln| ln == n } || lst.find { |ln| ln.downcase == n.downcase }
                        focus_addr(addr, @dasm.c_parser.toplevel.symbol[fn].untypedef)
                else
                        liststructs(n, addr)
                end
        end
end
get_cursor_pos() click to toggle source
# File metasm/gui/cstruct.rb, line 218
def get_cursor_pos
        [@curaddr, @curstruct, @caret_x, @caret_y, @view_y]
end
gui_update() click to toggle source
# File metasm/gui/cstruct.rb, line 354
def gui_update
        if @curstruct
                render_struct
        else
                @line_text_col = [[[:text, '/* no struct selected (list with "l") */']]]
        end

        @line_text = @line_text_col.map { |l| l.map { |c, s| s }.join }
        update_caret
        redraw
end
initialize_widget(dasm, parent_widget) click to toggle source
# File metasm/gui/cstruct.rb, line 11
def initialize_widget(dasm, parent_widget)
        @dasm = dasm
        @parent_widget = parent_widget

        @line_text_col = []   # each line is [[:col, 'text'], [:col, 'text']]
        @line_text = []
        @line_dereference = []        # linenr => [addr, struct] (args to focus_addr)
        @curaddr = nil
        @curstruct = nil
        @tabwidth = 8
        @view_x = @view_y = 0
        @caret_x = @caret_y = 0
        @cwidth = @cheight = 1        # widget size in chars
        @structdepth = 2

        @default_color_association = ColorTheme.merge :keyword => :blue
end
keypress(key) click to toggle source
# File metasm/gui/cstruct.rb, line 110
def keypress(key)
        case key
        when :left
                if @caret_x >= 1
                        @caret_x -= 1
                        update_caret
                end
        when :up
                if @caret_y > 0
                        @caret_y -= 1
                        update_caret
                end
        when :right
                if @caret_x < @line_text[@caret_y].to_s.length
                        @caret_x += 1
                        update_caret
                end
        when :down
                if @caret_y < @line_text.length
                        @caret_y += 1
                        update_caret
                end
        when :home
                @caret_x = @line_text[@caret_y].to_s[/^\s*/].length
                update_caret
        when :end
                @caret_x = @line_text[@caret_y].to_s.length
                update_caret
        when :pgup
                @caret_y -= @cheight/2
                @caret_y = 0 if @caret_y < 0
                update_caret
        when :pgdown
                @caret_y += @cheight/2
                @caret_y = @line_text.length if @caret_y > @line_text.length
                update_caret
        when :enter
                if l = @line_dereference[@caret_y]
                        if @parent_widget
                                @parent_widget.focus_addr(l[0], :cstruct, false, l[1])
                        else
                                focus_addr(l[0], l[1])
                        end
                end
        when ?+
                @structdepth += 1
                gui_update
        when ?-
                @structdepth -= 1
                gui_update
        when ?/
                @structdepth = 1
                gui_update
        when ?*
                @structdepth =  50
                gui_update
        when ?l
                liststructs
        when ?t
                inputbox('new struct name to use', :text => (@curstruct.name rescue '')) { |n| focus_struct_byname(n) }
        else return false
        end
        true
end
liststructs(partname=nil, addr=@curaddr) click to toggle source
# File metasm/gui/cstruct.rb, line 193
def liststructs(partname=nil, addr=@curaddr)
        tl = @dasm.c_parser.toplevel
        list = [['name', 'size']]
        list += tl.struct.keys.grep(String).sort.map { |stn|
                next if partname and stn !~ /#{partname}/i
                st = tl.struct[stn]
                [stn, @dasm.c_parser.sizeof(st)] if st.members
        }.compact
        list += tl.symbol.keys.grep(String).sort.map { |stn|
                next if partname and stn !~ /#{partname}/i
                st = tl.symbol[stn]
                next unless st.kind_of?(C::TypeDef) and st.untypedef.kind_of?(C::Union)
                [stn, @dasm.c_parser.sizeof(st)] if st.untypedef.members
        }.compact

        if partname and list.length == 2
                focus_addr(addr, tl.struct[list[1][0]] || tl.symbol[list[1][0]].untypedef)
                return
        end

        listwindow('structs', list) { |stn|
                focus_addr(addr, tl.struct[stn[0]] || tl.symbol[stn[0]].untypedef)
        }
end
mouse_wheel(dir, x, y) click to toggle source
# File metasm/gui/cstruct.rb, line 45
def mouse_wheel(dir, x, y)
        case dir
        when :up
                if @caret_y > 0
                        @view_y -= 4
                        @view_y = 0 if @view_y < 0
                        @caret_y -= 4
                        @caret_y = 0 if @caret_y < 0
                end
        when :down
                if @caret_y < @line_text.length - 1
                        @view_y += 4
                        @caret_y += 4
                end
        end
        redraw
end
paint() click to toggle source
# File metasm/gui/cstruct.rb, line 63
def paint
        @cwidth = width/@font_width
        @cheight = height/@font_height

        # adjust viewport to cursor
        sz_x = @line_text.map { |l| l.length }.max.to_i + 1
        sz_y = @line_text.length.to_i + 1
        @view_x = @caret_x - @cwidth + 1 if @caret_x > @view_x + @cwidth - 1
        @view_x = @caret_x if @caret_x < @view_x
        @view_x = sz_x - @cwidth - 1 if @view_x >= sz_x - @cwidth
        @view_x = 0 if @view_x < 0

        @view_y = @caret_y - @cheight + 1 if @caret_y > @view_y + @cheight - 1
        @view_y = @caret_y if @caret_y < @view_y
        @view_y = sz_y - @cheight - 1 if @view_y >= sz_y - @cheight
        @view_y = 0 if @view_y < 0

        # current cursor position
        x = 1
        y = 0

        @line_text_col[@view_y, @cheight + 1].each { |l|
                cx = 0
                l.each { |c, t|
                        cx += t.length
                        if cx-t.length > @view_x + @cwidth + 1
                        elsif cx < @view_x
                        else
                                t = t[(@view_x - cx + t.length)..-1] if cx-t.length < @view_x
                                draw_string_hl(c, x, y, t)
                                x += t.length * @font_width
                        end
                }
                x = 1
                y += @font_height
        }

        if focus?
                # draw caret
                cx = (@caret_x-@view_x)*@font_width+1
                cy = (@caret_y-@view_y)*@font_height
                draw_line_color(:caret, cx, cy, cx, cy+@font_height-1)
        end

        @oldcaret_x, @oldcaret_y = @caret_x, @caret_y
end
render_struct(obj=nil, off=nil, maxdepth=@structdepth) click to toggle source
# File metasm/gui/cstruct.rb, line 263
def render_struct(obj=nil, off=nil, maxdepth=@structdepth)
        render = lambda { |str, col|
                if @line_text_col.last[0] == col
                        @line_text_col.last[1] << str
                else
                        @line_text_col.last << [col, str]
                end
        }
        indent = ' ' * @tabwidth
        nl = lambda {
                @line_text_col << []
                render[indent * [@structdepth - maxdepth, 0].max, :text]
        }

        if not obj
                @line_text_col = [[]]
                @line_dereference = []

                struct = @curstruct
                if str = @dasm.get_section_at(@curaddr)
                        obj = @dasm.c_parser.decode_c_struct(struct, str[0].read(@dasm.c_parser.sizeof(struct)))
                else
                        render["/* unmapped area #{Expression[@curaddr]} */", :text]
                        return
                end
        else
                struct = obj.struct
        end

        if maxdepth <= 0
                render['{ /* type "+" to expand */ }', :text]
                return
        end

        # from AllocCStruct#to_s
        if struct.kind_of?(C::Array)
                render["#{struct.type} ar_#{Expression[@curaddr]}[#{struct.length}] = ", :text] if not off
                mlist = (0...struct.length)
                el = @dasm.c_parser.sizeof(struct.type)
                fldoff = mlist.inject({}) { |h, i| h.update i => i*el }
        elsif struct.kind_of?(C::Struct)
                render["struct #{struct.name || '_'} st_#{Expression[@curaddr]} = ", :text] if not off
                fldoff = struct.fldoffset
        else
                render["union #{struct.name || '_'} un_#{Expression[@curaddr]} = ", :text] if not off
        end
        mlist ||= struct.members
        render['{', :text]
        mlist.each { |k|
                if k.kind_of? C::Variable
                        ct = k.type
                        curoff = off.to_i + (fldoff && k.name ? fldoff[k.name].to_i : struct.offsetof(@dasm.c_parser, k))
                        val = obj[k]
                else
                        ct = struct.type
                        curoff = off.to_i + fldoff[k].to_i
                        val = obj[k]
                end
                nl[]
                render[indent, :text]
                render[k.kind_of?(Integer) ? "[#{k}]" : ".#{k.name || '?'}", :text]
                render[' = ', :text]
                if val.kind_of?(Integer)
                        if ct.pointer? and ct.pointed.untypedef.kind_of?(C::Union)
                                @line_dereference[@line_text_col.length-1] = [val, ct.pointed.untypedef]
                        end
                        if val >= 0x100
                                val = '0x%X' % val
                        elsif val <= -0x100
                                val = '-0x%X' % -val
                        else
                                val = val.to_s
                        end
                elsif val.kind_of?(C::AllocCStruct)
                        render_struct(val, curoff, maxdepth-1)
                        next
                elsif not val
                        val = 'NULL' # pointer with NULL value
                else
                        raise "unknown value #{val.inspect}"
                end
                render[val, :text]
                render[',', :text]
                render['   // +%x' % curoff, :comment]
        }
        nl[]
        render['}', :text]
        render[(off ? ',' : ';'), :text]
end
rightclick(x, y) click to toggle source
# File metasm/gui/cstruct.rb, line 35
def rightclick(x, y)
        click(x, y)
        @parent_widget.clone_window(@hl_word) if @hl_word
end
set_cursor_pos(p) click to toggle source
# File metasm/gui/cstruct.rb, line 222
def set_cursor_pos(p)
        focus_addr p[0], p[1]
        @caret_x, @caret_y, @view_y = p[2, 3]
        update_caret
end
update_caret() click to toggle source

hint that the caret moved redraws the caret, change the hilighted word, redraw if needed

# File metasm/gui/cstruct.rb, line 230
def update_caret
        if @caret_x < @view_x or @caret_x >= @view_x + @cwidth or @caret_y < @view_y or @caret_y >= @view_y + @cheight
                redraw
        elsif update_hl_word(@line_text[@caret_y], @caret_x, :c)
                redraw
        else
                invalidate_caret(@oldcaret_x-@view_x, @oldcaret_y-@view_y)
                invalidate_caret(@caret_x-@view_x, @caret_y-@view_y)
        end
        @oldcaret_x, @oldcaret_y = @caret_x, @caret_y
end