#---------------------------------------------------------------------
# idaxml.py - IDA XML classes
# 
# idaxml is a python library shipped with Ghidra
# https://github.com/NationalSecurityAgency/ghidra/blob/49c2010b63b56c8f20845f3970fedd95d003b1e9/GhidraBuild/IDAPro/Python/7xx/python/idaxml.py
#
# Ghidra is licensed under the Apache License 2.0
#
# Copyright 2019 NationalSecurityAgency - Ghidra
#
#---------------------------------------------------------------------

from __future__ import print_function
import ida_auto
import ida_bytes
import ida_diskio
import ida_enum
import ida_fpro
import ida_frame
import ida_funcs
import ida_ida
import ida_idaapi
import ida_idp
import ida_hexrays
import ida_kernwin
import ida_lines
import ida_loader
import ida_moves
import ida_nalt
import ida_name
import ida_netnode
import ida_pro
import ida_segment
import ida_segregs
import ida_struct
import ida_typeinf
import ida_ua
import ida_xref
import idautils
import idc
import datetime
import os
import sys
import time
from xml.etree import cElementTree


DEBUG = False  # print debug statements

IDAXML_VERSION = "5.0.1"
BASELINE_IDA_VERSION = 700
BASELINE_STR = '7.00'
IDA_SDK_VERSION = ida_pro.IDA_SDK_VERSION
BADADDR = idc.BADADDR
BADNODE = ida_netnode.BADNODE
PLUGIN = True
LOADER = not PLUGIN
AUTO_WAIT = True

# Added global structures
SYMBLE_TABLE_DICT = dict()


def is_ida_version_supported():
    """
    Determines if IDA version is supported by this idaxml module.

    Returns:
        True if IDA version is supported, else False.
    """
    supported = IDA_SDK_VERSION >= BASELINE_IDA_VERSION
    if not supported:
        idc.msg('\nThe IDA XML plugins and loader are not supported ' +
                'by this version of IDA.\n')
        idc.msg('Please use IDA ' + BASELINE_STR + ' or greater ' +
                'with this version of XML.\n')
    return supported


class Cancelled(Exception):
    pass


class FileError(Exception):
    pass


class MultipleAddressSpacesNotSupported(Exception):
    pass


class IdaXml:

    def __init__(self, arg):
        self.autorun = False if arg == 0 else True
        self.debug = DEBUG
        self.elements = {}
        self.counters = []
        self.tags = []
        self.xmlfile = 0
        self.options = None

    def cleanup(self):
        """
        Frees memory and closes message box and XML file at termination.
        """
        if self.options != None:
            self.options.Free()
        ida_kernwin.hide_wait_box()
        self.close_xmlfile()

    def close_xmlfile(self):
        """
        Closes the XML data file for the XML Exporter.
        """
        if self.xmlfile != 0:
            self.xmlfile.close()
            self.xmlfile = 0

    def dbg(self, message):
        """
        Outputs debug message if debug flag is enabled.

        Args:
            message: String containing the debug message.
        """
        if (self.debug == True):
            idc.msg(message)

    def display_summary(self, what):
        """
        Displays summary in IDA output window.
        """
        summary = ''
        total = 0
        for tag in self.tags:
            count = self.counters[self.elements[tag]]
            summary += "\n%-26s %8d" % (tag, count)
            total += count
        summary = "\n--------------------------------------" + summary
        summary += "\n--------------------------------------"
        summary += ("\n%-26s %8d" % ("Total XML Elements:", total))
        idc.msg(summary)
        if self.autorun == False:  # and self.plugin:
            frmt = "TITLE XML " + what + " Successful!\n"
            frmt += "ICON INFO\n"
            frmt += "AUTOHIDE NONE\n"
            frmt += "HIDECANCEL\n"
            fileline = '\n\nFile: %s' % self.filename
            details = '\nSee output window for details...'
            ida_kernwin.info("%s" % (frmt + fileline + details))

    def display_version(self, what):
        """
        Displays XML version info in IDA output window.

        Args:
            what: String indicating Exporter, Importer, or Loader 
        """
        # f = ida_diskio.idadir('python') + '/idaxml.py'
        # ftime = time.localtime(os.path.getmtime(f))
        # ts = time.strftime('%b %d %Y %H:%M:%S', ftime)
        version = "\nXML " + what + " v" + IDAXML_VERSION
        version += " : SDK " + str(IDA_SDK_VERSION)
        # version += " : Python : "+ ts + '\n'
        idc.msg(version)

    def open_file(self, filename, mode):
        """
        Opens filename to specified mode.

        Args:
            filename: String representing absolute filepath.
            mode: String representing mode for open.

        Returns
            File handle.

        Exceptions:
            Displays a warning and raises FileError exception
            if open fails.
        """
        try:
            f = open(filename, mode)
            return f
        except:
            fmt = "TITLE ERROR!\n"
            fmt += "ICON ERROR\n"
            fmt += "AUTOHIDE NONE\n"
            fmt += "HIDECANCEL\n"
            fmt += "Error opening file" + filename + "!\n"
            idc.warning(fmt)
            raise FileError

    def update_counter(self, tag):
        """
        Updates the counter for the element tag.

        Args:
            tag: String representing element tag.
        """
        if tag in self.elements:
            self.counters[self.elements[tag]] += 1
        else:
            self.elements[tag] = len(self.elements)
            self.counters.append(1)
            self.tags.append(tag)

    def update_status(self, tag):
        """
        Displays the processing status in the IDA window.

        Args:
            tag: String representing XML element tag
        """
        status = 'Processing ' + tag
        idc.msg('\n%-35s' % status)
        ida_kernwin.hide_wait_box()
        ida_kernwin.show_wait_box(status)


class XmlExporter(IdaXml):
    """
    XML Exporter contains methods to export an IDA database as a
        XML PROGRAM document.
    """

    def __init__(self, arg):
        """
        Initializes the XmlExporter attributes

        Args:
            arg: Integer, non-zero value enables auto-run feature for
                IDA batch (no gui) processing mode. Default is 0.
        """
        IdaXml.__init__(self, arg)
        self.indent_level = 0
        self.seg_addr = False
        self.has_overlays = False
        self.hexrays = False

        # initialize class variables from database
        self.inf = ida_idaapi.get_inf_structure()
        self.min_ea = self.inf.min_ea
        self.max_ea = self.inf.max_ea
        self.cbsize = (ida_idp.ph_get_cnbits() + 7) / 8
        self.processor = str.upper(ida_idp.get_idp_name())
        self.batch = ida_kernwin.cvar.batch

    def export_xml(self, filename):
        """
        Exports the IDA database to a XML PROGRAM document file.
        """
        SYMBLE_TABLE_DICT = dict()

        self.display_version('Exporter')
        self.check_and_load_decompiler()

        self.get_options()
        self.filename = filename

        if self.filename == None or len(self.filename) == 0:
            raise Cancelled
        self.xmlfile = self.open_file(self.filename, "w")

        ida_kernwin.show_wait_box("Exporting XML <PROGRAM> document ....")
        idc.msg("\n------------------------------------------------" +
                "-----------")
        idc.msg("\nExporting XML <PROGRAM> document ....")
        begin = time.clock()

        self.write_xml_declaration()
        ret_val = self.export_program()
        if not ret_val:
            print("GhIDA:: [!] export_xml cancelled")
            raise Cancelled

        # export database items based on options
        if (self.options.DataTypes.checked == True or
                self.options.DataDefinitions.checked == True or
                self.options.Functions.checked == True):
            self.export_datatypes()
        if (self.options.MemorySections.checked == True or
                self.options.MemoryContent.checked == True):
            self.export_memory_map()
        if (self.options.RegisterValues.checked == True):
            self.export_register_values()
        if (self.options.CodeBlocks.checked == True):
            self.export_code()
        if (self.options.DataDefinitions.checked == True):
            self.export_data()
        if (self.options.Comments.checked == True):
            self.export_comments()
            self.export_bookmarks()
        if (self.options.EntryPoints.checked == True):
            self.export_program_entry_points()
        if (self.options.Symbols.checked == True):
            self.export_symbol_table()
        if (self.options.Functions.checked == True):
            self.export_functions()
        if (self.options.MemoryReferences.checked == True or
                self.options.StackReferences.checked == True or
                self.options.Manual.checked == True or
                self.options.DataTypes.checked == True):
            self.export_markup()
        self.end_element(PROGRAM)

        idc.msg('\n%35s' % 'Total ')
        self.display_cpu_time(begin)
        ida_kernwin.hide_wait_box()
        self.display_summary('Export')
        idc.msg('\nDatabase exported to: ' + self.filename + '\n')

        print("GhIDA:: [DEBUG] found %d symbols" % len(SYMBLE_TABLE_DICT))
        return

    # TODO: Test decompiler comments in batch and gui modes
    def check_and_load_decompiler(self):
        """
        Checks for the presence of a decompiler plugin for the database.

        Note: The decompiler must be loaded by the XML Exporter plugin
            if it is running in batch mode. IDA will load the decompiler
            plugin automatically if not in batch mode.

        Note: There was no support for decompiler plugins in IDAPython until
            IDA 6.6, so skip if this is an older version.

        Note: Currently the 4 decompiler plugins for the  x86, x64,
            ARM32, and ARM64 are supported.
        """
        if self.batch == 0:
            self.hexrays = ida_hexrays.init_hexrays_plugin()
            return
        plugin = ''
        if self.processor == 'PC':
            if self.inf.is_64bit():
                plugin = "hexx64"
            elif self.inf.is_32bit():
                plugin = 'hexrays'
        elif self.processor == 'ARM':
            if self.inf.is_64bit():
                plugin = "hexarm64"
            elif self.inf.is_32bit():
                plugin = "hexarm"
        if len(plugin) > 0:
            try:
                ida_loader.load_plugin(plugin)
                self.hexrays = ida_hexrays.init_hexrays_plugin()
            except:
                return

    def check_char(self, ch):
        """
        Replaces a special XML character with an entity string.

        Args:
            ch: String containing the character to check.

        Returns:
            String containing either the character or the entity
            substition string.
        """
        if ((ord(ch) < 0x20) and (ord(ch) != 0x09 and
                                  ord(ch) != 0x0A and ord(ch) != 0x0D)):
            return ''
        elif ch == '&':
            return '&amp;'
        elif ch == '<':
            return "&lt;"
        elif ch == '>':
            return "&gt;"
        elif ch == '\'':
            return "&apos;"
        elif ch == '"':
            return "&quot;"
        elif ch == '\x7F':
            return ''
        elif ord(ch) > 0x7F:
            return '&#x' + format(ord(ch), "x") + ";"
        return ch

    def check_for_entities(self, text):
        """
        Checks all characters in a string for special XML characters.

        Args:
            text: String to check for special XML characters.

        Returns:
            String containing original string with substitutions for
                any special XML characters.
        """
        new = ''
        for c in text:
            new += self.check_char(c)
        return new

    def check_if_seg_contents(self, seg):
        """
        Determines if any address in a segment contains a value.

        Args:
            seg: IDA segment object

        Returns:
            True if any address in a segment contains a value.
            False if no address in a segment contains a value.
        """
        for addr in idautils.Heads(seg.start_ea, seg.end_ea):
            if idc.has_value(idc.get_full_flags(addr)) == True:
                return True
        return False

    def check_stack_frame(self, sframe):
        """
        Determines if stack frame contains any parameters or local variables.

        Args:
            sframe: IDA stack frame for a function.

        Returns:
            True if stack frame has parameters or local variables.
            False if stack frame has no parameters or local variables.
        """
        n = sframe.memqty
        for i in range(n):
            member = sframe.get_member(i)
            if member == None:
                continue
            mname = ida_struct.get_member_name(member.id)
            if mname != None and len(mname) > 0:
                if mname != " s" and mname != " r":
                    return True
        return False

    def close_binfile(self):
        """
        Closes the binary data file for the XML Exporter.
        """
        if self.binfile != 0:
            self.binfile.close()
            self.binfile = 0

    def close_tag(self, has_contents=False):
        """
        Closes the start tag for an XML element.

        Args:
            has_contents: Boolean indicating if the element has
            sub-elements or text.
        """
        if has_contents:
            self.write_to_xmlfile(">")
            self.indent_level += 1
        else:
            self.write_to_xmlfile(" />")

    def display_cpu_time(self, start):
        """
        Displays the elapsed CPU time since the start time.

        Args:
            start: Floating-point value representing start time in seconds.
        """
        idc.msg('CPU time: %6.4f' % (time.clock() - start))

    def end_element(self, tag, newline=True):
        """
        Writes the element end tag to the XML file.

        Args:
            tag: String containing the element name.
            newline: Boolean indicating if end tag should go on new line.
        """
        self.indent_level -= 1
        if newline:
            start = '\n' + ("    " * self.indent_level)
        else:
            start = ''
        self.write_to_xmlfile(start + "</" + tag + ">")

    # BIT_MASK not currently supported for ENUM
    def export_bitmask(self, eid, mask):
        """
        Exports an enum bitmask member as BIT_MASK element.

        Args:
            eid: Integer representing the IDA enum id
            mask: Integer representing the IDA enum mask value
        """
        name = idc.get_bmask_name(eid, mask)
        if name == None:
            return
        self.start_element(BIT_MASK)
        self.write_attribute(NAME, name)
        self.write_numeric_attribute(VALUE, mask)
        regcmt = idc.get_bmask_cmt(eid, mask, False)
        rptcmt = idc.get_bmask_cmt(eid, mask, True)
        has_comment = regcmt != None or rptcmt != None
        self.close_tag(has_comment)
        if regcmt != None and len(regcmt) > 0:
            self.export_regular_cmt(regcmt)
        if rptcmt != None and len(rptcmt) > 0:
            self.export_repeatable_cmt(rptcmt)
        if (has_comment):
            self.end_element(BIT_MASK)

    def export_bookmarks(self):
        """
        Exports marked location descriptions as BOOKMARK elements.
        """
        found = False
        timer = time.clock()
        for slot in range(0, 1025):
            address = idc.get_bookmark(slot)
            description = idc.get_bookmark_desc(slot)
            if address == BADADDR:
                continue
            if description == None:
                continue
            if found == False:
                found = True
                self.update_status(BOOKMARKS)
                self.start_element(BOOKMARKS, True)
            self.start_element(BOOKMARK)
            self.write_address_attribute(ADDRESS, address)
            self.write_attribute(DESCRIPTION, description)
            self.close_tag()
        if found:
            self.end_element(BOOKMARKS)
            self.display_cpu_time(timer)

    def export_c_comments(self):
        """
        Exports block and end-of-line comments entered in the decompiler
        interface.
        """
        if self.hexrays == False:
            return
        functions = idautils.Functions()
        if functions == None:
            return
        for addr in functions:
            try:
                if ida_segment.is_spec_ea(addr):
                    continue
                ccmts = ida_hexrays.restore_user_cmts(addr)
                if ccmts == None:
                    continue
                p = ida_hexrays.user_cmts_begin(ccmts)
                while p != ida_hexrays.user_cmts_end(ccmts):
                    cmk = ida_hexrays.user_cmts_first(p)
                    cmv = ida_hexrays.user_cmts_second(p)
                    if cmk.itp < (ida_hexrays.ITP_COLON + 1):
                        self.export_comment(cmk.ea, "end-of-line", cmv.c_str())
                    else:
                        self.export_comment(cmk.ea, "pre", cmv.c_str())
                    p = ida_hexrays.user_cmts_next(p)
                ida_hexrays.user_cmts_free(ccmts)
            except:
                continue

    def export_code(self):
        """
        Exports the address ranges of code sequences as CODE_BLOCK(s)
        with START and END address attributes.
        """
        addr = self.min_ea
        if idc.is_code(idc.get_full_flags(addr)) == False:
            addr = ida_bytes.next_that(addr, self.max_ea, idc.is_code)
        if (addr == BADADDR):
            return
        self.update_status(CODE)
        timer = time.clock()
        data = ida_bytes.next_that(addr, self.max_ea, idc.is_data)
        unknown = ida_bytes.next_unknown(addr, self.max_ea)
        self.start_element(CODE, True)
        while (addr != BADADDR):
            start = addr
            end = min(data, unknown)
            if (end == BADADDR):
                if (ida_segment.getseg(start).end_ea < self.max_ea):
                    codeend = ida_segment.getseg(start).end_ea - 1
                    addr = ida_segment.getseg(idc.next_addr(codeend)).start_ea
                    if idc.is_code(idc.get_full_flags(addr)) == False:
                        addr = ida_bytes.next_that(addr, self.max_ea,
                                                   idc.is_code)
                else:
                    codeend = self.max_ea - 1
                    addr = BADADDR
            else:
                if (ida_segment.getseg(start).end_ea < end):
                    codeend = ida_segment.getseg(start).end_ea - 1
                    addr = ida_segment.getseg(idc.next_addr(codeend)).start_ea
                    if idc.is_code(ida_bytes.get_full_flags(addr)) == False:
                        addr = ida_bytes.next_that(addr, self.max_ea,
                                                   idc.is_code)
                else:
                    codeend = idc.get_item_end(ida_bytes.prev_that(end,
                                                                   start, idc.is_code)) - 1
                    addr = ida_bytes.next_that(end, self.max_ea, idc.is_code)
                if (data < addr):
                    data = ida_bytes.next_that(addr, self.max_ea,
                                               idc.is_data)
                if (unknown < addr):
                    unknown = ida_bytes.next_unknown(addr, self.max_ea)
            self.start_element(CODE_BLOCK)
            self.write_address_attribute(START, start)
            self.write_address_attribute(END, codeend)
            self.close_tag()
        self.end_element(CODE)
        self.display_cpu_time(timer)

    def export_comment(self, addr, cmt_type, cmt):
        """
        Exports a <COMMENT> element with ADDRESS and TYPE attributes.
        The comment is exported as the element text (parsed character data).

        Args:
            addr: Integers representing address of comment.
            cmt_type: String indicating the comment type.
            cmt: String containing the comment.
        """
        self.start_element(COMMENT)
        self.write_address_attribute(ADDRESS, addr)
        self.write_attribute(TYPE, cmt_type)
        self.close_tag(True)
        # tag_remove seems to be losing last character
        # work around is to add a space
        cmt_text = ida_lines.tag_remove(cmt + ' ')
        self.write_text(cmt_text)
        self.end_element(COMMENT, False)

    def export_comments(self):
        """
        Exports all comments in the IDA database as <COMMENT> elements.
        """
        addr = self.min_ea
        if ida_bytes.has_cmt(idc.get_full_flags(addr)) == False:
            addr = ida_bytes.next_that(addr, self.max_ea, ida_bytes.has_cmt)
        if (addr == BADADDR):
            return
        self.update_status(COMMENTS)
        timer = time.clock()
        self.start_element(COMMENTS, True)
        while (addr != BADADDR):
            cmt = idc.get_cmt(addr, False)
            if (cmt != None):
                self.export_comment(addr, "end-of-line", cmt)
            cmt = idc.get_cmt(addr, True)
            if (cmt != None):
                self.export_comment(addr, "repeatable", cmt)
            addr = ida_bytes.next_that(addr, self.max_ea, ida_bytes.has_cmt)
        addr = self.min_ea
        if ida_bytes.has_extra_cmts(idc.get_full_flags(addr)) == False:
            addr = ida_bytes.next_that(
                addr, self.max_ea, ida_bytes.has_extra_cmts)
        while (addr != BADADDR):
            extra = idc.get_extra_cmt(addr, idc.E_PREV)
            if (extra != None):
                self.export_extra_comment(addr, "pre", idc.E_PREV)
            extra = idc.get_extra_cmt(addr, idc.E_NEXT)
            if (extra != None):
                self.export_extra_comment(addr, "post", idc.E_NEXT)
            addr = ida_bytes.next_that(
                addr, self.max_ea, ida_bytes.has_extra_cmts)
        self.export_c_comments()
        self.end_element(COMMENTS)
        self.display_cpu_time(timer)

    def export_data(self):
        """
        Exports the data items in the database as <DEFINED_DATA> elements.
        """
        addr = self.min_ea
        if idc.is_data(idc.get_full_flags(addr)) == False:
            addr = ida_bytes.next_that(addr, self.max_ea, idc.is_data)
        if (addr == BADADDR):
            return
        timer = time.clock()
        self.update_status(DATA)
        self.start_element(DATA, True)
        while (addr != BADADDR):
            f = idc.get_full_flags(addr)
            if ida_bytes.is_align(f) == True:
                addr = ida_bytes.next_that(addr, self.max_ea, idc.is_data)
                continue
            dtype = self.get_datatype(addr)
            size = idc.get_item_size(addr)
            ti = ida_nalt.opinfo_t()
            msize = ida_bytes.get_data_elsize(addr, f, ti)
            if ida_bytes.is_struct(f) == True:
                s = idc.get_struc_id(dtype)
                msize = idc.get_struc_size(s)
                if msize == 0:
                    msize = 1
            if idc.is_strlit(f) == False and size != msize:
                dtype = "%s[%d]" % (dtype, size / msize)
            self.start_element(DEFINED_DATA)
            self.write_address_attribute(ADDRESS, addr)
            self.write_attribute(DATATYPE, dtype)
            self.write_numeric_attribute(SIZE, size * self.cbsize)
            # TODO consider using GetTrueNameEx and Demangle
            demangled = ida_name.get_demangled_name(addr,
                                                    DEMANGLED_TYPEINFO, self.inf.demnames, idc.GN_STRICT)
            outbuf = ''
            # TODO: How to handle print_type for data mangled names?
            #outbuf = idaapi.print_type(addr, False)
            if demangled == "'string'":
                demangled == None
            has_typeinfo = ((demangled != None and len(demangled) > 0) or
                            (outbuf != None and len(outbuf) > 0))
            # TODO export_data: add DISPLAY_SETTINGS
            self.close_tag(has_typeinfo)
            if has_typeinfo == True:
                if demangled != None and len(demangled) > 0:
                    self.export_typeinfo_cmt(demangled)
                elif len(outbuf) > 0:
                    self.export_typeinfo_cmt(outbuf)
                self.end_element(DEFINED_DATA)
            addr = ida_bytes.next_that(addr, self.max_ea, idc.is_data)
        self.end_element(DATA)
        self.display_cpu_time(timer)

    def export_datatypes(self):
        """
        Exports the structures and enums in IDA database.
        """
        # skip if no structures/unions to export
        if idc.get_struc_qty() == 0:
            return
        self.update_status(DATATYPES)
        timer = time.clock()
        self.start_element(DATATYPES, True)
        self.export_structures()
        self.export_enums()
        self.end_element(DATATYPES)
        self.display_cpu_time(timer)

    def export_enum_member(self, cid, bf, mask, radix, signness):
        """
        Exports a member of an enum.

        Args:
            cid: Integer representing id of enum member
            bf: Boolean indicates if a bitfield
            mask: Integer representing bitmask if bitfield
            radix: Integer representing numeric display format
            signness: Boolean indicating if signed value 
        """
        cname = ida_enum.get_enum_member_name(cid)
        if cname == None or len(cname) == 0:
            return
        regcmt = ida_enum.get_enum_member_cmt(cid, False)
        rptcmt = ida_enum.get_enum_member_cmt(cid, True)
        has_comment = regcmt != None or rptcmt != None
        self.start_element(ENUM_ENTRY)
        self.write_attribute(NAME, cname)
        value = ida_enum.get_enum_member_value(cid)
        self.write_numeric_attribute(VALUE, value, radix, signness)
        # BIT_MASK attribute not currently supported for ENUM_ENTRY
        # if bf == True:
        #    self.write_numeric_attribute(BIT_MASK, mask)
        self.close_tag(has_comment)
        if regcmt != None and len(regcmt) > 0:
            self.export_regular_cmt(regcmt)
        if rptcmt != None and len(rptcmt) > 0:
            self.export_repeatable_cmt(rptcmt)
        if (has_comment):
            self.end_element(ENUM_ENTRY)

    def export_enum_members(self, eid, bf, eflags):
        """
        Exports the members of an enum.

        This function can only be called by IDA versions newer than 6.3 

        Args:
            eid: Integer representing id of enum
            bf: Boolean indicates if a bitfield
            eflags: Integer representing the enum flags
        """
        mask = 0xFFFFFFFF
        if bf == True:
            mask = idc.get_first_bmask(eid)
        first = True
        for n in range(idc.get_enum_size(eid)):
            if (first == True):
                value = ida_enum.get_first_enum_member(eid, mask)
                first = False
            else:
                value = ida_enum.get_next_enum_member(eid, value, mask)
            (cid, serial) = ida_enum.get_first_serial_enum_member(eid, value, mask)
            main_cid = cid
            while cid != BADNODE:
                self.export_enum_member(cid, bf, mask,
                                        ida_bytes.get_radix(eflags, 0),
                                        self.is_signed_data(eflags))
                last_value = ida_enum.get_last_enum_member(eid, mask)
                if value == last_value:
                    # ENUM BIT_MASK exporting not currently supported
                    #self.export_bitmask(eid, mask)
                    mask = idc.get_next_bmask(eid, mask)
                    first = True
                (cid, serial) = ida_enum.get_next_serial_enum_member(serial, main_cid)

    def export_enum_reference(self, addr, op):
        """
        Exports the enum reference for an operand at an address.

        Args:
            addr: Integer representing the instruction address.
            op: Integer representing the operand index (0-based)
        """
        (eid, serial) = ida_bytes.get_enum_id(addr, op)
        insn = ida_ua.insn_t()
        ida_ua.decode_insn(insn, addr)
        value = insn.ops[op].value
        cid = BADNODE
        last = idc.get_last_bmask(eid)
        if idc.is_bf(eid) == True:
            last = idc.get_last_bmask(eid)
            mask = idc.get_first_bmask(eid)
            while cid == BADNODE:
                cid = ida_enum.get_enum_member(eid, (value & mask), 0, mask)
                if cid != BADNODE or mask == last:
                    break
                mask = idc.get_next_bmask(eid, mask)
        else:
            cid = ida_enum.get_enum_member(eid, value, 0, last)
        if cid == BADNODE:
            return
        self.start_element(EQUATE_REFERENCE)
        self.write_address_attribute(ADDRESS, addr)
        self.write_numeric_attribute(OPERAND_INDEX, op, 10)
        self.write_numeric_attribute(
            VALUE, ida_enum.get_enum_member_value(cid))
        cname = ida_enum.get_enum_member_name(cid)
        if cname != None and len(cname) > 0:
            self.write_attribute(NAME, cname)
        if idc.is_bf(eid) == True:
            self.write_numeric_attribute("BIT_MASK", mask)
        self.close_tag()

    def export_enum_references(self, addr):
        """
        Finds and exports enum references at an address.

        Args:
            addr: Integer representing the instruction address.
        """
        f = idc.get_full_flags(addr)
        for op in range(2):
            if ida_bytes.is_enum(f, op) == True:
                self.export_enum_reference(addr, op)

    def export_enums(self):
        """
        Exports enumerations.
        """
        num_enums = idc.get_enum_qty()
        if (num_enums == 0):
            return
        for i in range(num_enums):
            self.start_element(ENUM)
            eid = idc.getn_enum(i)
            ename = idc.get_enum_name(eid)
            if (ename == None or len(ename) == 0):
                continue
            self.write_attribute(NAME, ename)
            ewidth = idc.get_enum_width(eid)
            if ewidth != 0 and ewidth <= 7:
                self.write_numeric_attribute(SIZE, 1 << (ewidth - 1), 10)
            eflags = idc.get_enum_flag(eid)
            bf = idc.is_bf(eid)
            # BIT_FIELD attribute not supported for ENUM export
            # if bf == True:
            #    self.write_attribute(BIT_FIELD, "yes")
            regcmt = idc.get_enum_cmt(eid, False)
            rptcmt = idc.get_enum_cmt(eid, True)
            has_children = ((idc.get_enum_size(eid) > 0) or
                            (regcmt != None) or (rptcmt != None) or
                            (ida_bytes.get_radix(eflags, 0) != 16) or
                            (self.is_signed_data(eflags) == True))
            self.close_tag(has_children)
            if (ida_bytes.get_radix(eflags, 0) != 16 or
                    self.is_signed_data(eflags) == True):
                self.start_element(DISPLAY_SETTINGS)
                if ida_bytes.get_radix(eflags, 0) != 16:
                    self.write_attribute(FORMAT, self.get_format(eflags))
                if self.is_signed_data(eflags) == True:
                    self.write_attribute(SIGNED, "yes")
                self.close_tag()
            if regcmt != None:
                self.export_regular_cmt(regcmt)
            if rptcmt != None:
                self.export_repeatable_cmt(rptcmt)
            self.export_enum_members(eid, bf, eflags)
            if (has_children):
                self.end_element(ENUM)

    def export_extra_comment(self, addr, cmt_type, extra):
        """
        Exports pre- and post- comments for an address.

        Args:
            addr: Integer representing the instruction address.
            cmt_type: String indicating comment type
            extra: Integer representing extra comment index
        """
        cmt = ''
        nextline = idc.get_extra_cmt(addr, extra)
        while (nextline != None):
            # workaround for tag_remove bug is to add space
            cmt += ida_lines.tag_remove(nextline + ' ')
            extra += 1
            nextline = idc.get_extra_cmt(addr, extra)
            if (nextline != None):
                cmt += '\n'
        self.export_comment(addr, cmt_type, cmt)

    def export_functions(self):
        """
        Exports information about all functions. 
        """
        functions = idautils.Functions()
        if functions == None:
            return
        self.update_status(FUNCTIONS)
        timer = time.clock()
        self.start_element(FUNCTIONS, True)
        for addr in functions:
            function = ida_funcs.get_func(addr)
            if ida_segment.is_spec_ea(function.start_ea) == True:
                continue
            self.start_element(FUNCTION)
            self.write_address_attribute(ENTRY_POINT, function.start_ea)
            if ida_bytes.has_user_name(idc.get_full_flags(addr)) == True:
                name = self.get_symbol_name(addr)
                if name != None and len(name) > 0:
                    self.write_attribute(NAME, name)
            if function.flags & idc.FUNC_LIB != 0:
                self.write_attribute(LIBRARY_FUNCTION, "y")
            self.close_tag(True)
            fchunks = idautils.Chunks(addr)
            for (startEA, endEA) in fchunks:
                self.start_element(ADDRESS_RANGE)
                self.write_address_attribute(START, startEA)
                self.write_address_attribute(END, endEA - 1)
                self.close_tag()
            regcmt = ida_funcs.get_func_cmt(function, False)
            if regcmt != None:
                self.export_regular_cmt(regcmt)
            rptcmt = ida_funcs.get_func_cmt(function, True)
            if rptcmt != None:
                self.export_repeatable_cmt(rptcmt)
            demangled = ida_name.get_demangled_name(addr,
                                                    DEMANGLED_TYPEINFO,
                                                    self.inf.demnames, True)
            if demangled != None and demangled == "'string'":
                demangled = None
            outbuf = ''
            # TODO: How to handle print_type for function typeinfo cmts
            #outbuf = idaapi.print_type(addr, False)
            has_typeinfo = (demangled != None or (outbuf != None and
                                                  len(outbuf) > 0))
            if demangled != None:
                self.export_typeinfo_cmt(demangled)
            elif has_typeinfo == True:
                self.export_typeinfo_cmt(outbuf[:-1])
            self.export_stack_frame(function)
            self.end_element(FUNCTION)
        self.end_element(FUNCTIONS)
        self.display_cpu_time(timer)

    def export_manual_instruction(self, addr):
        """
        Exports user-entered "manual instruction" at an address.

        Args:
            addr: Integer representing instruction address.
        """
        text = idc.get_manual_insn(addr)
        if text == None or len(text) == 0:
            return
        self.start_element(MANUAL_INSTRUCTION)
        self.write_address_attribute(ADDRESS, addr)
        self.close_tag(True)
        self.write_text(text)
        self.end_element(MANUAL_INSTRUCTION, False)

    def export_manual_operand(self, addr):
        """
        Exports user-entered "manual operands" at an address.

        Args:
            addr: Integer representing instruction address.
        """
        for op in range(ida_ida.UA_MAXOP):
            if ida_bytes.is_forced_operand(addr, op) == True:
                text = idc.get_forced_operand(addr, op)
                if text != None and len(text) > 0:
                    self.start_element(MANUAL_OPERAND)
                    self.write_address_attribute(ADDRESS, addr)
                    self.write_numeric_attribute(OPERAND_INDEX, op, 10)
                    self.close_tag(True)
                    self.write_text(text)
                    self.end_element(MANUAL_OPERAND, False)

    def export_markup(self):
        """
        Exports markup for instructions and data items including references
        and manual instructions and operands.
        """
        self.update_status(MARKUP)
        timer = time.clock()
        self.start_element(MARKUP, True)
        addr = self.min_ea
        while addr != BADADDR:
            f = idc.get_full_flags(addr)
            if self.options.MemoryReferences.checked == True:
                if ida_bytes.has_xref(f) == True:
                    self.export_user_memory_reference(addr)
                if ida_bytes.is_off(f, ida_bytes.OPND_ALL) == True:
                    self.export_memory_references(addr)
            if (self.options.Functions.checked == True and
                    self.options.StackReferences.checked == True and
                    ida_bytes.is_stkvar(f, ida_bytes.OPND_ALL) == True):
                self.export_stack_reference(addr)
            if (self.options.DataTypes.checked == True and
                    ida_bytes.is_enum(f, ida_bytes.OPND_ALL) == True):
                self.export_enum_references(addr)
            if self.options.Manual.checked == True:
                # TODO: Ask about OPND_ALL and retrieving additional manual operands
                # if ida_bytes.is_forced_operand(addr, ida_bytes.OPND_ALL) ==
                # True:
                if (ida_bytes.is_forced_operand(addr, 0) == True or
                        ida_bytes.is_forced_operand(addr, 1) == True):
                    self.export_manual_operand(addr)
                if ida_bytes.is_manual_insn(addr) == True:
                    self.export_manual_instruction(addr)
            addr = idc.next_head(addr, self.max_ea)
        self.end_element(MARKUP)
        self.display_cpu_time(timer)

    def export_members(self, s):
        """
        Exports the members of a structure or union.

        Args:
            s: IDA structure/union instance
        """
        nmembers = s.memqty
        for n in range(nmembers):
            m = s.get_member(n)
            offset = m.soff
            if s.is_union() == True:
                offset = 0
            self.start_element(MEMBER)
            self.write_numeric_attribute(OFFSET, offset)
            mname = ida_struct.get_member_name(m.id)
            if len(mname) > 0:
                self.write_attribute(NAME, mname)
            dtype = self.get_member_type(m)
            if ida_struct.is_varmember(m) == True:
                msize = 0
                size = 0
            else:
                mtibuf = ida_nalt.opinfo_t()
                mti = ida_struct.retrieve_member_info(mtibuf, m)
                # if IDA_SDK_VERSION < 640:
                #    msize = idaapi.get_type_size0(None, dtype)
                #    if msize == None or msize == 0:
                #        msize = ida_struct.get_member_size(m)
                # else:
                size = ida_struct.get_member_size(m)
                #msize = idaapi.get_data_type_size(m.flag, mtibuf)
                # TODO: How to handle get_date_type_size for structure members
                msize = size
                if size < msize:
                    size = msize
            if (size != msize):
                arraytype = self.get_member_type(m)
                dtype = "%s[%d]" % (arraytype, size / msize)
            self.write_attribute(DATATYPE, dtype)
            self.write_numeric_attribute(SIZE, size * self.cbsize)
            regcmt = ida_struct.get_member_cmt(m.id, False)
            rptcmt = ida_struct.get_member_cmt(m.id, True)
            hascmt = regcmt != None or rptcmt != None
            self.close_tag(hascmt)
            if (hascmt):
                if regcmt != None:
                    self.export_regular_cmt(regcmt)
                if rptcmt != None:
                    self.export_repeatable_cmt(rptcmt)
                self.end_element(MEMBER)

    def export_memory_contents(self, binfilename, binfile, start, end):
        """
        Exports the binary memory contents in the database.

        A MEMORY_CONTENTS element is generated for each contiguous address
        range where each address in the range contains a value.
        The binary values are store in a separate file (not the XML file),
        and the MEMORY_CONTENTS element identifies the file and the
        offset in the file where the address range is located.

        Args:
            binfilename: String containing the absolute filepath
            binfile: IDA file instance for binary file
            start: Integer representing the starting address
            end: Integer representing the ending address
        """
        length = 0
        startaddr = start
        for addr in range(start, end):
            # reset start address when length == 0
            if (length == 0):
                startaddr = addr
            has_val = ida_bytes.has_value(idc.get_full_flags(addr))
            if has_val == True:
                length += self.cbsize
            next_address = idc.next_addr(addr)
            if ((has_val == False) or (next_address != addr + 1) or
                    (next_address == end)):
                if length > 0:
                    offset = binfile.tell()
                    ida_loader.base2file(binfile.get_fp(), offset, startaddr,
                                         startaddr + length)
                    self.start_element(MEMORY_CONTENTS)
                    self.write_address_attribute(START_ADDR, startaddr)
                    self.write_attribute(FILE_NAME, binfilename)
                    self.write_numeric_attribute(FILE_OFFSET, offset)
                    self.write_numeric_attribute(LENGTH, length)
                    self.close_tag(False)
                    length = 0

    def export_memory_map(self):
        """
        Exports information about all memory blocks in the database.

        A MEMORY_SECTION is generated for each block (segment). If the
        memory block is initialized (has values), the contents are exported
        using the MEMORY_CONTENTS element.
        """
        nsegs = ida_segment.get_segm_qty()
        if (nsegs == 0):
            return
        self.update_status(MEMORY_MAP)
        timer = time.clock()
        binfilename = ''
        if (self.options.MemoryContent.checked == True):
            (binfilename, ext) = os.path.splitext(self.filename)
            binfilename += ".bytes"
            self.binfile = ida_fpro.qfile_t()
            self.binfile.open(binfilename, 'wb')
        self.start_element(MEMORY_MAP, True)
        for i in range(nsegs):
            self.export_memory_section(ida_segment.getnseg(i), binfilename)
        self.end_element(MEMORY_MAP)
        if (self.options.MemoryContent.checked == True):
            self.close_binfile()
        self.display_cpu_time(timer)

    def export_memory_reference(self, addr, op):
        """
        Exports the memory reference for operand at the address.

        Args:
            addr: Integer representing the instruction address.
            op: Integer representing the operand index (0-based)
        """
        f = idc.get_full_flags(addr)
        ri = ida_nalt.refinfo_t()
        if ida_nalt.get_refinfo(ri, addr, op) == 1:
            if ri.target != BADADDR:
                target = ri.target
            elif idc.is_code(f) == True:
                insn = ida_ua.insn_t()
                ida_ua.decode_insn(insn, addr)
                target = (insn.ops[op].value - ri.tdelta + ri.base) & ((1 << 64) - 1)
            elif idc.is_data(f) == True:
                target = (self.get_data_value(addr) - ri.tdelta + ri.base) & ((1 << 64) - 1)
            else:
                return
        else:
            return
        if ida_bytes.is_mapped(target) == False:
            return
        self.start_element(MEMORY_REFERENCE)
        self.write_address_attribute(ADDRESS, addr)
        self.write_numeric_attribute(OPERAND_INDEX, op, 10)
        self.write_address_attribute(TO_ADDRESS, target)
        self.write_attribute(PRIMARY, "y")
        self.close_tag()

    def export_memory_references(self, addr):
        """
        Exports the memory references for any operands at the address.

        Args:
            addr: Integer representing the instruction address.
        """
        f = idc.get_full_flags(addr)
        for op in range(ida_ida.UA_MAXOP):
            if ida_bytes.is_off(f, op) == True and (idc.is_data(f) == True or
                                                    (idc.is_code(f) == True and
                                                     self.is_imm_op(addr, op) == True)):
                self.export_memory_reference(addr, op)

    def export_memory_section(self, seg, binfilename):
        """
        Exports segment information as a MEMORY_SECTIONS element.

        Args:
            seg: IDA segment instance
            binfilename: String containing absolute filepath for binary file.
        """
        segname = ida_segment.get_segm_name(seg)
        self.start_element(MEMORY_SECTION)
        self.write_attribute(NAME, segname)
        self.write_address_attribute(START_ADDR, seg.start_ea)
        length = (seg.end_ea - seg.start_ea) * self.cbsize
        self.write_numeric_attribute(LENGTH, length)
        perms = ""
        if (seg.perm != 0):
            if (seg.perm & ida_segment.SEGPERM_READ != 0):
                perms += 'r'
            if (seg.perm & ida_segment.SEGPERM_WRITE != 0):
                perms += 'w'
            if (seg.perm & ida_segment.SEGPERM_EXEC != 0):
                perms += 'x'
            if (len(perms) > 0):
                self.write_attribute(PERMISSIONS, perms)
        has_contents = (self.options.MemoryContent.checked == True and
                        self.check_if_seg_contents(seg) == True)
        self.close_tag(has_contents)
        if (has_contents == True):
            self.export_memory_contents(os.path.basename(binfilename),
                                        self.binfile, seg.start_ea, seg.end_ea)
            self.end_element(MEMORY_SECTION)

    def export_program(self):
        """
        Exports basic information about the program as the PROGRAM,
        INFO_SOURCE, PROCESSOR, and COMPILER elements.
        """
        # output the PROGRAM element
        self.update_status(PROGRAM)
        timer = time.clock()
        self.start_element(PROGRAM)
        self.write_attribute(NAME, idc.get_root_filename())
        self.write_attribute(EXE_PATH, idc.get_input_file_path())
        etype = ida_loader.get_file_type_name()
        if (len(etype) > 0):
            self.write_attribute(EXE_FORMAT, etype)
        # check for presence of INPUT_MD5 netnode
        md5 = ida_netnode.netnode(INPUT_MD5)
        if md5 == BADNODE:
            input_md5 = idc.retrieve_input_file_md5()
        else:
            input_md5 = md5.supval(ida_nalt.RIDX_MD5)
        if input_md5 != None:
            self.write_attribute(INPUT_MD5, input_md5)
        self.close_tag(True)

        # output the INFO_SOURCE element
        self.start_element(INFO_SOURCE)
        tool = 'IDA-Pro ' + ida_kernwin.get_kernel_version()
        tool += ' XML plugin v' + IDAXML_VERSION + \
            ' (Python) SDK ' + str(IDA_SDK_VERSION)
        self.write_attribute(TOOL, tool)
        user = os.getenv("USERNAME", "UNKNOWN")
        if (user == "UNKNOWN"):
            user = os.getenv("USER", "UNKNOWN")
        self.write_attribute(USER, user)
        self.write_attribute(FILE, idc.get_idb_path())
        ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
        self.write_attribute(TIMESTAMP, ts)
        self.close_tag()

        # output the PROCESSOR element
        self.start_element(PROCESSOR)
        self.write_attribute(NAME, self.inf.procname)
        if self.inf.is_be() == True:
            byte_order = "big"
        else:
            byte_order = "little"
        self.write_attribute(ENDIAN, byte_order)
        self.seg_addr = False
        bitness = 1
        model_warning = False
        nsegs = ida_segment.get_segm_qty()
        if (nsegs > 0):
            bitness = ida_segment.getnseg(0).bitness
            for i in range(1, nsegs):
                seg = ida_segment.getnseg(i)
                if (seg.bitness != bitness):
                    model_warning = True
                if (seg.bitness > bitness):
                    bitness = seg.bitness
        addr_model = "32-bit"
        if (bitness == 0):
            addr_model = "16-bit"
        elif (bitness == 2):
            addr_model = "64-bit"
        self.write_attribute(ADDRESS_MODEL, addr_model)
        self.close_tag()
        if (model_warning):
            idc.msg("WARNING: Segments do not have same " +
                    "addressing model!\n")
        if (ida_idp.ph.id == ida_idp.PLFM_386 and bitness == 0):
            self.seg_addr = True
        # find any overlayed memory before processing addressable items
        self.find_overlay_memory()

        # output compiler info
        self.start_element(COMPILER)

        # AM - Added to correctly convert the compiler name from IDA
        # to Ghidra definitions. For x86 binaries, look at x86.ldefs
        # specifications.
        compiler_name = ida_typeinf.get_compiler_name(self.inf.cc.id)
        print("GhIDA:: [DEBUG] compiler name: %s" % compiler_name)
        new_compiler_name = ''

        if addr_model == '16-bit':
            new_compiler_name = 'default'

        elif addr_model == '32-bit':
            # Visual C++ is handled as "Visual Studio" by Ghidra
            # Delphi is supported by Ghidra
            # Borland C++ is supported by Ghidra
            if compiler_name == "Visual C++" or \
                    compiler_name == "Delphi" or \
                    compiler_name == "Borland C++":
                new_compiler_name = compiler_name

            # Rename GNU C++ into gcc
            elif compiler_name == "GNU C++":
                new_compiler_name = "gcc"

            # Watcom C++ is not supported by Ghidra
            elif compiler_name == "Watcom C++":
                print("GhIDA [!] Watcom C++ compiler not supported by Ghidra")
                return False

            # Visual Age C++ is not supported by Ghidra
            elif compiler_name == "Visual Age C++":
                print(
                    "GhIDA [!] Visual Age C++ compiler not supported by Ghidra")
                return False

            # Generic compiler are not supported by Ghidra
            elif compiler_name == 'Unknown':
                print("GhIDA [!] unknown compiler not supported by Ghidra")
                return False

            else:
                print("GhIDA [!] compiler not supported by Ghidra")
                return False

        elif addr_model == '64-bit':
            # Visual C++ is handled as "Visual Studio" by Ghidra
            if compiler_name == "Visual C++":
                new_compiler_name = compiler_name

            # Rename GNU C++ into gcc
            elif compiler_name == "GNU C++":
                new_compiler_name = "gcc"

            else:
                print("GhIDA [!] unknown compiler not supported by Ghidra")
                return False

        print("GhIDA:: [DEBUG] new_compiler_name: %s" % new_compiler_name)

        # self.write_attribute(
        #     NAME, ida_typeinf.get_compiler_name(self.inf.cc.id))
        self.write_attribute(NAME, new_compiler_name)
        self.close_tag()
        self.display_cpu_time(timer)
        return True

    def export_program_entry_points(self):
        """
        Exports entry points for the program.
        """
        nepts = idc.get_entry_qty()
        if (nepts == 0):
            return
        self.update_status(PROGRAM_ENTRY_POINTS)
        timer = time.clock()
        self.start_element(PROGRAM_ENTRY_POINTS, True)
        for i in range(nepts):
            self.start_element(PROGRAM_ENTRY_POINT)
            addr = idc.get_entry(idc.get_entry_ordinal(i))
            self.write_address_attribute(ADDRESS, addr)
            self.close_tag()
        self.end_element(PROGRAM_ENTRY_POINTS)
        self.display_cpu_time(timer)

    def export_register_values(self):
        """
        Exports segment register value ranges.
        """
        first = ida_idp.ph_get_reg_first_sreg()
        last = ida_idp.ph_get_reg_last_sreg() + 1
        has_segregareas = False
        for j in range(first, last):
            nsegregareas = ida_segregs.get_sreg_ranges_qty(j)
            if nsegregareas != 0:
                has_segregareas = True
                break
        if has_segregareas == False:
            return
        self.update_status(REGISTER_VALUES)
        timer = time.clock()
        self.start_element(REGISTER_VALUES, True)
        sr = ida_segregs.sreg_range_t()
        for j in range(first, last):
            nsegregareas = ida_segregs.get_sreg_ranges_qty(j)
            if nsegregareas == 0:
                continue
            for i in range(nsegregareas):
                success = ida_segregs.getn_sreg_range(sr, j, i)
                if success == False:
                    continue
                value = sr.val
                if value == idc.BADSEL:
                    continue
                regname = ida_idp.ph.regnames[j]
                if regname == None:
                    continue
                if regname.lower() == "cs":
                    continue
                if (ida_idp.ph.id == ida_idp.PLFM_TMS and
                        regname.lower() == "ds"):
                    continue
                self.start_element(REGISTER_VALUE_RANGE)
                self.write_attribute(REGISTER, ida_idp.ph.regnames[j])
                self.write_numeric_attribute(VALUE, value)
                self.write_address_attribute(START_ADDRESS, sr.start_ea)
                length = (sr.end_ea - sr.start_ea) * self.cbsize
                self.write_numeric_attribute(LENGTH, length)
                self.close_tag()
        self.end_element(REGISTER_VALUES)
        self.display_cpu_time(timer)

    def export_regular_cmt(self, cmt):
        """
        Exports the regular comment for an item.

        Args:
            cmt: String containing the regular comment.
        """
        self.write_comment_element(REGULAR_CMT, cmt)

    def export_repeatable_cmt(self, cmt):
        """
        Exports the repeatable comment for an item.

        Args:
            cmt: String containing the repeatable comment.
        """
        self.write_comment_element(REPEATABLE_CMT, cmt)

    def export_stack_frame(self, function):
        """
        Export information about a function stack frame including
        variables allocated on the stack.

        Args:
            function: IDA function instance
        """
        sframe = ida_struct.get_struc(function.frame)
        if sframe == None or sframe.memqty <= 0:
            return
        self.start_element(STACK_FRAME)
        self.write_numeric_attribute(LOCAL_VAR_SIZE, function.frsize)
        self.write_numeric_attribute(REGISTER_SAVE_SIZE, function.frregs)
        retsize = ida_frame.get_frame_retsize(function)
        self.write_numeric_attribute(RETURN_ADDR_SIZE, retsize)
        self.write_numeric_attribute(BYTES_PURGED, function.argsize)
        has_stack_vars = self.check_stack_frame(sframe)
        self.close_tag(has_stack_vars)
        if has_stack_vars == True:
            self.export_stack_vars(function, sframe)
            self.end_element(STACK_FRAME)

    def export_stack_reference(self, addr):
        """
        Exports references to stack variables at the address.

        Args:
            addr: Integer containing instruction address.
        """
        f = idc.get_full_flags(addr)
        for op in range(ida_ida.UA_MAXOP):
            if idc.is_code(f) == True and ida_bytes.is_stkvar(f, op) == True:
                insn = ida_ua.insn_t()
                ida_ua.decode_insn(insn, addr)
                opnd = insn.ops[op]
                # TODO:How to handle opnd.type for stack references
                optype = opnd.type
                if optype == idc.o_void:
                    continue
                # TODO:How to handle op_t_get_addr for stack references
                SV = ida_frame.get_stkvar(insn, opnd, opnd.value)
                if SV == None:
                    continue
                (sv, actval) = SV
                function = ida_funcs.get_func(addr)
                self.start_element(STACK_REFERENCE)
                self.write_address_attribute(ADDRESS, addr)
                self.write_numeric_attribute(OPERAND_INDEX, op, 10)
                offset = opnd.addr
                spoff = offset - function.frregs
                if offset > 0x7FFFFFFF:
                    offset -= 0x100000000
                if spoff > 0x7FFFFFFF:
                    spoff -= 0x100000000
                self.write_numeric_attribute(STACK_PTR_OFFSET, spoff,
                                             16, True)
                if (function.flags & idc.FUNC_FRAME) != 0:
                    self.write_numeric_attribute(FRAME_PTR_OFFSET,
                                                 offset, 16, True)
                self.close_tag()

    def export_stack_vars(self, function, sframe):
        """
        Exports the stack variables (parameters and locals) in a stack frame.

        Args:
            function: IDA function instance.
            sframe: IDA stack frame instance.
        """
        for i in range(sframe.memqty):
            member = sframe.get_member(i)
            if member == None:
                continue
            mname = ida_struct.get_member_name(member.id)
            if mname == None or len(mname) < 0:
                continue
            if mname == " s" or mname == " r":
                continue
            spoff = member.soff - function.frsize - function.frregs
            froff = member.soff - function.frsize
            self.start_element(STACK_VAR)
            self.write_numeric_attribute(STACK_PTR_OFFSET, spoff, 16, True)
            if function.flags & idc.FUNC_FRAME != 0:
                self.write_numeric_attribute(FRAME_PTR_OFFSET, froff, 16, True)
            pre = mname[0:4]
            if pre != "var_" and pre != "arg_":
                self.write_attribute(NAME, mname)
            f = member.flag
            size = ida_struct.get_member_size(member)
            mtype = self.get_member_type(member)
            msize = size
            if idc.is_struct(f) == True:
                msize = idc.get_struc_size(ida_struct.get_struc_id(mtype))
            elif idc.is_strlit(f) == False:
                mtibuf = ida_nalt.opinfo_t()
                mti = ida_struct.retrieve_member_info(mtibuf, member)
                # TODO: How to handle get_data_type_size (for stack vars)
                #msize = idaapi.get_data_type_size(f, mtibuf)
            if size < msize:
                size = msize
            if (idc.is_strlit(f) == False and ida_bytes.is_align(f) == False
                    and size != msize):
                mtype = "%s[%d]" % (mtype, size / msize)
            self.write_attribute(DATATYPE, mtype)
            self.write_numeric_attribute(SIZE, size * self.cbsize)
            regcmt = ida_struct.get_member_cmt(member.id, False)
            rptcmt = ida_struct.get_member_cmt(member.id, True)
            if regcmt != None:
                regcmt = ida_lines.tag_remove(regcmt + " ", 0)
            if rptcmt != None:
                rptrcmt = ida_lines.tag_remove(rptcmt + " ", 0)
            has_regcmt = regcmt != None and len(regcmt) > 0
            has_rptcmt = rptcmt != None and len(rptcmt) > 0
            has_content = has_regcmt or has_rptcmt
            self.close_tag(has_content)
            if has_content == True:
                if has_regcmt == True:
                    self.export_regular_cmt(regcmt)
                if has_rptcmt == True:
                    self.export_repeatable_cmt(rptcmt)
                self.end_element(STACK_VAR)

    def export_structures(self):
        """
        Exports information about all structures and unions.
        """
        structs = idautils.Structs()
        for struct in structs:
            (idx, sid, sname) = struct
            s = ida_struct.get_struc(sid)
            stype = STRUCTURE
            if s.is_union() == True:
                stype = UNION
            self.start_element(stype)
            self.write_attribute(NAME, sname)
            size = idc.get_struc_size(sid) * self.cbsize
            self.write_numeric_attribute(SIZE, size)
            if s.is_varstr() == True:
                self.write_attribute(VARIABLE_LENGTH, "y")
            regcmt = idc.get_struc_cmt(sid, False)
            rptcmt = idc.get_struc_cmt(sid, True)
            has_contents = regcmt != None or rptcmt != None or s.memqty > 0
            self.close_tag(has_contents)
            if (has_contents):
                if regcmt != None:
                    self.export_regular_cmt(regcmt)
                if rptcmt != None:
                    self.export_repeatable_cmt(rptcmt)
                if s.memqty > 0:
                    self.export_members(s)
                self.end_element(stype)

    def export_symbol(self, addr, name, stype=""):
        """
        Exports name for an address as a SYMBOL element. If the name is a
        demangled name, add the mangled name as the MANGLED attribute.

        Args:
            addr: Integer representing the symbol address.
            name: String containing the symbol name.
            stype: String indicating symbol type (global or local)
        """
        SYMBLE_TABLE_DICT[name] = addr

        self.start_element(SYMBOL)
        self.write_address_attribute(ADDRESS, addr)
        self.write_attribute(NAME, name)
        self.write_attribute(TYPE, stype)
        mangled = idc.get_name(addr, idc.GN_STRICT)
        if name != None and mangled != name:
            self.write_attribute("MANGLED", mangled)
        self.close_tag()

    def export_symbol_table(self):
        """
        Exports user-defined and non-default names as SYMBOL elements.
        """
        addr = self.min_ea
        if ida_bytes.has_any_name(idc.get_full_flags(addr)) == False:
            addr = ida_bytes.next_that(
                addr, self.max_ea, ida_bytes.has_any_name)
        if addr == BADADDR:
            return
        self.update_status(SYMBOL_TABLE)
        self.start_element(SYMBOL_TABLE, True)
        timer = time.clock()
        while addr != BADADDR:
            # only export meaningful names (user and auto)
            f = idc.get_full_flags(addr)
            if (ida_bytes.has_user_name(f) == True or
                    ida_bytes.has_auto_name(f) == True):
                # check for global name
                name = self.get_symbol_name(addr)
                if name != None and len(name) > 0:
                    self.export_symbol(addr, name)
                # check for local name
                if ida_nalt.has_lname(addr):
                    name = idc.get_name(addr, idc.GN_LOCAL)
                    if name != None and len(name) > 0:
                        self.export_symbol(addr, name, 'local')
            # get next address with any name
            addr = ida_bytes.next_that(addr, self.max_ea,
                                       ida_bytes.has_any_name)
        self.end_element(SYMBOL_TABLE)
        self.display_cpu_time(timer)

    def export_typeinfo_cmt(self, cmt):
        """
        Exports comment containing type information for data and functions.

        Args:
            cmt: String containing type info.
        """
        # older versions of IDAPython returned a '\n' at end of cmt
        if(len(cmt) > 0):
            while cmt[-1] == '\n':
                cmt = cmt[:-1]
        self.write_comment_element(TYPEINFO_CMT, cmt)

    def export_user_memory_reference(self, addr):
        """
        Exports a user-specified memory reference at the address.

        Args:
            addr: Integer representing the instruction address.
        """
        for xref in idautils.XrefsTo(addr, ida_xref.XREF_FAR):
            if xref.user == 1:
                self.start_element(MEMORY_REFERENCE)
                self.write_address_attribute(ADDRESS, xref.frm)
                self.write_address_attribute(TO_ADDRESS, xref.to)
                self.write_attribute(USER_DEFINED, "y")
                self.close_tag()

    def find_overlay_memory(self):
        """
        Determines if any memory blocks (segments) are overlays.

        A segment is an overlay if it translates to the same logical
        address as another segment. This is rare, but may occur, for
        example when a processor has a small logical address space
        (i.e. a 16-bit address is limited to 64K) and multiple physical
        segments are mapped into the same logical segment.
        """
        self.overlay = dict()
        self.has_overlays = False
        nsegs = ida_segment.get_segm_qty()
        if nsegs == 0:
            return
        s = ida_segment.getnseg(0)
        start = self.translate_address(s.start_ea)
        self.overlay[start] = False
        for i in range(1, nsegs):
            s = ida_segment.getnseg(i)
            space = self.get_space_name(s.start_ea)
            saddr = self.translate_address(s.start_ea)
            eaddr = self.translate_address(s.end_ea - 1)
            is_overlay = False
            for j in range(i):
                s2 = ida_segment.getnseg(j)
                space2 = self.get_space_name(s2.start_ea)
                if space == space2:
                    start = self.translate_address(s2.start_ea)
                    end = self.translate_address(s2.end_ea - 1)
                    if ((saddr >= start and saddr <= end) or
                            (eaddr >= start and eaddr <= end)):
                        is_overlay = True
                        self.has_overlays = True
                        break
            self.overlay[saddr] = is_overlay

    def get_address_string(self, addr):
        """
        Returns a string representing the address.

        The representation is typically a hex string of the address,
        but may include a segment or space name prefixe based on the
        processor or architecture.

        Args:
            addr: Integer representing a program address.
        """
        temp = "0x%X" % (
            addr - ida_segment.get_segm_base(ida_segment.getseg(addr)))
        space = self.get_space_name(addr)
        if space != None:
            temp = "%s:%04X" % (space,
                                addr - ida_segment.get_segm_base(ida_segment.getseg(addr)))
        else:
            if (ida_idp.ph_get_id() == ida_idp.PLFM_386 and
                    ida_segment.getseg(addr).bitness == 0):
                base = ida_segment.get_segm_para(ida_segment.getseg(addr))
                temp = "%04X:%04X" % (base, addr - (base << 4))
        if ida_idp.ph_get_id() == ida_idp.PLFM_C166:
            temp = "0x%X" % addr
        if self.has_overlays == True and self.is_overlay(addr) == True:
            oname = ida_segment.get_segm_name(ida_segment.getseg(addr))
            if len(oname) > 0:
                temp = oname + "::" + temp
        return temp

    def get_data_value(self, addr):
        """
        Returns the data item value at an address based on its size.

        Args:
            addr: Integer representing a program address.
        """
        size = idc.get_item_size(addr) * self.cbsize
        if size == 1:
            return ida_bytes.get_byte(addr)
        if size == 2:
            return ida_bytes.get_16bit(addr)
        if size == 4:
            return ida_bytes.get_32bit(addr)
        if size == 8:
            return ida_bytes.get_64bit(addr)
        return 0

    def get_datatype(self, addr):
        """
        Returns the datatype at an address.

        The type could be a basic type (byte, word, dword, etc.),
        a structure, an array, a pointer, or a string type.

        Args:
            addr: Integer representing a program address.
        """
        f = idc.get_full_flags(addr)
        t = self.get_type(f)
        if ida_bytes.is_struct(f) == True:
            opndbuf = ida_nalt.opinfo_t()
            opnd = ida_bytes.get_opinfo(opndbuf, addr, 0, f)
            return idc.get_struc_name(opnd.tid)
        if idc.is_strlit(f) == True:
            str_type = idc.get_str_type(addr)
            # print(ida_bytes.print_strlit_type(str_type))
            if str_type == ida_nalt.STRTYPE_TERMCHR:
                return "string"
            if str_type == ida_nalt.STRTYPE_PASCAL:
                return "string1"
            if str_type == ida_nalt.STRTYPE_LEN2:
                return "string2"
            if str_type == ida_nalt.STRTYPE_LEN4:
                return "string4"
            if str_type == ida_nalt.STRTYPE_C_16:
                return "unicode"
            if str_type == ida_nalt.STRTYPE_C_16:
                return "unicode2"
            if str_type == ida_nalt.STRTYPE_C_32:
                return "unicode4"
            return "string"
        if ida_bytes.is_off0(f) == True:
            return "pointer"
        return t

    def get_format(self, flags):
        """
        Returns the display format of a data item based on its flags.

        Args:
            flags: Integer representing IDA item flags

        Returns:
            String representing IDA display format.
        """
        if ida_bytes.is_char0(flags):
            return "char"
        radix = ida_bytes.get_radix(flags, 0)
        if radix == 2:
            return "binary"
        if radix == 8:
            return "octal"
        if radix == 10:
            return "decimal"
        return "hex"  # default

    def get_member_type(self, m):
        """
        Returns the datatype of a structure member.

        Args:
            m: IDA member instance.

        Returns:
            String representing member datatype.
        """
        f = m.flag
        t = self.get_type(f)
        if ida_bytes.is_off0(f) == True:
            t = "pointer"
        if ida_bytes.is_struct(f) == False:
            return t
        s = ida_struct.get_sptr(m)
        if (s == None):
            return t
        sname = idc.get_struc_name(s.id)
        if (sname == None):
            return t
        return sname

    def get_options(self):
        """
        Displays the options menu and retrieves the option settings. 
        """
        fmt = "HELP\n"
        fmt += "XML plugin (Python)\n"
        fmt += "IDA SDK: " + str(IDA_SDK_VERSION) + "\n"
        fmt += "\n"
        fmt += "The XML interface provides a dump of the IDA-Pro database as "
        fmt += "a XML \"PROGRAM\" document. The XML PROGRAM document contains "
        fmt += "information from the idb file in a readable text format, and "
        fmt += "can be viewed with a text editor or web browser.\n\n"
        fmt += "ENDHELP\n"
        fmt += "Export as XML PROGRAM document...."
        fmt += "\n <##Options##Memory Sections:{MemorySections}>"
        fmt += "\n <Memory Content:{MemoryContent}>"
        fmt += "\n <Segment Register Value Ranges:{RegisterValues}>"
        fmt += "\n <Data Types:{DataTypes}>"
        fmt += "\n <Code Blocks:{CodeBlocks}>"
        fmt += "\n <Data Definitions:{DataDefinitions}>"
        fmt += "\n <Comments:{Comments}>"
        fmt += "\n <Entry Points:{EntryPoints}>"
        fmt += "\n <Symbols:{Symbols}>"
        fmt += "\n <Functions:{Functions}>"
        fmt += "\n <Memory References:{MemoryReferences}>"
        fmt += "\n <Stack References:{StackReferences}>"
        fmt += "\n <Manual Instructions/Operands:{Manual}>{cGroup1}>"
        fmt += "\n\n"

        Opts = {'cGroup1': ida_kernwin.Form.ChkGroupControl((
            "MemorySections",
            "MemoryContent",
            "RegisterValues",
            "DataTypes",
            "CodeBlocks",
            "DataDefinitions",
            "Comments",
            "EntryPoints",
            "Symbols",
            "Functions",
            "MemoryReferences",
            "StackReferences",
            "Manual"
        ))}

        self.options = ida_kernwin.Form(fmt, Opts)
        self.options.Compile()

        self.options.MemorySections.checked = True
        self.options.MemoryContent.checked = True
        self.options.DataTypes.checked = True
        self.options.RegisterValues.checked = True
        self.options.CodeBlocks.checked = True
        self.options.DataDefinitions.checked = True
        self.options.Symbols.checked = True
        self.options.EntryPoints.checked = True
        self.options.Functions.checked = True
        self.options.Comments.checked = True
        self.options.MemoryReferences.checked = True
        self.options.StackReferences.checked = False
        self.options.Manual.checked = True

        if (self.autorun == False):
            ok = self.options.Execute()
            if (ok == 0):
                raise Cancelled

    def get_space_name(self, addr):
        """
        Returns the memory space name associated with an address.

        Args:
            addr: Integer representing a program address.

        Returns:
            String containg the memory space name.
            None if single address space architecture.

        Used for Harvard architectures (Intel 8051 and TMS, add others
        as needed). 
        """
        pid = ida_idp.ph_get_id()
        stype = ida_segment.segtype(addr)
        if pid == ida_idp.PLFM_8051:
            if stype == idc.SEG_CODE:
                return "CODE"
            else:
                if stype == idc.SEG_IMEM:
                    iaddr = addr - \
                        ida_segment.get_segm_base(ida_segment.getseg(addr))
                    if iaddr < 0x80:
                        return "INTMEM"
                    else:
                        return "SFR"
                else:
                    return "EXTMEM"
        if pid == ida_idp.PLFM_TMS:
            if stype == idc.SEG_CODE:
                return "CODE"
            else:
                return "DATA"
        return None

    def get_symbol_name(self, ea):
        """
        Returns the symbol name for the address.

        Args:
            ea: Integer representing the symbol address.

        Returns:
            String containing the symbol name.

        The demangled name will be returned if it exists, otherwise the
        displayed name is returned. Spaces (' ') will be replaced with '_'.
        """
        name = ida_name.get_demangled_name(ea, DEMANGLED_FORM,
                                           self.inf.demnames, idc.GN_STRICT)
        if name == None or len(name) == 0 or name == "`string'":
            name = idc.get_name(ea)
        if name != None:
            name = name.replace(" ", "_")
        return name

    def get_type(self, flags):
        """
        Returns a datatype string based on the item flags.

        Args:
            flags: IDA item flags.

        Returns:
            String representing item datatype.
        """
        if (self.cbsize == 2):
            if ida_bytes.is_byte(flags) == True:
                return "word"
            if ida_bytes.is_word(flags) == True:
                return "dword"
        if ida_bytes.is_byte(flags) == True:
            return "byte"
        if ida_bytes.is_word(flags) == True:
            return "word"
        if ida_bytes.is_dword(flags) == True:
            return "dword"
        if ida_bytes.is_qword(flags) == True:
            return "qword"
        if ida_bytes.is_oword(flags) == True:
            return "oword"
        if ida_bytes.is_tbyte(flags) == True:
            return "tbyte"
        if ida_bytes.is_float(flags) == True:
            return "float"
        if ida_bytes.is_double(flags) == True:
            return "double"
        if ida_bytes.is_pack_real(flags) == True:
            return "packed"
        if idc.is_strlit(flags) == True:
            return "ascii"
        if ida_bytes.is_struct(flags) == True:
            return "structure"
        if ida_bytes.is_align(flags) == True:
            return "align"
        return "unknown"

    def is_imm_op(self, addr, op):
        """
        Returns true if instruction operand at address is an immediate value.

        Args:
            addr: Integer representing instruction address.
            op: Integer representing operand index (0-based).

        Returns:
            True if instruction operand at address is an immediate value.
            False otherwise.
        """
        insn = ida_ua.insn_t()
        ida_ua.decode_insn(insn, addr)
        if (insn.ops[op].type == idc.o_imm):
            return True
        return False

    def is_overlay(self, addr):
        """
        Checks if memory block (segment) is an overlay.

        Args:
            addr: Integer representing a program address.

        Returns:
            True if memory block (segment) is an overlay.
        """
        if ida_idp.ph_get_id() == ida_idp.PLFM_C166:
            return False
        s = ida_segment.getseg(addr)
        if s.start_ea in self.overlay:
            return self.overlay[s.start_ea]
        return False

    def is_signed_data(self, flags):
        return (flags & ida_bytes.FF_SIGN) != 0

    def start_element(self, tag, close=False):
        """
        Outputs the start of a new element on a new indented line.

        Args:
            tag: String representing the element tag
            close: Boolean indicating if tag is should be closed.
        """
        if ida_kernwin.user_cancelled() == True:
            raise Cancelled
        self.write_to_xmlfile("\n" + ("    " * self.indent_level) + "<" + tag)
        if (close):
            self.close_tag(True)
        self.update_counter(tag)

    def translate_address(self, addr):
        """
        Returns the translated logical address.

        The logical address is adjusted for the segment base address.
        For 16-bit segmented memory, return the 20-bit address.

        Args:
            addr: Integer representing a program address.

        Returns:
            Integer representing the logical address.
        """
        if self.seg_addr == False:
            return addr - ida_segment.get_segm_base(ida_segment.getseg(addr))
        base = ida_segment.get_segm_para(ida_segment.getseg(addr))
        return (base << 16) + (addr - (base << 4))

    def write_address_attribute(self, name, addr):
        """
        Outputs an address attribute for an element.

        Args:
            name: String representing attribute name.
            addr: Integer representing a program address.
        """
        self.write_attribute(name, self.get_address_string(addr))

    def write_attribute(self, name, value):
        """
        Outputs an attribute (name and value) for an element.

        Args:
            name: String representing attribute name.
            value: String representing attribute value.
        """
        if name == None or value == None:
            return
        if (len(name) == 0) or (len(value) == 0):
            return
        attr = " " + name + '="' + self.check_for_entities(value) + '"'
        self.write_to_xmlfile(attr)

    def write_comment_element(self, name, cmt):
        """
        Outputs the tag and text for a comment element.
        Comment elements can be REGULAR_CMT, REPEATABLE_CMT, or TYPEINFO_CMT.

        Args:
            name: String representing the comment element name.
            cmt: String containing the comment.
        """
        self.start_element(name, True)
        self.write_text(cmt)
        self.end_element(name, False)

    def write_numeric_attribute(self, name, value, base=16, signedhex=False):
        """
        Outputs a numeric value attribute (name and value) for an element.

        Args:
            name: String representing the attribute name.
            value: Integer representing the attribute value.
            base: Integer representing numeric base to use for value.
            signedhex: Boolean indicating if hex representation of
                value is signed.
        """
        if base == 10:
            temp = "%d" % value
        else:
            if signedhex == True and value < 0:
                temp = "-0x%X" % abs(value)
            else:
                temp = "0x%X" % value
        self.write_attribute(name, temp)

    def write_text(self, text):
        """
        Outputs the parsed character text for an element.
        The text is checked for special characters.

        Args:
            text: String representing the element text.
        """
        self.write_to_xmlfile(self.check_for_entities(text))

    def write_to_xmlfile(self, buf):
        """
        Writes the buffer to the XML file.

        Args:
            buf: String containg data to write to XML file.
        """
        self.xmlfile.write(buf)
        self.dbg(buf)

    def write_xml_declaration(self):
        """
        Writes the XML Declarations at the start of the XML file.
        """
        self.dbg("\n")
        xml_declaration = "<?xml version=\"1.0\" standalone=\"yes\"?>"
        xml_declaration += "\n<?program_dtd version=\"1\"?>\n"
        self.write_to_xmlfile(xml_declaration)


class XmlImporter(IdaXml):
    """
    XmlImporter class contains methods to import an XML PROGRAM
        document into IDA.
    """

    def __init__(self, as_plugin, arg=0):
        """
        Initializes the XmlImporter attributes

        Args:
            as_plugin:
            debug: 
        """
        IdaXml.__init__(self, arg)
        self.plugin = as_plugin
        self.timers = dict()
        self.addr_mode = 1
        self.create = True
        self.dataseg = None
        self.deferred = []
        self.callbacks = {
            'start': {
                BOOKMARKS: self.update_import,
                CODE: self.update_import,
                COMMENTS: self.update_import,
                COMPILER: self.import_compiler,
                DATA: self.update_import,
                DATATYPES: self.update_import,
                EQUATES: self.update_import,
                FUNCTIONS: self.update_import,
                INFO_SOURCE: self.import_info_source,
                MARKUP: self.update_import,
                MEMORY_MAP: self.import_memory_map,
                PROCESSOR: self.import_processor,
                PROGRAM: self.import_program,
                PROGRAM_ENTRY_POINTS: self.update_import,
                REGISTER_VALUES: self.update_import,
                SYMBOL_TABLE: self.update_import},
            'end': {
                BOOKMARK: self.import_bookmark,
                CODE_BLOCK: self.import_codeblock,
                COMMENT: self.import_comment,
                DEFINED_DATA: self.import_defined_data,
                DESCRIPTION: self.import_description,
                ENUM: self.import_enum,
                EQUATE_GROUP: self.import_equate_group,
                EQUATE_REFERENCE: self.import_equate_reference,
                FUNCTION: self.import_function,
                FUNCTION_DEF: self.import_function_def,
                MANUAL_INSTRUCTION: self.import_manual_instruction,
                MANUAL_OPERAND: self.import_manual_operand,
                MEMORY_REFERENCE: self.import_memory_reference,
                MEMORY_SECTION: self.import_memory_section,
                PROGRAM_ENTRY_POINT: self.import_program_entry_point,
                REGISTER_VALUE_RANGE: self.import_register_value_range,
                STACK_REFERENCE: self.import_stack_reference,
                STRUCTURE: self.import_structure,
                SYMBOL: self.import_symbol,
                TYPE_DEF: self.import_typedef,
                UNION: self.import_union,
                # end element for elapse time
                BOOKMARKS: self.display_timer,
                CODE: self.display_timer,
                COMMENTS: self.display_timer,
                DATA: self.display_timer,
                DATATYPES: self.process_deferred,
                EQUATES: self.display_timer,
                FUNCTIONS: self.display_timer,
                MARKUP: self.display_timer,
                MEMORY_MAP: self.display_timer,
                PROGRAM: self.display_total_time,
                PROGRAM_ENTRY_POINTS: self.display_timer,
                REGISTER_VALUES: self.display_timer,
                SYMBOL_TABLE: self.display_timer}
        }

    def import_xml(self):
        """
        Imports the XML PROGRAM file into the database.
        """
        global event, element
        self.display_version('Importer' if self.plugin else 'Loader')
        displayMenu = self.autorun == False
        self.get_options(displayMenu)
        if self.plugin:
            self.filename = ida_kernwin.ask_file(0, "*.xml",
                                                 "Enter name of xml file:")
        else:
            self.filename = idc.get_input_file_path()
        if self.filename == None or len(self.filename) == 0:
            return
        idc.msg('\nImporting from: ' + self.filename + '\n')
        if self.plugin == False:
            ida_kernwin.hide_wait_box()
        ida_kernwin.show_wait_box("Importing XML PROGRAM document....")
        n = 0
        for event, element in cElementTree.iterparse(self.filename,
                                                     events=("start", "end")):
            if ida_kernwin.user_cancelled() == True:
                raise Cancelled

            if self.debug == True and event == 'start':
                msg = ''
                if element.tag != None:
                    msg += str(element.tag) + ' '
                if element.attrib != None:
                    msg += str(element.attrib) + ' '
                if element.text != None:
                    msg += str(element.text)
                if len(msg) > 0:
                    idc.msg('\n' + msg)

            if event in self.callbacks:
                if element.tag in self.callbacks[event]:
                    if event == 'start':
                        self.timers[element.tag] = time.clock()
                    self.callbacks[event][element.tag](element)
                    if event == 'end':
                        element.clear()
            if event == 'end':
                n += 1
        end = time.clock()
        ida_kernwin.hide_wait_box()
        self.display_summary('Import' if self.plugin else "Load")
        idc.msg('\nXML Elements parsed: ' + str(n) + '\n\n')
        return 1

    def get_options(self, display):
        """
        Displays the options menu and retrieves the option settings. 
        """
        fmt = "HELP\n"
        fmt += "XML PROGRAM loader/importer plugin (Python)\n"
        fmt += "IDA SDK: " + str(IDA_SDK_VERSION) + "\n\n"
        fmt += "The XML PROGRAM loader loads elements from a "
        fmt += "XML <PROGRAM> document to create an idb database.\n\n"
        fmt += "ENDHELP\n"
        fmt += "Import from XML PROGRAM document...."
        fmt += "\n <##Options##Code Blocks:{CodeBlocks}>"
        fmt += "\n <Entry Points:{EntryPoints}>"
        fmt += "\n <Segment Register Value Ranges:{RegisterValues}>"
        fmt += "\n <Data Types:{DataTypes}>"
        fmt += "\n <Data Definitions:{DataDefinitions}>"
        fmt += "\n <Symbols:{Symbols}>"
        fmt += "\n <Comments:{Comments}>"
        fmt += "\n <Bookmarks:{Bookmarks}>"
        fmt += "\n <Functions:{Functions}>"
        fmt += "\n <Memory References:{MemoryReferences}>"
        fmt += "\n <Equate/Enum References:{EquateReferences}>"
        fmt += "\n <Manual Instructions/Operands:{Manual}>{cGroup1}>"
        fmt += "\n\n"

        Opts = {'cGroup1': ida_kernwin.Form.ChkGroupControl((
            "CodeBlocks",
            "EntryPoints",
            "RegisterValues",
            "DataTypes",
            "DataDefinitions",
            "Symbols",
            "Comments",
            "Bookmarks",
            "Functions",
            "MemoryReferences",
            "EquateReferences",
            "Manual"
        ))}

        self.options = ida_kernwin.Form(fmt, Opts)
        self.options.Compile()

        self.options.CodeBlocks.checked = True
        self.options.EntryPoints.checked = True
        self.options.RegisterValues.checked = True
        self.options.DataTypes.checked = True
        self.options.DataDefinitions.checked = True
        self.options.Symbols.checked = True
        self.options.Functions.checked = True
        self.options.Comments.checked = True
        self.options.Bookmarks.checked = True
        self.options.MemoryReferences.checked = True
        self.options.EquateReferences.checked = True
        self.options.Manual.checked = True

        if display == True:
            ok = self.options.Execute()
            if (ok == 0):
                raise Cancelled

    def display_timer(self, element):
        """
        Displays the elapsed processing time for XML elements.

        Args:
            element: XML element object value containing the element tag.
        """
        if element.tag == MEMORY_MAP and self.plugin:
            return
        if element.tag in self.timers:
            idc.msg('elapsed time: %.4f' %
                    (time.clock() - self.timers[element.tag]))

    def display_total_time(self, element):
        """
        Displays the total processing time.

        Args:
            element: XML element object value (not used).
        """
        TOTAL = 'Total '
        idc.msg('\n%35selapsed time: %.4f' %
                (TOTAL, time.clock() - self.timers[PROGRAM]))

    def get_address(self, element, attr):
        """
        Returns the address value for an element.

        Args:
            element: XML element object.
            attr: String containing the address attribute name.

        Returns:
            Numeric value representing the address.
        """
        addrstr = element.get(attr)
        if '::' in addrstr:
            # overlayed addresses not currently handled
            return BADADDR
        elif ':' in addrstr:
            [segstr, offset_str] = string.split(addrstr, ':')
            offset = int(offset_str, 16)
            if self.is_int(segstr) == True:
                sgmt = int(segstr, 16)
                addr = (sgmt << 4) + offset
            else:
                # multiple address spaces not currently implemented
                addr = BADADDR
            return addr
        else:
            return int(element.get(attr), 16)

    def get_attribute(self, element, attr):
        """
        Returns the attribute value string.

        Args:
            element: XML element object.
            attr: String containing the attribute name.

        Returns:
            String representing the attribute value.
        """
        return element.get(attr)

    def get_attribute_value(self, element, attr):
        """
        Returns the numeric attribute value.

        Args:
            element: XML element object.
            attr: String containing the attribute name.

        Returns:
            Numeric value representing the attribute value.
        """
        val = element.get(attr)
        try:
            if val.upper().startswith('0X') or val.upper().startswith('-0X'):
                return int(val, 16)
            return int(val)
        except:
            idc.msg('\nUnable to decode string as value: ' + val)
            return 0

    def get_cbsize(self):
        """
        Returns the size of the addressable codebyte for the processor.

        Returns:
            Integer representing the number of 8-bit bytes in an
            addressable codebyte.
        """
        return (ida_idp.ph_get_cnbits() + 7) / 8

    def get_datatype_flags(self, datatype, size):
        """
        Returns the flags bitmask for the datatype.

        Args:
            datatype: String representing the datatype.
            size: Integer representing the datatype size.

        Returns:
            Integer representing the bitmask.
        """
        if datatype.lower().startswith("byte"):
            return ida_bytes.byte_flag()
        if datatype.lower().startswith("word"):
            return ida_bytes.word_flag()
        if datatype.lower().startswith("dword"):
            return ida_bytes.dword_flag()
        if datatype.lower().startswith("qword"):
            return ida_bytes.qword_flag()
        if datatype.lower().startswith("oword"):
            return ida_bytes.oword_flag()
        if datatype.lower().startswith("tbyte"):
            return ida_bytes.tbyte_flag()
        if datatype.lower().startswith("float"):
            return ida_bytes.float_flag()
        if datatype.lower().startswith("double"):
            return ida_bytes.double_flag()
        if datatype.lower().startswith("packed"):
            return ida_bytes.packreal_flag()
        if self.is_string_type(datatype):
            return ida_bytes.strlit_flag()
        if self.is_enumeration(datatype):
            return ida_bytes.enum_flag()
        if self.is_structure(datatype):
            return ida_bytes.stru_flag()
        # if size == 4:                               return
        # ida_bytes.dword_flag()
        return 0

    def get_string_type(self, datatype):
        if datatype.lower() == 'mbcstring':
            return ida_nalt.STRTYPE_C_16
        if datatype.lower().find('unicode') != -1:
            if datatype.lower().find('pascal') != -1:
                return ida_nalt.STRTYPE_LEN2_16
            return ida_nalt.STRTYPE_C_16
        if datatype.lower().find('pascal') != -1:
            return ida_nalt.STRTYPE_C_16
        return ida_nalt.STRTYPE_TERMCHR

    def has_attribute(self, element, attr):
        """
        Returns true if the XML element contains the named attribute.

        Args:
            element: XML element object
            attr: String containing name of the attribute

        Returns:
            True if the element contains the named attribute, otherwise False.
        """
        return attr in element.attrib

    def is_enumeration(self, datatype):
        """
        Returns true if datatype is an existing enumeration in the database.

        Args:
            datatype: String representing the datatype.

        Returns:
            True if the datatype is an enumeration in the database,
            otherwise False.
        """
        if ida_enum.get_enum(datatype) == BADNODE:
            return False
        return True

    def is_int(self, s):
        try:
            int(s, 16)
            return True
        except:
            return False

    def is_pointer_type(self, dtype):
        """
        Returns true if the datatype represents a pointer.

        Args:
            dtype: String representing the datatype.

        Returns:
            True if the datatype represents a pointer, otherwise False.
        """
        if dtype.lower().startswith("pointer") or dtype.endswith('*'):
            return True
        return False

    def is_string_type(self, datatype):
        """
        Returns true if the datatype represents a string type.

        Args:
            datatype: String representing the datatype.

        Returns:
            True if the datatype represents a string, otherwise False.
        """
        if datatype.lower().startswith("unicode"):
            return True
        if datatype.lower().startswith("string"):
            return True
        return False

    def is_structure(self, datatype):
        """
        Returns true if the datatype represents a structure in the database.

        Args:
            dtype: String representing the datatype.

        Returns:
            True if the datatype represents an existing structure,
            otherwise False.
        """
        if ida_struct.get_struc_id(datatype) == BADNODE:
            return False
        return True

    def import_address_range(self, address_range):
        """
        Processes ADDRESS_RANGE element.

        Args:
            address_range: XML element object containing start and end address
                attributes for the address range.

        Returns:
            Tuple containing two integers, the start and end address values.
        """
        start = self.get_address(address_range, START)
        end = self.get_address(address_range, END)
        self.update_counter(ADDRESS_RANGE)
        return (start, end)

    def import_bit_mask(self, bitmask, eid):
        """
        Processes a BIT_MASK element as an enum bitmask member.

        Args:
            bitmask: XML element object representing the IDA enum bitmask.
            eid: Integer representing the IDA enum id
        """
        name = self.get_attribute(bitmask, NAME)
        value = self.get_attribute_value(bitmask, VALUE)
        ida_enum.set_bmask_name(eid, value, name)
        cid = ida_enum.get_enum_member_by_name(name)
        self.update_counter(BIT_MASK)
        regcmt = bitmask.find(REGULAR_CMT)
        if regcmt != None:
            ida_enum.set_enum_member_cmt(cid, regcmt.text, False)
            self.update_counter(BIT_MASK + ':' + REGULAR_CMT)
        rptcmt = bitmask.find(REPEATABLE_CMT)
        if rptcmt != None:
            ida_enum.set_enum_member_cmt(cid, rptcmt.txt, True)
            self.update_counter(BIT_MASK + ':' + REPEATABLE_CMT)

    def import_bookmark(self, bookmark):
        """
        Processes a BOOKMARK element.

        Args:
            bookmark: XML element object containing bookmark data.
        """
        if self.options.Bookmarks.checked == False:
            return
        try:
            addr = self.get_address(bookmark, ADDRESS)
            if self.has_attribute(bookmark, TYPE):
                typ = self.get_attribute(bookmark, TYPE)
            category = ''
            if self.has_attribute(bookmark, CATEGORY):
                category = self.get_attribute(bookmark, CATEGORY)
            description = ''
            if self.has_attribute(bookmark, DESCRIPTION):
                description = self.get_attribute(bookmark, DESCRIPTION)
            if idc.is_mapped(addr) == False:
                msg = ("import_bookmark: address %X not enabled in database"
                       % addr)
                print(msg)
                return
            self.update_counter(BOOKMARK)
            for slot in range(ida_moves.MAX_MARK_SLOT):
                ea = idc.get_bookmark(slot)
                if ea == BADADDR:
                    idc.put_bookmark(addr, 0, 0, 0, slot, description)
                    break
        except:
            msg = "** Exception occurred in import_bookmark **"
            print("\n" + msg + "\n", sys.exc_type, sys.exc_value)

    def import_cmts(self, element, sid, typ):
        """
        Processes REGULAR_CMT and REPEATABLE_CMT elements for structures.

        Args:
            element: XML element object containing a REGULAR_CMT or
                REPEATABLE_CMT element
            sid: Integer representing the structure id
            typ: String indicating structure type (STRUCTURE or UNION)
        """
        regcmt = element.find(REGULAR_CMT)
        if regcmt != None:
            ida_struct.set_struc_cmt(sid, regcmt.text, False)
            self.update_counter(typ + ':' + REGULAR_CMT)
        rptcmt = element.find(REPEATABLE_CMT)
        if rptcmt != None:
            ida_struct.set_struc_cmt(sid, rptcmt.text, True)
            self.update_counter(typ + ':' + REPEATABLE_CMT)

    def import_codeblock(self, code_block):
        """
        Processes a CODE_BLOCK element by disassembling the address range.

        Args:
            code_block: XML element containing codeblock start and end
                addresses.
        """
        if self.options.CodeBlocks.checked == False:
            return
        start = self.get_address(code_block, START)
        end = self.get_address(code_block, END)
        ida_bytes.del_items(start, 3, end - start + 1)
        addr = start
        while (addr <= end):
            length = ida_ua.create_insn(addr)
            addr += ida_bytes.get_item_size(addr) * self.get_cbsize()
        self.update_counter(CODE_BLOCK)

    def import_comment(self, comment):
        """
        Processes a COMMENT element by creating the comment at the address.

        Args:
            comment: XML element containing the comment address, type,
                and text.
        """
        if self.options.Comments.checked == False:
            return
        addr = self.get_address(comment, ADDRESS)
        ctype = self.get_attribute(comment, TYPE)
        text = comment.text
        if ctype == 'pre':
            ida_lines.add_extra_cmt(addr, True, text)
        elif ctype == 'end-of-line':
            idc.set_cmt(addr, text, False)
        elif ctype == 'repeatable':
            idc.set_cmt(addr, text, True)
        elif ctype == 'post':
            ida_lines.add_extra_cmt(addr, False, text)
        self.update_counter(COMMENT + ':' + ctype)

    def import_compiler(self, compiler):
        """
        Processes the COMPILER element containing the compiler name.

        Args:
            compiler: XML element containing the compiler name.
        """
        name = self.get_attribute(compiler, NAME)
        self.update_counter(COMPILER)
        if self.plugin:
            return
        comp = idc.COMP_UNK
        if name == "Visual C++":
            comp = ida_typeinf.COMP_MS
        elif name == "Borland C++":
            comp = ida_typeinf.COMP_BC
        elif name == "Watcom C++":
            comp = ida_typeinf.COMP_WATCOM
        elif name == "GNU C++":
            comp = ida_typeinf.COMP_GNU
        elif name == "Visual Age C++":
            comp = ida_typeinf.COMP_VISAGE
        elif name == "Delphi":
            comp = ida_typeinf.COMP_BP
        ida_typeinf.set_compiler_id(comp)

    def import_defined_data(self, defined_data):
        """
        Processes a DEFINED_DATA element by creating a data item at the
            specified address.

        Args:
            defined_data: XML element containing the address and
                datatype information for the data item
        """
        if self.options.DataDefinitions.checked == False:
            return
        addr = self.get_address(defined_data, ADDRESS)
        datatype = self.get_attribute(defined_data, DATATYPE)
        size = self.get_attribute_value(defined_data, SIZE)
        self.update_counter(DEFINED_DATA)
        ti = ida_nalt.opinfo_t()
        if self.is_pointer_type(datatype):
            #idaapi.set_refinfo(ti, 0, 0, 0, REF_OFF32)
            flag = ida_bytes.dword_flag() | idc.FF_0OFF
            #idaapi.set_typeinfo(addr, 0, flag, ti)
        else:
            flag = self.get_datatype_flags(datatype, size)
        if flag == ida_bytes.strlit_flag():
            ida_bytes.create_strlit(addr, size, self.get_string_type(datatype))
        elif flag == ida_bytes.stru_flag():
            idc.create_struct(addr, size, datatype)
        else:
            idc.create_data(addr, flag, size, BADNODE)
        typecmt = defined_data.find(TYPEINFO_CMT)
        if typecmt != None:
            self.update_counter(DEFINED_DATA + ':' + TYPEINFO_CMT)

    def import_description(self, description):
        """
        Processes the DESCRIPTION element.

        Args:
            description: DESCRIPTION XML element.
        """
        self.update_counter(DESCRIPTION)
        # TODO: import_description: decide what to do with DESCRIPTION
        # print(description.text)

    def import_enum(self, enum):
        """
        Processes an ENUM element by creating the enumeration.

        Args:
            enum: XML element containing the enumeration name and
                member data.
        """
        if self.options.DataTypes.checked == False:
            return
        name = self.get_attribute(enum, NAME)
        if self.has_attribute(enum, NAMESPACE):
            namespace = self.get_attribute(enum, NAMESPACE)
        if self.has_attribute(enum, SIZE):
            size = self.get_attribute_value(enum, SIZE)
        eid = idc.add_enum(BADNODE, name,
                           ida_bytes.hex_flag() | ida_bytes.dword_flag())
        self.update_counter(ENUM)
        regcmt = enum.find(REGULAR_CMT)
        if regcmt != None:
            idc.set_enum_cmt(eid, regcmt.text, False)
            self.update_counter(ENUM + ':' + REGULAR_CMT)
        rptcmt = enum.find(REPEATABLE_CMT)
        if rptcmt != None:
            idc.set_enum_cmt(eid, rptcmt.text, True)
            self.update_counter(ENUM + ':' + REPEATABLE_CMT)
        display_settings = enum.find(DISPLAY_SETTINGS)
        if display_settings != None:
            self.update_counter(ENUM + ':' + DISPLAY_SETTINGS)
        enum_entries = enum.findall(ENUM_ENTRY)
        for enum_entry in enum_entries:
            self.import_enum_entry(enum_entry, eid)

    def import_enum_entry(self, enum_entry, eid):
        """
        Processes an ENUM_ENTRY by creating a member in the enumeration.

        Args:
            enum_entry: XML element containing the member name and value.
            eid: Integer representing the id of the enumeration.
        """
        name = self.get_attribute(enum_entry, NAME)
        value = self.get_attribute_value(enum_entry, VALUE)
        ida_enum.add_enum_member(eid, name, value)
        cid = idc.get_enum_member_by_name(name)
        self.update_counter(ENUM_ENTRY)
        regcmt = enum_entry.find(REGULAR_CMT)
        if regcmt != None:
            idc.set_enum_member_cmt(cid, regcmt.text, False)
            self.update_counter(ENUM_ENTRY + ':' + REGULAR_CMT)
        rptcmt = enum_entry.find(REPEATABLE_CMT)
        if rptcmt != None:
            idc.set_enum_member_cmt(cid, rptcmt.text, True)
            self.update_counter(ENUM_ENTRY + ':' + REPEATABLE_CMT)

    def import_equate(self, equate, eid):
        """
        Processes EQUATE element as member of an enumeration.

        Args:
            enum_entry: XML element containing the equate name and value.
            eid: Integer representing the id for the enumeration.
        """
        name = self.get_attribute(equate, NAME)
        value = self.get_attribute_value(equate, VALUE)
        bm = -1
        if self.has_attribute(equate, BIT_MASK):
            bm = self.get_attribute_value(equate, BIT_MASK)
        idc.add_enum_member(eid, name, value, bm)
        cid = idc.get_enum_member_by_name(name)
        self.update_counter(EQUATE)
        regcmt = equate.find(REGULAR_CMT)
        if regcmt != None:
            idc.set_enum_member_cmt(cid, regcmt.text, False)
            self.update_counter(EQUATE + ':' + REGULAR_CMT)
        rptcmt = equate.find(REPEATABLE_CMT)
        if rptcmt != None:
            idc.set_enum_member_cmt(cid, rptcmt.text, True)
            self.update_counter(EQUATE + ':' + REPEATABLE_CMT)

    def import_equate_group(self, equate_group):
        """
        Processes EQUATE_GROUP as IDA enumeration type.

        Args:
            equate_group: XML element containing the group name and
                equate definitions.
        """
        if self.options.DataTypes.checked == False:
            return
        msg = EQUATE_GROUP
        name = ''
        if self.has_attribute(equate_group, NAME):
            name = self.get_attribute(equate_group, NAME)
        bf = ''
        if self.has_attribute(equate_group, BIT_FIELD):
            bf = self.get_attribute(equate_group, BIT_FIELD)
        eid = idc.add_enum(BADADDR, name, ida_bytes.hex_flag())
        idc.set_enum_bf(eid, (bf == 'yes'))
        self.update_counter(EQUATE_GROUP)
        regcmt = equate_group.find(REGULAR_CMT)
        if regcmt != None:
            idc.set_enum_cmt(eid, regcmt.text, False)
            self.update_counter(EQUATE_GROUP + ':' + REGULAR_CMT)
        rptcmt = equate_group.find(REPEATABLE_CMT)
        if rptcmt != None:
            idc.set_enum_cmt(eid, rptcmt.text, True)
            self.update_counter(EQUATE_GROUP + ':' + REPEATABLE_CMT)
        equates = equate_group.findall(EQUATE)
        for equate in equates:
            self.import_equate(equate, eid)
        bit_masks = equate_group.findall(BIT_MASK)
        for bit_mask in bit_masks:
            self.import_bit_mask(bit_mask, eid)

    def import_equate_reference(self, equate_reference):
        if (self.options.DataTypes.checked == False or
                self.options.EquateReferences.checked == False):
            return
        self.update_counter(EQUATE_REFERENCE)
        addr = self.get_address(equate_reference, ADDRESS)
        name = ''
        if self.has_attribute(equate_reference, NAME):
            name = self.get_attribute(equate_reference, NAME)
        if name == '':
            return
        opnd = 0
        if self.has_attribute(equate_reference, OPERAND_INDEX):
            opnd = self.get_attribute_value(equate_reference, OPERAND_INDEX)
        value = None
        if self.has_attribute(equate_reference, VALUE):
            value = self.get_attribute_value(equate_reference, VALUE)
            cid = idc.get_enum_member_by_name(name)
        if cid == BADNODE:
            return
        eid = idc.get_enum_member_enum(cid)
        if eid == BADNODE:
            return
        idc.op_enum(addr, opnd, eid, 0)

    def import_function(self, function):
        """
        Creates a function using the FUNCTION attributes.

        Args:
            function: XML element containing the function address and
                attributes.
        """
        if self.options.Functions.checked == False:
            return
        try:
            entry_point = self.get_address(function, ENTRY_POINT)
            name = ''
            if self.has_attribute(function, NAME):
                name = self.get_attribute(function, NAME)
            libfunc = 'n'
            if self.has_attribute(function, LIBRARY_FUNCTION):
                libfunc = self.get_attribute(function, LIBRARY_FUNCTION)
            if idc.is_mapped(entry_point) == False:
                msg = ("import_function: address %X not enabled in database"
                       % entry_point)
                print(msg)
                return
            idc.add_func(entry_point, BADADDR)
            self.update_counter(FUNCTION)
            func = ida_funcs.get_func(entry_point)
            if libfunc == 'y':
                func.flags |= idc.FUNC_LIB
            ranges = function.findall(ADDRESS_RANGE)
            for addr_range in ranges:
                (start, end) = self.import_address_range(addr_range)
                ida_funcs.append_func_tail(func, start, end)
            # TODO: auto_wait is probably not needed...
            if AUTO_WAIT:
                ida_auto.auto_wait()
            regcmt = function.find(REGULAR_CMT)
            if regcmt != None:
                self.update_counter(FUNCTION + ':' + REGULAR_CMT)
                ida_funcs.set_func_cmt(func, regcmt.text, False)
            rptcmt = function.find(REPEATABLE_CMT)
            if rptcmt != None:
                self.update_counter(FUNCTION + ':' + REPEATABLE_CMT)
                ida_funcs.set_func_cmt(func, rptcmt.text, True)
            typecmt = function.find(TYPEINFO_CMT)
            if typecmt != None:
                self.update_counter(FUNCTION + ':' + TYPEINFO_CMT)
                # TODO: TYPECMTs
                #idc.SetType(entry_point, typecmt.text + ';')
            sf = function.find(STACK_FRAME)
            if sf != None:
                self.import_stack_frame(sf, func)
            register_vars = function.findall(REGISTER_VAR)
            for register_var in register_vars:
                self.import_register_var(register_var, func)
        except:
            msg = "** Exception occurred in import_function **"
            print("\n" + msg + "\n", sys.exc_type, sys.exc_value)

    def import_function_def(self, function_def):
        # import_function_def: NOT IMPLEMENTED
        if self.options.DataTypes.checked == False:
            return
        self.update_counter(FUNCTION_DEF)

    def import_info_source(self, info_source):
        """
        Processes INFO_SOURCE containing information about the
            source of the XML PROGRAM file.

        Args:
            info_source: XML element containing attributes that identify
                the source of the PROGRAM data.
        """
        if self.has_attribute(info_source, TOOL):
            tool = self.get_attribute(info_source, TOOL)
        if self.has_attribute(info_source, USER):
            user = self.get_attribute(info_source, USER)
        if self.has_attribute(info_source, FILE):
            f = self.get_attribute(info_source, FILE)
        if self.has_attribute(info_source, TIMESTAMP):
            ts = self.get_attribute(info_source, TIMESTAMP)
        self.update_counter(INFO_SOURCE)

    def import_manual_instruction(self, manual_instruction):
        """
        Creates a manual instruction.

        Args:
            manual_instruction: XML element containing MANUAL_INSTRUCTION.
        """
        if self.options.Manual.checked == False:
            return
        addr = self.get_address(manual_instruction, ADDRESS)
        idc.set_manual_insn(addr, manual_instruction.text)
        self.update_counter(MANUAL_INSTRUCTION)

    def import_manual_operand(self, manual_operand):
        """
        Creates a manual operand at an address.

        Args:
            manual_operand: MANUAL_OPERAND XML element.
        """
        if self.options.Manual.checked == False:
            return
        addr = self.get_address(manual_operand, ADDRESS)
        op = self.get_attribute_value(manual_operand, OPERAND_INDEX)
        if idc.is_mapped(addr):
            ida_bytes.set_forced_operand(addr, op, manual_operand.text)
            self.update_counter(MANUAL_OPERAND)

    def process_deferred(self, element):
        """
        Processes the list of deferred structure members when the
        DATATYPES end element is encountered.

        Args:
            element: XML end element for DATATYPES 
        """
        for (member, sptr) in self.deferred:
            self.import_member(member, sptr, False)
        self.display_timer(element)

    def import_member(self, member, sptr, defer=True):
        """
        Creates a member for a structure.

        Args:
            member: MEMBER XML element.
            sptr:
            defer:  boolean indicating if processing a member should be
                    deferred when the type is unknown. A member should
                    only be deferred on the first pass, not when processing
                    the deferred list.
        """
        offset = self.get_attribute_value(member, OFFSET)
        datatype = self.get_attribute(member, DATATYPE)
        if self.has_attribute(member, DATATYPE_NAMESPACE):
            dt_namespace = self.get_attribute(member, DATATYPE_NAMESPACE)
        name = ''
        if self.has_attribute(member, NAME):
            name = self.get_attribute(member, NAME)
        size = 0
        if self.has_attribute(member, SIZE):
            size = self.get_attribute_value(member, SIZE)
        ti = ida_nalt.opinfo_t()
        if self.is_pointer_type(datatype):
            flag = ida_bytes.dword_flag() | idc.FF_0OFF
            r = ida_nalt.refinfo_t()
            r.init(ida_nalt.get_reftype_by_size(4) | ida_nalt.REFINFO_NOBASE)
            ti.ri = r
        else:
            flag = self.get_datatype_flags(datatype, size)
        if flag == 0 and defer == True:
            self.deferred.append((member, sptr))
            return
        if flag == ida_bytes.enum_flag():
            t = idc.get_enum(datatype)
            ti.ec.tid = t
            ti.ec.serial = idc.get_enum_idx(t)
        if flag == ida_bytes.stru_flag():
            t = idc.get_struc_id(datatype)
            ti.tid = t
        error = ida_struct.add_struc_member(sptr, name, offset, flag, ti, size)
        mbr = ida_struct.get_member(sptr, offset)
        self.import_member_cmts(member, mbr)
        self.update_counter(MEMBER)

    def import_member_cmts(self, member, mbr):
        """
        Processes REGULAR_CMT and REPEATABLE_CMT elements for members.

        Args:
            element: XML element object containing a REGULAR_CMT or
                REPEATABLE_CMT element
            mbr: Integer representing the member id
        """
        regcmt = member.find(REGULAR_CMT)
        if regcmt != None:
            idc.set_member_cmt(mbr, regcmt.text, False)
            self.update_counter(MEMBER + ':' + REGULAR_CMT)
        rptcmt = member.find(REPEATABLE_CMT)
        if rptcmt != None:
            idc.set_member_cmt(mbr, rptcmt.text, True)
            self.update_counter(MEMBER + ':' + REPEATABLE_CMT)

    def import_members(self, element, sptr):
        """
        Add data members to a structure.

        Args:
            element: STRUCTURE XML element containing MEMBER sub-elements.
            sptr:
        """
        members = element.findall(MEMBER)
        for member in members:
            self.import_member(member, sptr)

    def import_memory_contents(self, memory_contents, start, size):
        """
        Processes MEMORY_CONTENTS to load data for a memory block.

        Args:
            memory_contents: MEMORY_CONTENTS XML element.
        """
        if memory_contents.get(START_ADDR) == None:
            saddr = start
        else:
            saddr = self.get_address(memory_contents, START_ADDR)
        fname = self.get_attribute(memory_contents, FILE_NAME)
        offset = self.get_attribute_value(memory_contents, FILE_OFFSET)
        if memory_contents.get(LENGTH) == None:
            length = size
        else:
            length = self.get_attribute_value(memory_contents, LENGTH)
        #(binfilename, ext) = os.path.splitext(self.filename)
        #binfilename += ".bytes"
        (binfilename, fileext) = os.path.split(self.filename)
        binfilename += "/" + fname
        binfile = ida_idaapi.loader_input_t()
        binfile.open(binfilename)
        binfile.file2base(offset, saddr, saddr + length, False)
        binfile.close()
        self.update_counter(MEMORY_CONTENTS)

    def import_memory_map(self, memory_map):
        """
        Processes the MEMORY_MAP element.

        Args:
            memory_map: MEMORY_MAP XML element.

        MEMORY_MAP is only processed by the IDA loader. It is ignored when
            run as an IDA plugin.
        """
        # import memory sections only when run as loader
        if self.plugin:
            return
        self.update_import(memory_map)

    def import_memory_reference(self, memory_reference):
        """
        Processes the MEMORY_REFERENCE element.
        Currently nothing is done with MEMORY_REFERENCEs.

        Args:
            memory_reference: MEMORY_REFERENCE XML element.
        """
        if self.options.MemoryReferences.checked == False:
            return
        # initialize implied attributes
        user = None
        op = None
        primary = None
        base_addr = None
        addr = self.get_address(memory_reference, ADDRESS)
        if self.has_attribute(memory_reference, OPERAND_INDEX):
            op = self.get_attribute_value(memory_reference, OPERAND_INDEX)
        if self.has_attribute(memory_reference, USER_DEFINED):
            user = self.get_attribute(memory_reference, USER_DEFINED)
        to_addr = self.get_address(memory_reference, TO_ADDRESS)
        if self.has_attribute(memory_reference, BASE_ADDRESS):
            base_addr = self.get_address(memory_reference, BASE_ADDRESS)
        if self.has_attribute(memory_reference, PRIMARY):
            primary = self.get_attribute(memory_reference, PRIMARY)
        self.update_counter(MEMORY_REFERENCE)
        # TODO: import_memory_reference: store refs? maybe only user-defined?
        """
        if user == 'y':
            #print("%08X %08X" % (addr, to_addr), op, primary)
            pass
        """

    def import_memory_section(self, memory_section):
        """
        Creates a memory segment in the database.

        Args:
            memory_section: MEMORY_SECTION XML element.

        MEMORY_SECTION is only processed by the IDA loader. It is ignored
            when run as an IDA plugin.
        """
        # TODO: import_memory_section - handle overlays?
        # import memory sections only when run as loader
        if self.plugin:
            return
        name = self.get_attribute(memory_section, NAME)
        length = self.get_attribute_value(memory_section, LENGTH)

        s = ida_segment.segment_t()
        addrstr = self.get_attribute(memory_section, START_ADDR)
        seg_str = ''
        if '::' in addrstr:
            # overlay - skip for now
            print('  ** Overlayed memory block %s skipped **  ' % name)
            msg = 'Overlayed memory block %s skipped!' % name
            msg += "\n\nXML Import does not currently support"
            msg += "\noverlayed memory blocks."
            idc.warning(msg)
            return
        elif ':' in addrstr:
            [seg_str, offset_str] = string.split(addrstr, ':')
            offset = int(offset_str, 16)
            if self.is_int(seg_str):
                base = int(seg_str, 16)
                sel = ida_segment.setup_selector(base)
                start = self.get_address(memory_section, START_ADDR)
            else:
                raise MultipleAddressSpacesNotSupported
                return
        else:
            sel = ida_segment.allocate_selector(0)
            start = self.get_address(memory_section, START_ADDR)

        s.sel = sel
        s.start_ea = start
        s.end_ea = start + length
        s.bitness = self.addr_mode

        perms = ''
        if self.has_attribute(memory_section, PERMISSIONS):
            perms = self.get_attribute(memory_section, PERMISSIONS)
        s.perm = 0
        if 'r' in perms:
            s.perm |= ida_segment.SEGPERM_READ
        if 'w' in perms:
            s.perm |= ida_segment.SEGPERM_WRITE
        if 'x' in perms:
            s.perm |= ida_segment.SEGPERM_EXEC
        ok = ida_segment.add_segm_ex(s, name, "",
                                     idc.ADDSEG_OR_DIE | idc.ADDSEG_QUIET)
        self.update_counter(MEMORY_SECTION)
        for memory_contents in memory_section.findall(MEMORY_CONTENTS):
            self.import_memory_contents(memory_contents, start, length)

    def import_processor(self, processor):
        """
        Processes the PROCESSOR element.

        Args:
            processor: PROCESSOR XML element.
        """
        name = self.get_attribute(processor, NAME)
        self.update_counter(PROCESSOR)
        if self.plugin:
            return
        address_model = self.get_attribute(processor, ADDRESS_MODEL)
        if address_model != None:
            if str.lower(address_model) == '16-bit':
                self.addr_mode = 0
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_PC_FLAT, 0)
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_64BIT, 0)
            elif str.lower(address_model) == '32-bit':
                self.addr_mode = 1
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_PC_FLAT, 1)
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_64BIT, 0)
            elif str.lower(address_model) == '64-bit':
                self.addr_mode = 2
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_PC_FLAT, 1)
                idc.set_flag(idc.INF_LFLAGS, idc.LFLG_64BIT, 1)

    def import_program(self, program):
        """
        Processes the PROGRAM element.

        Args:
            program: PROGRAM XML element.
        """
        self.update_status(PROGRAM)
        self.update_counter(PROGRAM)
        if self.plugin:
            return
        name = self.get_attribute(program, NAME)
        if self.has_attribute(program, EXE_PATH):
            epath = self.get_attribute(program, EXE_PATH)
            idc.set_root_filename(epath)
        else:
            idc.set_root_filename(name)
        if self.has_attribute(program, EXE_FORMAT):
            eformat = self.get_attribute(program, EXE_FORMAT)
            RootNode = ida_netnode.netnode('Root Node')
            RootNode.supset(ida_nalt.RIDX_FILE_FORMAT_NAME, eformat)
        if self.has_attribute(program, IMAGE_BASE):
            base = self.get_attribute_value(program, IMAGE_BASE)
            ida_nalt.set_imagebase(base)
        if self.has_attribute(program, INPUT_MD5):
            input_md5 = self.get_attribute(program, INPUT_MD5)
            # store original md5 in a special netnode
            md5 = ida_netnode.netnode(INPUT_MD5, len(INPUT_MD5), True)
            md5.supset(ida_nalt.RIDX_MD5, input_md5)

    def import_program_entry_point(self, program_entry_point):
        """
        Defines a program entry point.

        Args:
            program_entry_point: PROGRAM_ENTRY_POINT XML element.
                Contains the entry point address.
        """
        if self.options.EntryPoints.checked == False:
            return
        addr = self.get_address(program_entry_point, ADDRESS)
        idc.add_entry(addr, addr, "", True)
        self.update_counter(PROGRAM_ENTRY_POINT)

    def import_register_value_range(self, register_value_range):
        """
        Defines the address range for a register value.

        Args:
            register_value_range: REGISTER_VALUE_RANGE XML element.
                Contains the register, value, start address and range length.
        """
        if self.options.RegisterValues.checked == False:
            return
        self.update_counter(REGISTER_VALUE_RANGE)
        reg = self.get_attribute(register_value_range, REGISTER)
        if reg == 'cs':
            return
        value = self.get_attribute_value(register_value_range, VALUE)
        addr = self.get_address(register_value_range, START_ADDRESS)
        length = self.get_attribute_value(register_value_range, LENGTH)
        r = ida_idp.str2reg(reg)
        if r >= ida_idp.ph_get_reg_first_sreg() and r <= ida_idp.ph_get_reg_last_sreg():
            ida_segregs.split_sreg_range(addr, r, value, idc.SR_user, True)

    def import_register_var(self, register_var, func):
        """
        Defines a register variable for a function.

        Args:
            register_var: REGISTER_VAR XML element.
                Contains register, variable name, and datatype.
            func: IDA function object
        """
        name = self.get_attribute(register_var, NAME)
        reg = self.get_attribute(register_var, REGISTER)
        if self.has_attribute(register_var, DATATYPE):
            datatype = self.get_attribute(register_var, DATATYPE)
        if self.has_attribute(register_var, DATATYPE_NAMESPACE):
            namespace = self.get_attribute(register_var, DATATYPE_NAMESPACE)
        idc.define_local_var(func.start_ea, func.endEA, reg, name)
        self.update_counter(REGISTER_VAR)

    def import_stack_frame(self, stack_frame, func):
        """
        Defines a stack frame for a function.

        Args:
            stack_frame: STACK_FRAME element with STACK_VAR child elements.
        """
        if self.has_attribute(stack_frame, LOCAL_VAR_SIZE):
            lvsize = self.get_attribute_value(stack_frame, LOCAL_VAR_SIZE)
        if self.has_attribute(stack_frame, PARAM_OFFSET):
            param_offset = self.get_attribute_value(stack_frame, PARAM_OFFSET)
        if self.has_attribute(stack_frame, REGISTER_SAVE_SIZE):
            reg_save_size = self.get_attribute_value(stack_frame,
                                                     REGISTER_SAVE_SIZE)
        if self.has_attribute(stack_frame, RETURN_ADDR_SIZE):
            retaddr_size = self.get_attribute_value(stack_frame,
                                                    RETURN_ADDR_SIZE)
        if self.has_attribute(stack_frame, BYTES_PURGED):
            bytes_purged = self.get_attribute_value(stack_frame, BYTES_PURGED)
        self.update_counter(STACK_FRAME)
        for stack_var in stack_frame.findall(STACK_VAR):
            self.import_stack_var(stack_var, func)

    def import_stack_reference(self, stack_reference):
        # import_stack_reference: NOT IMPLEMENTED
        self.update_counter(STACK_REFERENCE)
        pass

    def import_stack_var(self, stack_var, func):
        """
        Processes STACK_VAR element.

        Args:
            stack_var: STACK_VAR XML element.

        Stack variables are created by IDA's function analysis. 
        Only the STACK_VAR NAME attribute is used to set the name for
        a stack variable at the specified stack/frame offset. 
        """
        spoffset = self.get_attribute_value(stack_var, STACK_PTR_OFFSET)
        datatype = self.get_attribute(stack_var, DATATYPE)
        offset = spoffset + func.frsize + func.frregs
        if self.has_attribute(stack_var, FRAME_PTR_OFFSET):
            fpoffset = self.get_attribute_value(stack_var, FRAME_PTR_OFFSET)
            offset = fpoffset + func.frsize
        name = ''
        if self.has_attribute(stack_var, NAME):
            name = self.get_attribute(stack_var, NAME)
        if self.has_attribute(stack_var, DATATYPE_NAMESPACE):
            namespace = self.get_attribute(stack_var, DATATYPE_NAMESPACE)
        if self.has_attribute(stack_var, SIZE):
            size = self.get_attribute_value(stack_var, SIZE)
        self.update_counter(STACK_VAR)
        sf = ida_frame.get_frame(func)
        if name != '':
            ida_struct.set_member_name(sf, offset, name)

    def import_structure(self, structure):
        """
        Adds a structure.

        Args:
            structure: STRUCTURE XML element.
                Contains the STRUCTURE attributes and child elements.
        """
        if self.options.DataTypes.checked == False:
            return
        name = self.get_attribute(structure, NAME)
        dtyp = idc.get_struc_id(name)
        if dtyp != BADNODE:
            # duplicate name, try adding name space
            if self.has_attribute(structure, NAMESPACE) == False:
                return
            namespace = self.get_attribute(structure, NAMESPACE)
            name = namespace + '__' + name
            name.replace('/', '_')
            name.replace('.', '_')
            dtyp = idc.get_struc_id(name)
            # skip if still duplicate (could add sequence #)
            if dtyp != BADNODE:
                return
        size = 0
        if self.has_attribute(structure, SIZE):
            size = self.get_attribute_value(structure, SIZE)
        if self.has_attribute(structure, VARIABLE_LENGTH):
            vl = self.get_attribute_value(structure, VARIABLE_LENGTH)
            isVariableLength = vl == 'y'
        sid = idc.add_struc(-1, name, 0)
        sptr = ida_struct.get_struc(sid)
        self.update_counter(STRUCTURE)
        self.import_cmts(structure, sid, STRUCTURE)
        self.import_members(structure, sptr)
        if idc.get_struc_size(sptr) < size:
            t = ida_nalt.opinfo_t()
            ida_struct.add_struc_member(
                sptr, "", size - 1, ida_bytes.byte_flag(), t, 1)

    def import_symbol(self, symbol):
        """
        Adds a symbol name at the specified address.

        Args:
            symbol: SYMBOL XML element.
                Contains symbol name and address. Optionally includes
                type and mangled symbol.
        """
        if self.options.Symbols.checked == False:
            return
        addr = self.get_address(symbol, ADDRESS)
        name = self.get_attribute(symbol, NAME)
        if self.has_attribute(symbol, MANGLED):
            name = self.get_attribute(symbol, MANGLED)
        flag = idc.SN_NOWARN
        if self.has_attribute(symbol, TYPE):
            typ = self.get_attribute(symbol, TYPE)
            if typ == 'local':
                flag |= idc.SN_LOCAL
        idc.set_name(addr, name, flag)
        self.update_counter(SYMBOL)

    def import_typedef(self, type_def):
        # import_typedef: NOT IMPLEMENTED
        if self.options.DataTypes.checked == False:
            return
        self.update_counter(TYPE_DEF)

    def import_union(self, union):
        """
        Adds a union datatype.

        Args:
            union: UNION XML element.
                Contains UNION attributes and child elements.
        """
        if self.options.DataTypes.checked == False:
            return
        name = self.get_attribute(union, NAME)
        dtyp = idc.get_struc_id(name)
        if dtyp != BADNODE:
            # duplicate name, try adding name space
            if self.has_attribute(union, NAMESPACE) == False:
                return
            namespace = self.get_attribute(union, NAMESPACE)
            name = namespace + '__' + name
            name.replace('/', '_')
            name.replace('.', '_')
            dtyp = idc.get_struc_id(name)
            # skip if still duplicate (could add sequence #)
            if dtyp != BADNODE:
                return
        size = 0
        if self.has_attribute(union, SIZE):
            size = self.get_attribute_value(union, SIZE)
        sid = idc.add_struc(BADADDR, name, True)
        sptr = ida_struct.get_struc(sid)
        self.update_counter(UNION)
        self.import_cmts(union, sid, UNION)
        self.import_members(union, sptr)
        if idc.get_struc_size(sptr) < size:
            t = ida_nalt.opinfo_t()
            ida_struct.add_struc_member(
                sptr, "", size - 1, ida_bytes.byte_flag(), t, 1)

    def update_import(self, element):
        """
        Update the element counter and processing status.

        Args:
            element: XML element

        This function is used to process certain high-level elements
        (such as COMMENTS, CODE_BLOCKS, SYMBOL_TABLE, FUNCTIONS, etc.)
        that are used to group sub-elements.
        """
        self.update_counter(element.tag)
        self.update_status(element.tag)


# Global constants
# mangled name inhibit flags are not currently exposed in python api
# inhibit flags for symbol names
# DEMANGLE_FORM (MNG_SHORT_FORM | MNG_NOBASEDT | MNG_NOCALLC | MNG_NOCSVOL)
DEMANGLED_FORM = 0x0ea3ffe7
# inhibit flags for typeinfo cmts
# DEMANGLED_TYPEINFO (MNG_LONG_FORM)
DEMANGLED_TYPEINFO = 0x06400007


# Global XML string constants for elements and attributes
ADDRESS = 'ADDRESS'
ADDRESS_MODEL = 'ADDRESS_MODEL'
ADDRESS_RANGE = 'ADDRESS_RANGE'
BASE_ADDRESS = 'BASE_ADDRESS'
BIT_FIELD = 'BIT_FIELD'
BIT_MAPPED = 'BIT_MAPPED'
BIT_MASK = 'BIT_MASK'
BOOKMARK = 'BOOKMARK'
BOOKMARKS = 'BOOKMARKS'
BYTES = 'BYTES'
BYTES_PURGED = 'BYTES_PURGED'
CATEGORY = 'CATEGORY'
CODE = 'CODE'
CODE_BLOCK = 'CODE_BLOCK'
COMMENT = 'COMMENT'
COMMENTS = 'COMMENTS'
COMPILER = 'COMPILER'
DATA = 'DATA'
DATATYPE = 'DATATYPE'
DATATYPES = 'DATATYPES'
DATATYPE_NAMESPACE = 'DATATYPE_NAMESPACE'
DEFINED_DATA = 'DEFINED_DATA'
DESCRIPTION = 'DESCRIPTION'
DISPLAY_SETTINGS = 'DISPLAY_SETTINGS'
END = 'END'
ENDIAN = 'ENDIAN'
ENTRY_POINT = 'ENTRY_POINT'
ENUM = 'ENUM'
ENUM_ENTRY = 'ENUM_ENTRY'
EQUATE = 'EQUATE'
EQUATES = 'EQUATES'
EQUATE_GROUP = 'EQUATE_GROUP'
EQUATE_REFERENCE = 'EQUATE_REFERENCE'
EXE_FORMAT = 'EXE_FORMAT'
EXE_PATH = 'EXE_PATH'
EXT_LIBRARY = 'EXT_LIBRARY'
EXT_LIBRARY_REFERENCE = 'EXT_LIBRARY_REFERENCE'
EXT_LIBRARY_TABLE = 'EXT_LIBRARY_TABLE'
FAMILY = 'FAMILY'
FILE = 'FILE'
FILE_NAME = 'FILE_NAME'
FILE_OFFSET = 'FILE_OFFSET'
FOLDER = 'FOLDER'
FORMAT = 'FORMAT'
FRAGMENT = 'FRAGMENT'
FRAME_PTR_OFFSET = 'FRAME_PTR_OFFSET'
FUNCTION = 'FUNCTION'
FUNCTIONS = 'FUNCTIONS'
FUNCTION_DEF = 'FUNCTION_DEF'
IMAGE_BASE = 'IMAGE_BASE'
INPUT_MD5 = 'INPUT_MD5'
INFO_SOURCE = 'INFO_SOURCE'
LANGUAGE_PROVIDER = 'LANGUAGE_PROVIDER'
LENGTH = 'LENGTH'
LIB_ADDR = 'LIB_ADDR'
LIB_LABEL = 'LIB_LABEL'
LIB_ORDINAL = 'LIB_ORDINAL'
LIB_PROG_NAME = 'LIB_PROG_NAME'
LIBRARY_FUNCTION = 'LIBRARY_FUNCTION'
LOCAL_VAR_SIZE = 'LOCAL_VAR_SIZE'
MANGLED = 'MANGLED'
MANUAL_INSTRUCTION = 'MANUAL_INSTRUCTION'
MANUAL_OPERAND = 'MANUAL_OPERAND'
MARKUP = 'MARKUP'
MEMBER = 'MEMBER'
MEMORY_CONTENTS = 'MEMORY_CONTENTS'
MEMORY_MAP = 'MEMORY_MAP'
MEMORY_REFERENCE = 'MEMORY_REFERENCE'
MEMORY_SECTION = 'MEMORY_SECTION'
NAME = 'NAME'
NAMESPACE = 'NAMESPACE'
OFFSET = 'OFFSET'
OPERAND_INDEX = 'OPERAND_INDEX'
PARAM_OFFSET = 'PARAM_OFFSET'
PATH = 'PATH'
PERMISSIONS = 'PERMISSIONS'
PRIMARY = 'PRIMARY'
PROCESSOR = 'PROCESSOR'
PROGRAM = 'PROGRAM'
PROGRAM_ENTRY_POINT = 'PROGRAM_ENTRY_POINT'
PROGRAM_ENTRY_POINTS = 'PROGRAM_ENTRY_POINTS'
PROGRAM_TREES = 'PROGRAM_TREES'
PROPERTIES = 'PROPERTIES'
PROPERTY = 'PROPERTY'
REGISTER = 'REGISTER'
REGISTER_SAVE_SIZE = 'REGISTER_SAVE_SIZE'
REGISTER_VALUES = 'REGISTER_VALUES'
REGISTER_VALUE_RANGE = 'REGISTER_VALUE_RANGE'
REGISTER_VAR = 'REGISTER_VAR'
REGULAR_CMT = 'REGULAR_CMT'
RELOCATION = 'RELOCATION'
RELOCATION_TABLE = 'RELOCATION_TABLE'
REPEATABLE_CMT = 'REPEATABLE_CMT'
RETURN_ADDR_SIZE = 'RETURN_ADDR_SIZE'
RETURN_TYPE = 'RETURN_TYPE'
SHOW_TERMINATOR = 'SHOW_TERMINATOR'
SIGNED = 'SIGNED'
SIZE = 'SIZE'
SOURCE_ADDRESS = 'SOURCE_ADDRESS'
SOURCE_TYPE = 'SOURCE_TYPE'
STACK_FRAME = 'STACK_FRAME'
STACK_PTR_OFFSET = 'STACK_PTR_OFFSET'
STACK_REFERENCE = 'STACK_REFERENCE'
STACK_VAR = 'STACK_VAR'
START = 'START'
START_ADDR = 'START_ADDR'
START_ADDRESS = 'START_ADDRESS'
STRUCTURE = 'STRUCTURE'
SYMBOL = 'SYMBOL'
SYMBOL_TABLE = 'SYMBOL_TABLE'
TIMESTAMP = 'TIMESTAMP'
TOOL = 'TOOL'
TO_ADDRESS = 'TO_ADDRESS'
TREE = 'TREE'
TYPE = 'TYPE'
TYPEINFO_CMT = 'TYPEINFO_CMT'
TYPE_DEF = 'TYPE_DEF'
UNION = 'UNION'
USER = 'USER'
USER_DEFINED = 'USER_DEFINED'
VALUE = 'VALUE'
VARIABLE_LENGTH = 'VARIABLE_LENGTH'
ZERO_PAD = 'ZERO_PAD'