#include "pch.h"
#include <iostream>
#include <string>
#include <sstream>
#include <Windows.h>
namespace { // Анонимное пространство имен, ограничивает область видимости переменных
// Перечисление для состояний окна Lineage 2
enum class L2ConsoleState {
Loading = 0, // Загрузка
Unknown = 1, // Неизвестно
Login = 2, // Вход в аккаунт
CharCreate = 3,// Создание персонажа
CharSelect = 4,// Выбор персонажа
InGame = 5 // В игре
};
class UL2ConsoleWnd; // Предварительное объявление структуры окна консоли L2
UL2ConsoleWnd* UL2ConsoleWndPtr = nullptr; // Указатель на экземпляр UL2ConsoleWnd
const uintptr_t consoleOffset = 0x3663bc; // Смещение в памяти до адреса UL2ConsoleWnd
const uintptr_t stateOffset = 0x38; // Смещение в структуре UL2ConsoleWnd состояния окна игры
const uintptr_t selectedCharacterNumOffset = 0x48; // Смещение в структуре UL2ConsoleWnd index выбранного персонажа
bool consoleCreated = false; // Флаг, указывающий, была ли уже создана консоль
}
// Создает консоль, если она еще не создана
void CreateConsole() {
if (!consoleCreated) {
AllocConsole(); // Создаем окно консоли
FILE* fDummy;
freopen_s(&fDummy, "CONOUT$", "w", stdout); // Перенаправляем стандартный вывод в консоль
freopen_s(&fDummy, "CONIN$", "r", stdin); // Перенаправляем стандартный ввод в консоль
std::cout << "Console created" << std::endl; // Выводим сообщение о создании консоли
consoleCreated = true; // Устанавливаем флаг, что консоль создана
}
}
// Функция инициализации (запускается в отдельном потоке)
DWORD WINAPI init(LPVOID lpParameter) {
HMODULE hNwindowModule = nullptr; // Дескриптор модуля nwindow.dll
while ((hNwindowModule = GetModuleHandleW(L"nwindow.dll")) == nullptr) { // Ждем загрузки nwindow.dll
Sleep(1000); // Задержка 1 сек.
}
uintptr_t pUL2ConsoleWnd = reinterpret_cast<uintptr_t>(hNwindowModule) + consoleOffset; // Вычисляем адрес UL2ConsoleWnd
while ((UL2ConsoleWndPtr = *reinterpret_cast<UL2ConsoleWnd**>(pUL2ConsoleWnd)) == nullptr) { // Ждем, пока UL2ConsoleWndPtr не будет установлен
Sleep(300); // Задержка 300 мс
}
// Получаем указатели на состояние консоли и номер выбранного персонажа, используя смещения
L2ConsoleState* statePtr = reinterpret_cast<L2ConsoleState*>(reinterpret_cast<uintptr_t>(UL2ConsoleWndPtr) + stateOffset);
int* SelectedCharacterNum = reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(UL2ConsoleWndPtr) + selectedCharacterNumOffset);
int lastSelectedChar = -1; // Переменная для отслеживания последнего выбранного персонажа
while (true) { // Бесконечный цикл
if (*statePtr == L2ConsoleState::CharSelect) // Если текущее состояние консоли - выбор персонажа
{
if (!consoleCreated) // Создаем консоль, если она еще не создана
CreateConsole();
int currentSelectedChar = *SelectedCharacterNum; // Получаем текущий выбранный номер персонажа
if (currentSelectedChar != lastSelectedChar) // Если выбранный номер персонажа изменился
{
lastSelectedChar = currentSelectedChar; // Обновляем последний выбранный номер
std::cout << "SelectedCharacterNum = " << currentSelectedChar << std::endl; // Выводим сообщение с номером выбранного персонажа
}
}
Sleep(100); // Задержка 100 мс
}
return 0;
}
// Точка входа DLL
extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: // При присоединении DLL к процессу
DisableThreadLibraryCalls(hModule); // Отключаем уведомления о событиях потока для данной DLL
if (HANDLE hThread = CreateThread(nullptr, 0, init, nullptr, 0, nullptr)) { // Создаем отдельный поток для функции init
CloseHandle(hThread); // Закрываем дескриптор потока, поток продолжит работу в фоне
}
break;
}
return TRUE; // Возвращаем TRUE при успехе
}