#include "pch.h"
#include "Vulkan_Spoofing.h"

#include <Config.h>
#include <SysUtils.h>

#include <proxies/KernelBase_Proxy.h>
#include <hooks/VulkanwDx12_Hooks.h>

#include <magic_enum.hpp>

#include <detours/detours.h>

#include <vulkan/vulkan_core.h>
#include <misc/IdentifyGpu.h>

static std::map<std::string, bool> vkDeviceExtensions;
static std::map<std::string, bool> vkInstanceExtensions;

typedef struct VkDummyProps
{
    VkStructureType sType;
    void* pNext;
} VkDummyProps;

static PFN_vkGetPhysicalDeviceProperties o_vkGetPhysicalDeviceProperties = nullptr;
static PFN_vkGetPhysicalDeviceProperties2 o_vkGetPhysicalDeviceProperties2 = nullptr;
static PFN_vkGetPhysicalDeviceProperties2KHR o_vkGetPhysicalDeviceProperties2KHR = nullptr;
static PFN_vkGetPhysicalDeviceMemoryProperties o_vkGetPhysicalDeviceMemoryProperties = nullptr;
static PFN_vkGetPhysicalDeviceMemoryProperties2 o_vkGetPhysicalDeviceMemoryProperties2 = nullptr;
static PFN_vkGetPhysicalDeviceMemoryProperties2KHR o_vkGetPhysicalDeviceMemoryProperties2KHR = nullptr;
static PFN_vkEnumerateDeviceExtensionProperties o_vkEnumerateDeviceExtensionProperties = nullptr;
static PFN_vkEnumerateInstanceExtensionProperties o_vkEnumerateInstanceExtensionProperties = nullptr;

static uint32_t vkEnumerateInstanceExtensionPropertiesCount = 0;
static uint32_t vkEnumerateDeviceExtensionPropertiesCount = 0;
static bool vkEnumerateDeviceExtensionPropertiesListed = false;
static bool vkEnumerateInstanceExtensionPropertiesListed = false;

inline static void hkvkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
                                                         VkPhysicalDeviceMemoryProperties* pMemoryProperties)
{
    o_vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);

    if (pMemoryProperties == nullptr)
        return;

    for (size_t i = 0; i < pMemoryProperties->memoryHeapCount; i++)
    {
        if (pMemoryProperties->memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
        {
            uint64_t newMemSize = (uint64_t) Config::Instance()->VulkanVRAM.value() * 1073741824; // 1024 * 1024 * 1024
            pMemoryProperties->memoryHeaps[i].size = newMemSize;
        }
    }
}

inline static void hkvkGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice,
                                                          VkPhysicalDeviceMemoryProperties2* pMemoryProperties)
{
    o_vkGetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);

    if (pMemoryProperties == nullptr ||
        pMemoryProperties->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2)
        return;

    for (size_t i = 0; i < pMemoryProperties->memoryProperties.memoryHeapCount; i++)
    {
        if (pMemoryProperties->memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
        {
            uint64_t newMemSize = (uint64_t) Config::Instance()->VulkanVRAM.value() * 1073741824; // 1024 * 1024 * 1024
            pMemoryProperties->memoryProperties.memoryHeaps[i].size = newMemSize;
        }
    }
}

inline static void hkvkGetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice,
                                                             VkPhysicalDeviceMemoryProperties2* pMemoryProperties)
{
    o_vkGetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);

    if (pMemoryProperties == nullptr ||
        pMemoryProperties->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR)
        return;

    for (size_t i = 0; i < pMemoryProperties->memoryProperties.memoryHeapCount; i++)
    {
        if (pMemoryProperties->memoryProperties.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
        {
            uint64_t newMemSize = (uint64_t) Config::Instance()->VulkanVRAM.value() * 1073741824; // 1024 * 1024 * 1024
            pMemoryProperties->memoryProperties.memoryHeaps[i].size = newMemSize;
        }
    }
}

inline static void hkvkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device,
                                                   VkPhysicalDeviceProperties* properties)
{
    o_vkGetPhysicalDeviceProperties(physical_device, properties);

    auto targetVendorIdMatches = !Config::Instance()->TargetVendorId.has_value() ||
                                 Config::Instance()->TargetVendorId.value() == properties->vendorID;

    auto targetDeviceIdMatches = !Config::Instance()->TargetDeviceId.has_value() ||
                                 Config::Instance()->TargetDeviceId.value() == properties->deviceID;

    // Spoof
    if (!State::Instance().skipSpoofing && targetVendorIdMatches && targetDeviceIdMatches)
    {
        auto deviceName = wstring_to_string(Config::Instance()->SpoofedGPUName.value_or_default());
        std::strcpy(properties->deviceName, deviceName.c_str());

        properties->vendorID = Config::Instance()->SpoofedVendorId.value_or_default();
        properties->deviceID = Config::Instance()->SpoofedDeviceId.value_or_default();
        properties->driverVersion = VK_MAKE_API_VERSION(999, 99, 0, 0);

        LOG_DEBUG("Spoofed");
    }
    else
    {
        LOG_DEBUG("Skipping spoofing");
    }
}

inline static void hkvkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, VkPhysicalDeviceProperties2* properties2)
{
    o_vkGetPhysicalDeviceProperties2(phys_dev, properties2);

    auto targetVendorIdMatches = !Config::Instance()->TargetVendorId.has_value() ||
                                 Config::Instance()->TargetVendorId.value() == properties2->properties.vendorID;

    auto targetDeviceIdMatches = !Config::Instance()->TargetDeviceId.has_value() ||
                                 Config::Instance()->TargetDeviceId.value() == properties2->properties.deviceID;

    // Spoof
    if (!State::Instance().skipSpoofing && targetVendorIdMatches && targetDeviceIdMatches)
    {
        auto deviceName = wstring_to_string(Config::Instance()->SpoofedGPUName.value_or_default());
        std::strcpy(properties2->properties.deviceName, deviceName.c_str());
        properties2->properties.vendorID = Config::Instance()->SpoofedVendorId.value_or_default();
        properties2->properties.deviceID = Config::Instance()->SpoofedDeviceId.value_or_default();
        properties2->properties.driverVersion = VK_MAKE_API_VERSION(999, 99, 0, 0);

        // If spoofing Nvidia
        if (Config::Instance()->SpoofedVendorId.value_or_default() == VendorId::Nvidia)
        {
            auto next = (VkDummyProps*) properties2->pNext;

            while (next != nullptr)
            {
                if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES)
                {
                    auto ddp = (VkPhysicalDeviceDriverProperties*) (void*) next;
                    ddp->driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY;
                    std::strcpy(ddp->driverName, "NVIDIA");
                    std::strcpy(ddp->driverInfo, "999.99");
                }

                next = (VkDummyProps*) next->pNext;
            }
        }

        LOG_DEBUG("Spoofed");
    }
    else
    {
        LOG_DEBUG("Skipping spoofing");
    }
}

inline static void hkvkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev,
                                                       VkPhysicalDeviceProperties2* properties2)
{
    o_vkGetPhysicalDeviceProperties2KHR(phys_dev, properties2);

    auto targetVendorIdMatches = !Config::Instance()->TargetVendorId.has_value() ||
                                 Config::Instance()->TargetVendorId.value() == properties2->properties.vendorID;

    auto targetDeviceIdMatches = !Config::Instance()->TargetDeviceId.has_value() ||
                                 Config::Instance()->TargetDeviceId.value() == properties2->properties.deviceID;

    // Spoof
    if (!State::Instance().skipSpoofing && targetVendorIdMatches && targetDeviceIdMatches)
    {
        auto deviceName = wstring_to_string(Config::Instance()->SpoofedGPUName.value_or_default());
        std::strcpy(properties2->properties.deviceName, deviceName.c_str());
        properties2->properties.vendorID = Config::Instance()->SpoofedVendorId.value_or_default();
        properties2->properties.deviceID = Config::Instance()->SpoofedDeviceId.value_or_default();
        properties2->properties.driverVersion = VK_MAKE_API_VERSION(999, 99, 0, 0);

        // If spoofing Nvidia
        if (Config::Instance()->SpoofedVendorId.value_or_default() == VendorId::Nvidia)
        {
            auto next = (VkDummyProps*) properties2->pNext;

            while (next != nullptr)
            {
                if (next->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES)
                {
                    auto ddp = (VkPhysicalDeviceDriverProperties*) (void*) next;
                    ddp->driverID = VK_DRIVER_ID_NVIDIA_PROPRIETARY;
                    std::strcpy(ddp->driverName, "NVIDIA");
                    std::strcpy(ddp->driverInfo, "999.99");
                }

                next = (VkDummyProps*) next->pNext;
            }
        }

        LOG_DEBUG("Spoofed");
    }
    else
    {
        LOG_DEBUG("Skipping spoofing");
    }
}

inline static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback(
    VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
    const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
{
    LOG_TRACE("{}", pCallbackData->pMessage);
    return VK_FALSE; // return VK_TRUE to abort calls that triggered validation errors
}

VkResult VulkanSpoofing::hkvkCreateInstance(VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator,
                                            VkInstance* pInstance)
{
    if (State::Instance().creatingD3DDevice)
    {
        LOG_INFO("Skipping because DXVK/VKD3D is creating a D3D device");
        return VK_SUCCESS;
    }

    if (pCreateInfo == nullptr)
        return VK_ERROR_INITIALIZATION_FAILED;

    if (vkInstanceExtensions.size() == 0)
    {
        auto enumarate = o_vkEnumerateInstanceExtensionProperties;
        if (o_vkEnumerateInstanceExtensionProperties == nullptr)
        {
            if (vulkanModule == nullptr)
                vulkanModule = KernelBaseProxy::GetModuleHandleA_()("vulkan-1.dll");

            if (vulkanModule != nullptr)
            {
                enumarate = (PFN_vkEnumerateInstanceExtensionProperties) KernelBaseProxy::GetProcAddress_()(
                    vulkanModule, "vkEnumerateInstanceExtensionProperties");
            }

            if (enumarate == nullptr)
            {
                enumarate = vkEnumerateInstanceExtensionProperties;
            }
        }

        if (enumarate != nullptr)
        {
            LOG_INFO("vkInstanceExtensions is empty, enumerating instance extensions");
            vkEnumerateInstanceExtensionPropertiesListed = true;
            vkEnumerateInstanceExtensionPropertiesCount = 0;

            enumarate(VK_NULL_HANDLE, &vkEnumerateInstanceExtensionPropertiesCount, VK_NULL_HANDLE);
            std::vector<VkExtensionProperties> extensions(vkEnumerateInstanceExtensionPropertiesCount);
            enumarate(VK_NULL_HANDLE, &vkEnumerateInstanceExtensionPropertiesCount, extensions.data());
            for (const auto& ext : extensions)
            {
                vkInstanceExtensions[ext.extensionName] = true;
                LOG_DEBUG("  {}", ext.extensionName);
            }
        }
    }

    if (pCreateInfo->pApplicationInfo != nullptr && pCreateInfo->pApplicationInfo->pApplicationName != nullptr)
    {
        LOG_DEBUG("ApplicationName: {}", pCreateInfo->pApplicationInfo->pApplicationName);
    }

    static std::vector<const char*> newExtensionList;
    newExtensionList.clear();

    LOG_DEBUG("Extensions ({}):", pCreateInfo->enabledExtensionCount);
    for (size_t i = 0; i < pCreateInfo->enabledExtensionCount; i++)
    {
        LOG_DEBUG("  {}", pCreateInfo->ppEnabledExtensionNames[i]);
        newExtensionList.push_back(pCreateInfo->ppEnabledExtensionNames[i]);
    }

    auto primaryGpu = IdentifyGpu::getPrimaryGpu();

    if (primaryGpu.dlssCapable && Config::Instance()->DLSSEnabled.value_or_default())
    {
        LOG_INFO("Adding NVNGX Vulkan extensions");
        if (vkInstanceExtensions.contains(std::string(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
            newExtensionList.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
        }

        if (vkInstanceExtensions.contains(std::string(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
            newExtensionList.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
        }

        if (vkInstanceExtensions.contains(std::string(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
            newExtensionList.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
        }
    }

    LOG_INFO("Adding FFX Vulkan extensions");
    if (vkInstanceExtensions.contains(std::string(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
        newExtensionList.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
    }

    LOG_INFO("Adding Vulkan w/Dx12 extensions");
    if (vkInstanceExtensions.contains(std::string(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
    }

    if (vkInstanceExtensions.contains(std::string(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
    }

    LOG_DEBUG("Layer count: {}", pCreateInfo->enabledLayerCount);
    for (size_t i = 0; i < pCreateInfo->enabledLayerCount; i++)
        LOG_DEBUG("  {}", pCreateInfo->ppEnabledLayerNames[i]);

#ifdef VULKAN_DEBUG_LAYER
    debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
    debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
                                      VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
                                      VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;

    debugCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
                                  VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
                                  VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;

    debugCreateInfo.pfnUserCallback = &VulkanDebugCallback;

    static std::vector<const char*> newLayerList;
    newLayerList.clear();
    newLayerList.push_back("VK_LAYER_KHRONOS_validation");

    for (size_t i = 0; i < pCreateInfo->enabledLayerCount; i++)
        newLayerList.push_back(pCreateInfo->ppEnabledLayerNames[i]);

    pCreateInfo->enabledLayerCount = static_cast<uint32_t>(newLayerList.size());
    pCreateInfo->ppEnabledLayerNames = newLayerList.data();

    auto next = (VkDummyProps*) pCreateInfo;

    while (next->pNext != nullptr)
    {
        next = (VkDummyProps*) next->pNext;
    }

    next->pNext = &debugCreateInfo;

    newExtensionList.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
#endif

    pCreateInfo->enabledExtensionCount = static_cast<uint32_t>(newExtensionList.size());
    pCreateInfo->ppEnabledExtensionNames = newExtensionList.data();

    return VK_SUCCESS;
}

VkResult VulkanSpoofing::hkvkCreateDevice(VkPhysicalDevice physicalDevice, VkDeviceCreateInfo* pCreateInfo,
                                          const VkAllocationCallbacks* pAllocator, VkDevice* pDevice)
{
    if (State::Instance().creatingD3DDevice)
    {
        LOG_INFO("Skipping because DXVK/VKD3D is creating a D3D device");
        return VK_SUCCESS;
    }

    LOG_FUNC();

    if (vkDeviceExtensions.size() == 0)
    {
        LOG_INFO("vkDeviceExtensions is empty, enumerating device extensions");
        auto enumarate = o_vkEnumerateDeviceExtensionProperties;
        if (o_vkEnumerateDeviceExtensionProperties == nullptr)
        {
            if (vulkanModule == nullptr)
                vulkanModule = KernelBaseProxy::GetModuleHandleA_()("vulkan-1.dll");

            if (vulkanModule != nullptr)
            {
                enumarate = (PFN_vkEnumerateDeviceExtensionProperties) KernelBaseProxy::GetProcAddress_()(
                    vulkanModule, "vkEnumerateDeviceExtensionProperties");
            }

            if (enumarate == nullptr)
            {
                enumarate = vkEnumerateDeviceExtensionProperties;
            }
        }

        if (enumarate != nullptr)
        {
            vkEnumerateDeviceExtensionPropertiesListed = true;
            vkEnumerateDeviceExtensionPropertiesCount = 0;

            enumarate(physicalDevice, nullptr, &vkEnumerateDeviceExtensionPropertiesCount, nullptr);
            std::vector<VkExtensionProperties> extensions(vkEnumerateDeviceExtensionPropertiesCount);
            enumarate(physicalDevice, nullptr, &vkEnumerateDeviceExtensionPropertiesCount, extensions.data());

            for (const auto& ext : extensions)
            {
                vkDeviceExtensions[ext.extensionName] = true;
                LOG_DEBUG("  {}", ext.extensionName);
            }
        }
    }

    static std::vector<const char*> newExtensionList;
    newExtensionList.clear();

    auto primaryGpu = IdentifyGpu::getPrimaryGpu();

    LOG_DEBUG("Checking extensions and removing Streamline ones");
    for (size_t i = 0; i < pCreateInfo->enabledExtensionCount; i++)
    {
        auto extName = pCreateInfo->ppEnabledExtensionNames[i];

        if (Config::Instance()->VulkanExtensionSpoofing.value_or_default() && primaryGpu.vendorId != VendorId::Nvidia)
        {
            auto binaryImport = std::strcmp(extName, VK_NVX_BINARY_IMPORT_EXTENSION_NAME) == 0;
            auto imgViewHandle = std::strcmp(extName, VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME) == 0;
            auto bufferDeviceAddr = std::strcmp(extName, VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) == 0;
            auto mvPerViewAttr = std::strcmp(extName, VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME) == 0;
            auto nvLowLatency = std::strcmp(extName, VK_NV_LOW_LATENCY_EXTENSION_NAME) == 0;
            auto nvOpticalFlow = std::strcmp(extName, VK_NV_OPTICAL_FLOW_EXTENSION_NAME) == 0;
            auto nvPresentMeter = std::strcmp(extName, VK_NV_PRESENT_METERING_EXTENSION_NAME) == 0;

            if (binaryImport || imgViewHandle || bufferDeviceAddr || mvPerViewAttr || nvLowLatency || nvOpticalFlow ||
                nvPresentMeter)
            {
                LOG_DEBUG("Removing {}", extName);
                continue;
            }
        }

        // LOG_DEBUG("Adding {}", extName);
        newExtensionList.push_back(extName);
    }

    if (primaryGpu.vendorId == VendorId::Nvidia)
    {
        LOG_INFO("Adding NVNGX Vulkan extensions");
        if (vkDeviceExtensions.contains(std::string(VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME);
            newExtensionList.push_back(VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME);
        }

        if (vkDeviceExtensions.contains(std::string(VK_NV_LOW_LATENCY_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_NV_LOW_LATENCY_EXTENSION_NAME);
            newExtensionList.push_back(VK_NV_LOW_LATENCY_EXTENSION_NAME);
        }

        if (vkDeviceExtensions.contains(std::string(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
            newExtensionList.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
        }

        if (vkDeviceExtensions.contains(std::string(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)))
        {
            LOG_DEBUG("  Adding {}", VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
            newExtensionList.push_back(VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
        }

        if (primaryGpu.dlssCapable)
        {
            if (vkDeviceExtensions.contains(std::string(VK_NVX_BINARY_IMPORT_EXTENSION_NAME)))
            {
                LOG_DEBUG("  Adding {}", VK_NVX_BINARY_IMPORT_EXTENSION_NAME);
                newExtensionList.push_back(VK_NVX_BINARY_IMPORT_EXTENSION_NAME);
            }

            if (vkDeviceExtensions.contains(std::string(VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME)))
            {
                LOG_DEBUG("  Adding {}", VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME);
                newExtensionList.push_back(VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME);
            }
        }
    }

    LOG_INFO("Adding FFX Vulkan extensions");
    if (vkDeviceExtensions.contains(std::string(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
    }

    LOG_INFO("Adding XeSS Vulkan extensions");
    if (vkDeviceExtensions.contains(std::string(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_SHADER_INTEGER_DOT_PRODUCT_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME);
        newExtensionList.push_back(VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME);
    }

    LOG_INFO("Adding Vk w/Dx12 Vulkan extensions");

    if (vkDeviceExtensions.contains(std::string(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
    }

    if (vkDeviceExtensions.contains(std::string(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
    }

#ifdef USE_QUEUE_SUBMIT_2_KHR
    LOG_INFO("Adding QueueSubmit2 Vulkan extensions");
    if (vkDeviceExtensions.contains(std::string(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)))
    {
        LOG_DEBUG("  Adding {}", VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
        newExtensionList.push_back(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
    }
#endif

    if (State::Instance().vkAntiLagSupported)
    {
        LOG_INFO("Adding AntiLag extension");
        newExtensionList.push_back(VK_AMD_ANTI_LAG_EXTENSION_NAME);
    }

    pCreateInfo->enabledExtensionCount = static_cast<uint32_t>(newExtensionList.size());
    pCreateInfo->ppEnabledExtensionNames = newExtensionList.data();

    LOG_DEBUG("Final extension count: {0}", pCreateInfo->enabledExtensionCount);

    // We already listing extensions above, no need for this
    LOG_DEBUG("Extensions:");

    for (size_t i = 0; i < pCreateInfo->enabledExtensionCount; i++)
        LOG_DEBUG("  {0}", pCreateInfo->ppEnabledExtensionNames[i]);

    return VK_SUCCESS;
}

inline static VkResult hkvkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName,
                                                              uint32_t* pPropertyCount,
                                                              VkExtensionProperties* pProperties)
{
    LOG_FUNC();

    uint32_t count = 0;

    if (pPropertyCount != nullptr)
        count = *pPropertyCount;

    if (pProperties == nullptr)
        count = 0;

    auto result = o_vkEnumerateDeviceExtensionProperties(physicalDevice, pLayerName, pPropertyCount, pProperties);

    if (result != VK_SUCCESS)
    {
        LOG_ERROR("o_vkEnumerateDeviceExtensionProperties({}) result: {:X}", count, (UINT) result);
        return result;
    }

    if (!State::Instance().skipSpoofing)
    {
        // Count query, modify and add 5 to final count
        if (pProperties == nullptr && pPropertyCount != nullptr && count == 0)
        {
            if (State::Instance().activeFgInput == FGInput::DLSSG ||
                State::Instance().activeFgInput == FGInput::NvngxFG)
                *pPropertyCount += 7;
            else
                *pPropertyCount += 5;

            vkEnumerateDeviceExtensionPropertiesCount = *pPropertyCount;
            LOG_TRACE("vkEnumerateDeviceExtensionProperties count: {}", *pPropertyCount);
            return result;
        }

        // If this is request of our modified count query (count == vkEnumerateDeviceExtensionPropertiesCount)
        if (pProperties != nullptr && pPropertyCount != nullptr && *pPropertyCount > 0 &&
            count == vkEnumerateDeviceExtensionPropertiesCount)
        {
            // Set back modified extension count
            *pPropertyCount = count;

            // And fill extension info at the end
            VkExtensionProperties bi { VK_NVX_BINARY_IMPORT_EXTENSION_NAME, VK_NVX_BINARY_IMPORT_SPEC_VERSION };
            memcpy(&pProperties[*pPropertyCount - 1], &bi, sizeof(VkExtensionProperties));

            VkExtensionProperties ivh { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME,
                                        VK_NVX_IMAGE_VIEW_HANDLE_SPEC_VERSION };
            memcpy(&pProperties[*pPropertyCount - 2], &ivh, sizeof(VkExtensionProperties));

            VkExtensionProperties mpva { VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME,
                                         VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION };
            memcpy(&pProperties[*pPropertyCount - 3], &mpva, sizeof(VkExtensionProperties));

            VkExtensionProperties ll { VK_NV_LOW_LATENCY_EXTENSION_NAME, VK_NV_LOW_LATENCY_SPEC_VERSION };
            memcpy(&pProperties[*pPropertyCount - 4], &ll, sizeof(VkExtensionProperties));

            VkExtensionProperties bda { VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
                                        VK_EXT_BUFFER_DEVICE_ADDRESS_SPEC_VERSION };
            memcpy(&pProperties[*pPropertyCount - 5], &bda, sizeof(VkExtensionProperties));

            if (State::Instance().activeFgInput == FGInput::DLSSG ||
                State::Instance().activeFgInput == FGInput::NvngxFG)
            {
                VkExtensionProperties of { VK_NV_OPTICAL_FLOW_EXTENSION_NAME, VK_NV_OPTICAL_FLOW_SPEC_VERSION };
                memcpy(&pProperties[*pPropertyCount - 6], &of, sizeof(VkExtensionProperties));

                VkExtensionProperties pm { VK_NV_PRESENT_METERING_EXTENSION_NAME, VK_NV_PRESENT_METERING_SPEC_VERSION };
                memcpy(&pProperties[*pPropertyCount - 7], &pm, sizeof(VkExtensionProperties));
            }
        }
        else
        {
            LOG_DEBUG("Not adding any extensions!");
        }
    }

    if (!vkEnumerateDeviceExtensionPropertiesListed && count != 0)
    {
        vkEnumerateDeviceExtensionPropertiesListed = true;

        auto minusCount = 5;
        if (State::Instance().activeFgInput == FGInput::DLSSG || State::Instance().activeFgInput == FGInput::NvngxFG)
            minusCount = 7;

        LOG_DEBUG("Extensions returned:");
        for (uint32_t i = 0; i < *pPropertyCount; i++)
        {
            LOG_DEBUG("  {}", pProperties[i].extensionName);

            if (!State::Instance().skipSpoofing && i < (*pPropertyCount - minusCount))
                vkDeviceExtensions.insert_or_assign(std::string(pProperties[i].extensionName), true);
        }
    }

    LOG_FUNC_RESULT(result);

    return result;
}

inline static VkResult hkvkEnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount,
                                                                VkExtensionProperties* pProperties)
{
    LOG_FUNC();

    auto count = *pPropertyCount;

    if (pProperties == nullptr)
        count = 0;

    auto result = o_vkEnumerateInstanceExtensionProperties(pLayerName, pPropertyCount, pProperties);

    if (result != VK_SUCCESS)
    {
        LOG_ERROR("o_vkEnumerateInstanceExtensionProperties({}) result: {:X}", count, (UINT) result);
        return result;
    }

    if (!State::Instance().skipSpoofing)
    {
        if (pLayerName == nullptr && pProperties == nullptr && count == 0)
        {
            LOG_TRACE("hkvkEnumerateDeviceExtensionProperties count: {}", vkEnumerateDeviceExtensionPropertiesCount);
            return result;
        }
    }

    if (pPropertyCount != nullptr && pProperties != nullptr && count != 0)
    {
        if (!vkEnumerateInstanceExtensionPropertiesListed)
        {
            vkEnumerateInstanceExtensionPropertiesListed = true;

            LOG_DEBUG("Extensions returned:");
            for (size_t i = 0; i < *pPropertyCount; i++)
            {
                LOG_DEBUG("  {}", pProperties[i].extensionName);
                vkInstanceExtensions.insert_or_assign(std::string(pProperties[i].extensionName), true);
            }
        }
    }

    LOG_FUNC_RESULT(result);

    return result;
}

PFN_vkVoidFunction VulkanSpoofing::hkvkGetInstanceProcAddr(const PFN_vkVoidFunction orgFunc, const char* pName)
{
    auto procName = std::string(pName);

    auto result = Vulkan_wDx12::GetInstanceProcAddr(orgFunc, pName);
    if (result != VK_NULL_HANDLE)
        return result;

    if (Config::Instance()->VulkanSpoofing.value_or_default())
    {
        if (procName == std::string("vkGetPhysicalDeviceProperties"))
        {
            if (o_vkGetPhysicalDeviceProperties == nullptr)
                o_vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties;
        }
        else if (procName == std::string("vkGetPhysicalDeviceProperties2"))
        {
            if (o_vkGetPhysicalDeviceProperties2 == nullptr)
                o_vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties2");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties2;
        }
        else if (procName == std::string("vkGetPhysicalDeviceProperties2KHR"))
        {
            if (o_vkGetPhysicalDeviceProperties2KHR == nullptr)
                o_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties2KHR");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties2KHR;
        }
    }

    if (Config::Instance()->VulkanExtensionSpoofing.value_or_default())
    {
        if (procName == std::string("vkEnumerateInstanceExtensionProperties"))
        {
            if (o_vkEnumerateInstanceExtensionProperties == nullptr)
                o_vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) orgFunc;

            LOG_DEBUG("vkEnumerateInstanceExtensionProperties");
            return (PFN_vkVoidFunction) hkvkEnumerateInstanceExtensionProperties;
        }
        else if (procName == std::string("vkEnumerateDeviceExtensionProperties"))
        {
            if (o_vkEnumerateDeviceExtensionProperties == nullptr)
                o_vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) orgFunc;

            LOG_DEBUG("vkEnumerateDeviceExtensionProperties");
            return (PFN_vkVoidFunction) hkvkEnumerateDeviceExtensionProperties;
        }
    }

    if (Config::Instance()->VulkanVRAM.has_value())
    {
        if (procName == std::string("vkGetPhysicalDeviceMemoryProperties"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties;
        }
        else if (procName == std::string("vkGetPhysicalDeviceMemoryProperties2"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties2 == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties2");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties2;
        }
        else if (procName == std::string("vkGetPhysicalDeviceMemoryProperties2KHR"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties2KHR == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties2KHR");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties2KHR;
        }
    }

    return orgFunc;
}

PFN_vkVoidFunction VulkanSpoofing::hkvkGetDeviceProcAddr(const PFN_vkVoidFunction orgFunc, const char* pName)
{
    auto procName = std::string(pName);

    auto result = Vulkan_wDx12::GetDeviceProcAddr(orgFunc, pName);
    if (result != VK_NULL_HANDLE)
        return result;

    if (Config::Instance()->VulkanSpoofing.value_or_default())
    {
        if (procName == std::string("vkGetPhysicalDeviceProperties"))
        {
            if (o_vkGetPhysicalDeviceProperties == nullptr)
                o_vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties;
        }
        else if (procName == std::string("vkGetPhysicalDeviceProperties2"))
        {
            if (o_vkGetPhysicalDeviceProperties2 == nullptr)
                o_vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties2");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties2;
        }
        else if (procName == std::string("vkGetPhysicalDeviceProperties2KHR"))
        {
            if (o_vkGetPhysicalDeviceProperties2KHR == nullptr)
                o_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceProperties2KHR");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceProperties2KHR;
        }
    }

    if (Config::Instance()->VulkanExtensionSpoofing.value_or_default())
    {
        if (procName == std::string("vkEnumerateInstanceExtensionProperties"))
        {
            if (o_vkEnumerateInstanceExtensionProperties == nullptr)
                o_vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) orgFunc;

            LOG_DEBUG("vkEnumerateInstanceExtensionProperties");
            return (PFN_vkVoidFunction) hkvkEnumerateInstanceExtensionProperties;
        }
        else if (procName == std::string("vkEnumerateDeviceExtensionProperties"))
        {
            if (o_vkEnumerateDeviceExtensionProperties == nullptr)
                o_vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) orgFunc;

            LOG_DEBUG("vkEnumerateDeviceExtensionProperties");
            return (PFN_vkVoidFunction) hkvkEnumerateDeviceExtensionProperties;
        }
    }

    if (Config::Instance()->VulkanVRAM.has_value())
    {
        if (procName == std::string("vkGetPhysicalDeviceMemoryProperties"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties;
        }
        else if (procName == std::string("vkGetPhysicalDeviceMemoryProperties2"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties2 == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties2");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties2;
        }
        else if (procName == std::string("vkGetPhysicalDeviceMemoryProperties2KHR"))
        {
            if (o_vkGetPhysicalDeviceMemoryProperties2KHR == nullptr)
                o_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR) orgFunc;

            LOG_DEBUG("vkGetPhysicalDeviceMemoryProperties2KHR");
            return (PFN_vkVoidFunction) hkvkGetPhysicalDeviceMemoryProperties2KHR;
        }
    }

    return orgFunc;
}

void VulkanSpoofing::HookForVulkanSpoofing(HMODULE vulkanModule)
{
    Vulkan_wDx12::Hook(vulkanModule);

    if (Config::Instance()->VulkanSpoofing.value_or_default() && o_vkGetPhysicalDeviceProperties == nullptr)
    {
        FARPROC address = nullptr;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceProperties");
        o_vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) address;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceProperties2");
        o_vkGetPhysicalDeviceProperties2 = (PFN_vkGetPhysicalDeviceProperties2) address;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceProperties2KHR");
        o_vkGetPhysicalDeviceProperties2KHR = (PFN_vkGetPhysicalDeviceProperties2KHR) address;

        if (o_vkGetPhysicalDeviceProperties != nullptr)
        {
            LOG_INFO("Attaching Vulkan device spoofing hooks");

            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());

            if (o_vkGetPhysicalDeviceProperties)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceProperties, hkvkGetPhysicalDeviceProperties);

            if (o_vkGetPhysicalDeviceProperties2)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceProperties2, hkvkGetPhysicalDeviceProperties2);

            if (o_vkGetPhysicalDeviceProperties2KHR)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceProperties2KHR, hkvkGetPhysicalDeviceProperties2KHR);

            auto detourResult = DetourTransactionCommit();
            if (detourResult != NO_ERROR)
            {
                LOG_ERROR("Failed to attach Vulkan device spoofing hooks: {:X}", detourResult);
                o_vkGetPhysicalDeviceProperties = nullptr;
                o_vkGetPhysicalDeviceProperties2 = nullptr;
                o_vkGetPhysicalDeviceProperties2KHR = nullptr;
            }
        }
    }
}

void VulkanSpoofing::HookForVulkanExtensionSpoofing(HMODULE vulkanModule)
{
    if (o_vkEnumerateInstanceExtensionProperties == nullptr)
    {
        FARPROC address = nullptr;

        if (Config::Instance()->VulkanExtensionSpoofing.value_or_default())
        {
            address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkEnumerateInstanceExtensionProperties");
            o_vkEnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) address;

            address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkEnumerateDeviceExtensionProperties");
            o_vkEnumerateDeviceExtensionProperties = (PFN_vkEnumerateDeviceExtensionProperties) address;
        }

        if (o_vkEnumerateInstanceExtensionProperties != nullptr || o_vkEnumerateDeviceExtensionProperties != nullptr)
        {
            LOG_INFO("Attaching Vulkan extensions spoofing hooks");

            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());

            if (o_vkEnumerateInstanceExtensionProperties)
                DetourAttach(&(PVOID&) o_vkEnumerateInstanceExtensionProperties,
                             hkvkEnumerateInstanceExtensionProperties);

            if (o_vkEnumerateDeviceExtensionProperties)
                DetourAttach(&(PVOID&) o_vkEnumerateDeviceExtensionProperties, hkvkEnumerateDeviceExtensionProperties);

            auto detourResult = DetourTransactionCommit();
            if (detourResult != NO_ERROR)
            {
                LOG_ERROR("Failed to attach Vulkan extensions spoofing hooks: {:X}", detourResult);
                o_vkEnumerateInstanceExtensionProperties = nullptr;
                o_vkEnumerateDeviceExtensionProperties = nullptr;
            }
        }
    }
}

void VulkanSpoofing::HookForVulkanVRAMSpoofing(HMODULE vulkanModule)
{
    if (Config::Instance()->VulkanVRAM.has_value() && o_vkGetPhysicalDeviceMemoryProperties == nullptr)
    {
        FARPROC address = nullptr;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceMemoryProperties");
        o_vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties) address;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceMemoryProperties2");
        o_vkGetPhysicalDeviceMemoryProperties2 = (PFN_vkGetPhysicalDeviceMemoryProperties2) address;

        address = KernelBaseProxy::GetProcAddress_()(vulkanModule, "vkGetPhysicalDeviceMemoryProperties2KHR");
        o_vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR) address;

        if (o_vkGetPhysicalDeviceMemoryProperties != nullptr || o_vkGetPhysicalDeviceMemoryProperties2 != nullptr ||
            o_vkGetPhysicalDeviceMemoryProperties2KHR != nullptr)
        {
            LOG_INFO("Attaching Vulkan VRAM spoofing hooks");

            DetourTransactionBegin();
            DetourUpdateThread(GetCurrentThread());

            if (o_vkGetPhysicalDeviceMemoryProperties != nullptr)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceMemoryProperties, hkvkGetPhysicalDeviceMemoryProperties);

            if (o_vkGetPhysicalDeviceMemoryProperties2 != nullptr)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceMemoryProperties2, hkvkGetPhysicalDeviceMemoryProperties2);

            if (o_vkGetPhysicalDeviceMemoryProperties2KHR != nullptr)
                DetourAttach(&(PVOID&) o_vkGetPhysicalDeviceMemoryProperties2KHR,
                             hkvkGetPhysicalDeviceMemoryProperties2KHR);

            auto detourResult = DetourTransactionCommit();
            if (detourResult != NO_ERROR)
            {
                LOG_ERROR("Failed to attach Vulkan VRAM spoofing hooks: {:X}", detourResult);
                o_vkGetPhysicalDeviceMemoryProperties = nullptr;
                o_vkGetPhysicalDeviceMemoryProperties2 = nullptr;
                o_vkGetPhysicalDeviceMemoryProperties2KHR = nullptr;
            }
        }
    }
}
