Jacky Akoka

87 papers A 8B 4C 5Journal 24Unranked 44
YearRankTypeTitle / Venue / Authors
2025 J jnl
Data Knowl. Eng.
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2025 J jnl
Decis. Support Syst.
Jacky Akoka, Isabelle Comyn-Wattiau
2024 conf
INFORSID
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2024 conf
INFORSID
Jacky Akoka, Isabelle Comyn-Wattiau
2024 J jnl
Data Knowl. Eng.
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2023 conf
DESRIST
Jacky Akoka, Isabelle Comyn-Wattiau, Veda C. Storey
2023 J jnl
Decis. Support Syst.
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2023 conf
ER (Companion)
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2022 conf
DESRIST
Nicolas Prat, Jacky Akoka, Isabelle Comyn-Wattiau, Veda C. Storey
2022 conf
INFORSID
Jacky Akoka, Isabelle Comyn-Wattiau
2022 C conf
NLDB
Wissam Mammar Kouadri, Jacky Akoka, Isabelle Comyn-Wattiau, Cédric du Mouza
2021 conf
ER (Workshops)
Jacky Akoka, Isabelle Comyn-Wattiau, Cédric du Mouza, Nicolas Prat
2020 A conf
ER
Jacky Akoka, Isabelle Comyn-Wattiau, Stéphane Lamassé, Cédric du Mouza
2020 J jnl
CoRR
Jacky Akoka, Isabelle Comyn-Wattiau, Stéphane Lamassé, Cédric du Mouza
2019 conf
DESRIST
Jacky Akoka, Isabelle Comyn-Wattiau, Cédric du Mouza, Nicolas Prat
2019 conf
INFORSID
Jacky Akoka, Isabelle Comyn-Wattiau
2019 B conf
SERVICES
Jacky Akoka, Isabelle Comyn-Wattiau
2019 conf
HICSS
Jacky Akoka, Isabelle Comyn-Wattiau, Stéphane Lamassé, Cédric du Mouza
2019 conf
ADBIS (Short Papers and Workshops)
Isabelle Comyn-Wattiau, Jacky Akoka
2018 conf
INFORSID
Jacky Akoka, Nabil Laoufi, Nadira Lammari
2018 J jnl
Enterp. Model. Inf. Syst. Archit.
Jacky Akoka, Isabelle Comyn-Wattiau
2017 conf
ER Workshops
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat
2017 conf
Conceptual Modeling Perspectives
Jacky Akoka, Isabelle Comyn-Wattiau
2017 conf
DESRIST
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat, Veda C. Storey
2017 conf
INFORSID
Jacky Akoka, Isabelle Wattiau
2017 conf
IEEE BigData
Isabelle Comyn-Wattiau, Jacky Akoka
2017 J jnl
Comput. Stand. Interfaces
Jacky Akoka, Isabelle Comyn-Wattiau, Nabil Laoufi
2016 C conf
WETICE
Hanane Ariouat, Awatef Hicheur Cairns, Kamel Barkaoui, Jacky Akoka, Nasser Khelifa
2016 conf
DESRIST
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat
2015 J jnl
J. Manag. Inf. Syst.
Nicolas Prat, Isabelle Comyn-Wattiau, Jacky Akoka
2015 conf
ER Workshops
Jacky Akoka, Isabelle Comyn-Wattiau, Nabil Laoufi
2014 J jnl
Inf. Resour. Manag. J.
Jacky Akoka, Isabelle Comyn-Wattiau, Cédric du Mouza, Hammou Fadili, Nadira Lammari, Elisabeth Métais, Samira Si-Said Cherfi
2014 conf
PACIS
Nicolas Prat, Isabelle Comyn-Wattiau, Jacky Akoka
2014 C conf
SEKE
Olfa Chourabi, Jacky Akoka
2014 J jnl
Ingénierie des Systèmes d Inf.
Jacky Akoka, Isabelle Comyn-Wattiau
2013 conf
ICEIS (2)
Khoutir Bouchbout, Jacky Akoka, Zaia Alimazighi
2012 J jnl
Expert Syst. Appl.
Nicolas Prat, Jacky Akoka, Isabelle Comyn-Wattiau
2012 J jnl
Bus. Process. Manag. J.
Khoutir Bouchbout, Jacky Akoka, Zaia Alimazighi
2012 conf
INFORSID
Manuel Vasquez, Nadira Lammari, Isabelle Comyn-Wattiau, Jacky Akoka
2012 C conf
DOLAP
Nicolas Prat, Imen Megdiche, Jacky Akoka
2012 B conf
RCIS
Nicolas Prat, Jacky Akoka, Isabelle Comyn-Wattiau
2011 conf
SESS@ICSE
Nadira Lammari, Jean-Sylvain Bucumi, Jacky Akoka, Isabelle Comyn-Wattiau
2011 B conf
RCIS
Kashif Mehmood, Samira Si-Said Cherfi, Isabelle Comyn-Wattiau, Jacky Akoka
2011 J jnl
Data Knowl. Eng.
Nicolas Prat, Isabelle Comyn-Wattiau, Jacky Akoka
2011 conf
ICIQ
Samira Si-Said Cherfi, Jacky Akoka, Isabelle Comyn-Wattiau
2011 C conf
SEKE
Nicolas Prat, Jacky Akoka, Isabelle Comyn-Wattiau
2010 conf
ECIS
Jacky Akoka, Isabelle Comyn-Wattiau
2010 conf
SemWiki@ESWC
Lydia Nadia Khelifa, Nadira Lammari, Hammou Fadili, Jacky Akoka
2010 J jnl
Ingénierie des Systèmes d Inf.
Isabelle Comyn-Wattiau, Jacky Akoka, Laure Berti-Équille
2010 conf
ECIS
Nicolas Prat, Isabelle Comyn-Wattiau, Jacky Akoka
2010 conf
DBKDA
Cédric du Mouza, Elisabeth Métais, Nadira Lammari, Jacky Akoka, Tatiana Aubonnet, Isabelle Comyn-Wattiau, Hammou Fadili, Samira Si-Said Cherfi
2009 J jnl
Ingénierie des Systèmes d Inf.
Jacky Akoka, Isabelle Comyn-Wattiau, Samira Si-Said Cherfi
2009 conf
INFORSID
Gwenaël Kersulec, Samira Si-Said Cherfi, Isabelle Comyn-Wattiau, Jacky Akoka
2009 J jnl
Ingénierie des Systèmes d Inf.
Jacky Akoka, Isabelle Comyn-Wattiau
2008 A conf
ER
Samira Si-Said Cherfi, Isabelle Comyn-Wattiau, Jacky Akoka
2008 B conf
RCIS
Jacky Akoka, Isabelle Comyn-Wattiau, Samira Si-Said Cherfi
2007 conf
ICEIS (3)
Jacky Akoka, Laure Berti-Équille, Omar Boucelma, Mokrane Bouzeghoub, Isabelle Comyn-Wattiau, Mireille Cosquer, Virginie Goasdoué-Thion, Zoubida Kedad, Sylvaine Nugier, Verónika Peralta, Samira Si-Said Cherfi
2007 J jnl
Data Knowl. Eng.
Nadira Lammari, Isabelle Comyn-Wattiau, Jacky Akoka
2007 conf
ER (Tutorials, Posters, Panels & Industrial Contributions)
Samira Si-Said Cherfi, Jacky Akoka, Isabelle Comyn-Wattiau
2006 J jnl
Decis. Support Syst.
Nicolas Prat, Jacky Akoka, Isabelle Comyn-Wattiau
2006 conf
AIM Conference
Jacky Akoka, Isabelle Comyn-Wattiau
2006 conf
Active Conceptual Modeling of Learning
Peter P. Chen, Leah Y. Wong, Lois M. L. Delcambre, Jacky Akoka, Arne Sølvberg, Raymond A. Liuzzi
2006 A conf
ER
Samira Si-Said Cherfi, Jacky Akoka, Isabelle Comyn-Wattiau
2005 ed.
ER (Workshops)
Jacky Akoka, Stephen W. Liddle, Il-Yeol Song, Michela Bertolotto, Isabelle Comyn-Wattiau, Samira Si-Said Cherfi, Willem-Jan van den Heuvel, Bernhard Thalheim, Manuel Kolp, Paolo Bresciani, Juan Trujillo, Christian Kop, Heinrich C. Mayr
2004 J jnl
EMISA Forum
Nadira Lammari, Jacky Akoka, Isabelle Comyn-Wattiau
2003 conf
OOIS
Nadira Lammari, Jacky Akoka, Isabelle Comyn-Wattiau
2002 conf
Engineering Information Systems in the Internet Context
Nadira Lammari, Isabelle Comyn-Wattiau, Jacky Akoka
2002 A conf
ER
Samira Si-Said Cherfi, Jacky Akoka, Isabelle Comyn-Wattiau
2002 conf
BDA
Nicolas Prat, Jacky Akoka
2002 conf
BDA
Samira Si-Said Cherfi, Jacky Akoka, Isabelle Comyn-Wattiau
2001 A conf
ER
Jacky Akoka, Isabelle Comyn-Wattiau, Nicolas Prat
2001 J jnl
Ingénierie des Systèmes d Inf.
Jacky Akoka, Isabelle Comyn-Wattiau
2000 conf
INFORSID
Nadira Lammari, Jacky Akoka, Isabelle Comyn-Wattiau
1999 A ed.
ER
Jacky Akoka, Mokrane Bouzeghoub, Isabelle Comyn-Wattiau, Elisabeth Métais
1999 ed.
Conceptual Modeling
Peter P. Chen, Jacky Akoka, Hannu Kangassalo, Bernhard Thalheim
1999 J jnl
Netw. Inf. Syst. J.
Isabelle Comyn-Wattiau, Jacky Akoka
1999 conf
ER (Workshops)
Jacky Akoka, Isabelle Comyn-Wattiau, Nadira Lammari
1999 conf
INFORSID
Jacky Akoka, Isabelle Comyn-Wattiau
1998 conf
BDA
Isabelle Comyn-Wattiau, Jacky Akoka, Zoubida Kedad
1997 conf
Conceptual Modeling
Jacky Akoka
1996 J jnl
Data Knowl. Eng.
Jacky Akoka, Isabelle Comyn-Wattiau
1996 A conf
ER
Isabelle Comyn-Wattiau, Jacky Akoka
1993 A conf
ER
Jacky Akoka, Isabelle Comyn-Wattiau
1993 conf
BDA
Isabelle Comyn-Wattiau, Jacky Akoka
1981 J jnl
Inf. Manag.
Jacky Akoka
1980 conf
DDB
Jacky Akoka
1980 J jnl
IEEE Trans. Computers
Peter P. Chen, Jacky Akoka
redb/extractors/decompiler/_archive/DecompileBinja-archive.py
← Index redb/extractors/decompiler/_archive/DecompileBinja-archive.py python
import hashlib
import inspect
import json
import os
import time
import threading
from datetime import datetime, timezone
from typing import Dict, Any, Optional

from redb.extractors.enum import Tag
from redb.extractors.extractor import Extractor
import magic
import pefile
import ppdeep
import tlsh

# Import our BinjaDecompiler (conditional)
from redb.extractors.decompiler.bninja.decompiler import BinaryNinjaDecompiler

class DecompileBinja(Extractor):
    def __init__(
        self,
        filepath,
        log,
        exporters=None,
        index_prefix=None,
        elastic_index=None,
        known_benign=False,
        known_malicious=False,
        filetype=None,
    ):
        super().__init__(
            filepath,
            log,
            exporters,
            index_prefix,
            elastic_index,
            known_benign,
            known_malicious,
        )
        self.log.debug(inspect.currentframe().f_code.co_name)

        # Check if Binary Ninja is available
       # if not BINARYNINJA_AVAILABLE:
        #     self.log.error("Binary Ninja is not available in this container")
        #    raise ImportError(
        #        "Binary Ninja module not found - not available in feature extraction container"
        #    )
        self.analysis_results = None
        self.binja_decompiler = None
        self.filetype = filetype

        # Convert TIMEOUT to integer with a default of 1200 seconds (20 minutes)
        try:
            self.BINJA_TIMEOUT = int(os.getenv("BINJA_TIMEOUT", "1200"))
        except ValueError:
            self.log.warning(
                "Invalid BINJA_TIMEOUT value, using default of 1200 seconds"
            )
            self.BINJA_TIMEOUT = 1200

        # Convert TIMEOUT to integer with a default of 1200 seconds (20 minutes)
        try:
            self.DECOMPILE_EXTRACTOR_TIMEOUT = int(
                os.getenv("DECOMPILE_EXTRACTOR_TIMEOUT", "2580")
            )
        except ValueError:
            self.log.warning(
                "Invalid DECOMPILE_EXTRACTOR_TIMEOUT value, using default of 2580 seconds"
            )
            self.DECOMPILE_EXTRACTOR_TIMEOUT = 2580

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cleanup_run()

    def calculate_md5(self, input_str):
        """Calculate MD5 hash of a string."""
        return hashlib.md5(input_str.encode("utf-8")).hexdigest()

    def is_dotnet(self):
        """Check if the binary is a .NET assembly.

        Returns:
            bool: True if the file is a .NET assembly, False otherwise
        """
        try:
            if self.filetype == "pebin":
                file_type = magic.from_buffer(self.binary)
                if ".Net" in file_type:
                    return True
                pe = pefile.PE(self.filepath)
                for entry in pe.OPTIONAL_HEADER.DATA_DIRECTORY:
                    # IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR is typically 14
                    if (
                        entry.name == "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"
                        and entry.Size > 0
                    ):
                        return True
                return False
        except AttributeError as e:
            self.log.error(
                f"AttributeError error dotnet file {self.hash.sha256} Full error : {e}"
            )
            return False

    def cleanup_run(self):
        """Clean up after analysis."""
        try:
            # BinaryNinjaDecompiler uses context manager pattern (__enter__/__exit__)
            # Cleanup happens automatically when exiting the 'with' block
            self.binja_decompiler = None

            # Force garbage collection
            import gc

            gc.collect()

        except Exception as e:
            self.log.error(f"Error in cleanup: {e}")

    def analyze_binary(self) -> Optional[Dict[str, Any]]:
        """Run Binary Ninja analysis and return results."""
        self.log.debug("Starting binary analysis")

        try:
            # Use BinaryNinjaDecompiler as a context manager to ensure proper setup/cleanup
            with BinaryNinjaDecompiler(
                filepath=self.filepath,
                timeout=self.BINJA_TIMEOUT,
                log=self.log,
                exporters=self.exporters,
                index_prefix=self.index_prefix,
                filetype=self.filetype,
            ) as decompiler:
                self.binja_decompiler = decompiler

                if decompiler.extract():
                    # Store results before context manager exits
                    results = decompiler.analysis_results
                    return results
                else:
                    self.log.error("BinaryNinjaDecompiler extraction failed")
                    return None

        except Exception as e:
            self.log.error(f"Error in Binary Ninja analysis: {e}")
            import traceback
            self.log.error(f"Traceback: {traceback.format_exc()}")
            return None

        finally:
            self.cleanup_run()

    def extract(self):
        """Extract and process all analysis results."""
        self.log.debug(inspect.currentframe().f_code.co_name)

        # Create a flag to track if extraction completed
        extraction_completed = False
        extraction_result = False
        extraction_error = None

        # Define the extraction process as a separate function
        def do_extraction():
            nonlocal extraction_completed, extraction_result, extraction_error
            try:
                results = self.analyze_binary()
                if not results:
                    extraction_result = False
                else:
                    self.analysis_results = results
                    extraction_result = True
            except Exception as e:
                extraction_error = e
                extraction_result = False
            finally:
                extraction_completed = True

        # Start extraction in a separate thread
        extraction_thread = threading.Thread(target=do_extraction)
        extraction_thread.daemon = True
        extraction_thread.start()

        # Wait for the extraction to complete or timeout
        start_time = time.time()
        while (
            not extraction_completed
            and (time.time() - start_time) < self.DECOMPILE_EXTRACTOR_TIMEOUT
        ):
            time.sleep(1)

        if not extraction_completed:
            self.log.error(
                f"Extraction timed out after {self.DECOMPILE_EXTRACTOR_TIMEOUT} seconds"
            )
            # Force cleanup
            self.cleanup_run()
            return None

        if extraction_error:
            self.log.error(f"Error in extraction: {extraction_error}")
            return None

        # Return the actual analysis results, not just a boolean
        return self.analysis_results if extraction_result else None

    def prepare_export_data(self, exporter_type: str) -> Any:
        """Prepare data for database export."""
        self.log.debug(inspect.currentframe().f_code.co_name)
        if not self.analysis_results:
            return None

        # # Delegate to the BinjaDecompiler for consistent export formatting
        # if self.binja_decompiler:
        #     return self.binja_decompiler.prepare_export_data(exporter_type)
        # else:
        #     self.log.error("BinjaDecompiler not available for export preparation")
        #     return None

        if exporter_type == "ClickHouseExporter":
            now = datetime.now(timezone.utc)

            def prepare_array_field(value, array_type):
                """Helper to prepare array fields with proper null handling"""
                if value is None:
                    return []
                return value

            # Add a helper function to handle empty strings
            def ensure_not_empty(value, default="UNKNOWN"):
                """Ensure a string value is not empty"""
                if value is None or value == "":
                    return default
                return value

            def ssdeep_disassembly(func):
                try:
                    if len(func) > 1:
                        return ppdeep.hash(func)
                    return ""
                except Exception as e:
                    self.log.error(f"Error in disassembly ssdeep hash calculation: {e}")
                    return ""

            def tlsh_disassembly(func):
                try:
                    if len(func) >= 50:
                        return tlsh.hash(func.encode("utf-8"))
                    return ""
                except Exception as e:
                    self.log.error(f"Error in disassembly tlsh hash calculation: {e}")
                    return ""

            return {
                "multi_table": True,
                "decompiled_content": {
                    "table": "code_binja_decompiled_functions_content",
                    "data": [
                        [
                            f["decompiled_function_hash"],
                            f["decompiled_function"],
                            f["function_type"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "decompiled_function_hash",
                        "decompiled_function",
                        "function_type",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "decompiled_refs": {
                    "table": "code_binja_decompiled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            f["decompiled_function_hash"],
                            f["disassembled_function_hash"],  # New linking field
                            f["decompiled_function_name"],
                            f["decompiled_function_prototype"],
                            f["decompiled_function_address"],
                            now,
                        ]
                        for f in self.analysis_results["decompiled"]
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "decompiled_function_hash",
                        "disassembled_function_hash",  # New linking field
                        "decompiled_function_name",
                        "decompiled_function_prototype",
                        "decompiled_function_address",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",  # New linking field
                        "LowCardinality(String)",
                        "LowCardinality(String)",
                        "UInt64",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_content": {
                    "table": "code_binja_disassembled_functions_content",
                    "data": [
                        [
                            f["disassembled_function_hash"],
                            f.get("disassembled_function", ""),
                            f.get("disassembled_function_no_addresses", ""),
                            f.get("function_type", "UNKNOWN"),
                            f.get("instructions_count", 0),
                            prepare_array_field(
                                f.get("instructions_types"), "LowCardinality(String)"
                            ),
                            f.get("control_flow_count", 0),
                            prepare_array_field(
                                f.get("memory_access_pattern"), "LowCardinality(String)"
                            ),
                            prepare_array_field(
                                f.get("register_usage"), "LowCardinality(String)"
                            ),
                            f.get("data_references_count", 0),
                            f.get("max_block_size", 0),
                            f.get("num_calls", 0),
                            f.get("stack_size", 0),
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "disassembled_function_hash",
                        "disassembled_function",
                        "disassembled_function_no_addresses",
                        "function_type",
                        "instructions_count",
                        "instructions_types",
                        "control_flow_count",
                        "memory_access_pattern",
                        "register_usage",
                        "data_references_count",
                        "max_block_size",
                        "num_calls",
                        "stack_size",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "String",
                        "String",
                        "Enum8('USER'=1, 'LIBRARY'=2, 'THUNK'=3, 'EXTERNAL'=4, 'UNKNOWN'=5)",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Array(LowCardinality(String))",
                        "Array(LowCardinality(String))",
                        "UInt32",
                        "Nullable(UInt32)",
                        "Nullable(UInt32)",
                        "Nullable(Int32)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "disassembled_refs": {
                    "table": "code_binja_disassembled_functions_references",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            f["disassembled_function_hash"],
                            f["decompiled_function_hash"],
                            f["disassembled_function_name"],
                            f["disassembled_function_address"],
                            ssdeep_disassembly(
                                f.get("disassembled_function_no_addresses", "")
                            ),
                            tlsh_disassembly(
                                f.get("disassembled_function_no_addresses", "")
                            ),
                            now,
                        ]
                        for f in self.analysis_results["disassembled"]
                    ],
                    "column_names": [
                        "sha256",
                        "sha1",
                        "md5",
                        "disassembled_function_hash",
                        "decompiled_function_hash",
                        "disassembled_function_name",
                        "disassembled_function_address",
                        "ssdeep_disassembly",
                        "tlsh_disassembly",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "FixedString(40)",
                        "FixedString(32)",
                        "FixedString(64)",
                        "Nullable(FixedString(64))",
                        "LowCardinality(String)",
                        "UInt64",
                        "Nullable(String)",
                        "Nullable(FixedString(72))",
                        "DateTime64(3, 'UTC')",
                    ],
                },
                "cfg_blocks": {
                    "table": "code_binja_cfg_blocks",
                    "data": [
                        [
                            b["block_id"],
                            self.analysis_results["sha256"],
                            self.analysis_results["sha1"],
                            self.analysis_results["md5"],
                            b["function_address"],
                            b["block_start_address"],
                            b["block_end_address"],
                            b["block_size"],
                            b["instructions_count"],
                            b["block_instructions"],  # MD5 hash of instructions
                            b["predecessor_blocks"],
                            b["successor_blocks"],
                            b["depth"],
                            b["position"],
                            b["branch_type"],
                            b["block_type"],
                            b["flags"],
                            b["dominators"],
                            b["post_dominators"],
                            now,
                        ]
                        # Flatten: iterate through all functions, then all blocks in each function
                        for func_cfg in self.analysis_results["cfg"]
                        if func_cfg is not None  # Handle None from failed extractions
                        for b in func_cfg["blocks"]
                    ],
                    "column_names": [
                        "block_id",
                        "sha256",
                        "sha1",
                        "md5",
                        "function_address",
                        "block_start_address",
                        "block_end_address",
                        "block_size",
                        "instructions_count",
                        "block_instructions_hash",
                        "predecessor_blocks",
                        "successor_blocks",
                        "depth",
                        "position",
                        "branch_type",
                        "block_type",
                        "flags",
                        "dominators",
                        "post_dominators",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",  # block_id (SHA256)
                        "FixedString(64)",  # sha256
                        "FixedString(40)",  # sha1
                        "FixedString(32)",  # md5
                        "UInt64",  # function_address
                        "UInt64",  # block_start_address
                        "UInt64",  # block_end_address
                        "UInt32",  # block_size
                        "UInt16",  # instructions_count
                        "FixedString(32)",  # block_instructions_hash (MD5)
                        "Array(UInt64)",  # predecessor_blocks
                        "Array(UInt64)",  # successor_blocks
                        "UInt16",  # depth
                        "UInt16",  # position
                        "Enum8('DIRECT'=1, 'CONDITIONAL'=2, 'CALL'=3, 'RETURN'=4, 'FALLTHROUGH'=5, 'INDIRECT'=6, 'UNKNOWN'=7)",  # branch_type
                        "Enum8('CODE'=1, 'DATA'=2, 'THUNK'=3)",  # block_type
                        "Array(String)",  # flags (EntryBlock, ExitBlock, LoopBlock)
                        "Array(UInt16)",  # dominators
                        "Array(UInt16)",  # post_dominators
                        "DateTime64(3, 'UTC')",  # analysis_date
                    ],
                },
                "function_analysis_errors": {
                    "table": "function_analysis_errors_binja",
                    "data": [
                        [
                            self.analysis_results["sha256"],
                            f["function_name"],
                            f["function_address"],
                            f.get("error_location", "unknown"),
                            f.get("error_message", ""),
                            f.get("error_details", ""),
                            f.get("error_type", "unknown"),
                            self.calculate_md5(
                                f"{f['error_message']}{f['function_name']}{f['function_address']}{f['error_location']}"
                            ),
                            "new",
                            now,
                        ]
                        for f in self.analysis_results["errors"]
                    ],
                    "column_names": [
                        "sha256",
                        "function_name",
                        "function_address",
                        "error_location",
                        "error_message",
                        "error_details",
                        "error_type",
                        "error_hash",
                        "status",
                        "analysis_date",
                    ],
                    "column_type_names": [
                        "FixedString(64)",
                        "Nullable(String)",
                        "UInt64",
                        "LowCardinality(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "Nullable(String)",
                        "FixedString(32)",
                        "Enum8('new'=1, 'investigating'=2, 'fixed'=3, 'wontfix'=4)",
                        "DateTime64(3, 'UTC')",
                    ],
                },
            }

    def tag(self) -> str:
        """Return the tag for this extractor."""
        return Tag.DECOMPILED.value

    def get_clickhouse_table(self) -> str:
        """Not used directly as we're handling multiple tables."""
        pass


if __name__ == "__main__":
    # Setup basic logging
    import logging

    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger("DecompileBinja")

    # Parse command line arguments
    import argparse

    parser = argparse.ArgumentParser(description="Binary Ninja Decompiler Wrapper")
    parser.add_argument("filepath", help="Path to the binary file to analyze")
    parser.add_argument(
        "--output", "-o", help="Output JSON file path (default: stdout)"
    )
    parser.add_argument(
        "--timeout",
        "-t",
        type=int,
        default=1200,
        help="Analysis timeout in seconds (default: 1200)",
    )
    args = parser.parse_args()

    # Create and run the extractor
    with DecompileBinja(args.filepath, logger) as extractor:
        success = extractor.extract()

        if not success:
            logger.error("Analysis failed")
            exit(1)

        # Output results
        if args.output:
            with open(args.output, "w") as f:
                json.dump(extractor.analysis_results, f)
            logger.info(f"Results written to {args.output}")
        else:
            print(json.dumps(extractor.analysis_results))