654 lines
20 KiB
C++
654 lines
20 KiB
C++
#include "UI.h"
|
|
|
|
#include "FPSModule.h"
|
|
#include "ModuleManager.h"
|
|
#include "SettingsTAB.h"
|
|
#include "helpers.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<char>(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<BaseModule*> 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<int>(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<int>(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<int>(IM_ARRAYSIZE(tabs)); ++i) {
|
|
const bool is_active = (static_cast<int>(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<MainTab>(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<LONG_PTR>(GameWndProcHook));
|
|
if (prev == 0 && GetLastError() != 0) {
|
|
return;
|
|
}
|
|
|
|
g_game_wndproc = reinterpret_cast<WNDPROC>(prev);
|
|
if (SetTimer(g_Runtime.game_hwnd, kGameThreadTickTimerId, kGameThreadTickMs, nullptr) == 0) {
|
|
if (g_game_wndproc) {
|
|
SetWindowLongPtrW(g_Runtime.game_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(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<LONG_PTR>(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);
|
|
}
|