#pragma comment(lib, "d3d11.lib")
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")

#include <iostream>
#include <Windows.h>
#include <string>
#include <thread>
#include <chrono>
#include <filesystem>
#include <atomic>
#include <memory>
#include <d3d11.h>
#include <tchar.h>
#include <shlobj.h>
#include <fstream>
#include <vector>     // Dodane dla HWID
#include <sstream>    // Dodane dla HWID i raportowania błędów
#include <iomanip>    // Dodane dla HWID (std::hex, std::setw)
#include <algorithm>  // Do std::transform

// --- Windows API for HWID, Registry and Process handling ---
#include <winreg.h>    // Dodane dla rejestru
#include <intrin.h>    // Dodane dla __cpuid
#include <iphlpapi.h>  // Dodane dla GetAdaptersInfo
#include <comdef.h>    // Dodane dla _bstr_t
#include <wbemidl.h>   // Dodane dla WMI
#include <setupapi.h>  // Dodane dla dysku
#include <devguid.h>   // Dodane dla dysku
#include <regstr.h>    // Dodane dla dysku
#include <winioctl.h>  // Dodane dla dysku
#include <tlhelp32.h>  // Dodane dla GetCurrentProcessId i KillProcesses

// --- CURL for API requests ---
#include <curl/curl.h> // Dodane dla komunikacji API

// --- OpenSSL for SHA256 ---
#include <openssl/sha.h> // Dodane dla SHA256

// Oryginalne nagłówki loadera
#include "base/driver/driver.hpp"
#include "base/gui/render.hpp"
#include "base/utils/Authentication/skStr.h"
#include "base/utils/Bytes/driver.h"
#include "base/utils/Bytes/mapper.h"
#include "base/utils/Authentication/Protection/protect/protectmain.h"

#pragma warning(push)
#pragma warning(disable : 4996) // Wyłącz ostrzeżenia o niezabezpieczonych funkcjach (np. sprintf_s)

// --- ZMIEŃ TO NA NAZWĘ PRODUKTU, DLA KTÓREGO KOMPILUJESZ TEN CHEAT ---
#define EXPECTED_PRODUCT "fortnite" // Ustawione na "fortnite" zgodnie z życzeniem
// --- KONIEC ZMIANY ---

// --- Nowe definicje kolorów dla motywu Neon ---
const ImVec4 NEON_BLUE_PRIMARY = ImVec4(0.0f, 0.8f, 1.0f, 1.0f);      // Jasny, cyjanowy niebieski
const ImVec4 NEON_BLUE_DARK = ImVec4(0.0f, 0.5f, 0.8f, 1.0f);         // Nieco ciemniejszy niebieski
const ImVec4 NEON_PURPLE_PRIMARY = ImVec4(0.4f, 0.2f, 1.0f, 1.0f);    // Jasny fiolet
const ImVec4 NEON_PURPLE_DARK = ImVec4(0.2f, 0.1f, 0.6f, 1.0f);       // Ciemniejszy fiolet

const ImVec4 BACKGROUND_START_COLOR = ImVec4(0.02f, 0.02f, 0.05f, 1.0f); // Bardzo ciemny niebiesko-fioletowy
const ImVec4 BACKGROUND_END_COLOR = ImVec4(0.05f, 0.02f, 0.1f, 1.0f);   // Bardzo ciemny fioletowo-niebieski

const ImVec4 PANEL_BACKGROUND_COLOR = ImVec4(0.05f, 0.05f, 0.1f, 0.95f); // Lekko przezroczysty, ciemny niebieski
const ImVec4 PANEL_BORDER_COLOR = ImVec4(0.0f, 0.8f, 1.0f, 0.7f);        // Neonowo-niebieska ramka

const ImVec4 STATUS_SUCCESS_GREEN = ImVec4(0.0f, 0.8f, 0.4f, 1.0f);     // Neonowa zieleń
const ImVec4 STATUS_ERROR_RED = ImVec4(1.0f, 0.2f, 0.2f, 1.0f);         // Jasna czerwień
const ImVec4 STATUS_WARNING_YELLOW = ImVec4(1.0f, 0.85f, 0.0f, 1.0f);   // Neonowa żółć

const ImVec4 TEXT_PRIMARY_COLOR = ImVec4(0.85f, 0.85f, 0.9f, 1.0f);    // Jasny, neutralny tekst
const ImVec4 TEXT_BRIGHT_NEON = NEON_BLUE_PRIMARY;                     // Jasny neonowy tekst dla tytułów

// --- Koniec definicji kolorów ---


static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static IDXGISwapChain* g_pSwapChain = nullptr;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;

bool CreateDeviceD3D(HWND hWnd);
void CleanupDeviceD3D();
void CreateRenderTarget();
void CleanupRenderTarget();
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

enum class AppState {
    LOGIN,
    LOADING_CHEAT,
    CHEAT_RUNNING
};

enum class LoadingStage {
    IDLE,
    SECURITY_INIT,
    DRIVER_CHECK,
    DRIVER_LOADING,
    DRIVER_READY,
    WAIT_FOR_GAME,
    GAME_DETECTED,
    INJECTING,
    COMPLETED
};

static AppState g_appState = AppState::LOGIN;
static LoadingStage g_loadingStage = LoadingStage::IDLE;
static bool g_shouldStartCheat = false;
static bool g_cheatCompleted = false;
static std::string g_statusMessage = "";
static ImVec4 g_statusColor = STATUS_WARNING_YELLOW; // Domyślny kolor statusu

namespace Config {
    constexpr int PROCESS_CHECK_INTERVAL_MS = 500;
    constexpr int SPINNER_UPDATE_MS = 100;
    constexpr int PROGRESS_BAR_WIDTH = 50;

    inline const std::string& GetTargetProcess() {
        static std::string s = skCrypt("FortniteClient-Win64-Shipping.exe").decrypt();
        return s;
    }

    inline const std::string& GetDriverPath() {
        static std::string s = skCrypt("C:\\Windows\\System32\\Tasks\\driver.sys").decrypt();
        return s;
    }

    inline const std::string& GetMapperPath() {
        static std::string s = skCrypt("C:\\Windows\\System32\\Tasks\\mapper.exe").decrypt();
        return s;
    }
}
namespace Utils {

    void SleepMs(int milliseconds) {
        std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
    }
    inline std::wstring ToWideString(const std::string& str) {
        if (str.empty()) return std::wstring();
        int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
        std::wstring result(size, 0);
        MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &result[0], size);
        return result;
    }

    inline std::string ToNarrowString(const std::wstring& wstr) {
        if (wstr.empty()) return std::string();
        int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
        std::string result(size, 0);
        WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &result[0], size, nullptr, nullptr);
        return result;
    }

    inline bool FileExists(const std::string& path) {
        return std::filesystem::exists(path);
    }

    inline bool DeleteFile(const std::string& path) {
        try {
            return std::filesystem::remove(path);
        }
        catch (...) {
            return false;
        }
    }

    bool WriteBytesToFile(const std::string& path, const std::vector<unsigned char>& data) {
        try {
            std::ofstream file(path, std::ios::binary | std::ios::trunc);
            if (!file.is_open()) return false;
            file.write(reinterpret_cast<const char*>(data.data()), data.size());
            file.flush();
            return file.good();
        }
        catch (...) {
            return false;
        }
    }

    bool WriteBytesToFileSecure(const std::string& path, const std::vector<unsigned char>& data, int maxRetries = 3) {
        try {
            std::filesystem::path p(path);
            if (p.has_parent_path()) {
                std::filesystem::create_directories(p.parent_path());
            }
        }
        catch (...) {
            return false;
        }

        for (int attempt = 0; attempt < maxRetries; ++attempt) {
            try {
                std::string tempPath = path + ".tmp";

                std::ofstream file(tempPath, std::ios::binary | std::ios::trunc);
                if (!file.is_open()) {
                    SleepMs(200 * (attempt + 1));
                    continue;
                }

                file.write(reinterpret_cast<const char*>(data.data()), data.size());
                file.flush();
                file.close();

                if (!file.good()) {
                    std::filesystem::remove(tempPath);
                    SleepMs(200 * (attempt + 1));
                    continue;
                }

                if (std::filesystem::file_size(tempPath) != data.size()) {
                    std::filesystem::remove(tempPath);
                    SleepMs(200 * (attempt + 1));
                    continue;
                }

                if (std::filesystem::exists(path)) {
                    std::filesystem::remove(path);
                }

                std::filesystem::rename(tempPath, path);

                if (std::filesystem::exists(path) && std::filesystem::file_size(path) == data.size()) {
                    return true;
                }
            }
            catch (...) {
                try {
                    std::filesystem::remove(path + ".tmp");
                }
                catch (...) {}
            }

            SleepMs(200 * (attempt + 1));
        }

        return false;
    }

    bool WriteBytesToFileWinAPI(const std::string& path, const std::vector<unsigned char>& data) {
        std::wstring wpath = ToWideString(path);

        size_t pos = wpath.find_last_of(L"\\/");
        if (pos != std::wstring::npos) {
            std::wstring dir = wpath.substr(0, pos);
            CreateDirectoryW(dir.c_str(), nullptr);
        }

        HANDLE hFile = CreateFileW(
            wpath.c_str(),
            GENERIC_WRITE,
            0,
            nullptr,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
            nullptr
        );

        if (hFile == INVALID_HANDLE_VALUE) {
            return false;
        }

        DWORD bytesWritten = 0;
        BOOL success = WriteFile(
            hFile,
            data.data(),
            static_cast<DWORD>(data.size()),
            &bytesWritten,
            nullptr
        );

        FlushFileBuffers(hFile);
        CloseHandle(hFile);

        return success && (bytesWritten == data.size());
    }

    struct WriteResult {
        bool success;
        DWORD errorCode;
        std::string errorMessage;
    };

    WriteResult WriteBytesToFileDetailed(const std::string& path, const std::vector<unsigned char>& data) {
        WriteResult result = { false, 0, "" };
        std::wstring wpath = ToWideString(path);

        size_t pos = wpath.find_last_of(L"\\/");
        if (pos != std::wstring::npos) {
            CreateDirectoryW(wpath.substr(0, pos).c_str(), nullptr);
        }

        HANDLE hFile = CreateFileW(
            wpath.c_str(),
            GENERIC_WRITE,
            0,
            nullptr,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
            nullptr
        );

        if (hFile == INVALID_HANDLE_VALUE) {
            result.errorCode = GetLastError();
            switch (result.errorCode) {
            case ERROR_ACCESS_DENIED:
                result.errorMessage = "Access denied - Run as admin or check AV";
                break;
            case ERROR_PATH_NOT_FOUND:
                result.errorMessage = "Path not found";
                break;
            case ERROR_SHARING_VIOLATION:
                result.errorMessage = "File in use by another process";
                break;
            default:
                result.errorMessage = "Failed to create file (Error: " + std::to_string(result.errorCode) + ")";
            }
            return result;
        }

        DWORD bytesWritten = 0;
        if (!WriteFile(hFile, data.data(), static_cast<DWORD>(data.size()), &bytesWritten, nullptr)) {
            result.errorCode = GetLastError();
            result.errorMessage = "Write failed (Error: " + std::to_string(result.errorCode) + ")";
            CloseHandle(hFile);
            return result;
        }

        FlushFileBuffers(hFile);
        CloseHandle(hFile);

        if (bytesWritten != data.size()) {
            result.errorMessage = "Incomplete write: " + std::to_string(bytesWritten) + "/" + std::to_string(data.size());
            return result;
        }

        result.success = true;
        result.errorMessage = "Success";
        return result;
    }

    bool WriteBytesToFileRobust(const std::string& path, const std::vector<unsigned char>& data) {
        if (WriteBytesToFileSecure(path, data, 3)) {
            return true;
        }

        SleepMs(300);

        if (WriteBytesToFileWinAPI(path, data)) {
            return true;
        }

        SleepMs(300);

        try {
            std::filesystem::path p(path);
            std::string tempPath = std::filesystem::temp_directory_path().string() + "\\" + p.filename().string();

            if (WriteBytesToFileWinAPI(tempPath, data)) {
                try {
                    std::filesystem::copy_file(tempPath, path, std::filesystem::copy_options::overwrite_existing);
                    std::filesystem::remove(tempPath);
                    return true;
                }
                catch (...) {
                    return false;
                }
            }
        }
        catch (...) {}

        return false;
    }

    std::string FormatErrorCode(const std::string& category, int code) {
        char buffer[32];
        sprintf_s(buffer, "%s-%04X", category.c_str(), code);
        return buffer;
    }


    std::string GetLastErrorString() {
        DWORD error = GetLastError();
        if (error == 0) return "No error";

        LPSTR messageBuffer = nullptr;
        size_t size = FormatMessageA(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            error,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            (LPSTR)&messageBuffer,
            0,
            nullptr
        );

        std::string message(messageBuffer, size);
        LocalFree(messageBuffer);
        return message;
    }
}

// --- HWID Generation Functions ---
std::string GetRealCPUID() {
    std::string result;
    int cpuInfo[4] = { 0 };

    __cpuid(cpuInfo, 0);

    char vendor[13];
    memcpy(vendor, &cpuInfo[1], 4);
    memcpy(vendor + 4, &cpuInfo[3], 4);
    memcpy(vendor + 8, &cpuInfo[2], 4);
    vendor[12] = '\0';

    result = vendor;

    __cpuid(cpuInfo, 1);
    result += std::to_string(cpuInfo[0]);
    result += std::to_string(cpuInfo[3]);

    return result;
}

std::string GetRealDiskSerial() {
    std::string serial;

    HANDLE hDevice = CreateFileA("\\\\.\\PhysicalDrive0",
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if (hDevice != INVALID_HANDLE_VALUE) {
        STORAGE_PROPERTY_QUERY query;
        query.PropertyId = StorageDeviceProperty;
        query.QueryType = PropertyStandardQuery;

        STORAGE_DESCRIPTOR_HEADER header;
        DWORD bytesReturned;

        if (DeviceIoControl(hDevice,
            IOCTL_STORAGE_QUERY_PROPERTY,
            &query,
            sizeof(query),
            &header,
            sizeof(header),
            &bytesReturned,
            NULL)) {

            DWORD bufferSize = header.Size;
            std::vector<BYTE> buffer(bufferSize);
            PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)buffer.data();

            if (DeviceIoControl(hDevice,
                IOCTL_STORAGE_QUERY_PROPERTY,
                &query,
                sizeof(query),
                deviceDescriptor,
                bufferSize,
                &bytesReturned,
                NULL)) {

                if (deviceDescriptor->SerialNumberOffset != 0) {
                    serial = (char*)deviceDescriptor + deviceDescriptor->SerialNumberOffset;
                }
            }
        }
        CloseHandle(hDevice);
    }

    if (serial.empty()) {
        HKEY hKey;
        if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
            "HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port 0\\Scsi Bus 0\\Target Id 0\\Logical Unit Id 0",
            0, KEY_READ, &hKey) == ERROR_SUCCESS) {

            char buffer[256];
            DWORD bufferSize = sizeof(buffer);
            if (RegQueryValueExA(hKey, "SerialNumber", NULL, NULL, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) {
                serial = buffer;
            }
            RegCloseKey(hKey);
        }
    }

    return serial;
}

std::string GetRealMACFromRegistry() {
    std::string mac_address;

    ULONG bufferSize = sizeof(IP_ADAPTER_INFO);
    PIP_ADAPTER_INFO adapterInfo = (PIP_ADAPTER_INFO)malloc(bufferSize);

    if (GetAdaptersInfo(adapterInfo, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
        free(adapterInfo);
        adapterInfo = (PIP_ADAPTER_INFO)malloc(bufferSize);
    }

    if (GetAdaptersInfo(adapterInfo, &bufferSize) == NO_ERROR) {
        PIP_ADAPTER_INFO adapter = adapterInfo;
        while (adapter) {
            if (adapter->Type == MIB_IF_TYPE_ETHERNET) {
                char mac_str[18];
                sprintf_s(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X",
                    adapter->Address[0], adapter->Address[1], adapter->Address[2],
                    adapter->Address[3], adapter->Address[4], adapter->Address[5]);
                mac_address = mac_str;
                break;
            }
            adapter = adapter->Next;
        }
    }

    if (adapterInfo) {
        free(adapterInfo);
    }

    return mac_address;
}

std::string GetMotherboardUUID() {
    std::string uuid;

    HRESULT hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) return "";

    hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

    IWbemLocator* pLoc = NULL;
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);

    if (FAILED(hres)) {
        CoUninitialize();
        return "";
    }

    IWbemServices* pSvc = NULL;
    hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);

    if (FAILED(hres)) {
        pLoc->Release();
        CoUninitialize();
        return "";
    }

    hres = CoSetProxyBlanket(
        pSvc,                         // Pointer to proxy (IUnknown* pProxy)
        RPC_C_AUTHN_WINNT,            // Authentication service (DWORD dwAuthnSvc)
        RPC_C_AUTHZ_NONE,             // Authorization service (DWORD dwAuthzSvc)
        NULL,                         // Authentication principal name (OLECHAR* pServerPrincName)
        RPC_C_AUTHN_LEVEL_CALL,       // Authentication level (DWORD dwAuthnLevel)
        RPC_C_IMP_LEVEL_IMPERSONATE,  // Impersonation level (DWORD dwImpLevel)
        NULL,                         // Authentication information (RPC_AUTH_IDENTITY_HANDLE pAuthInfo)
        EOAC_NONE                     // Proxy capabilities (DWORD dwCapabilities)
    );

    if (FAILED(hres)) {
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return "";
    }

    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_ComputerSystemProduct"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

    if (FAILED(hres)) {
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return "";
    }

    IWbemClassObject* pclsObj = NULL;
    ULONG uReturn = 0;

    while (pEnumerator) {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn) break;

        VARIANT vtProp;
        hr = pclsObj->Get(L"UUID", 0, &vtProp, 0, 0);
        if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) {
            _bstr_t uuidStr(vtProp.bstrVal, false);
            uuid = uuidStr;
            VariantClear(&vtProp);
            break;
        }
        VariantClear(&vtProp);
        pclsObj->Release();
    }

    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    CoUninitialize();

    return uuid;
}

// --- GetRealHWID: Uses SHA256 ---
std::string GetRealHWID() {
    std::string hwid_components_str;
    hwid_components_str += GetRealCPUID();
    hwid_components_str += "_";
    hwid_components_str += GetRealDiskSerial();
    hwid_components_str += "_";
    hwid_components_str += GetRealMACFromRegistry();
    hwid_components_str += "_";
    hwid_components_str += GetMotherboardUUID();

    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256((const unsigned char*)hwid_components_str.c_str(), hwid_components_str.length(), hash);

    std::stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }
    return ss.str();
}
// --- END HWID Generation Functions ---


// --- API Communication Functions ---
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
    userp->append((char*)contents, size * nmemb);
    return size * nmemb;
}

std::string makeApiRequest(const std::string& endpoint, const std::string& jsonData) {
    CURL* curl;
    CURLcode res;
    std::string response;

    curl = curl_easy_init();
    if (curl) {
        std::string url = "https://xenoware.fun/api.php?endpoint=" + endpoint;

        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // FOR TESTING ONLY! In production, set to 1L and ensure SSL certs are handled correctly.

        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Content-Type: application/json");
        headers = curl_slist_append(headers, "Authorization: 8f4da91b2c6e7d5a3f0e1d2c3b4a5968"); // Ensure this key is secure!
        headers = curl_slist_append(headers, "User-Agent: XenoWareClient/1.0");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        res = curl_easy_perform(curl);
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);

        if (res != CURLE_OK) {
            // You can add cURL error logging here
        }
    }
    return response;
}

// Simple JSON parsing function
std::string extractFromJson(const std::string& json, const std::string& key) {
    std::string search_key = "\"" + key + "\":\"";
    size_t start_pos = json.find(search_key);
    if (start_pos == std::string::npos) {
        return "";
    }
    start_pos += search_key.length();
    size_t end_pos = json.find("\"", start_pos);
    if (end_pos == std::string::npos) {
        return "";
    }
    return json.substr(start_pos, end_pos - start_pos);
}
// --- END API Communication Functions ---


// --- Registry Functions ---
std::string ReadKeyFromRegistry(const std::string& product) {
    HKEY hKey;
    const char* subKey = "Software\\XenoWare\\OnetimeKeys";
    std::string onetime_key;

    if (RegOpenKeyExA(HKEY_CURRENT_USER, subKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
        char buffer[1024];
        DWORD bufferSize = sizeof(buffer);
        DWORD type = REG_SZ;

        if (RegQueryValueExA(hKey, product.c_str(), NULL, &type, (LPBYTE)buffer, &bufferSize) == ERROR_SUCCESS) {
            onetime_key = buffer;
        }
        RegCloseKey(hKey);
    }
    return onetime_key;
}

bool DeleteKeyFromRegistry(const std::string& product) {
    HKEY hKey;
    const char* subKey = "Software\\XenoWare\\OnetimeKeys";

    if (RegOpenKeyExA(HKEY_CURRENT_USER, subKey, 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS) {
        if (RegDeleteValueA(hKey, product.c_str()) == ERROR_SUCCESS) {
            RegCloseKey(hKey);
            return true;
        }
        RegCloseKey(hKey);
    }
    return false;
}
// --- END Registry Functions ---


struct Particle {
    float x, y;
    float speed;
    float size;
    float alpha;
    float lifetime;
    // New: direction for more varied movement
    float dirX, dirY;
};

struct AnimatedFloat {
    float current = 0.0f;
    float target = 0.0f;
    float speed = 5.0f;

    void Update(float dt) {
        current += (target - current) * speed * dt;
    }

    void SetTarget(float t) { target = t; }
    float Get() const { return current; }
};

class DriverManager {
public:
    bool Initialize() {
        g_loadingStage = LoadingStage::DRIVER_CHECK;
        std::cout << "Checking driver status...\n";
        g_statusColor = STATUS_WARNING_YELLOW;
        Utils::SleepMs(800);

        bool connected = kernel->initialize_handle();
        if (!connected) {
            g_loadingStage = LoadingStage::DRIVER_LOADING;
            std::cout << "Driver not found, loading now...\n";
            Utils::SleepMs(500);

            if (!LoadDriver()) return false;
            if (!ReconnectToKernel()) return false;
        }
        else {
            std::cout << "Driver already loaded\n";
            g_statusColor = STATUS_SUCCESS_GREEN;
            Utils::SleepMs(1000);
        }

        g_loadingStage = LoadingStage::DRIVER_READY;
        std::cout << "Driver ready\n";
        g_statusColor = STATUS_SUCCESS_GREEN;
        Utils::SleepMs(500);
        return true;
    }

private:
    bool LoadDriver() {
        BlockInput(true);

        std::cout << "Preparing driver files...\n";
        if (!PrepareDriverFiles()) {
            BlockInput(false);
            return false;
        }

        std::cout << "Loading kernel modules...\n";
        if (!ExecuteMapper()) {
            BlockInput(false);
            return false;
        }

        CleanupDriverFiles();
        std::cout << "Driver loaded successfully\n";
        g_statusColor = STATUS_SUCCESS_GREEN;
        BlockInput(false);
        return true;
    }

    bool PrepareDriverFiles() {
        Utils::SleepMs(300);

        auto driverResult = Utils::WriteBytesToFileDetailed(Config::GetDriverPath(), CheatDriver);
        if (!driverResult.success) {
            std::cout << "Driver: " + driverResult.errorMessage << "\n";
            g_statusColor = STATUS_ERROR_RED;

            Utils::SleepMs(200);
            if (!Utils::WriteBytesToFileRobust(Config::GetDriverPath(), CheatDriver)) {
                std::cout << "Driver failed (all methods) - " + Utils::GetLastErrorString() << "\n";
                return false;
            }
            std::cout << "Driver prepared (fallback method)\n";
            g_statusColor = STATUS_WARNING_YELLOW;
        }

        Utils::SleepMs(200);

        auto mapperResult = Utils::WriteBytesToFileDetailed(Config::GetMapperPath(), cheat_mapper_bytes);
        if (!mapperResult.success) {
            std::cout << "Mapper: " + mapperResult.errorMessage << "\n";
            g_statusColor = STATUS_ERROR_RED;

            Utils::SleepMs(200);
            if (!Utils::WriteBytesToFileRobust(Config::GetMapperPath(), cheat_mapper_bytes)) {
                std::cout << "Mapper failed (all methods) - " + Utils::GetLastErrorString() << "\n";
                return false;
            }
            std::cout << "Mapper prepared (fallback method)\n";
            g_statusColor = STATUS_WARNING_YELLOW;
        }

        Utils::SleepMs(300);
        return true;
    }

    bool ExecuteMapper() {
        Utils::SleepMs(300);

        if (!Utils::FileExists(Config::GetDriverPath())) {
            std::cout << "Driver file missing before execution\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }

        if (!Utils::FileExists(Config::GetMapperPath())) {
            std::cout << "Mapper file missing before execution\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }

        std::string command = Config::GetMapperPath() + " " + Config::GetDriverPath();

        STARTUPINFOA si = { sizeof(si) };
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
        PROCESS_INFORMATION pi = {};

        std::vector<char> cmdLine(command.begin(), command.end());
        cmdLine.push_back('\0');

        bool success = CreateProcessA(
            nullptr,
            cmdLine.data(),
            nullptr,
            nullptr,
            FALSE,
            CREATE_NO_WINDOW,
            nullptr,
            nullptr,
            &si,
            &pi
        );

        if (success) {
            for (int i = 0; i <= 100; i += 10) {
                Utils::SleepMs(100);
            }

            DWORD exitCode = 0;
            WaitForSingleObject(pi.hProcess, INFINITE);
            GetExitCodeProcess(pi.hProcess, &exitCode);

            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);

            if (exitCode != 0) {
                std::cout << "Mapper returned error code: " + std::to_string(exitCode) << "\n";
                g_statusColor = STATUS_ERROR_RED;
                return false;
            }
        }
        else {
            DWORD error = GetLastError();
            std::cout << "Module loading failed - Error: " + std::to_string(error) << "\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }

        return success;
    }

    void CleanupDriverFiles() {
        Utils::DeleteFile(Config::GetDriverPath());
        Utils::DeleteFile(Config::GetMapperPath());

        if (Utils::FileExists(Config::GetDriverPath()) || Utils::FileExists(Config::GetMapperPath())) {
            std::cout << "Warning: Cleanup incomplete (files may be locked)\n";
            g_statusColor = STATUS_WARNING_YELLOW;
        }
    }

    bool ReconnectToKernel() {
        Utils::SleepMs(1000);
        std::cout << "Reconnecting to kernel...\n";
        Utils::SleepMs(800);

        bool connected = kernel->initialize_handle();
        if (!connected) {
            std::cout << "Kernel connection failed - Driver may not be loaded\n";
            g_statusColor = STATUS_ERROR_RED;
        }
        else {
            std::cout << "Kernel connected successfully\n";
            g_statusColor = STATUS_SUCCESS_GREEN;
        }

        return connected;
    }
};

class ProcessManager {
public:
    bool WaitForTarget() {
        g_loadingStage = LoadingStage::WAIT_FOR_GAME;
        std::cout << "Please launch Fortnite...\n";
        g_statusColor = STATUS_WARNING_YELLOW;

        Utils::SleepMs(1000);

        std::wstring target = Utils::ToWideString(Config::GetTargetProcess());
        int pid = 0;

        while (!pid) {
            pid = kernel->get_process_pid(target);
            if (pid) break;
            Utils::SleepMs(Config::PROCESS_CHECK_INTERVAL_MS);
        }

        g_loadingStage = LoadingStage::GAME_DETECTED;
        std::cout << "Fortnite detected!";
        g_statusColor = STATUS_SUCCESS_GREEN;
        Utils::SleepMs(500);

        std::cout << "Waiting for game initialization...\n";
        Utils::SleepMs(2000);

        return AttachToProcess(pid);
    }

private:
    bool AttachToProcess(int pid) {
        g_loadingStage = LoadingStage::INJECTING;
        std::cout << "Injecting into Fortnite...\n";
        g_statusColor = STATUS_WARNING_YELLOW;

        Utils::SleepMs(200);

        if (!kernel->attach(pid)) {
            std::cout << "Failed to attach to process\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }

        Utils::SleepMs(200);

        auto baseAddress = kernel->get_image_base(nullptr);
        if (!baseAddress) {
            std::cout << "Failed to resolve base address\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }
        kernel->BaseAddress = baseAddress;

        Utils::SleepMs(200);

        if (!kernel->dtb) {
            std::cout << "Failed to resolve DTB\n";
            g_statusColor = STATUS_ERROR_RED;
            return false;
        }

        Utils::SleepMs(300);
        std::cout << "Successfully injected!\n";
        g_statusColor = STATUS_SUCCESS_GREEN;

        return true;
    }
};

class OverlayManager {
public:
    bool Initialize() {
        // --- MessageBox before Waiting for overlay ---
        int msgBoxResult = MessageBoxA(
            NULL,
            "Have you already launched Fortnite and started your overlay (e.g., Discord Overlay)?\n\nClick 'Yes' to continue, 'No' to return to the menu.",
            "XenoWare Loader - Confirmation",
            MB_YESNO | MB_ICONQUESTION
        );

        if (msgBoxResult == IDNO) {
            std::cout << "Overlay setup cancelled by user.\n";
            g_statusColor = STATUS_ERROR_RED;
            return false; // Return to login screen
        }
        // --- End MessageBox ---

        std::cout << "Waiting for overlay...\n";
        g_statusColor = STATUS_WARNING_YELLOW;

        HWND hwnd = nullptr;
        while (!hwnd) {
            hwnd = FindWindowA("Chrome_WidgetWin_1", "Discord Overlay"); // Target Discord Overlay for now
            if (hwnd) {
                Overlay.hwnd = hwnd;
                break;
            }
            Utils::SleepMs(Config::PROCESS_CHECK_INTERVAL_MS);
        }

        std::cout << "Overlay ready\n";
        g_statusColor = STATUS_SUCCESS_GREEN;
        return true;
    }
};

class LoginApp {
private:
    bool isLoggedIn = false;
    std::string subscription = "Not logged in";

    std::string errorMessage = "";
    // --- New private function for key verification ---
    bool PerformKeyVerification(const std::string& keyStr, bool isAutomaticLogin) {
        // 1. Generate HWID locally
        std::string local_hwid_loader = GetRealHWID();

        // 2. Prepare JSON data for verification
        std::string verification_jsonData = "{\"OnetimeKey\":\"" + keyStr +
            "\",\"Product\":\"" + EXPECTED_PRODUCT +
            "\",\"Username\":\"placeholder_username\"," // API will ignore this
            "\"Hwid\":\"" + local_hwid_loader + "\"}";

        // 3. Send request to API
        std::string api_response_str = makeApiRequest("verify_onetime_key", verification_jsonData);

        // 4. Parse API response
        std::string status = extractFromJson(api_response_str, "status");
        std::string message = extractFromJson(api_response_str, "message");
        std::string product_from_api = extractFromJson(api_response_str, "product");
        // std::string username_from_api = extractFromJson(api_response_str, "username"); // Not used directly in UI
        // std::string user_role_from_api = extractFromJson(api_response_str, "role"); // Not used directly in UI

        if (status == "success") {
            // 5. Loader-side verification: does the product from API match the expected product?
            if (product_from_api != EXPECTED_PRODUCT) {
                errorMessage = "Key is for product '" + product_from_api + "', but this loader is for '" + EXPECTED_PRODUCT + "'.";
                return false;
            }
            else {
                isLoggedIn = true;
                subscription = product_from_api + " (Verified)"; // Set subscription status

                // 6. Delete key from registry after successful verification
                if (DeleteKeyFromRegistry(EXPECTED_PRODUCT)) {
                    // Key deleted successfully
                }
                else {
                    errorMessage = "Failed to delete one-time key from registry. Manual removal may be required.";
                    // We don't return false, because the key was valid and used.
                }
                return true;
            }
        }
        else {
            errorMessage = message.empty() ? "API verification failed." : message;
            return false;
        }
    }

public:
    LoginApp() {
        // --- Automatic login attempt at application start ---
        std::string storedKey = ReadKeyFromRegistry(EXPECTED_PRODUCT);
        if (!storedKey.empty()) {
            PerformKeyVerification(storedKey, true);
        }
        // If automatic login fails, isLoggedIn remains false,
        // and RenderLoginScreen will be displayed as fallback.
    }

    void DrawTitleBar() {
        system("cls");
        std::cout << "____  ___                   __      __                       \n";
        std::cout << "\\   \\/  /____   ____   ____/  \\    /  \\_____ _______   ____  \n";
        std::cout << " \\     // __ \\ /    \\ /  _ \\   \\/\\/   /\\__  \\_  __ \\_/ __ \\ \n";
        std::cout << " /     \\  ___/|   |  (  <_> )        /  / __ \\|  | \\/\\  ___/ \n";
        std::cout << "/___/\\  \\___  >___|  /\\____/ \\__/\\  /  (____  /__|    \\___  >\n";
        std::cout << "      \\_/   \\/     \\/             \\/        \\/            \\/ \n\n\n";
    }

    void RenderLoginMenu() {
		SetWindowTextA(GetConsoleWindow(), "XenoWare Loader");
        DrawTitleBar();

        std::cout << "Enter your one-time key: ";
        std::string key = "";
        std::getline(std::cin, key);
		if (key.empty()) {
			errorMessage = "Key cannot be empty.";
			return;
		}
        if (!PerformKeyVerification(key, false))
        {
			std::cout << "Login failed: " << errorMessage << std::endl;
			Utils::SleepMs(2000);
        }
        else
        {
			std::cout << "Login successful! Welcome to XenoWare." << std::endl;
			Utils::SleepMs(2000);
        }
    }

    void RenderMainMenu()
    {
        if (!isLoggedIn) {
            RenderLoginMenu();
        }
        else {
            std::cout << "Initializing security...\n";
            g_statusColor = STATUS_WARNING_YELLOW;
            g_shouldStartCheat = true;
            g_appState = AppState::LOADING_CHEAT;
            g_loadingStage = LoadingStage::SECURITY_INIT;
        }
    }

    bool IsLoggedIn() const { return isLoggedIn; }
    bool IsAuthenticated() const {
        return isLoggedIn;
    }
};


// Funkcja do zabijania procesów z raportowaniem błędów
void KillProcesses(const std::vector<std::wstring>& processNames) {
    std::stringstream errorMessages;
    bool anyFailure = false;

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        std::cout << "Failed to create process snapshot for killing. Error: " << std::to_string(GetLastError()) << std::endl;
        g_statusColor = STATUS_ERROR_RED;
        return;
    }

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hSnapshot, &pe32)) {
        CloseHandle(hSnapshot);
        std::cout << "Failed to get first process for killing. Error: " + std::to_string(GetLastError()) << std::endl;
        g_statusColor = STATUS_ERROR_RED;
        return;
    }

    do {
        for (const auto& targetProcessName : processNames) {
            std::wstring currentProcessName(pe32.szExeFile);
            // Porównanie nazw procesów bez uwzględniania wielkości liter
            if (_wcsicmp(currentProcessName.c_str(), targetProcessName.c_str()) == 0) {
                HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe32.th32ProcessID);
                if (hProcess != NULL) {
                    if (TerminateProcess(hProcess, 0)) {
                        // Proces zakończony pomyślnie
                        // Opcjonalnie: logowanie sukcesu
                        // std::cout << "Successfully killed process: " << Utils::ToNarrowString(targetProcessName) << std::endl;
                    }
                    else {
                        // Nie udało się zakończyć procesu
                        anyFailure = true;
                        errorMessages << "Failed to terminate: " << Utils::ToNarrowString(targetProcessName)
                            << " (Error: " << GetLastError() << ") ";
                    }
                    CloseHandle(hProcess);
                }
                else {
                    // Nie udało się otworzyć procesu (np. brak uprawnień)
                    anyFailure = true;
                    errorMessages << "Failed to open process: " << Utils::ToNarrowString(targetProcessName)
                        << " (Error: " << GetLastError() << ") ";
                }
            }
        }
    } while (Process32Next(hSnapshot, &pe32));

    CloseHandle(hSnapshot);

    if (anyFailure) {
        std::cout << "Process termination issues: " + errorMessages.str() << std::endl;
        g_statusColor = STATUS_ERROR_RED;
    }
    else {
        std::cout << "Conflicting processes terminated successfully." << std::endl;
        g_statusColor = STATUS_SUCCESS_GREEN;
    }
}

void RunCheatLoader() {
    try {
        g_loadingStage = LoadingStage::SECURITY_INIT;
        std::cout << "Initializing security..." << std::endl;
        g_statusColor = STATUS_WARNING_YELLOW;

        // Lista procesów do zabicia
        std::vector<std::wstring> processesToKill = {
            Utils::ToWideString("EpicGamesLauncher.exe"),
            Utils::ToWideString("FortniteLauncher.exe"),
            Utils::ToWideString("FortniteClient-Win64-Shipping_BE.exe"),
            Utils::ToWideString("FortniteClient-Win64-Shipping.exe"),
            Utils::ToWideString("EasyAntiCheat.exe")
        };

        // Wywołanie funkcji zabijającej procesy
        std::cout << "Attempting to terminate conflicting processes..." << std::endl;
        g_statusColor = STATUS_WARNING_YELLOW;
        KillProcesses(processesToKill);
        Utils::SleepMs(1500); // Daj chwilę na zakończenie procesów i wyświetlenie statusu

        // Jeśli procesy nie zostały pomyślnie zakończone, możesz zdecydować, czy kontynuować.
        // Na przykład, jeśli g_statusColor jest czerwony, możesz wyświetlić błąd i wrócić do ekranu logowania.
        // if (g_statusColor == STATUS_ERROR_RED) {
        //     g_appState = AppState::LOGIN;
        //     g_cheatCompleted = false;
        //     g_loadingStage = LoadingStage::IDLE;
        //     return; // Zakończ działanie loadera
        // }


        mainprotect();
        Utils::SleepMs(1000);

        DriverManager driver;
        if (!driver.Initialize()) {
            g_appState = AppState::LOGIN;
            g_cheatCompleted = false;
            g_loadingStage = LoadingStage::IDLE;
            std::cout << "Driver initialization failed" << std::endl;
            g_statusColor = STATUS_ERROR_RED;
            return;
        }

        ProcessManager process;
        if (!process.WaitForTarget()) {
            g_appState = AppState::LOGIN;
            g_cheatCompleted = false;
            g_loadingStage = LoadingStage::IDLE;
            std::cout << "Process attachment failed" << std::endl;
            g_statusColor = STATUS_ERROR_RED;
            return;
        }

        OverlayManager overlay;
        if (!overlay.Initialize()) {
            g_appState = AppState::LOGIN;
            g_cheatCompleted = false;
            g_loadingStage = LoadingStage::IDLE;
            std::cout << "Overlay initialization failed" << std::endl;
            g_statusColor = STATUS_ERROR_RED;
            return;
        }

        g_loadingStage = LoadingStage::COMPLETED;
        std::cout << "Injection complete! Starting overlay..." << std::endl;
        g_statusColor = STATUS_SUCCESS_GREEN;
        Utils::SleepMs(1500);

        std::thread lootThread(CacheLevels);
        lootThread.detach();

        g_cheatCompleted = true;
        g_appState = AppState::CHEAT_RUNNING;

        StartOverlay();
    }
    catch (...) {
        g_appState = AppState::LOGIN;
        g_cheatCompleted = false;
        g_loadingStage = LoadingStage::IDLE;
        std::cout << "Unknown error occurred" << std::endl;
        g_statusColor = STATUS_ERROR_RED;
    }
}

int main(int, char**)
{
    AllocConsole();
    freopen("conin$", "r", stdin);
    freopen("conout$", "w", stdout);
    freopen("conout$", "w", stderr);

    curl_global_init(CURL_GLOBAL_DEFAULT);

    //ImGui::StyleColorsDark();

    //ImGuiStyle& style = ImGui::GetStyle();
    //style.WindowRounding = 0.0f;
    //style.WindowBorderSize = 0.0f;
    //style.FrameRounding = 8.0f;
    //style.FrameBorderSize = 1.5f;

    //ImVec4* colors = style.Colors;
    //// POPRAWKA: Przypisanie ImVec4 bezpośrednio
    //colors[ImGuiCol_WindowBg] = BACKGROUND_START_COLOR;
    //colors[ImGuiCol_Border] = NEON_BLUE_PRIMARY;
    //colors[ImGuiCol_Button] = NEON_BLUE_DARK;
    //colors[ImGuiCol_ButtonHovered] = NEON_BLUE_PRIMARY;
    //colors[ImGuiCol_ButtonActive] = NEON_BLUE_DARK;
    //colors[ImGuiCol_Text] = TEXT_PRIMARY_COLOR; // Global text color

    WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"XenoWare Loader BETA", nullptr };
    ::RegisterClassExW(&wc);
    HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"XenoWare Loader BETA", WS_POPUP, 100, 100, 960, 680, nullptr, nullptr, wc.hInstance, nullptr);

    if (!CreateDeviceD3D(hwnd))
    {
        CleanupDeviceD3D();
        ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
        curl_global_cleanup();
        return 1;
    }

    ::ShowWindow(hwnd, SW_SHOWDEFAULT);
    ::UpdateWindow(hwnd);

    IMGUI_CHECKVERSION();
    ImGui::CreateContext();

    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
    ImFontConfig cfg;
    cfg.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_NoHinting | ImGuiFreeTypeBuilderFlags_LightHinting | ImGuiFreeTypeBuilderFlags_LoadColor;

    io.Fonts->AddFontFromMemoryTTF(PoppinsMedium, sizeof(PoppinsMedium), 18.f, &cfg, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());

    ImGui_ImplWin32_Init(hwnd);
    ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);


    ImGuiStyle& s = ImGui::GetStyle();
    s.FramePadding = ImVec2(15, 20);
    s.ItemSpacing = ImVec2(10, 10);
    s.FrameRounding = 2.f;
    s.WindowRounding = 20.f;
    s.WindowBorderSize = 0.f;
    s.PopupBorderSize = 0.f;
    s.WindowPadding = ImVec2(0, 0);
    s.ChildBorderSize = 1.f;
    s.Colors[ImGuiCol_Border] = ImVec4(0.f, 0.f, 0.f, 0.f);
    s.Colors[ImGuiCol_Separator] = ImVec4(1.f, 1.f, 1.f, 0.2f);
    s.Colors[ImGuiCol_BorderShadow] = ImVec4(0.f, 0.f, 0.f, 0.f);
    s.WindowShadowSize = 0;
    s.PopupRounding = 5.f;
    s.ScrollbarSize = 1;
    s.SeparatorTextPadding = ImVec2(10, 10);

    //io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 17.0f); // Consider a more futuristic font here

    /*LoginApp app;

    if (app.IsLoggedIn()) {
        std::cout << "Authenticated. Initializing cheat...";
        g_statusColor = STATUS_SUCCESS_GREEN;
        g_shouldStartCheat = true;
        g_appState = AppState::LOADING_CHEAT;
        g_loadingStage = LoadingStage::SECURITY_INIT;
    }
    else {
        std::cout << "Awaiting one-time key...";
        g_statusColor = STATUS_WARNING_YELLOW;
    }*/

    g_statusColor = STATUS_SUCCESS_GREEN;
    g_shouldStartCheat = true;
    g_appState = AppState::LOADING_CHEAT;
    g_loadingStage = LoadingStage::SECURITY_INIT;

    std::thread* cheatThread = nullptr;

    bool done = false;
    auto lastTime = std::chrono::high_resolution_clock::now();

    while (!done)
    {
        MSG msg;
        while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            if (msg.message == WM_QUIT)
                done = true;
        }
        if (done)
            break;

        if (g_shouldStartCheat && g_appState == AppState::LOADING_CHEAT && !cheatThread) {
            g_shouldStartCheat = false;
            cheatThread = new std::thread(RunCheatLoader);
            cheatThread->detach();
        }

        if (g_appState == AppState::CHEAT_RUNNING) {
            ::ShowWindow(hwnd, SW_HIDE);
            Utils::SleepMs(100);
            continue;
        }

        //app.RenderMainMenu();
    }

    ImGui_ImplDX11_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    ::DestroyWindow(hwnd);
    ::UnregisterClassW(wc.lpszClassName, wc.hInstance);

    curl_global_cleanup();
    return 0;
}

bool CreateDeviceD3D(HWND hWnd)
{
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 2;
    sd.BufferDesc.Width = 0;
    sd.BufferDesc.Height = 0;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    UINT createDeviceFlags = 0;
    D3D_FEATURE_LEVEL featureLevel;
    const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
    HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res == DXGI_ERROR_UNSUPPORTED)
        res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res != S_OK)
        return false;

    CreateRenderTarget();
    return true;
}

void CleanupDeviceD3D()
{
    CleanupRenderTarget();
    if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
    if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
    if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
}

void CreateRenderTarget()
{
    ID3D11Texture2D* pBackBuffer;
    g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
    pBackBuffer->Release();
}

void CleanupRenderTarget()
{
    if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
}

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

static bool g_isDragging = false;
static POINT g_dragOffset = { 0, 0 };

LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    switch (msg)
    {
    case WM_LBUTTONDOWN:
    {
        POINT pt;
        GetCursorPos(&pt);
        ScreenToClient(hWnd, &pt);

        RECT rect;
        GetClientRect(hWnd, &rect);

        if (pt.y < 70 && pt.x < rect.right - 55)
        {
            g_isDragging = true;
            POINT cursorPos;
            GetCursorPos(&cursorPos);
            RECT windowRect;
            GetWindowRect(hWnd, &windowRect);
            g_dragOffset.x = cursorPos.x - windowRect.left;
            g_dragOffset.y = cursorPos.y - windowRect.top;
            SetCapture(hWnd);
        }
        break;
    }
    case WM_LBUTTONUP:
    {
        if (g_isDragging)
        {
            g_isDragging = false;
            ReleaseCapture();
        }
        break;
    }
    case WM_MOUSEMOVE:
    {
        if (g_isDragging)
        {
            POINT cursorPos;
            GetCursorPos(&cursorPos);
            SetWindowPos(hWnd, nullptr,
                cursorPos.x - g_dragOffset.x,
                cursorPos.y - g_dragOffset.y,
                0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
        break;
    }
    case WM_SIZE:
        if (wParam == SIZE_MINIMIZED)
            return 0;
        if (g_pd3dDevice != nullptr)
        {
            CleanupRenderTarget();
            g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
            CreateRenderTarget();
        }
        return 0;
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU)
            return 0;
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}
#pragma warning(pop)
