diff --git a/BUILDING.txt b/BUILDING.txt
index ddd57e9..dc2abea 100644
--- a/BUILDING.txt
+++ b/BUILDING.txt
@@ -16,8 +16,10 @@ REQUIREMENTS
- Python [2.5.1, 2.6.1, 2.7]
http://www.python.org/
- - Simplified Wrapper Interface Generator (SWIG) [2.0]
+ - Simplified Wrapper Interface Generator (SWIG) [2.0.12]
http://www.swig.org/
+ Hex-Rays cannot guarantee support for IDAPython
+ versions built with other versions of SWIG.
- Unix utilities (GNU patch on Windows):
http://www.research.att.com/sw/tools/uwin/ or
diff --git a/build.py b/build.py
index f817d22..ecd4990 100644
--- a/build.py
+++ b/build.py
@@ -24,19 +24,23 @@ from distutils import sysconfig
VERBOSE = True
IDA_MAJOR_VERSION = 6
-IDA_MINOR_VERSION = 6
+IDA_MINOR_VERSION = 7
if 'IDA' in os.environ:
IDA_SDK = os.environ['IDA']
else:
- IDA_SDK = os.path.join("..", "swigsdk-versions", ("%d.%d" % (IDA_MAJOR_VERSION, IDA_MINOR_VERSION)))
+ IDA_SDK = os.path.join("..", "..", "include")
+ if not os.path.exists(IDA_SDK):
+ IDA_SDK = os.path.join("..", "swigsdk-versions", ("%d.%d" % (IDA_MAJOR_VERSION, IDA_MINOR_VERSION)))
+ assert os.path.exists(IDA_SDK), "Could not find IDA SDK include path"
+
# End of user configurable options
# IDAPython version
VERSION_MAJOR = 1
VERSION_MINOR = 7
-VERSION_PATCH = 0
+VERSION_PATCH = 1
# Determine Python version
PYTHON_MAJOR_VERSION = int(platform.python_version()[0])
@@ -89,7 +93,7 @@ BINDIST_MANIFEST = [
"examples/structure.py",
"examples/ex_gdl_qflow_chart.py",
"examples/ex_strings.py",
- "examples/ex_add_menu_item.py",
+ "examples/ex_actions.py",
"examples/ex_func_chooser.py",
"examples/ex_choose2.py",
"examples/ex_debug_names.py",
@@ -157,7 +161,6 @@ SRCDIST_MANIFEST = [
"swig/graph.i",
"swig/fpro.i",
"swig/hexrays.i",
- "tools/gendocs.py",
]
# -----------------------------------------------------------------------
@@ -396,7 +399,8 @@ def build_plugin(
platform_macros.append("WITH_HEXRAYS")
SWIG_OPTIONS += ' -DWITH_HEXRAYS '
- platform_macros.append("NDEBUG")
+ platform_macros.append("DEBUG")
+# platform_macros.append("NDEBUG")
if not '--no-early-load' in sys.argv:
platform_macros.append("PLUGINFIX")
@@ -411,6 +415,13 @@ def build_plugin(
res = os.system(swigcmd)
assert res == 0, "Failed to build the wrapper with SWIG"
+ # If we are running on windows, we have to patch some directors'
+ # virtual methods, so they have the right calling convention.
+ # Without that, compilation just won't succeed.
+ if platform == "win32":
+ res = os.system("python patch_directors_cc.py -f idaapi.h")
+ assert res == 0, "Failed to patch directors' calling conventions"
+
# Compile the wrapper
res = builder.compile("idaapi",
includes=[ PYTHON_INCLUDE_DIRECTORY, ida_include_directory ],
diff --git a/examples/ex_actions.py b/examples/ex_actions.py
new file mode 100644
index 0000000..265991a
--- /dev/null
+++ b/examples/ex_actions.py
@@ -0,0 +1,119 @@
+import idaapi
+
+class SayHi(idaapi.action_handler_t):
+ def __init__(self, message):
+ idaapi.action_handler_t.__init__(self)
+ self.message = message
+
+ def activate(self, ctx):
+ print "Hi, %s" % (self.message)
+ return 1
+
+ # You can implement update(), to inform IDA when:
+ # * your action is enabled
+ # * update() should queried again
+ # E.g., returning 'idaapi.AST_ENABLE_FOR_FORM' will
+ # tell IDA that this action is available while the
+ # user is in the current widget, and that update()
+ # must be queried again once the user gives focus
+ # to another widget.
+ #
+ # For example, the following update() implementation
+ # will let IDA know that the action is available in
+ # "IDA View-*" views, and that it's not even worth
+ # querying update() anymore until the user has moved
+ # to another view..
+ def update(self, ctx):
+ return idaapi.AST_ENABLE_FOR_FORM if ctx.form_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_FORM
+
+
+print "Creating a custom icon from raw data!"
+# Stunned panda face icon data.
+icon_data = "".join([
+ "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1F\xF3\xFF\x61\x00\x00\x02\xCA\x49\x44\x41\x54\x78\x5E\x65",
+ "\x53\x6D\x48\x53\x6F\x14\x3F\xBA\xB5\xB7\xA0\x8D\x20\x41\xF2\xBA\x5D\xB6\x0F\x56\xF4\x41\xA2\xC0\x9C\xE9\xB4\x29\x4A\x7D\xB0\x22\x7A\x11\x02\x23\x48\x2A\xD4\x74\x53\x33\x3F\xD4",
+ "\x3E\x4A\x50\x19\xE4\xB0\xD0\x22\xCD\x44\x45\x4A\x31\x8C\x92\xA2\x3E\x65\x0A\x4D\xCB\x96\x7E\xE8\xD5\x97\xCC\xFE\xFE\x37\xA7\x77\xDB\xBD\xA7\xE7\x3C\xBE\x05\x9E\xED\xB7\xB3\xF3",
+ "\x7B\x39\xF7\xEE\x19\x17\xA8\xAC\x56\xDB\x54\x82\x60\x41\xB3\x59\xBC\xFF\xAC\xF9\xCA\xB5\xAE\x86\xCA\xF9\x4E\xAF\x1B\x3B\xEA\x5D\x48\x9D\x66\xE2\x49\x27\x9F\xD5\x66\x9B\xA2\x1C",
+ "\x22\x02\xD0\x40\xE4\x81\x6C\x3B\x76\x37\x56\xE3\x37\x5F\x2F\x62\xE8\x0B\xD3\x66\x19\x7E\x53\xA7\x99\x78\xAE\x1F\x64\x3E\x21\x71\x69\x09\x5F\x20\x98\x2D\x58\x70\x24\x07\x07\x7B",
+ "\x6F\xB0\x79\x82\x61\x81\x21\xCC\xDE\x21\x54\x16\x02\xD4\x69\x26\x9E\x74\xEE\xCB\xCF\x4D\xC7\x44\xB3\x88\x7C\x81\xC5\x22\xFE\x6C\xB9\xE9\x46\x67\x46\x1A\x8A\x16\x2B\x0A\x5B\x05",
+ "\x74\x66\x65\xE1\x98\x6F\x00\x31\x32\x87\x9F\x59\x77\x66\x66\x61\x42\xBC\xC0\xF5\x6C\x47\x1A\x36\xD7\xB9\x51\x14\xC5\x1E\xBE\xA0\xC3\x5B\xD9\x98\x99\xE1\xC0\xCE\xBE\x57\x48\xD7",
+ "\x9A\x63\x68\xEA\x7C\x8A\xF6\x14\x3B\x9F\xF6\xA6\xA4\x60\xEB\xE3\x3E\x9C\x5F\xD6\x5A\x7A\xFA\x71\xBF\xC3\x81\x3D\x4D\x35\x0D\x7C\xC1\xF3\x87\x57\x43\xF9\x87\x8F\x21\x95\x5E\xAB",
+ "\x41\x83\x4E\x83\x54\xDB\x92\x76\x20\xCA\xBF\xD0\x99\x9D\xBB\x4E\xDB\xBD\xC7\x8E\x2F\x5A\x3D\x74\x3D\x50\x03\x80\x7E\x7A\x7A\x06\x46\x47\xFD\xA0\x33\x6C\x84\x18\x46\x0C\xBD\x1F",
+ "\x86\x2D\x71\x71\x00\x52\x10\x16\x17\xE6\xC1\xE7\x1B\x61\x9A\x81\x69\x31\x30\xFC\x61\x14\xB4\x3A\x3D\x20\x82\x1E\x58\xA9\x15\x05\x41\x14\x05\xB8\x58\xEE\x82\x7D\xE9\x99\x20\xCB",
+ "\x32\x94\x95\x95\xC3\xA5\xD2\x53\x00\x51\x09\xAA\x4B\x0B\xA1\xB8\xA4\x0C\x52\x53\x33\x40\xA5\x52\x81\xDB\x5D\x01\xA2\x45\x00\x45\x51\x80\x2A\x36\x12\x8D\x42\x49\x51\x01\x44\xE5",
+ "\x18\x90\x22\x0A\x98\x8C\x46\xF0\x54\x14\x42\x6D\x7D\x3B\xE4\x1C\x75\x41\xAD\xB7\x1D\x3C\x55\x85\x60\x32\x19\x41\x8A\x2A\xDC\x57\x5C\x74\x12\x28\x47\xA5\x8E\x44\xE4\xF0\x76\x5B",
+ "\x82\xA6\xCD\x5B\x0D\xB2\x12\xE6\xE4\x06\xB5\x1A\x66\xA7\x26\x41\x92\xC2\xA0\xD5\x6A\x60\x67\x92\x19\xAE\x7B\xCE\x70\x4D\x15\xAB\x01\xAD\xC1\x08\x3F\x46\x64\x6E\x8E\x9D\xF9\x13",
+ "\xE8\x1A\xFF\xE4\x63\x8A\x0E\xE6\x02\x41\xF8\x3F\x18\x82\x40\x28\x04\xFD\xDD\x75\xF0\xB6\xFF\x2E\x75\x9A\x89\x27\x9D\xFB\xC8\x4F\x39\xBE\xE0\xB4\xAB\xCE\x35\xFE\x71\x00\x16\x17",
+ "\x25\x76\x50\x26\x76\x6B\x61\x86\x08\xE4\x1D\xAF\x81\xBC\x13\x97\xA9\xD3\x4C\x3C\xE9\xDC\x47\x7E\xCA\xF1\x05\x0C\x5F\x7D\xFE\xEF\x35\x03\xAF\x9F\x00\xB0\x73\x30\x9A\xE2\x81\x0E",
+ "\xF6\xC1\xED\x52\xB8\x77\xAB\x98\x3A\xCD\xC4\x73\x9D\x7C\x6F\xDE\xF9\xCF\x53\x0E\xFE\xA9\xCD\xAE\xB3\x87\xCE\x75\x35\x54\xE1\xD0\xCB\x47\x38\x39\x36\x88\xFF\x4D\xF8\x57\x41\x33",
+ "\xF1\xA4\x93\x0F\x00\x36\xAD\x3E\x4C\x6B\xC5\xC9\x5D\x77\x6A\x2F\xB4\x31\xA3\xC4\x40\x4F\x21\x0F\xD1\x4C\x3C\xE9\x2B\xE1\xF5\x0B\xD6\x90\xC8\x90\x4C\xE6\x35\xD0\xCC\x79\x5E\xFF",
+ "\x2E\xF8\x0B\x2F\x3D\xE5\xC3\x97\x06\xCF\xCF\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82"])
+act_icon = idaapi.load_custom_icon(data=icon_data, format="png")
+
+hooks = None
+act_name = "example:add_action"
+
+if idaapi.register_action(idaapi.action_desc_t(
+ act_name, # Name. Acts as an ID. Must be unique.
+ "Say hi!", # Label. That's what users see.
+ SayHi("developer"), # Handler. Called when activated, and for updating
+ "Ctrl+F12", # Shortcut (optional)
+ "Greets the user", # Tooltip (optional)
+ act_icon)): # Icon ID (optional)
+ print "Action registered. Attaching to menu."
+
+ # Insert the action in the menu
+ if idaapi.attach_action_to_menu("Edit/Export data", act_name, idaapi.SETMENU_APP):
+ print "Attached to menu."
+ else:
+ print "Failed attaching to menu."
+
+ # Insert the action in a toolbar
+ if idaapi.attach_action_to_toolbar("AnalysisToolBar", act_name):
+ print "Attached to toolbar."
+ else:
+ print "Failed attaching to toolbar."
+
+ # We will also want our action to be available in the context menu
+ # for the "IDA View-A" widget.
+ #
+ # To do that, we could in theory retrieve a reference to "IDA View-A", and
+ # then request to "permanently" attach the action to it, using something
+ # like this:
+ # idaapi.attach_action_to_popup(ida_view_a, None, act_name, None)
+ #
+ # but alas, that won't do: widgets in IDA are very "volatile", and
+ # can be deleted & re-created on some occasions (e.g., starting a
+ # debugging session), and our efforts to permanently register our
+ # action on "IDA View-A" would be annihilated as soon as "IDA View-A"
+ # is deleted.
+ #
+ # Instead, we can opt for a different method: attach our action on-the-fly,
+ # when the popup for "IDA View-A" is being populated, right before
+ # it is displayed.
+ class Hooks(idaapi.UI_Hooks):
+ def finish_populating_tform_popup(self, form, popup):
+ # We'll add our action to all "IDA View-*"s.
+ # If we wanted to add it only to "IDA View-A", we could
+ # also discriminate on the widget's title:
+ #
+ # if idaapi.get_tform_title(form) == "IDA View-A":
+ # ...
+ #
+ if idaapi.get_tform_type(form) == idaapi.BWN_DISASM:
+ idaapi.attach_action_to_popup(form, popup, act_name, None)
+
+ hooks = Hooks()
+ hooks.hook()
+else:
+ print "Action found; unregistering."
+ # No need to call detach_action_from_menu(); it'll be
+ # done automatically on destruction of the action.
+ if idaapi.unregister_action(act_name):
+ print "Unregistered."
+ else:
+ print "Failed to unregister action."
+
+ if hooks is not None:
+ hooks.unhook()
+ hooks = None
diff --git a/examples/ex_askusingform.py b/examples/ex_askusingform.py
index 0734b24..61ba573 100644
--- a/examples/ex_askusingform.py
+++ b/examples/ex_askusingform.py
@@ -344,7 +344,7 @@ Dropdown list test
# --------------------------------------------------------------------------
def test_dropdown(execute=True):
- """Test the combobox controls"""
+ """Test the combobox controls, in a modal dialog"""
f = MyForm3()
f, args = f.Compile()
if execute:
@@ -360,6 +360,18 @@ def test_dropdown(execute=True):
f.Free()
+# --------------------------------------------------------------------------
+tdn_form = None
+def test_dropdown_nomodal():
+ """Test the combobox controls, in a non-modal form"""
+ global tdn_form
+ if tdn_form is None:
+ tdn_form = MyForm3()
+ tdn_form.modal = False
+ tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB
+ tdn_form, _ = tdn_form.Compile()
+ tdn_form.Open()
+
#
diff --git a/examples/ex_choose2.py b/examples/ex_choose2.py
index c4d72f3..00c2349 100644
--- a/examples/ex_choose2.py
+++ b/examples/ex_choose2.py
@@ -1,85 +1,133 @@
-import idaapi
-from idaapi import Choose2
-
-class MyChoose2(Choose2):
-
- def __init__(self, title, nb = 5, deflt=1):
- Choose2.__init__(self, title, [ ["Address", 10], ["Name", 30] ])
- self.n = 0
- self.items = [ self.make_item() for x in xrange(0, nb+1) ]
- self.icon = 5
- self.selcount = 0
- self.deflt = deflt
- self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"]
- print("created %s" % str(self))
-
- def OnClose(self):
- print "closed", str(self)
-
- def OnEditLine(self, n):
- self.items[n][1] = self.items[n][1] + "*"
- print("editing %d" % n)
-
- def OnInsertLine(self):
- self.items.append(self.make_item())
- print("insert line")
-
- def OnSelectLine(self, n):
- self.selcount += 1
- Warning("[%02d] selectline '%s'" % (self.selcount, n))
-
- def OnGetLine(self, n):
- print("getline %d" % n)
- return self.items[n]
-
- def OnGetSize(self):
- n = len(self.items)
- print("getsize -> %d" % n)
- return n
-
- def OnDeleteLine(self, n):
- print("del %d " % n)
- del self.items[n]
- return n
-
- def OnRefresh(self, n):
- print("refresh %d" % n)
- return n
-
- def OnCommand(self, n, cmd_id):
- if cmd_id == self.cmd_a:
- print "command A selected @", n
- elif cmd_id == self.cmd_b:
- print "command B selected @", n
- else:
- print "Unknown command:", cmd_id, "@", n
- return 1
-
- def OnGetIcon(self, n):
- r = self.items[n]
- t = self.icon + r[1].count("*")
- print "geticon", n, t
- return t
-
- def show(self):
- t = self.Show()
- if t < 0:
- return False
- self.cmd_a = self.AddCommand("command A")
- self.cmd_b = self.AddCommand("command B")
- return True
-
- def make_item(self):
- r = [str(self.n), "func_%04d" % self.n]
- self.n += 1
- return r
-
- def OnGetLineAttr(self, n):
- print("getlineattr %d" % n)
- if n == 1:
- return [0xFF0000, 0]
-
-for i in xrange(1, 5+1):
- c = MyChoose2("choose2 - sample %d" % i, i*2, deflt=i)
- r = c.show()
- print r
+import idaapi
+from idaapi import Choose2
+
+#
+
+
+class chooser_handler_t(idaapi.action_handler_t):
+ def __init__(self, thing):
+ idaapi.action_handler_t.__init__(self)
+ self.thing = thing
+
+ def activate(self, ctx):
+ sel = []
+ for i in xrange(len(ctx.chooser_selection)):
+ sel.append(str(ctx.chooser_selection.at(i)))
+ print "command %s selected @ %s" % (self.thing, ", ".join(sel))
+
+ def update(self, ctx):
+ return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM
+
+
+class MyChoose2(Choose2):
+
+ def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False):
+ Choose2.__init__(
+ self,
+ title,
+ [ ["Address", 10], ["Name", 30] ],
+ flags = flags,
+ width = width,
+ height = height,
+ embedded = embedded)
+ self.n = 0
+ self.items = [ self.make_item() for x in xrange(0, nb+1) ]
+ self.icon = 5
+ self.selcount = 0
+ self.modal = modal
+ self.popup_names = ["Inzert", "Del leet", "Ehdeet", "Ree frech"]
+
+ print("created %s" % str(self))
+
+ def OnClose(self):
+ print "closed", str(self)
+
+ def OnEditLine(self, n):
+ self.items[n][1] = self.items[n][1] + "*"
+ print("editing %d" % n)
+
+ def OnInsertLine(self):
+ self.items.append(self.make_item())
+ print("insert line")
+
+ def OnSelectLine(self, n):
+ self.selcount += 1
+ Warning("[%02d] selectline '%s'" % (self.selcount, n))
+
+ def OnGetLine(self, n):
+ print("getline %d" % n)
+ return self.items[n]
+
+ def OnGetSize(self):
+ n = len(self.items)
+ print("getsize -> %d" % n)
+ return n
+
+ def OnDeleteLine(self, n):
+ print("del %d " % n)
+ del self.items[n]
+ return n
+
+ def OnRefresh(self, n):
+ print("refresh %d" % n)
+ return n
+
+ def OnGetIcon(self, n):
+ r = self.items[n]
+ t = self.icon + r[1].count("*")
+ print "geticon", n, t
+ return t
+
+ def show(self):
+ return self.Show(self.modal) >= 0
+
+ def make_item(self):
+ r = [str(self.n), "func_%04d" % self.n]
+ self.n += 1
+ return r
+
+ def OnGetLineAttr(self, n):
+ print("getlineattr %d" % n)
+ if n == 1:
+ return [0xFF0000, 0]
+
+
+# -----------------------------------------------------------------------
+def test_choose2(modal=False):
+ global c
+ c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal)
+ r = c.show()
+ form = idaapi.get_current_tform()
+ for thing in ["A", "B"]:
+ idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing)
+
+# -----------------------------------------------------------------------
+def test_choose2_embedded():
+ global c
+ c = MyChoose2("Choose2 - embedded", nb=12, embedded = True, width=123, height=222)
+ r = c.Embedded()
+ if r == 1:
+ try:
+ if test_embedded:
+ o, sel = _idaapi.choose2_get_embedded(c)
+ print("o=%s, type(o)=%s" % (str(o), type(o)))
+ test_embedded(o)
+ finally:
+ c.Close()
+
+# -----------------------------------------------------------------------
+if __name__ == '__main__':
+
+ # Register actions
+ for thing in ["A", "B"]:
+ actname = "choose2:act%s" % thing
+ idaapi.register_action(
+ idaapi.action_desc_t(
+ actname,
+ "command %s" % thing,
+ chooser_handler_t(thing)))
+
+ #test_choose2_embedded()
+ test_choose2(False)
+
+#
diff --git a/examples/ex_custview.py b/examples/ex_custview.py
index 48e6a71..82bcf35 100644
--- a/examples/ex_custview.py
+++ b/examples/ex_custview.py
@@ -7,6 +7,18 @@ import idc
from idaapi import simplecustviewer_t
#
+class say_something_handler_t(idaapi.action_handler_t):
+ def __init__(self, thing):
+ idaapi.action_handler_t.__init__(self)
+ self.thing = thing
+
+ def activate(self, ctx):
+ print self.thing
+
+ def update(self, ctx):
+ return idaapi.AST_ENABLE_ALWAYS
+
+
# -----------------------------------------------------------------------
class mycv_t(simplecustviewer_t):
def Create(self, sn=None):
@@ -18,8 +30,6 @@ class mycv_t(simplecustviewer_t):
# Create the customviewer
if not simplecustviewer_t.Create(self, title):
return False
- self.menu_hello = self.AddPopupMenu("Hello")
- self.menu_world = self.AddPopupMenu("World")
for i in xrange(0, 100):
self.AddLine("Line %d" % i)
@@ -120,13 +130,6 @@ class mycv_t(simplecustviewer_t):
return False
return True
- def OnPopup(self):
- """
- Context menu popup is about to be shown. Create items dynamically if you wish
- @return: Boolean. True if you handled the event
- """
- print "OnPopup"
-
def OnHint(self, lineno):
"""
Hint requested for the given line number.
@@ -137,22 +140,6 @@ class mycv_t(simplecustviewer_t):
"""
return (1, "OnHint, line=%d" % lineno)
- def OnPopupMenu(self, menu_id):
- """
- A context (or popup) menu item was executed.
- @param menu_id: ID previously registered with AddPopupMenu()
- @return: Boolean
- """
- print "OnPopupMenu, menu_id=%d" % menu_id
- if menu_id == self.menu_hello:
- print "Hello"
- elif menu_id == self.menu_world:
- print "World"
- else:
- # Unhandled
- return False
- return True
-
# -----------------------------------------------------------------------
try:
# created already?
@@ -169,7 +156,16 @@ def show_win():
print "Failed to create!"
return None
x.Show()
+ tcc = x.GetTCustomControl()
+
+ # Register actions
+ for thing in ["Hello", "World"]:
+ actname = "custview:say_%s" % thing
+ idaapi.register_action(
+ idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing)))
+ idaapi.attach_action_to_popup(tcc, None, actname)
return x
+
mycv = show_win()
if not mycv:
del mycv
diff --git a/examples/ex_graph.py b/examples/ex_graph.py
index a53298d..38f912d 100644
--- a/examples/ex_graph.py
+++ b/examples/ex_graph.py
@@ -3,11 +3,24 @@
# in Python
# (c) Hex-Rays
#
-from idaapi import GraphViewer
+from idaapi import *
+
+class GraphCloser(action_handler_t):
+ def __init__(self, graph):
+ action_handler_t.__init__(self)
+ self.graph = graph
+
+ def activate(self, ctx):
+ self.graph.Close()
+
+ def update(self, ctx):
+ return AST_ENABLE_ALWAYS
+
class MyGraph(GraphViewer):
def __init__(self, funcname, result):
- GraphViewer.__init__(self, "call graph of " + funcname)
+ self.title = "call graph of " + funcname
+ GraphViewer.__init__(self, self.title)
self.funcname = funcname
self.result = result
@@ -23,25 +36,15 @@ class MyGraph(GraphViewer):
def OnGetText(self, node_id):
return str(self[node_id])
- def OnCommand(self, cmd_id):
- """
- Triggered when a menu command is selected through the menu or its hotkey
- @return: None
- """
- if self.cmd_close == cmd_id:
- self.Close()
- return
-
- print "command:", cmd_id
-
def Show(self):
if not GraphViewer.Show(self):
return False
- self.cmd_close = self.AddCommand("Close", "F2")
- if self.cmd_close == 0:
- print "Failed to add popup menu item!"
+ actname = "graph_closer:%s" % self.title
+ register_action(action_desc_t(actname, "Close %s" % self.title, GraphCloser(self)))
+ attach_action_to_popup(self.GetTCustomControl(), None, actname)
return True
+
def show_graph():
f = idaapi.get_func(here())
if not f:
diff --git a/examples/vds3.py b/examples/vds3.py
index faf0673..3057751 100644
--- a/examples/vds3.py
+++ b/examples/vds3.py
@@ -16,10 +16,28 @@ import idautils
import idaapi
import idc
-import traceback
-
NETNODE_NAME = '$ hexrays-inverted-if'
+inverter_actname = "vds3:invert"
+
+class invert_action_handler_t(idaapi.action_handler_t):
+ def __init__(self, inverter):
+ idaapi.action_handler_t.__init__(self)
+ self.inverter = inverter
+
+ def activate(self, ctx):
+ vdui = idaapi.get_tform_vdui(ctx.form)
+ self.inverter.invert_if_event(vdui)
+ return 1
+
+ def update(self, ctx):
+ vdui = idaapi.get_tform_vdui(ctx.form)
+ if vdui:
+ return idaapi.AST_ENABLE_FOR_FORM
+ else:
+ return idaapi.AST_DISABLE_FOR_FORM
+
+
class hexrays_callback_info(object):
def __init__(self):
@@ -123,14 +141,12 @@ class hexrays_callback_info(object):
def invert_if_event(self, vu):
cfunc = vu.cfunc.__deref__()
-
i = self.find_if_statement(vu)
if not i:
return False
if self.invert_if(cfunc, i):
vu.refresh_ctext()
-
self.add_location(i.ea)
return True
@@ -157,39 +173,27 @@ class hexrays_callback_info(object):
return
- def menu_callback(self):
- try:
- self.invert_if_event(self.vu)
- except:
- traceback.print_exc()
- return 0
-
def event_callback(self, event, *args):
- try:
- if event == idaapi.hxe_keyboard:
- vu, keycode, shift = args
+ if event == idaapi.hxe_populating_popup:
+ form, phandle, vu = args
+ res = idaapi.attach_action_to_popup(vu.ct, None, inverter_actname)
- if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("I") and shift == 0:
- if self.invert_if_event(vu):
- return 1
-
- elif event == idaapi.hxe_right_click:
- self.vu, = args
- idaapi.add_custom_viewer_popup_item(self.vu.ct, "Invert then/else", "I", self.menu_callback)
-
- elif event == idaapi.hxe_maturity:
- cfunc, maturity = args
-
- if maturity == idaapi.CMAT_FINAL:
- self.restore(cfunc)
- except:
- traceback.print_exc()
+ elif event == idaapi.hxe_maturity:
+ cfunc, maturity = args
+ if maturity == idaapi.CMAT_FINAL:
+ self.restore(cfunc)
return 0
if idaapi.init_hexrays_plugin():
i = hexrays_callback_info()
+ idaapi.register_action(
+ idaapi.action_desc_t(
+ inverter_actname,
+ "Invert then/else",
+ invert_action_handler_t(i),
+ "I"))
idaapi.install_hexrays_callback(i.event_callback)
else:
print 'invert-if: hexrays is not available.'
diff --git a/examples/vds_xrefs.py b/examples/vds_xrefs.py
index d748add..b78eb26 100644
--- a/examples/vds_xrefs.py
+++ b/examples/vds_xrefs.py
@@ -221,7 +221,10 @@ class XrefsForm(idaapi.PluginForm):
addresses.append(parent.ea)
self.functions.append(cfunc.entry_ea)
- self.items.append((parent.ea, idc.GetFunctionName(cfunc.entry_ea), self.get_decompiled_line(cfunc, int(parent.ea))))
+ self.items.append((
+ parent.ea,
+ idc.GetFunctionName(cfunc.entry_ea),
+ self.get_decompiled_line(cfunc, parent.ea)))
return []
@@ -260,57 +263,62 @@ class XrefsForm(idaapi.PluginForm):
def OnClose(self, form):
pass
+
+class show_xrefs_ah_t(idaapi.action_handler_t):
+ def __init__(self):
+ idaapi.action_handler_t.__init__(self)
+ self.sel = None
+
+ def activate(self, ctx):
+ vu = idaapi.get_tform_vdui(ctx.form)
+ if not vu or not self.sel:
+ print "No vdui? Strange, since this action should be enabled only for pseudocode views."
+ return 0
+
+ form = XrefsForm(self.sel)
+ form.Show()
+ return 1
+
+ def update(self, ctx):
+ vu = idaapi.get_tform_vdui(ctx.form)
+ if not vu:
+ return idaapi.AST_DISABLE_FOR_FORM
+ else:
+ vu.get_current_item(idaapi.USE_KEYBOARD)
+ item = vu.item
+ self.sel = None
+ if item.citype == idaapi.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'):
+ # if an expression is selected. verify that it's either a cot_obj, cot_memref or cot_memptr
+ self.sel = item.it.to_specific_type
+
+ elif item.citype == idaapi.VDI_FUNC:
+ # if the function itself is selected, show xrefs to it.
+ self.sel = item.f
+
+ return idaapi.AST_ENABLE if self.sel else idaapi.AST_DISABLE
+
class hexrays_callback_info(object):
def __init__(self):
- self.vu = None
return
- def show_xrefs(self, vu):
-
- vu.get_current_item(idaapi.USE_KEYBOARD)
- item = vu.item
-
- sel = None
- if item.citype == idaapi.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'):
- # if an expression is selected. verify that it's either a cot_obj, cot_memref or cot_memptr
- sel = item.it.to_specific_type
-
- elif item.citype == idaapi.VDI_FUNC:
- # if the function itself is selected, show xrefs to it.
- sel = item.f
- else:
- return False
-
- form = XrefsForm(sel)
- form.Show()
- return True
-
- def menu_callback(self):
- self.show_xrefs(self.vu)
- return 0
-
def event_callback(self, event, *args):
try:
- if event == idaapi.hxe_keyboard:
- vu, keycode, shift = args
-
- if idaapi.lookup_key_code(keycode, shift, True) == idaapi.get_key_code("X") and shift == 0:
- if self.show_xrefs(vu):
- return 1
-
- elif event == idaapi.hxe_right_click:
- self.vu = args[0]
- idaapi.add_custom_viewer_popup_item(self.vu.ct, "Xrefs", "X", self.menu_callback)
-
+ if event == idaapi.hxe_populating_popup:
+ form, phandle, vu = args
+ idaapi.attach_action_to_popup(form, phandle, "vdsxrefs:show", None)
except:
traceback.print_exc()
return 0
if idaapi.init_hexrays_plugin():
- i = hexrays_callback_info()
- idaapi.install_hexrays_callback(i.event_callback)
+ adesc = idaapi.action_desc_t('vdsxrefs:show', 'Show xrefs', show_xrefs_ah_t(), "Ctrl+X")
+ if idaapi.register_action(adesc):
+ i = hexrays_callback_info()
+ idaapi.install_hexrays_callback(i.event_callback)
+ else:
+ print "Couldn't register action."
else:
- print 'invert-if: hexrays is not available.'
+ print 'hexrays is not available.'
diff --git a/hrdoc.cfg b/hrdoc.cfg
new file mode 100644
index 0000000..2d60bcd
--- /dev/null
+++ b/hrdoc.cfg
@@ -0,0 +1,148 @@
+[epydoc]
+# The list of objects to document. Objects can be named using
+# dotted names, module filenames, or package directory names.
+# Aliases for this option include "objects" and "values".
+modules: idc, idautils, idaapi
+#modules: pywraps
+
+# The type of output that should be generated. Should be one
+# of: html, text, latex, dvi, ps, pdf.
+output: html
+
+# The path to the output directory. May be relative or absolute.
+target: hr-html/
+
+# An integer indicating how verbose epydoc should be. The default
+# value is 0; negative values will supress warnings and errors;
+# positive values will give more verbose output.
+verbosity: 0
+
+# A boolean value indicating that Epydoc should show a tracaback
+# in case of unexpected error. By default don't show tracebacks
+debug: 0
+
+# If True, don't try to use colors or cursor control when doing
+# textual output. The default False assumes a rich text prompt
+simple-term: 0
+
+
+### Generation options
+
+# The default markup language for docstrings, for modules that do
+# not define __docformat__. Defaults to epytext.
+docformat: epytext
+
+# Whether or not parsing should be used to examine objects.
+parse: yes
+
+# Whether or not introspection should be used to examine objects.
+introspect: yes
+
+# Don't examine in any way the modules whose dotted name match this
+# regular expression pattern.
+#exclude
+
+# Don't perform introspection on the modules whose dotted name match this
+# regular expression pattern.
+#exclude-introspect
+
+# Don't perform parsing on the modules whose dotted name match this
+# regular expression pattern.
+#exclude-parse
+
+# The format for showing inheritance objects.
+# It should be one of: 'grouped', 'listed', 'included'.
+inheritance: listed
+
+# Whether or not to inclue private variables. (Even if included,
+# private variables will be hidden by default.)
+private: no
+
+# Whether or not to list each module's imports.
+imports: no
+
+# Whether or not to include syntax highlighted source code in
+# the output (HTML only).
+sourcecode: no
+
+# Whether or not to includea a page with Epydoc log, containing
+# effective option at the time of generation and the reported logs.
+include-log: no
+
+
+### Output options
+
+# The documented project's name.
+name: IDAPython
+
+# The CSS stylesheet for HTML output. Can be the name of a builtin
+# stylesheet, or the name of a file.
+css: white
+
+# The documented project's URL.
+url: http://code.google.com/p/idapython/
+
+# HTML code for the project link in the navigation bar. If left
+# unspecified, the project link will be generated based on the
+# project's name and URL.
+link: Hex-Rays
+
+# The "top" page for the documentation. Can be a URL, the name
+# of a module or class, or one of the special names "trees.html",
+# "indices.html", or "help.html"
+#top: os.path
+
+# An alternative help file. The named file should contain the
+# body of an HTML file; navigation bars will be added to it.
+#help: my_helpfile.html
+
+# Whether or not to include a frames-based table of contents.
+frames: yes
+
+# Whether each class should be listed in its own section when
+# generating LaTeX or PDF output.
+separate-classes: no
+
+
+### API linking options
+
+# Define a new API document. A new interpreted text role
+# will be created
+#external-api: epydoc
+
+# Use the records in this file to resolve objects in the API named NAME.
+#external-api-file: epydoc:api-objects.txt
+
+# Use this URL prefix to configure the string returned for external API.
+#external-api-root: epydoc:http://epydoc.sourceforge.net/api
+
+
+### Graph options
+
+# The list of graph types that should be automatically included
+# in the output. Graphs are generated using the Graphviz "dot"
+# executable. Graph types include: "classtree", "callgraph",
+# "umlclass". Use "all" to include all graph types
+#graph: classtree
+
+# The path to the Graphviz "dot" executable, used to generate
+# graphs.
+#dotpath: /usr/local/bin/dot
+
+# The name of one or more pstat files (generated by the profile
+# or hotshot module). These are used to generate call graphs.
+#pstat: profile.out
+
+# Specify the font used to generate Graphviz graphs.
+# (e.g., helvetica or times).
+graph-font: Helvetica
+
+# Specify the font size used to generate Graphviz graphs.
+#graph-font-size: 10
+
+
+### Return value options
+
+# The condition upon which Epydoc should exit with a non-zero
+# exit status. Possible values are error, warning, docstring_warning
+#fail-on: error
\ No newline at end of file
diff --git a/hrdoc.css b/hrdoc.css
new file mode 100644
index 0000000..dc2b99e
--- /dev/null
+++ b/hrdoc.css
@@ -0,0 +1,334 @@
+
+a { text-decoration: none; }
+a:link {color: #0033CC}
+a:visited {color: #0033CC}
+a:hover {color: #0099FF}
+a:active {color: #0033CC}
+
+h3
+{
+ margin-top: 22px;
+}
+
+p
+{
+ margin-left: 12px;
+ margin-right: 12px;
+ margin-top: 12px;
+}
+
+
+body
+{
+ background-color:#ffffff;
+ font-family: Verdana;
+ font-size: 12px;
+}
+
+.pre
+{
+ font-family: FixedSys, Courier, Monospace;
+ white-space: pre;
+ color: #0000A0;
+ display: inline;
+}
+
+.checkpoint
+{
+ font-weight: bold;
+ background-color: #FFFF80;
+}
+
+.hi /* highlight */
+{
+ display: inline;
+ background-color: #FFFF70;
+}
+
+/*---------------------- The main page, not used now -------------------*/
+
+#main
+{
+ margin: 0 auto 0 auto;
+ position: relative;
+ background-image:url(/images/main.jpg);
+ width: 845px;
+ height: 568px;
+ color:#0033CC;
+ font-family: Verdana;
+ font-style: italic;
+ border: 1px solid blue;
+}
+
+#logo
+{
+ position: absolute;
+ right: 10px;
+ top: 15px;
+ background-image: url(/images/bigname.png);
+ width: 357px;
+ height: 78px;
+}
+
+#menu
+{
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+}
+
+#menu li
+{
+ display: block;
+ list-style-type: none;
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+}
+
+#menu a, #action a
+{
+ margin: 0px 0px 0px 0px;
+ padding-left: 8px;
+ padding-right: 8px;
+ display: block;
+ width: 140px;
+}
+
+#action a
+{
+ margin: 0px 0px 0px 0px;
+ padding-left: 8px;
+ padding-right: 8px;
+ display: block;
+ width: 100%;
+}
+
+#menu li a:hover, #action:hover
+{
+ background-image: url(/images/bigbutton.jpg);
+ color: white;
+}
+
+#menu a:hover, #action a:hover { color:#0033CC }
+
+#action
+{
+ position: absolute;
+ top: 300px;
+ left: 600px;
+ display: block;
+ width:220px;
+ padding-right: 8px;
+ border: 2px solid red;
+}
+
+
+/*--------------------------------------------------------------------------*/
+#regular-page
+{
+ margin: 0 auto;
+ padding: 0;
+ width: 800px;
+ display: block;
+ border: 1px solid blue;
+ position: relative;
+}
+
+#navmenu
+{
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 4px 0px;
+ position: relative;
+ background-color: #000000;
+ height: 28px;
+}
+
+#navigation
+{
+ text-align: center;
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+}
+
+#navigation li
+{
+ display: inline;
+ list-style-type: none;
+}
+
+#navigation a
+{
+ width: 99px;
+ height: 20px;
+ margin: 0px 0px 0px 0px;
+ padding: 6px 0px 2px 0px;
+ position: absolute;
+ font-weight: bold;
+ display: inline;
+}
+
+#b1 { left: 0px; }
+#b2 { left: 99px; }
+#b3 { left: 198px; }
+#b4 { left: 297px; }
+#b5 { left: 396px; }
+#b6 { left: 495px; }
+#b7 { left: 594px; }
+#b8 { left: 693px; }
+
+#navigation li a
+{
+ color: #77BBFF;
+}
+
+#navigation li a:hover
+{
+ color: white;
+}
+
+#navigation a:hover
+{
+ background-image:url(/images/bigbutton.jpg);
+}
+
+#header
+{
+ display: block;
+ background-image:url(/images/header.jpg);
+ background-repeat: no-repeat;
+ background-color: #000000;
+ height: 160px;
+ margin: 0px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+ position: relative;
+}
+
+#header-text
+{
+ margin: 0px 0px 0px 0px;
+ font-size: 24px;
+/* font-style: italic;*/
+ color: #77BBFF;
+ position: absolute;
+ bottom: 1px;
+ right: 10px;
+ padding-right: 5px;
+ text-align: right;
+}
+
+#col1, #col2
+{
+ padding: 10px 7px 10px 7px;
+}
+
+#col1
+{
+ float: left;
+ width: 170px;
+ font-size: 24px;
+ font-style: italic;
+}
+
+#col2
+{
+ float: right;
+ width: 600px;
+ border-left: 1px solid #0000FF;
+}
+
+#bmenu
+{
+ color: #77BBFF;
+ text-align: center;
+ font-size: 10px;
+ clear: both;
+ list-style-type: none;
+ margin-bottom: 2px;
+}
+
+#bmenu li
+{
+ display: inline;
+}
+
+#footer
+{
+ color: #77BBFF;
+ text-align: center;
+ font-size: 10px;
+ clear: both;
+ margin-top: 0px;
+ padding-left: 2px;
+ padding-bottom: 2px;
+}
+
+#footer a:link {color: #0099FF}
+#footer a:visited {color: #0099FF}
+#footer a:hover {color: #0033CC}
+
+/*------ User Manual ------------------------------------------------------*/
+
+#manual h1
+{
+ font-family: "Trebuchet MS", Trebuchet, Verdana, Arial, sans-serif;
+ font-size: 22px;
+ background: #bfffbf;
+ text-align: center;
+}
+
+#manual h3
+{
+ font-size: 18px;
+ font-weight: bold;
+ line-height: 20px;
+ background: #bfffff;
+}
+
+#manual
+{
+ padding: 0;
+ font-family: "Trebuchet MS", Trebuchet, Verdana, Arial, sans-serif;
+ font-size: medium;
+ width: 570px;
+}
+
+/*------ Comparison Page --------------------------------------------------*/
+
+.compare
+{
+ border-top: 1px solid blue;
+}
+
+.cmphdr
+{
+ font-size: 22px;
+ text-align: center;
+ padding: 10px 7px 10px 7px;
+}
+
+.cmptext
+{
+ white-space: pre;
+ font-family: FixedSys, Courier, Monospace;
+ font-size: x-small;
+ color: blue;
+ background: white;
+}
+
+.cmpasm
+{
+ background: #DDEEFF;
+}
+
+.cmpc
+{
+ background: #EEFFEE;
+}
+
+.cmptell
+{
+ padding: 10px 7px 10px 7px;
+ clear: both;
+ font-size: small;
+ width: 600px;
+}
+
diff --git a/hrdoc.py b/hrdoc.py
new file mode 100644
index 0000000..e815e1c
--- /dev/null
+++ b/hrdoc.py
@@ -0,0 +1,116 @@
+import os
+import sys
+import shutil
+from glob import glob
+
+# --------------------------------------------------------------------------
+DOC_DIR = 'hr-html'
+PYWRAPS_FN = 'idaapi.py'
+
+# --------------------------------------------------------------------------
+def add_footer(lines):
+ S1 = 'Generated by Epydoc'
+ S2 = ''
+ p = lines.find(S1)
+
+ if p == -1:
+ return None
+
+ p = lines.find(S2, p)
+ if p == -1:
+ return None
+
+ p += len(S2)
+
+ return lines[0:p] + '\n' + lines[p:]
+
+# --------------------------------------------------------------------------
+def define_idaapi_resolver():
+ """
+ Whenever a module named \"idaapi_\" is
+ spotted, turn it into \"idaapi\".
+ """
+ import epydoc.apidoc
+ dn = epydoc.apidoc.DottedName.__init__
+ def resolver(piece):
+ if piece is not None and isinstance(piece, basestring) and piece.startswith("idaapi_"):
+ return "idaapi"
+ else:
+ return piece
+ def wrapper(self, *pieces, **options):
+ return dn(self, *map(resolver, pieces), **options);
+ epydoc.apidoc.DottedName.__init__ = wrapper
+
+# --------------------------------------------------------------------------
+def gen_docs():
+ import epydoc.cli
+ import swigdocs
+
+ define_idaapi_resolver()
+
+ swigdocs.gen_docs(outfn = 'pywraps.py')
+ # append obj/x86_win_vc_32/idaapi.py to it
+# os.system(r'copy /b idaapi.py+..\obj\x86_win_vc_32\idaapi.py idaapi.py')
+
+ # delete all output files
+ for fn in glob('hr-html/*'):
+ os.unlink(fn)
+
+ epydoc.cli.optparse.sys.argv = [ 'epydoc',
+ '--config', '../hrdoc.cfg',
+ '--simple-term'
+ ]
+
+ # Generate the documentation
+ epydoc.cli.cli()
+
+# --------------------------------------------------------------------------
+def patch_docs():
+ shutil.copy('../../hrdoc.css', 'epydoc.css')
+ os.system('chmod +w epydoc.css')
+
+ for fn in glob('*.html'):
+ f = open(fn, 'r')
+ lines = f.read()
+ f.close()
+
+ r = add_footer(lines)
+ if not r:
+ print "-",
+ continue
+
+ f = open(fn, 'w')
+ f.write(r)
+ f.close()
+ print "+",
+
+ print "\nDocumentation patched!"
+
+# --------------------------------------------------------------------------
+def main():
+ # Save old directory and adjust import path
+ curdir = os.getcwd() + os.sep
+ sys.path.append(curdir + 'python')
+ sys.path.append(curdir + 'tools')
+ sys.path.append(curdir + 'docs')
+
+ old_dir = os.getcwd()
+
+ try:
+ print "Generating documentation....."
+
+ os.chdir('docs')
+ gen_docs()
+
+ os.chdir(DOC_DIR)
+ patch_docs()
+
+ print "Documentation generated!"
+
+ finally:
+ os.chdir(old_dir)
+
+# --------------------------------------------------------------------------
+if __name__ == '__main__':
+ main()
+ Exit(0)
diff --git a/idaapi.i b/idaapi.i
new file mode 100644
index 0000000..34f2cba
--- /dev/null
+++ b/idaapi.i
@@ -0,0 +1,4 @@
+// We need this file just to avoid a warning from swig that the input file is not
+// specifed precisely and swig had to find it using the header path.
+
+%include "swig/idaapi.i"
\ No newline at end of file
diff --git a/inject_pydoc.py b/inject_pydoc.py
new file mode 100644
index 0000000..a6a849d
--- /dev/null
+++ b/inject_pydoc.py
@@ -0,0 +1,340 @@
+#
+# This (non-idiomatic) python script is in charge of
+# 1) Parsing all .i files in the 'swig/' directory, and
+# collecting all function, classes & methods comments
+# that can be found between / tags.
+# 2) Reading, line by line, the idaapi_.py.raw
+# file, and for each function, class & method found
+# there, associate a possily previously-harvested
+# pydoc documentation.
+# 3) Generating the idaapi_.py file.
+#
+
+import re
+import os
+import os.path
+
+DOCSTR_MARKER = "\"\"\""
+
+# --------------------------------------------------------------------------
+def split_oneliner_comments(lines):
+ out_lines = []
+ for line in lines:
+
+ line = line.rstrip()
+
+ if line.startswith("#"):
+ out_lines.append(line)
+ continue
+
+ if len(line) == 0:
+ out_lines.append("")
+ continue
+
+ pfx = None
+ while line.find(DOCSTR_MARKER) > -1:
+ idx = line.find(DOCSTR_MARKER)
+ meat = line[0:idx]
+ try:
+ if len(meat.strip()) == 0:
+ pfx = meat
+ out_lines.append(pfx + DOCSTR_MARKER)
+ else:
+ out_lines.append((pfx if pfx is not None else "") + meat)
+ out_lines.append((pfx if pfx is not None else "") + DOCSTR_MARKER)
+ except:
+ raise BaseException("Error at line: " + line)
+ line = line[idx + len(DOCSTR_MARKER):]
+ if len(line.strip()) > 0:
+ out_lines.append((pfx if pfx is not None else "") + line)
+ return out_lines
+
+# --------------------------------------------------------------------------
+def dedent(lines):
+ if len(lines) < 1:
+ return lines
+ line0 = lines[0]
+ indent = len(line0) - len(line0.lstrip())
+ if indent < 0:
+ raise BaseException("Couldn't find \" in '" + line0 + "'")
+ expect = " " * indent
+ def proc(l):
+ #print "DE-INDENTING '%s'" % l
+ if len(l) == 0:
+ return l # Keep empty lines
+ prefix = l[0:indent]
+ if prefix != expect:
+ raise BaseException("Line: '" + l + "' has wrong indentation. Expected " + str(indent) + " spaces.")
+ return l[indent:]
+ return map(proc, lines)
+
+# --------------------------------------------------------------------------
+def get_fun_name(line):
+ return re.search("def ([^\(]*)\(", line).group(1)
+
+# --------------------------------------------------------------------------
+def get_class_name(line):
+ return re.search("class ([^\(:]*)[\(:]?", line).group(1)
+
+# --------------------------------------------------------------------------
+def get_indent_string(line):
+ indent = len(line) - len(line.lstrip())
+ return " " * indent
+
+# --------------------------------------------------------------------------
+class collect_idaapi_pydoc_t(object):
+ """
+ Search in all files in the 'plugins/idapython/swig/' directory
+ for possible additional we could use later.
+ """
+ S_UNKNOWN = 0
+ S_IN_PYDOC = 1
+ S_IN_DOCSTR = 2
+ # S_STOP = 5
+ PYDOC_START = "#"
+ PYDOC_END = "#"
+ DOCSTR_MARKER = DOCSTR_MARKER #"\"\"\""
+ state = S_UNKNOWN
+ lines = None
+
+ def __init__(self):
+ self.idaapi_pydoc = {"funcs" : {}, "classes" : {}}
+
+ def next(self):
+ line = self.lines[0]
+ self.lines = self.lines[1:]
+ return line
+
+ def set_fun(self, name, collected):
+ self.idaapi_pydoc["funcs"][name] = dedent(collected)
+
+ def collect_fun(self, fun_name):
+ collected = []
+ while len(self.lines) > 0:
+ line = self.next()
+ if self.state is self.S_IN_PYDOC:
+ if line.startswith(self.PYDOC_END):
+ self.state = self.S_UNKNOWN
+ return self.set_fun(fun_name, collected)
+ elif line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_DOCSTR
+ elif not line.startswith(" "):
+ return self.set_fun(fun_name, collected)
+ elif self.state is self.S_IN_DOCSTR:
+ if line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_PYDOC
+ return self.set_fun(fun_name, collected)
+ else:
+ collected.append(line)
+ else:
+ raise BaseException("Unexpected state: " + str(self.state))
+
+ def set_method(self, cls, method_name, collected):
+ cls["methods"][method_name] = dedent(collected)
+
+ def collect_method(self, cls, method_name):
+ collected = []
+ while len(self.lines) > 0:
+ line = self.next()
+ if self.state is self.S_IN_PYDOC:
+ if line.startswith(self.PYDOC_END):
+ self.state = self.S_UNKNOWN
+ return self.set_method(cls, method_name, collected)
+ elif line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_DOCSTR
+ elif self.state is self.S_IN_DOCSTR:
+ if line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_PYDOC
+ return self.set_method(cls, method_name, collected)
+ else:
+ collected.append(line)
+
+ def set_class(self, name, cls_data, collected):
+ cls_data["doc"] = dedent(collected) if len(collected) > 0 else None
+ self.idaapi_pydoc["classes"][name] = cls_data
+
+ def collect_cls(self, cls_name):
+ collected = []
+ cls = {"methods":{},"doc":None}
+ while len(self.lines) > 0:
+ line = self.next()
+ if self.state is self.S_IN_PYDOC:
+ if line.startswith(" def "):
+ self.collect_method(cls, get_fun_name(line))
+ if self.state == self.S_UNKNOWN: # method marked end of
+ return self.set_class(cls_name, cls, collected)
+ elif line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_DOCSTR
+ elif line.startswith(self.PYDOC_END):
+ self.state = self.S_UNKNOWN
+ return self.set_class(cls_name, cls, collected)
+ elif len(line) > 1 and not line.startswith(" "):
+ return self.set_class(cls_name, cls, collected)
+ elif self.state is self.S_IN_DOCSTR:
+ if line.find(self.DOCSTR_MARKER) > -1:
+ self.state = self.S_IN_PYDOC
+ else:
+ collected.append(line)
+
+ def collect_file_pydoc(self, filename):
+ self.state = self.S_UNKNOWN
+ with open(filename, "rt") as f:
+ self.lines = split_oneliner_comments(f.readlines())
+ context = None
+ doc = []
+ while len(self.lines) > 0:
+ line = self.next()
+ if self.state is self.S_UNKNOWN:
+ if line.startswith(self.PYDOC_START):
+ self.state = self.S_IN_PYDOC
+ elif self.state is self.S_IN_PYDOC:
+ if line.startswith("def "):
+ self.collect_fun(get_fun_name(line))
+ elif line.startswith("class "):
+ self.collect_cls(get_class_name(line))
+ elif line.startswith(self.PYDOC_END):
+ self.state = self.S_UNKNOWN
+
+ def collect(self, dirpath):
+ for root, dirs, files in os.walk(dirpath):
+ for f in files:
+ self.collect_file_pydoc(os.path.join(root, f))
+ return self.idaapi_pydoc
+
+
+# --------------------------------------------------------------------------
+class idaapi_fixer_t(object):
+ lines = None
+
+ def __init__(self, collected_info):
+ self.collected_info = collected_info
+
+ def next(self):
+ line = self.lines[0]
+ self.lines = self.lines[1:]
+ return line
+
+ def copy(self, out):
+ line = self.next()
+ out.append(line)
+ return line
+
+ def push_front(self, line):
+ self.lines.insert(0, line)
+
+ def get_fun_info(self, fun_name):
+ if fun_name in self.collected_info["funcs"]:
+ return self.collected_info["funcs"][fun_name]
+ else:
+ return None
+
+ def get_class_info(self, class_name):
+ if class_name in self.collected_info["classes"]:
+ return self.collected_info["classes"][class_name]
+ else:
+ return None
+
+ def get_method_info(self, class_info, method_name):
+ if method_name in class_info["methods"]:
+ return class_info["methods"][method_name]
+ else:
+ return None
+
+ def fix_fun(self, out, class_info=None):
+ line = self.copy(out)
+ fun_name = get_fun_name(line)
+ line = self.copy(out)
+ if line.find(DOCSTR_MARKER) > -1:
+ # Determine indentation level
+ indent = get_indent_string(line)
+ while True:
+ line = self.next()
+ if line.find(DOCSTR_MARKER) > -1:
+ if class_info is None:
+ found = self.get_fun_info(fun_name)
+ else:
+ found = self.get_method_info(class_info, fun_name)
+ if found is not None:
+ out.append("\n")
+ for fl in found:
+ out.append(indent + fl)
+ out.append(line)
+ break
+ else:
+ out.append(line)
+
+ def fix_method(self, class_info, out):
+ return self.fix_fun(out, class_info)
+
+ def fix_cls(self, out):
+ line = self.copy(out)
+ cls_name = get_class_name(line)
+ class_info = self.get_class_info(cls_name)
+ if class_info is None:
+ return
+
+ line = self.copy(out)
+ indent = get_indent_string(line)
+
+ # If class has doc, maybe inject additional
+ if line.find(DOCSTR_MARKER) > -1:
+ while True:
+ line = self.next()
+ if line.find(DOCSTR_MARKER) > -1:
+ doc = class_info["doc"]
+ if doc is not None:
+ out.append("\n")
+ for dl in doc:
+ out.append(indent + dl)
+ out.append(line)
+ break
+ else:
+ out.append(line)
+
+ # Iterate on class methods, and possibly patch
+ # their docstring
+ method_start = indent + "def "
+ while True:
+ line = self.next()
+ # print "Fixing methods.. Line is '%s'" % line
+ if line.startswith(indent) or line.strip() == "":
+ if line.startswith(method_start):
+ self.push_front(line)
+ self.fix_method(class_info, out)
+ else:
+ out.append(line)
+ else:
+ self.push_front(line)
+ break
+
+ def fix_file(self, idaapi_filename, out_filename):
+ with open(idaapi_filename, "rt") as f:
+ self.lines = split_oneliner_comments(f.readlines())
+ out = []
+ while len(self.lines) > 0:
+ line = self.next()
+ # print "LINE: %s" % line
+ if line.startswith("def "):
+ self.push_front(line)
+ self.fix_fun(out)
+ elif line.startswith("class "):
+ self.push_front(line)
+ self.fix_cls(out)
+ else:
+ out.append(line)
+ with open(out_filename, "wt") as o:
+ for ol in out:
+ o.write(ol)
+ o.write("\n")
+
+# --------------------------------------------------------------------------
+if __name__ == '__main__':
+ import sys
+ collecter = collect_idaapi_pydoc_t()
+ collected = collecter.collect(sys.argv[1])
+ # import pprint
+ # pprint.pprint(collected, indent=2)
+ fixer = idaapi_fixer_t(collected)
+ target_file = sys.argv[2]
+ result_file = sys.argv[3]
+ fixer.fix_file(target_file, result_file)
diff --git a/patch_directors_cc.py b/patch_directors_cc.py
new file mode 100644
index 0000000..eaa38d8
--- /dev/null
+++ b/patch_directors_cc.py
@@ -0,0 +1,57 @@
+
+import os, shutil, sys, optparse
+
+if __name__ == "__main__":
+
+ p = optparse.OptionParser(description='Patch calling conventions for some functions, so it builds on windows')
+ p.add_option('-v', "--verbose", dest="verbose", action="store_true")
+ p.add_option('-f', "--file", dest="path", type="string", help="File name, without extension.")
+ opts, _ = p.parse_args(sys.argv[1:])
+
+ if not opts.path:
+ p.print_help()
+ sys.exit(1)
+
+ patches = [
+ # user_lvar_visitor_t
+ "virtual int idaapi handle_retrieved_info",
+ "virtual int idaapi handle_retrieved_mapping",
+ "virtual int idaapi get_info_qty_for_saving",
+ "virtual bool idaapi get_info_for_saving",
+ "virtual lvar_mapping_t const *idaapi get_info_mapping_for_saving",
+
+ # ctree_visitor_t
+ "virtual int idaapi visit_insn",
+ "virtual int idaapi visit_expr",
+ "virtual int idaapi leave_insn",
+ "virtual int idaapi leave_expr",
+
+ # ctree_parentee_t
+ "virtual int idaapi visit_insn",
+ "virtual int idaapi visit_expr",
+ "virtual int idaapi leave_insn",
+ "virtual int idaapi leave_expr",
+
+ # cfunc_parentee_t
+ "virtual int idaapi visit_insn",
+ "virtual int idaapi visit_expr",
+ "virtual int idaapi leave_insn",
+ "virtual int idaapi leave_expr",
+ ]
+
+ path = opts.path
+ outlines = []
+ outpath = "%s.cc" % path
+ with open(path, "r") as f:
+ lines = f.readlines()
+ for line in lines:
+ for patch in patches:
+ from_text = patch.replace("idaapi ", "")
+ if line.find(from_text) > -1:
+ line = line.replace(from_text, patch)
+ patches.remove(patch)
+ break
+ outlines.append(line)
+ with open(outpath, "w") as f:
+ f.writelines(outlines)
+ shutil.move(outpath, path)
diff --git a/python.cpp b/python.cpp
index c4f13b7..a43e6f1 100644
--- a/python.cpp
+++ b/python.cpp
@@ -288,9 +288,27 @@ static void PythonEvalOrExec(
}
else
{
- qstring result_str;
- if ( py_result.o != Py_None && PyW_ObjectToString(py_result.o, &result_str) )
- msg("%s\n", result_str.c_str());
+ if ( py_result.o != Py_None )
+ {
+ bool ok = false;
+ if ( PyUnicode_Check(py_result.o) )
+ {
+ newref_t py_result_utf8(PyUnicode_AsUTF8String(py_result.o));
+ ok = py_result_utf8 != NULL;
+ if ( ok )
+ umsg("%s\n", PyString_AS_STRING(py_result_utf8.o));
+ }
+ else
+ {
+ qstring result_str;
+ ok = PyW_ObjectToString(py_result.o, &result_str);
+ if ( ok )
+ msg("%s\n", result_str.c_str());
+ }
+
+ if ( !ok )
+ msg("*** IDAPython: Couldn't convert evaluation result\n");
+ }
}
}
}
@@ -419,8 +437,9 @@ static int PyRunFile(const char *FileName)
// C runtime library could not be loaded. So we check the disk space before
// calling it.
char curdir[QMAXPATH];
- if ( _getcwd(curdir, sizeof(curdir)) == NULL
- || getdspace(curdir) == 0 )
+ // check if the current directory is accessible. if not, qgetcwd won't return
+ qgetcwd(curdir, sizeof(curdir));
+ if ( getdspace(curdir) == 0 )
{
warning("No free disk space on %s, python will not be available", curdir);
return 0;
@@ -428,7 +447,7 @@ static int PyRunFile(const char *FileName)
#endif
PYW_GIL_CHECK_LOCKED_SCOPE();
- PyObject *file_obj = PyFile_FromString((char*)FileName, "r"); //lint !e1776
+ PyObject *file_obj = PyFile_FromString((char*)FileName, "r"); //lint !e605
PyObject *globals = GetMainGlobals();
if ( globals == NULL || file_obj == NULL )
{
@@ -703,6 +722,18 @@ bool idaapi IDAPython_extlang_run(
return ok;
}
+//-------------------------------------------------------------------------
+static void wrap_in_function(qstring *out, const qstring &body, const char *name)
+{
+ out->sprnt("def %s():\n", name);
+ // dont copy trailing whitespace
+ int i = body.length()-1;
+ while ( i >= 0 && qisspace(body.at(i)) )
+ i--;
+ out->append(body.substr(0, i+1));
+ out->replace("\n", "\n ");
+}
+
//-------------------------------------------------------------------------
// Compile callback for Python external language evaluator
bool idaapi IDAPython_extlang_compile(
@@ -722,11 +753,9 @@ bool idaapi IDAPython_extlang_compile(
// try compiling as a list of statements
// wrap them into a function
handle_python_error(errbuf, errbufsize);
- qstring expr_copy = expr;
- expr_copy.replace("\n", "\n ");
- qstring qexpr;
- qexpr.sprnt("def %s():\n %s", name, expr_copy.c_str());
- code = (PyCodeObject *)Py_CompileString(qexpr.c_str(), "", Py_file_input);
+ qstring func;
+ wrap_in_function(&func, expr, name);
+ code = (PyCodeObject *)Py_CompileString(func.c_str(), "", Py_file_input);
if ( code == NULL )
{
handle_python_error(errbuf, errbufsize);
@@ -1200,7 +1229,7 @@ bool idaapi IDAPYthon_cli_complete_line(
if ( py_complete == NULL )
return false;
- newref_t py_ret(PyObject_CallFunction(py_complete.o, "sisi", prefix, n, line, x)); //lint !e1776
+ newref_t py_ret(PyObject_CallFunction(py_complete.o, "sisi", prefix, n, line, x)); //lint !e605
// Swallow the error
PyW_GetError(completion);
@@ -1294,7 +1323,24 @@ void convert_idc_args()
PyObject_SetAttrString(py_mod.o, S_IDC_ARGS_VARNAME, py_args.o);
}
+#ifdef WITH_HEXRAYS
+//-------------------------------------------------------------------------
+static bool is_hexrays_plugin(const plugin_info_t *pinfo)
+{
+ bool is_hx = false;
+ if ( pinfo != NULL && pinfo->entry != NULL )
+ {
+ const plugin_t *p = pinfo->entry;
+ if ( streq(p->wanted_name, "Hex-Rays Decompiler") )
+ is_hx = true;
+ }
+ return is_hx;
+}
+#endif
+
+
//------------------------------------------------------------------------
+//lint -esym(715,va) Symbol not referenced
static int idaapi on_ui_notification(void *, int code, va_list va)
{
#ifdef WITH_HEXRAYS
@@ -1323,16 +1369,10 @@ static int idaapi on_ui_notification(void *, int code, va_list va)
break;
#ifdef WITH_HEXRAYS
- // FIXME: HACK! THERE SHOULD BE A UI (or IDB?) NOTIFICATION
- // WHEN A PLUGIN GETS [UN]LOADED!
- // In the meantime, we're checking to see whether the Hex-Rays
- // plugin gets loaded/pulled away.
- case ui_add_menu_item:
+ case ui_plugin_loaded:
if ( hexdsp == NULL )
{
- (void)va_arg(va, char *); // Drop 'menupath'
- const char *name = va_arg(va, char *); // Look for 'name'.
- if ( streq(name, "Jump to pseudocode") )
+ if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) )
{
init_hexrays_plugin(0);
if ( hexdsp != NULL )
@@ -1341,14 +1381,13 @@ static int idaapi on_ui_notification(void *, int code, va_list va)
}
break;
- case ui_del_menu_item:
+ case ui_plugin_unloading:
{
if ( hexdsp != NULL )
{
// Hex-Rays will close. Make sure all the refcounted cfunc_t objects
// are cleared right away.
- const char *menupath = va_arg(va, char *);
- if ( streq(menupath, "Jump/Jump to pseudocode") )
+ if ( is_hexrays_plugin(va_arg(va, plugin_info_t *)) )
{
hexrays_clear_python_cfuncptr_t_references();
hexdsp = NULL;
diff --git a/python/idautils.py b/python/idautils.py
index 890ca46..c5748ba 100644
--- a/python/idautils.py
+++ b/python/idautils.py
@@ -374,8 +374,8 @@ def DecodePreviousInstruction(ea):
@param ea: address to decode
@return: None or a new insn_t instance
"""
- inslen = idaapi.decode_prev_insn(ea)
- if inslen == 0:
+ prev_addr = idaapi.decode_prev_insn(ea)
+ if prev_addr == idaapi.BADADDR:
return None
return idaapi.cmd.copy()
@@ -462,7 +462,8 @@ def GetInputFileMD5():
class Strings(object):
"""
- Returns the string list.
+ Allows iterating over the string list. The set of strings will not be modified.
+ , unless asked explicitly at setup()-time..
Example:
s = Strings()
@@ -483,8 +484,34 @@ class Strings(object):
self.length = si.length
"""string length"""
+ def is_1_byte_encoding(self):
+ return not self.is_2_bytes_encoding() and not self.is_4_bytes_encoding()
+
+ def is_2_bytes_encoding(self):
+ return (self.type & 7) in [idaapi.ASCSTR_UTF16, idaapi.ASCSTR_ULEN2, idaapi.ASCSTR_ULEN4]
+
+ def is_4_bytes_encoding(self):
+ return (self.type & 7) == idaapi.ASCSTR_UTF32
+
+ def _toseq(self, as_unicode):
+ if self.is_2_bytes_encoding():
+ conv = idaapi.ACFOPT_UTF16
+ pyenc = "utf-16"
+ elif self.is_4_bytes_encoding():
+ conv = idaapi.ACFOPT_UTF8
+ pyenc = "utf-8"
+ else:
+ conv = idaapi.ACFOPT_ASCII
+ pyenc = 'ascii'
+ strbytes = idaapi.get_ascii_contents2(self.ea, self.length, self.type, conv)
+ return unicode(strbytes, pyenc, 'replace') if as_unicode else strbytes
+
def __str__(self):
- return idc.GetString(self.ea, self.length, self.type)
+ return self._toseq(False)
+
+ def __unicode__(self):
+ return self._toseq(True)
+
STR_C = 0x0001
"""C-style ASCII string"""
@@ -505,8 +532,7 @@ class Strings(object):
"""Clears the strings list cache"""
self.refresh(0, 0) # when ea1=ea2 the kernel will clear the cache
-
- def __init__(self, default_setup = True):
+ def __init__(self, default_setup = False):
"""
Initializes the Strings enumeration helper class
@@ -515,10 +541,11 @@ class Strings(object):
self.size = 0
if default_setup:
self.setup()
+ else:
+ self.refresh()
self._si = idaapi.string_info_t()
-
def refresh(self, ea1=None, ea2=None):
"""Refreshes the strings list"""
if ea1 is None:
@@ -750,14 +777,6 @@ def ProcessUiActions(actions, flags=0):
helper = __process_ui_actions_helper(actions, flags)
return False if len(helper) < 1 else idaapi.execute_ui_requests((helper,))
-# ----------------------------------------------------------------------------
-def IsBatchMode():
- """
- Checks if batch mode is enabled
-
- @return: True if batch mode is enabled and False otherwise
- """
- return idaapi.cvar.batch != 0
# -----------------------------------------------------------------------
class peutils_t(object):
diff --git a/python/idc.py b/python/idc.py
index 4fc2345..7406f7d 100644
--- a/python/idc.py
+++ b/python/idc.py
@@ -1450,7 +1450,11 @@ def PatchByte(ea, value):
@param ea: linear address
@param value: new value of the byte
- @return: 1 if successful, 0 if not
+ @return: 1 if the database has been modified,
+ 0 if either the debugger is running and the process' memory
+ has value 'value' at address 'ea',
+ or the debugger is not running, and the IDB
+ has value 'value' at address 'ea already.
"""
return idaapi.patch_byte(ea, value)
@@ -1462,7 +1466,11 @@ def PatchWord(ea, value):
@param ea: linear address
@param value: new value of the word
- @return: 1 if successful, 0 if not
+ @return: 1 if the database has been modified,
+ 0 if either the debugger is running and the process' memory
+ has value 'value' at address 'ea',
+ or the debugger is not running, and the IDB
+ has value 'value' at address 'ea already.
"""
return idaapi.patch_word(ea, value)
@@ -1474,11 +1482,31 @@ def PatchDword(ea, value):
@param ea: linear address
@param value: new value of the double word
- @return: 1 if successful, 0 if not
+ @return: 1 if the database has been modified,
+ 0 if either the debugger is running and the process' memory
+ has value 'value' at address 'ea',
+ or the debugger is not running, and the IDB
+ has value 'value' at address 'ea already.
"""
return idaapi.patch_long(ea, value)
+def PatchQword(ea, value):
+ """
+ Change value of a quad word
+
+ @param ea: linear address
+ @param value: new value of the quad word
+
+ @return: 1 if the database has been modified,
+ 0 if either the debugger is running and the process' memory
+ has value 'value' at address 'ea',
+ or the debugger is not running, and the IDB
+ has value 'value' at address 'ea already.
+ """
+ return idaapi.patch_qword(ea, value)
+
+
def SetFlags(ea, flags):
"""
Set new value of flags
@@ -1880,7 +1908,7 @@ def GetFloat(ea):
Get value of a floating point number (4 bytes)
This function assumes number stored using IEEE format
and in the same endianness as integers.
-
+
@param ea: linear address
@return: float
@@ -2253,7 +2281,7 @@ def GetOpnd(ea, n):
@return: the current text representation of operand or ""
"""
-
+
if not isCode(idaapi.get_flags_novalue(ea)):
return ""
@@ -2280,21 +2308,21 @@ def GetOpType(ea, n):
return -1 if inslen == 0 else idaapi.cmd.Operands[n].type
-o_void = idaapi.o_void # No Operand ----------
-o_reg = idaapi.o_reg # General Register (al,ax,es,ds...) reg
-o_mem = idaapi.o_mem # Direct Memory Reference (DATA) addr
-o_phrase = idaapi.o_phrase # Memory Ref [Base Reg + Index Reg] phrase
-o_displ = idaapi.o_displ # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
-o_imm = idaapi.o_imm # Immediate Value value
-o_far = idaapi.o_far # Immediate Far Address (CODE) addr
-o_near = idaapi.o_near # Immediate Near Address (CODE) addr
-o_idpspec0 = idaapi.o_idpspec0 # IDP specific type
-o_idpspec1 = idaapi.o_idpspec1 # IDP specific type
-o_idpspec2 = idaapi.o_idpspec2 # IDP specific type
-o_idpspec3 = idaapi.o_idpspec3 # IDP specific type
-o_idpspec4 = idaapi.o_idpspec4 # IDP specific type
-o_idpspec5 = idaapi.o_idpspec5 # IDP specific type
-o_last = idaapi.o_last # first unused type
+o_void = idaapi.o_void # No Operand ----------
+o_reg = idaapi.o_reg # General Register (al,ax,es,ds...) reg
+o_mem = idaapi.o_mem # Direct Memory Reference (DATA) addr
+o_phrase = idaapi.o_phrase # Memory Ref [Base Reg + Index Reg] phrase
+o_displ = idaapi.o_displ # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
+o_imm = idaapi.o_imm # Immediate Value value
+o_far = idaapi.o_far # Immediate Far Address (CODE) addr
+o_near = idaapi.o_near # Immediate Near Address (CODE) addr
+o_idpspec0 = idaapi.o_idpspec0 # Processor specific type
+o_idpspec1 = idaapi.o_idpspec1 # Processor specific type
+o_idpspec2 = idaapi.o_idpspec2 # Processor specific type
+o_idpspec3 = idaapi.o_idpspec3 # Processor specific type
+o_idpspec4 = idaapi.o_idpspec4 # Processor specific type
+o_idpspec5 = idaapi.o_idpspec5 # Processor specific type
+ # There can be more processor specific types
# x86
o_trreg = idaapi.o_idpspec0 # trace register
@@ -2308,7 +2336,7 @@ o_xmmreg = idaapi.o_idpspec5 # xmm register
o_reglist = idaapi.o_idpspec1 # Register list (for LDM/STM)
o_creglist = idaapi.o_idpspec2 # Coprocessor register list (for CDP)
o_creg = idaapi.o_idpspec3 # Coprocessor register (for LDC/STC)
-o_fpreg = idaapi.o_idpspec4 # Floating point register
+o_fpreg_arm = idaapi.o_idpspec4 # Floating point register
o_fpreglist = idaapi.o_idpspec5 # Floating point register list
o_text = (idaapi.o_idpspec5+1) # Arbitrary text stored in the operand
@@ -3116,6 +3144,20 @@ def Message(msg):
idaapi.msg(msg)
+def UMessage(msg):
+ """
+ Display an UTF-8 string in the message window
+
+ The result of the stringification of the arguments
+ will be treated as an UTF-8 string.
+
+ @param msg: message to print (formatting is done in Python)
+
+ This function can be used to debug IDC scripts
+ """
+ idaapi.umsg(msg)
+
+
def Warning(msg):
"""
Display a message in a message box
@@ -6917,16 +6959,16 @@ def ApplyType(ea, py_type, flags = TINFO_DEFINITE):
@return: Boolean
"""
- if py_type != None:
+ if py_type is None:
+ py_type = ""
+ if isinstance(py_type, basestring) and len(py_type) == 0:
+ pt = ("", "")
+ else:
if len(py_type) == 3:
pt = py_type[1:] # skip name component
else:
pt = py_type
- return idaapi.apply_type(idaapi.cvar.idati, pt[0], pt[1], ea, flags)
- if idaapi.has_ti(ea):
- idaapi.del_tinfo(ea)
- return True
- return False
+ return idaapi.apply_type(idaapi.cvar.idati, pt[0], pt[1], ea, flags)
def SetType(ea, newtype):
"""
@@ -6941,7 +6983,7 @@ def SetType(ea, newtype):
@return: 1-ok, 0-failed.
"""
if newtype is not '':
- pt = ParseType(newtype, 0)
+ pt = ParseType(newtype, 1) # silent
if pt is None:
# parsing failed
return None
diff --git a/python/init.py b/python/init.py
index 227357b..dbbb531 100644
--- a/python/init.py
+++ b/python/init.py
@@ -28,9 +28,8 @@ class IDAPythonStdOut:
Dummy file-like class that receives stout and stderr
"""
def write(self, text):
- # Swap out the unprintable characters
- text = text.decode('ascii', 'replace').encode('ascii', 'replace')
- # Print to IDA message window
+ # NB: in case 'text' is Unicode, msg() will decode it
+ # and call umsg() to print it
_idaapi.msg(text)
def flush(self):
diff --git a/pywraps.hpp b/pywraps.hpp
index 10a9612..7a264ea 100644
--- a/pywraps.hpp
+++ b/pywraps.hpp
@@ -93,6 +93,18 @@ public:
} while ( false )
+//-------------------------------------------------------------------------
+struct exc_report_t
+{
+ ~exc_report_t()
+ {
+ if ( PyErr_Occurred() )
+ PyErr_Print();
+ }
+};
+#define PYW_GIL_GET_AND_REPORT_ERROR PYW_GIL_GET; exc_report_t exc;
+
+
//------------------------------------------------------------------------
// All the exported functions from PyWraps are forward declared here
insn_t *insn_t_get_clink(PyObject *self);
@@ -125,25 +137,38 @@ struct ref_t
~ref_t() { decref(); }
ref_t &operator=(const ref_t &other)
{
- decref();
+ // We *must* first (possibly) set & incref the other object,
+ // because decref() might call the Python's deallocator, which
+ // might have side-effects, that might affect this ref_t
+ // instance.
+ // If that's too many 'might' to your taste, let me illustrate.
+ //
+ // py_plgform.hpp's 'plgform_t' holds a 'ref_t' instance, named 'py_obj'.
+ // If the actual, Qt widget wrapped by that plgform_t gets destroyed,
+ // plgform_t::unhook() will be called, which will assign an
+ // empty ref_t instance to its 'py_obj'.
+ // That will decrement the refcount, and might call the deallocator:
+ // the plgform_t::destroy static function.
+ // That function will 'delete' the plgform_t object.
+ // But, in the ~plgform_t() destructor, the 'py_obj' object will be
+ // destroyed too: decreasing once again the refcnt (which now falls to -1).
+ // At this point, all hell breaks loose (or is allowed to).
+ PyObject *was = o;
o = other.o;
incref();
+ if ( was != NULL )
+ Py_DECREF(was);
return *this;
}
void incref() const { if ( o != NULL ) Py_INCREF(o); }
- void decref() const { if ( o != NULL ) Py_DECREF(o); }
+ void decref() const { if ( o != NULL ) { QASSERT(30469, o->ob_refcnt > 0); Py_DECREF(o); } }
bool operator==(PyObject *other) const { return o == other; }
bool operator!=(PyObject *other) const { return ! ((*this) == other); }
bool operator==(const ref_t &other) const { return o == other.o; }
bool operator!=(const ref_t &other) const { return ! ((*this) == other); }
-
- // operator PyObject *() const { return o; }
- // PyObject *operator ->() const { return o; }
- // PyObject &operator *() const { return *o; }
- //protected:
};
//-------------------------------------------------------------------------
@@ -318,6 +343,21 @@ bool PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec);
// Converts a Python list to a qstrvec
bool PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec);
+//-------------------------------------------------------------------------
+PyObject *qstrvec2pylist(qstrvec_t &vec);
+
+//-------------------------------------------------------------------------
+inline bool PyWStringOrNone_Check(PyObject *tp)
+{
+ return tp == Py_None || PyString_Check(tp);
+}
+
+//-------------------------------------------------------------------------
+inline const p_list * PyW_Fields(PyObject *tp)
+{
+ return tp == Py_None ? NULL : (const p_list *) PyString_AsString(tp);
+}
+
//---------------------------------------------------------------------------
//
// notify_when()
diff --git a/pywraps/deploy.bat b/pywraps/deploy.bat
index 142e80b..256ad74 100644
--- a/pywraps/deploy.bat
+++ b/pywraps/deploy.bat
@@ -1,2 +1,2 @@
@echo off
-c:\python27\python.exe deploy_all.py
+deploy_all.py
diff --git a/pywraps/deploy_all.py b/pywraps/deploy_all.py
index c2e7dcf..2bedb56 100644
--- a/pywraps/deploy_all.py
+++ b/pywraps/deploy_all.py
@@ -143,6 +143,12 @@ deploys = {
"tgt" : "../swig/lines.i"
},
+ "registry" : {
+ "tag" : "py_registry",
+ "src" : ["py_registry.hpp"],
+ "tgt" : "../swig/registry.i"
+ },
+
"pc_win32_appcall" : {
"tag" : "appcalltest",
"src" : ["py_appcall.py"],
@@ -155,6 +161,12 @@ deploys = {
"tgt" : "../../../tests/input/pc_win32_custdata1.pe.hints"
},
+ "ex_choose2" : {
+ "tag" : "py_choose2ex1",
+ "src" : ["py_choose2.py"],
+ "tgt" : "../examples/ex_choose2.py"
+ },
+
"ex_formchooser" : {
"tag" : "ex_formchooser",
"src" : ["py_askusingform.py"],
diff --git a/pywraps/py_askusingform.hpp b/pywraps/py_askusingform.hpp
index 88f6b3c..cca2790 100644
--- a/pywraps/py_askusingform.hpp
+++ b/pywraps/py_askusingform.hpp
@@ -126,7 +126,7 @@ static void formchgcbfa_refresh_field(size_t p_fa, int fid)
}
//---------------------------------------------------------------------------
-static void formchgcbfa_close(size_t p_fa, int fid, int close_normally)
+static void formchgcbfa_close(size_t p_fa, int close_normally)
{
DECLARE_FORM_ACTIONS;
fa->close(close_normally);
@@ -362,6 +362,12 @@ static size_t py_get_AskUsingForm()
return (size_t)AskUsingForm_c;
}
+static size_t py_get_OpenForm()
+{
+ // See comments above.
+ return (size_t)OpenForm_c;
+}
+
//
#endif // __PY_ASKUSINGFORM__
\ No newline at end of file
diff --git a/pywraps/py_askusingform.py b/pywraps/py_askusingform.py
index ada80d3..96ee205 100644
--- a/pywraps/py_askusingform.py
+++ b/pywraps/py_askusingform.py
@@ -29,6 +29,7 @@ try:
_idaapi.CHOOSER_POPUP_MENU = 1
pywraps = object_t()
pywraps.py_get_AskUsingForm = lambda: 0
+ pywraps.py_get_OpenForm = lambda: 0
class Choose2(object):
CH_MULTI = 1
@@ -891,7 +892,7 @@ class Form(object):
def __init__(self, form, controls):
"""
Contruct a Form class.
- This class wraps around AskUsingForm() and provides an easier / alternative syntax for describing forms.
+ This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms.
The form control names are wrapped inside the opening and closing curly braces and the control themselves are
defined and instantiated via various form controls (subclasses of Form).
@@ -908,6 +909,15 @@ class Form(object):
self.title = None
"""The Form title. It will be filled when the form is compiled"""
+ self.modal = True
+ """By default, forms are modal"""
+
+ self.openform_flags = 0
+ """
+ If non-modal, these flags will be passed to OpenForm.
+ This is an OR'ed combination of the PluginForm.FORM_* values.
+ """
+
def Free(self):
"""
@@ -1039,6 +1049,11 @@ class Form(object):
"""
# First argument is the form string
args = [None]
+
+ # Second argument, if form is not modal, is the set of flags
+ if not self.modal:
+ args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET
+
ctrlcnt = 1
# Reset all group control internal flags
@@ -1121,6 +1136,22 @@ class Form(object):
ctrlcnt += 1
+ # If no FormChangeCb instance was passed, and thus there's no '%/'
+ # in the resulting form string, let's provide a minimal one, so that
+ # we will retrieve 'p_fa', and thus actions that rely on it will work.
+ if form.find(Form.FT_FORMCHG) < 0:
+ form = form + Form.FT_FORMCHG
+ fccb = Form.FormChangeCb(lambda *args: 1)
+ self.Add("___dummyfchgcb", fccb)
+ # Regardless of the actual position of '%/' in the form
+ # string, a formchange callback _must_ be right after
+ # the form string.
+ if self.modal:
+ inspos = 1
+ else:
+ inspos = 2
+ args.insert(inspos, fccb.get_arg())
+
# Patch in the final form string
args[0] = form
@@ -1157,18 +1188,34 @@ class Form(object):
return self.__args is not None
- def Execute(self):
- """
- Displays a compiled form.
- @return: 1 - ok ; 0 - cancel
- """
+ def _ChkCompiled(self):
if not self.Compiled():
raise SyntaxError("Form is not compiled")
- # Call AskUsingForm()
+
+ def Execute(self):
+ """
+ Displays a modal dialog containing the compiled form.
+ @return: 1 - ok ; 0 - cancel
+ """
+ self._ChkCompiled()
+ if not self.modal:
+ raise SyntaxError("Form is not modal. Open() should be instead")
+
return AskUsingForm(*self.__args)
+ def Open(self):
+ """
+ Opens a widget containing the compiled form.
+ """
+ self._ChkCompiled()
+ if self.modal:
+ raise SyntaxError("Form is modal. Execute() should be instead")
+
+ OpenForm(*self.__args)
+
+
def EnableField(self, ctrl, enable):
"""
Enable or disable an input field
@@ -1311,15 +1358,18 @@ try:
# Setup the numeric argument size
Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL
AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm())
+ OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm())
except:
def AskUsingForm__(*args):
warning("AskUsingForm() needs ctypes library in order to work")
return 0
+ def OpenForm__(*args):
+ warning("OpenForm() needs ctypes library in order to work")
def AskUsingForm(*args):
"""
- Calls the AskUsingForm()
+ Calls AskUsingForm()
@param: Compiled Arguments obtain through the Form.Compile() function
@return: 1 = ok, 0 = cancel
"""
@@ -1328,6 +1378,15 @@ def AskUsingForm(*args):
set_script_timeout(old)
return r
+def OpenForm(*args):
+ """
+ Calls OpenForm()
+ @param: Compiled Arguments obtain through the Form.Compile() function
+ """
+ old = set_script_timeout(0)
+ r = OpenForm__(*args)
+ set_script_timeout(old)
+
#
@@ -1671,7 +1730,7 @@ Dropdown list test
# --------------------------------------------------------------------------
def test_dropdown(execute=True):
- """Test the combobox controls"""
+ """Test the combobox controls, in a modal dialog"""
f = MyForm3()
f, args = f.Compile()
if execute:
@@ -1687,6 +1746,18 @@ def test_dropdown(execute=True):
f.Free()
+# --------------------------------------------------------------------------
+tdn_form = None
+def test_dropdown_nomodal():
+ """Test the combobox controls, in a non-modal form"""
+ global tdn_form
+ if tdn_form is None:
+ tdn_form = MyForm3()
+ tdn_form.modal = False
+ tdn_form.openform_flags = idaapi.PluginForm.FORM_TAB
+ tdn_form, _ = tdn_form.Compile()
+ tdn_form.Open()
+
#
# --------------------------------------------------------------------------
diff --git a/pywraps/py_bytes.hpp b/pywraps/py_bytes.hpp
index 485a8fb..e60cea6 100644
--- a/pywraps/py_bytes.hpp
+++ b/pywraps/py_bytes.hpp
@@ -153,15 +153,21 @@ ACFOPT_ESCAPE = 0x00000010 # for ACFOPT_ASCII, convert non-printable
def get_ascii_contents2(ea, len, type, flags = ACFOPT_ASCII):
"""
- Get contents of ascii string
- This function returns the displayed part of the string
+ Get bytes contents at location, possibly converted.
It works even if the string has not been created in the database yet.
+ Note that this will always return a simple string of bytes
+ (i.e., a 'str' instance), and not a string of unicode characters.
+
+ If you want auto-conversion to unicode strings (that is: real strings),
+ you should probably be using the idautils.Strings class.
+
@param ea: linear address of the string
@param len: length of the string in bytes (including terminating 0)
- @param type: type of the string
- @param flags: combination of ACFOPT_...
- @return: string contents (not including terminating 0) or None
+ @param type: type of the string. Represents both the character encoding,
+ and the 'type' of string at the given location.
+ @param flags: combination of ACFOPT_..., to perform output conversion.
+ @return: a bytes-filled str object.
"""
pass
#
@@ -177,7 +183,7 @@ static PyObject *py_get_ascii_contents2(
return NULL;
size_t used_size;
- if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size) )
+ if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size, flags) )
{
qfree(buf);
Py_RETURN_NONE;
diff --git a/pywraps/py_choose2.hpp b/pywraps/py_choose2.hpp
index 4e317fb..37abeac 100644
--- a/pywraps/py_choose2.hpp
+++ b/pywraps/py_choose2.hpp
@@ -375,6 +375,56 @@ private:
}
}
+ bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const
+ {
+ if ( get_embedded() != NULL )
+ {
+ // For embedded chooser, the "caption" will be overloaded to encode
+ // the AskUsingForm's title, caption and embedded chooser id
+ // Title:EmbeddedChooserID:Caption
+
+ char title_buf[MAXSTR];
+ const char *ptitle;
+
+ static const char delimiter[] = ":";
+ char temp[MAXSTR];
+ qstrncpy(temp, caption, sizeof(temp));
+
+ char *ctx;
+ char *p = qstrtok(temp, delimiter, &ctx);
+ if ( p == NULL )
+ return false;
+
+ // Copy the title
+ char title_str[MAXSTR];
+ qstrncpy(title_str, p, sizeof(title_str));
+
+ // Copy the echooser ID
+ p = qstrtok(NULL, delimiter, &ctx);
+ if ( p == NULL )
+ return false;
+
+ char id_str[10];
+ qstrncpy(id_str, p, sizeof(id_str));
+
+ // Form the new title of the form: "AskUsingFormTitle:EchooserId"
+ qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str);
+
+ // Adjust the title
+ *out_title = title_buf;
+
+ // Adjust the caption
+ p = qstrtok(NULL, delimiter, &ctx);
+ *out_caption = caption + (p - temp);
+ }
+ else
+ {
+ *out_title = title;
+ *out_caption = caption;
+ }
+ return true;
+ }
+
public:
//------------------------------------------------------------------------
// Public methods
@@ -420,68 +470,24 @@ public:
}
int add_command(
- const char *caption,
- int flags=0,
- int menu_index=-1,
- int icon=-1)
+ const char *_caption,
+ int flags=0,
+ int menu_index=-1,
+ int icon=-1)
{
if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS )
return -1;
- // For embedded chooser, the "caption" will be overloaded to encode
- // the AskUsingForm's title, caption and embedded chooser id
- // Title:EmbeddedChooserID:Caption
- char title_buf[MAXSTR];
- const char *ptitle;
-
- // Embedded chooser?
- if ( get_embedded() != NULL )
- {
- static const char delimiter[] = ":";
- char temp[MAXSTR];
- qstrncpy(temp, caption, sizeof(temp));
-
- char *p = strtok(temp, delimiter);
- if ( p == NULL )
- return -1;
-
- // Copy the title
- char title_str[MAXSTR];
- qstrncpy(title_str, p, sizeof(title_str));
-
- // Copy the echooser ID
- p = strtok(NULL, delimiter);
- if ( p == NULL )
- return -1;
-
- char id_str[10];
- qstrncpy(id_str, p, sizeof(id_str));
-
- // Form the new title of the form: "AskUsingFormTitle:EchooserId"
- qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str);
-
- // Adjust the title
- ptitle = title_buf;
-
- // Adjust the caption
- p = strtok(NULL, delimiter);
- caption += (p - temp);
- }
- else
- {
- ptitle = title.c_str();
- }
-
- if ( !add_chooser_command(
- ptitle,
- caption,
- menu_cbs[menu_cb_idx],
- menu_index,
- icon,
- flags))
- {
+ qstring title, caption;
+ if ( !split_chooser_caption(&title, &caption, _caption)
+ || !add_chooser_command(
+ title.c_str(),
+ caption.c_str(),
+ menu_cbs[menu_cb_idx],
+ menu_index,
+ icon,
+ flags) )
return -1;
- }
return menu_cb_idx++;
}
@@ -862,17 +868,14 @@ PyObject *choose2_get_embedded(PyObject *self)
//------------------------------------------------------------------------
int choose2_add_command(
- PyObject *self,
- const char *caption,
- int flags=0,
- int menu_index=-1,
- int icon=-1)
+ PyObject *self,
+ const char *caption,
+ int flags=0,
+ int menu_index=-1,
+ int icon=-1)
{
py_choose2_t *c2 = choose2_find_instance(self);
- if ( c2 != NULL )
- return c2->add_command(caption, flags, menu_index, icon);
- else
- return -2;
+ return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon);
}
//------------------------------------------------------------------------
diff --git a/pywraps/py_choose2.py b/pywraps/py_choose2.py
index 2b65e60..dbce4d6 100644
--- a/pywraps/py_choose2.py
+++ b/pywraps/py_choose2.py
@@ -54,6 +54,8 @@ class Choose2(object):
CH_ATTRS = 0x10
CH_NOIDB = 0x20
"""use the chooser even without an open database, same as x0=-2"""
+ CH_UTF8 = 0x40
+ """string encoding is utf-8"""
CH_BUILTIN_MASK = 0xF80000
@@ -162,12 +164,11 @@ class Choose2(object):
icon = -1,
emb=None):
"""
- Adds a new chooser command
- Save the returned value and later use it in the OnCommand handler
-
- @return: Returns a negative value on failure or the command index
+ Deprecated: Use
+ - register_action()
+ - attach_action_to_menu()
+ - attach_action_to_popup()
"""
-
# Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl
if self.embedded and ((emb is None) or (emb != 2002)):
raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()")
@@ -275,6 +276,24 @@ class Choose2(object):
#
# -----------------------------------------------------------------------
+#
+
+
+class chooser_handler_t(idaapi.action_handler_t):
+ def __init__(self, thing):
+ idaapi.action_handler_t.__init__(self)
+ self.thing = thing
+
+ def activate(self, ctx):
+ sel = []
+ for i in xrange(len(ctx.chooser_selection)):
+ sel.append(str(ctx.chooser_selection.at(i)))
+ print "command %s selected @ %s" % (self.thing, ", ".join(sel))
+
+ def update(self, ctx):
+ return idaapi.AST_ENABLE_FOR_FORM if idaapi.is_chooser_tform(ctx.form_type) else idaapi.AST_DISABLE_FOR_FORM
+
+
class MyChoose2(Choose2):
def __init__(self, title, nb = 5, flags=0, width=None, height=None, embedded=False, modal=False):
@@ -328,15 +347,6 @@ class MyChoose2(Choose2):
print("refresh %d" % n)
return n
- def OnCommand(self, n, cmd_id):
- if cmd_id == self.cmd_a:
- print "command A selected @", n
- elif cmd_id == self.cmd_b:
- print "command B selected @", n
- else:
- print "Unknown command:", cmd_id, "@", n
- return 1
-
def OnGetIcon(self, n):
r = self.items[n]
t = self.icon + r[1].count("*")
@@ -344,14 +354,7 @@ class MyChoose2(Choose2):
return t
def show(self):
- t = self.Show(self.modal)
- if t < 0:
- return False
- if not self.modal:
- self.cmd_a = self.AddCommand("command A")
- self.cmd_b = self.AddCommand("command B")
- print("Show() returned: %d\n" % t)
- return True
+ return self.Show(self.modal) >= 0
def make_item(self):
r = [str(self.n), "func_%04d" % self.n]
@@ -369,6 +372,9 @@ def test_choose2(modal=False):
global c
c = MyChoose2("Choose2 - sample 1", nb=10, modal=modal)
r = c.show()
+ form = idaapi.get_current_tform()
+ for thing in ["A", "B"]:
+ idaapi.attach_action_to_popup(form, None, "choose2:act%s" % thing)
# -----------------------------------------------------------------------
def test_choose2_embedded():
@@ -386,5 +392,17 @@ def test_choose2_embedded():
# -----------------------------------------------------------------------
if __name__ == '__main__':
+
+ # Register actions
+ for thing in ["A", "B"]:
+ actname = "choose2:act%s" % thing
+ idaapi.register_action(
+ idaapi.action_desc_t(
+ actname,
+ "command %s" % thing,
+ chooser_handler_t(thing)))
+
#test_choose2_embedded()
- test_choose2(False)
\ No newline at end of file
+ test_choose2(False)
+
+#
diff --git a/pywraps/py_custview.hpp b/pywraps/py_custview.hpp
index aa181cf..40cf216 100644
--- a/pywraps/py_custview.hpp
+++ b/pywraps/py_custview.hpp
@@ -134,6 +134,9 @@ public:
};
//---------------------------------------------------------------------------
+// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t,
+// just like py_graph.hpp's py_graph_t does.
+// There should be a way to "merge" the two mechanisms; they are similar.
class customviewer_t
{
protected:
@@ -166,13 +169,6 @@ private:
qstring _curline;
intvec_t _installed_popups;
- static bool idaapi s_popup_cb(void *ud)
- {
- PYW_GIL_GET;
- customviewer_t *_this = (customviewer_t *)ud;
- return _this->on_popup();
- }
-
static bool idaapi s_popup_menu_cb(void *ud)
{
size_t mid = (size_t)ud;
@@ -271,6 +267,10 @@ private:
}
public:
+
+ inline TForm *get_tform() { return _form; }
+ inline TCustomControl *get_tcustom_control() { return _cv; }
+
//
// All the overridable callbacks
//
@@ -1111,6 +1111,22 @@ bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl)
DECL_THIS;
return _this == NULL ? false : _this->edit_line(nline, py_sl);
}
+
+//-------------------------------------------------------------------------
+TForm *pyscv_get_tform(PyObject *py_this)
+{
+ DECL_THIS;
+ return _this == NULL ? NULL : _this->get_tform();
+}
+
+//-------------------------------------------------------------------------
+TCustomControl *pyscv_get_tcustom_control(PyObject *py_this)
+{
+ DECL_THIS;
+ return _this == NULL ? NULL : _this->get_tcustom_control();
+}
+
+
#undef DECL_THIS
//
//---------------------------------------------------------------------------
diff --git a/pywraps/py_custview.py b/pywraps/py_custview.py
index 948afae..afbb0c6 100644
--- a/pywraps/py_custview.py
+++ b/pywraps/py_custview.py
@@ -204,6 +204,24 @@ class simplecustviewer_t(object):
"""Returns True if the current view is the focused view"""
return _idaapi.pyscv_is_focused(self.__this)
+ def GetTForm(self):
+ """
+ Return the TForm hosting this view.
+
+ @return: The TForm that hosts this view, or None.
+ """
+ return _idaapi.pyscv_get_tform(self.__this)
+
+ def GetTCustomControl(self):
+ """
+ Return the TCustomControl underlying this view.
+
+ @return: The TCustomControl underlying this view, or None.
+ """
+ return _idaapi.pyscv_get_tcustom_control(self.__this)
+
+
+
# Here are all the supported events
#
# def OnClick(self, shift):
@@ -278,6 +296,18 @@ class simplecustviewer_t(object):
#
+class say_something_handler_t(idaapi.action_handler_t):
+ def __init__(self, thing):
+ idaapi.action_handler_t.__init__(self)
+ self.thing = thing
+
+ def activate(self, ctx):
+ print self.thing
+
+ def update(self, ctx):
+ return idaapi.AST_ENABLE_ALWAYS
+
+
# -----------------------------------------------------------------------
class mycv_t(simplecustviewer_t):
def Create(self, sn=None):
@@ -289,8 +319,6 @@ class mycv_t(simplecustviewer_t):
# Create the customviewer
if not simplecustviewer_t.Create(self, title):
return False
- self.menu_hello = self.AddPopupMenu("Hello")
- self.menu_world = self.AddPopupMenu("World")
for i in xrange(0, 100):
self.AddLine("Line %d" % i)
@@ -391,13 +419,6 @@ class mycv_t(simplecustviewer_t):
return False
return True
- def OnPopup(self):
- """
- Context menu popup is about to be shown. Create items dynamically if you wish
- @return: Boolean. True if you handled the event
- """
- print "OnPopup"
-
def OnHint(self, lineno):
"""
Hint requested for the given line number.
@@ -408,22 +429,6 @@ class mycv_t(simplecustviewer_t):
"""
return (1, "OnHint, line=%d" % lineno)
- def OnPopupMenu(self, menu_id):
- """
- A context (or popup) menu item was executed.
- @param menu_id: ID previously registered with AddPopupMenu()
- @return: Boolean
- """
- print "OnPopupMenu, menu_id=%d" % menu_id
- if menu_id == self.menu_hello:
- print "Hello"
- elif menu_id == self.menu_world:
- print "World"
- else:
- # Unhandled
- return False
- return True
-
# -----------------------------------------------------------------------
try:
# created already?
@@ -440,7 +445,16 @@ def show_win():
print "Failed to create!"
return None
x.Show()
+ tcc = x.GetTCustomControl()
+
+ # Register actions
+ for thing in ["Hello", "World"]:
+ actname = "custview:say_%s" % thing
+ idaapi.register_action(
+ idaapi.action_desc_t(actname, "Say %s" % thing, say_something_handler_t(thing)))
+ idaapi.attach_action_to_popup(tcc, None, actname)
return x
+
mycv = show_win()
if not mycv:
del mycv
diff --git a/pywraps/py_cvt.hpp b/pywraps/py_cvt.hpp
index a6603e9..1094a16 100644
--- a/pywraps/py_cvt.hpp
+++ b/pywraps/py_cvt.hpp
@@ -61,323 +61,6 @@ struct scfld_t
#define FT_BAD_TYPE -2
#define FT_OK 1
-// //-----------------------------------------------------------------------
-// class pycvt_t
-// {
-// struct attr_t
-// {
-// qstring str;
-// uint64 u64;
-// // User is responsible to release this attribute when done
-// PyObject *py_obj;
-// };
-
-// //-----------------------------------------------------------------------
-// static int get_attr(
-// PyObject *py_obj,
-// const char *attrname,
-// int ft,
-// attr_t &val)
-// {
-// ref_t py_attr(PyW_TryGetAttrString(py_obj, attrname));
-// if ( py_attr == NULL )
-// return FT_NOT_FOUND;
-
-// int cvt = FT_OK;
-// if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr.o) )
-// val.str = PyString_AsString(py_attr.o);
-// else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr.o, &val.u64) )
-// ; // nothing to be done
-// // A string array?
-// else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC )
-// && (PyList_CheckExact(py_attr.o) || PyW_IsSequenceType(py_attr.o)) )
-// {
-// // Return a reference to the attribute
-// val.py_obj = py_attr.o;
-// // Do not decrement the reference to this attribute
-// py_attr = NULL;
-// }
-// else
-// cvt = FT_BAD_TYPE;
-// return cvt;
-// }
-
-// //-----------------------------------------------------------------------
-// static int idaapi make_str_list_cb(
-// PyObject *py_item,
-// Py_ssize_t index,
-// void *ud)
-// {
-// if ( !PyString_Check(py_item) )
-// return CIP_FAILED;
-// char **a = (char **)ud;
-// a[index] = qstrdup(PyString_AsString(py_item));
-// return CIP_OK;
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts an IDC list of strings to a C string list
-// static Py_ssize_t str_list_to_str_arr(
-// PyObject *py_list,
-// char ***arr)
-// {
-// // Take the size
-// Py_ssize_t size = pyvar_walk_list(py_list);
-
-// // Allocate a buffer
-// char **a = (char **)qalloc((size + 1) * sizeof(char *));
-
-// // Walk and populate
-// size = pyvar_walk_list(py_list, make_str_list_cb, a);
-
-// // Make the list NULL terminated
-// a[size] = NULL;
-
-// // Return the list to the user
-// *arr = a;
-
-// // Return the size of items processed
-// return size;
-// }
-
-// //-----------------------------------------------------------------------
-// typedef qvector uint64vec_t;
-// static int idaapi make_int_list(
-// PyObject *py_item,
-// Py_ssize_t /*index*/,
-// void *ud)
-// {
-// uint64 val;
-// if ( !PyW_GetNumber(py_item, &val) )
-// return CIP_FAILED;
-// uint64vec_t *vec = (uint64vec_t *)ud;
-// vec->push_back(val);
-// return CIP_OK;
-// }
-
-// public:
-// //-----------------------------------------------------------------------
-// // Frees a NULL terminated list of fields
-// static void free_fields(
-// const scfld_t *fields,
-// void *store_area)
-// {
-// for ( int i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// break;
-
-// void *store = (void *)((char *)store_area + fd.field_offs);
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-// switch ( ft )
-// {
-// case FT_STR: // Simple string
-// {
-// char **s = (char **)store;
-// if ( *s != NULL )
-// {
-// qfree(*s);
-// *s = NULL;
-// }
-// }
-// break;
-
-// case FT_STRARR: // Array of strings
-// {
-// char ***op = (char ***)store, **p = *op;
-// while ( *p != NULL )
-// qfree((void *)*p++);
-// qfree(*op);
-// *op = NULL;
-// }
-// break;
-
-// case FT_NUM16ARR:
-// {
-// uint16 **arr = (uint16 **)store;
-// if ( *arr != NULL )
-// {
-// qfree(*arr);
-// *arr = NULL;
-// }
-// }
-// break;
-// }
-// }
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts from a C structure to Python
-// static int from_c(
-// const scfld_t *fields,
-// void *read_area,
-// PyObject *py_obj)
-// {
-// PyObject *py_attr;
-// int i;
-// bool ok = false;
-// for ( i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// {
-// ok = true;
-// break;
-// }
-
-// // Point to structure member
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-// void *read = (void *)((char *)read_area + fd.field_offs);
-// // Create the python attribute properly
-// if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM )
-// {
-// if ( ft == FT_NUM16 )
-// py_attr = Py_BuildValue("H", *(uint16 *)read);
-// else if ( ft == FT_NUM32 )
-// py_attr = Py_BuildValue("I", *(uint32 *)read);
-// else if ( ft == FT_INT )
-// py_attr = Py_BuildValue("i", *(int *)read);
-// else if ( ft == FT_SIZET )
-// py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read);
-// else if ( ft == FT_SSIZET )
-// py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read);
-// }
-// else if ( ft == FT_STR || ft == FT_CHAR )
-// {
-// if ( ft == FT_STR )
-// py_attr = PyString_FromString(*(char **)read);
-// else
-// py_attr = Py_BuildValue("c", *(char *)read);
-// }
-// else if ( ft == FT_STRARR )
-// {
-// char **arr = *(char ***)read;
-// py_attr = PyList_New(0);
-// while ( *arr != NULL )
-// PyList_Append(py_attr, PyString_FromString(*arr++));
-// }
-// else
-// continue;
-// PyObject_SetAttrString(py_obj, fd.field_name, py_attr);
-// Py_XDECREF(py_attr);
-// }
-// return ok ? -1 : i;
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts fields from IDC and field description into a C structure
-// // If 'use_extlang' is specified, then the passed idc_obj is considered
-// // to be an opaque object and thus can be queried only through extlang
-// static int from_script(
-// const scfld_t *fields,
-// void *store_area,
-// PyObject *py_obj)
-// {
-// int i;
-// bool ok = false;
-// attr_t attr;
-// for ( i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// {
-// ok = true;
-// break;
-// }
-
-// // Get field type
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-
-// // Point to structure member
-// void *store = (void *)((char *)store_area + fd.field_offs);
-
-// // Retrieve attribute and type
-// int cvt = get_attr(py_obj, fd.field_name, ft, attr);
-
-// // Attribute not found?
-// if ( cvt == FT_NOT_FOUND )
-// {
-// // Skip optional fields
-// if ( fd.is_optional )
-// continue;
-// break;
-// }
-
-// if ( ft == FT_STR )
-// *(char **)store = qstrdup(attr.str.c_str());
-// else if ( ft == FT_NUM32 )
-// *(uint32 *)store = uint32(attr.u64);
-// else if ( ft == FT_NUM16 )
-// *(uint16 *)store = attr.u64 & 0xffff;
-// else if ( ft == FT_INT )
-// *(int *)store = int(attr.u64);
-// else if ( ft == FT_SIZET )
-// *(size_t *)store = size_t(attr.u64);
-// else if ( ft == FT_SSIZET )
-// *(ssize_t *)store = ssize_t(attr.u64);
-// else if ( ft == FT_CHAR )
-// *(char *)store = *attr.str.c_str();
-// else if ( ft == FT_STRARR )
-// {
-// str_list_to_str_arr(attr.py_obj, (char ***)store);
-// Py_DECREF(attr.py_obj);
-// }
-// else if ( ft == FT_CHRARR_STATIC )
-// {
-// size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16;
-// if ( sz == 0 )
-// break;
-// uint64vec_t w;
-// char *a = (char *) store;
-// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) )
-// {
-// sz = qmin(w.size(), sz);
-// for ( size_t i=0; i < sz; i++ )
-// a[i] = w[i] & 0xFF;
-// }
-// }
-// else if ( ft == FT_NUM16ARR )
-// {
-// uint64vec_t w;
-// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 )
-// {
-// size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16;
-// bool zero_term;
-// if ( max_sz == 0 )
-// {
-// zero_term = true;
-// max_sz = w.size();
-// }
-// else
-// {
-// zero_term = false;
-// max_sz = qmin(max_sz, w.size());
-// }
-// // Allocate as much as we parsed elements
-// // Add one more element if list was zero terminated
-// uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ;
-// for ( size_t i=0; i < max_sz; i++ )
-// a[i] = w[i] & 0xFF;
-
-// if ( zero_term )
-// a[max_sz] = 0;
-// *(uint16 **)store = a;
-// }
-// }
-// else
-// {
-// // Unsupported field type!
-// break;
-// }
-// }
-// return ok ? -1 : i;
-// }
-// };
-
//-------------------------------------------------------------------------
Py_ssize_t pyvar_walk_list(
const ref_t &py_list,
@@ -1052,9 +735,6 @@ bool pyw_convert_idc_args(
{
// PyTuple_SetItem() steals the reference.
py_obj.incref();
- if ( cvt == CIP_OK_OPAQUE )
- // We want opaque objects to still exist even when the tuple is gone.
- py_obj.incref();
QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0);
}
else
diff --git a/pywraps/py_dbg.hpp b/pywraps/py_dbg.hpp
index c0d7a13..85db5f5 100644
--- a/pywraps/py_dbg.hpp
+++ b/pywraps/py_dbg.hpp
@@ -723,5 +723,63 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va)
}
return code;
}
+
+//------------------------------------------------------------------------
+/*
+#
+def py_list_bptgrps():
+ """
+ Returns list of breakpoint group names
+ @return: A list of strings or None on failure
+ """
+ pass
+#
+*/
+static PyObject *py_list_bptgrps()
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+
+ qstrvec_t args;
+ if ( list_bptgrps(&args) == 0 )
+ Py_RETURN_NONE;
+ return qstrvec2pylist(args);
+}
+
+//------------------------------------------------------------------------
+/*
+#
+def move_bpt_to_grp():
+ """
+ Sets new group for the breakpoint
+ """
+ pass
+#
+*/
+static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ set_bpt_group(*bpt, grp_name);
+}
+
+/*
+#
+def internal_get_sreg_base():
+ """
+ Get the sreg base, for the given thread.
+
+ @return: The sreg base, or BADADDR on failure.
+ """
+ pass
+#
+*/
+static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ ea_t answer;
+ return internal_get_sreg_base(tid, sreg_value, &answer) < 1
+ ? BADADDR
+ : answer;
+}
+
//
#endif
diff --git a/pywraps/py_graph.hpp b/pywraps/py_graph.hpp
index 1d30e62..bacb741 100644
--- a/pywraps/py_graph.hpp
+++ b/pywraps/py_graph.hpp
@@ -32,13 +32,16 @@ protected:
private:
enum
{
- GRCODE_HAVE_USER_HINT = 0x00010000,
- GRCODE_HAVE_CLICKED = 0x00020000,
- GRCODE_HAVE_DBL_CLICKED = 0x00040000,
- GRCODE_HAVE_GOTFOCUS = 0x00080000,
- GRCODE_HAVE_LOSTFOCUS = 0x00100000,
- GRCODE_HAVE_CHANGED_CURRENT = 0x00200000,
- GRCODE_HAVE_COMMAND = 0x00400000
+ GRCODE_HAVE_USER_HINT = 0x00010000,
+ GRCODE_HAVE_CLICKED = 0x00020000,
+ GRCODE_HAVE_DBL_CLICKED = 0x00040000,
+ GRCODE_HAVE_GOTFOCUS = 0x00080000,
+ GRCODE_HAVE_LOSTFOCUS = 0x00100000,
+ GRCODE_HAVE_CHANGED_CURRENT = 0x00200000,
+ GRCODE_HAVE_COMMAND = 0x00400000,
+ GRCODE_HAVE_CREATING_GROUP = 0x00800000,
+ GRCODE_HAVE_DELETING_GROUP = 0x01000000,
+ GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000,
};
struct nodetext_cache_t
{
@@ -142,6 +145,7 @@ private:
// Check return value to OnRefresh() call
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id));
+ PyW_ShowCbErr(S_ON_COMMAND);
}
// Refresh user-defined graph node number and edges
@@ -189,6 +193,7 @@ private:
(char *)S_ON_CLICK,
"i",
item2->n));
+ PyW_ShowCbErr(S_ON_CLICK);
return result == NULL || !PyObject_IsTrue(result.o);
}
@@ -210,29 +215,38 @@ private:
(char *)S_ON_DBL_CLICK,
"i",
item->node));
+ PyW_ShowCbErr(S_ON_DBL_CLICK);
return result == NULL || !PyObject_IsTrue(result.o);
}
// a graph viewer got focus
void on_gotfocus(graph_viewer_t * /*view*/)
{
+ if ( self.o == NULL )
+ return;
+
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(
PyObject_CallMethod(
self.o,
(char *)S_ON_ACTIVATE,
NULL));
+ PyW_ShowCbErr(S_ON_ACTIVATE);
}
// a graph viewer lost focus
void on_lostfocus(graph_viewer_t * /*view*/)
{
+ if ( self.o == NULL )
+ return;
+
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(
PyObject_CallMethod(
self.o,
(char *)S_ON_DEACTIVATE,
NULL));
+ PyW_ShowCbErr(S_ON_DEACTIVATE);
}
// a new graph node became the current node
@@ -251,6 +265,7 @@ private:
(char *)S_ON_SELECT,
"i",
curnode));
+ PyW_ShowCbErr(S_ON_SELECT);
return !(result != NULL && PyObject_IsTrue(result.o));
}
@@ -258,7 +273,6 @@ private:
int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- printf("my_g: %p; my_nodes: %p\n", my_g, my_nodes);
newref_t py_nodes(PyList_New(my_nodes->size()));
int i;
intvec_t::const_iterator p;
@@ -270,6 +284,7 @@ private:
(char *)S_ON_CREATING_GROUP,
"O",
py_nodes.o));
+ PyW_ShowCbErr(S_ON_CREATING_GROUP);
return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o);
}
@@ -299,6 +314,10 @@ private:
void jump_to_node(int nid)
{
+ ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES));
+ if ( nid >= PyList_Size(nodes.o) )
+ return;
+
viewer_center_on(view, nid);
int x, y;
@@ -342,6 +361,7 @@ private:
if ( pview != NULL )
viewer_fit_window(pview);
bind(self, pview);
+ install_custom_viewer_handlers();
refresh();
lookup_info.commit(e, form, view);
}
@@ -379,13 +399,16 @@ public:
cmdid_pyg.clear(this);
}
- static void SelectNode(PyObject *self, int /*nid*/)
+ static void SelectNode(PyObject *self, int nid)
{
+ if ( nid < 0 )
+ return;
+
py_graph_t *_this = view_extract_this(self);
if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) )
return;
- _this->jump_to_node(0);
+ _this->jump_to_node(nid);
}
static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey)
@@ -465,6 +488,9 @@ void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out)
out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT);
out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS);
out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS);
+ out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP);
+ out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP);
+ out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY);
}
//-------------------------------------------------------------------------
@@ -476,6 +502,7 @@ void py_graph_t::on_user_refresh(mutable_graph_t *g)
// Check return value to OnRefresh() call
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL));
+ PyW_ShowCbErr(S_ON_REFRESH);
if ( ret != NULL && PyObject_IsTrue(ret.o) )
{
// Refer to the nodes
@@ -547,6 +574,7 @@ bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **st
// Not cached, call Python
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node));
+ PyW_ShowCbErr(S_ON_GETTEXT);
if ( result == NULL )
return false;
@@ -594,6 +622,7 @@ int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_s
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode));
+ PyW_ShowCbErr(S_ON_HINT);
bool ok = result != NULL && PyString_Check(result.o);
if ( ok )
*hint = qstrdup(PyString_AsString(result.o));
@@ -647,7 +676,10 @@ int py_graph_t::gr_callback(int code, va_list va)
ret = on_dblclicked(view, item);
}
else
- ret = 1; // ignore
+ ret = 0; // We don't want to ignore the double click, but rather
+ // fallback to the default behavior (e.g., double-clicking
+ // on an edge will to jump to the node on the other side
+ // of that edge.)
break;
//
case grcode_gotfocus:
@@ -698,28 +730,43 @@ int py_graph_t::gr_callback(int code, va_list va)
break;
//
case grcode_creating_group: // a group is being created
+ if ( has_callback(GRCODE_HAVE_CREATING_GROUP) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
intvec_t *nodes = va_arg(va, intvec_t*);
ret = on_creating_group(g, nodes);
}
+ else
+ {
+ ret = 0; // Ok to create
+ }
break;
//
case grcode_deleting_group: // a group is being deleted
+ if ( has_callback(GRCODE_HAVE_DELETING_GROUP) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
int old_group = va_arg(va, int);
ret = on_deleting_group(g, old_group);
}
+ else
+ {
+ ret = 0; // Ok to delete
+ }
break;
//
case grcode_group_visibility: // a group is being collapsed/uncollapsed
+ if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
int group = va_arg(va, int);
bool expand = bool(va_arg(va, int));
ret = on_group_visibility(g, group, expand);
}
+ else
+ {
+ ret = 0; // Ok.
+ }
break;
//
default:
diff --git a/pywraps/py_graph.py b/pywraps/py_graph.py
index 6de065b..689a31b 100644
--- a/pywraps/py_graph.py
+++ b/pywraps/py_graph.py
@@ -70,10 +70,9 @@ class GraphViewer(CustomIDAMemo):
def AddCommand(self, title, hotkey):
"""
- Adds a menu command to the graph. Call this command after the graph is shown (with Show()).
- Once a command is added, a command id is returned. The commands are handled inside the OnCommand() handler
-
- @return: 0 on failure or the command id
+ Deprecated: Use
+ - register_action()
+ - attach_action_to_popup()
"""
return _idaapi.pyg_add_command(self, title, hotkey)
@@ -155,9 +154,8 @@ class GraphViewer(CustomIDAMemo):
#
# def OnCommand(self, cmd_id):
# """
-# Triggered when a menu command is selected through the menu or its hotkey
-# @return: None
+# Deprecated
# """
-# print "command:", cmd_id
+# pass
#
#
diff --git a/pywraps/py_idaapi.hpp b/pywraps/py_idaapi.hpp
index d7247ce..df07b69 100644
--- a/pywraps/py_idaapi.hpp
+++ b/pywraps/py_idaapi.hpp
@@ -69,6 +69,7 @@ static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick";
static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos";
static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched";
static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver";
+static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved";
#ifdef __PYWRAPS__
@@ -623,7 +624,6 @@ void *pyobj_get_clink(PyObject *pyobj)
}
//
-
//
//------------------------------------------------------------------------
/*
@@ -644,12 +644,7 @@ static PyObject *py_parse_command_line(const char *cmdline)
qstrvec_t args;
if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 )
Py_RETURN_NONE;
-
- PyObject *py_list = PyList_New(args.size());
- for ( size_t i=0; icollect_pyobject_callbacks(self);
- if ( !ok )
+ if ( ok )
+ py_view->install_custom_viewer_handlers();
+ else
delete py_view;
}
return ok;
}
+//-------------------------------------------------------------------------
+bool py_idaview_t::Unbind(PyObject *self)
+{
+ py_idaview_t *_this = view_extract_this(self);
+ if ( _this == NULL )
+ return false;
+ _this->unbind();
+ return true;
+}
+
//-------------------------------------------------------------------------
bool pyidag_bind(PyObject *self)
{
return py_idaview_t::Bind(self);
}
+//-------------------------------------------------------------------------
+bool pyidag_unbind(PyObject *self)
+{
+ return py_idaview_t::Unbind(self);
+}
+
//
//--------------------------------------------------------------------------
//
bool pyidag_bind(PyObject *self);
+bool pyidag_unbind(PyObject *self);
//
#endif // __PY_IDA_VIEW__
diff --git a/pywraps/py_idaview.py b/pywraps/py_idaview.py
index 95b606a..3d3783e 100644
--- a/pywraps/py_idaview.py
+++ b/pywraps/py_idaview.py
@@ -13,4 +13,8 @@ class IDAViewWrapper(CustomIDAMemo):
def Bind(self):
return _idaapi.pyidag_bind(self)
+
+ def Unbind(self):
+ return _idaapi.pyidag_unbind(self)
+
#
diff --git a/pywraps/py_idp.hpp b/pywraps/py_idp.hpp
index d322a38..fe4e0d6 100644
--- a/pywraps/py_idp.hpp
+++ b/pywraps/py_idp.hpp
@@ -748,6 +748,10 @@ public:
{
}
+ virtual void auto_empty_finally()
+ {
+ }
+
virtual int rename(ea_t ea, const char *new_name)
{
return 0;
@@ -776,6 +780,15 @@ public:
{
}
+ virtual void auto_empty()
+ {
+ }
+
+ virtual int auto_queue_empty(atype_t type)
+ {
+ return 1; // Keep the queue empty.
+ }
+
virtual void add_func(func_t *func)
{
}
@@ -834,38 +847,38 @@ public:
return unhook_from_notification_point(HT_IDB, IDB_Callback, this);
}
// Hook functions to override in Python
- virtual int byte_patched(ea_t /*ea*/) { return 0; };
- virtual int cmt_changed(ea_t, bool /*repeatable_cmt*/) { return 0; };
+ virtual int byte_patched(ea_t /*ea*/) { return 0; }
+ virtual int cmt_changed(ea_t, bool /*repeatable_cmt*/) { return 0; }
virtual int area_cmt_changed(areacb_t * /*areas*/, area_t * /*area*/, const char * /*cmt*/, bool /*repeatable*/) { return 0; }
- virtual int ti_changed(ea_t /*ea*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; };
- virtual int op_ti_changed(ea_t /*ea*/, int /*n*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; };
- virtual int op_type_changed(ea_t /*ea*/, int /*n*/) { return 0; };
- virtual int enum_created(enum_t /*id*/) { return 0; };
- virtual int enum_deleted(enum_t /*id*/) { return 0; };
- virtual int enum_bf_changed(enum_t /*id*/) { return 0; };
- virtual int enum_renamed(enum_t /*id*/) { return 0; };
- virtual int enum_cmt_changed(enum_t /*id*/) { return 0; };
- virtual int enum_member_created(enum_t /*id*/, const_t cid) { return 0; };
- virtual int enum_member_deleted(enum_t /*id*/, const_t cid) { return 0; };
- virtual int struc_created(tid_t /*struc_id*/) { return 0; };
- virtual int struc_deleted(tid_t /*struc_id*/) { return 0; };
- virtual int struc_renamed(struc_t * /*sptr*/) { return 0; };
- virtual int struc_expanded(struc_t * /*sptr*/) { return 0; };
- virtual int struc_cmt_changed(tid_t /*struc_id*/) { return 0; };
- virtual int struc_member_created(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; };
- virtual int struc_member_deleted(struc_t * /*sptr*/, tid_t /*member_id*/, ea_t /*offset*/) { return 0; };
- virtual int struc_member_renamed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; };
- virtual int struc_member_changed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; };
- virtual int thunk_func_created(func_t * /*pfn*/) { return 0; };
- virtual int func_tail_appended(func_t * /*pfn*/, func_t * /*tail*/) { return 0; };
- virtual int func_tail_removed(func_t * /*pfn*/, ea_t /*tail_ea*/) { return 0; };
- virtual int tail_owner_changed(func_t * /*tail*/, ea_t /*owner_func*/) { return 0; };
- virtual int func_noret_changed(func_t * /*pfn*/) { return 0; };
- virtual int segm_added(segment_t * /*s*/) { return 0; };
- virtual int segm_deleted(ea_t /*startEA*/) { return 0; };
- virtual int segm_start_changed(segment_t * /*s*/) { return 0; };
- virtual int segm_end_changed(segment_t * /*s*/) { return 0; };
- virtual int segm_moved(ea_t /*from*/, ea_t /*to*/, asize_t /*size*/) { return 0; };
+ virtual int ti_changed(ea_t /*ea*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; }
+ virtual int op_ti_changed(ea_t /*ea*/, int /*n*/, const type_t * /*type*/, const p_list * /*fnames*/) { return 0; }
+ virtual int op_type_changed(ea_t /*ea*/, int /*n*/) { return 0; }
+ virtual int enum_created(enum_t /*id*/) { return 0; }
+ virtual int enum_deleted(enum_t /*id*/) { return 0; }
+ virtual int enum_bf_changed(enum_t /*id*/) { return 0; }
+ virtual int enum_renamed(enum_t /*id*/) { return 0; }
+ virtual int enum_cmt_changed(enum_t /*id*/) { return 0; }
+ virtual int enum_member_created(enum_t /*id*/, const_t cid) { return 0; }
+ virtual int enum_member_deleted(enum_t /*id*/, const_t cid) { return 0; }
+ virtual int struc_created(tid_t /*struc_id*/) { return 0; }
+ virtual int struc_deleted(tid_t /*struc_id*/) { return 0; }
+ virtual int struc_renamed(struc_t * /*sptr*/) { return 0; }
+ virtual int struc_expanded(struc_t * /*sptr*/) { return 0; }
+ virtual int struc_cmt_changed(tid_t /*struc_id*/) { return 0; }
+ virtual int struc_member_created(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; }
+ virtual int struc_member_deleted(struc_t * /*sptr*/, tid_t /*member_id*/, ea_t /*offset*/) { return 0; }
+ virtual int struc_member_renamed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; }
+ virtual int struc_member_changed(struc_t * /*sptr*/, member_t * /*mptr*/) { return 0; }
+ virtual int thunk_func_created(func_t * /*pfn*/) { return 0; }
+ virtual int func_tail_appended(func_t * /*pfn*/, func_t * /*tail*/) { return 0; }
+ virtual int func_tail_removed(func_t * /*pfn*/, ea_t /*tail_ea*/) { return 0; }
+ virtual int tail_owner_changed(func_t * /*tail*/, ea_t /*owner_func*/) { return 0; }
+ virtual int func_noret_changed(func_t * /*pfn*/) { return 0; }
+ virtual int segm_added(segment_t * /*s*/) { return 0; }
+ virtual int segm_deleted(ea_t /*startEA*/) { return 0; }
+ virtual int segm_start_changed(segment_t * /*s*/) { return 0; }
+ virtual int segm_end_changed(segment_t * /*s*/) { return 0; }
+ virtual int segm_moved(ea_t /*from*/, ea_t /*to*/, asize_t /*size*/) { return 0; }
};
//
@@ -951,6 +964,12 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va)
break;
}
+ case processor_t::auto_empty_finally:
+ {
+ proxy->auto_empty_finally();
+ break;
+ }
+
case processor_t::rename:
{
ea_t ea = va_arg(va, ea_t);
@@ -1000,6 +1019,19 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va)
break;
}
+ case processor_t::auto_empty:
+ {
+ proxy->auto_empty();
+ break;
+ }
+
+ case processor_t::auto_queue_empty:
+ {
+ atype_t type = va_arg(va, atype_t);
+ ret = proxy->auto_queue_empty(type);
+ break;
+ }
+
case processor_t::add_func:
{
func_t *func = va_arg(va, func_t *);
diff --git a/pywraps/py_kernwin.hpp b/pywraps/py_kernwin.hpp
index 40916b6..6452c10 100644
--- a/pywraps/py_kernwin.hpp
+++ b/pywraps/py_kernwin.hpp
@@ -218,6 +218,72 @@ def readsel2(view, p0, p1):
#
*/
+//------------------------------------------------------------------------
+/*
+#
+def umsg(text):
+ """
+ Prints text into IDA's Output window
+
+ @param text: text to print
+ Can be Unicode, or string in UTF-8 encoding
+ @return: number of bytes printed
+ """
+ pass
+#
+*/
+static PyObject* py_umsg(PyObject *o)
+{
+ PyObject* utf8 = NULL;
+ if ( PyUnicode_Check(o) )
+ {
+ utf8 = PyUnicode_AsUTF8String(o);
+ o = utf8;
+ }
+ else if ( !PyString_Check(o) )
+ {
+ PyErr_SetString(PyExc_TypeError, "A unicode or UTF-8 string expected");
+ return NULL;
+ }
+ int rc;
+ Py_BEGIN_ALLOW_THREADS;
+ rc = umsg("%s", PyString_AsString(o));
+ Py_END_ALLOW_THREADS;
+ Py_XDECREF(utf8);
+ return PyInt_FromLong(rc);
+}
+
+//------------------------------------------------------------------------
+/*
+#
+def msg(text):
+ """
+ Prints text into IDA's Output window
+
+ @param text: text to print
+ Can be Unicode, or string in local encoding
+ @return: number of bytes printed
+ """
+ pass
+#
+*/
+static PyObject* py_msg(PyObject *o)
+{
+ if ( PyUnicode_Check(o) )
+ return py_umsg(o);
+
+ if ( !PyString_Check(o) )
+ {
+ PyErr_SetString(PyExc_TypeError, "A string expected");
+ return NULL;
+ }
+ int rc;
+ Py_BEGIN_ALLOW_THREADS;
+ rc = msg("%s", PyString_AsString(o));
+ Py_END_ALLOW_THREADS;
+ return PyInt_FromLong(rc);
+}
+
//------------------------------------------------------------------------
/*
#
@@ -299,18 +365,17 @@ PyObject *py_str2user(const char *str)
//------------------------------------------------------------------------
/*
#
-def process_ui_action(name, flags):
+def process_ui_action(name):
"""
Invokes an IDA UI action by name
@param name: action name
- @param flags: Reserved. Must be zero
@return: Boolean
"""
pass
#
*/
-static bool py_process_ui_action(const char *name, int flags)
+static bool py_process_ui_action(const char *name, int flags = 0)
{
return process_ui_action(name, flags, NULL);
}
@@ -319,12 +384,7 @@ static bool py_process_ui_action(const char *name, int flags)
/*
#
def del_menu_item(menu_ctx):
- """
- Deletes a menu item previously added with add_menu_item()
-
- @param menu_ctx: value returned by add_menu_item()
- @return: Boolean
- """
+ """Deprecated. Use detach_menu_item()/unregister_action() instead."""
pass
#
*/
@@ -337,7 +397,6 @@ static bool py_del_menu_item(PyObject *py_ctx)
py_add_del_menu_item_ctx *ctx = (py_add_del_menu_item_ctx *)PyCObject_AsVoidPtr(py_ctx);
bool ok = del_menu_item(ctx->menupath.c_str());
-
if ( ok )
{
Py_DECREF(ctx->cb_data);
@@ -455,21 +514,7 @@ PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc)
/*
#
def add_menu_item(menupath, name, hotkey, flags, callback, args):
- """
- Adds a menu item
-
- @param menupath: path to the menu item after or before which the insertion will take place
- @param name: name of the menu item (~x~ is used to denote Alt-x hot letter)
- @param hotkey: hotkey for the menu item (may be empty)
- @param flags: one of SETMENU_... consts
- @param callback: function which gets called when the user selects the menu item.
- The function callback is of the form:
- def callback(*args):
- pass
- @param args: tuple containing the arguments
-
- @return: None or a menu context (to be used by del_menu_item())
- """
+ """Deprecated. Use register_action()/attach_menu_item() instead."""
pass
#
*/
@@ -816,6 +861,45 @@ class UI_Hooks(object):
"""
pass
+ def updating_actions(self, ctx):
+ """
+ The UI is about to batch-update some actions.
+
+ @param ctx: The action_update_ctx_t instance
+ @return: Ignored
+ """
+ pass
+
+ def updated_actions(self):
+ """
+ The UI is done updating actions.
+
+ @return: Ignored
+ """
+ pass
+
+ def populating_tform_popup(self, form, popup):
+ """
+ The UI is populating the TForm's popup menu.
+ Now is a good time to call idaapi.attach_action_to_popup()
+
+ @param form: The form
+ @param popup: The popup menu.
+ @return: Ignored
+ """
+ pass
+
+ def finish_populating_tform_popup(self, form, popup):
+ """
+ The UI is about to be done populating the TForm's popup menu.
+ Now is a good time to call idaapi.attach_action_to_popup()
+
+ @param form: The form
+ @param popup: The popup menu.
+ @return: Ignored
+ """
+ pass
+
def term(self):
"""
IDA is terminated and the database is already closed.
@@ -873,7 +957,242 @@ public:
PYW_GIL_CHECK_LOCKED_SCOPE();
Py_RETURN_NONE;
};
+
+ virtual void current_tform_changed(TForm * /*form*/, TForm * /*previous_form*/)
+ {
+ }
+
+ virtual void updating_actions(action_update_ctx_t *ctx)
+ {
+ }
+
+ virtual void updated_actions()
+ {
+ }
+
+ virtual void populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/)
+ {
+ }
+
+ virtual void finish_populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/)
+ {
+ }
};
+
+//-------------------------------------------------------------------------
+bool py_register_action(action_desc_t *desc)
+{
+ bool ok = register_action(*desc);
+ if ( ok )
+ {
+ // Success. We are managing this handler from now on,
+ // and must prevent it from being destroyed.
+ py_action_handlers[desc->name] = desc->handler;
+ // Let's set this to NULL, so when the wrapping Python action_desc_t
+ // instance is deleted, it doesn't try to delete the handler (See
+ // kernwin.i's action_desc_t::~action_desc_t()).
+ desc->handler = NULL;
+ }
+ return ok;
+}
+
+//-------------------------------------------------------------------------
+bool py_unregister_action(const char *name)
+{
+ bool ok = unregister_action(name);
+ if ( ok )
+ {
+ py_action_handler_t *handler =
+ (py_action_handler_t *) py_action_handlers[name];
+ delete handler;
+ py_action_handlers.erase(name);
+ }
+ return ok;
+}
+
+//-------------------------------------------------------------------------
+bool py_attach_dynamic_action_to_popup(
+ TForm *form,
+ TPopupMenu *popup_handle,
+ action_desc_t *desc,
+ const char *popuppath = NULL,
+ int flags = 0)
+{
+ bool ok = attach_dynamic_action_to_popup(
+ form, popup_handle, *desc, popuppath, flags);
+ if ( ok )
+ // Set the handler to null, so the desc won't destroy
+ // it, as it noticed ownership was taken by IDA.
+ // In addition, we don't need to register into the
+ // 'py_action_handlers', because IDA will destroy the
+ // handler as soon as the popup menu is closed.
+ desc->handler = NULL;
+ return ok;
+}
+
+// This is similar to a twinline_t, with improved memory management:
+// twinline_t has a dummy destructor, that performs no cleanup.
+struct disasm_line_t
+{
+ disasm_line_t() : at(NULL) {}
+ ~disasm_line_t() { qfree(at); }
+ disasm_line_t(const disasm_line_t &other) { *this = other; }
+ disasm_line_t &operator=(const disasm_line_t &other)
+ {
+ qfree(at);
+ at = other.at == NULL ? NULL : other.at->clone();
+ return *this;
+ }
+ place_t *at;
+ qstring line;
+ color_t prefix_color;
+ bgcolor_t bg_color;
+ bool is_default;
+};
+DECLARE_TYPE_AS_MOVABLE(disasm_line_t);
+typedef qvector disasm_text_t;
+
+//-------------------------------------------------------------------------
+void py_gen_disasm_text(ea_t ea1, ea_t ea2, disasm_text_t &text, bool truncate_lines)
+{
+ text_t _text;
+ gen_disasm_text(ea1, ea2, _text, truncate_lines);
+ for ( size_t i = 0, n = _text.size(); i < n; ++i )
+ {
+ const twinline_t &tl = _text[i];
+ disasm_line_t &dl = text.push_back();
+ dl.at = tl.at; // Transfer ownership
+ dl.line.inject(tl.line); // Transfer ownership
+ }
+}
+
+//-------------------------------------------------------------------------
+// Although 'TCustomControl*' and 'TForm*' instances can both be used
+// for attach_action_to_popup() at a binary-level, IDAPython SWIG bindings
+// require that a 'TForm *' wrapper be passed to wrap_attach_action_to_popup().
+// Thus, we provide another attach_action_to_popup() version, that
+// accepts a 'TCustomControl' as first argument.
+//
+// Since user-created GraphViewer are created like so:
+// +-------- PluginForm ----------+
+// |+----- TCustomControl -------+|
+// || ||
+// || ||
+// || ||
+// || ||
+// || ||
+// |+----------------------------+|
+// +------------------------------+
+// The user cannot use GetTForm(), and use that to attach
+// an action to, because that'll attach the action to the PluginForm
+// instance.
+// Instead, the user must use GetTCustomControl(), and call
+// this function below with it.
+bool attach_action_to_popup(
+ TCustomControl *tcc,
+ TPopupMenu *popup_handle,
+ const char *name,
+ const char *popuppath = NULL,
+ int flags = 0)
+{
+ return attach_action_to_popup((TForm*) tcc, popup_handle, name, popuppath, flags);
+}
+
+//-------------------------------------------------------------------------
+/*
+#
+def set_nav_colorizer(callback):
+ """
+ Set a new colorizer for the navigation band.
+
+ The 'callback' is a function of 2 arguments:
+ - ea (the EA to colorize for)
+ - nbytes (the number of bytes at that EA)
+ and must return a 'long' value.
+
+ The previous colorizer is returned, allowing
+ the new 'callback' to use 'call_nav_colorizer'
+ with it.
+
+ Note that the previous colorizer is returned
+ only the first time set_nav_colorizer() is called:
+ due to the way the colorizers API is defined in C,
+ it is impossible to chain more than 2 colorizers
+ in IDAPython: the original, IDA-provided colorizer,
+ and a user-provided one.
+
+ Example: colorizer inverting the color provided by the IDA colorizer:
+ def my_colorizer(ea, nbytes):
+ global ida_colorizer
+ orig = idaapi.call_nav_colorizer(ida_colorizer, ea, nbytes)
+ return long(~orig)
+
+ ida_colorizer = idaapi.set_nav_colorizer(my_colorizer)
+ """
+ pass
+#
+*/
+nav_colorizer_t *py_set_nav_colorizer(PyObject *new_py_colorizer)
+{
+ static ref_t py_colorizer;
+ struct ida_local lambda_t
+ {
+ static uint32 idaapi call_py_colorizer(ea_t ea, asize_t nbytes)
+ {
+ PYW_GIL_GET;
+
+ if ( py_colorizer == NULL ) // Shouldn't happen.
+ return 0;
+ newref_t pyres = PyObject_CallFunction(
+ py_colorizer.o, "KK",
+ (unsigned long long) ea,
+ (unsigned long long) nbytes);
+ PyW_ShowCbErr("nav_colorizer");
+ if ( pyres.o == NULL )
+ return 0;
+ if ( !PyLong_Check(pyres.o) )
+ {
+ static bool warned = false;
+ if ( !warned )
+ {
+ msg("WARNING: set_nav_colorizer() callback must return a 'long'.\n");
+ warned = true;
+ }
+ return 0;
+ }
+ return PyLong_AsLong(pyres.o);
+ }
+ };
+
+ bool need_install = py_colorizer == NULL;
+ py_colorizer = borref_t(new_py_colorizer);
+ return need_install
+ ? set_nav_colorizer(lambda_t::call_py_colorizer)
+ : NULL;
+}
+
+//-------------------------------------------------------------------------
+/*
+#
+def call_nav_colorizer(colorizer, ea, nbytes):
+ """
+ To be used with the IDA-provided colorizer, that is
+ returned as result of the first call to set_nav_colorizer().
+
+ This is a trivial trampoline, so that SWIG can generate a
+ wrapper that will do the types checking.
+ """
+ pass
+#
+*/
+uint32 py_call_nav_colorizer(
+ nav_colorizer_t *col,
+ ea_t ea,
+ asize_t nbytes)
+{
+ return col(ea, nbytes);
+}
+
//
//---------------------------------------------------------------------------
@@ -932,6 +1251,44 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va)
}
break;
}
+
+ case ui_current_tform_changed:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TForm *prev_form = va_arg(va, TForm *);
+ proxy->current_tform_changed(form, prev_form);
+ }
+ break;
+
+ case ui_updating_actions:
+ {
+ action_update_ctx_t *ctx = va_arg(va, action_update_ctx_t *);
+ proxy->updating_actions(ctx);
+ }
+ break;
+
+
+ case ui_updated_actions:
+ {
+ proxy->updated_actions();
+ }
+ break;
+
+ case ui_populating_tform_popup:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TPopupMenu *popup = va_arg(va, TPopupMenu *);
+ proxy->populating_tform_popup(form, popup);
+ }
+ break;
+
+ case ui_finish_populating_tform_popup:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TPopupMenu *popup = va_arg(va, TPopupMenu *);
+ proxy->finish_populating_tform_popup(form, popup);
+ }
+ break;
}
}
catch (Swig::DirectorException &e)
diff --git a/pywraps/py_kernwin.py b/pywraps/py_kernwin.py
index ec8c1dd..1d2b6c5 100644
--- a/pywraps/py_kernwin.py
+++ b/pywraps/py_kernwin.py
@@ -32,7 +32,7 @@ DP_INSIDE = 0x0010
# this flag alone cannot be used to determine orientation
DP_BEFORE = 0x0020
# used with combination of other flags
-DP_RAW = 0x0040
+DP_TAB = 0x0040
DP_FLOATING = 0x0080
# ----------------------------------------------------------------------
@@ -83,6 +83,17 @@ def askseg(defval, format):
else:
return None
+# ----------------------------------------------------------------------
+class action_handler_t:
+ def __init__(self):
+ pass
+
+ def activate(self, ctx):
+ return 0
+
+ def update(self, ctx):
+ pass
+
#
# ----------------------------------------------------------------------
diff --git a/pywraps/py_registry.hpp b/pywraps/py_registry.hpp
new file mode 100644
index 0000000..e7a5e02
--- /dev/null
+++ b/pywraps/py_registry.hpp
@@ -0,0 +1,112 @@
+
+#ifndef __PY_REGISTRY__
+#define __PY_REGISTRY__
+
+//
+//-------------------------------------------------------------------------
+static PyObject *_py_reg_subkey_children(const char *name, bool subkeys)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ PyObject *result = NULL;
+ qstrvec_t children;
+ Py_BEGIN_ALLOW_THREADS;
+ if ( reg_subkey_children(&children, name, subkeys) )
+ {
+ result = PyList_New(children.size());
+ if ( result != NULL )
+ for ( size_t i = 0, n = children.size(); i < n; ++i )
+ PyList_SET_ITEM(result, i, PyString_FromString(children[i].c_str()));
+ }
+ Py_END_ALLOW_THREADS;
+ if ( result == NULL )
+ Py_RETURN_NONE;
+ else
+ return result;
+}
+//
+
+
+//
+//-------------------------------------------------------------------------
+PyObject *py_reg_read_string(const char *name, const char *subkey = NULL, const char *def = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ char utf8[MAXSTR * 10];
+ bool ok;
+ Py_BEGIN_ALLOW_THREADS;
+ if ( def == NULL )
+ {
+ ok = reg_read_string(name, utf8, sizeof(utf8), subkey);
+ }
+ else
+ {
+ reg_read_string(name, sizeof(utf8), utf8, def, subkey);
+ ok = true;
+ }
+ Py_END_ALLOW_THREADS;
+ return PyString_FromString(ok ? utf8 : "");
+}
+
+//-------------------------------------------------------------------------
+regval_type_t py_reg_data_type(const char *name, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ regval_type_t rt = reg_unknown;
+ Py_BEGIN_ALLOW_THREADS;
+ reg_data_type(&rt, name, subkey);
+ Py_END_ALLOW_THREADS;
+ return rt;
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_read_binary(const char *name, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ bytevec_t bytes;
+ bool ok;
+ Py_BEGIN_ALLOW_THREADS;
+ ok = reg_read_binary(name, &bytes, subkey);
+ Py_END_ALLOW_THREADS;
+ if ( ok )
+ return PyString_FromStringAndSize((const char *) bytes.begin(), bytes.size());
+ else
+ Py_RETURN_NONE;
+}
+
+//-------------------------------------------------------------------------
+void py_reg_write_binary(const char *name, PyObject *py_bytes, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ if ( PyString_Check(py_bytes) )
+ {
+ char *py_bytes_raw = NULL;
+ Py_ssize_t py_size = 0;
+ PyString_AsStringAndSize(py_bytes, &py_bytes_raw, &py_size);
+ bytevec_t bytes;
+ bytes.append(py_bytes_raw, py_size);
+ Py_BEGIN_ALLOW_THREADS;
+ reg_write_binary(name, bytes.begin(), bytes.size(), subkey);
+ Py_END_ALLOW_THREADS;
+ }
+ else
+ {
+ PyErr_SetString(PyExc_ValueError, "Bytes string expected!");
+ }
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_subkey_subkeys(const char *name)
+{
+ return _py_reg_subkey_children(name, true);
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_subkey_values(const char *name)
+{
+ return _py_reg_subkey_children(name, false);
+}
+
+//
+
+
+#endif // __PY_REGISTRY__
diff --git a/pywraps/py_typeinf.hpp b/pywraps/py_typeinf.hpp
index 3411e48..d15d02c 100644
--- a/pywraps/py_typeinf.hpp
+++ b/pywraps/py_typeinf.hpp
@@ -67,9 +67,9 @@ PyObject *py_calc_type_size(const til_t *ti, PyObject *tp)
def apply_type(ti, ea, tp_name, py_type, py_fields, flags)
"""
Apply the specified type to the address
- @param ti: Type info. 'idaapi.cvar.idati' can be passed.
+ @param ti: Type info library. 'idaapi.cvar.idati' can be used.
@param py_type: type string
- @param py_fields: type fields
+ @param py_fields: fields string (may be empty or None)
@param ea: the address of the object
@param flags: combination of TINFO_... constants or 0
@return: Boolean
@@ -80,17 +80,44 @@ def apply_type(ti, ea, tp_name, py_type, py_fields, flags)
static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
}
const type_t *type = (const type_t *) PyString_AsString(py_type);
- const p_list *fields = (const p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
bool rc;
Py_BEGIN_ALLOW_THREADS;
- tinfo_t tif;
- rc = tif.deserialize(ti, &type, &fields, NULL) && apply_tinfo2(ea, tif, flags);
+ struc_t *sptr;
+ member_t *mptr = get_member_by_id(ea, &sptr);
+ if ( type[0] == '\0' )
+ {
+ if ( mptr != NULL )
+ {
+ rc = mptr->has_ti();
+ if ( rc )
+ del_member_tinfo(sptr, mptr);
+ }
+ else
+ {
+ rc = has_ti(ea);
+ if ( rc )
+ del_tinfo2(ea);
+ }
+ }
+ else
+ {
+ tinfo_t tif;
+ rc = tif.deserialize(ti, &type, &fields, NULL);
+ if ( rc )
+ {
+ if ( mptr != NULL )
+ rc = set_member_tinfo2(sptr, mptr, 0, tif, 0);
+ else
+ rc = apply_tinfo2(ea, tif, flags);
+ }
+ }
Py_END_ALLOW_THREADS;
return rc;
}
@@ -138,7 +165,7 @@ PyObject *py_unpack_object_from_idb(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -150,7 +177,7 @@ PyObject *py_unpack_object_from_idb(
// Unpack
type_t *type = (type_t *) PyString_AsString(py_type);
- p_list *fields = (p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
idc_value_t idc_obj;
error_t err;
Py_BEGIN_ALLOW_THREADS;
@@ -188,7 +215,7 @@ def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0):
Returns the error_t returned by idaapi.pack_object_to_idb
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param bytes: the bytes to unpack
@param pio_flags: flags used while unpacking
@return:
@@ -206,7 +233,7 @@ PyObject *py_unpack_object_from_bv(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) )
{
PyErr_SetString(PyExc_ValueError, "Incorrect argument type!");
return NULL;
@@ -218,7 +245,7 @@ PyObject *py_unpack_object_from_bv(
// Get type strings
type_t *type = (type_t *) PyString_AsString(py_type);
- p_list *fields = (p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Make a byte vector
bytevec_t bytes;
@@ -262,7 +289,7 @@ def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0):
Returns the error_t returned by idaapi.pack_object_to_idb
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param ea: ea to be used while packing
@param pio_flags: flags used while unpacking
"""
@@ -278,7 +305,7 @@ PyObject *py_pack_object_to_idb(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -296,7 +323,7 @@ PyObject *py_pack_object_to_idb(
// Get type strings
type_t *type = (type_t *)PyString_AsString(py_type);
- p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Pack
// error_t err;
@@ -315,7 +342,7 @@ def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0):
Packs a typed object to a string
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param base_ea: base ea used to relocate the pointers in the packed object
@param pio_flags: flags used while unpacking
@return:
@@ -335,7 +362,7 @@ PyObject *py_pack_object_to_bv(
int pio_flags=0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -353,7 +380,7 @@ PyObject *py_pack_object_to_bv(
// Get type strings
type_t *type = (type_t *)PyString_AsString(py_type);
- p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Pack
relobj_t bytes;
@@ -497,7 +524,7 @@ int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize)
PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -509,7 +536,7 @@ PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *nam
qstring res;
const type_t *type = (type_t *)PyString_AsString(py_type);
- const p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
bool ok;
Py_BEGIN_ALLOW_THREADS;
tinfo_t tif;
@@ -536,43 +563,78 @@ char idc_get_local_type_name(int ordinal, char *buf, size_t bufsize)
//
//-------------------------------------------------------------------------
-// A set of tinfo_t objects that were created from IDAPython.
+// A set of tinfo_t & details objects that were created from IDAPython.
// This is necessary in order to clear all the "type details" that are
// associated, in the kernel, with the tinfo_t instances.
//
// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is
// closed, but the "type details" must be cleared _before_ the IDB is closed.
-static qvector python_tinfos;
+static qvector py_tinfo_t_vec;
+static qvector py_ptr_type_data_t_vec;
+static qvector py_array_type_data_t_vec;
+static qvector py_func_type_data_t_vec;
+static qvector py_udt_type_data_t_vec;
+
+static void __clear(tinfo_t *inst) { inst->clear(); }
+static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); }
+static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); }
+static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); }
+static void __clear(udt_type_data_t *inst) { inst->clear(); }
+
void til_clear_python_tinfo_t_instances(void)
{
- // Pre-emptive strike: clear all the python-exposed tinfo_t instances: if that
- // were not done here, ~tinfo_t() calls happening as part of the python shutdown
- // process will try and clear() their details. ..but the kernel's til-related
- // functions will already have deleted those details at that point.
- for ( size_t i = 0, n = python_tinfos.size(); i < n; ++i )
- python_tinfos[i]->clear();
- // NOTE: Don't clear() the array of pointers. All the python-exposed tinfo_t
+ // Pre-emptive strike: clear all the python-exposed tinfo_t
+ // (& related types) instances: if that were not done here,
+ // ~tinfo_t() calls happening as part of the python shutdown
+ // process will try and clear() their details. ..but the kernel's
+ // til-related functions will already have deleted those details
+ // at that point.
+ //
+ // NOTE: Don't clear() the arrays of pointers. All the python-exposed
// instances will be deleted through the python shutdown/ref-decrementing
// process anyway (which will cause til_deregister_..() calls), and the
// entries will be properly pulled out of the vector when that happens.
+#define BATCH_CLEAR(Type) \
+ do \
+ { \
+ for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \
+ __clear(py_##Type##_vec[i]); \
+ } while ( false )
+
+ BATCH_CLEAR(tinfo_t);
+ BATCH_CLEAR(ptr_type_data_t);
+ BATCH_CLEAR(array_type_data_t);
+ BATCH_CLEAR(func_type_data_t);
+ BATCH_CLEAR(udt_type_data_t);
+#undef BATCH_CLEAR
}
-void til_register_python_tinfo_t_instance(tinfo_t *tif)
-{
- // Let's add_unique() it, because every reference to an object's
- // tinfo_t property will end up trying to register it.
- python_tinfos.add_unique(tif);
-}
-
-void til_deregister_python_tinfo_t_instance(tinfo_t *tif)
-{
- qvector::iterator found = python_tinfos.find(tif);
- if ( found != python_tinfos.end() )
- {
- tif->clear();
- python_tinfos.erase(found);
+#define DEF_REG_UNREG_REFCOUNTED(Type) \
+ void til_register_python_##Type##_instance(Type *inst) \
+ { \
+ /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \
+ /* to an object's tinfo_t property will end up trying to register it. */ \
+ py_##Type##_vec.add_unique(inst); \
+ } \
+ \
+ void til_deregister_python_##Type##_instance(Type *inst) \
+ { \
+ qvector::iterator found = py_##Type##_vec.find(inst); \
+ if ( found != py_##Type##_vec.end() ) \
+ { \
+ __clear(inst); \
+ /* tif->clear();*/ \
+ py_##Type##_vec.erase(found); \
+ } \
}
-}
+
+DEF_REG_UNREG_REFCOUNTED(tinfo_t);
+DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(array_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(func_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(udt_type_data_t);
+
+#undef DEF_REG_UNREG_REFCOUNTED
//
diff --git a/pywraps/py_ua.hpp b/pywraps/py_ua.hpp
index aad6b0c..c94603e 100644
--- a/pywraps/py_ua.hpp
+++ b/pywraps/py_ua.hpp
@@ -14,7 +14,6 @@ op_t *op_t_get_clink(PyObject *self)
{
return (op_t *)pyobj_get_clink(self);
}
-
//
//-------------------------------------------------------------------------
@@ -329,6 +328,49 @@ bool py_out_name_expr(
return op == NULL ? false : out_name_expr(*op, ea, off);
}
+//-------------------------------------------------------------------------
+/*
+#
+def construct_macro(insn):
+ """
+ See ua.hpp's construct_macro().
+ """
+ pass
+#
+*/
+bool py_construct_macro(bool enable, PyObject *build_macro)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+
+ if ( !PyCallable_Check(build_macro) )
+ return false;
+
+ static qstack macro_builders;
+
+ macro_builders.push(newref_t(build_macro));
+ struct ida_local lambda_t
+ {
+ static bool idaapi call_build_macro(insn_t &s, bool may_go_forward)
+ {
+ PyObject *py_builder = macro_builders.top().o;
+ newref_t pyres = PyObject_CallFunction(
+ py_builder, "O",
+ may_go_forward ? Py_True : Py_False);
+ PyW_ShowCbErr("build_macro");
+ if ( pyres.o == NULL || pyres.o == Py_None )
+ return false;
+ insn_t *_s = insn_t_get_clink(pyres.o);
+ if ( _s == NULL )
+ return false;
+ s = *_s;
+ return true;
+ }
+ };
+ bool res = construct_macro(enable, lambda_t::call_build_macro);
+ macro_builders.pop();
+ return res;
+}
+
//-------------------------------------------------------------------------
static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i)
{
diff --git a/pywraps/py_ua.py b/pywraps/py_ua.py
index 2d8eb9a..ccee1f3 100644
--- a/pywraps/py_ua.py
+++ b/pywraps/py_ua.py
@@ -403,22 +403,22 @@ CF_HLL = 0x10000 # Instruction may be present in a high level language functio
#
# op_t.type
-# Description Data field
-o_void = 0 # No Operand ----------
-o_reg = 1 # General Register (al,ax,es,ds...) reg
-o_mem = 2 # Direct Memory Reference (DATA) addr
-o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase
-o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
-o_imm = 5 # Immediate Value value
-o_far = 6 # Immediate Far Address (CODE) addr
-o_near = 7 # Immediate Near Address (CODE) addr
-o_idpspec0 = 8 # IDP specific type
-o_idpspec1 = 9 # IDP specific type
-o_idpspec2 = 10 # IDP specific type
-o_idpspec3 = 11 # IDP specific type
-o_idpspec4 = 12 # IDP specific type
-o_idpspec5 = 13 # IDP specific type
-o_last = 14 # first unused type
+# Description Data field
+o_void = 0 # No Operand ----------
+o_reg = 1 # General Register (al,ax,es,ds...) reg
+o_mem = 2 # Direct Memory Reference (DATA) addr
+o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase
+o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
+o_imm = 5 # Immediate Value value
+o_far = 6 # Immediate Far Address (CODE) addr
+o_near = 7 # Immediate Near Address (CODE) addr
+o_idpspec0 = 8 # Processor specific type
+o_idpspec1 = 9 # Processor specific type
+o_idpspec2 = 10 # Processor specific type
+o_idpspec3 = 11 # Processor specific type
+o_idpspec4 = 12 # Processor specific type
+o_idpspec5 = 13 # Processor specific type
+ # There can be more processor specific types
#
# op_t.dtyp
@@ -485,7 +485,7 @@ class processor_t(pyidc_opaque_object_t):
short processor names similar to the one in ph.psnames.
This method can be overridden to return to the kernel a different IDP description.
"""
- return self.plnames[0] + ':' + ':'.join(self.psnames)
+ return '\x01'.join(map(lambda t: '\x01'.join(t), zip(self.plnames, self.psnames)))
def get_uFlag(self):
"""Use this utility function to retrieve the 'uFlag' global variable"""
diff --git a/pywraps/py_view_base.hpp b/pywraps/py_view_base.hpp
index 79be7a4..6c211a8 100644
--- a/pywraps/py_view_base.hpp
+++ b/pywraps/py_view_base.hpp
@@ -138,12 +138,16 @@ class py_customidamemo_t
GRBASE_HAVE_CLOSE = 0x080,
GRBASE_HAVE_VIEW_SWITCHED = 0x100,
GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200,
+ GRBASE_HAVE_VIEW_MOUSE_MOVED = 0x400,
};
static void ensure_view_callbacks_installed();
int cb_flags;
- // number of arguments for OnViewClick implementation
- int ovc_num_args;
+ // number of arguments for:
+ int ovc_num_args; // OnViewClick implementation
+ int ovdc_num_args; // OnViewDblclick implementation
+ int ovmo_num_args; // OnViewMouseOver implementation
+ int ovmm_num_args; // OnViewMouseMoved implementation
protected:
ref_t self;
@@ -177,11 +181,15 @@ protected:
bool collect_pyobject_callbacks(PyObject *self);
virtual void collect_class_callbacks_ids(callbacks_ids_t *out);
+ void install_custom_viewer_handlers();
+
// Bi-directionally bind/unbind the Python object and this controller.
bool bind(PyObject *_self, TCustomControl *view);
void unbind();
static lookup_info_t lookup_info;
+ friend TForm *pycim_get_tform(PyObject *self);
+ friend TCustomControl *pycim_get_tcustom_control(PyObject *self);
public:
py_customidamemo_t();
@@ -211,8 +219,16 @@ public:
void on_view_close();
void on_view_switched(tcc_renderer_type_t rt);
void on_view_mouse_over(const view_mouse_event_t *event);
+ void on_view_mouse_moved(const view_mouse_event_t *event);
inline bool has_callback(int flag) { return (cb_flags & flag) != 0; }
int get_py_method_arg_count(char *method_name);
+
+ // View events that are bound with 'set_custom_viewer_handler()'.
+ static void idaapi s_on_view_mouse_moved(
+ TCustomControl *cv,
+ int shift,
+ view_mouse_event_t *e,
+ void *ud);
};
//-------------------------------------------------------------------------
@@ -223,6 +239,9 @@ py_customidamemo_t::py_customidamemo_t()
PYGLOG("%p: py_customidamemo_t()\n", this);
ensure_view_callbacks_installed();
ovc_num_args = -1;
+ ovdc_num_args = -1;
+ ovmo_num_args = -1;
+ ovmm_num_args = -1;
}
//-------------------------------------------------------------------------
@@ -262,7 +281,7 @@ void py_customidamemo_t::ensure_view_callbacks_installed()
py_view->on_view_keydown(key, state);
}
break;
- case view_popup:
+ case obsolete_view_popup:
py_view->on_view_popup();
break;
case view_click:
@@ -514,6 +533,18 @@ void py_customidamemo_t::unbind()
view = NULL;
}
+//-------------------------------------------------------------------------
+void idaapi py_customidamemo_t::s_on_view_mouse_moved(
+ TCustomControl *cv,
+ int shift,
+ view_mouse_event_t *e,
+ void *ud)
+{
+ PYW_GIL_GET;
+ py_customidamemo_t *_this = (py_customidamemo_t *) ud;
+ _this->on_view_mouse_moved(e);
+}
+
//-------------------------------------------------------------------------
int py_customidamemo_t::get_py_method_arg_count(char *method_name)
{
@@ -544,6 +575,7 @@ void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out)
out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE);
out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED);
out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER);
+ out->add(S_ON_VIEW_MOUSE_MOVED, GRBASE_HAVE_VIEW_MOUSE_MOVED);
}
//-------------------------------------------------------------------------
@@ -567,9 +599,22 @@ bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o)
if ( have > 0 && attr != NULL )
cb_flags |= have;
}
+
return true;
}
+//-------------------------------------------------------------------------
+void py_customidamemo_t::install_custom_viewer_handlers()
+{
+ if ( has_callback(GRBASE_HAVE_VIEW_MOUSE_MOVED) )
+ {
+ // Set user-data
+ set_custom_viewer_handler(view, CVH_USERDATA, (void *)this);
+
+ //
+ set_custom_viewer_handler(view, CVH_MOUSEMOVE, (void *)s_on_view_mouse_moved);
+ }
+}
//-------------------------------------------------------------------------
#define CHK_EVT(flag_needed) \
@@ -577,10 +622,22 @@ bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o)
return; \
PYW_GIL_CHECK_LOCKED_SCOPE()
+
#ifdef PYGDBG_ENABLED
-#define CHK_RES() PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o)
+#define CHK_RES() \
+ do \
+ { \
+ PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o); \
+ if (PyErr_Occurred()) \
+ PyErr_Print(); \
+ } while ( false )
#else
-#define CHK_RES()
+#define CHK_RES() \
+ do \
+ { \
+ if (PyErr_Occurred()) \
+ PyErr_Print(); \
+ } while ( false )
#endif
//-------------------------------------------------------------------------
@@ -632,13 +689,33 @@ void py_customidamemo_t::on_view_popup()
CHK_RES();
}
+//-------------------------------------------------------------------------
+static PyObject *build_renderer_pos_swig_proxy(const view_mouse_event_t *event)
+{
+ return SWIG_NewPointerObj(
+ SWIG_as_voidptr(&event->renderer_pos),
+ SWIGTYPE_p_renderer_pos_info_t,
+ 0);
+}
+
//-------------------------------------------------------------------------
void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
{
CHK_EVT(GRBASE_HAVE_VIEW_CLICK);
if ( ovc_num_args < 0 )
ovc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_CLICK);
- if ( ovc_num_args == 5 )
+ if ( ovc_num_args == 6 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_CLICK,
+ "iiiiO",
+ event->x, event->y, event->state, event->button, rpos));
+ CHK_RES();
+ }
+ else if ( ovc_num_args == 5 )
{
newref_t result(
PyObject_CallMethod(
@@ -646,6 +723,7 @@ void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
(char *)S_ON_VIEW_CLICK,
"iiii",
event->x, event->y, event->state, event->button));
+ CHK_RES();
}
else
{
@@ -655,21 +733,37 @@ void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
(char *)S_ON_VIEW_CLICK,
"iii",
event->x, event->y, event->state));
+ CHK_RES();
}
- CHK_RES();
}
//-------------------------------------------------------------------------
void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event)
{
CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK);
- newref_t result(
- PyObject_CallMethod(
- self.o,
- (char *)S_ON_VIEW_DBLCLICK,
- "iii",
- event->x, event->y, event->state));
- CHK_RES();
+ if ( ovdc_num_args < 0 )
+ ovdc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_DBLCLICK);
+ if ( ovdc_num_args == 5 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_DBLCLICK,
+ "iiiO",
+ event->x, event->y, event->state, rpos));
+ CHK_RES();
+ }
+ else
+ {
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_DBLCLICK,
+ "iii",
+ event->x, event->y, event->state));
+ CHK_RES();
+ }
}
//-------------------------------------------------------------------------
@@ -701,32 +795,55 @@ void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt)
}
//-------------------------------------------------------------------------
-void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
+static ref_t build_current_graph_item_tuple(int *out_icode, const view_mouse_event_t *event)
{
- CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER);
- if ( event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY )
+ const selection_item_t *item = event->location.item;
+ ref_t tuple;
+ if ( (event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY)
+ && item != NULL )
{
- const selection_item_t *item = event->location.item;
- int icode;
- ref_t tuple;
- if ( item != NULL )
+ if ( item->is_node )
{
- if ( item->is_node )
- {
- icode = 1;
- tuple = newref_t(Py_BuildValue("(i)", item->node));
- }
- else
- {
- icode = 2;
- tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst));
- }
+ *out_icode = 1;
+ tuple = newref_t(Py_BuildValue("(i)", item->node));
}
else
{
- icode = 0;
- tuple = newref_t(Py_BuildValue("()"));
+ *out_icode = 2;
+ tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst));
}
+ }
+ else
+ {
+ *out_icode = 0;
+ tuple = newref_t(Py_BuildValue("()"));
+ }
+ return tuple;
+}
+
+//-------------------------------------------------------------------------
+void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
+{
+ CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER);
+ if ( ovmo_num_args < 0 )
+ ovmo_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_OVER);
+ if ( event->rtype != TCCRT_GRAPH && event->rtype != TCCRT_PROXIMITY )
+ return;
+
+ int icode;
+ ref_t tuple = build_current_graph_item_tuple(&icode, event);
+ if ( ovmo_num_args == 7 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_MOUSE_OVER,
+ "iiiiOO",
+ event->x, event->y, event->state, icode, tuple.o, rpos));
+ CHK_RES();
+ }
+ else
+ {
newref_t result(PyObject_CallMethod(
self.o,
(char *)S_ON_VIEW_MOUSE_OVER,
@@ -736,6 +853,27 @@ void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
}
}
+//-------------------------------------------------------------------------
+void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event)
+{
+ CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_MOVED);
+ if ( ovmm_num_args < 0 )
+ ovmm_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_MOVED);
+
+ int icode;
+ ref_t tuple = build_current_graph_item_tuple(&icode, event);
+ if ( ovmm_num_args == 7 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_MOUSE_MOVED,
+ "iiiiOO",
+ event->x, event->y, event->state, icode, tuple.o, rpos));
+ CHK_RES();
+ }
+}
+
#undef CHK_RES
#undef CHK_EVT
@@ -748,6 +886,10 @@ void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
GET_THIS(); \
if ( _this == NULL ) \
return
+#define CHK_THIS_OR_NULL() \
+ GET_THIS(); \
+ if ( _this == NULL ) \
+ return NULL;
#define CHK_THIS_OR_NONE() \
GET_THIS(); \
if ( _this == NULL ) \
@@ -829,7 +971,28 @@ PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject
return _this->set_groups_visibility(groups, expand, new_current);
}
+//-------------------------------------------------------------------------
+TForm *pycim_get_tform(PyObject *self)
+{
+ CHK_THIS_OR_NULL();
+ TForm *form = NULL;
+ if ( !py_customidamemo_t::lookup_info.find_by_py_view(&form, NULL, _this) )
+ return NULL;
+ return form;
+}
+
+//-------------------------------------------------------------------------
+TCustomControl *pycim_get_tcustom_control(PyObject *self)
+{
+ CHK_THIS_OR_NULL();
+ TCustomControl *tcc = NULL;
+ if ( !py_customidamemo_t::lookup_info.find_by_py_view(NULL, &tcc, _this) )
+ return NULL;
+ return tcc;
+}
+
#undef CHK_THIS_OR_NONE
+#undef CHK_THIS_OR_NULL
#undef CHK_THIS
#undef GET_THIS
@@ -848,6 +1011,8 @@ void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt);
PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos);
PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current);
PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current);
+TForm *pycim_get_tform(PyObject *self);
+TCustomControl *pycim_get_tcustom_control(PyObject *self);
//
diff --git a/pywraps/py_view_base.py b/pywraps/py_view_base.py
index 4048468..2d68c93 100644
--- a/pywraps/py_view_base.py
+++ b/pywraps/py_view_base.py
@@ -104,4 +104,21 @@ class CustomIDAMemo(object):
"""
return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current)
+ def GetTForm(self):
+ """
+ Return the TForm hosting this view.
+
+ @return: The TForm that hosts this view, or None.
+ """
+ return _idaapi.pycim_get_tform(self)
+
+ def GetTCustomControl(self):
+ """
+ Return the TCustomControl underlying this view.
+
+ @return: The TCustomControl underlying this view, or None.
+ """
+ return _idaapi.pycim_get_tcustom_control(self)
+
+
#
diff --git a/pywraps/pywraps.vcproj b/pywraps/pywraps.vcproj
index f742bb8..f0dad19 100644
--- a/pywraps/pywraps.vcproj
+++ b/pywraps/pywraps.vcproj
@@ -985,7 +985,7 @@
>
always return a simple string of bytes
+ (i.e., a 'str' instance), and not a string of unicode characters.
+
+ If you want auto-conversion to unicode strings (that is: real strings),
+ you should probably be using the idautils.Strings class.
+
@param ea: linear address of the string
@param len: length of the string in bytes (including terminating 0)
- @param type: type of the string
- @param flags: combination of ACFOPT_...
- @return: string contents (not including terminating 0) or None
+ @param type: type of the string. Represents both the character encoding,
+ and the 'type' of string at the given location.
+ @param flags: combination of ACFOPT_..., to perform output conversion.
+ @return: a bytes-filled str object.
"""
pass
#
@@ -775,7 +781,7 @@ static PyObject *py_get_ascii_contents2(
return NULL;
size_t used_size;
- if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size) )
+ if ( !get_ascii_contents2(ea, len, type, buf, len+1, &used_size, flags) )
{
qfree(buf);
Py_RETURN_NONE;
diff --git a/swig/dbg.i b/swig/dbg.i
index 572d250..18c3b77 100644
--- a/swig/dbg.i
+++ b/swig/dbg.i
@@ -28,6 +28,20 @@ typedef struct
%ignore set_manual_regions;
%ignore inform_idc_about_debthread;
%ignore is_dbgmem_valid;
+
+%rename (list_bptgrps) py_list_bptgrps;
+%apply qstring *result { qstring *grp_name };
+%ignore qvector::operator==;
+%ignore qvector::operator!=;
+%ignore qvector::find;
+%ignore qvector::has;
+%ignore qvector::del;
+%ignore qvector::add_unique;
+%template(bpt_vec_t) qvector;
+
+%ignore internal_get_sreg_base;
+%rename (internal_get_sreg_base) py_internal_get_sreg_base;
+
// We want ALL wrappers around what is declared in dbg.hpp
// to release the GIL when calling into the IDA api: those
// might be very long operations, that even require some
@@ -384,6 +398,64 @@ int idaapi DBG_Callback(void *ud, int notification_code, va_list va)
}
return code;
}
+
+//------------------------------------------------------------------------
+/*
+#
+def py_list_bptgrps():
+ """
+ Returns list of breakpoint group names
+ @return: A list of strings or None on failure
+ """
+ pass
+#
+*/
+static PyObject *py_list_bptgrps()
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+
+ qstrvec_t args;
+ if ( list_bptgrps(&args) == 0 )
+ Py_RETURN_NONE;
+ return qstrvec2pylist(args);
+}
+
+//------------------------------------------------------------------------
+/*
+#
+def move_bpt_to_grp():
+ """
+ Sets new group for the breakpoint
+ """
+ pass
+#
+*/
+static void move_bpt_to_grp(bpt_t *bpt, const char *grp_name)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ set_bpt_group(*bpt, grp_name);
+}
+
+/*
+#
+def internal_get_sreg_base():
+ """
+ Get the sreg base, for the given thread.
+
+ @return: The sreg base, or BADADDR on failure.
+ """
+ pass
+#
+*/
+static ea_t py_internal_get_sreg_base(thid_t tid, int sreg_value)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ ea_t answer;
+ return internal_get_sreg_base(tid, sreg_value, &answer) < 1
+ ? BADADDR
+ : answer;
+}
+
//
%}
diff --git a/swig/diskio.i b/swig/diskio.i
index 466f4e5..0a505e0 100644
--- a/swig/diskio.i
+++ b/swig/diskio.i
@@ -2,6 +2,7 @@
%ignore enumerate_files;
%rename (enumerate_files) py_enumerate_files;
%ignore enumerate_system_files;
+%ignore enumerate_sorted_files;
%ignore ioport_bit_t;
%ignore ioport_bits_t;
%ignore ioport_t;
diff --git a/swig/expr.i b/swig/expr.i
index b2d9cce..23556c5 100644
--- a/swig/expr.i
+++ b/swig/expr.i
@@ -38,6 +38,7 @@
%ignore idc_stacksize;
%ignore idc_calldepth;
%ignore expr_printf;
+%ignore expr_uprintf;
%ignore expr_sprintf;
%ignore expr_printfer;
%ignore init_idc;
diff --git a/swig/frame.i b/swig/frame.i
index 568bfd2..63ba9f6 100644
--- a/swig/frame.i
+++ b/swig/frame.i
@@ -29,4 +29,6 @@
%ignore calc_frame_offset;
%ignore add_stkvar;
+%template(xreflist_t) qvector;
+
%include "frame.hpp"
diff --git a/swig/graph.i b/swig/graph.i
index 15c9332..7248435 100644
--- a/swig/graph.i
+++ b/swig/graph.i
@@ -35,13 +35,16 @@ protected:
private:
enum
{
- GRCODE_HAVE_USER_HINT = 0x00010000,
- GRCODE_HAVE_CLICKED = 0x00020000,
- GRCODE_HAVE_DBL_CLICKED = 0x00040000,
- GRCODE_HAVE_GOTFOCUS = 0x00080000,
- GRCODE_HAVE_LOSTFOCUS = 0x00100000,
- GRCODE_HAVE_CHANGED_CURRENT = 0x00200000,
- GRCODE_HAVE_COMMAND = 0x00400000
+ GRCODE_HAVE_USER_HINT = 0x00010000,
+ GRCODE_HAVE_CLICKED = 0x00020000,
+ GRCODE_HAVE_DBL_CLICKED = 0x00040000,
+ GRCODE_HAVE_GOTFOCUS = 0x00080000,
+ GRCODE_HAVE_LOSTFOCUS = 0x00100000,
+ GRCODE_HAVE_CHANGED_CURRENT = 0x00200000,
+ GRCODE_HAVE_COMMAND = 0x00400000,
+ GRCODE_HAVE_CREATING_GROUP = 0x00800000,
+ GRCODE_HAVE_DELETING_GROUP = 0x01000000,
+ GRCODE_HAVE_GROUP_VISIBILITY = 0x02000000,
};
struct nodetext_cache_t
{
@@ -145,6 +148,7 @@ private:
// Check return value to OnRefresh() call
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_COMMAND, "n", id));
+ PyW_ShowCbErr(S_ON_COMMAND);
}
// Refresh user-defined graph node number and edges
@@ -192,6 +196,7 @@ private:
(char *)S_ON_CLICK,
"i",
item2->n));
+ PyW_ShowCbErr(S_ON_CLICK);
return result == NULL || !PyObject_IsTrue(result.o);
}
@@ -213,29 +218,38 @@ private:
(char *)S_ON_DBL_CLICK,
"i",
item->node));
+ PyW_ShowCbErr(S_ON_DBL_CLICK);
return result == NULL || !PyObject_IsTrue(result.o);
}
// a graph viewer got focus
void on_gotfocus(graph_viewer_t * /*view*/)
{
+ if ( self.o == NULL )
+ return;
+
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(
PyObject_CallMethod(
self.o,
(char *)S_ON_ACTIVATE,
NULL));
+ PyW_ShowCbErr(S_ON_ACTIVATE);
}
// a graph viewer lost focus
void on_lostfocus(graph_viewer_t * /*view*/)
{
+ if ( self.o == NULL )
+ return;
+
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(
PyObject_CallMethod(
self.o,
(char *)S_ON_DEACTIVATE,
NULL));
+ PyW_ShowCbErr(S_ON_DEACTIVATE);
}
// a new graph node became the current node
@@ -254,6 +268,7 @@ private:
(char *)S_ON_SELECT,
"i",
curnode));
+ PyW_ShowCbErr(S_ON_SELECT);
return !(result != NULL && PyObject_IsTrue(result.o));
}
@@ -261,7 +276,6 @@ private:
int on_creating_group(mutable_graph_t *my_g, intvec_t *my_nodes)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- printf("my_g: %p; my_nodes: %p\n", my_g, my_nodes);
newref_t py_nodes(PyList_New(my_nodes->size()));
int i;
intvec_t::const_iterator p;
@@ -273,6 +287,7 @@ private:
(char *)S_ON_CREATING_GROUP,
"O",
py_nodes.o));
+ PyW_ShowCbErr(S_ON_CREATING_GROUP);
return (py_result == NULL || !PyInt_Check(py_result.o)) ? 1 : PyInt_AsLong(py_result.o);
}
@@ -302,6 +317,10 @@ private:
void jump_to_node(int nid)
{
+ ref_t nodes(PyW_TryGetAttrString(self.o, S_M_NODES));
+ if ( nid >= PyList_Size(nodes.o) )
+ return;
+
viewer_center_on(view, nid);
int x, y;
@@ -345,6 +364,7 @@ private:
if ( pview != NULL )
viewer_fit_window(pview);
bind(self, pview);
+ install_custom_viewer_handlers();
refresh();
lookup_info.commit(e, form, view);
}
@@ -382,13 +402,16 @@ public:
cmdid_pyg.clear(this);
}
- static void SelectNode(PyObject *self, int /*nid*/)
+ static void SelectNode(PyObject *self, int nid)
{
+ if ( nid < 0 )
+ return;
+
py_graph_t *_this = view_extract_this(self);
if ( _this == NULL || !lookup_info.find_by_py_view(NULL, NULL, _this) )
return;
- _this->jump_to_node(0);
+ _this->jump_to_node(nid);
}
static Py_ssize_t AddCommand(PyObject *self, const char *title, const char *hotkey)
@@ -468,6 +491,9 @@ void py_graph_t::collect_class_callbacks_ids(callbacks_ids_t *out)
out->add(S_ON_SELECT, GRCODE_HAVE_CHANGED_CURRENT);
out->add(S_ON_ACTIVATE, GRCODE_HAVE_GOTFOCUS);
out->add(S_ON_DEACTIVATE, GRCODE_HAVE_LOSTFOCUS);
+ out->add(S_ON_CREATING_GROUP, GRCODE_HAVE_CREATING_GROUP);
+ out->add(S_ON_DELETING_GROUP, GRCODE_HAVE_DELETING_GROUP);
+ out->add(S_ON_GROUP_VISIBILITY, GRCODE_HAVE_GROUP_VISIBILITY);
}
//-------------------------------------------------------------------------
@@ -479,6 +505,7 @@ void py_graph_t::on_user_refresh(mutable_graph_t *g)
// Check return value to OnRefresh() call
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t ret(PyObject_CallMethod(self.o, (char *)S_ON_REFRESH, NULL));
+ PyW_ShowCbErr(S_ON_REFRESH);
if ( ret != NULL && PyObject_IsTrue(ret.o) )
{
// Refer to the nodes
@@ -550,6 +577,7 @@ bool py_graph_t::on_user_text(mutable_graph_t * /*g*/, int node, const char **st
// Not cached, call Python
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_GETTEXT, "i", node));
+ PyW_ShowCbErr(S_ON_GETTEXT);
if ( result == NULL )
return false;
@@ -597,6 +625,7 @@ int py_graph_t::on_user_hint(mutable_graph_t *, int mousenode, int /*mouseedge_s
PYW_GIL_CHECK_LOCKED_SCOPE();
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_HINT, "i", mousenode));
+ PyW_ShowCbErr(S_ON_HINT);
bool ok = result != NULL && PyString_Check(result.o);
if ( ok )
*hint = qstrdup(PyString_AsString(result.o));
@@ -650,7 +679,10 @@ int py_graph_t::gr_callback(int code, va_list va)
ret = on_dblclicked(view, item);
}
else
- ret = 1; // ignore
+ ret = 0; // We don't want to ignore the double click, but rather
+ // fallback to the default behavior (e.g., double-clicking
+ // on an edge will to jump to the node on the other side
+ // of that edge.)
break;
//
case grcode_gotfocus:
@@ -701,28 +733,43 @@ int py_graph_t::gr_callback(int code, va_list va)
break;
//
case grcode_creating_group: // a group is being created
+ if ( has_callback(GRCODE_HAVE_CREATING_GROUP) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
intvec_t *nodes = va_arg(va, intvec_t*);
ret = on_creating_group(g, nodes);
}
+ else
+ {
+ ret = 0; // Ok to create
+ }
break;
//
case grcode_deleting_group: // a group is being deleted
+ if ( has_callback(GRCODE_HAVE_DELETING_GROUP) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
int old_group = va_arg(va, int);
ret = on_deleting_group(g, old_group);
}
+ else
+ {
+ ret = 0; // Ok to delete
+ }
break;
//
case grcode_group_visibility: // a group is being collapsed/uncollapsed
+ if ( has_callback(GRCODE_HAVE_GROUP_VISIBILITY) )
{
mutable_graph_t *g = va_arg(va, mutable_graph_t*);
int group = va_arg(va, int);
bool expand = bool(va_arg(va, int));
ret = on_group_visibility(g, group, expand);
}
+ else
+ {
+ ret = 0; // Ok.
+ }
break;
//
default:
@@ -844,10 +891,9 @@ class GraphViewer(CustomIDAMemo):
def AddCommand(self, title, hotkey):
"""
- Adds a menu command to the graph. Call this command after the graph is shown (with Show()).
- Once a command is added, a command id is returned. The commands are handled inside the OnCommand() handler
-
- @return: 0 on failure or the command id
+ Deprecated: Use
+ - register_action()
+ - attach_action_to_popup()
"""
return _idaapi.pyg_add_command(self, title, hotkey)
@@ -929,10 +975,9 @@ class GraphViewer(CustomIDAMemo):
#
# def OnCommand(self, cmd_id):
# """
-# Triggered when a menu command is selected through the menu or its hotkey
-# @return: None
+# Deprecated
# """
-# print "command:", cmd_id
+# pass
#
#
%}
diff --git a/swig/hexrays.i b/swig/hexrays.i
index 1e1da9f..c557d69 100644
--- a/swig/hexrays.i
+++ b/swig/hexrays.i
@@ -57,6 +57,7 @@
%ignore cexpr_t::cexpr_t(mbl_array_t *mba, const lvar_t &v);
%ignore lvar_t::is_promoted_arg;
%ignore lvar_t::lvar_t;
+%ignore vdloc_t::is_fpu_mreg;
%ignore strtype_info_t::find_strmem;
%ignore file_printer_t::_print;
%ignore file_printer_t;
@@ -120,51 +121,72 @@ public:
cexpr_t *cexpr const { return (cexpr_t *)self; }
};
-#define CITEM_MEMBER_REF(name) \
- name##_t *name const { return self->##name; }
-
//---------------------------------------------------------------------
// swig doesn't very much like the way the union is done in this class so we need to wrap all these up.
-%extend cinsn_t {
- CITEM_MEMBER_REF(cblock)
- CITEM_MEMBER_REF(cexpr)
- CITEM_MEMBER_REF(cif)
- CITEM_MEMBER_REF(cfor)
- CITEM_MEMBER_REF(cwhile)
- CITEM_MEMBER_REF(cdo)
- CITEM_MEMBER_REF(cswitch)
- CITEM_MEMBER_REF(creturn)
- CITEM_MEMBER_REF(cgoto)
- CITEM_MEMBER_REF(casm)
-};
+#define CITEM_MEMBER_REF(name) \
+ c##name##_t *c##name const { if ( self->op == cit_##name ) { return self->c##name; } else { return NULL; } }
+%extend cinsn_t {
+ CITEM_MEMBER_REF(block);
+ CITEM_MEMBER_REF(expr);
+ CITEM_MEMBER_REF(if);
+ CITEM_MEMBER_REF(for);
+ CITEM_MEMBER_REF(while);
+ CITEM_MEMBER_REF(do);
+ CITEM_MEMBER_REF(switch);
+ CITEM_MEMBER_REF(return);
+ CITEM_MEMBER_REF(goto);
+ CITEM_MEMBER_REF(asm);
+};
+#undef CITEM_MEMBER_REF
+
+//-------------------------------------------------------------------------
#define CEXPR_MEMBER_REF(type, name) \
- type name const { return self->##name; }
+ type name const { return self->##name; }
+
+#define CEXPR_CONDITIONAL_MEMBER_REF(type, name, condition, default_value) \
+ type name const { if ( condition ) { return self->##name; } else { return default_value; } }
%extend cexpr_t {
- CEXPR_MEMBER_REF(cnumber_t*, n)
- CEXPR_MEMBER_REF(fnumber_t*, fpc)
- const var_ref_t& v { return self->v; }
- CEXPR_MEMBER_REF(ea_t, obj_ea)
- CEXPR_MEMBER_REF(int, refwidth)
- CEXPR_MEMBER_REF(cexpr_t*, x)
- CEXPR_MEMBER_REF(cexpr_t*, y)
- CEXPR_MEMBER_REF(carglist_t*, a)
- CEXPR_MEMBER_REF(int, m)
- CEXPR_MEMBER_REF(cexpr_t*, z)
- CEXPR_MEMBER_REF(int, ptrsize)
- CEXPR_MEMBER_REF(cinsn_t*, insn)
- CEXPR_MEMBER_REF(char*, helper)
- CEXPR_MEMBER_REF(char*, string)
+ CEXPR_CONDITIONAL_MEMBER_REF(cnumber_t*, n, self->op == cot_num, NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(fnumber_t*, fpc, self->op == cot_fnum, NULL);
+ var_ref_t* v const { if ( self->op == cot_var ) { return &self->v; } else { return NULL; } }
+ CEXPR_CONDITIONAL_MEMBER_REF(ea_t, obj_ea, self->op == cot_obj, BADADDR);
+ CEXPR_MEMBER_REF(int, refwidth);
+ CEXPR_CONDITIONAL_MEMBER_REF(cexpr_t*, x, op_uses_x(self->op), NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(cexpr_t*, y, op_uses_y(self->op), NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(carglist_t*, a, self->op == cot_call, NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(int, m, self->op == cot_memptr || self->op == cot_memref, 0);
+ CEXPR_CONDITIONAL_MEMBER_REF(cexpr_t*, z, op_uses_z(self->op), NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(int, ptrsize, self->op == cot_ptr || self->op == cot_memptr, 0);
+ CEXPR_MEMBER_REF(cinsn_t*, insn);
+ CEXPR_CONDITIONAL_MEMBER_REF(char*, helper, self->op == cot_helper, NULL);
+ CEXPR_CONDITIONAL_MEMBER_REF(char*, string, self->op == cot_str, NULL);
};
+#undef CEXPR_CONDITIONAL_MEMBER_REF
+#undef CEXPR_MEMBER_REF
+
+//-------------------------------------------------------------------------
+#define CTREE_ITEM_MEMBER_REF(type, name) \
+ type name const { return self->##name; }
+
+#define CTREE_CONDITIONAL_ITEM_MEMBER_REF(type, name, wanted_citype) \
+ type name const { if ( self->citype == wanted_citype ) { return self->##name; } else { return NULL; } }
+
%extend ctree_item_t {
- CEXPR_MEMBER_REF(citem_t *, it)
- CEXPR_MEMBER_REF(lvar_t*, l)
- CEXPR_MEMBER_REF(cfunc_t*, f)
- const treeloc_t& loc { return self->loc; }
+ CTREE_ITEM_MEMBER_REF(citem_t *, it);
+ CTREE_CONDITIONAL_ITEM_MEMBER_REF(cexpr_t*, e, VDI_EXPR);
+ CTREE_CONDITIONAL_ITEM_MEMBER_REF(cinsn_t*, i, VDI_EXPR);
+ CTREE_CONDITIONAL_ITEM_MEMBER_REF(lvar_t*, l, VDI_LVAR);
+ CTREE_CONDITIONAL_ITEM_MEMBER_REF(cfunc_t*, f, VDI_FUNC);
+ treeloc_t* loc const { if ( self->citype == VDI_TAIL ) { return &self->loc; } else { return NULL; } }
};
+#undef CTREE_CONDITIONAL_ITEM_MEMBER_REF
+#undef CTREE_ITEM_MEMBER_REF
+
+//-------------------------------------------------------------------------
/* for qvector instanciations where the class is a pointer (cinsn_t, citem_t) we need
to fix the at() return type, otherwise swig mistakenly thinks it is "cinsn_t *&" and nonsense ensues. */
%extend qvector< cinsn_t *> {
@@ -303,28 +325,20 @@ void delete_qstring_printer_t(qstring_printer_t *qs)
//---------------------------------------------------------------------
static int hexrays_python_call(ref_t fct, ref_t args)
{
- PYW_GIL_GET;
+ PYW_GIL_GET;
- int result;
- int ecode1 = 0 ;
-
- newref_t resultobj(PyEval_CallObject(fct.o, args.o));
- if ( resultobj == NULL )
- {
- msg("IDAPython: Hex-rays python callback raised an exception.\n");
-
- // we can't do much else than clear the exception since this was not called from Python.
- // XXX: print stack trace?
- PyErr_Clear();
- return 0;
- }
-
- ecode1 = SWIG_AsVal_int(resultobj.o, &result);
- if (SWIG_IsOK(ecode1))
- return result;
-
- msg("IDAPython: Hex-rays python callback returned non-integer; value ignored.\n");
+ newref_t resultobj(PyEval_CallObject(fct.o, args.o));
+ if (PyErr_Occurred())
+ {
+ PyErr_Print();
return 0;
+ }
+
+ int result;
+ if ( SWIG_IsOK(SWIG_AsVal_int(resultobj.o, &result)) )
+ return result;
+ msg("IDAPython: Hex-rays python callback returned non-integer; value ignored.\n");
+ return 0;
}
//---------------------------------------------------------------------
@@ -342,179 +356,195 @@ static bool idaapi __python_custom_viewer_popup_item_callback(void *ud)
//---------------------------------------------------------------------
static int idaapi __hexrays_python_callback(void *ud, hexrays_event_t event, va_list va)
{
- PYW_GIL_GET;
+ PYW_GIL_GET;
- int ret;
- borref_t fct((PyObject *)ud);
- switch(event)
- {
- case hxe_maturity:
- ///< Ctree maturity level is being changed.
- ///< cfunc_t *cfunc
- ///< ctree_maturity_t new_maturity
- {
- cfunc_t *arg0 = va_arg(va, cfunc_t *);
- ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ));
- newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_interr:
- ///< Internal error has occurred.
- ///< int errcode
- {
- int arg0 = va_argi(va, int);
- newref_t args(Py_BuildValue("(ii)", event, arg0));
- ret = hexrays_python_call(fct, args);
- }
- break;
+ int ret;
+ borref_t fct((PyObject *)ud);
+ switch(event)
+ {
+ case hxe_maturity:
+ ///< Ctree maturity level is being changed.
+ ///< cfunc_t *cfunc
+ ///< ctree_maturity_t new_maturity
+ {
+ cfunc_t *arg0 = va_arg(va, cfunc_t *);
+ ctree_maturity_t arg1 = va_argi(va, ctree_maturity_t);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ));
+ newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_interr:
+ ///< Internal error has occurred.
+ ///< int errcode
+ {
+ int arg0 = va_argi(va, int);
+ newref_t args(Py_BuildValue("(ii)", event, arg0));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
- case hxe_print_func:
- ///< Printing ctree and generating text.
- ///< cfunc_t *cfunc
- ///< vc_printer_t *vp
- ///< Returns: 1 if text has been generated by the plugin
- {
- cfunc_t *arg0 = va_arg(va, cfunc_t *);
- vc_printer_t *arg1 = va_arg(va, vc_printer_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ));
- newref_t arg1obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 ));
- newref_t args(Py_BuildValue("(iOO)", event, arg0obj.o, arg1obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
+ case hxe_print_func:
+ ///< Printing ctree and generating text.
+ ///< cfunc_t *cfunc
+ ///< vc_printer_t *vp
+ ///< Returns: 1 if text has been generated by the plugin
+ {
+ cfunc_t *arg0 = va_arg(va, cfunc_t *);
+ vc_printer_t *arg1 = va_arg(va, vc_printer_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_cfunc_t, 0 ));
+ newref_t arg1obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg1), SWIGTYPE_p_vc_printer_t, 0 ));
+ newref_t args(Py_BuildValue("(iOO)", event, arg0obj.o, arg1obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
- // User interface related events:
- case hxe_open_pseudocode:
- ///< New pseudocode view has been opened.
- ///< vdui_t *vu
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_switch_pseudocode:
- ///< Existing pseudocode view has been reloaded
- ///< with a new function. Its text has not been
- ///< refreshed yet, only cfunc and mba pointers are ready.
- ///< vdui_t *vu
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_refresh_pseudocode:
- ///< Existing pseudocode text has been refreshed.
- ///< vdui_t *vu
- ///< See also hxe_text_ready, which happens earlier
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_close_pseudocode:
- ///< Pseudocode view is being closed.
- ///< vdui_t *vu
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_keyboard:
- ///< Keyboard has been hit.
- ///< vdui_t *vu
- ///< int key_code (VK_...)
- ///< int shift_state
- ///< Should return: 1 if the event has been handled
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- int arg1 = va_argi(va, int);
- int arg2 = va_argi(va, int);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iOii)", event, arg0obj.o, arg1, arg2));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_right_click:
- ///< Mouse right click. We can add menu items now.
- ///< vdui_t *vu
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_double_click:
- ///< Mouse double click.
- ///< vdui_t *vu
- ///< int shift_state
- ///< Should return: 1 if the event has been handled
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- int arg1 = va_argi(va, int);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_curpos:
- ///< Current cursor position has been changed.
- ///< (for example, by left-clicking or using keyboard)
- ///< vdui_t *vu
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_create_hint:
- ///< Create a hint for the current item.
- ///< vdui_t *vu
- ///< qstring *result_hint
- ///< int *implines
- ///< Possible return values:
- ///< 0: the event has not been handled
- ///< 1: hint has been created (should set *implines to nonzero as well)
- ///< 2: hint has been created but the standard hints must be
- ///< appended by the decompiler
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- case hxe_text_ready:
- ///< Decompiled text is ready.
- ///< vdui_t *vu
- ///< This event can be used to modify the output text (sv).
- ///< The text uses regular color codes (see lines.hpp)
- ///< COLOR_ADDR is used to store pointers to ctree elements
- {
- vdui_t *arg0 = va_arg(va, vdui_t *);
- newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
- newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
- ret = hexrays_python_call(fct, args);
- }
- break;
- default:
- //~ msg("IDAPython: Unknown event `%u' occured\n", event);
- ret = 0;
- break;
- }
+ // User interface related events:
+ case hxe_open_pseudocode:
+ ///< New pseudocode view has been opened.
+ ///< vdui_t *vu
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_switch_pseudocode:
+ ///< Existing pseudocode view has been reloaded
+ ///< with a new function. Its text has not been
+ ///< refreshed yet, only cfunc and mba pointers are ready.
+ ///< vdui_t *vu
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_refresh_pseudocode:
+ ///< Existing pseudocode text has been refreshed.
+ ///< vdui_t *vu
+ ///< See also hxe_text_ready, which happens earlier
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_close_pseudocode:
+ ///< Pseudocode view is being closed.
+ ///< vdui_t *vu
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_keyboard:
+ ///< Keyboard has been hit.
+ ///< vdui_t *vu
+ ///< int key_code (VK_...)
+ ///< int shift_state
+ ///< Should return: 1 if the event has been handled
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ int arg1 = va_argi(va, int);
+ int arg2 = va_argi(va, int);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iOii)", event, arg0obj.o, arg1, arg2));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_right_click:
+ ///< Mouse right click. We can add menu items now.
+ ///< vdui_t *vu
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_double_click:
+ ///< Mouse double click.
+ ///< vdui_t *vu
+ ///< int shift_state
+ ///< Should return: 1 if the event has been handled
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ int arg1 = va_argi(va, int);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iOi)", event, arg0obj.o, arg1));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_curpos:
+ ///< Current cursor position has been changed.
+ ///< (for example, by left-clicking or using keyboard)
+ ///< vdui_t *vu
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_create_hint:
+ ///< Create a hint for the current item.
+ ///< vdui_t *vu
+ ///< qstring *result_hint
+ ///< int *implines
+ ///< Possible return values:
+ ///< 0: the event has not been handled
+ ///< 1: hint has been created (should set *implines to nonzero as well)
+ ///< 2: hint has been created but the standard hints must be
+ ///< appended by the decompiler
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_text_ready:
+ ///< Decompiled text is ready.
+ ///< vdui_t *vu
+ ///< This event can be used to modify the output text (sv).
+ ///< The text uses regular color codes (see lines.hpp)
+ ///< COLOR_ADDR is used to store pointers to ctree elements
+ {
+ vdui_t *arg0 = va_arg(va, vdui_t *);
+ newref_t arg0obj(SWIG_NewPointerObj(SWIG_as_voidptr(arg0), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t args(Py_BuildValue("(iO)", event, arg0obj.o));
+ ret = hexrays_python_call(fct, args);
+ }
+ break;
+ case hxe_populating_popup:
+ ///< Populating popup menu. We can add menu items now.
+ ///< TForm *form
+ ///< TPopupMenu *popup_handle
+ ///< vdui_t *vu
+ {
+ TForm *form = va_arg(va, TForm *);
+ TPopupMenu *pp = va_arg(va, TPopupMenu*);
+ vdui_t *vdui = va_arg(va, vdui_t *);
+ newref_t py_form(SWIG_NewPointerObj(SWIG_as_voidptr(form), SWIGTYPE_p_Forms__TForm, 0));
+ newref_t py_popup(SWIG_NewPointerObj(SWIG_as_voidptr(pp), SWIGTYPE_p_Menus__TPopupMenu, 0));
+ newref_t py_vdui(SWIG_NewPointerObj(SWIG_as_voidptr(vdui), SWIGTYPE_p_vdui_t, 0 ));
+ newref_t py_args(Py_BuildValue("(iOOO)", event, py_form.o, py_popup.o, py_vdui.o));
+ ret = hexrays_python_call(fct, py_args);
+ }
+ break;
+ default:
+ //~ msg("IDAPython: Unknown event `%u' occured\n", event);
+ ret = 0;
+ break;
+ }
- return ret;
+ return ret;
}
%}
@@ -677,17 +707,37 @@ cfuncptr_t _decompile(func_t *pfn, hexrays_failure_t *hf);
%python_callback_in(PyObject *custom_viewer_popup_item_callback);
%ignore cexpr_t::get_1num_op(const cexpr_t **, const cexpr_t **) const;
+%ignore cexpr_t::find_ptr_or_array(bool) const;
+
#pragma SWIG nowarn=503
%warnfilter(514) user_lvar_visitor_t; // Director base class 'x' has no virtual destructor.
%warnfilter(514) ctree_visitor_t; // ditto
%warnfilter(514) ctree_parentee_t; // ditto
%warnfilter(514) cfunc_parentee_t; // ditto
%warnfilter(473) user_lvar_visitor_t::get_info_mapping_for_saving; // Returning a pointer or reference in a director method is not recommended.
+
%feature("director") ctree_visitor_t;
%feature("director") ctree_parentee_t;
%feature("director") cfunc_parentee_t;
%feature("director") user_lvar_visitor_t;
+
+// http://www.swig.org/Doc2.0/SWIGDocumentation.html#Python_nn36
+// http://www.swig.org/Doc2.0/SWIGDocumentation.html#Customization_exception_special_variables
+%define %possible_director_exc(Method)
+%exception Method {
+ try {
+ $action
+ } catch ( Swig::DirectorException & ) {
+ // A DirectorException might be raised in deeper layers.
+ SWIG_fail;
+ }
+}
+%enddef
+%possible_director_exc(ctree_visitor_t::apply_to)
+%possible_director_exc(ctree_visitor_t::apply_to_exprs)
%include "hexrays.hpp"
+%exception; // Delete & restore handlers
+%exception_set_default_handlers();
%pythoncode %{
@@ -1099,7 +1149,7 @@ _map_as_dict(user_cmts_t, 'user_cmts', treeloc_t, citem_cmt_t)
_map_as_dict(user_numforms_t, 'user_numforms', operand_locator_t, number_format_t)
_map_as_dict(user_iflags_t, 'user_iflags', citem_locator_t, (int, long))
_map_as_dict(user_unions_t, 'user_unions', (int, long), intvec_t)
-_map_as_dict(eamap_t, 'eamap', int, cinsnptrvec_t)
+_map_as_dict(eamap_t, 'eamap', long, cinsnptrvec_t)
#_map_as_dict(boundaries_t, 'boundaries', cinsn_t, areaset_t)
%}
diff --git a/swig/idaapi.i b/swig/idaapi.i
index 9de2edb..850cf36 100644
--- a/swig/idaapi.i
+++ b/swig/idaapi.i
@@ -17,10 +17,73 @@
#pragma SWIG nowarn=454 // Setting a pointer/reference variable may leak memory
%constant size_t SIZE_MAX = size_t(-1);
+%{
+
+#ifndef USE_DANGEROUS_FUNCTIONS
+ #define USE_DANGEROUS_FUNCTIONS 1
+#endif
+
+#include
+
+void raise_python_stl_bad_alloc(const std::bad_alloc &ba)
+{
+ Py_INCREF(PyExc_MemoryError);
+ PyErr_SetString(PyExc_MemoryError, "Out of memory (bad_alloc)");
+}
+
+void raise_python_unknown_exception()
+{
+ Py_INCREF(PyExc_RuntimeError);
+ PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
+}
+
+void raise_python_stl_exception(const std::exception &e)
+{
+ const char *what = e.what();
+ if ( what == NULL || what[0] == '\0' )
+ {
+ raise_python_unknown_exception();
+ }
+ else
+ {
+ Py_INCREF(PyExc_RuntimeError);
+ PyErr_SetString(PyExc_RuntimeError, what);
+ }
+}
+%}
+
+%define %exception_set_default_handlers()
+%exception {
+ try
+ {
+ $action
+ }
+ catch ( const std::bad_alloc &ba ) { raise_python_stl_bad_alloc(ba); SWIG_fail; }
+ catch ( const std::exception &e ) { raise_python_stl_exception(e); SWIG_fail; }
+ catch (...) { raise_python_unknown_exception(); SWIG_fail; }
+}
+%enddef
+%exception_set_default_handlers();
// Enable automatic docstring generation
%feature(autodoc,0);
+%{
+/* strnlen() arrived on OSX at v10.7. Provide it ourselves if needed. */
+#ifdef __MAC__
+#ifndef MAC_OS_X_VERSION_10_7
+#define MAC_OS_X_VERSION_10_7 1070
+#endif
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
+inline size_t strnlen(const char *s, size_t maxlen)
+{
+ const char *found = (const char *) memchr(s, 0, maxlen);
+ return found != NULL ? size_t(found - s) : maxlen;
+}
+#endif
+#endif
+%}
+
%define SWIG_DECLARE_PY_CLINKED_OBJECT(type)
%inline %{
static PyObject *type##_create()
@@ -59,10 +122,6 @@ static PyObject *type##_get_clink_ptr(PyObject *self)
%{
#include
-#ifndef USE_DANGEROUS_FUNCTIONS
- #define USE_DANGEROUS_FUNCTIONS 1
-#endif
-
#ifdef HAVE_SSIZE_T
#define _SSIZE_T_DEFINED 1
#endif
@@ -102,6 +161,7 @@ static PyObject *type##_get_clink_ptr(PyObject *self)
#include "strlist.hpp"
#include "struct.hpp"
#include "typeinf.hpp"
+#include "registry.hpp"
#include "ua.hpp"
#include "xref.hpp"
#include "ieee.h"
@@ -174,323 +234,6 @@ struct scfld_t
#define FT_BAD_TYPE -2
#define FT_OK 1
-// //-----------------------------------------------------------------------
-// class pycvt_t
-// {
-// struct attr_t
-// {
-// qstring str;
-// uint64 u64;
-// // User is responsible to release this attribute when done
-// PyObject *py_obj;
-// };
-
-// //-----------------------------------------------------------------------
-// static int get_attr(
-// PyObject *py_obj,
-// const char *attrname,
-// int ft,
-// attr_t &val)
-// {
-// ref_t py_attr(PyW_TryGetAttrString(py_obj, attrname));
-// if ( py_attr == NULL )
-// return FT_NOT_FOUND;
-
-// int cvt = FT_OK;
-// if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr.o) )
-// val.str = PyString_AsString(py_attr.o);
-// else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr.o, &val.u64) )
-// ; // nothing to be done
-// // A string array?
-// else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC )
-// && (PyList_CheckExact(py_attr.o) || PyW_IsSequenceType(py_attr.o)) )
-// {
-// // Return a reference to the attribute
-// val.py_obj = py_attr.o;
-// // Do not decrement the reference to this attribute
-// py_attr = NULL;
-// }
-// else
-// cvt = FT_BAD_TYPE;
-// return cvt;
-// }
-
-// //-----------------------------------------------------------------------
-// static int idaapi make_str_list_cb(
-// PyObject *py_item,
-// Py_ssize_t index,
-// void *ud)
-// {
-// if ( !PyString_Check(py_item) )
-// return CIP_FAILED;
-// char **a = (char **)ud;
-// a[index] = qstrdup(PyString_AsString(py_item));
-// return CIP_OK;
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts an IDC list of strings to a C string list
-// static Py_ssize_t str_list_to_str_arr(
-// PyObject *py_list,
-// char ***arr)
-// {
-// // Take the size
-// Py_ssize_t size = pyvar_walk_list(py_list);
-
-// // Allocate a buffer
-// char **a = (char **)qalloc((size + 1) * sizeof(char *));
-
-// // Walk and populate
-// size = pyvar_walk_list(py_list, make_str_list_cb, a);
-
-// // Make the list NULL terminated
-// a[size] = NULL;
-
-// // Return the list to the user
-// *arr = a;
-
-// // Return the size of items processed
-// return size;
-// }
-
-// //-----------------------------------------------------------------------
-// typedef qvector uint64vec_t;
-// static int idaapi make_int_list(
-// PyObject *py_item,
-// Py_ssize_t /*index*/,
-// void *ud)
-// {
-// uint64 val;
-// if ( !PyW_GetNumber(py_item, &val) )
-// return CIP_FAILED;
-// uint64vec_t *vec = (uint64vec_t *)ud;
-// vec->push_back(val);
-// return CIP_OK;
-// }
-
-// public:
-// //-----------------------------------------------------------------------
-// // Frees a NULL terminated list of fields
-// static void free_fields(
-// const scfld_t *fields,
-// void *store_area)
-// {
-// for ( int i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// break;
-
-// void *store = (void *)((char *)store_area + fd.field_offs);
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-// switch ( ft )
-// {
-// case FT_STR: // Simple string
-// {
-// char **s = (char **)store;
-// if ( *s != NULL )
-// {
-// qfree(*s);
-// *s = NULL;
-// }
-// }
-// break;
-
-// case FT_STRARR: // Array of strings
-// {
-// char ***op = (char ***)store, **p = *op;
-// while ( *p != NULL )
-// qfree((void *)*p++);
-// qfree(*op);
-// *op = NULL;
-// }
-// break;
-
-// case FT_NUM16ARR:
-// {
-// uint16 **arr = (uint16 **)store;
-// if ( *arr != NULL )
-// {
-// qfree(*arr);
-// *arr = NULL;
-// }
-// }
-// break;
-// }
-// }
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts from a C structure to Python
-// static int from_c(
-// const scfld_t *fields,
-// void *read_area,
-// PyObject *py_obj)
-// {
-// PyObject *py_attr;
-// int i;
-// bool ok = false;
-// for ( i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// {
-// ok = true;
-// break;
-// }
-
-// // Point to structure member
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-// void *read = (void *)((char *)read_area + fd.field_offs);
-// // Create the python attribute properly
-// if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM )
-// {
-// if ( ft == FT_NUM16 )
-// py_attr = Py_BuildValue("H", *(uint16 *)read);
-// else if ( ft == FT_NUM32 )
-// py_attr = Py_BuildValue("I", *(uint32 *)read);
-// else if ( ft == FT_INT )
-// py_attr = Py_BuildValue("i", *(int *)read);
-// else if ( ft == FT_SIZET )
-// py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read);
-// else if ( ft == FT_SSIZET )
-// py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read);
-// }
-// else if ( ft == FT_STR || ft == FT_CHAR )
-// {
-// if ( ft == FT_STR )
-// py_attr = PyString_FromString(*(char **)read);
-// else
-// py_attr = Py_BuildValue("c", *(char *)read);
-// }
-// else if ( ft == FT_STRARR )
-// {
-// char **arr = *(char ***)read;
-// py_attr = PyList_New(0);
-// while ( *arr != NULL )
-// PyList_Append(py_attr, PyString_FromString(*arr++));
-// }
-// else
-// continue;
-// PyObject_SetAttrString(py_obj, fd.field_name, py_attr);
-// Py_XDECREF(py_attr);
-// }
-// return ok ? -1 : i;
-// }
-
-// //-----------------------------------------------------------------------
-// // Converts fields from IDC and field description into a C structure
-// // If 'use_extlang' is specified, then the passed idc_obj is considered
-// // to be an opaque object and thus can be queried only through extlang
-// static int from_script(
-// const scfld_t *fields,
-// void *store_area,
-// PyObject *py_obj)
-// {
-// int i;
-// bool ok = false;
-// attr_t attr;
-// for ( i=0; ; i++ )
-// {
-// // End of list?
-// const scfld_t &fd = fields[i];
-// if ( fd.field_name == NULL )
-// {
-// ok = true;
-// break;
-// }
-
-// // Get field type
-// int ft = fd.field_type & ~FT_VALUE_MASK;
-
-// // Point to structure member
-// void *store = (void *)((char *)store_area + fd.field_offs);
-
-// // Retrieve attribute and type
-// int cvt = get_attr(py_obj, fd.field_name, ft, attr);
-
-// // Attribute not found?
-// if ( cvt == FT_NOT_FOUND )
-// {
-// // Skip optional fields
-// if ( fd.is_optional )
-// continue;
-// break;
-// }
-
-// if ( ft == FT_STR )
-// *(char **)store = qstrdup(attr.str.c_str());
-// else if ( ft == FT_NUM32 )
-// *(uint32 *)store = uint32(attr.u64);
-// else if ( ft == FT_NUM16 )
-// *(uint16 *)store = attr.u64 & 0xffff;
-// else if ( ft == FT_INT )
-// *(int *)store = int(attr.u64);
-// else if ( ft == FT_SIZET )
-// *(size_t *)store = size_t(attr.u64);
-// else if ( ft == FT_SSIZET )
-// *(ssize_t *)store = ssize_t(attr.u64);
-// else if ( ft == FT_CHAR )
-// *(char *)store = *attr.str.c_str();
-// else if ( ft == FT_STRARR )
-// {
-// str_list_to_str_arr(attr.py_obj, (char ***)store);
-// Py_DECREF(attr.py_obj);
-// }
-// else if ( ft == FT_CHRARR_STATIC )
-// {
-// size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16;
-// if ( sz == 0 )
-// break;
-// uint64vec_t w;
-// char *a = (char *) store;
-// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) )
-// {
-// sz = qmin(w.size(), sz);
-// for ( size_t i=0; i < sz; i++ )
-// a[i] = w[i] & 0xFF;
-// }
-// }
-// else if ( ft == FT_NUM16ARR )
-// {
-// uint64vec_t w;
-// if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 )
-// {
-// size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16;
-// bool zero_term;
-// if ( max_sz == 0 )
-// {
-// zero_term = true;
-// max_sz = w.size();
-// }
-// else
-// {
-// zero_term = false;
-// max_sz = qmin(max_sz, w.size());
-// }
-// // Allocate as much as we parsed elements
-// // Add one more element if list was zero terminated
-// uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ;
-// for ( size_t i=0; i < max_sz; i++ )
-// a[i] = w[i] & 0xFF;
-
-// if ( zero_term )
-// a[max_sz] = 0;
-// *(uint16 **)store = a;
-// }
-// }
-// else
-// {
-// // Unsupported field type!
-// break;
-// }
-// }
-// return ok ? -1 : i;
-// }
-// };
-
//-------------------------------------------------------------------------
Py_ssize_t pyvar_walk_list(
const ref_t &py_list,
@@ -1165,9 +908,6 @@ bool pyw_convert_idc_args(
{
// PyTuple_SetItem() steals the reference.
py_obj.incref();
- if ( cvt == CIP_OK_OPAQUE )
- // We want opaque objects to still exist even when the tuple is gone.
- py_obj.incref();
QASSERT(30412, PyTuple_SetItem(py_tuple.o, i, py_obj.o) == 0);
}
else
@@ -1254,6 +994,7 @@ static const char S_ON_VIEW_DBLCLICK[] = "OnViewDblclick";
static const char S_ON_VIEW_CURPOS[] = "OnViewCurpos";
static const char S_ON_VIEW_SWITCHED[] = "OnViewSwitched";
static const char S_ON_VIEW_MOUSE_OVER[] = "OnViewMouseOver";
+static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved";
#ifdef __PYWRAPS__
@@ -2693,7 +2434,7 @@ class __IDAPython_Completion_Util(object):
return s
-# Instantiate a completion object
+# Instantiate an IDAPython command completion object (for use with IDA's CLI bar)
IDAPython_Completion = __IDAPython_Completion_Util()
def _listify_types(*classes):
@@ -2775,6 +2516,17 @@ NW_REMOVE = 0x0010
SWIG_DECLARE_PY_CLINKED_OBJECT(qstrvec_t)
+%{
+PyObject *qstrvec2pylist(qstrvec_t &vec)
+{
+ size_t n = vec.size();
+ PyObject *py_list = PyList_New(n);
+ for ( size_t i=0; i < n; ++i )
+ PyList_SetItem(py_list, i, PyString_FromString(vec[i].c_str()));
+ return py_list;
+}
+%}
+
%inline %{
//
@@ -2797,12 +2549,7 @@ static PyObject *py_parse_command_line(const char *cmdline)
qstrvec_t args;
if ( parse_command_line3(cmdline, &args, NULL, LP_PATH_WITH_ARGS) == 0 )
Py_RETURN_NONE;
-
- PyObject *py_list = PyList_New(args.size());
- for ( size_t i=0; i
@@ -1011,6 +1022,12 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va)
break;
}
+ case processor_t::auto_empty_finally:
+ {
+ proxy->auto_empty_finally();
+ break;
+ }
+
case processor_t::rename:
{
ea_t ea = va_arg(va, ea_t);
@@ -1060,6 +1077,19 @@ int idaapi IDP_Callback(void *ud, int notification_code, va_list va)
break;
}
+ case processor_t::auto_empty:
+ {
+ proxy->auto_empty();
+ break;
+ }
+
+ case processor_t::auto_queue_empty:
+ {
+ atype_t type = va_arg(va, atype_t);
+ ret = proxy->auto_queue_empty(type);
+ break;
+ }
+
case processor_t::add_func:
{
func_t *func = va_arg(va, func_t *);
diff --git a/swig/kernwin.i b/swig/kernwin.i
index 0ad12a4..b23bc9d 100644
--- a/swig/kernwin.i
+++ b/swig/kernwin.i
@@ -1,6 +1,8 @@
// Ignore the va_list functions
%ignore AskUsingForm_cv;
%ignore AskUsingForm_c;
+%ignore OpenForm_cv;
+%ignore OpenForm_c;
%ignore close_form;
%ignore vaskstr;
%ignore strvec_t;
@@ -21,11 +23,8 @@
%ignore msg;
%rename (msg) py_msg;
-%ignore warning;
-%rename (warning) py_warning;
-
-%ignore error;
-%rename (error) py_error;
+%ignore umsg;
+%rename (umsg) py_umsg;
%ignore textctrl_info_t;
%ignore vinfo;
@@ -55,10 +54,14 @@
%ignore trim;
%ignore skipSpaces;
%ignore stristr;
+%ignore set_nav_colorizer;
+%rename (set_nav_colorizer) py_set_nav_colorizer;
+%rename (call_nav_colorizer) py_call_nav_colorizer;
%ignore get_highlighted_identifier;
%rename (get_highlighted_identifier) py_get_highlighted_identifier;
+
// CLI
%ignore cli_t;
%ignore install_command_interpreter;
@@ -66,6 +69,15 @@
%ignore remove_command_interpreter;
%rename (remove_command_interpreter) py_remove_command_interpreter;
+%ignore action_desc_t::handler;
+%ignore action_handler_t;
+%ignore register_action;
+%rename (register_action) py_register_action;
+%ignore unregister_action;
+%rename (unregister_action) py_unregister_action;
+%ignore attach_dynamic_action_to_popup;
+%rename (attach_dynamic_action_to_popup) py_attach_dynamic_action_to_popup;
+
%include "typemaps.i"
%rename (asktext) py_asktext;
@@ -93,31 +105,72 @@
%rename (_askaddr) askaddr;
%rename (_askseg) askseg;
+%ignore qvector::operator==;
+%ignore qvector::operator!=;
+%ignore qvector::find;
+%ignore qvector::has;
+%ignore qvector::del;
+%ignore qvector::add_unique;
+
+%ignore gen_disasm_text;
+%rename (gen_disasm_text) py_gen_disasm_text;
+
%feature("director") UI_Hooks;
+
+//-------------------------------------------------------------------------
+%{
+struct py_action_handler_t : public action_handler_t
+{
+ py_action_handler_t(); // No.
+ py_action_handler_t(PyObject *_o)
+ : pyah(borref_t(_o)), has_activate(false), has_update(false)
+ {
+ ref_t act(PyW_TryGetAttrString(pyah.o, "activate"));
+ if ( act != NULL && PyCallable_Check(act.o) > 0 )
+ has_activate = true;
+
+ ref_t upd(PyW_TryGetAttrString(pyah.o, "update"));
+ if ( upd != NULL && PyCallable_Check(upd.o) > 0 )
+ has_update = true;
+ }
+ virtual idaapi ~py_action_handler_t()
+ {
+ PYW_GIL_GET;
+ // NOTE: We need to do the decref _within_ the PYW_GIL_GET scope,
+ // and not leave it to the destructor to clean it up, because when
+ // ~ref_t() gets called, the GIL will have already been released.
+ pyah = ref_t();
+ }
+ virtual int idaapi activate(action_activation_ctx_t *ctx)
+ {
+ if ( !has_activate )
+ return 0;
+ PYW_GIL_GET_AND_REPORT_ERROR;
+ newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_activation_ctx_t, 0));
+ newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"activate", (char *) "O", pyctx.o));
+ return PyErr_Occurred() ? 0 : ((pyres != NULL && PyInt_Check(pyres.o)) ? PyInt_AsLong(pyres.o) : 0);
+ }
+ virtual action_state_t idaapi update(action_update_ctx_t *ctx)
+ {
+ if ( !has_update )
+ return AST_DISABLE;
+ PYW_GIL_GET_AND_REPORT_ERROR;
+ newref_t pyctx(SWIG_NewPointerObj(SWIG_as_voidptr(ctx), SWIGTYPE_p_action_update_ctx_t, 0));
+ newref_t pyres(PyObject_CallMethod(pyah.o, (char *)"update", (char *) "O", pyctx.o));
+ return PyErr_Occurred() ? AST_DISABLE_ALWAYS : ((pyres != NULL && PyInt_Check(pyres.o)) ? action_state_t(PyInt_AsLong(pyres.o)) : AST_DISABLE);
+ }
+private:
+ ref_t pyah;
+ bool has_activate;
+ bool has_update;
+};
+
+typedef std::map py_action_handlers_t;
+static py_action_handlers_t py_action_handlers;
+
+%}
+
%inline %{
-int py_msg(const char *format)
-{
- int rc;
- Py_BEGIN_ALLOW_THREADS;
- rc = msg("%s", format);
- Py_END_ALLOW_THREADS;
- return rc;
-}
-
-void py_warning(const char *format)
-{
- Py_BEGIN_ALLOW_THREADS;
- warning("%s", format);
- Py_END_ALLOW_THREADS;
-}
-
-void py_error(const char *format)
-{
- Py_BEGIN_ALLOW_THREADS;
- error("%s", format);
- Py_END_ALLOW_THREADS;
-}
-
void refresh_lists(void)
{
Py_BEGIN_ALLOW_THREADS;
@@ -348,6 +401,72 @@ def readsel2(view, p0, p1):
#
*/
+//------------------------------------------------------------------------
+/*
+#
+def umsg(text):
+ """
+ Prints text into IDA's Output window
+
+ @param text: text to print
+ Can be Unicode, or string in UTF-8 encoding
+ @return: number of bytes printed
+ """
+ pass
+#
+*/
+static PyObject* py_umsg(PyObject *o)
+{
+ PyObject* utf8 = NULL;
+ if ( PyUnicode_Check(o) )
+ {
+ utf8 = PyUnicode_AsUTF8String(o);
+ o = utf8;
+ }
+ else if ( !PyString_Check(o) )
+ {
+ PyErr_SetString(PyExc_TypeError, "A unicode or UTF-8 string expected");
+ return NULL;
+ }
+ int rc;
+ Py_BEGIN_ALLOW_THREADS;
+ rc = umsg("%s", PyString_AsString(o));
+ Py_END_ALLOW_THREADS;
+ Py_XDECREF(utf8);
+ return PyInt_FromLong(rc);
+}
+
+//------------------------------------------------------------------------
+/*
+#
+def msg(text):
+ """
+ Prints text into IDA's Output window
+
+ @param text: text to print
+ Can be Unicode, or string in local encoding
+ @return: number of bytes printed
+ """
+ pass
+#
+*/
+static PyObject* py_msg(PyObject *o)
+{
+ if ( PyUnicode_Check(o) )
+ return py_umsg(o);
+
+ if ( !PyString_Check(o) )
+ {
+ PyErr_SetString(PyExc_TypeError, "A string expected");
+ return NULL;
+ }
+ int rc;
+ Py_BEGIN_ALLOW_THREADS;
+ rc = msg("%s", PyString_AsString(o));
+ Py_END_ALLOW_THREADS;
+ return PyInt_FromLong(rc);
+}
+
//------------------------------------------------------------------------
/*
#
@@ -429,18 +548,17 @@ PyObject *py_str2user(const char *str)
//------------------------------------------------------------------------
/*
#
-def process_ui_action(name, flags):
+def process_ui_action(name):
"""
Invokes an IDA UI action by name
@param name: action name
- @param flags: Reserved. Must be zero
@return: Boolean
"""
pass
#
*/
-static bool py_process_ui_action(const char *name, int flags)
+static bool py_process_ui_action(const char *name, int flags = 0)
{
return process_ui_action(name, flags, NULL);
}
@@ -449,12 +567,7 @@ static bool py_process_ui_action(const char *name, int flags)
/*
#
def del_menu_item(menu_ctx):
- """
- Deletes a menu item previously added with add_menu_item()
-
- @param menu_ctx: value returned by add_menu_item()
- @return: Boolean
- """
+ """Deprecated. Use detach_menu_item()/unregister_action() instead."""
pass
#
*/
@@ -467,7 +580,6 @@ static bool py_del_menu_item(PyObject *py_ctx)
py_add_del_menu_item_ctx *ctx = (py_add_del_menu_item_ctx *)PyCObject_AsVoidPtr(py_ctx);
bool ok = del_menu_item(ctx->menupath.c_str());
-
if ( ok )
{
Py_DECREF(ctx->cb_data);
@@ -585,21 +697,7 @@ PyObject *py_add_hotkey(const char *hotkey, PyObject *pyfunc)
/*
#
def add_menu_item(menupath, name, hotkey, flags, callback, args):
- """
- Adds a menu item
-
- @param menupath: path to the menu item after or before which the insertion will take place
- @param name: name of the menu item (~x~ is used to denote Alt-x hot letter)
- @param hotkey: hotkey for the menu item (may be empty)
- @param flags: one of SETMENU_... consts
- @param callback: function which gets called when the user selects the menu item.
- The function callback is of the form:
- def callback(*args):
- pass
- @param args: tuple containing the arguments
-
- @return: None or a menu context (to be used by del_menu_item())
- """
+ """Deprecated. Use register_action()/attach_menu_item() instead."""
pass
#
*/
@@ -946,6 +1044,45 @@ class UI_Hooks(object):
"""
pass
+ def updating_actions(self, ctx):
+ """
+ The UI is about to batch-update some actions.
+
+ @param ctx: The action_update_ctx_t instance
+ @return: Ignored
+ """
+ pass
+
+ def updated_actions(self):
+ """
+ The UI is done updating actions.
+
+ @return: Ignored
+ """
+ pass
+
+ def populating_tform_popup(self, form, popup):
+ """
+ The UI is populating the TForm's popup menu.
+ Now is a good time to call idaapi.attach_action_to_popup()
+
+ @param form: The form
+ @param popup: The popup menu.
+ @return: Ignored
+ """
+ pass
+
+ def finish_populating_tform_popup(self, form, popup):
+ """
+ The UI is about to be done populating the TForm's popup menu.
+ Now is a good time to call idaapi.attach_action_to_popup()
+
+ @param form: The form
+ @param popup: The popup menu.
+ @return: Ignored
+ """
+ pass
+
def term(self):
"""
IDA is terminated and the database is already closed.
@@ -1003,8 +1140,243 @@ public:
PYW_GIL_CHECK_LOCKED_SCOPE();
Py_RETURN_NONE;
};
+
+ virtual void current_tform_changed(TForm * /*form*/, TForm * /*previous_form*/)
+ {
+ }
+
+ virtual void updating_actions(action_update_ctx_t *ctx)
+ {
+ }
+
+ virtual void updated_actions()
+ {
+ }
+
+ virtual void populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/)
+ {
+ }
+
+ virtual void finish_populating_tform_popup(TForm * /*form*/, TPopupMenu * /*popup*/)
+ {
+ }
};
+//-------------------------------------------------------------------------
+bool py_register_action(action_desc_t *desc)
+{
+ bool ok = register_action(*desc);
+ if ( ok )
+ {
+ // Success. We are managing this handler from now on,
+ // and must prevent it from being destroyed.
+ py_action_handlers[desc->name] = desc->handler;
+ // Let's set this to NULL, so when the wrapping Python action_desc_t
+ // instance is deleted, it doesn't try to delete the handler (See
+ // kernwin.i's action_desc_t::~action_desc_t()).
+ desc->handler = NULL;
+ }
+ return ok;
+}
+
+//-------------------------------------------------------------------------
+bool py_unregister_action(const char *name)
+{
+ bool ok = unregister_action(name);
+ if ( ok )
+ {
+ py_action_handler_t *handler =
+ (py_action_handler_t *) py_action_handlers[name];
+ delete handler;
+ py_action_handlers.erase(name);
+ }
+ return ok;
+}
+
+//-------------------------------------------------------------------------
+bool py_attach_dynamic_action_to_popup(
+ TForm *form,
+ TPopupMenu *popup_handle,
+ action_desc_t *desc,
+ const char *popuppath = NULL,
+ int flags = 0)
+{
+ bool ok = attach_dynamic_action_to_popup(
+ form, popup_handle, *desc, popuppath, flags);
+ if ( ok )
+ // Set the handler to null, so the desc won't destroy
+ // it, as it noticed ownership was taken by IDA.
+ // In addition, we don't need to register into the
+ // 'py_action_handlers', because IDA will destroy the
+ // handler as soon as the popup menu is closed.
+ desc->handler = NULL;
+ return ok;
+}
+
+// This is similar to a twinline_t, with improved memory management:
+// twinline_t has a dummy destructor, that performs no cleanup.
+struct disasm_line_t
+{
+ disasm_line_t() : at(NULL) {}
+ ~disasm_line_t() { qfree(at); }
+ disasm_line_t(const disasm_line_t &other) { *this = other; }
+ disasm_line_t &operator=(const disasm_line_t &other)
+ {
+ qfree(at);
+ at = other.at == NULL ? NULL : other.at->clone();
+ return *this;
+ }
+ place_t *at;
+ qstring line;
+ color_t prefix_color;
+ bgcolor_t bg_color;
+ bool is_default;
+};
+DECLARE_TYPE_AS_MOVABLE(disasm_line_t);
+typedef qvector disasm_text_t;
+
+//-------------------------------------------------------------------------
+void py_gen_disasm_text(ea_t ea1, ea_t ea2, disasm_text_t &text, bool truncate_lines)
+{
+ text_t _text;
+ gen_disasm_text(ea1, ea2, _text, truncate_lines);
+ for ( size_t i = 0, n = _text.size(); i < n; ++i )
+ {
+ const twinline_t &tl = _text[i];
+ disasm_line_t &dl = text.push_back();
+ dl.at = tl.at; // Transfer ownership
+ dl.line.inject(tl.line); // Transfer ownership
+ }
+}
+
+//-------------------------------------------------------------------------
+// Although 'TCustomControl*' and 'TForm*' instances can both be used
+// for attach_action_to_popup() at a binary-level, IDAPython SWIG bindings
+// require that a 'TForm *' wrapper be passed to wrap_attach_action_to_popup().
+// Thus, we provide another attach_action_to_popup() version, that
+// accepts a 'TCustomControl' as first argument.
+//
+// Since user-created GraphViewer are created like so:
+// +-------- PluginForm ----------+
+// |+----- TCustomControl -------+|
+// || ||
+// || ||
+// || ||
+// || ||
+// || ||
+// |+----------------------------+|
+// +------------------------------+
+// The user cannot use GetTForm(), and use that to attach
+// an action to, because that'll attach the action to the PluginForm
+// instance.
+// Instead, the user must use GetTCustomControl(), and call
+// this function below with it.
+bool attach_action_to_popup(
+ TCustomControl *tcc,
+ TPopupMenu *popup_handle,
+ const char *name,
+ const char *popuppath = NULL,
+ int flags = 0)
+{
+ return attach_action_to_popup((TForm*) tcc, popup_handle, name, popuppath, flags);
+}
+
+//-------------------------------------------------------------------------
+/*
+#
+def set_nav_colorizer(callback):
+ """
+ Set a new colorizer for the navigation band.
+
+ The 'callback' is a function of 2 arguments:
+ - ea (the EA to colorize for)
+ - nbytes (the number of bytes at that EA)
+ and must return a 'long' value.
+
+ The previous colorizer is returned, allowing
+ the new 'callback' to use 'call_nav_colorizer'
+ with it.
+
+ Note that the previous colorizer is returned
+ only the first time set_nav_colorizer() is called:
+ due to the way the colorizers API is defined in C,
+ it is impossible to chain more than 2 colorizers
+ in IDAPython: the original, IDA-provided colorizer,
+ and a user-provided one.
+
+ Example: colorizer inverting the color provided by the IDA colorizer:
+ def my_colorizer(ea, nbytes):
+ global ida_colorizer
+ orig = idaapi.call_nav_colorizer(ida_colorizer, ea, nbytes)
+ return long(~orig)
+
+ ida_colorizer = idaapi.set_nav_colorizer(my_colorizer)
+ """
+ pass
+#
+*/
+nav_colorizer_t *py_set_nav_colorizer(PyObject *new_py_colorizer)
+{
+ static ref_t py_colorizer;
+ struct ida_local lambda_t
+ {
+ static uint32 idaapi call_py_colorizer(ea_t ea, asize_t nbytes)
+ {
+ PYW_GIL_GET;
+
+ if ( py_colorizer == NULL ) // Shouldn't happen.
+ return 0;
+ newref_t pyres = PyObject_CallFunction(
+ py_colorizer.o, "KK",
+ (unsigned long long) ea,
+ (unsigned long long) nbytes);
+ PyW_ShowCbErr("nav_colorizer");
+ if ( pyres.o == NULL )
+ return 0;
+ if ( !PyLong_Check(pyres.o) )
+ {
+ static bool warned = false;
+ if ( !warned )
+ {
+ msg("WARNING: set_nav_colorizer() callback must return a 'long'.\n");
+ warned = true;
+ }
+ return 0;
+ }
+ return PyLong_AsLong(pyres.o);
+ }
+ };
+
+ bool need_install = py_colorizer == NULL;
+ py_colorizer = borref_t(new_py_colorizer);
+ return need_install
+ ? set_nav_colorizer(lambda_t::call_py_colorizer)
+ : NULL;
+}
+
+//-------------------------------------------------------------------------
+/*
+#
+def call_nav_colorizer(colorizer, ea, nbytes):
+ """
+ To be used with the IDA-provided colorizer, that is
+ returned as result of the first call to set_nav_colorizer().
+
+ This is a trivial trampoline, so that SWIG can generate a
+ wrapper that will do the types checking.
+ """
+ pass
+#
+*/
+uint32 py_call_nav_colorizer(
+ nav_colorizer_t *col,
+ ea_t ea,
+ asize_t nbytes)
+{
+ return col(ea, nbytes);
+}
+
+
//---------------------------------------------------------------------------
uint32 idaapi choose_sizer(void *self)
@@ -1208,7 +1580,7 @@ static void formchgcbfa_refresh_field(size_t p_fa, int fid)
}
//---------------------------------------------------------------------------
-static void formchgcbfa_close(size_t p_fa, int fid, int close_normally)
+static void formchgcbfa_close(size_t p_fa, int close_normally)
{
DECLARE_FORM_ACTIONS;
fa->close(close_normally);
@@ -1444,6 +1816,12 @@ static size_t py_get_AskUsingForm()
return (size_t)AskUsingForm_c;
}
+static size_t py_get_OpenForm()
+{
+ // See comments above.
+ return (size_t)OpenForm_c;
+}
+
//
%}
@@ -1503,6 +1881,44 @@ int idaapi UI_Callback(void *ud, int notification_code, va_list va)
}
break;
}
+
+ case ui_current_tform_changed:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TForm *prev_form = va_arg(va, TForm *);
+ proxy->current_tform_changed(form, prev_form);
+ }
+ break;
+
+ case ui_updating_actions:
+ {
+ action_update_ctx_t *ctx = va_arg(va, action_update_ctx_t *);
+ proxy->updating_actions(ctx);
+ }
+ break;
+
+
+ case ui_updated_actions:
+ {
+ proxy->updated_actions();
+ }
+ break;
+
+ case ui_populating_tform_popup:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TPopupMenu *popup = va_arg(va, TPopupMenu *);
+ proxy->populating_tform_popup(form, popup);
+ }
+ break;
+
+ case ui_finish_populating_tform_popup:
+ {
+ TForm *form = va_arg(va, TForm *);
+ TPopupMenu *popup = va_arg(va, TPopupMenu *);
+ proxy->finish_populating_tform_popup(form, popup);
+ }
+ break;
}
}
catch (Swig::DirectorException &e)
@@ -1912,6 +2328,56 @@ private:
}
}
+ bool split_chooser_caption(qstring *out_title, qstring *out_caption, const char *caption) const
+ {
+ if ( get_embedded() != NULL )
+ {
+ // For embedded chooser, the "caption" will be overloaded to encode
+ // the AskUsingForm's title, caption and embedded chooser id
+ // Title:EmbeddedChooserID:Caption
+
+ char title_buf[MAXSTR];
+ const char *ptitle;
+
+ static const char delimiter[] = ":";
+ char temp[MAXSTR];
+ qstrncpy(temp, caption, sizeof(temp));
+
+ char *ctx;
+ char *p = qstrtok(temp, delimiter, &ctx);
+ if ( p == NULL )
+ return false;
+
+ // Copy the title
+ char title_str[MAXSTR];
+ qstrncpy(title_str, p, sizeof(title_str));
+
+ // Copy the echooser ID
+ p = qstrtok(NULL, delimiter, &ctx);
+ if ( p == NULL )
+ return false;
+
+ char id_str[10];
+ qstrncpy(id_str, p, sizeof(id_str));
+
+ // Form the new title of the form: "AskUsingFormTitle:EchooserId"
+ qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str);
+
+ // Adjust the title
+ *out_title = title_buf;
+
+ // Adjust the caption
+ p = qstrtok(NULL, delimiter, &ctx);
+ *out_caption = caption + (p - temp);
+ }
+ else
+ {
+ *out_title = title;
+ *out_caption = caption;
+ }
+ return true;
+ }
+
public:
//------------------------------------------------------------------------
// Public methods
@@ -1957,68 +2423,24 @@ public:
}
int add_command(
- const char *caption,
- int flags=0,
- int menu_index=-1,
- int icon=-1)
+ const char *_caption,
+ int flags=0,
+ int menu_index=-1,
+ int icon=-1)
{
if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS )
return -1;
- // For embedded chooser, the "caption" will be overloaded to encode
- // the AskUsingForm's title, caption and embedded chooser id
- // Title:EmbeddedChooserID:Caption
- char title_buf[MAXSTR];
- const char *ptitle;
-
- // Embedded chooser?
- if ( get_embedded() != NULL )
- {
- static const char delimiter[] = ":";
- char temp[MAXSTR];
- qstrncpy(temp, caption, sizeof(temp));
-
- char *p = strtok(temp, delimiter);
- if ( p == NULL )
- return -1;
-
- // Copy the title
- char title_str[MAXSTR];
- qstrncpy(title_str, p, sizeof(title_str));
-
- // Copy the echooser ID
- p = strtok(NULL, delimiter);
- if ( p == NULL )
- return -1;
-
- char id_str[10];
- qstrncpy(id_str, p, sizeof(id_str));
-
- // Form the new title of the form: "AskUsingFormTitle:EchooserId"
- qsnprintf(title_buf, sizeof(title_buf), "%s:%s", title_str, id_str);
-
- // Adjust the title
- ptitle = title_buf;
-
- // Adjust the caption
- p = strtok(NULL, delimiter);
- caption += (p - temp);
- }
- else
- {
- ptitle = title.c_str();
- }
-
- if ( !add_chooser_command(
- ptitle,
- caption,
- menu_cbs[menu_cb_idx],
- menu_index,
- icon,
- flags))
- {
+ qstring title, caption;
+ if ( !split_chooser_caption(&title, &caption, _caption)
+ || !add_chooser_command(
+ title.c_str(),
+ caption.c_str(),
+ menu_cbs[menu_cb_idx],
+ menu_index,
+ icon,
+ flags) )
return -1;
- }
return menu_cb_idx++;
}
@@ -2399,17 +2821,14 @@ PyObject *choose2_get_embedded(PyObject *self)
//------------------------------------------------------------------------
int choose2_add_command(
- PyObject *self,
- const char *caption,
- int flags=0,
- int menu_index=-1,
- int icon=-1)
+ PyObject *self,
+ const char *caption,
+ int flags=0,
+ int menu_index=-1,
+ int icon=-1)
{
py_choose2_t *c2 = choose2_find_instance(self);
- if ( c2 != NULL )
- return c2->add_command(caption, flags, menu_index, icon);
- else
- return -2;
+ return c2 == NULL ? -2 : c2->add_command(caption, flags, menu_index, icon);
}
//------------------------------------------------------------------------
@@ -2993,6 +3412,9 @@ public:
};
//---------------------------------------------------------------------------
+// FIXME: This should inherit py_view_base.hpp's py_customidamemo_t,
+// just like py_graph.hpp's py_graph_t does.
+// There should be a way to "merge" the two mechanisms; they are similar.
class customviewer_t
{
protected:
@@ -3025,13 +3447,6 @@ private:
qstring _curline;
intvec_t _installed_popups;
- static bool idaapi s_popup_cb(void *ud)
- {
- PYW_GIL_GET;
- customviewer_t *_this = (customviewer_t *)ud;
- return _this->on_popup();
- }
-
static bool idaapi s_popup_menu_cb(void *ud)
{
size_t mid = (size_t)ud;
@@ -3130,6 +3545,10 @@ private:
}
public:
+
+ inline TForm *get_tform() { return _form; }
+ inline TCustomControl *get_tcustom_control() { return _cv; }
+
//
// All the overridable callbacks
//
@@ -3980,11 +4399,67 @@ bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl)
DECL_THIS;
return _this == NULL ? false : _this->edit_line(nline, py_sl);
}
+
+//-------------------------------------------------------------------------
+TForm *pyscv_get_tform(PyObject *py_this)
+{
+ DECL_THIS;
+ return _this == NULL ? NULL : _this->get_tform();
+}
+
+//-------------------------------------------------------------------------
+TCustomControl *pyscv_get_tcustom_control(PyObject *py_this)
+{
+ DECL_THIS;
+ return _this == NULL ? NULL : _this->get_tcustom_control();
+}
+
+
#undef DECL_THIS
//
%}
%include "kernwin.hpp"
+
+%template(disasm_text_t) qvector;
+
+%extend action_desc_t {
+ action_desc_t(
+ const char *name,
+ const char *label,
+ PyObject *handler,
+ const char *shortcut = NULL,
+ const char *tooltip = NULL,
+ int icon = -1)
+ {
+ action_desc_t *ad = new action_desc_t();
+#define DUPSTR(Prop) ad->Prop = Prop == NULL ? NULL : qstrdup(Prop)
+ DUPSTR(name);
+ DUPSTR(label);
+ DUPSTR(shortcut);
+ DUPSTR(tooltip);
+#undef DUPSTR
+ ad->icon = icon;
+ ad->handler = new py_action_handler_t(handler);
+ ad->owner = &PLUGIN;
+ return ad;
+ }
+
+ ~action_desc_t()
+ {
+ if ( $self->handler != NULL ) // Ownership not taken?
+ delete $self->handler;
+#define FREESTR(Prop) qfree((char *) $self->Prop)
+ FREESTR(name);
+ FREESTR(label);
+ FREESTR(shortcut);
+ FREESTR(tooltip);
+#undef FREESTR
+ delete $self;
+ }
+}
+
+//-------------------------------------------------------------------------
uint32 choose_choose(PyObject *self,
int flags,
int x0,int y0,
@@ -4040,7 +4515,7 @@ DP_INSIDE = 0x0010
# this flag alone cannot be used to determine orientation
DP_BEFORE = 0x0020
# used with combination of other flags
-DP_RAW = 0x0040
+DP_TAB = 0x0040
DP_FLOATING = 0x0080
# ----------------------------------------------------------------------
@@ -4091,6 +4566,17 @@ def askseg(defval, format):
else:
return None
+# ----------------------------------------------------------------------
+class action_handler_t:
+ def __init__(self):
+ pass
+
+ def activate(self, ctx):
+ return 0
+
+ def update(self, ctx):
+ pass
+
class Choose2(object):
@@ -4111,6 +4597,8 @@ class Choose2(object):
CH_ATTRS = 0x10
CH_NOIDB = 0x20
"""use the chooser even without an open database, same as x0=-2"""
+ CH_UTF8 = 0x40
+ """string encoding is utf-8"""
CH_BUILTIN_MASK = 0xF80000
@@ -4219,12 +4707,11 @@ class Choose2(object):
icon = -1,
emb=None):
"""
- Adds a new chooser command
- Save the returned value and later use it in the OnCommand handler
-
- @return: Returns a negative value on failure or the command index
+ Deprecated: Use
+ - register_action()
+ - attach_action_to_menu()
+ - attach_action_to_popup()
"""
-
# Use the 'emb' as a sentinel. It will be passed the correct value from the EmbeddedChooserControl
if self.embedded and ((emb is None) or (emb != 2002)):
raise RuntimeError("Please add a command through EmbeddedChooserControl.AddCommand()")
@@ -5154,7 +5641,7 @@ class Form(object):
def __init__(self, form, controls):
"""
Contruct a Form class.
- This class wraps around AskUsingForm() and provides an easier / alternative syntax for describing forms.
+ This class wraps around AskUsingForm() or OpenForm() and provides an easier / alternative syntax for describing forms.
The form control names are wrapped inside the opening and closing curly braces and the control themselves are
defined and instantiated via various form controls (subclasses of Form).
@@ -5171,6 +5658,15 @@ class Form(object):
self.title = None
"""The Form title. It will be filled when the form is compiled"""
+ self.modal = True
+ """By default, forms are modal"""
+
+ self.openform_flags = 0
+ """
+ If non-modal, these flags will be passed to OpenForm.
+ This is an OR'ed combination of the PluginForm.FORM_* values.
+ """
+
def Free(self):
"""
@@ -5302,6 +5798,11 @@ class Form(object):
"""
# First argument is the form string
args = [None]
+
+ # Second argument, if form is not modal, is the set of flags
+ if not self.modal:
+ args.append(self.openform_flags | 0x80) # Add FORM_QWIDGET
+
ctrlcnt = 1
# Reset all group control internal flags
@@ -5384,6 +5885,22 @@ class Form(object):
ctrlcnt += 1
+ # If no FormChangeCb instance was passed, and thus there's no '%/'
+ # in the resulting form string, let's provide a minimal one, so that
+ # we will retrieve 'p_fa', and thus actions that rely on it will work.
+ if form.find(Form.FT_FORMCHG) < 0:
+ form = form + Form.FT_FORMCHG
+ fccb = Form.FormChangeCb(lambda *args: 1)
+ self.Add("___dummyfchgcb", fccb)
+ # Regardless of the actual position of '%/' in the form
+ # string, a formchange callback _must_ be right after
+ # the form string.
+ if self.modal:
+ inspos = 1
+ else:
+ inspos = 2
+ args.insert(inspos, fccb.get_arg())
+
# Patch in the final form string
args[0] = form
@@ -5420,18 +5937,34 @@ class Form(object):
return self.__args is not None
- def Execute(self):
- """
- Displays a compiled form.
- @return: 1 - ok ; 0 - cancel
- """
+ def _ChkCompiled(self):
if not self.Compiled():
raise SyntaxError("Form is not compiled")
- # Call AskUsingForm()
+
+ def Execute(self):
+ """
+ Displays a modal dialog containing the compiled form.
+ @return: 1 - ok ; 0 - cancel
+ """
+ self._ChkCompiled()
+ if not self.modal:
+ raise SyntaxError("Form is not modal. Open() should be instead")
+
return AskUsingForm(*self.__args)
+ def Open(self):
+ """
+ Opens a widget containing the compiled form.
+ """
+ self._ChkCompiled()
+ if self.modal:
+ raise SyntaxError("Form is modal. Execute() should be instead")
+
+ OpenForm(*self.__args)
+
+
def EnableField(self, ctrl, enable):
"""
Enable or disable an input field
@@ -5574,15 +6107,18 @@ try:
# Setup the numeric argument size
Form.NumericArgument.DefI64 = _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL
AskUsingForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_AskUsingForm())
+ OpenForm__ = ctypes.CFUNCTYPE(ctypes.c_long)(_idaapi.py_get_OpenForm())
except:
def AskUsingForm__(*args):
warning("AskUsingForm() needs ctypes library in order to work")
return 0
+ def OpenForm__(*args):
+ warning("OpenForm() needs ctypes library in order to work")
def AskUsingForm(*args):
"""
- Calls the AskUsingForm()
+ Calls AskUsingForm()
@param: Compiled Arguments obtain through the Form.Compile() function
@return: 1 = ok, 0 = cancel
"""
@@ -5591,6 +6127,15 @@ def AskUsingForm(*args):
set_script_timeout(old)
return r
+def OpenForm(*args):
+ """
+ Calls OpenForm()
+ @param: Compiled Arguments obtain through the Form.Compile() function
+ """
+ old = set_script_timeout(0)
+ r = OpenForm__(*args)
+ set_script_timeout(old)
+
#
@@ -6066,6 +6611,24 @@ class simplecustviewer_t(object):
"""Returns True if the current view is the focused view"""
return _idaapi.pyscv_is_focused(self.__this)
+ def GetTForm(self):
+ """
+ Return the TForm hosting this view.
+
+ @return: The TForm that hosts this view, or None.
+ """
+ return _idaapi.pyscv_get_tform(self.__this)
+
+ def GetTCustomControl(self):
+ """
+ Return the TCustomControl underlying this view.
+
+ @return: The TCustomControl underlying this view, or None.
+ """
+ return _idaapi.pyscv_get_tcustom_control(self.__this)
+
+
+
# Here are all the supported events
#
# def OnClick(self, shift):
diff --git a/swig/lines.i b/swig/lines.i
index fe3b634..34f10a4 100644
--- a/swig/lines.i
+++ b/swig/lines.i
@@ -29,6 +29,9 @@
%ignore save_line_in_array;
%ignore init_lines_array;
%ignore finish_makeline;
+%ignore finish_makeline_ex;
+%ignore generate_many_lines_ex;
+%ignore MakeNull_ex;
%ignore gen_labeled_line;
%ignore gen_lname_line;
%ignore makeline_producer_t;
@@ -43,6 +46,7 @@
%ignore term_lines;
%ignore gl_namedone;
%ignore data_as_stack;
+%ignore unhide_hint_text;
%ignore calc_stack_alignment;
%ignore align_down_to_stack;
%ignore align_up_to_stack;
diff --git a/swig/loader.i b/swig/loader.i
index 355d7fb..520a16b 100644
--- a/swig/loader.i
+++ b/swig/loader.i
@@ -111,6 +111,7 @@
%ignore is_embedded_dbfile_ext;
%ignore cpp_namespaces;
%ignore max_trusted_idb_count;
+%ignore no_disk_space_handler;
%ignore mem2base;
%rename (mem2base) py_mem2base;
diff --git a/swig/nalt.i b/swig/nalt.i
index 4893158..0d555db 100644
--- a/swig/nalt.i
+++ b/swig/nalt.i
@@ -6,12 +6,120 @@
%ignore NALT_EA;
%ignore enum_import_names;
%rename (enum_import_names) py_enum_import_names;
+
+%ignore get_wide_value;
+%ignore set_wide_value;
+%ignore del_wide_value;
+
+%ignore get_strid;
+%ignore _set_strid;
+%ignore _del_strid;
+%ignore set_strid;
+%ignore del_strid;
+%ignore xrefpos_t;
+%ignore get_xrefpos;
+%ignore set_xrefpos;
+%ignore del_xrefpos;
+
+%ignore set_aflags0;
+%ignore get_aflags0;
+%ignore del_aflags0;
+
+%ignore get_linnum0;
+%ignore set_linnum0;
+%ignore del_linnum0;
+
+%ignore get_enum_id0;
+%ignore set_enum_id0;
+%ignore del_enum_id0;
+%ignore get_enum_id1;
+%ignore set_enum_id1;
+%ignore del_enum_id1;
+
+%ignore set_ind_purged;
+
+%ignore get_str_type;
+%ignore set_str_type;
+%ignore del_str_type;
+
+%ignore _get_item_color;
+%ignore _set_item_color;
+%ignore _del_item_color;
+
+%ignore get_nalt_cmt;
+%ignore set_nalt_cmt;
+%ignore del_nalt_cmt;
+%ignore get_nalt_rptcmt;
+%ignore set_nalt_rptcmt;
+%ignore del_nalt_rptcmt;
+%ignore get_fop1;
+%ignore set_fop1;
+%ignore del_fop1;
+%ignore get_fop2;
+%ignore set_fop2;
+%ignore del_fop2;
+%ignore get_fop3;
+%ignore set_fop3;
+%ignore del_fop3;
+%ignore get_fop4;
+%ignore set_fop4;
+%ignore del_fop4;
+%ignore get_fop5;
+%ignore set_fop5;
+%ignore del_fop5;
+%ignore get_fop6;
+%ignore set_fop6;
+%ignore del_fop6;
+%ignore get_manual_insn0;
+%ignore set_manual_insn0;
+%ignore del_manual_insn0;
+%ignore get_graph_groups0;
+%ignore set_graph_groups0;
+%ignore del_graph_groups0;
+
+%ignore switch_info_t;
+%ignore switch_info_ex_t;
+%ignore get_switch_info_ex;
+%ignore set_switch_info_ex;
+%ignore del_switch_info_ex;
+
+%ignore refinfo_t::_get_target;
+%ignore refinfo_t::_get_value;
+%ignore refinfo_t::_get_opval;
+
%ignore custom_refinfo_handler_t;
%ignore custom_refinfo_handlers_t;
%ignore register_custom_refinfo;
%ignore unregister_custom_refinfo;
%ignore get_custom_refinfos;
+%ignore write_struc_path;
+%ignore read_struc_path;
+%ignore del_struc_path;
+%ignore get_stroff0;
+%ignore set_stroff0;
+%ignore del_stroff0;
+%ignore get_stroff1;
+%ignore set_stroff1;
+%ignore del_stroff1;
+
+%ignore get__segtrans;
+%ignore set__segtrans;
+%ignore del__segtrans;
+
+%ignore get_switch_info;
+%ignore set_switch_info;
+%ignore del_switch_info;
+%ignore get_ti;
+%ignore set_ti;
+%ignore del_ti;
+%ignore get_op_tinfo;
+%ignore set_op_tinfo;
+%ignore del_tinfo;
+%ignore get_op_ti;
+%ignore set_op_ti;
+%ignore del_ti;
+
%template (ids_array) wrapped_array_t;
%extend strpath_t {
diff --git a/swig/netnode.i b/swig/netnode.i
index 2fb850c..5646fcd 100644
--- a/swig/netnode.i
+++ b/swig/netnode.i
@@ -4,6 +4,7 @@
%ignore RootNode;
%ignore for_all_supvals;
%ignore netErrorHandler;
+%ignore netNoDiskSpaceHandler;
%ignore netnode_key_count;
%ignore netnode_check;
diff --git a/swig/queue.i b/swig/queue.i
index 8861bdf..62a97c9 100644
--- a/swig/queue.i
+++ b/swig/queue.i
@@ -2,7 +2,7 @@
%ignore QueueGet;
// Kernel-only & unexported symbols
-%ignore QueueDel;
+%ignore QueueDel(ea_t);
%ignore init_queue;
%ignore save_queue;
%ignore term_queue;
diff --git a/swig/registry.i b/swig/registry.i
new file mode 100644
index 0000000..0553d2a
--- /dev/null
+++ b/swig/registry.i
@@ -0,0 +1,145 @@
+
+%ignore reg_bin_op;
+%ignore reg_str_op;
+%ignore reg_int_op;
+%ignore _RVN_;
+%ignore REG_VAL_NAME;
+%ignore REG_BOOL_FUNC;
+%ignore REG_INT_FUNC;
+%ignore MAX_HISTORY_FILES_DEF;
+%ignore regkey_history;
+%ignore max_history_files;
+%ignore regget_history;
+%ignore reg_update_history;
+%ignore reg_history_size_truncate;
+
+%ignore reg_read_string;
+%rename (reg_read_string) py_reg_read_string;
+
+%ignore reg_data_type;
+%rename (reg_data_type) py_reg_data_type;
+
+%ignore reg_read_binary;
+%rename (reg_read_binary) py_reg_read_binary;
+%ignore reg_write_binary;
+%rename (reg_write_binary) py_reg_write_binary;
+
+%ignore reg_read_binary_part;
+
+/* inline bool reg_subkey_subkeys(qstrvec_t *out, const char *name) */
+%ignore reg_subkey_subkeys;
+%rename (reg_subkey_subkeys) py_reg_subkey_subkeys;
+%ignore reg_subkey_values;
+%rename (reg_subkey_values) py_reg_subkey_values;
+%ignore reg_subkey_children;
+
+%{
+//
+//-------------------------------------------------------------------------
+static PyObject *_py_reg_subkey_children(const char *name, bool subkeys)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ PyObject *result = NULL;
+ qstrvec_t children;
+ Py_BEGIN_ALLOW_THREADS;
+ if ( reg_subkey_children(&children, name, subkeys) )
+ {
+ result = PyList_New(children.size());
+ if ( result != NULL )
+ for ( size_t i = 0, n = children.size(); i < n; ++i )
+ PyList_SET_ITEM(result, i, PyString_FromString(children[i].c_str()));
+ }
+ Py_END_ALLOW_THREADS;
+ if ( result == NULL )
+ Py_RETURN_NONE;
+ else
+ return result;
+}
+//
+%}
+
+%inline %{
+//
+//-------------------------------------------------------------------------
+PyObject *py_reg_read_string(const char *name, const char *subkey = NULL, const char *def = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ char utf8[MAXSTR * 10];
+ bool ok;
+ Py_BEGIN_ALLOW_THREADS;
+ if ( def == NULL )
+ {
+ ok = reg_read_string(name, utf8, sizeof(utf8), subkey);
+ }
+ else
+ {
+ reg_read_string(name, sizeof(utf8), utf8, def, subkey);
+ ok = true;
+ }
+ Py_END_ALLOW_THREADS;
+ return PyString_FromString(ok ? utf8 : "");
+}
+
+//-------------------------------------------------------------------------
+regval_type_t py_reg_data_type(const char *name, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ regval_type_t rt = reg_unknown;
+ Py_BEGIN_ALLOW_THREADS;
+ reg_data_type(&rt, name, subkey);
+ Py_END_ALLOW_THREADS;
+ return rt;
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_read_binary(const char *name, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ bytevec_t bytes;
+ bool ok;
+ Py_BEGIN_ALLOW_THREADS;
+ ok = reg_read_binary(name, &bytes, subkey);
+ Py_END_ALLOW_THREADS;
+ if ( ok )
+ return PyString_FromStringAndSize((const char *) bytes.begin(), bytes.size());
+ else
+ Py_RETURN_NONE;
+}
+
+//-------------------------------------------------------------------------
+void py_reg_write_binary(const char *name, PyObject *py_bytes, const char *subkey = NULL)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+ if ( PyString_Check(py_bytes) )
+ {
+ char *py_bytes_raw = NULL;
+ Py_ssize_t py_size = 0;
+ PyString_AsStringAndSize(py_bytes, &py_bytes_raw, &py_size);
+ bytevec_t bytes;
+ bytes.append(py_bytes_raw, py_size);
+ Py_BEGIN_ALLOW_THREADS;
+ reg_write_binary(name, bytes.begin(), bytes.size(), subkey);
+ Py_END_ALLOW_THREADS;
+ }
+ else
+ {
+ PyErr_SetString(PyExc_ValueError, "Bytes string expected!");
+ }
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_subkey_subkeys(const char *name)
+{
+ return _py_reg_subkey_children(name, true);
+}
+
+//-------------------------------------------------------------------------
+PyObject *py_reg_subkey_values(const char *name)
+{
+ return _py_reg_subkey_children(name, false);
+}
+
+//
+%}
+
+%include "registry.hpp"
diff --git a/swig/srarea.i b/swig/srarea.i
index 440c43c..309e888 100644
--- a/swig/srarea.i
+++ b/swig/srarea.i
@@ -1,14 +1,44 @@
// Ignore kernel-only symbols
-%ignore create_srarea;
-%ignore kill_srareras;
-%ignore del_srarea;
-%ignore break_srarea;
-%ignore set_srarea_start;
-%ignore set_srarea_end;
%ignore repairSRarea;
+
%ignore init_srarea;
%ignore term_srarea;
+%ignore reset_srarea;
+%ignore add_srarea_from_cache;
+%ignore srareas_got_loaded;
%ignore save_srarea;
+%ignore create_segment_registers_area;
+%ignore set_segment_register_start;
+%ignore set_segment_register_end;
+%ignore kill_srareras;
+%ignore create_srarea;
+%ignore del_srareas;
+%ignore move_srareas;
+%ignore delete_v660_segreg_t;
+%ignore v660_segreg_t;
+
+%ignore SRareas_get_area;
+%ignore SRareas_get_area_qty;
+%ignore SRareas_getn_area;
+%ignore SRareas_update;
+%ignore SRareas_get_area_num;
+%ignore SRareas_get_next_area;
+%ignore SRareas_get_prev_area;
+%ignore SRareas_next_area_ptr;
+%ignore SRareas_prev_area_ptr;
+%ignore SRareas_first_area_ptr;
+%ignore SRareas_choose_area2;
+%ignore SRareas_may_start_at;
+%ignore SRareas_may_end_at;
+%ignore SRareas_set_start;
+%ignore SRareas_set_end;
+%ignore SRareas_prepare_to_create;
+%ignore SRareas_create_area;
+%ignore SRareas_for_all_areas2;
+%ignore SRareas_del_area;
+
+%ignore segreg_t::tag(int n);
+%ignore segreg_t::reg(int n);
#define R_es 29
#define R_cs 30
diff --git a/swig/typeconv.i b/swig/typeconv.i
index a7c7578..45b76a4 100644
--- a/swig/typeconv.i
+++ b/swig/typeconv.i
@@ -1,3 +1,30 @@
+//-------------------------------------------------------------------------
+// For some reason, SWIG converts char arrays by computing the size
+// from the end of the array, and stops when it encounters a '\0'.
+// That doesn't work for us, as our API doesn't guarantee that
+// bytes past the length we are interested in will be zeroed-out.
+// In other words, the following code should *never* be present
+// in idaapi_include.cpp:
+// -------------------------
+// while (size && ([size - 1] == '\0')) --size;
+// -------------------------
+//
+%typemap(out) char [ANY], const char[ANY]
+{
+ %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0)));
+}
+
+%typemap(varout) char [ANY], const char[ANY]
+{
+ %set_output(SWIG_FromCharPtrAndSize($1, strnlen($1, $1_dim0)));
+}
+
+
+%typemap(out) ssize_t
+{
+ $result = PyLong_FromLongLong($1);
+}
+
//---------------------------------------------------------------------
// Convert an incoming Python list to a tid_t[] array
%typemap(in) tid_t[ANY](tid_t temp[$1_dim0]) {
@@ -39,10 +66,6 @@
$1 = ($1_ltype) qalloc(MAXSTR+1);
}
-%typemap(out) ssize_t {
- /* REMOVING ssize_t return value in $symname */
-}
-
%typemap(argout) (TYPEMAP,SIZE) {
Py_XDECREF(resultobj);
if (result > 0)
@@ -88,9 +111,6 @@
%typemap(in,numinputs=0) (TYPEMAP, SIZE) {
$1 = (char *) qalloc(MAXSPECSIZE+1);
}
-%typemap(out) ssize_t {
- /* REMOVING ssize_t return value in $symname */
-}
%typemap(argout) (TYPEMAP,SIZE) {
Py_XDECREF(resultobj);
if (result > 0)
@@ -115,9 +135,6 @@
%typemap(in,numinputs=0) (TYPEMAP, SIZE) {
$1 = (char *) qalloc(MAXSPECSIZE+1);
}
-%typemap(out) ssize_t {
- /* REMOVING ssize_t return value in $symname */
-}
%typemap(argout) (TYPEMAP,SIZE) {
Py_XDECREF(resultobj);
if (result)
@@ -166,8 +183,6 @@
}
$1 = $input;
}
-
-// Convert ea_t
%typemap(in) ea_t
{
uint64 $1_temp;
@@ -178,6 +193,10 @@
}
$1 = ea_t($1_temp);
}
+// Use PyLong_FromUnsignedLongLong, because 'long' is 4 bytes on
+// windows, and thus the ea_t would be truncated at the
+// PyLong_FromUnsignedLong(unsigned int) call time.
+%typemap(out) ea_t "$result = PyLong_FromUnsignedLongLong($1);"
//---------------------------------------------------------------------
// IN qstring
@@ -208,6 +227,15 @@
%apply qstring { _qstring }
%apply qstring* { _qstring* }
+//---------------------------------------------------------------------
+// varargs (mostly kernwin.hpp)
+//---------------------------------------------------------------------
+// This is used for functions like warning(), info() and so on
+%typemap(in) (const char *format, ...)
+{
+ $1 = "%s"; /* Fix format string to %s */
+ $2 = (void *) PyString_AsString($input); /* Get string argument */
+};
#ifdef __EA64__
%apply longlong *INOUT { sval_t *value };
@@ -221,6 +249,14 @@
%apply unsigned int *OUTPUT { ea_t *ea1, ea_t *ea2 }; // read_selection()
#endif
+%apply qstring *result { qstring *label };
+%apply qstring *result { qstring *shortcut };
+%apply qstring *result { qstring *tooltip };
+%apply int *OUTPUT { int *icon };
+%apply int *OUTPUT { action_state_t *state };
+%apply bool *OUTPUT { bool *checkable };
+%apply bool *OUTPUT { bool *checked };
+%apply bool *OUTPUT { bool *visibility };
//-------------------------------------------------------------------------
// The following is to be used to expose an array of items
diff --git a/swig/typeinf.i b/swig/typeinf.i
index 6fbee7b..259b060 100644
--- a/swig/typeinf.i
+++ b/swig/typeinf.i
@@ -71,7 +71,6 @@
%ignore argloc_t::dstr;
%ignore extract_pstr;
-%ignore extract_name;
%ignore skipName;
%ignore extract_comment;
%ignore skipComment;
@@ -180,43 +179,78 @@
%{
//
//-------------------------------------------------------------------------
-// A set of tinfo_t objects that were created from IDAPython.
+// A set of tinfo_t & details objects that were created from IDAPython.
// This is necessary in order to clear all the "type details" that are
// associated, in the kernel, with the tinfo_t instances.
//
// Unfortunately the IDAPython plugin has to terminate _after_ the IDB is
// closed, but the "type details" must be cleared _before_ the IDB is closed.
-static qvector python_tinfos;
+static qvector py_tinfo_t_vec;
+static qvector py_ptr_type_data_t_vec;
+static qvector py_array_type_data_t_vec;
+static qvector py_func_type_data_t_vec;
+static qvector py_udt_type_data_t_vec;
+
+static void __clear(tinfo_t *inst) { inst->clear(); }
+static void __clear(ptr_type_data_t *inst) { inst->obj_type.clear(); inst->closure.clear(); }
+static void __clear(array_type_data_t *inst) { inst->elem_type.clear(); }
+static void __clear(func_type_data_t *inst) { inst->clear(); inst->rettype.clear(); }
+static void __clear(udt_type_data_t *inst) { inst->clear(); }
+
void til_clear_python_tinfo_t_instances(void)
{
- // Pre-emptive strike: clear all the python-exposed tinfo_t instances: if that
- // were not done here, ~tinfo_t() calls happening as part of the python shutdown
- // process will try and clear() their details. ..but the kernel's til-related
- // functions will already have deleted those details at that point.
- for ( size_t i = 0, n = python_tinfos.size(); i < n; ++i )
- python_tinfos[i]->clear();
- // NOTE: Don't clear() the array of pointers. All the python-exposed tinfo_t
+ // Pre-emptive strike: clear all the python-exposed tinfo_t
+ // (& related types) instances: if that were not done here,
+ // ~tinfo_t() calls happening as part of the python shutdown
+ // process will try and clear() their details. ..but the kernel's
+ // til-related functions will already have deleted those details
+ // at that point.
+ //
+ // NOTE: Don't clear() the arrays of pointers. All the python-exposed
// instances will be deleted through the python shutdown/ref-decrementing
// process anyway (which will cause til_deregister_..() calls), and the
// entries will be properly pulled out of the vector when that happens.
+#define BATCH_CLEAR(Type) \
+ do \
+ { \
+ for ( size_t i = 0, n = py_##Type##_vec.size(); i < n; ++i ) \
+ __clear(py_##Type##_vec[i]); \
+ } while ( false )
+
+ BATCH_CLEAR(tinfo_t);
+ BATCH_CLEAR(ptr_type_data_t);
+ BATCH_CLEAR(array_type_data_t);
+ BATCH_CLEAR(func_type_data_t);
+ BATCH_CLEAR(udt_type_data_t);
+#undef BATCH_CLEAR
}
-void til_register_python_tinfo_t_instance(tinfo_t *tif)
-{
- // Let's add_unique() it, because every reference to an object's
- // tinfo_t property will end up trying to register it.
- python_tinfos.add_unique(tif);
-}
-
-void til_deregister_python_tinfo_t_instance(tinfo_t *tif)
-{
- qvector::iterator found = python_tinfos.find(tif);
- if ( found != python_tinfos.end() )
- {
- tif->clear();
- python_tinfos.erase(found);
+#define DEF_REG_UNREG_REFCOUNTED(Type) \
+ void til_register_python_##Type##_instance(Type *inst) \
+ { \
+ /* Let's add_unique() it, because in the case of tinfo_t, every reference*/ \
+ /* to an object's tinfo_t property will end up trying to register it. */ \
+ py_##Type##_vec.add_unique(inst); \
+ } \
+ \
+ void til_deregister_python_##Type##_instance(Type *inst) \
+ { \
+ qvector::iterator found = py_##Type##_vec.find(inst); \
+ if ( found != py_##Type##_vec.end() ) \
+ { \
+ __clear(inst); \
+ /* tif->clear();*/ \
+ py_##Type##_vec.erase(found); \
+ } \
}
-}
+
+DEF_REG_UNREG_REFCOUNTED(tinfo_t);
+DEF_REG_UNREG_REFCOUNTED(ptr_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(array_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(func_type_data_t);
+DEF_REG_UNREG_REFCOUNTED(udt_type_data_t);
+
+#undef DEF_REG_UNREG_REFCOUNTED
//
%}
@@ -245,6 +279,34 @@ void til_deregister_python_tinfo_t_instance(tinfo_t *tif)
}
%ignore tinfo_t::~tinfo_t(void);
+//---------------------------------------------------------------------
+// NOTE: This will ***NOT*** work for tinfo_t objects. Those must
+// be created and owned (or not) according to the kind of access.
+// To implement that, we use typemaps (see typeconv.i).
+%define %simple_tinfo_t_container_lifecycle(Type, CtorSig, ParamsList)
+%extend Type {
+ Type CtorSig
+ {
+ Type *inst = new Type ParamsList;
+ til_register_python_##Type##_instance(inst);
+ return inst;
+ }
+
+ ~Type(void)
+ {
+ til_deregister_python_##Type##_instance($self);
+ delete $self;
+ }
+}
+%enddef
+%simple_tinfo_t_container_lifecycle(ptr_type_data_t, (tinfo_t c=tinfo_t(), uchar bps=0), (c, bps));
+%simple_tinfo_t_container_lifecycle(array_type_data_t, (size_t b=0, size_t n=0), (b, n));
+%simple_tinfo_t_container_lifecycle(func_type_data_t, (), ());
+%simple_tinfo_t_container_lifecycle(udt_type_data_t, (), ());
+
+%template(funcargvec_t) qvector;
+%template(udtmembervec_t) qvector;
+
%include "typeinf.hpp"
// Custom wrappers
@@ -324,9 +386,9 @@ PyObject *py_calc_type_size(const til_t *ti, PyObject *tp)
def apply_type(ti, ea, tp_name, py_type, py_fields, flags)
"""
Apply the specified type to the address
- @param ti: Type info. 'idaapi.cvar.idati' can be passed.
+ @param ti: Type info library. 'idaapi.cvar.idati' can be used.
@param py_type: type string
- @param py_fields: type fields
+ @param py_fields: fields string (may be empty or None)
@param ea: the address of the object
@param flags: combination of TINFO_... constants or 0
@return: Boolean
@@ -337,17 +399,44 @@ def apply_type(ti, ea, tp_name, py_type, py_fields, flags)
static bool py_apply_type(til_t *ti, PyObject *py_type, PyObject *py_fields, ea_t ea, int flags)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
}
const type_t *type = (const type_t *) PyString_AsString(py_type);
- const p_list *fields = (const p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
bool rc;
Py_BEGIN_ALLOW_THREADS;
- tinfo_t tif;
- rc = tif.deserialize(ti, &type, &fields, NULL) && apply_tinfo2(ea, tif, flags);
+ struc_t *sptr;
+ member_t *mptr = get_member_by_id(ea, &sptr);
+ if ( type[0] == '\0' )
+ {
+ if ( mptr != NULL )
+ {
+ rc = mptr->has_ti();
+ if ( rc )
+ del_member_tinfo(sptr, mptr);
+ }
+ else
+ {
+ rc = has_ti(ea);
+ if ( rc )
+ del_tinfo2(ea);
+ }
+ }
+ else
+ {
+ tinfo_t tif;
+ rc = tif.deserialize(ti, &type, &fields, NULL);
+ if ( rc )
+ {
+ if ( mptr != NULL )
+ rc = set_member_tinfo2(sptr, mptr, 0, tif, 0);
+ else
+ rc = apply_tinfo2(ea, tif, flags);
+ }
+ }
Py_END_ALLOW_THREADS;
return rc;
}
@@ -395,7 +484,7 @@ PyObject *py_unpack_object_from_idb(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -407,7 +496,7 @@ PyObject *py_unpack_object_from_idb(
// Unpack
type_t *type = (type_t *) PyString_AsString(py_type);
- p_list *fields = (p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
idc_value_t idc_obj;
error_t err;
Py_BEGIN_ALLOW_THREADS;
@@ -445,7 +534,7 @@ def unpack_object_from_bv(ti, tp, fields, bytes, pio_flags = 0):
Returns the error_t returned by idaapi.pack_object_to_idb
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param bytes: the bytes to unpack
@param pio_flags: flags used while unpacking
@return:
@@ -463,7 +552,7 @@ PyObject *py_unpack_object_from_bv(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) && !PyString_Check(py_bytes) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) || !PyString_Check(py_bytes) )
{
PyErr_SetString(PyExc_ValueError, "Incorrect argument type!");
return NULL;
@@ -475,7 +564,7 @@ PyObject *py_unpack_object_from_bv(
// Get type strings
type_t *type = (type_t *) PyString_AsString(py_type);
- p_list *fields = (p_list *) PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Make a byte vector
bytevec_t bytes;
@@ -519,7 +608,7 @@ def pack_object_to_idb(obj, ti, tp, fields, ea, pio_flags = 0):
Returns the error_t returned by idaapi.pack_object_to_idb
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param ea: ea to be used while packing
@param pio_flags: flags used while unpacking
"""
@@ -535,7 +624,7 @@ PyObject *py_pack_object_to_idb(
int pio_flags = 0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -553,7 +642,7 @@ PyObject *py_pack_object_to_idb(
// Get type strings
type_t *type = (type_t *)PyString_AsString(py_type);
- p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Pack
// error_t err;
@@ -572,7 +661,7 @@ def pack_object_to_bv(obj, ti, tp, fields, base_ea, pio_flags = 0):
Packs a typed object to a string
@param ti: Type info. 'idaapi.cvar.idati' can be passed.
@param tp: type string
- @param fields: type fields
+ @param fields: fields string (may be empty or None)
@param base_ea: base ea used to relocate the pointers in the packed object
@param pio_flags: flags used while unpacking
@return:
@@ -592,7 +681,7 @@ PyObject *py_pack_object_to_bv(
int pio_flags=0)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -610,7 +699,7 @@ PyObject *py_pack_object_to_bv(
// Get type strings
type_t *type = (type_t *)PyString_AsString(py_type);
- p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
// Pack
relobj_t bytes;
@@ -754,7 +843,7 @@ int idc_get_local_type(int ordinal, int flags, char *buf, size_t maxsize)
PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *name, int flags)
{
PYW_GIL_CHECK_LOCKED_SCOPE();
- if ( !PyString_Check(py_type) && !PyString_Check(py_fields) )
+ if ( !PyString_Check(py_type) || !PyWStringOrNone_Check(py_fields) )
{
PyErr_SetString(PyExc_ValueError, "Typestring must be passed!");
return NULL;
@@ -766,7 +855,7 @@ PyObject *idc_print_type(PyObject *py_type, PyObject *py_fields, const char *nam
qstring res;
const type_t *type = (type_t *)PyString_AsString(py_type);
- const p_list *fields = (p_list *)PyString_AsString(py_fields);
+ const p_list *fields = PyW_Fields(py_fields);
bool ok;
Py_BEGIN_ALLOW_THREADS;
tinfo_t tif;
diff --git a/swig/ua.i b/swig/ua.i
index f6b6896..7a7a827 100644
--- a/swig/ua.i
+++ b/swig/ua.i
@@ -18,7 +18,6 @@
%ignore out_insert;
%ignore get_immval;
%ignore get_spoiled_reg;
-%ignore construct_macro;
%ignore decode_preceding_insn;
%ignore init_ua;
%ignore term_ua;
@@ -30,6 +29,9 @@
%ignore get_immval;
%ignore ua_stkvar;
+%ignore construct_macro;
+%rename (construct_macro) py_construct_macro;
+
%include "ua.hpp"
%rename (init_output_buffer) py_init_output_buffer;
@@ -354,6 +356,49 @@ bool py_out_name_expr(
return op == NULL ? false : out_name_expr(*op, ea, off);
}
+//-------------------------------------------------------------------------
+/*
+#
+def construct_macro(insn):
+ """
+ See ua.hpp's construct_macro().
+ """
+ pass
+#
+*/
+bool py_construct_macro(bool enable, PyObject *build_macro)
+{
+ PYW_GIL_CHECK_LOCKED_SCOPE();
+
+ if ( !PyCallable_Check(build_macro) )
+ return false;
+
+ static qstack macro_builders;
+
+ macro_builders.push(newref_t(build_macro));
+ struct ida_local lambda_t
+ {
+ static bool idaapi call_build_macro(insn_t &s, bool may_go_forward)
+ {
+ PyObject *py_builder = macro_builders.top().o;
+ newref_t pyres = PyObject_CallFunction(
+ py_builder, "O",
+ may_go_forward ? Py_True : Py_False);
+ PyW_ShowCbErr("build_macro");
+ if ( pyres.o == NULL || pyres.o == Py_None )
+ return false;
+ insn_t *_s = insn_t_get_clink(pyres.o);
+ if ( _s == NULL )
+ return false;
+ s = *_s;
+ return true;
+ }
+ };
+ bool res = construct_macro(enable, lambda_t::call_build_macro);
+ macro_builders.pop();
+ return res;
+}
+
//-------------------------------------------------------------------------
static PyObject *insn_t_get_op_link(PyObject *py_insn_lnk, int i)
{
@@ -913,7 +958,6 @@ op_t *op_t_get_clink(PyObject *self)
{
return (op_t *)pyobj_get_clink(self);
}
-
//
%}
@@ -1323,22 +1367,22 @@ CF_HLL = 0x10000 # Instruction may be present in a high level language functio
#
# op_t.type
-# Description Data field
-o_void = 0 # No Operand ----------
-o_reg = 1 # General Register (al,ax,es,ds...) reg
-o_mem = 2 # Direct Memory Reference (DATA) addr
-o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase
-o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
-o_imm = 5 # Immediate Value value
-o_far = 6 # Immediate Far Address (CODE) addr
-o_near = 7 # Immediate Near Address (CODE) addr
-o_idpspec0 = 8 # IDP specific type
-o_idpspec1 = 9 # IDP specific type
-o_idpspec2 = 10 # IDP specific type
-o_idpspec3 = 11 # IDP specific type
-o_idpspec4 = 12 # IDP specific type
-o_idpspec5 = 13 # IDP specific type
-o_last = 14 # first unused type
+# Description Data field
+o_void = 0 # No Operand ----------
+o_reg = 1 # General Register (al,ax,es,ds...) reg
+o_mem = 2 # Direct Memory Reference (DATA) addr
+o_phrase = 3 # Memory Ref [Base Reg + Index Reg] phrase
+o_displ = 4 # Memory Reg [Base Reg + Index Reg + Displacement] phrase+addr
+o_imm = 5 # Immediate Value value
+o_far = 6 # Immediate Far Address (CODE) addr
+o_near = 7 # Immediate Near Address (CODE) addr
+o_idpspec0 = 8 # Processor specific type
+o_idpspec1 = 9 # Processor specific type
+o_idpspec2 = 10 # Processor specific type
+o_idpspec3 = 11 # Processor specific type
+o_idpspec4 = 12 # Processor specific type
+o_idpspec5 = 13 # Processor specific type
+ # There can be more processor specific types
#
# op_t.dtyp
@@ -1405,7 +1449,7 @@ class processor_t(pyidc_opaque_object_t):
short processor names similar to the one in ph.psnames.
This method can be overridden to return to the kernel a different IDP description.
"""
- return self.plnames[0] + ':' + ':'.join(self.psnames)
+ return '\x01'.join(map(lambda t: '\x01'.join(t), zip(self.plnames, self.psnames)))
def get_uFlag(self):
"""Use this utility function to retrieve the 'uFlag' global variable"""
diff --git a/swig/view.i b/swig/view.i
index 9d32214..3a5b8f1 100644
--- a/swig/view.i
+++ b/swig/view.i
@@ -137,12 +137,16 @@ class py_customidamemo_t
GRBASE_HAVE_CLOSE = 0x080,
GRBASE_HAVE_VIEW_SWITCHED = 0x100,
GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200,
+ GRBASE_HAVE_VIEW_MOUSE_MOVED = 0x400,
};
static void ensure_view_callbacks_installed();
int cb_flags;
- // number of arguments for OnViewClick implementation
- int ovc_num_args;
+ // number of arguments for:
+ int ovc_num_args; // OnViewClick implementation
+ int ovdc_num_args; // OnViewDblclick implementation
+ int ovmo_num_args; // OnViewMouseOver implementation
+ int ovmm_num_args; // OnViewMouseMoved implementation
protected:
ref_t self;
@@ -176,11 +180,15 @@ protected:
bool collect_pyobject_callbacks(PyObject *self);
virtual void collect_class_callbacks_ids(callbacks_ids_t *out);
+ void install_custom_viewer_handlers();
+
// Bi-directionally bind/unbind the Python object and this controller.
bool bind(PyObject *_self, TCustomControl *view);
void unbind();
static lookup_info_t lookup_info;
+ friend TForm *pycim_get_tform(PyObject *self);
+ friend TCustomControl *pycim_get_tcustom_control(PyObject *self);
public:
py_customidamemo_t();
@@ -210,8 +218,16 @@ public:
void on_view_close();
void on_view_switched(tcc_renderer_type_t rt);
void on_view_mouse_over(const view_mouse_event_t *event);
+ void on_view_mouse_moved(const view_mouse_event_t *event);
inline bool has_callback(int flag) { return (cb_flags & flag) != 0; }
int get_py_method_arg_count(char *method_name);
+
+ // View events that are bound with 'set_custom_viewer_handler()'.
+ static void idaapi s_on_view_mouse_moved(
+ TCustomControl *cv,
+ int shift,
+ view_mouse_event_t *e,
+ void *ud);
};
//-------------------------------------------------------------------------
@@ -222,6 +238,9 @@ py_customidamemo_t::py_customidamemo_t()
PYGLOG("%p: py_customidamemo_t()\n", this);
ensure_view_callbacks_installed();
ovc_num_args = -1;
+ ovdc_num_args = -1;
+ ovmo_num_args = -1;
+ ovmm_num_args = -1;
}
//-------------------------------------------------------------------------
@@ -261,7 +280,7 @@ void py_customidamemo_t::ensure_view_callbacks_installed()
py_view->on_view_keydown(key, state);
}
break;
- case view_popup:
+ case obsolete_view_popup:
py_view->on_view_popup();
break;
case view_click:
@@ -513,6 +532,18 @@ void py_customidamemo_t::unbind()
view = NULL;
}
+//-------------------------------------------------------------------------
+void idaapi py_customidamemo_t::s_on_view_mouse_moved(
+ TCustomControl *cv,
+ int shift,
+ view_mouse_event_t *e,
+ void *ud)
+{
+ PYW_GIL_GET;
+ py_customidamemo_t *_this = (py_customidamemo_t *) ud;
+ _this->on_view_mouse_moved(e);
+}
+
//-------------------------------------------------------------------------
int py_customidamemo_t::get_py_method_arg_count(char *method_name)
{
@@ -543,6 +574,7 @@ void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out)
out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE);
out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED);
out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER);
+ out->add(S_ON_VIEW_MOUSE_MOVED, GRBASE_HAVE_VIEW_MOUSE_MOVED);
}
//-------------------------------------------------------------------------
@@ -566,9 +598,22 @@ bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o)
if ( have > 0 && attr != NULL )
cb_flags |= have;
}
+
return true;
}
+//-------------------------------------------------------------------------
+void py_customidamemo_t::install_custom_viewer_handlers()
+{
+ if ( has_callback(GRBASE_HAVE_VIEW_MOUSE_MOVED) )
+ {
+ // Set user-data
+ set_custom_viewer_handler(view, CVH_USERDATA, (void *)this);
+
+ //
+ set_custom_viewer_handler(view, CVH_MOUSEMOVE, (void *)s_on_view_mouse_moved);
+ }
+}
//-------------------------------------------------------------------------
#define CHK_EVT(flag_needed) \
@@ -576,10 +621,22 @@ bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o)
return; \
PYW_GIL_CHECK_LOCKED_SCOPE()
+
#ifdef PYGDBG_ENABLED
-#define CHK_RES() PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o)
+#define CHK_RES() \
+ do \
+ { \
+ PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o); \
+ if (PyErr_Occurred()) \
+ PyErr_Print(); \
+ } while ( false )
#else
-#define CHK_RES()
+#define CHK_RES() \
+ do \
+ { \
+ if (PyErr_Occurred()) \
+ PyErr_Print(); \
+ } while ( false )
#endif
//-------------------------------------------------------------------------
@@ -631,13 +688,33 @@ void py_customidamemo_t::on_view_popup()
CHK_RES();
}
+//-------------------------------------------------------------------------
+static PyObject *build_renderer_pos_swig_proxy(const view_mouse_event_t *event)
+{
+ return SWIG_NewPointerObj(
+ SWIG_as_voidptr(&event->renderer_pos),
+ SWIGTYPE_p_renderer_pos_info_t,
+ 0);
+}
+
//-------------------------------------------------------------------------
void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
{
CHK_EVT(GRBASE_HAVE_VIEW_CLICK);
if ( ovc_num_args < 0 )
ovc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_CLICK);
- if ( ovc_num_args == 5 )
+ if ( ovc_num_args == 6 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_CLICK,
+ "iiiiO",
+ event->x, event->y, event->state, event->button, rpos));
+ CHK_RES();
+ }
+ else if ( ovc_num_args == 5 )
{
newref_t result(
PyObject_CallMethod(
@@ -645,6 +722,7 @@ void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
(char *)S_ON_VIEW_CLICK,
"iiii",
event->x, event->y, event->state, event->button));
+ CHK_RES();
}
else
{
@@ -654,21 +732,37 @@ void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
(char *)S_ON_VIEW_CLICK,
"iii",
event->x, event->y, event->state));
+ CHK_RES();
}
- CHK_RES();
}
//-------------------------------------------------------------------------
void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event)
{
CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK);
- newref_t result(
- PyObject_CallMethod(
- self.o,
- (char *)S_ON_VIEW_DBLCLICK,
- "iii",
- event->x, event->y, event->state));
- CHK_RES();
+ if ( ovdc_num_args < 0 )
+ ovdc_num_args = get_py_method_arg_count((char*)S_ON_VIEW_DBLCLICK);
+ if ( ovdc_num_args == 5 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_DBLCLICK,
+ "iiiO",
+ event->x, event->y, event->state, rpos));
+ CHK_RES();
+ }
+ else
+ {
+ newref_t result(
+ PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_DBLCLICK,
+ "iii",
+ event->x, event->y, event->state));
+ CHK_RES();
+ }
}
//-------------------------------------------------------------------------
@@ -700,32 +794,55 @@ void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt)
}
//-------------------------------------------------------------------------
-void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
+static ref_t build_current_graph_item_tuple(int *out_icode, const view_mouse_event_t *event)
{
- CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER);
- if ( event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY )
+ const selection_item_t *item = event->location.item;
+ ref_t tuple;
+ if ( (event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY)
+ && item != NULL )
{
- const selection_item_t *item = event->location.item;
- int icode;
- ref_t tuple;
- if ( item != NULL )
+ if ( item->is_node )
{
- if ( item->is_node )
- {
- icode = 1;
- tuple = newref_t(Py_BuildValue("(i)", item->node));
- }
- else
- {
- icode = 2;
- tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst));
- }
+ *out_icode = 1;
+ tuple = newref_t(Py_BuildValue("(i)", item->node));
}
else
{
- icode = 0;
- tuple = newref_t(Py_BuildValue("()"));
+ *out_icode = 2;
+ tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst));
}
+ }
+ else
+ {
+ *out_icode = 0;
+ tuple = newref_t(Py_BuildValue("()"));
+ }
+ return tuple;
+}
+
+//-------------------------------------------------------------------------
+void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
+{
+ CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER);
+ if ( ovmo_num_args < 0 )
+ ovmo_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_OVER);
+ if ( event->rtype != TCCRT_GRAPH && event->rtype != TCCRT_PROXIMITY )
+ return;
+
+ int icode;
+ ref_t tuple = build_current_graph_item_tuple(&icode, event);
+ if ( ovmo_num_args == 7 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_MOUSE_OVER,
+ "iiiiOO",
+ event->x, event->y, event->state, icode, tuple.o, rpos));
+ CHK_RES();
+ }
+ else
+ {
newref_t result(PyObject_CallMethod(
self.o,
(char *)S_ON_VIEW_MOUSE_OVER,
@@ -735,6 +852,27 @@ void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
}
}
+//-------------------------------------------------------------------------
+void py_customidamemo_t::on_view_mouse_moved(const view_mouse_event_t *event)
+{
+ CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_MOVED);
+ if ( ovmm_num_args < 0 )
+ ovmm_num_args = get_py_method_arg_count((char*)S_ON_VIEW_MOUSE_MOVED);
+
+ int icode;
+ ref_t tuple = build_current_graph_item_tuple(&icode, event);
+ if ( ovmm_num_args == 7 )
+ {
+ PyObject *rpos = build_renderer_pos_swig_proxy(event);
+ newref_t result(PyObject_CallMethod(
+ self.o,
+ (char *)S_ON_VIEW_MOUSE_MOVED,
+ "iiiiOO",
+ event->x, event->y, event->state, icode, tuple.o, rpos));
+ CHK_RES();
+ }
+}
+
#undef CHK_RES
#undef CHK_EVT
@@ -747,6 +885,10 @@ void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
GET_THIS(); \
if ( _this == NULL ) \
return
+#define CHK_THIS_OR_NULL() \
+ GET_THIS(); \
+ if ( _this == NULL ) \
+ return NULL;
#define CHK_THIS_OR_NONE() \
GET_THIS(); \
if ( _this == NULL ) \
@@ -828,7 +970,28 @@ PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject
return _this->set_groups_visibility(groups, expand, new_current);
}
+//-------------------------------------------------------------------------
+TForm *pycim_get_tform(PyObject *self)
+{
+ CHK_THIS_OR_NULL();
+ TForm *form = NULL;
+ if ( !py_customidamemo_t::lookup_info.find_by_py_view(&form, NULL, _this) )
+ return NULL;
+ return form;
+}
+
+//-------------------------------------------------------------------------
+TCustomControl *pycim_get_tcustom_control(PyObject *self)
+{
+ CHK_THIS_OR_NULL();
+ TCustomControl *tcc = NULL;
+ if ( !py_customidamemo_t::lookup_info.find_by_py_view(NULL, &tcc, _this) )
+ return NULL;
+ return tcc;
+}
+
#undef CHK_THIS_OR_NONE
+#undef CHK_THIS_OR_NULL
#undef CHK_THIS
#undef GET_THIS
@@ -850,6 +1013,8 @@ void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt);
PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos);
PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current);
PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current);
+TForm *pycim_get_tform(PyObject *self);
+TCustomControl *pycim_get_tcustom_control(PyObject *self);
//
%}
@@ -959,6 +1124,23 @@ class CustomIDAMemo(object):
"""
return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current)
+ def GetTForm(self):
+ """
+ Return the TForm hosting this view.
+
+ @return: The TForm that hosts this view, or None.
+ """
+ return _idaapi.pycim_get_tform(self)
+
+ def GetTCustomControl(self):
+ """
+ Return the TCustomControl underlying this view.
+
+ @return: The TCustomControl underlying this view, or None.
+ """
+ return _idaapi.pycim_get_tcustom_control(self)
+
+
#
%}
@@ -970,6 +1152,7 @@ class py_idaview_t : public py_customidamemo_t
public:
static bool Bind(PyObject *self);
+ static bool Unbind(PyObject *self);
};
//-------------------------------------------------------------------------
@@ -1015,24 +1198,43 @@ bool py_idaview_t::Bind(PyObject *self)
if ( ok )
{
ok = py_view->collect_pyobject_callbacks(self);
- if ( !ok )
+ if ( ok )
+ py_view->install_custom_viewer_handlers();
+ else
delete py_view;
}
return ok;
}
+//-------------------------------------------------------------------------
+bool py_idaview_t::Unbind(PyObject *self)
+{
+ py_idaview_t *_this = view_extract_this(self);
+ if ( _this == NULL )
+ return false;
+ _this->unbind();
+ return true;
+}
+
//-------------------------------------------------------------------------
bool pyidag_bind(PyObject *self)
{
return py_idaview_t::Bind(self);
}
+//-------------------------------------------------------------------------
+bool pyidag_unbind(PyObject *self)
+{
+ return py_idaview_t::Unbind(self);
+}
+
//
%}
%inline %{
//
bool pyidag_bind(PyObject *self);
+bool pyidag_unbind(PyObject *self);
//
%}
@@ -1051,5 +1253,9 @@ class IDAViewWrapper(CustomIDAMemo):
def Bind(self):
return _idaapi.pyidag_bind(self)
+
+ def Unbind(self):
+ return _idaapi.pyidag_unbind(self)
+
#
%}