# Source code for binaryninja

    # Copyright (c) 2015-2017 Vector 35 LLC
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to
    # deal in the Software without restriction, including without limitation the
    # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
    # sell copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    # IN THE SOFTWARE.

    from __future__ import absolute_import
    import atexit
    import sys
    import ctypes
    from time import gmtime

    # 2-3 compatibility
    try:
    	import builtins  # __builtins__ for python2
    except ImportError:
    	pass
    def range(*args):
    	""" A Python2 and Python3 Compatible Range Generator """
    	try:
    		return xrange(*args)
    	except NameError:
    		return builtins.range(*args)

    def with_metaclass(meta, *bases):
        """Create a base class with a metaclass."""
        class metaclass(type):
            def __new__(cls, name, this_bases, d):
                return meta(name, bases, d)

            @classmethod
            def __prepare__(cls, name, this_bases):
                return meta.__prepare__(name, bases)
        return type.__new__(metaclass, 'temporary_class', (), {})

    def cstr(arg):
    	if isinstance(arg, bytes) or arg is None:
    		return arg
    	else:
    		return arg.encode('charmap')

    def pyNativeStr(arg):
    	if isinstance(arg, str):
    		return arg
    	else:
    		return arg.decode('charmap')

    # Binary Ninja components
    import binaryninja._binaryninjacore as core
    # __all__ = [
    # 	"enums",
    # 	"databuffer",
    # 	"filemetadata",
    # 	"fileaccessor",
    # 	"binaryview",
    # 	"transform",
    # 	"architecture",
    # 	"basicblock",
    # 	"function",
    # 	"log",
    # 	"lowlevelil",
    # 	"mediumlevelil",
    # 	"types",
    # 	"functionrecognizer",
    # 	"update",
    # 	"plugin",
    # 	"callingconvention",
    # 	"platform",
    # 	"demangle",
    # 	"mainthread",
    # 	"interaction",
    # 	"lineardisassembly",
    # 	"undoaction",
    # 	"highlight",
    # 	"scriptingprovider",
    # 	"pluginmanager",
    # 	"setting",
    # 	"metadata",
    # ]
    from binaryninja.enums import *
    from binaryninja.databuffer import *
    from binaryninja.filemetadata import *
    from binaryninja.fileaccessor import *
    from binaryninja.binaryview import *
    from binaryninja.transform import *
    from binaryninja.architecture import *
    from binaryninja.basicblock import *
    from binaryninja.function import *
    from binaryninja.log import *
    from binaryninja.lowlevelil import *
    from binaryninja.mediumlevelil import *
    from binaryninja.types import *
    from binaryninja.functionrecognizer import *
    from binaryninja.update import *
    from binaryninja.plugin import *
    from binaryninja.callingconvention import *
    from binaryninja.platform import *
    from binaryninja.demangle import *
    from binaryninja.mainthread import *
    from binaryninja.interaction import *
    from binaryninja.lineardisassembly import *
    from binaryninja.undoaction import *
    from binaryninja.highlight import *
    from binaryninja.scriptingprovider import *
    from binaryninja.downloadprovider import *
    from binaryninja.pluginmanager import *
    from binaryninja.setting import *
    from binaryninja.metadata import *

    def shutdown():
    	"""
    	``shutdown`` cleanly shuts down the core, stopping all workers and closing all log files.
    	"""
    	core.BNShutdown()

    atexit.register(shutdown)

    def get_unique_identifier():
    	return core.BNGetUniqueIdentifierString()

    def get_install_directory():
    	"""
    	``get_install_directory`` returns a string pointing to the installed binary currently running

    	..warning:: ONLY for use within the Binary Ninja UI, behavior is undefined and unreliable if run headlessly
    	"""
    	return core.BNGetInstallDirectory()

    _plugin_api_name = "python2"

    class PluginManagerLoadPluginCallback(object):
    	"""Callback for BNLoadPluginForApi("python2", ...), dynamicly loads python plugins."""
    	def __init__(self):
    		self.cb = ctypes.CFUNCTYPE(
    			ctypes.c_bool,
    			ctypes.c_char_p,
    			ctypes.c_char_p,
    			ctypes.c_void_p)(self._load_plugin)

    	def _load_plugin(self, repo_path, plugin_path, ctx):
    		try:
    			repo = RepositoryManager()[repo_path]
    			plugin = repo[plugin_path]

    			if plugin.api != _plugin_api_name:
    				raise ValueError("Plugin api name is not " + _plugin_api_name)

    			if not plugin.installed:
    				plugin.installed = True

    			if repo.full_path not in sys.path:
    				sys.path.append(repo.full_path)

    			__import__(plugin.path)
    			log_info("Successfully loaded plugin: {}/{}: ".format(repo_path, plugin_path))
    			return True
    		except KeyError:
    			log_error("Failed to find python plugin: {}/{}".format(repo_path, plugin_path))
    		except ImportError as ie:
    			log_error("Failed to import python plugin: {}/{}: {}".format(repo_path, plugin_path, ie))
    		return False

    load_plugin = PluginManagerLoadPluginCallback()
    core.BNRegisterForPluginLoading(_plugin_api_name, load_plugin.cb, 0)

    class _DestructionCallbackHandler(object):
    	def __init__(self):
    		self._cb = core.BNObjectDestructionCallbacks()
    		self._cb.context = 0
    		self._cb.destructBinaryView = self._cb.destructBinaryView.__class__(self.destruct_binary_view)
    		self._cb.destructFileMetadata = self._cb.destructFileMetadata.__class__(self.destruct_file_metadata)
    		self._cb.destructFunction = self._cb.destructFunction.__class__(self.destruct_function)
    		core.BNRegisterObjectDestructionCallbacks(self._cb)

    	def destruct_binary_view(self, ctxt, view):
    		BinaryView._unregister(view)

    	def destruct_file_metadata(self, ctxt, f):
    		FileMetadata._unregister(f)

    	def destruct_function(self, ctxt, func):
    		Function._unregister(func)

    _plugin_init = False

    def _init_plugins():
    	global _plugin_init
    	if not _plugin_init:
    		_plugin_init = True
    		core.BNInitCorePlugins()
    		core.BNInitUserPlugins()
    		core.BNInitRepoPlugins()
    	if not core.BNIsLicenseValidated():
    		raise RuntimeError("License is not valid. Please supply a valid license.")

    _destruct_callbacks = _DestructionCallbackHandler()

    bundled_plugin_path = core.BNGetBundledPluginDirectory()
    user_plugin_path = core.BNGetUserPluginDirectory()

    core_version = core.BNGetVersionString()
    '''Core version'''

    core_build_id = core.BNGetBuildId()
    '''Build ID'''

    core_serial = core.BNGetSerialNumber()
    '''Serial Number'''

    core_expires = gmtime(core.BNGetLicenseExpirationTime())
    '''License Expiration'''

    core_product = core.BNGetProduct()
    '''Product string from the license file'''

    core_product_type = core.BNGetProductType()
    '''Product type from the license file'''

    core_license_count = core.BNGetLicenseCount()
    '''License count from the license file'''