User: Review the python code, what does the code do?
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: The provided Python code is part of an automated system designed for binary decompilation, validation, verification, and C++ code generation. Here's a high-level overview of what each component does:

1. **Main Program (`main.py`)**:
   - Initializes by gathering metadata about a binary file (`Civ5XP`).
   - Creates an output directory for C++ files if it doesn't exist.
   - Executes the `decompile_functions` function to decompile the binary file and store its functions.
   - Validates each decompiled function against a predefined schema.
   - Saves valid functions to a database.
   - Verifies decompiled functions by comparing with the binary code.
   - Executes a feedback loop to refine decompilation and verification results.
   - Generates C++ source and header files from the database and creates a CMake build configuration.
   - Closes the database connection.

2. **Configuration (`config.py`)**:
   - Contains configuration settings such as paths for binary and output files, logging, and C++ generation settings.
   - Configures the language model for generating C++ code from pseudo-C code.

3. **Decompilation Utilities (`decompilation.py`)**:
   - Uses the `pyhidra` library to open and analyze a binary file.
   - Decompiles functions from this binary and attempts to convert them into pseudo-C code.
   - Utilizes `generate_cpp_code` to translate pseudo-C into C++.
   - Validates each function's metadata and stores it in a database if valid.
   - Calls the `verify_function` method to ensure the generated C++ code matches the original binary’s behavior.

4. **Verification (`verification.py`)**:
   - Uses `angr` for static binary analysis to create a control flow graph from the binary.
   - Compares the generated LLVM IR from the C++ code with the analyzed binary.
   - Validates that function addresses from LLVM IR match those found in the binary.

5. **Validation (`validation.py`)**:
   - Validates function metadata using a JSON schema to ensure it matches expected data structures.
   - Logs any validation errors.

6. **Database Utilities (`db_utils.py`)**:
   - Defines and manages several SQLite tables to store function information, decompilation outputs, C++ generation data, and verification feedback.
   - Provides utilities to save, fetch, and update database records.

7. **Feedback Loop (`feedback_loop.py`)**:
   - Logs discrepancies found during verification, updates database with feedback, and refines the input prompts.

8. **Project Info (`project_info.py`)**:
   - Collects metadata about the project using `pyhidra`, such as function counts, memory blocks, symbols, etc.
   - Also checks for missing required libraries and logs this information.

9. **C++ Code Generation (`cpp_generator.py`)**:
   - Uses a language model (`ollama/qwen2.5:14b`) to translate pseudo-C code into structured C++.
   - Conducts a consistency check between pseudo-C and generated C++ and triggers feedback if necessary.

10. **File Structuring (`generate_file_structure.py`)**:
    - Retrieves verified functions from the database and organizes them into header and source files.
    - Generates a CMake file to facilitate building the decompiled C++ project. 

Overall, this code appears to be an integrated system for decompiling a binary, generating equivalent C++ code, and ensuring its correctness through validation and verification.
User: similarity_analysis.py   # New: Calculates similarity ratios and graph-based comparisons for validation
similarity_analysis.py utility for additional validation techniques, such as similarity ratios and graph-based comparisons
Implement the new similarity_analysis.py within the project to ensure the automatic bruteforcing reverse engineering to gain the C++ code improve in the loop and doesn't degrade or loop for no benefit.
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To incorporate the `similarity_analysis.py` utility into your existing project for further validation techniques using similarity ratios and graph-based comparisons, you will need to adjust the project structure a bit. Here's a general plan on how to do that:

1. **Create `similarity_analysis.py`**:
   Implement the logic for calculating similarity ratios and performing graph-based comparisons in a new utility.

```python
# utils/similarity_analysis.py

import logging
from difflib import SequenceMatcher
import networkx as nx  # Example: NetworkX for graph-based comparisons

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def graph_based_comparison(graph_a, graph_b):
    """Compare two graphs and return a similarity measure."""
    # Example: Using NetworkX graph isomorphism, you might need a more 
    # sophisticated comparison based on your requirement.
    matcher = nx.isomorphism.GraphMatcher(graph_a, graph_b)
    is_isomorphic = matcher.is_isomorphic()
    return is_isomorphic

def run_analysis_func(func_cpp, original_cpp):
    """Run similar analysis on a decompiled function versus original code."""
    # Example: Calculate textual similarity
    similarity_score = calculate_similarity(func_cpp, original_cpp)
    
    # Example: Compare control flow graph or dependency graph structure
    func_graph = nx.DiGraph()  # An example graph representation
    original_graph = nx.DiGraph()  # An example graph representation
    graph_similarity = graph_based_comparison(func_graph, original_graph)

    return {"text_similarity": similarity_score, "graph_similarity": graph_similarity}
```

2. **Integrate with Main Process**:
   Update the necessary functions to call the `run_analysis_func`.

```python
# main.py
from utils.similarity_analysis import run_analysis_func

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            analysis_results = run_analysis_func(func_data['cpp_code'], func_data['original_code'])  # Assuming original code is available
            if analysis_results['text_similarity'] > 0.8 and analysis_results['graph_similarity']:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'.")
        
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```

3. **Update `generate_cpp_code` to Incorporate Similarity Analysis**:

Ensure the C++ generation function checks similarity, which could now use the enhanced `run_analysis_func` functionality.

```python
# Inside utils/cpp_generator.py
from utils.similarity_analysis import run_analysis_func

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Your existing code
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')

        # New similarity check using similarity analysis
        analysis_results = run_analysis_func(pseudo_c_code, cpp_code)
        if not analysis_results['text_similarity'] > 0.9 or not analysis_results['graph_similarity']:
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})

        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```

4. **Incorporate Graph-Based Analysis Where Appropriate**:
   Throughout the system, whether validating or verifying, include graph-based techniques to ensure that logic structures (like control flows) match between original and decompiled code.

This outline illustrates how you can start integrating the new `similarity_analysis.py` functionality into your overall decompilation and code generation pipeline. Ensure usage adapts appropriately to real data structures (like constructing graphs based on actual parsing), adjust thresholds for similarity checks based on testing, and feel free to expand the analysis sophistication to suit your project's needs.
User: Let's retry, the metrics are required to check how close we are with C++ generation and the loop feedback for the entire process to automatically bruteforce reverse engineer.
```
Diffing Assembler: Comparing the assembler output of generated C++ against the original binary ensures that the behavior matches at a low level, providing a direct validation check.
Diffing Control Flow Graphs (CFG): Control flow graphs represent function execution paths. Matching these between the decompiled output and the original binary will highlight any missing or extraneous branches, revealing structural inconsistencies.
Similarity Ratio Calculation: Calculating similarity ratios between functions, both at the assembly and C++ levels, can provide an automated metric for assessing whether the LLM-generated code resembles the expected output.
```
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To implement automated brute-force reverse engineering for C++ code generation with validation and feedback loops, we'll introduce more robust metrics and analyses, such as diffing assembler outputs, comparing control flow graphs (CFGs), and calculating similarity ratios. This approach will help ensure that the generated C++ code is as close to the original binary as possible.

Here's how you can enhance your code using these techniques:

1. **Implement `similarity_analysis.py`**:

```python
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        # Compile generated C++ to binary
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)

        # Use objdump or similar to get assembler output
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()

        # Calculate similarity
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    """Compare control flow graphs from original binary and generated code."""
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        
        # Generate CFG for the C++ code (simplified with NetworkX for demo)
        cpp_graph = nx.DiGraph()
        # Add nodes/edges based on parsed C++ code
        
        # Compare with original CFG
        # This is a placeholder; you'd actually need to extract the CFG through parsing tools or similar
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_data, cpp_code_path):
    """Run similarity and CFG analysis."""
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])
    return asm_similarity, cfg_similarity
```

2. **Integrate the Analysis into the Main Process**:

Update the main process to utilize your new `similarity_analysis.py` module.

```python
# main.py

from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_data, cpp_code_path)

            # Save if both assembler and CFG similarities look good enough
            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```

3. **Feedback Loop and Refinement**:

Enhance the feedback loop to adjust prompts and refine C++ generation based on analysis results:

```python
# utils/feedback_loop.py

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```

By introducing these new metrics and analyses, you can iteratively improve the generated C++ code quality, ensuring it closely mirrors the original binary's functionality, and facilitate automated brute-force refinement in your reverse engineering workflow.
User: How are we keeping track of the similarity for each of the functions as the loop automatically brute-forces the reverse engineering for C++ code? target binary -> ghidra decompile (inc assembly) -> llm + ghidra decompile -> cpp generator -> assembly match check to original etc...
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_data, cpp_code_path)

            # Save if both assembler and CFG similarities look good enough
            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        # Compile generated C++ to binary
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)

        # Use objdump or similar to get assembler output
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()

        # Calculate similarity
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    """Compare control flow graphs from original binary and generated code."""
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        
        # Generate CFG for the C++ code (simplified with NetworkX for demo)
        cpp_graph = nx.DiGraph()
        # Add nodes/edges based on parsed C++ code
        
        # Compare with original CFG
        # This is a placeholder; you'd actually need to extract the CFG through parsing tools or similar
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_data, cpp_code_path):
    """Run similarity and CFG analysis."""
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])
    return asm_similarity, cfg_similarity
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To keep track of the similarity for each function as the loop automatically brute-forces the reverse engineering process for C++ code, you can implement a mechanism to log the similarity metrics and continuously refine the output. This involves enhancing your existing workflow to log these metrics, save them for each function, and incorporate them into the feedback loop.

Here's how you can enhance your implementation:

1. **Log Similarity Metrics**:

   Use `similarity_analysis.py` to calculate and log the similarity metrics (both assembly and control flow graph similarities). Ensure the results are stored in the database for each function to keep a record of the evolution of the similarity.

```python
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr
from utils.db_utils import save_similarity_metrics

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        cpp_graph = nx.DiGraph()
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_name, func_data, cpp_code_path):
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])

    save_similarity_metrics(func_name, asm_similarity, cfg_similarity)
    
    return asm_similarity, cfg_similarity
```

2. **Database Utilities**:

   Modify `db_utils.py` to include a function for saving similarity metrics.

```python
# utils/db_utils.py

def save_similarity_metrics(func_name, asm_similarity, cfg_similarity):
    try:
        cursor.execute('''
            INSERT INTO similarity_metrics (name, asm_similarity, cfg_similarity, timestamp)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (func_name, asm_similarity, cfg_similarity))
        conn.commit()
    except sqlite3.Error as e:
        logging.error(f"Error saving similarity metrics for '{func_name}': {e}")
```

3. **Integrate Logging and Feedback in Main Process**:

Update `main.py` to handle logging and triggering feedback loops based on similarity results.

```python
# main.py

from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```

By continuously logging and evaluating these similarity metrics, you can refine your feedback loop to improve C++ code generation over each iteration of brute-forcing the reverse engineering process. This allows the system to gradually close the gap between generated and original binary performances.
User: Update the framework:
```
binary_decompilation_project/
├── main.py                     # Main orchestration script for running the decompilation pipeline
├── config.py                   # Configuration for API URLs, model settings, and debug mode
├── requirements.txt            # Python dependencies
├── logs/
│   └── debug.log               # Log file for detailed debug output
├── schemas/
│   └── function_schema.json    # JSON schema for validating function metadata
├── utils/                      # Utility scripts for different stages of the pipeline
│   ├── decompilation.py        # Main script for Ghidra-based decompilation and initial C++ generation with LLM
│   ├── validation.py           # Validates function metadata against JSONSchema
│   ├── verification.py         # Verifies generated C++ code using llvmlite and angr
│   ├── feedback_loop.py        # Implements an iterative feedback loop for function improvement
│   ├── db_utils.py             # Handles database interactions for storing function metadata
│   ├── project_info.py         # Gathers project metadata from Ghidra and external libraries
│   ├── cpp_generator.py        # Generates C++ code and function signatures from pseudo-C code
│   └── generate_file_structure.py  # Generates structured C++ header and source files from verified functions in the database
└── output/
    ├── include/                # Directory for generated C++ header files
    ├── src/                    # Directory for generated C++ source files
    └── CMakeLists.txt          # CMake configuration file for building the generated C++ code
```
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()
  
def save_similarity_metrics(func_name, asm_similarity, cfg_similarity):
    try:
        cursor.execute('''
            INSERT INTO similarity_metrics (name, asm_similarity, cfg_similarity, timestamp)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (func_name, asm_similarity, cfg_similarity))
        conn.commit()
    except sqlite3.Error as e:
        logging.error(f"Error saving similarity metrics for '{func_name}': {e}")

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr
from utils.db_utils import save_similarity_metrics

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        cpp_graph = nx.DiGraph()
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_name, func_data, cpp_code_path):
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])

    save_similarity_metrics(func_name, asm_similarity, cfg_similarity)
    
    return asm_similarity, cfg_similarity
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To update the framework as per your specifications, we'll need to make some adjustments and improvements. Here's how the updated project structure and files can look:

### Project Structure
```plaintext
binary_decompilation_project/
├── main.py                     # Main orchestration script for running the decompilation pipeline
├── config.py                   # Configuration for API URLs, model settings, and debug mode
├── requirements.txt            # Python dependencies
├── logs/
│   └── debug.log               # Log file for detailed debug output
├── schemas/
│   └── function_schema.json    # JSON schema for validating function metadata
├── utils/                      # Utility scripts for different stages of the pipeline
│   ├── decompilation.py        # Main script for Ghidra-based decompilation and initial C++ generation with LLM
│   ├── validation.py           # Validates function metadata against JSONSchema
│   ├── verification.py         # Verifies generated C++ code using llvmlite and angr
│   ├── feedback_loop.py        # Implements an iterative feedback loop for function improvement
│   ├── db_utils.py             # Handles database interactions for storing function metadata
│   ├── project_info.py         # Gathers project metadata from Ghidra and external libraries
│   ├── cpp_generator.py        # Generates C++ code and function signatures from pseudo-C code
│   ├── generate_file_structure.py  # Generates structured C++ header and source files from verified functions in the database
│   └── similarity_analysis.py  # Analyzes similarity through assembly and CFG comparisons
└── output/
    ├── include/                # Directory for generated C++ header files
    ├── src/                    # Directory for generated C++ source files
    └── CMakeLists.txt          # CMake configuration file for building the generated C++ code
```

### Key Files

#### `main.py`
```python
import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to [aborted]
User: Lets create a mock binary to test the entire process chain and record results to figure out what needs to fixed and improved, e.g. better/more llm json mode tool prompts (maybe in future the llm is asked to create its own prompts) and better use of the other tooling to improve step processing and outputs.
Could we use cmake to test for installed compilers and build a complex C++ hello world app (with heaps of functions) using the multiple compilers in different ways (clang, msvc, optismisation levels, debug or release builds, stripped or unstripped) to then run through the entire process using them as target binaries eventually checking the actual C++ source at the end to confirm in what steps in the process chain do we need fix or improve. The debug and output could also be feed to llm to help figure out the problems and potential fixes?
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()
  
def save_similarity_metrics(func_name, asm_similarity, cfg_similarity):
    try:
        cursor.execute('''
            INSERT INTO similarity_metrics (name, asm_similarity, cfg_similarity, timestamp)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (func_name, asm_similarity, cfg_similarity))
        conn.commit()
    except sqlite3.Error as e:
        logging.error(f"Error saving similarity metrics for '{func_name}': {e}")

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr
from utils.db_utils import save_similarity_metrics

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        cpp_graph = nx.DiGraph()
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_name, func_data, cpp_code_path):
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])

    save_similarity_metrics(func_name, asm_similarity, cfg_similarity)
    
    return asm_similarity, cfg_similarity
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To set up a testing framework that compiles a complex C++ "Hello World" application using multiple compilers and configurations, and then runs it through your decompilation and verification process, we can make use of CMake scripts. These scripts will automate the compilation for different compilers and settings such as optimization levels, debug vs. release builds, and the inclusion of debug symbols. 

Here’s how you can achieve this:

### 1. Write a C++ Hello World Application

Create a complex `hello_world.cpp` file with multiple functions and classes.

```cpp
// hello_world.cpp

#include <iostream>
#include <string>

class Greeter {
public:
    Greeter(const std::string& name) : name(name) {}
    void greet() const {
        std::cout << "Hello, " << name << "!" << std::endl;
    }
private:
    std::string name;
};

int add(int a, int b) {
    return a + b;
}

void print_sum(int a, int b) {
    std::cout << "The sum of " << a << " and " << b << " is " << add(a, b) << std::endl;
}

int main() {
    Greeter greeter("World");
    greeter.greet();
    print_sum(3, 4);
    return 0;
}
```

### 2. Create a CMakeLists.txt File

Set up `CMakeLists.txt` to build your application using different compilers and configurations.

```cmake
# CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(ComplexHelloWorld)

add_executable(hello_world hello_world.cpp)

# Set default build type to Release
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

# Compiler flags for different configurations
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

# Allow user to specify preferred compiler with -DCMAKE_CXX_COMPILER
# Example: cmake .. -DCMAKE_CXX_COMPILER=g++

# Function to test multiple configurations
add_custom_target(test_all_builds
    COMMAND ${CMAKE_COMMAND} ..
    && cmake --build . --config Debug
    && cmake --build . --config Release
    && cmake --build . --config RelWithDebInfo
)
```

### 3. Compile the Application Using Multiple Compilers and Configurations

You can automate this using a shell script or by running the commands manually. Here's a shell script example:

```bash
#!/bin/bash

set -e

BUILD_DIR="build"
COMPILERS=("g++" "clang++")
BUILD_TYPES=("Debug" "Release" "RelWithDebInfo")

# Create build directory
mkdir -p $BUILD_DIR
cd $BUILD_DIR

for COMPILER in "${COMPILERS[@]}"; do
    for BUILD_TYPE in "${BUILD_TYPES[@]}"; do
        echo "Building with $COMPILER: $BUILD_TYPE..."
        cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_COMPILER=$COMPILER
        cmake --build .
    done
done

cd ..
```

This script compiles the `hello_world` executable for both `g++` and `clang++` using different build configurations (Debug, Release, and RelWithDebInfo).

### 4. Run the Generated Binaries Through Your Decompilation Process

Modify your script to target the generated binaries:

- **Run decompilation**: Update your decompilation pipeline to target each binary.
- **Log results**: Collect and log the results of decompilation, comparison, and verification.
- **Automate improvements**: Utilize the generated logs and metrics to improve LLM prompts and decompilation accuracy.

Here's an example using your Python script setup:

```python
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def run_with_binary(binary_path):
    BINARY_PATH = binary_path
    # Follow the usual steps
    gather_project_metadata(BINARY_PATH)
    decompiled_functions = decompile_functions(BINARY_PATH)
    
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

def main():
    # Assume binaries are in a 'binaries' directory
    binaries = [os.path.join("binaries", f) for f in os.listdir("binaries") if os.path.isfile(os.path.join("binaries", f))]
    for binary in binaries:
        run_with_binary(binary)

if __name__ == "__main__":
    main()
```

This setup assumes that the compiled binaries are stored in a `binaries` directory and iterates over each binary to run it through your existing framework. It allows you to test how each step in the process works, identify issues, and refine the decompilation and analysis pipelines as needed.
User: We are on Windows so may have some Visual Studio toolkits and llvm so create batch to check system for compilers for the cmake files. Update the structure to add testapp src folder and option to the main.py to run with the compiled testapp binary.
Lets create a mock binary to test the entire process chain and record results to figure out what needs to fixed and improved, e.g. better/more llm json mode tool prompts (maybe in future the llm is asked to create its own prompts) and better use of the other tooling to improve step processing and outputs.
Could we use cmake to test for installed compilers and build a complex C++ hello world app (with heaps of functions) using the multiple compilers in different ways (clang, msvc, optismisation levels, debug or release builds, stripped or unstripped) to then run through the entire process using them as target binaries eventually checking the actual C++ source at the end to confirm in what steps in the process chain do we need fix or improve. The debug and output could also be feed to llm to help figure out the problems and potential fixes?
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()
  
def save_similarity_metrics(func_name, asm_similarity, cfg_similarity):
    try:
        cursor.execute('''
            INSERT INTO similarity_metrics (name, asm_similarity, cfg_similarity, timestamp)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (func_name, asm_similarity, cfg_similarity))
        conn.commit()
    except sqlite3.Error as e:
        logging.error(f"Error saving similarity metrics for '{func_name}': {e}")

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr
from utils.db_utils import save_similarity_metrics

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        cpp_graph = nx.DiGraph()
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_name, func_data, cpp_code_path):
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])

    save_similarity_metrics(func_name, asm_similarity, cfg_similarity)
    
    return asm_similarity, cfg_similarity
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To set up an automated C++ project on Windows using different compilers and configurations, we'll create a complex "Hello World" application in C++, build it using CMake, and integrate it into your process chain. We need to check for installed compilers like Visual Studio and LLVM Clang and run through their different optimization and debug levels to use them as target binaries. Here is a step-by-step guide:

### Project Update
1. **Structure Update**:
   - Add a `testapp` directory for source files.
   - Create a new batch script for setting up and compiling code.

2. **CMake Configuration**:
   - Modify the CMake file to target different compilers and configurations.
   - Ensure CMake handles checking for installed compilers.

3. **Script for Compiling on Windows**:
   - Create a batch file that automates checking for installed compilers and runs CMake with various settings.

4. **Adjust Your Python Test to Include the Test App**:
   - Modify the `main.py` to accept and incorporate the compiled test binaries.

Let's break it down into concrete steps:

---

### 1. Project Structure

```plaintext
binary_decompilation_project/
├── main.py                     
├── config.py                  
├── requirements.txt            
├── logs/
│   └── debug.log               
├── schemas/
│   └── function_schema.json    
├── utils/
│   ├── decompilation.py        
│   ├── validation.py           
│   ├── verification.py         
│   ├── feedback_loop.py        
│   ├── db_utils.py             
│   ├── project_info.py  
│   ├── cpp_generator.py        
│   ├── generate_file_structure.py 
│   └── similarity_analysis.py
├── output/
│   ├── include/
│   ├── src/                    
│   └── CMakeLists.txt
├── testapp/                      # Source code for the test app
│   ├── src/
│   └── CMakeLists.txt           
└── compile_and_test.bat         # Windows batch file for compiling the test app
```

### 2. CMake Configuration

##### `testapp/CMakeLists.txt`
```cmake
cmake_minimum_required(VERSION 3.10)
project(TestApp)

set(SRC_FILES
    src/hello_world.cpp
    # Add more cpp files if needed
)

add_executable(testapp ${SRC_FILES})

# Check for preferred compiler
find_program(CLANG_COMPILER clang++)
find_program(MSVC_COMPILER cl)
if(CLANG_COMPILER)
  message(STATUS "Clang compiler found.")
  set(CMAKE_CXX_COMPILER "${CLANG_COMPILER}")
elseif(MSVC_COMPILER)
  message(STATUS "MSVC compiler found.")
  set(CMAKE_CXX_COMPILER "${MSVC_COMPILER}")
endif()

# Define multiple build configurations
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
```

##### `hello_world.cpp`
```cpp
// testapp/src/hello_world.cpp

#include <iostream>
#include <string>
#include <vector>

class Greeter {
public:
    Greeter(const std::string& name) : name(name) {}
    void greet() const {
        std::cout << "Hello, " << name << "!" << std::endl;
    }
private:
    std::string name;
};

int add(int a, int b) { return a + b; }

void print_sum(int a, int b) {
    std::cout << "The sum of " << a << " and " << b << " is " << add(a, b) << std::endl;
}

int main() {
    std::vector<std::string> names = {"World", "C++"};
    for(const auto& name : names) {
        Greeter greeter(name);
        greeter.greet();
    }
    print_sum(42, 58);
    return 0;
}
```

### 3. Batch Script to Compile

Create `compile_and_test.bat` to manage builds.

```batch
@echo off
setlocal

REM Set build directory
set BUILD_DIR=testapp\build
if not exist %BUILD_DIR% mkdir %BUILD_DIR%
cd %BUILD_DIR%

REM Check for compilers and setup for them
where /R "C:\Program Files\LLVM\bin" clang++
if %errorlevel%==0 (
    set COMPILER=clang++
) else (
    where cl.exe
    if %errorlevel%==0 (
        set COMPILER=cl
    ) else (
        echo No supported compiler found.
        exit /b 1
    )
)

REM Compile with different configurations
call cmake .. -DCMAKE_CXX_COMPILER=%COMPILER% -DCMAKE_BUILD_TYPE=Debug
call cmake --build . --config Debug
call cmake .. -DCMAKE_CXX_COMPILER=%COMPILER% -DCMAKE_BUILD_TYPE=Release
call cmake --build . --config Release

cd ../..
endlocal
```

### 4. Python Script Updates

Update `main.py` to include test binaries:

```python
def run_with_binary(binary_path):
    global BINARY_PATH  # Update this line from the previous code snippet
    BINARY_PATH = binary_path
    deque_functions = decompile_functions(BINARY_PATH)
    # Additional steps here...

def main():
    # Run compile script
    os.system("compile_and_test.bat")

    # Include test binaries in analysis
    binaries = [os.path.join("testapp", "build", f) for f in os.listdir("testapp/build") if f.endswith('.exe')]
    for binary in binaries:
        run_with_binary(binary)

if __name__ == "__main__":
    main()
```

### Conclusion

This setup creates a mock C++ "Hello World" application with multiple functions and builds it using CMake for different compilers on a Windows system. The batch script checks for available compilers and automates the build process, integrating these compiled binaries into your Python analysis pipeline. Adjustments to prompts, tools, and feedback mechanisms will be key as you iterate based on the results.
User: We only want main.py to run the testapp when it is selected or should we trigger it by another py file, e.g. testapp.py to avoid confusion with the main.py. Can we replace ompile_and_test.bat and CMakeLists.txt with just testapp.py and keep the original main.py code?
```
cmake_minimum_required(VERSION 3.10)
project(TestApp)

set(SRC_FILES
    src/hello_world.cpp
    # Add more cpp files if needed
)

add_executable(testapp ${SRC_FILES})

# Check for preferred compiler
find_program(CLANG_COMPILER clang++)
find_program(MSVC_COMPILER cl)
if(CLANG_COMPILER)
  message(STATUS "Clang compiler found.")
  set(CMAKE_CXX_COMPILER "${CLANG_COMPILER}")
elseif(MSVC_COMPILER)
  message(STATUS "MSVC compiler found.")
  set(CMAKE_CXX_COMPILER "${MSVC_COMPILER}")
endif()

# Define multiple build configurations
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
```
```
// testapp/src/hello_world.cpp

#include <iostream>
#include <string>
#include <vector>

class Greeter {
public:
    Greeter(const std::string& name) : name(name) {}
    void greet() const {
        std::cout << "Hello, " << name << "!" << std::endl;
    }
private:
    std::string name;
};

int add(int a, int b) { return a + b; }

void print_sum(int a, int b) {
    std::cout << "The sum of " << a << " and " << b << " is " << add(a, b) << std::endl;
}

int main() {
    std::vector<std::string> names = {"World", "C++"};
    for(const auto& name : names) {
        Greeter greeter(name);
        greeter.greet();
    }
    print_sum(42, 58);
    return 0;
}
```
```
@echo off
setlocal

REM Set build directory
set BUILD_DIR=testapp\build
if not exist %BUILD_DIR% mkdir %BUILD_DIR%
cd %BUILD_DIR%

REM Check for compilers and setup for them
where /R "C:\Program Files\LLVM\bin" clang++
if %errorlevel%==0 (
    set COMPILER=clang++
) else (
    where cl.exe
    if %errorlevel%==0 (
        set COMPILER=cl
    ) else (
        echo No supported compiler found.
        exit /b 1
    )
)

REM Compile with different configurations
call cmake .. -DCMAKE_CXX_COMPILER=%COMPILER% -DCMAKE_BUILD_TYPE=Debug
call cmake --build . --config Debug
call cmake .. -DCMAKE_CXX_COMPILER=%COMPILER% -DCMAKE_BUILD_TYPE=Release
call cmake --build . --config Release

cd ../..
endlocal
```
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: To streamline the process and avoid confusion, you can separate the testing and compilation of your test app into a dedicated Python script (`testapp.py`). This script will handle the compilation of the test application using CMake and various compilers, then run the generated binaries through your existing process. Here’s how you can set up and structure this:

### Project Structure

```plaintext
binary_decompilation_project/
├── main.py                     # Main orchestration script for running the decompilation pipeline
├── testapp.py                  # Script for compiling and testing the test application
├── config.py                   # Configuration for API URLs, model settings, and debug mode
├── requirements.txt            # Python dependencies
├── logs/
│   └── debug.log               # Log file for detailed debug output
├── schemas/
│   └── function_schema.json    # JSON schema for validating function metadata
├── utils/                      # Utility scripts for different stages of the pipeline
│   ├── decompilation.py        
│   ├── validation.py           
│   ├── verification.py         
│   ├── feedback_loop.py        
│   ├── db_utils.py             
│   ├── project_info.py  
│   ├── cpp_generator.py        
│   ├── generate_file_structure.py 
│   └── similarity_analysis.py
├── output/
│   ├── include/
│   ├── src/                    
│   └── CMakeLists.txt
└── testapp/                    # Source code for the test app
    ├── src/
    │   └── hello_world.cpp     # Test application C++ source
    └── CMakeLists.txt          # CMake configuration for test app
```

### 1. CMake Configuration for the Test App

##### `testapp/CMakeLists.txt`

```cmake
cmake_minimum_required(VERSION 3.10)
project(TestApp)

set(SRC_FILES
    src/hello_world.cpp
)

add_executable(testapp ${SRC_FILES})

# Enable multiple configurations
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")

# Choose compiler if available
find_program(CLANG_COMPILER clang++)
find_program(MSVC_COMPILER cl)
if(CLANG_COMPILER)
  message(STATUS "Clang compiler found.")
  set(CMAKE_CXX_COMPILER "${CLANG_COMPILER}")
elseif(MSVC_COMPILER)
  message(STATUS "MSVC compiler found.")
  set(CMAKE_CXX_COMPILER "${MSVC_COMPILER}")
endif()
```

### 2. Test Application Source

##### `testapp/src/hello_world.cpp`

```cpp
#include <iostream>
#include <vector>
#include <string>

class Greeter {
public:
    Greeter(const std::string& name) : name(name) {}
    void greet() const {
        std::cout << "Hello, " << name << "!" << std::endl;
    }
private:
    std::string name;
};

int add(int a, int b) {
    return a + b;
}

void print_sum(int a, int b) {
    std::cout << "The sum of " << a << " and " << b << " is " << add(a, b) << std::endl;
}

int main() {
    std::vector<std::string> names = {"World", "C++"};
    for (const auto& name : names) {
        Greeter greeter(name);
        greeter.greet();
    }
    print_sum(42, 58);
    return 0;
}
```

### 3. Python Script for Testing

Create `testapp.py` to manage the build and testing of the test application.

##### `testapp.py`

```python
import os
import subprocess
import sys

def check_compilers():
    compilers = {}
    try:
        # Check for Clang
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        # Check for MSVC
        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Building with {name} in {build_type} mode...")
                try:
                    subprocess.run(["cmake", "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run(["cmake", "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```

### Conclusion

By separating out the `testapp` building and running to a `testapp.py`, you encapsulate this behavior outside your primary application, keeping `main.py` focused on the core process. At the same time, `testapp.py` allows you to test the hypothetical Hello World application in various configurations using CMake and multiple compilers on Windows. This flexibility in compilation will help you identify at which points in the process improvements can be made by analyzing the generated output and using the logs from each step.
User: Help fix the error.
```
Building with clang in Debug mode...
Traceback (most recent call last):
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 42, in <module>
    main()
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 39, in main
    build_and_test()
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 31, in build_and_test
    subprocess.run(["cmake", "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 548, in run
    with Popen(*popenargs, **kwargs) as process:
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 1538, in _execute_child
    hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
FileNotFoundError: [WinError 2] The system cannot find the file specified

```
```
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db
from utils.similarity_analysis import run_analysis

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)

    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            cpp_code_path = os.path.join(CPP_OUTPUT_DIR, f"{func_name}.cpp")
            asm_similarity, cfg_similarity = run_analysis(func_name, func_data, cpp_code_path)

            if asm_similarity > 0.8 and cfg_similarity:
                save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
            else:
                logging.warning(f"Similarity analysis failed for function '{func_name}'. Feedback loop triggered.")

    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
```
```
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
```
```
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
```
```
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
```
```
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
```
```
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()
  
def save_similarity_metrics(func_name, asm_similarity, cfg_similarity):
    try:
        cursor.execute('''
            INSERT INTO similarity_metrics (name, asm_similarity, cfg_similarity, timestamp)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (func_name, asm_similarity, cfg_similarity))
        conn.commit()
    except sqlite3.Error as e:
        logging.error(f"Error saving similarity metrics for '{func_name}': {e}")

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
```
```
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
```
```
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
```
```
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
```
```
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
```
```
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}
```
```
# utils/similarity_analysis.py

from difflib import SequenceMatcher
import logging
import subprocess
import networkx as nx
import angr
from utils.db_utils import save_similarity_metrics

def calculate_similarity(a, b):
    """Calculate similarity ratio between two strings."""
    return SequenceMatcher(None, a, b).ratio()

def diff_assembler(original_binary, generated_cpp_path):
    """Compare assembler outputs for the generated and original binaries."""
    try:
        generated_binary = generated_cpp_path.replace('.cpp', '')
        subprocess.run(["g++", generated_cpp_path, "-o", generated_binary], check=True)
        original_asm = subprocess.check_output(["objdump", "-d", original_binary]).decode()
        generated_asm = subprocess.check_output(["objdump", "-d", generated_binary]).decode()
        similarity_ratio = calculate_similarity(original_asm, generated_asm)
        return similarity_ratio
    except subprocess.CalledProcessError as e:
        logging.error(f"Assembly diffing failed: {e}")
        return 0.0

def compare_control_flow_graphs(binary_path, cpp_code):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg_orig = project.analyses.CFGFast()
        cpp_graph = nx.DiGraph()
        matcher = nx.isomorphism.GraphMatcher(cfg_orig.graph, cpp_graph)
        is_isomorphic = matcher.is_isomorphic()
        return is_isomorphic
    except Exception as e:
        logging.error(f"CFG comparison failed: {e}")
        return False

def run_analysis(func_name, func_data, cpp_code_path):
    asm_similarity = diff_assembler(func_data['binary_path'], cpp_code_path)
    cfg_similarity = compare_control_flow_graphs(func_data['binary_path'], func_data['cpp_code'])

    save_similarity_metrics(func_name, asm_similarity, cfg_similarity)
    
    return asm_similarity, cfg_similarity
```
```
import os
import subprocess
import sys

def check_compilers():
    compilers = {}
    try:
        # Check for Clang
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        # Check for MSVC
        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Building with {name} in {build_type} mode...")
                try:
                    subprocess.run(["cmake", "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run(["cmake", "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: The error you're encountering indicates that the `cmake` command was not found by the `subprocess.run` call in your Python script. This generally happens because CMake is not installed or its executable directory is not added to the system's `PATH`.

Here's how you can troubleshoot and resolve the issue:

1. **Ensure CMake is Installed**:

   - Visit [cmake.org/download](https://cmake.org/download/) to download the latest version of CMake for Windows.
   - Install CMake, ideally with the option to add CMake to the system `PATH` automatically.

2. **Verify CMake is in the PATH**:

   - Open a Command Prompt (`cmd`).
   - Type `cmake --version` to check if it's recognized. If the version information displays, your PATH is set correctly. If not, you'll need to modify the PATH.

3. **Manually Add CMake to `PATH`** (if necessary):

   - Right-click on "This PC" or "My Computer" and select "Properties".
   - Click on "Advanced system settings".
   - Click on "Environment Variables".
   - Under "System variables", scroll down and select "Path", then click "Edit".
   - Click "New" and add the path to the directory where `cmake.exe` is located (e.g., `C:\Program Files\CMake\bin`).
   - Click OK, then apply the changes.

4. **Adjust `testapp.py` to Handle Path Errors Gracefully**:

   Update your `testapp.py` to include better error handling when locating and using CMake:

```python
import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Verify CMake is available
    try:
        cmake_version = subprocess.run(["cmake", "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("CMake is not installed or not available in PATH.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    subprocess.run(["cmake", "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run(["cmake", "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```

After making these changes, try running `testapp.py` again. It should now provide more informative feedback if the issue relates to CMake being unavailable. If CMake is correctly set up, the script should proceed to check compilers and build the application.
User: Could I unpack the cmake bin files in the structure?
```
Traceback (most recent call last):
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 49, in <module>
    main()
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 46, in main
    build_and_test()
  File "C:\Users\user\Documents\Civ5XP\testapp.py", line 25, in build_and_test
    cmake_version = subprocess.run(["cmake", "--version"], capture_output=True, text=True, check=True)
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 548, in run
    with Popen(*popenargs, **kwargs) as process:
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "C:\Users\user\AppData\Local\Programs\Python\Python312\Lib\subprocess.py", line 1538, in _execute_child
    hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
FileNotFoundError: [WinError 2] The system cannot find the file specified

```
```
# testapp.py

import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Verify CMake is available
    try:
        cmake_version = subprocess.run(["cmake", "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("CMake is not installed or not available in PATH.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    subprocess.run(["cmake", "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run(["cmake", "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: Yes, you can unpack CMake binaries directly into your project structure if adding CMake to your system's PATH is not an option. Here's how you can do it:

### Steps to Include CMake Binaries Locally

1. **Download CMake Portable ZIP**:
   - Go to the [CMake Download Page](https://cmake.org/download/).
   - Download the Windows ZIP for "cmake-X.X.X-win64-x64.zip".

2. **Unpack CMake Binaries**:
   - Extract the contents of the ZIP file into a directory within your project structure, e.g., `binary_decompilation_project/cmake/bin/`.

3. **Modify `testapp.py` to Use Local CMake**:
   - Update the script to use the local path for running the `cmake` command.

Here is how you can modify `testapp.py` to include the path to the local CMake binaries:

```python
import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Specify the path to the local CMake binaries
    cmake_path = os.path.join("..", "..", "cmake", "bin", "cmake.exe")

    try:
        cmake_version = subprocess.run([cmake_path, "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("Local CMake is not properly set up.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    subprocess.run([cmake_path, "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run([cmake_path, "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```

### Key Points

- **CMake Path**: Update the `cmake_path` variable to point to your local CMake installation directory.
- **Testing**: This script checks for available compilers and uses the local CMake executable. You can test builds with both Clang and MSVC if they're available on your system.
- **Running the Test**: Run `testapp.py` to compile and test the application. This approach avoids dependencies on global PATH settings, making your project more portable and easier to set up in different environments.

This setup ensures that your workflow does not depend on external system configurations, which is especially beneficial in team settings or when sharing your project with others.
User: Help fix the error.
```
= RESTART: C:\Users\user\Documents\Civ5XP\testapp.py
cmake version 3.30.5

CMake suite maintained and supported by Kitware (kitware.com/cmake).

Trying to build with clang in Debug mode...
Error during compilation with clang Debug: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=Debug']' returned non-zero exit status 1.
Trying to build with clang in Release mode...
Error during compilation with clang Release: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=Release']' returned non-zero exit status 1.
Trying to build with clang in RelWithDebInfo mode...
Error during compilation with clang RelWithDebInfo: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=RelWithDebInfo']' returned non-zero exit status 1.
```
```
# testapp.py

import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Specify the path to the local CMake binaries
    cmake_path = os.path.join("..", "..", "cmake", "bin", "cmake.exe")

    try:
        cmake_version = subprocess.run([cmake_path, "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("Local CMake is not properly set up.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    subprocess.run([cmake_path, "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}"], check=True)
                    subprocess.run([cmake_path, "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: The error message you're encountering indicates that CMake was able to check its version, but there was an issue when attempting to configure the project with the specified compiler (Clang, in this case). This suggests that something might be wrong with the configuration step.

Here's how you can troubleshoot and fix this problem:

1. **Check CMakeLists.txt**:
   
   Ensure that your `CMakeLists.txt` is correctly set up to handle different compilers and doesn't have any syntax issues. Make sure that all paths and variables are correctly specified.

2. **Validate Compiler Path**:

   Make sure the path to the Clang compiler (`clang++.exe`) is correct. You can try running `C:\Program Files\LLVM\bin\clang++.exe --version` directly in your command prompt to check if it's accessible.

3. **Specify CMake Generator**:

   On Windows, CMake might need a specific "generator" to use. You can specify a generator by adding the `-G` switch to your CMake command. For Visual Studio, it might be something like `-G "Visual Studio 16 2019"`.

4. **Review Error Messages**:

   Enable more verbose logging to get additional information about what might be causing the error. Append `-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON` to your CMake configuration command:

```python
subprocess.run([
    cmake_path, "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}",
    "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"], check=True)
```

5. **Update the `testapp.py`** to output more log details in case of failure:

```python
import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Specify the path to the local CMake binaries
    cmake_path = os.path.join("..", "..", "cmake", "bin", "cmake.exe")

    try:
        cmake_version = subprocess.run([cmake_path, "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("Local CMake is not properly set up.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    # Add verbosity and error output capture
                    result = subprocess.run([
                        cmake_path, "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}",
                        "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"], capture_output=True, text=True, check=True)
                    print(result.stdout)
                    subprocess.run([
                        cmake_path, "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")
                    print(f"Error output: {err.stderr}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```

By adding verbosity, you might capture more detailed error messages in the output that can help pinpoint what is causing the configuration or build step to fail. Adjust the paths and settings according to the actual build environment. If there are specific messages about missing files, directories, or dependency issues, address those accordingly.
User: Help fix the error.
```
= RESTART: C:\Users\user\Documents\Civ5XP\testapp.py
cmake version 3.30.5

CMake suite maintained and supported by Kitware (kitware.com/cmake).

Trying to build with clang in Debug mode...
Error during compilation with clang Debug: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=Debug', '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON']' returned non-zero exit status 1.
Error output: CMake Error: Could not find CMAKE_ROOT !!!
CMake has most likely not been installed correctly.
Modules directory not found in
C:/Users/user/Documents/Civ5XP/cmake/share/cmake-3.30
CMake Error: Error executing cmake::LoadCache(). Aborting.


Trying to build with clang in Release mode...
Error during compilation with clang Release: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=Release', '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON']' returned non-zero exit status 1.
Error output: CMake Error: Could not find CMAKE_ROOT !!!
CMake has most likely not been installed correctly.
Modules directory not found in
C:/Users/user/Documents/Civ5XP/cmake/share/cmake-3.30
CMake Error: Error executing cmake::LoadCache(). Aborting.


Trying to build with clang in RelWithDebInfo mode...
Error during compilation with clang RelWithDebInfo: Command '['..\\..\\cmake\\bin\\cmake.exe', '..', '-DCMAKE_CXX_COMPILER=C:\\Program Files\\LLVM\\bin\\clang++.exe', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', '-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON']' returned non-zero exit status 1.
Error output: CMake Error: Could not find CMAKE_ROOT !!!
CMake has most likely not been installed correctly.
Modules directory not found in
C:/Users/user/Documents/Civ5XP/cmake/share/cmake-3.30
CMake Error: Error executing cmake::LoadCache(). Aborting.
```
```
# testapp.py

import os
import subprocess

def check_compilers():
    compilers = {}
    try:
        clang = subprocess.run(["where", "clang++"], capture_output=True, text=True)
        compilers['clang'] = clang.stdout.strip() if clang.returncode == 0 else None

        msvc = subprocess.run(["where", "cl"], capture_output=True, text=True)
        compilers['msvc'] = msvc.stdout.strip() if msvc.returncode == 0 else None
    except Exception as e:
        print(f"Error checking compilers: {e}")
    return compilers

def build_and_test():
    build_dir = "testapp/build"
    os.makedirs(build_dir, exist_ok=True)
    os.chdir(build_dir)

    # Specify the path to the local CMake binaries
    cmake_path = os.path.join("..", "..", "cmake", "bin", "cmake.exe")

    try:
        cmake_version = subprocess.run([cmake_path, "--version"], capture_output=True, text=True, check=True)
        print(cmake_version.stdout)
    except subprocess.CalledProcessError:
        print("Local CMake is not properly set up.")
        return

    compilers = check_compilers()

    for name, compiler in compilers.items():
        if compiler:
            for build_type in ['Debug', 'Release', 'RelWithDebInfo']:
                print(f"Trying to build with {name} in {build_type} mode...")
                try:
                    # Add verbosity and error output capture
                    result = subprocess.run([
                        cmake_path, "..", f"-DCMAKE_CXX_COMPILER={compiler}", f"-DCMAKE_BUILD_TYPE={build_type}",
                        "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"], capture_output=True, text=True, check=True)
                    print(result.stdout)
                    subprocess.run([
                        cmake_path, "--build", ".", "--config", build_type], check=True)
                except subprocess.CalledProcessError as err:
                    print(f"Error during compilation with {name} {build_type}: {err}")
                    print(f"Error output: {err.stderr}")

    os.chdir('../..')

def main():
    build_and_test()

if __name__ == "__main__":
    main()
```
```plaintext
# main.py

import os
from config import BINARY_PATH, CPP_OUTPUT_DIR
from utils.project_info import gather_project_metadata
from utils.decompilation import decompile_functions
from utils.validation import validate_function
from utils.verification import verify_function
from utils.db_utils import save_to_database, close_database
from utils.generate_file_structure import generate_files_from_db

def main():
    gather_project_metadata(BINARY_PATH)
    os.makedirs(CPP_OUTPUT_DIR, exist_ok=True)
    decompiled_functions = decompile_functions(BINARY_PATH)
    for func_name, func_data in decompiled_functions.items():
        if validate_function(func_data):
            save_to_database(func_name, func_data['offset'], func_data['assembly_code'], func_data['decompiled_code'], "")
    verified_functions = verify_function(BINARY_PATH, decompiled_functions)
    feedback_loop(decompiled_functions, verified_functions)
    generate_files_from_db()
    close_database()

if __name__ == "__main__":
    main()
# config.py

# Base URL for the Ollama API running on WSL2
OLLAMA_BASE_URL = "http://172.31.72.252:11434"

# Model to use in Ollama
LLM_MODEL = "ollama/qwen2.5:14b"

# Debug mode toggle
DEBUG_MODE = True

# Paths for project files and directories
BINARY_PATH = r"Civ5XP"               # Path to the binary file for decompilation
CPP_OUTPUT_DIR = "output"             # Directory for C++ output files
LOG_FILE = "logs/debug.log"           # Log file path
DATABASE_PATH = "binary_decompilation.db"  # Database file path

# Decompilation and C++ generation settings
MAX_CONTEXT_LENGTH = 128000           # Max context length for LLM input
MAX_TOKENS = 8192                     # Max tokens for LLM output
CXX_STANDARD = 17                     # C++ standard for generated code

# Function call model configuration for LLM
MODEL_FUNCTION_CALL_SETTINGS = {
    "name": "generate_cpp",
    "parameters": [
        {"name": "cpp_code", "type": "string"},
        {"name": "warnings", "type": "list"},
        {"name": "feedback", "type": "string"}
    ]
}

# CMake file generation options
CMAKE_MINIMUM_VERSION = "3.10"
TARGET_NAME = "DecompiledProject"     # Target name in CMakeLists.txt
ADDITIONAL_LIBRARIES = ["libA", "libB"]  # Additional libraries to link in CMakeLists.txt
LLVM_PATH = "/usr/local/llvm"  # Adjust to your actual LLVM path if needed
INCLUDE_DIRECTORIES = ["include", "/path/to/other/includes"]
# utils/decompilation.py

from utils.verification import verify_function, compile_cpp_to_llvm_ir
from utils.validation import validate_function
from utils.feedback_loop import feedback_loop

def decompile_functions(binary_file=BINARY_PATH):
    project_metadata = gather_project_metadata(binary_file)
    function_summaries = {}

    with pyhidra.open_program(binary_file) as flat_api:
        program = flat_api.getCurrentProgram()
        listing = program.getListing()
        decompiler = DecompInterface()
        decompiler.openProgram(program)

        for function in listing.getFunctions(True):
            function_name = function.getName()
            function_offset = function.getEntryPoint().getOffset()

            db_entry = load_function_from_database(function_name)
            if db_entry:
                logging.debug(f"Function '{function_name}' already in database.")
                continue

            try:
                results = decompiler.decompileFunction(function, 0, TaskMonitor.DUMMY)
                if results and results.decompiledFunction is not None:
                    decompiled_code = results.getDecompiledFunction().getC()
                    assembly_code = str(function.getBody())
                    cpp_code = generate_cpp_code(decompiled_code, function_name)

                    # Decompile function and collect metadata
                    function_metadata = {
                        "name": function_name,
                        "offset": function_offset,
                        "assembly_code": assembly_code,
                        "decompiled_code": decompiled_code,
                        "symbol_names": [symbol.getName() for symbol in function.getSymbols()],
                        "comments": extract_comments_from_function(function),
                        "cpp_code": cpp_code
                    }

                    # Store to database after validation
                    if validate_function(function_metadata):
                        save_to_database(function_name, function_metadata, 'decompilation_output')
                        function_summaries[function_name] = function_metadata

                        # Verification step - LLVM IR and Control Flow Graph matching
                        llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
                        verification_result = verify_function(binary_file, {function_name: function_metadata})
                        if not verification_result:
                            logging.warning(f"Verification failed for '{function_name}'. Triggering feedback loop.")
                            feedback_loop({function_name: function_metadata}, function_summaries)
                    else:
                        logging.warning(f"Validation failed for '{function_name}'.")

                else:
                    logging.error(f"Failed decompiling '{function_name}'.")

            except Exception as e:
                logging.error(f"Error processing '{function_name}': {str(e)}")

    close_database()
    return function_summaries
# utils/verification.py

import angr
import llvmlite.binding as llvm
import logging
import subprocess
import tempfile
from config import DEBUG_MODE
from utils.db_utils import load_project_metadata

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def verify_function(binary_path, functions_data):
    project_metadata = load_project_metadata()
    min_address = int(project_metadata.get("min_address", "0"), 16)
    max_address = int(project_metadata.get("max_address", "FFFFFFFF"), 16)

    for func_name, func_data in functions_data.items():
        cpp_code = func_data.get("cpp_code")
        if cpp_code:
            llvm_ir = compile_cpp_to_llvm_ir(cpp_code)
            if llvm_ir:
                result = compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address)
                if result:
                    logging.info(f"Verification passed for '{func_name}'.")
                else:
                    logging.warning(f"Verification failed for '{func_name}'.")

def compile_cpp_to_llvm_ir(cpp_code):
    try:
        with tempfile.NamedTemporaryFile(suffix=".cpp", delete=False) as cpp_file:
            cpp_file.write(cpp_code.encode())
            cpp_filename = cpp_file.name

        llvm_filename = cpp_filename.replace(".cpp", ".ll")
        subprocess.run(["clang", "-emit-llvm", "-S", cpp_filename, "-o", llvm_filename], check=True)
        
        with open(llvm_filename, "r") as llvm_ir_file:
            llvm_ir = llvm_ir_file.read()
        return llvm_ir
    except subprocess.CalledProcessError as e:
        logging.error(f"Clang compilation failed: {str(e)}")
        return None
    except Exception as e:
        logging.error(f"Error compiling C++ to LLVM IR: {str(e)}")
        return None

def compare_with_binary(binary_path, func_name, llvm_ir, min_address, max_address):
    try:
        project = angr.Project(binary_path, auto_load_libs=False)
        cfg = project.analyses.CFGFast()
        
        llvm_func_addresses = extract_function_addresses_from_llvm(llvm_ir, func_name)
        
        for address in llvm_func_addresses:
            if not (min_address <= address <= max_address):
                logging.warning(f"Address {address} for '{func_name}' out of valid range.")
                continue
            binary_func = cfg.kb.functions.get_by_addr(address)
            if binary_func is None:
                logging.warning(f"Function '{func_name}' at address {address} not found in binary.")
                return False
        return True
    except Exception as e:
        logging.error(f"Error comparing LLVM IR with binary: {str(e)}")
        return False

def extract_function_addresses_from_llvm(llvm_ir, func_name):
    addresses = []
    for line in llvm_ir.splitlines():
        if func_name in line and "define" in line:
            # Use a more accurate regex or parser for proper extraction
            # Assuming format like `@func_name = external addrspace(0) constant i32 0x...`
            address_match = re.search(r'0x[0-9A-Fa-f]+', line)
            if address_match:
                address = int(address_match.group(0), 16)
                addresses.append(address)
    return addresses
# utils/validation.py

import jsonschema
import logging
import json
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

with open("schemas/function_schema.json") as f:
    function_schema = json.load(f)

def validate_function(function_data):
    try:
        jsonschema.validate(instance=function_data, schema=function_schema)
        logging.debug(f"Validation passed: {function_data.get('name', 'unknown')}")
        return True
    except jsonschema.ValidationError as e:
        logging.error(f"Validation error: {e}")
        return False
# utils/db_utils.py

import sqlite3
from pathlib import Path

db_path = Path("binary_decompilation.db")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()

# Functions table
cursor.execute('''
CREATE TABLE IF NOT EXISTS functions (
    function_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT UNIQUE,
    address TEXT,
    entry_point TEXT,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Decompilation Output table
cursor.execute('''
CREATE TABLE IF NOT EXISTS decompilation_output (
    function_id INTEGER,
    assembly_code TEXT,
    pseudo_c_code TEXT,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# C++ Generation table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cpp_generation (
    function_id INTEGER,
    cpp_code TEXT,
    warnings TEXT,
    feedback TEXT,
    validation_status TEXT,
    last_generated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')

# Verification and Feedback table
cursor.execute('''
CREATE TABLE IF NOT EXISTS verification_feedback (
    function_id INTEGER,
    discrepancies TEXT,
    refined_prompt TEXT,
    verification_status TEXT,
    last_verified TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (function_id) REFERENCES functions(function_id)
)
''')
conn.commit()

# Project Metadata table
cursor.execute('''
CREATE TABLE IF NOT EXISTS project_metadata (
    project_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_file_name TEXT,
    last_modified TEXT,
    readonly BOOLEAN,
    program_name TEXT,
    language_id TEXT,
    compiler_id TEXT,
    processor TEXT,
    endian TEXT,
    address_size INTEGER,
    min_address TEXT,
    max_address TEXT,
    num_bytes INTEGER,
    num_memory_blocks INTEGER,
    num_instructions INTEGER,
    num_defined_data INTEGER,
    num_functions INTEGER,
    num_symbols INTEGER,
    num_data_types INTEGER,
    analyzed BOOLEAN,
    created_with_ghidra_version TEXT,
    file_type TEXT,
    file_location TEXT,
    elf_original_image_base TEXT,
    relocatable BOOLEAN,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')

# Missing Libraries table
cursor.execute('''
CREATE TABLE IF NOT EXISTS missing_libraries (
    library_id INTEGER PRIMARY KEY AUTOINCREMENT,
    project_id INTEGER,
    library_name TEXT,
    last_checked TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (project_id) REFERENCES project_metadata(project_id)
)
''')
conn.commit()

def save_project_metadata(metadata):
    fields = ', '.join(metadata.keys())
    placeholders = ', '.join(['?'] * len(metadata))
    values = tuple(metadata.values())
    cursor.execute(f'''
        INSERT INTO project_metadata ({fields})
        VALUES ({placeholders})
    ''', values)
    conn.commit()

def save_missing_library(project_id, library_name):
    cursor.execute('''
        INSERT INTO missing_libraries (project_id, library_name)
        VALUES (?, ?)
    ''', (project_id, library_name))
    conn.commit()

def save_to_database(function_id, data, table):
    try:
        fields = ', '.join(data.keys())
        placeholders = ', '.join(['?'] * len(data))
        values = tuple(data.values())
        cursor.execute(f'''
            INSERT OR REPLACE INTO {table} ({fields})
            VALUES ({placeholders})
        ''', values)
        conn.commit()
    except sqlite3.Error as e:
        print(f"Database error: {e}")

def load_function_from_database(function_name):
    cursor.execute('SELECT * FROM functions WHERE name = ?', (function_name,))
    return cursor.fetchone()

def close_database():
    conn.close()

# context manager for database operations
from contextlib import contextmanager

@contextmanager
def get_db_cursor():
    conn = sqlite3.connect(db_path)
    try:
        yield conn.cursor()
    finally:
        conn.commit()
        conn.close()
# utils/feedback_loop.py

import logging
from utils.db_utils import save_to_database
from config import DEBUG_MODE

logging.basicConfig(filename="logs/debug.log", level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def log_discrepancies(function_id, discrepancy_details):
    logging.debug(f"Logging discrepancy for function_id '{function_id}': {discrepancy_details}")
    save_to_database(function_id, {"discrepancies": str(discrepancy_details)}, "verification_feedback")

def refine_prompts(discrepancies):
    if not discrepancies:
        logging.debug("No discrepancies found; no prompt refinement necessary.")
        return

    for func_id, details in discrepancies:
        logging.debug(f"Refining prompts based on discrepancy for function_id '{func_id}'")

        # Adjustments to prompt structure or content based on discrepancy type
        if "type_mismatch" in details:
            prompt_modifier = "Ensure strict type compliance in generated C++."
        elif "missing_code" in details:
            prompt_modifier = "Double-check all control flows and branches."

        refined_prompt = {
            "role": "user",
            "content": f"Refine the C++ output based on these observations:\n{details}\n{prompt_modifier}"
        }
        save_to_database(func_id, {"refined_prompt": str(refined_prompt)}, "verification_feedback")
        logging.debug(f"Updated prompt for function_id '{func_id}': {refined_prompt}")

def feedback_loop(decompiled_functions, verified_functions):
    discrepancies = []
    for func_id, details in decompiled_functions.items():
        if func_id not in verified_functions or verified_functions[func_id]["cpp_code"] != details["cpp_code"]:
            discrepancy_details = {"decompiled": details, "verified": verified_functions.get(func_id)}
            discrepancies.append((func_id, discrepancy_details))
            log_discrepancies(func_id, discrepancy_details)

    refine_prompts(discrepancies)
# utils/project_info.py

import os
import logging
import pyhidra
from utils.db_utils import save_project_metadata, save_missing_library

logging.basicConfig(filename="logs/project_info.log", level=logging.INFO)

def gather_project_metadata(binary_path):
    with pyhidra.open_program(binary_path) as flat_api:
        program = flat_api.getCurrentProgram()
        metadata = {
            "project_file_name": program.getDomainFile().getName(),
            "last_modified": program.getModificationDate().toString(),
            "readonly": program.isReadonly(),
            "program_name": program.getName(),
            "language_id": program.getLanguageID().toString(),
            "compiler_id": program.getCompilerSpec().getCompilerSpecID().toString(),
            "processor": program.getLanguage().getProcessor().toString(),
            "endian": program.getLanguage().isBigEndian(),
            "address_size": program.getDefaultPointerSize(),
            "min_address": program.getMinAddress().toString(),
            "max_address": program.getMaxAddress().toString(),
            "num_bytes": program.getMemory().getNumAddresses(),
            "num_memory_blocks": program.getMemoryBlockCount(),
            "num_instructions": program.getListing().getNumInstructions(),
            "num_defined_data": program.getListing().getNumDefinedData(),
            "num_functions": program.getFunctionManager().getFunctionCount(),
            "num_symbols": program.getSymbolTable().getNumSymbols(),
            "num_data_types": len(program.getDataTypeManager().getAllDataTypes()),
            "analyzed": program.isAnalyzed(),
            "created_with_ghidra_version": program.getVersion(),
            "file_type": program.getExecutableFormat(),
            "file_location": program.getExecutablePath(),
            "elf_original_image_base": program.getExecutableBase(),
            "relocatable": program.isRelocatable()
        }
        
        save_project_metadata(metadata)

        # Log and save required libraries
        required_libs = program.getMemory().getExternalLibraries()
        for lib in required_libs:
            if not os.path.exists(lib):
                logging.warning(f"Missing library: {lib}")
                save_missing_library(lib)

        logging.info("Project metadata and library information gathered and saved.")
# utils/cpp_generator.py

import logging
from config import LLM_MODEL, DEBUG_MODE, MAX_CONTEXT_LENGTH, MAX_TOKENS, MODEL_FUNCTION_CALL_SETTINGS, LOG_FILE
from litellm import completion

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_cpp_code(pseudo_c_code, function_name, symbol_names="", comments=""):
    try:
        # Include symbols and comments in the prompt for context-awareness
        prompt_content = f"Convert the following pseudo-C code to structured C++:\n{pseudo_c_code}"
        if symbol_names or comments:
            prompt_content += f"\n\nSymbols:\n{symbol_names}\nComments:\n{comments}"
        
        cpp_code_response = completion(
            model=LLM_MODEL,
            messages=[{
                "role": "user",
                "content": prompt_content
            }],
            format="json",
            max_context_length=MAX_CONTEXT_LENGTH,
            max_tokens=MAX_TOKENS,
            function_call=MODEL_FUNCTION_CALL_SETTINGS
        )
        
        # Similarity check and feedback trigger
        cpp_code = cpp_code_response.get('output', {}).get('cpp_code', '')
        if not check_similarity(pseudo_c_code, cpp_code):
            logging.warning(f"Similarity check failed for '{function_name}'. Triggering feedback loop.")
            feedback_loop({function_name: function_metadata})
        
        return cpp_code
    except Exception as e:
        logging.error(f"Error generating C++ code for function '{function_name}': {str(e)}")
        return None
# utils/generate_file_structure.py

import os
import logging
from config import (DATABASE_PATH, CPP_OUTPUT_DIR, CMAKE_MINIMUM_VERSION, TARGET_NAME, CXX_STANDARD,
                    ADDITIONAL_LIBRARIES, LLVM_PATH, INCLUDE_DIRECTORIES, DEBUG_MODE, LOG_FILE)

logging.basicConfig(filename=LOG_FILE, level=logging.DEBUG if DEBUG_MODE else logging.INFO)

def generate_files_from_db():
    # Connect to database and fetch functions
    with sqlite3.connect(DATABASE_PATH) as conn:
        cursor = conn.cursor()
        project_metadata = gather_project_metadata()

        cursor.execute("SELECT function_id, name, cpp_code, offset, signature FROM cpp_generation WHERE validation_status = 'verified'")
        functions = cursor.fetchall()
        
        header_content = {}
        source_content = {}

        for func_id, func_name, cpp_code, offset, signature in functions:
            header_file, source_file = determine_file_structure(func_name, project_metadata)

            if header_file not in header_content:
                header_content[header_file] = ""
            header_content[header_file] += f"{signature};\n"

            if source_file not in source_content:
                source_content[source_file] = ""
            source_content[source_file] += cpp_code + "\n"

        # Write header and source files
        for filename, content in header_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "include", filename), "w") as f:
                f.write("#pragma once\n\n" + content)
        
        for filename, content in source_content.items():
            with open(os.path.join(CPP_OUTPUT_DIR, "src", filename), "w") as f:
                f.write(content)

        generate_cmake_file(project_metadata)

def determine_file_structure(func_name, project_metadata):
    module = project_metadata.get("modules", {}).get(func_name)
    namespace = project_metadata.get("namespaces", {}).get(func_name)

    if module:
        module_dir = os.path.join(CPP_OUTPUT_DIR, "src", module)
        os.makedirs(module_dir, exist_ok=True)
        header_file = os.path.join(module, f"{func_name}.h")
        source_file = os.path.join(module, f"{func_name}.cpp")
    elif namespace:
        namespace_dir = os.path.join(CPP_OUTPUT_DIR, "src", namespace)
        os.makedirs(namespace_dir, exist_ok=True)
        header_file = os.path.join(namespace, f"{func_name}.h")
        source_file = os.path.join(namespace, f"{func_name}.cpp")
    else:
        header_file = f"{func_name}.h"
        source_file = f"{func_name}.cpp"

    return header_file, source_file

def generate_cmake_file(project_metadata):
    # Build CMake content
    library_includes = "\n".join([f"target_link_libraries({TARGET_NAME} {lib})" for lib in ADDITIONAL_LIBRARIES])
    include_directories = "\n".join([f"include_directories({dir})" for dir in INCLUDE_DIRECTORIES])

    cmake_content = f"""
    cmake_minimum_required(VERSION {CMAKE_MINIMUM_VERSION})
    project({TARGET_NAME})

    set(CMAKE_CXX_STANDARD {CXX_STANDARD})

    {include_directories}

    # Add sources
    file(GLOB SOURCES "src/**/*.cpp")

    # Define executable
    add_executable({TARGET_NAME} ${{SOURCES}})

    # Link libraries
    {library_includes}

    # LLVM configuration (if needed)
    if (EXISTS "{LLVM_PATH}")
        find_package(LLVM REQUIRED PATHS "{LLVM_PATH}")
        target_include_directories({TARGET_NAME} PRIVATE ${{LLVM_INCLUDE_DIRS}})
        target_link_libraries({TARGET_NAME} ${{LLVM_LIBS}})
        add_definitions(${{LLVM_DEFINITIONS}})
    endif()
    """

    with open(os.path.join(CPP_OUTPUT_DIR, "CMakeLists.txt"), "w") as cmake_file:
        cmake_file.write(cmake_content)
function_schema.json:
{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "address": {"type": "string"},
    "offset": {"type": "integer"},
    "instructions": {
      "type": "array",
      "items": {"type": "string"}
    },
    "pseudo_code": {"type": "string"},
    "cpp_code": {"type": "string"}
  },
  "required": ["name", "address", "instructions", "pseudo_code", "cpp_code"]
}

```
Assistant: