t7x/src/client/component/console.cpp

215 lines
6.2 KiB
C++
Raw Normal View History

2022-05-21 06:04:08 -04:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "resource.hpp"
#include "game/game.hpp"
2022-05-21 06:04:08 -04:00
#include <utils/thread.hpp>
#include <utils/hook.hpp>
2022-06-02 15:50:30 -04:00
#define CONSOLE_BUFFER_SIZE 16384
#define WINDOW_WIDTH 608
2022-05-21 06:04:08 -04:00
namespace console
{
2022-05-23 11:57:45 -04:00
namespace
{
HANDLE logo;
2022-09-17 02:00:43 -04:00
std::atomic_bool started{false};
std::atomic_bool terminate_runner{false};
2022-05-30 14:37:58 -04:00
void print_message(const char* message)
{
2022-09-17 07:42:25 -04:00
#ifndef NDEBUG
2022-09-24 09:40:43 -04:00
OutputDebugStringA(message);
2022-09-17 07:42:25 -04:00
#endif
2022-09-17 02:00:43 -04:00
if (started && !terminate_runner)
2022-05-30 14:37:58 -04:00
{
game::Com_Printf(0, 0, "%s", message);
2022-05-30 14:37:58 -04:00
}
}
void print_stub(const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char buffer[1024]{0};
const int res = vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap);
2022-05-30 15:25:52 -04:00
(void)res;
2022-05-30 14:37:58 -04:00
print_message(buffer);
va_end(ap);
}
2022-06-02 14:42:58 -04:00
LRESULT con_wnd_proc(const HWND hwnd, const UINT msg, const WPARAM wparam, const LPARAM lparam)
{
2022-06-02 14:42:58 -04:00
switch (msg)
{
case WM_CTLCOLOREDIT:
2022-06-02 14:42:58 -04:00
case WM_CTLCOLORSTATIC:
SetBkColor(reinterpret_cast<HDC>(wparam), RGB(50, 50, 50));
SetTextColor(reinterpret_cast<HDC>(wparam), RGB(232, 230, 227));
return reinterpret_cast<INT_PTR>(CreateSolidBrush(RGB(50, 50, 50)));
2022-06-02 14:49:03 -04:00
case WM_CLOSE:
2022-06-02 07:42:32 -04:00
game::Cbuf_AddText(0, "quit\n");
2022-06-02 14:49:03 -04:00
[[fallthrough]];
default:
2022-06-02 14:42:58 -04:00
return utils::hook::invoke<LRESULT>(0x142333520_g, hwnd, msg, wparam, lparam);
}
}
2022-06-02 14:42:58 -04:00
LRESULT input_line_wnd_proc(const HWND hwnd, const UINT msg, const WPARAM wparam, const LPARAM lparam)
{
2022-06-02 14:42:58 -04:00
return utils::hook::invoke<LRESULT>(0x142333820_g, hwnd, msg, wparam, lparam);
}
2022-06-02 14:42:58 -04:00
void sys_create_console_stub(const HINSTANCE h_instance)
{
2022-06-02 07:42:32 -04:00
// C6262
char text[CONSOLE_BUFFER_SIZE];
2022-06-02 14:42:58 -04:00
char clean_console_buffer[CONSOLE_BUFFER_SIZE];
const auto* class_name = "BOIII WinConsole";
const auto* window_name = "BOIII Console";
2022-06-02 14:42:58 -04:00
WNDCLASSA wnd_class{};
wnd_class.style = 0;
wnd_class.lpfnWndProc = con_wnd_proc;
wnd_class.cbClsExtra = 0;
wnd_class.cbWndExtra = 0;
wnd_class.hInstance = h_instance;
wnd_class.hIcon = LoadIconA(h_instance, reinterpret_cast<LPCSTR>(1));
wnd_class.hCursor = LoadCursorA(nullptr, reinterpret_cast<LPCSTR>(0x7F00));
wnd_class.hbrBackground = CreateSolidBrush(RGB(50, 50, 50));
wnd_class.lpszMenuName = nullptr;
wnd_class.lpszClassName = class_name;
if (!RegisterClassA(&wnd_class))
{
return;
}
RECT rect{};
2022-06-02 14:42:58 -04:00
rect.left = 0;
rect.right = 620;
rect.top = 0;
rect.bottom = 450;
AdjustWindowRect(&rect, 0x80CA0000, 0);
2022-06-02 14:42:58 -04:00
auto dc = GetDC(GetDesktopWindow());
const auto swidth = GetDeviceCaps(dc, 8);
const auto sheight = GetDeviceCaps(dc, 10);
ReleaseDC(GetDesktopWindow(), dc);
utils::hook::set<int>(game::s_wcd::windowWidth, (rect.right - rect.left + 1));
utils::hook::set<int>(game::s_wcd::windowHeight, (rect.bottom - rect.top + 1));
utils::hook::set<HWND>(game::s_wcd::hWnd, CreateWindowExA(
2022-06-02 14:42:58 -04:00
0, class_name, window_name, 0x80CA0000, (swidth - 600) / 2, (sheight - 450) / 2,
rect.right - rect.left + 1, rect.bottom - rect.top + 1, nullptr, nullptr,
h_instance, nullptr));
if (!*game::s_wcd::hWnd)
{
return;
}
// create fonts
2022-06-02 14:42:58 -04:00
dc = GetDC(*game::s_wcd::hWnd);
const auto n_height = MulDiv(8, GetDeviceCaps(dc, 90), 72);
utils::hook::set<HFONT>(game::s_wcd::hfBufferFont, CreateFontA(
2022-06-02 14:42:58 -04:00
-n_height, 0, 0, 0, 300, 0, 0, 0, 1u, 0, 0, 0, 0x31u, "Courier New"));
2022-06-02 14:42:58 -04:00
ReleaseDC(*game::s_wcd::hWnd, dc);
if (logo)
{
utils::hook::set<HWND>(game::s_wcd::codLogo, CreateWindowExA(
2022-06-02 14:42:58 -04:00
0, "Static", nullptr, 0x5000000Eu, 5, 5, 0, 0, *game::s_wcd::hWnd,
reinterpret_cast<HMENU>(1), h_instance, nullptr));
SendMessageA(*game::s_wcd::codLogo, 0x172u, 0, reinterpret_cast<LPARAM>(logo));
}
// create the input line
utils::hook::set<HWND>(game::s_wcd::hwndInputLine, CreateWindowExA(
2022-06-02 15:50:30 -04:00
0, "edit", nullptr, 0x50800080u, 6, 400, WINDOW_WIDTH, 20, *game::s_wcd::hWnd,
2022-06-02 14:42:58 -04:00
reinterpret_cast<HMENU>(0x65), h_instance, nullptr));
utils::hook::set<HWND>(game::s_wcd::hwndBuffer, CreateWindowExA(
2022-06-02 15:50:30 -04:00
0, "edit", nullptr, 0x50A00844u, 6, 70, WINDOW_WIDTH, 324, *game::s_wcd::hWnd,
2022-06-02 14:42:58 -04:00
reinterpret_cast<HMENU>(0x64), h_instance, nullptr));
SendMessageA(*game::s_wcd::hwndBuffer, WM_SETFONT, reinterpret_cast<WPARAM>(*game::s_wcd::hfBufferFont), 0);
2022-06-02 14:42:58 -04:00
utils::hook::set<WNDPROC>(game::s_wcd::SysInputLineWndProc, reinterpret_cast<WNDPROC>(SetWindowLongPtrA(
*game::s_wcd::hwndInputLine, -4,
reinterpret_cast<LONG_PTR>(input_line_wnd_proc))));
2022-09-18 09:11:16 -04:00
SendMessageA(*game::s_wcd::hwndInputLine, WM_SETFONT, reinterpret_cast<WPARAM>(*game::s_wcd::hfBufferFont),
0);
SetFocus(*game::s_wcd::hwndInputLine);
game::Con_GetTextCopy(text, 0x4000);
2022-06-02 14:42:58 -04:00
game::Conbuf_CleanText(text, clean_console_buffer);
SetWindowTextA(*game::s_wcd::hwndBuffer, clean_console_buffer);
}
2022-05-23 11:57:45 -04:00
}
2022-05-21 06:04:08 -04:00
class component final : public component_interface
{
public:
void post_unpack() override
{
2022-09-18 09:11:16 -04:00
//utils::hook::jump(0x1423337F0_g, print_message);
//utils::hook::jump(0x142333660_g, print_message);
2022-06-02 14:42:58 -04:00
const auto self = utils::nt::library::get_by_address(sys_create_console_stub);
logo = LoadImageA(self.get_handle(), MAKEINTRESOURCEA(IMAGE_LOGO), 0, 0, 0, LR_COPYFROMRESOURCE);
2022-05-30 14:37:58 -04:00
utils::hook::jump(printf, print_stub);
2022-09-17 02:00:43 -04:00
terminate_runner = false;
2022-05-21 06:04:08 -04:00
this->console_runner_ = utils::thread::create_named_thread("Console IO", [this]
{
2022-05-27 13:08:39 -04:00
{
2022-09-16 14:54:48 -04:00
static utils::hook::detour sys_create_console_hook;
sys_create_console_hook.create(0x1423339C0_g, sys_create_console_stub);
game::Sys_ShowConsole();
2022-09-17 02:00:43 -04:00
started = true;
2022-05-27 13:08:39 -04:00
}
2022-05-23 11:57:45 -04:00
MSG msg{};
2022-09-17 02:00:43 -04:00
while (!terminate_runner)
2022-05-23 11:57:45 -04:00
{
2022-05-29 12:51:24 -04:00
if (PeekMessageW(&msg, nullptr, NULL, NULL, PM_REMOVE))
2022-05-23 11:57:45 -04:00
{
TranslateMessage(&msg);
2022-05-29 12:51:24 -04:00
DispatchMessageW(&msg);
2022-05-23 11:57:45 -04:00
}
else
{
std::this_thread::sleep_for(1ms);
}
}
2022-05-21 06:04:08 -04:00
});
}
void pre_destroy() override
{
2022-09-17 02:00:43 -04:00
terminate_runner = true;
2022-05-21 06:04:08 -04:00
if (this->console_runner_.joinable())
{
this->console_runner_.join();
}
}
private:
std::thread console_runner_;
};
}
REGISTER_COMPONENT(console::component)