Загрузка данных
#include "pch.h"
#include <Windows.h>
#include <MinHook.h>
#include <d3d11.h>
#include <dxgi.h>
#include <iostream>
// Подключаем ImGui
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
// ==================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ====================
typedef HRESULT(WINAPI* tPresent)(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags);
tPresent fpPresent = NULL;
ID3D11Device* g_pd3dDevice = NULL;
ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
ID3D11RenderTargetView* g_mainRenderTargetView = NULL;
HWND g_hWnd = NULL;
// Настройки нашего мода
bool show_menu = true;
bool show_stats = true;
// ==================== ФУНКЦИИ IMGUI ====================
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// Обработчик сообщений окна
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
switch (msg) {
case WM_SIZE:
if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED) {
// Обработка изменения размера окна
}
return 0;
case WM_SYSCOMMAND:
if ((wParam & 0xfff0) == SC_KEYMENU) // Отключаем меню по Alt
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return CallWindowProc(ImGui_ImplWin32_GetWndProc(), hWnd, msg, wParam, lParam);
}
// Инициализация ImGui
void InitImGui() {
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark(); // Темная тема (как в игре)
ImGui_ImplWin32_Init(g_hWnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
}
// Отрисовка меню (Виджет Боевая эффективность)
void RenderMenu() {
if (!show_menu) return;
// Стиль окна как на скриншотах
ImGui::SetNextWindowSize(ImVec2(400, 450), ImGuiCond_FirstUseEver);
ImGui::Begin("Боевая эффективность (Stats Mod)", &show_menu);
// Левая колонка с иконками (имитация)
ImGui::BeginChild("LeftPanel", ImVec2(50, 0), true);
ImGui::Text("X"); // Иконка мечей
ImGui::Text("O"); // Иконка монеты
ImGui::Text("S"); // Иконка звезды
ImGui::Text("Z"); // Иконка молнии
ImGui::EndChild();
ImGui::SameLine();
// Правая колонка с контентом
ImGui::BeginChild("RightPanel", ImVec2(0, 0), false);
ImGui::Text("Статистика за сессию");
ImGui::Separator();
// Чекбоксы для выбора отображения
static bool show_battles = true;
static bool show_wins = true;
static bool show_winrate = true;
static bool show_avg_damage = true;
static bool show_avg_exp = true;
ImGui::Checkbox("Проведено боёв", &show_battles);
ImGui::Checkbox("Победы", &show_wins);
ImGui::Checkbox("Процент побед", &show_winrate);
ImGui::Checkbox("Средний урон", &show_avg_damage);
ImGui::Checkbox("Средний опыт", &show_avg_exp);
ImGui::Separator();
ImGui::Text("Показатели:");
// ЗДЕСЬ ВЫВОДЯТСЯ ДАННЫЕ. ПОКА ЭТО ЗАГЛУШКИ (ЦИФРЫ С ТВОИХ СКРИНШОТОВ)
// Чтобы подтянуть реальные данные, нужно заменить цифры на переменные из памяти игры
if (show_battles) ImGui::Text("Проведено боёв: 9");
if (show_wins) ImGui::Text("Победы: 6");
if (show_winrate) ImGui::Text("Процент побед: 66.67%%");
if (show_avg_damage) ImGui::Text("Средний урон: 1913");
if (show_avg_exp) ImGui::Text("Средний опыт: 782");
ImGui::EndChild();
ImGui::End();
}
// ==================== ХУК DIRECTX 11 ====================
HRESULT WINAPI hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) {
static bool init = false;
if (!init) {
if (SUCCEEDED(pSwapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_pd3dDevice))) {
g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);
DXGI_SWAP_CHAIN_DESC sd;
pSwapChain->GetDesc(&sd);
g_hWnd = sd.OutputWindow;
// Создаем Render Target
ID3D11Texture2D* pBackBuffer = NULL;
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
if (pBackBuffer) {
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &g_mainRenderTargetView);
pBackBuffer->Release();
}
// Подменяем WndProc
ImGui_ImplWin32_SetWndProc((WNDPROC)SetWindowLongPtr(g_hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc));
InitImGui();
init = true;
}
}
if (init) {
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// Рисуем наше меню
RenderMenu();
ImGui::Render();
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, NULL);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
return fpPresent(pSwapChain, SyncInterval, Flags);
}
// ==================== ПОТОК МОДА ====================
DWORD WINAPI MainThread(LPVOID lpParam) {
// Выделяем консоль для отладки
AllocConsole();
FILE* fDummy;
freopen_s(&fDummy, "CONOUT$", "w", stdout);
std::cout << "[stats_mod] Загрузка мода..." << std::endl;
// Ждем загрузки dxgi.dll
HMODULE hDxgi = NULL;
while (!hDxgi) {
hDxgi = GetModuleHandleA("dxgi.dll");
Sleep(100);
}
// Создаем фиктивное устройство для поиска VTable
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = GetForegroundWindow();
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
IDXGISwapChain* pSwapChain = NULL;
ID3D11Device* pDevice = NULL;
ID3D11DeviceContext* pContext = NULL;
D3D_FEATURE_LEVEL featureLevel;
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &sd, &pSwapChain, &pDevice, &featureLevel, &pContext))) {
std::cout << "[!] Ошибка создания фиктивного устройства DX11" << std::endl;
return 1;
}
// Получаем адрес Present из виртуальной таблицы (индекс 8)
void** pVTable = *reinterpret_cast<void***>(pSwapChain);
void* pPresentAddr = pVTable[8];
// Освобождаем фиктивные объекты
pSwapChain->Release();
pDevice->Release();
pContext->Release();
// Инициализируем MinHook
if (MH_Initialize() != MH_OK) {
std::cout << "[!] Ошибка MinHook" << std::endl;
return 1;
}
// Ставим хук на Present
if (MH_CreateHook(pPresentAddr, &hkPresent, reinterpret_cast<LPVOID*>(&fpPresent)) != MH_OK) {
std::cout << "[!] Не удалось захукать Present" << std::endl;
return 1;
}
if (MH_EnableHook(pPresentAddr) != MH_OK) {
std::cout << "[!] Не удалось включить хук Present" << std::endl;
return 1;
}
std::cout << "[+] Мод успешно загружен! Нажми INSERT для скрытия меню." << std::endl;
// Цикл обработки нажатий клавиш
while (true) {
if (GetAsyncKeyState(VK_INSERT) & 1) {
show_menu = !show_menu;
}
Sleep(50);
}
return 0;
}
// ==================== ТОЧКА ВХОДА ====================
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
CreateThread(nullptr, 0, MainThread, hModule, 0, nullptr);
break;
case DLL_PROCESS_DETACH:
MH_DisableHook(MH_ALL_HOOKS);
MH_Uninitialize();
break;
}
return TRUE;
}