#include "pch.h"

// =================================================================
// 0. IL2CPP Structs and Function Type Definitions
// =================================================================

typedef void (*Il2CppMethodPointer)();
struct Il2CppDomain;
struct Il2CppAssembly;
struct Il2CppThread;
struct Il2CppClass;
struct Il2CppMethod; // Forward declaration
struct Il2CppImage;

// IL2CPP API function pointer type definitions
using il2cpp_get_root_domain_prot = Il2CppDomain * (*)();
static il2cpp_get_root_domain_prot il2cpp_get_root_domain = nullptr; // harmless

using il2cpp_thread_attach_prot = Il2CppThread * (*)(Il2CppDomain*);
static il2cpp_thread_attach_prot il2cpp_thread_attach = nullptr; // harmless

using il2cpp_domain_get_assemblies_prot = const Il2CppAssembly** (*)(Il2CppDomain*, size_t*);
static il2cpp_domain_get_assemblies_prot il2cpp_domain_get_assemblies = nullptr; //harmless

using il2cpp_class_from_name_prot = Il2CppClass * (*)(const Il2CppImage*, const char*, const char*);
static il2cpp_class_from_name_prot il2cpp_class_from_name = nullptr; // not working, test needed

using il2cpp_class_get_methods_prot = const Il2CppMethod* (*)(Il2CppClass*, void**);
static il2cpp_class_get_methods_prot il2cpp_class_get_methods = nullptr; // not working, test needed

using il2cpp_method_get_name_prot = const char* (*)(const Il2CppMethod*);
static il2cpp_method_get_name_prot il2cpp_method_get_name = nullptr; // not working

using il2cpp_assembly_get_image_prot = const Il2CppImage* (*)(const Il2CppAssembly*);
static il2cpp_assembly_get_image_prot il2cpp_assembly_get_image = nullptr; // harmless

using il2cpp_image_get_name_prot = const char* (*)(const Il2CppImage*);
static il2cpp_image_get_name_prot il2cpp_image_get_name = nullptr; // harmless

// IL2CPP runtime invoke function pointer type
using il2cpp_runtime_invoke_prot = void* (*)(const Il2CppMethod*, void*, void**, void**);
static il2cpp_runtime_invoke_prot il2cpp_runtime_invoke = nullptr; // harmless

// Global variables for storing search results and log synchronization
std::vector<const Il2CppMethod*> found_methods;
std::mutex log_mutex;

// Global variable for storing ShowErrorScreen methods
std::vector<const Il2CppMethod*> error_screen_methods;


/// GVARS BEGIN

// CameraManager tracking related variables
const Il2CppMethod* g_cachedGetInstanceMethod = nullptr; // Cache the get_Instance method
void* lastCameraManager = nullptr;                      // Track the previous CameraManager instance

// TarkovApplication instance tracking
void* g_TarkovApplicationInstance = nullptr;
void* g_CameraManagerInstance = nullptr;

// =======================
//         IMGUI
// =======================
static bool g_ShowMenu = true;
static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static IDXGISwapChain* g_pSwapChain = nullptr;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
static WNDPROC g_oOriginalWndProc = nullptr;

// =======================
//        MINHOOK
// =======================
static bool g_MinHookInitialized = false;
static bool g_Hooked = false;

/// GVARS END

#define STATIC_FIELDS_OFFSET 0xB8

/// RNG START
auto getRandomSeed()
-> std::seed_seq
{
    // This gets a source of actual, honest-to-god randomness
    std::random_device source;

    // Here, we fill an array of random data from the source
    unsigned int random_data[10];
    for (auto& elem : random_data) {
        elem = source();
    }

    // this creates the random seed sequence out of the random data
    return std::seed_seq(random_data + 0, random_data + 10);
}

double randomnumber() {
    // Making rng static ensures that it stays the same
    // Between different invocations of the function
    static auto seed = getRandomSeed();
    static std::default_random_engine rng(seed);

    std::uniform_real_distribution<double> dist(0.0, 1.0);
    return dist(rng);
}
/// RNG END

/// IMGUI START
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

BOOL isGameReady() 
{
    HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
    if (!gameWindow)
    {
        return false;
    }
    return true;
}

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_KEYDOWN && wParam == VK_INSERT)
        g_ShowMenu = !g_ShowMenu;

    if (g_ShowMenu && ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    return CallWindowProc(g_oOriginalWndProc, hWnd, msg, wParam, lParam);
}

// Present hooks
typedef HRESULT(__stdcall* PresentFn)(IDXGISwapChain*, UINT, UINT);
static PresentFn oPresent = nullptr;

HRESULT __stdcall hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags)
{
    static bool initialized = false;

    if (!initialized)
    {
        if (!pSwapChain)
            return S_OK;

        g_pSwapChain = pSwapChain;

        if (FAILED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice)))
            return S_OK; 

        g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);

        DXGI_SWAP_CHAIN_DESC sd{};
        pSwapChain->GetDesc(&sd);
        HWND hWnd = sd.OutputWindow;

        g_oOriginalWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);

        IMGUI_CHECKVERSION();
        ImGui::CreateContext();
        ImGuiIO& io = ImGui::GetIO(); (void)io;
        io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
        ImGui::StyleColorsDark();

        if (!ImGui_ImplWin32_Init(hWnd) || !ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext))
            return S_OK;

        ID3D11Texture2D* pBackBuffer = nullptr;
        if (FAILED(pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)))
            return S_OK;

        if (FAILED(g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView)))
        {
            pBackBuffer->Release();
            return S_OK;
        }
        pBackBuffer->Release();

        initialized = true;
    }

    // render
    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    if (g_ShowMenu)
    {
        ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
        ImGui::Begin("Operator PvE for v1.0 - build 0.0.1", &g_ShowMenu);
        ImGui::TextColored(ImVec4(0, 1, 0, 1), "第一阶段彻底成功！");
        ImGui::Separator();
        ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
        ImGui::Text("按 INSERT 键切换菜单");
        ImGui::Text("所有初始化已完成，无递归漏洞");
        ImGui::TextColored(ImVec4(1, 1, 0, 1), "你可以开始第二阶段了！");
        ImGui::End();
    }

    ImGui::Render();
    g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

    return oPresent(pSwapChain, SyncInterval, Flags);
}

void log_info(const char* fmt, ...);
void InitMinHook()
{
    if (g_MinHookInitialized) return;

    if (MH_Initialize() == MH_OK)
    {
        g_MinHookInitialized = true;
        log_info("[+] MinHook initialized");
        return;
    }

    log_info("[-] MinHook not initialized");
}

void HookPresent()
{
    if (g_Hooked) return;

    HWND gameWindow = FindWindowA("UnityWndClass", nullptr);
    if (!gameWindow)
    {
        return; 
    }
    
    InitMinHook();

    IDXGISwapChain* pSwapChain = nullptr;
    ID3D11Device* pDevice = nullptr;

    DXGI_SWAP_CHAIN_DESC sd{};
    sd.BufferCount = 1;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = gameWindow;
    sd.SampleDesc.Count = 1;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    HRESULT hr = D3D11CreateDeviceAndSwapChain(
        nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
        D3D11_SDK_VERSION, &sd, &pSwapChain, &pDevice, nullptr, nullptr);

    if (FAILED(hr) || !pSwapChain || !pDevice)
    {
        if (pSwapChain) pSwapChain->Release();
        if (pDevice)    pDevice->Release();
        return; 
    }

    void** vtable = *(void***)pSwapChain;
    void* presentAddr = vtable[8];

    if (MH_CreateHook(presentAddr, hkPresent, (LPVOID*)&oPresent) != MH_OK)
    {
        log_info("[-] MinHook creation failed");
        goto post;
    }

    if (MH_EnableHook(presentAddr) != MH_OK)
    {
        log_info("[-] MinHook enable failed");
        goto post;
    }

    log_info("[+] MinHook Present hooked @ %p -> %p", presentAddr, hkPresent);
    g_Hooked = true;

post:
    pSwapChain->Release();
    pDevice->Release();
}

/// IMGUI END

// =================================================================
// 1. Logging and Helper Functions
// =================================================================

// Thread-safe logging function to avoid conflicts during multi-threaded writes
void log_info(const char* fmt, ...)
{
    std::lock_guard<std::mutex> lock(log_mutex);
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    printf("\n");
    va_end(args);
}

/**
 *   @brief Finds methods with specified names in a given IL2CPP class
 *   @Param klass Target IL2CPP class pointer
 *   @Param names List of method names to find
 */
void find_methods_by_names(Il2CppClass* klass, const std::vector<std::string>& names)
{
    found_methods.clear();
    if (!klass) return;
    void* iter = nullptr;
    const Il2CppMethod* method;
    while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(klass, &iter)))
    {
        const char* mname = il2cpp_method_get_name(method);
        if (!mname) continue;
        for (const auto& target : names) // Use const auto& for optimized iteration
        {
            if (strcmp(mname, target.c_str()) == 0)
            {
                found_methods.push_back(method);
                log_info("[FIND] Method %s found @ %p", mname, method);
            }
        }
    }
}

/**
 *   @brief Core patch function: Applies a RET Hook (0xC3)
 *   @details Changes the first byte of the method's entry address to RET (0xC3), causing the function to return immediately, effectively disabling the feature.
 *   @Param method Target IL2Cpp method pointer
 */
void patch_method_ret(const Il2CppMethod* method)
{
    if (!method) return;
    // Get the actual function pointer of the method
    void* fn = *(void**)method;
    if (!fn)
    {
        log_info("[-] Method pointer is null. Skipping patch.");
        return;
    }
    DWORD oldProtect;
    // Change memory protection to PAGE_EXECUTE_READWRITE
    if (!VirtualProtect(fn, 1, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        log_info("[-] VirtualProtect failed on address %p", fn);
        return;
    }
    // Write the RET instruction (0xC3)
    *(uint8_t*)fn = 0xC3;
    // Restore original memory protection
    VirtualProtect(fn, 1, oldProtect, &oldProtect);
    log_info("[+] Patch successfully applied, address %p -> RET", fn);
}

// Safe version of patch_method_ret with waiting
void patch_method_ret_safe(const Il2CppMethod* method) {
    if (!method) return;
    void* fn = nullptr;
    while (!(fn = *(void**)method)) {
        Sleep(50);
    }
    DWORD oldProtect;
    if (VirtualProtect(fn, 16, PAGE_EXECUTE_READWRITE, &oldProtect)) {
        uint8_t* code = (uint8_t*)fn;
        code[0] = 0xC3;
        VirtualProtect(fn, 16, oldProtect, &oldProtect);
    }
}

/**
 *   @brief Core patch function: Forces a boolean Getter to return a specified value
 *   @details Changes the method machine code to MOV AL, value; RET, used to force a property to return True or False.
 *   @Param fn Target function pointer
 *   @Param value Boolean value to force return
 */
void patch_bool_getter(void* fn, bool value)
{
    if (!fn) return;
    DWORD oldProtect;
    // Change memory protection
    if (!VirtualProtect(fn, 16, PAGE_EXECUTE_READWRITE, &oldProtect))
    {
        log_info("[-] VirtualProtect failed on patch_bool_getter at address %p", fn);
        return;
    }
    uint8_t* code = (uint8_t*)fn;
    code[0] = 0xB0;                 // mov al, imm8 (opcode)
    code[1] = value ? 1 : 0;        // immediate value (1 or 0)
    code[2] = 0xC3;                 // ret (return instruction)
    // Restore original memory protection
    VirtualProtect(fn, 16, oldProtect, &oldProtect);
}

void patch_float_getter(void* fn, float value)
{
    if (!fn) return;
    static float forced_value = 0.0f;
    forced_value = value;

    uint8_t stub[32];
    memset(stub, 0x90, sizeof(stub));
    stub[0] = 0x48;
    stub[1] = 0xB8;
    uint64_t addr = (uint64_t)&forced_value;
    memcpy(&stub[2], &addr, sizeof(addr)); // imm64
    int idx = 2 + (int)sizeof(addr);
    stub[idx++] = 0xF3;
    stub[idx++] = 0x0F;
    stub[idx++] = 0x10;
    stub[idx++] = 0x00;
    stub[idx++] = 0xC3;

    DWORD oldProtect;
    SIZE_T len = idx;
    VirtualProtect(fn, len, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy(fn, stub, len);
    FlushInstructionCache(GetCurrentProcess(), fn, len);
    VirtualProtect(fn, len, oldProtect, &oldProtect);
}

/**
 *   @brief Finds and applies the RET Hook patch to a group of methods
 */
void patch_methods_with_logging(Il2CppClass* klass,
    const char* ns, const char* name,
    const std::vector<std::string>& methods)
{
    if (!klass)
    {
        log_info("[-] Cannot find class %s.%s. Skipping patch.", ns, name);
        return;
    }
    log_info("[PATCHING] Applying patches for class %s.%s", ns, name);
    void* iter = nullptr;
    const Il2CppMethod* method;
    while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(klass, &iter)))
    {
        const char* mname = il2cpp_method_get_name(method);
        if (!mname) continue;
        for (const auto& t : methods)
        {
            if (strcmp(mname, t.c_str()) == 0)
            {
                void* fn = *(void**)method;
                log_info("[PATCH] %s.%s.%s @ %p", ns, name, mname, fn);
                patch_method_ret(method);
            }
        }
    }
}

/**
 *   @brief Finds the BattleEye initialization method (EFT.TarkovApplication)
 */
const Il2CppMethod* find_battleye_init(Il2CppClass* main_application)
{
    void* iter = nullptr;
    const Il2CppMethod* method;
    while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(main_application, &iter)))
    {
        const char* name = il2cpp_method_get_name(method);
        if (!name) continue;
        // In some versions, the BE init method name is obfuscated, identified by a characteristic byte sequence
        if ((unsigned(name[0]) & 0xFF) == 0xEE &&
            (unsigned(name[1]) & 0xFF) == 0x80 &&
            (unsigned(name[2]) & 0xFF) == 0x81)
            return method;

        // In other versions, it might be ValidateAnticheat
        if (strcmp(name, "ValidateAnticheat") == 0)
            return method;
    }
    return nullptr;
}

// Wait for battleye init method
const Il2CppMethod* wait_for_battleye_init(Il2CppClass* main_application) {
    const Il2CppMethod* method = nullptr;
    while (!(method = find_battleye_init(main_application))) Sleep(200);
    return method;
}

// Find show error screen methods
void find_show_error_screen(Il2CppClass* preloader_ui)
{
    error_screen_methods.clear();
    void* iter = nullptr;
    const Il2CppMethod* method;
    while ((method = il2cpp_class_get_methods(preloader_ui, &iter)))
    {
        const char* name = il2cpp_method_get_name(method);
        if (!name) continue;
        if (strcmp(name, "ShowErrorScreen") == 0)
        {
            error_screen_methods.push_back(method);
        }
    }
}

/**
 *   @brief Finds the Unity core Assembly-CSharp.dll module
 */
Il2CppImage* il2cpp_image_loaded(const char* image_name)
{
    Il2CppDomain* domain = il2cpp_get_root_domain();
    if (!domain) return nullptr;
    size_t size = 0;
    const Il2CppAssembly* const* asmbl = il2cpp_domain_get_assemblies(domain, &size);
    if (!asmbl) return nullptr;
    for (size_t i = 0; i < size; i++)
    {
        const Il2CppImage* img = il2cpp_assembly_get_image(asmbl[i]);
        if (!img) continue;
        const char* nm = il2cpp_image_get_name(img);
        if (!nm) continue;
        if (_stricmp(nm, image_name) == 0)
            return (Il2CppImage*)img;
    }
    return nullptr;
}

/**
 *   @brief Gets the singleton instance of EFT.CameraControl.CameraManager via IL2CPP runtime invoke.
 *   @Param image Il2CppImage pointer of Assembly-CSharp.dll
 *   @returN Pointer to the CameraManager instance, or nullptr on failure
 */
void* get_camera_manager_instance(Il2CppImage* image)
{
    // 1. Find CameraManager class
    auto cameraManagerClass = il2cpp_class_from_name(image, "EFT.CameraControl", "CameraManager");
    if (!cameraManagerClass) return nullptr;

    // 2. Find and cache get_Instance method (only search on first execution)
    if (!g_cachedGetInstanceMethod) {
        void* iter = nullptr;
        const Il2CppMethod* method;
        while ((method = (const Il2CppMethod*)il2cpp_class_get_methods(cameraManagerClass, &iter)))
        {
            const char* mname = il2cpp_method_get_name(method);
            if (!mname) continue;
            if (strcmp(mname, "get_Instance") == 0) {
                g_cachedGetInstanceMethod = method;
                break;
            }
        }
        if (!g_cachedGetInstanceMethod) return nullptr; // get_Instance not found
    }

    // 3. Execute il2cpp_runtime_invoke
    if (!il2cpp_runtime_invoke) return nullptr; // Ensure API is loaded

    void* exc = nullptr; // Exception pointer
    // Call static method get_Instance, so instance object pointer is nullptr
    void* instance = il2cpp_runtime_invoke(g_cachedGetInstanceMethod, nullptr, nullptr, &exc);

    // 4. Check call result and exception
    if (exc) {
        // In a real application, exception information could be logged
        return nullptr;
    }
    return instance;
}

// Get TarkovApplication instance
void* get_tarkov_application_instance(Il2CppClass* klass)
{
    uintptr_t static_fields_ptr = *(uintptr_t*)((uintptr_t)klass + STATIC_FIELDS_OFFSET);
    if (!static_fields_ptr) return nullptr;
    uintptr_t* static_fields = (uintptr_t*)static_fields_ptr;
    for (int i = 0; i < 32; i++)
    {
        uintptr_t candidate = static_fields[i];
        if (!candidate) continue;
        Il2CppClass* objClass = *(Il2CppClass**)candidate;
        if (objClass == klass)
            return (void*)candidate;
    }
    return nullptr;
}

// =================================================================
// 2. Main Execution Logic
// =================================================================

void start()
{
    // Enable console output
    AllocConsole();
    FILE* conout = nullptr;
    if (freopen_s(&conout, "CONOUT$", "w", stdout) != 0)
    {
        log_info("freopen_s failed");
    }

    // 1. Wait for GameAssembly.dll to load and get IL2CPP API addresses
    HMODULE il2cpp = nullptr;
    while (!(il2cpp = GetModuleHandleA("GameAssembly.dll")))
    {
        log_info("[-] Waiting for GameAssembly.dll...");
        Sleep(2000);
    }
    printf("GameAssembly Address = %p\n", il2cpp);
    
    // Get all required IL2CPP core function pointers
    il2cpp_get_root_domain = (il2cpp_get_root_domain_prot)GetProcAddress(il2cpp, "il2cpp_domain_get");
    il2cpp_thread_attach = (il2cpp_thread_attach_prot)GetProcAddress(il2cpp, "il2cpp_thread_attach");
    il2cpp_domain_get_assemblies = (il2cpp_domain_get_assemblies_prot)GetProcAddress(il2cpp, "il2cpp_domain_get_assemblies");
    il2cpp_class_from_name = find_il2cpp_class_from_name(il2cpp);
    if (! il2cpp_class_from_name)
    {
        log_info("[-] il2cpp_class_from_name was not found.");
        Sleep(10000);
        exit(-1);
    }
    log_info("[+] il2cpp_class_from_name loaded.");

    il2cpp_class_get_methods = find_il2cpp_class_get_methods(il2cpp);
    if (!il2cpp_class_get_methods)
    {
        log_info("[-] il2cpp_class_get_methods was not found.");
        Sleep(10000);
        exit(-1);
    }
    log_info("[+] il2cpp_class_get_methods loaded.");

    il2cpp_method_get_name = find_il2cpp_method_get_name(il2cpp);
    if (!il2cpp_method_get_name)
    {
        log_info("[-] il2cpp_method_get_name was not found.");
        Sleep(10000);
        exit(-1);
    }
    log_info("[+] il2cpp_method_get_name loaded.");

    il2cpp_assembly_get_image = (il2cpp_assembly_get_image_prot)GetProcAddress(il2cpp, "il2cpp_assembly_get_image");
    il2cpp_image_get_name = (il2cpp_image_get_name_prot)GetProcAddress(il2cpp, "il2cpp_image_get_name");
    // Load runtime invoke function
    il2cpp_runtime_invoke = (il2cpp_runtime_invoke_prot)GetProcAddress(il2cpp, "il2cpp_runtime_invoke");

    // Add a startup delay to avoid operating too early
    printf("[-] Waiting for game window.");
    while (!isGameReady())
    {
        printf(".");
        Sleep(100);
    }
    log_info("");

    Il2CppDomain* domain = il2cpp_get_root_domain();
    if (!domain) {
        log_info("[-] Could not get root domain. Exiting.");
        return;
    }
    il2cpp_thread_attach(domain);
    // 2. Wait for Assembly-CSharp.dll to load
    Il2CppImage* image = nullptr;
    while (!(image = il2cpp_image_loaded("Assembly-CSharp.dll")))
    {
        log_info("[-] Waiting for Assembly-CSharp.dll...");
        Sleep(2000);
    }
    printf("[+] Assembly-CSharp.dll Address @ %p\n", image);

    // 3. Thread Attach: Must be done after the core DLL (Assembly-CSharp) is loaded
    il2cpp_thread_attach(domain);
    printf("[+] Successfully attached to IL2CPP domain\n");
    Sleep(2000);
    // 4. Find and apply BattleEye & UI patches

    // Find EFT.TarkovApplication class
    auto main_app = il2cpp_class_from_name(image, "EFT", "TarkovApplication");
    if (!main_app)
    {
        printf("[-] Could not find TarkovApplication. Patching aborted.\n");
    }
    else
    {
        printf("[+] TarkovApplication Address @ %p\n", main_app);
        // Patch BattleEye initialization method
        const Il2CppMethod* be = find_battleye_init(main_app);
        if (be)
        {
            log_info("[+] BattleEye initialization method found.");
            patch_method_ret(be);
        }
        else {
            log_info("[-] BattleEye initialization method not found. Will attempt manual DLL unload.");
        }
    }

    // Find EFT.UI.PreloaderUI class to disable the error screen
    auto preloader_ui = il2cpp_class_from_name(image, "EFT.UI", "PreloaderUI");
    if (!preloader_ui)
    {
        printf("[-] Could not find PreloaderUI. Skipping error screen patch.\n");
    }
    else
    {
        printf("[+] PreloaderUI Address @ %p\n", preloader_ui);
        // Patch ShowErrorScreen
        error_screen_methods.clear();
        const Il2CppMethod* m = nullptr;
        void* iter = nullptr;
        while ((m = (const Il2CppMethod*)il2cpp_class_get_methods(preloader_ui, &iter)))
        {
            const char* name = il2cpp_method_get_name(m);
            if (!name) continue;
            if (strcmp(name, "ShowErrorScreen") == 0)
            {
                printf("[+] Found ShowErrorScreen @ %p\n", m);
                error_screen_methods.push_back(m);
            }
        }

        for (const auto& method_to_patch : error_screen_methods)
            patch_method_ret(method_to_patch);
    }

    // 5. Apply core PVE patches (RET Hooks)

    // Make the player immortal and disable fall damage
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.HealthSystem", "ActiveHealthController"),
        "EFT.HealthSystem", "ActiveHealthController",
        { "ChangeHydration", "ChangeEnergy", "HandleFall", "SetEncumbered", "SetOverEncumbered", "AddFatigue" });

    // Infinite stamina
    patch_methods_with_logging(il2cpp_class_from_name(image, "", "Stamina"),
        "", "Stamina", { "Process", "Consume" });

    // Disable AI attack and tracking
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT", "BotMemory"),
        "EFT", "BotMemory",
        { "Spotted", "SetLastTimeSeeEnemy", "LoseVisionCurrentEnemy", "LastEnemyVisionOld", "set_GoalEnemy" });

    // Disable recoil (multiple classes)
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations.NewRecoil", "NewRecoilShotEffect"),
        "EFT.Animations.NewRecoil", "NewRecoilShotEffect",
        { "AddRecoilForce" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "NewRotationRecoilProcess"),
        "EFT.Animations", "NewRotationRecoilProcess",
        { "CalculateRecoil" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "OldRecoilShotEffect"),
        "EFT.Animations", "OldRecoilShotEffect",
        { "AddRecoilForce" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "OldRotationRecoilProcess"),
        "EFT.Animations", "OldRotationRecoilProcess",
        { "CalculateRecoil" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Animations", "RecoilProcessBase"),
        "EFT.Animations", "RecoilProcessBase",
        { "CalculateRecoil" });

    // Disable boundary and trap damage
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "BarbedWire"),
        "EFT.Interactive", "BarbedWire",
        { "AddPenalty", "ProceedDamage" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "Minefield"),
        "EFT.Interactive", "Minefield",
        { "DealExplosionDamage", "Explode" });
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.Interactive", "SniperFiringZone"),
        "EFT.Interactive", "SniperFiringZone",
        { "Shoot" });

    // Disable weapon wear/durability loss
    patch_methods_with_logging(il2cpp_class_from_name(image, "EFT.InventoryLogic", "Weapon"),
        "EFT.InventoryLogic", "Weapon",
        { "OnShot" });

    // Can breach any door with the "Breach" option successfully
    auto door_class = il2cpp_class_from_name(image, "EFT.Interactive", "Door");
    find_methods_by_names(door_class, { "BreachSuccessRoll" });
    if (!found_methods.empty())
    {
        void* fn = *(void**)found_methods[0];
        patch_bool_getter(fn, true);
        log_info("Patched Door.BreachSuccessRoll @ %p to return TRUE", fn);
    }

    // 6. Apply Boolean Getter Patches (Force return False)

    auto skill_class = il2cpp_class_from_name(image, "EFT", "SkillManager");
    if (skill_class) {
        find_methods_by_names(skill_class, { "get_AttentionEliteLuckySearchValue" });
        if (!found_methods.empty()) {
            struct MyIl2CppMethod { void* methodPointer; };
            void* fn = ((MyIl2CppMethod*)found_methods[0])->methodPointer;
            if (fn) {
                patch_float_getter(fn, 1.0f);
                printf("patched EFT.SkillManager.get_AttentionEliteLuckySearchValue @ %p -> returns 1.0f\n", fn);
            }
            else {
                printf("[-] methodPointer is null for get_AttentionEliteLuckySearchValue\n");
            }
        }
        else {
            printf("[-] EFT.SkillManager.get_AttentionEliteLuckySearchValue unfound\n");
        }
    }
    else {
        printf("[-] EFT.SkillManager unfound\n");
    }

    auto weapon_class = il2cpp_class_from_name(image, "EFT.InventoryLogic", "Weapon");
    // Disable weapon malfunction
    find_methods_by_names(weapon_class, { "get_AllowMalfunction" });
    if (!found_methods.empty())
    {
        void* fn = *(void**)found_methods[0];
        patch_bool_getter(fn, false);
        log_info("Patched get_AllowMalfunction @ %p to return FALSE", fn);
    }
    // Disable weapon overheating
    find_methods_by_names(weapon_class, { "get_AllowOverheat" });
    if (!found_methods.empty())
    {
        void* fn = *(void**)found_methods[0];
        patch_bool_getter(fn, false);
        log_info("Patched get_AllowOverheat @ %p to return FALSE", fn);
    }

    // 7. Unload BattleEye client DLL
    HMODULE beclient = GetModuleHandleA("BEClient_x64.dll");
    if (beclient)
    {
        printf("[+] BEClient_x64 @ %p is unloading...\n", beclient);
        FreeLibrary(beclient);
    }

    // =================================================================
    // 8. Idle Loop and CameraManager Tracking (Core Merge)
    // =================================================================

    log_info("\n[+] All patches applied. Entering idle tracking loop.");
    log_info("[!] This cheat is free of charge and available on unknowncheats.me .");
    log_info("[!] If you payed for this cheat, you've been scammed.");
    log_info("[!] 此作弊模块在 unknowncheats.me 上免费公开共享。");
    log_info("[!] 如果你花钱了，你被骗了，请举报卖家。");

    // Keep the thread alive to prevent it from exiting the IL2CPP domain, avoiding fatal GC errors.
    while (true)
    {
        // Keep the thread active, using 3-second sleep to minimize CPU usage and set the tracking interval
        Sleep(3000);

        HookPresent(); // yolo

        // Signature repetition
        if ((int)(randomnumber() * 10) < 2)
        {
            log_info("[!] This cheat is free of charge and available on unknowncheats.me .");
            log_info("[!] If you payed for this cheat, you've been scammed.");
            log_info("[!] 此作弊模块在 unknowncheats.me 上免费公开共享。");
            log_info("[!] 如果你花钱了，你被骗了，请举报卖家。");
        }

        // TarkovApplication instance tracking
        if (!g_TarkovApplicationInstance)
        {
            g_TarkovApplicationInstance = get_tarkov_application_instance(main_app);
            if (g_TarkovApplicationInstance)
                log_info("[+] TarkovApplication = 0x%llX", (uintptr_t)g_TarkovApplicationInstance);
        }

        // CameraManager tracking logic
        void* cm = get_camera_manager_instance(image);
        if (!cm)
            continue;
        // Track CameraManager instance changes - print on initial detection OR when instance changes
        if (cm == g_CameraManagerInstance)
            continue;

        if (!g_CameraManagerInstance) {
            log_info("[+] CameraManager initial detection: 0x%llX", (uintptr_t)cm);
        }
        else {
            log_info("[+] CameraManager instance updated: 0x%llX", (uintptr_t)cm);
        }

        g_CameraManagerInstance = cm;
    }
}

// =================================================================
// 3. DLL Entry Point
// =================================================================

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
{
    // Create a new thread to run the main logic when the DLL is attached to the process
    if (reason == DLL_PROCESS_ATTACH)
        CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)start, nullptr, 0, nullptr);
    return TRUE;
}
