#include "UI.h" #include "FPSModule.h" #include "ModuleManager.h" #include "SettingsTAB.h" #include "helpers.h" #include #include #include #include #include namespace { ID3D11Device* g_d3d_device = nullptr; ID3D11DeviceContext* g_d3d_context = nullptr; IDXGISwapChain* g_swap_chain = nullptr; ID3D11RenderTargetView* g_main_rtv = nullptr; HWND g_overlay_hwnd = nullptr; bool g_overlay_interactive = false; WNDPROC g_game_wndproc = nullptr; bool g_game_wndproc_installed = false; bool g_game_thread_attached_logged = false; constexpr LONG_PTR kOverlayExStyleInteractive = WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW; constexpr LONG_PTR kOverlayExStylePassthrough = kOverlayExStyleInteractive | WS_EX_TRANSPARENT; constexpr UINT_PTR kGameThreadTickTimerId = 0xEF01; constexpr UINT kGameThreadTickMs = 16; void CleanupRenderTarget() { if (g_main_rtv) { g_main_rtv->Release(); g_main_rtv = nullptr; } } void CreateRenderTarget() { ID3D11Texture2D* back_buffer = nullptr; if (g_swap_chain && SUCCEEDED(g_swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)))) { g_d3d_device->CreateRenderTargetView(back_buffer, nullptr, &g_main_rtv); back_buffer->Release(); } } bool CreateDeviceD3D(HWND hwnd) { DXGI_SWAP_CHAIN_DESC 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; constexpr D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; D3D_FEATURE_LEVEL created_level{}; HRESULT hr = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, levels, ARRAYSIZE(levels), D3D11_SDK_VERSION, &sd, &g_swap_chain, &g_d3d_device, &created_level, &g_d3d_context); if (FAILED(hr)) { return false; } CreateRenderTarget(); return true; } void CleanupDeviceD3D() { CleanupRenderTarget(); if (g_swap_chain) { g_swap_chain->Release(); g_swap_chain = nullptr; } if (g_d3d_context) { g_d3d_context->Release(); g_d3d_context = nullptr; } if (g_d3d_device) { g_d3d_device->Release(); g_d3d_device = nullptr; } } bool GetGameRect(RECT* rect) { if (!rect) { return false; } if (!g_Runtime.game_hwnd || !IsWindow(g_Runtime.game_hwnd)) { g_Runtime.game_hwnd = FindWindowA("UnityWndClass", nullptr); } if (!g_Runtime.game_hwnd || !IsWindow(g_Runtime.game_hwnd)) { return false; } return GetWindowRect(g_Runtime.game_hwnd, rect) == TRUE; } void ApplyOverlayInteractivity(bool interactive) { if (!g_overlay_hwnd) { return; } if (g_overlay_interactive == interactive) { return; } const LONG_PTR ex_style = interactive ? kOverlayExStyleInteractive : kOverlayExStylePassthrough; SetWindowLongPtrW(g_overlay_hwnd, GWL_EXSTYLE, ex_style); UINT flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW; if (!interactive) { flags |= SWP_NOACTIVATE; } SetWindowPos(g_overlay_hwnd, HWND_TOPMOST, 0, 0, 0, 0, flags); if (interactive) { BringWindowToTop(g_overlay_hwnd); SetForegroundWindow(g_overlay_hwnd); SetFocus(g_overlay_hwnd); } g_overlay_interactive = interactive; } bool ContainsNoCase(const std::string& text, const std::string& needle) { if (needle.empty()) { return true; } auto lower = [](const std::string& s) { std::string out = s; std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); return out; }; const std::string hay = lower(text); const std::string ned = lower(needle); return hay.find(ned) != std::string::npos; } bool MainTabToCategory(MainTab tab, ModuleCategory* out_category) { if (!out_category) { return false; } switch (tab) { case MainTab::Player: *out_category = ModuleCategory::Player; return true; case MainTab::ESP: *out_category = ModuleCategory::ESP; return true; case MainTab::World: *out_category = ModuleCategory::World; return true; case MainTab::Visual: *out_category = ModuleCategory::Visual; return true; default: break; } return false; } void HandleTabHotkeys() { if (IsKeyPressedOnce(VK_F1)) g_Runtime.active_tab.store(MainTab::About, std::memory_order_relaxed); if (IsKeyPressedOnce(VK_F2)) g_Runtime.active_tab.store(MainTab::Player, std::memory_order_relaxed); if (IsKeyPressedOnce(VK_F3)) g_Runtime.active_tab.store(MainTab::ESP, std::memory_order_relaxed); if (IsKeyPressedOnce(VK_F4)) g_Runtime.active_tab.store(MainTab::World, std::memory_order_relaxed); if (IsKeyPressedOnce(VK_F5)) g_Runtime.active_tab.store(MainTab::Visual, std::memory_order_relaxed); if (IsKeyPressedOnce(VK_F6)) g_Runtime.active_tab.store(MainTab::Settings, std::memory_order_relaxed); } void RenderAboutPanel() { ImGui::Text("%s", "Endfield CTB3"); ImGui::Text("%s", "Version: 1.0.0"); ImGui::Text("%s", "Author: gktwo"); ImGui::Separator(); ImGui::BulletText("F12: Show/Hide panel"); ImGui::BulletText("F1..F6: Switch tabs quickly"); ImGui::BulletText("Use search to filter modules"); ImGui::BulletText("Use Quick Toggles for fast ON/OFF"); } void RenderCategoryModules(MainTab selected_tab, const std::string& filter) { if (!g_Runtime.il2cpp_thread_attached.load(std::memory_order_relaxed)) { ImGui::TextDisabled("IL2CPP thread is not attached."); ImGui::TextDisabled("Skipping module calls to prevent GC crash."); return; } ModuleCategory category{}; if (!MainTabToCategory(selected_tab, &category)) { return; } ModuleManager& manager = GetModuleManager(); std::vector modules = manager.GetModulesByCategory(category); if (modules.empty()) { ImGui::TextDisabled("No modules available."); return; } if (ImGui::CollapsingHeader("Quick Toggles", ImGuiTreeNodeFlags_DefaultOpen)) { int shown = 0; for (BaseModule* mod : modules) { if (!mod) { continue; } const bool visible = filter.empty() || ContainsNoCase(mod->Name(), filter) || ContainsNoCase(mod->TabLabel(), filter); if (!visible) { continue; } bool on = mod->Enabled(); const std::string label = mod->TabLabel() + "##quick"; if (ImGui::Checkbox(label.c_str(), &on)) { mod->SetEnabled(on); } shown++; } if (shown == 0) { ImGui::TextDisabled("No module matches \"%s\".", filter.c_str()); } } ImGui::Separator(); int rendered = 0; for (BaseModule* mod : modules) { if (!mod) { continue; } const bool visible = filter.empty() || ContainsNoCase(mod->Name(), filter) || ContainsNoCase(mod->TabLabel(), filter); if (!visible) { continue; } bool pushed_id = false; try { ImGui::PushID(mod->Name().c_str()); pushed_id = true; mod->Render(); ImGui::PopID(); pushed_id = false; } catch (...) { if (pushed_id) { ImGui::PopID(); } Log("[ERROR] Failed to render overlay for module %s", mod->Name().c_str()); } rendered++; } if (rendered == 0) { ImGui::TextDisabled("No module matches \"%s\".", filter.c_str()); } } void RenderFpsOverlay() { if (!IsFPSOverlayEnabled()) { return; } ImGuiIO& io = ImGui::GetIO(); const int fps_rounded = static_cast(io.Framerate + 0.5f); char text[32] = {}; sprintf_s(text, "FPS: %d", fps_rounded); ImU32 color = IM_COL32(255, 64, 64, 255); if (fps_rounded >= 60) { color = IM_COL32(64, 255, 96, 255); } else if (fps_rounded >= 30) { color = IM_COL32(255, 225, 80, 255); } ImDrawList* draw = ImGui::GetForegroundDrawList(); draw->AddText(ImVec2(12.0f, 12.0f), IM_COL32(0, 0, 0, 255), text); draw->AddText(ImVec2(10.0f, 10.0f), color, text); } void RenderSidebar() { static constexpr const char* tabs[] = { "About", "Player", "ESP", "World", "Visual", "Settings", }; static constexpr const char* tab_buttons[] = { "F1 About", "F2 Player", "F3 ESP", "F4 World", "F5 Visual", "F6 Settings", }; static char module_filter[96] = {}; static int language_idx = static_cast(g_Runtime.language.load(std::memory_order_relaxed)); HandleTabHotkeys(); ImGui::SetNextWindowSize(ImVec2(1320.0f, 980.0f), ImGuiCond_Always); ImGui::SetNextWindowSizeConstraints(ImVec2(1100.0f, 850.0f), ImVec2(1800.0f, 1500.0f)); ImGui::SetNextWindowBgAlpha(0.96f); if (!ImGui::Begin("Sidebar", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar)) { ImGui::End(); return; } ImGui::TextUnformatted("EF - Cheeto"); ImGui::SameLine(); ImGui::TextDisabled("(F12 show/hide)"); ImGui::TextUnformatted(Tr("This is free, if you spend money, then you have been deceived")); ImGui::Separator(); const char* languages[] = {"English", "Chinese (Simplified)"}; ImGui::SetNextItemWidth(320.0f); if (ImGui::Combo("##Language", &language_idx, languages, IM_ARRAYSIZE(languages))) { SetLanguage(language_idx == 0 ? Language::English : Language::ZhCN); } MainTab selected = g_Runtime.active_tab.load(std::memory_order_relaxed); const float available_width = ImGui::GetContentRegionAvail().x; const float nav_width = std::clamp(available_width * 0.45f, 460.0f, 620.0f); ImGui::BeginChild("##nav", ImVec2(nav_width, 0.0f), true); ImGui::TextDisabled("Navigation"); ImGui::Separator(); for (int i = 0; i < static_cast(IM_ARRAYSIZE(tabs)); ++i) { const bool is_active = (static_cast(selected) == i); if (is_active) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.30f, 0.45f, 0.63f, 0.95f)); } if (ImGui::Button(tab_buttons[i], ImVec2(-1.0f, 62.0f))) { selected = static_cast(i); } if (is_active) { ImGui::PopStyleColor(); } } ImGui::Separator(); ImGui::TextDisabled("Hotkeys: F1..F6 switch tabs"); ImGui::TextDisabled("F12 show/hide menu"); ImGui::EndChild(); g_Runtime.active_tab.store(selected, std::memory_order_relaxed); ImGui::SameLine(); ImGui::BeginChild("##content", ImVec2(0.0f, 0.0f), true); if (selected != MainTab::About && selected != MainTab::Settings) { ImGui::InputTextWithHint("##module_filter", "Search module (name/tab)...", module_filter, IM_ARRAYSIZE(module_filter)); ImGui::Separator(); } else { module_filter[0] = '\0'; } switch (selected) { case MainTab::About: RenderAboutPanel(); break; case MainTab::Settings: RenderSettingsTab(); break; default: RenderCategoryModules(selected, module_filter); break; } ImGui::EndChild(); ImGui::End(); } void DispatchModulesOnGameThread() { SetGameThreadContext(true); g_Runtime.game_thread_id.store(GetCurrentThreadId(), std::memory_order_relaxed); if (EnsureIl2CppThreadAttached()) { g_Runtime.il2cpp_thread_attached.store(true, std::memory_order_relaxed); if (!g_game_thread_attached_logged) { Log("IL2CPP game-thread dispatcher attached"); g_game_thread_attached_logged = true; } GetModuleManager().DispatchHotkeys(); GetModuleManager().DispatchUpdate(); } else { g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed); g_game_thread_attached_logged = false; } SetGameThreadContext(false); } LRESULT CALLBACK GameWndProcHook(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_TIMER && wparam == kGameThreadTickTimerId) { DispatchModulesOnGameThread(); return 0; } if (g_game_wndproc) { return CallWindowProcW(g_game_wndproc, hwnd, msg, wparam, lparam); } return DefWindowProcW(hwnd, msg, wparam, lparam); } void InstallGameThreadDispatcher() { if (g_game_wndproc_installed) { return; } if (!g_Runtime.game_hwnd || !IsWindow(g_Runtime.game_hwnd)) { return; } SetLastError(0); LONG_PTR prev = SetWindowLongPtrW(g_Runtime.game_hwnd, GWLP_WNDPROC, reinterpret_cast(GameWndProcHook)); if (prev == 0 && GetLastError() != 0) { return; } g_game_wndproc = reinterpret_cast(prev); if (SetTimer(g_Runtime.game_hwnd, kGameThreadTickTimerId, kGameThreadTickMs, nullptr) == 0) { if (g_game_wndproc) { SetWindowLongPtrW(g_Runtime.game_hwnd, GWLP_WNDPROC, reinterpret_cast(g_game_wndproc)); } g_game_wndproc = nullptr; return; } g_game_wndproc_installed = true; Log("Installed game-thread dispatcher"); } void UninstallGameThreadDispatcher() { if (!g_game_wndproc_installed) { return; } if (g_Runtime.game_hwnd && IsWindow(g_Runtime.game_hwnd)) { KillTimer(g_Runtime.game_hwnd, kGameThreadTickTimerId); if (g_game_wndproc) { SetWindowLongPtrW(g_Runtime.game_hwnd, GWLP_WNDPROC, reinterpret_cast(g_game_wndproc)); } } g_game_wndproc = nullptr; g_game_wndproc_installed = false; g_game_thread_attached_logged = false; g_Runtime.il2cpp_thread_attached.store(false, std::memory_order_relaxed); } } // namespace LRESULT CALLBACK OverlayWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (ImGui::GetCurrentContext() != nullptr) { if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam)) { return 1; } } switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_SIZE: if (g_swap_chain != nullptr && wparam != SIZE_MINIMIZED) { CleanupRenderTarget(); g_swap_chain->ResizeBuffers(0, LOWORD(lparam), HIWORD(lparam), DXGI_FORMAT_UNKNOWN, 0); CreateRenderTarget(); } return 0; case WM_SYSCOMMAND: if ((wparam & 0xFFF0) == SC_KEYMENU) { return 0; } break; default: break; } return DefWindowProcW(hwnd, msg, wparam, lparam); } bool InitializeOverlayWindow() { if (g_overlay_hwnd) { return true; } WNDCLASSEXW wc{}; wc.cbSize = sizeof(wc); wc.style = CS_CLASSDC; wc.lpfnWndProc = OverlayWndProc; wc.hInstance = GetModuleHandleW(nullptr); wc.hCursor = LoadCursorW(nullptr, IDC_ARROW); wc.lpszClassName = L"EF_Cheeto_OverlayWndClass"; if (!RegisterClassExW(&wc)) { return false; } RECT rect{}; if (!GetGameRect(&rect)) { rect = {0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)}; } g_overlay_hwnd = CreateWindowExW( kOverlayExStylePassthrough, wc.lpszClassName, L"EF-Cheeto Overlay", WS_POPUP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, wc.hInstance, nullptr); if (!g_overlay_hwnd) { UnregisterClassW(wc.lpszClassName, wc.hInstance); return false; } SetLayeredWindowAttributes(g_overlay_hwnd, RGB(0, 0, 0), 255, LWA_ALPHA); MARGINS margins{-1}; DwmExtendFrameIntoClientArea(g_overlay_hwnd, &margins); if (!CreateDeviceD3D(g_overlay_hwnd)) { DestroyWindow(g_overlay_hwnd); g_overlay_hwnd = nullptr; UnregisterClassW(wc.lpszClassName, wc.hInstance); return false; } ShowWindow(g_overlay_hwnd, SW_SHOWDEFAULT); UpdateWindow(g_overlay_hwnd); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.IniFilename = "imgui.ini"; io.LogFilename = "imgui_log.txt"; io.FontGlobalScale = 2.00f; ImGuiStyle& style = ImGui::GetStyle(); style.WindowRounding = 7.0f; style.FrameRounding = 6.0f; style.GrabRounding = 6.0f; style.ScrollbarRounding = 8.0f; style.FramePadding = ImVec2(10.0f, 8.0f); style.ItemSpacing = ImVec2(10.0f, 10.0f); style.CellPadding = ImVec2(8.0f, 8.0f); ImGui_ImplWin32_Init(g_overlay_hwnd); ImGui_ImplDX11_Init(g_d3d_device, g_d3d_context); g_Runtime.overlay_hwnd = g_overlay_hwnd; Log("Initialized"); return true; } void RunOverlayLoop() { if (!InitializeOverlayWindow()) { return; } MSG msg{}; while (g_Runtime.running.load(std::memory_order_relaxed)) { while (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); if (msg.message == WM_QUIT) { g_Runtime.running.store(false, std::memory_order_relaxed); } } if (!g_Runtime.running.load(std::memory_order_relaxed)) { break; } RECT rect{}; const bool show_panel = g_Runtime.show_panel.load(std::memory_order_relaxed); ApplyOverlayInteractivity(show_panel); if (GetGameRect(&rect) && g_overlay_hwnd) { InstallGameThreadDispatcher(); UINT pos_flags = SWP_NOOWNERZORDER; if (!show_panel) { pos_flags |= SWP_NOACTIVATE; } SetWindowPos(g_overlay_hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pos_flags); } if (IsKeyPressedOnce(VK_F12)) { const bool new_state = !g_Runtime.show_panel.load(std::memory_order_relaxed); g_Runtime.show_panel.store(new_state, std::memory_order_relaxed); ApplyOverlayInteractivity(new_state); } ImGui_ImplDX11_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); RenderFpsOverlay(); if (show_panel) { RenderSidebar(); } ImGui::Render(); const float clear_color[4] = {0.f, 0.f, 0.f, 0.f}; g_d3d_context->OMSetRenderTargets(1, &g_main_rtv, nullptr); g_d3d_context->ClearRenderTargetView(g_main_rtv, clear_color); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); g_swap_chain->Present(1, 0); } } void ShutdownOverlayWindow() { UninstallGameThreadDispatcher(); ImGui_ImplDX11_Shutdown(); ImGui_ImplWin32_Shutdown(); if (ImGui::GetCurrentContext()) { ImGui::DestroyContext(); } CleanupDeviceD3D(); if (g_overlay_hwnd) { DestroyWindow(g_overlay_hwnd); g_overlay_hwnd = nullptr; } HINSTANCE hinst = GetModuleHandleW(nullptr); UnregisterClassW(L"EF_Cheeto_OverlayWndClass", hinst); }