diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index ba7ab28e..447250e8 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -6,7 +6,7 @@ #include "steam/steam.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include #include diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 5f5b2f9e..04676927 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -5,7 +5,7 @@ #include "game/game.hpp" #include "game/dvars.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "game_console.hpp" #include "scheduler.hpp" #include "dvars.hpp" diff --git a/src/client/component/console.cpp b/src/client/component/console.cpp deleted file mode 100644 index e87bd0db..00000000 --- a/src/client/component/console.cpp +++ /dev/null @@ -1,695 +0,0 @@ -#include -#include "console.hpp" -#include "loader/component_loader.hpp" - -#include "game/game.hpp" - -#include -#include -#include - -namespace -{ - bool is_dark_mode_windows() - { - HKEY reg_key; - if (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_QUERY_VALUE, - ®_key) == - ERROR_SUCCESS) - { - DWORD light_theme_value = 0x1; - DWORD length = sizeof(light_theme_value); - RegQueryValueExA(reg_key, "AppsUseLightTheme", nullptr, nullptr, reinterpret_cast(&light_theme_value), &length); - RegCloseKey(reg_key); - return light_theme_value == 0x0; - } - - return false; - } - - static bool darkmode = is_dark_mode_windows(); -} - -namespace game_console -{ - void print(int type, const std::string& data); -} - -namespace console -{ - bool is_enabled() - { - static const auto noconsole = utils::flags::has_flag("noconsole"); - return game::environment::is_dedi() || !noconsole; - } - - namespace native - { - bool is_enabled() - { - static const auto nativeconsole = utils::flags::has_flag("nativeconsole"); - return nativeconsole; - } - - static std::atomic_bool ingame = false; - static std::atomic_bool exit = false; - - DWORD WINAPI console(LPVOID) - { - ShowWindow(GetConsoleWindow(), SW_SHOW); - SetConsoleTitleA("IW7-Mod Console"); - - std::string cmd; - exit = false; - - while (!exit) - { - std::getline(std::cin, cmd); - if (ingame) - { - game::Cbuf_AddText(0, cmd.data()); - } - } - - return 0; - } - } - - namespace syscon - { -#define CONSOLE_BACKGROUND_COLOR darkmode ? RGB(50, 50, 50) : RGB(255, 255, 255) -#define CONSOLE_TEXT_COLOR darkmode ? RGB(232, 230, 227) : RGB(0, 0, 0) - - // todo: - // - history - // - resize - - struct WinConData - { - HWND hWnd; - HWND hwndBuffer; - HWND codLogo; - HFONT hfBufferFont; - HWND hwndInputLine; - char consoleText[512]; - int windowWidth; - int windowHeight; - WNDPROC SysInputLineWndProc; - char cleanBuffer[65536]; - _RTL_CRITICAL_SECTION critSect; - } s_wcd; - - HBRUSH bg_brush; - - HICON icon; - HANDLE logo; - - LRESULT ConWndProc(const HWND hwnd, const UINT umsg, const WPARAM wparam, const LPARAM lparam) - { - switch (umsg) - { - case WM_ACTIVATE: - if (LOWORD(wparam) != WA_INACTIVE) - { - SetFocus(s_wcd.hwndInputLine); - } - break; - case WM_CLOSE: - game::Cbuf_AddCall(0, game::Com_Quit_f); - DestroyWindow(hwnd); - return 0; - case WM_CTLCOLOREDIT: - case WM_CTLCOLORSTATIC: - SetBkColor(reinterpret_cast(wparam), CONSOLE_BACKGROUND_COLOR); - SetTextColor(reinterpret_cast(wparam), CONSOLE_TEXT_COLOR); - return reinterpret_cast(bg_brush); - } - - return DefWindowProcA(hwnd, umsg, wparam, lparam); - } - - unsigned int Conbuf_CleanText(const char* source, char* target, size_t size) - { - char* b = target; - int i = 0; - while (source && source[i] && (reinterpret_cast(b) - reinterpret_cast(target)) < (size - 1)) - { - if (source[i] == '\n' && source[i + 1] == '\r') - { - b[0] = '\r'; - b[1] = '\n'; - b += 2; - i++; - } - else if (source[i] == '\r' || source[i] == '\n') - { - b[0] = '\r'; - b[1] = '\n'; - b += 2; - } - else if (source[0] == '^' && source[1] && source[1] != '^' && source[1] >= 48 && source[1] <= 64) // Q_IsColorString - { - i++; - } - else - { - *b = source[i]; - b++; - } - i++; - } - - *b = '\0'; - return static_cast(b - target); - } - - void Conbuf_AppendText(const char* pmsg) - { - const char* msg; - unsigned int buf_len; - static unsigned int s_total_chars = 0; - - if (s_wcd.hwndBuffer) - { - // if the message is REALLY long, use just the last portion of it - if (strlen(pmsg) > ((sizeof(s_wcd.cleanBuffer) / 2) - 1)) - { - msg = pmsg + strlen(pmsg) - ((sizeof(s_wcd.cleanBuffer) / 2) + 1); - } - else - { - msg = pmsg; - } - - // copy into an intermediate buffer - buf_len = Conbuf_CleanText(msg, s_wcd.cleanBuffer, sizeof(s_wcd.cleanBuffer)); - - s_total_chars += buf_len; - - if (s_total_chars <= sizeof(s_wcd.cleanBuffer)) - { - SendMessageA(s_wcd.hwndBuffer, EM_SETSEL, 0xFFFF, 0xFFFF); - } - else - { - SendMessageA(s_wcd.hwndBuffer, EM_SETSEL, 0, -1); - s_total_chars = buf_len; - } - SendMessageA(s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xFFFF); - SendMessageA(s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0); - SendMessageA(s_wcd.hwndBuffer, EM_REPLACESEL, 0, reinterpret_cast(s_wcd.cleanBuffer)); - } - } - - void Sys_Print(const char* msg) - { - if (console::is_enabled()) - { - Conbuf_AppendText(msg); - } - } - - LRESULT InputLineWndProc(const HWND hwnd, const UINT umsg, const WPARAM wparam, const LPARAM lparam) - { - char dest[sizeof(s_wcd.consoleText) + 8]; - - switch (umsg) - { - case WM_KILLFOCUS: - if (reinterpret_cast(wparam) == s_wcd.hWnd) - { - SetFocus(hwnd); - return 0; - } - break; - case WM_CHAR: - const auto key = wparam; - - // enter the line - if (key == VK_RETURN) - { - memset(dest, 0, sizeof(dest)); - memset(s_wcd.consoleText, 0, sizeof(s_wcd.consoleText)); - - const auto length = GetWindowTextA(s_wcd.hwndInputLine, s_wcd.consoleText, sizeof(s_wcd.consoleText)); - if (length) - { - sprintf_s(dest, sizeof(dest), "]%s\n", s_wcd.consoleText); - SetWindowTextA(s_wcd.hwndInputLine, ""); - - Sys_Print(dest); - game::Cbuf_AddText(0, s_wcd.consoleText); - } - - return 0; - } - break; - } - - return CallWindowProcA(s_wcd.SysInputLineWndProc, hwnd, umsg, wparam, lparam); - } - - void Sys_CreateConsole(const HINSTANCE hinstance) - { - RECT rect; - WNDCLASSA wndclass; - HDC hdc; - int nheight; - int swidth, sheight; - DWORD DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX; - - const char* class_name = "IW7-Mod WinConsole"; - const char* window_name = "IW7-Mod Console"; - - memset(&rect, 0, sizeof(rect)); - memset(&wndclass, 0, sizeof(rect)); - memset(&hdc, 0, sizeof(hdc)); - - memset(&s_wcd, 0, sizeof(s_wcd)); - - wndclass.style = 0; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; - wndclass.lpfnWndProc = ConWndProc; - wndclass.hInstance = hinstance; - wndclass.hIcon = icon; - wndclass.hbrBackground = bg_brush; - wndclass.hCursor = LoadCursorA(0, IDC_ARROW); - wndclass.lpszMenuName = nullptr; - wndclass.lpszClassName = class_name; - - if (!RegisterClassA(&wndclass)) - { - return; - } - - rect.top = 0; - rect.left = 0; - rect.right = 620; - rect.bottom = 450; - AdjustWindowRect(&rect, DEDSTYLE, 0); - - hdc = GetDC(GetDesktopWindow()); - swidth = GetDeviceCaps(hdc, HORZRES); - sheight = GetDeviceCaps(hdc, VERTRES); - ReleaseDC(GetDesktopWindow(), hdc); - - s_wcd.windowHeight = rect.bottom - rect.top + 1; - s_wcd.windowWidth = rect.right - rect.left + 1; - - // create main window - s_wcd.hWnd = CreateWindowExA( - 0, - class_name, - window_name, - DEDSTYLE, - (swidth - 600) / 2, - (sheight - 450) / 2, - rect.right - rect.left + 1, - rect.bottom - rect.top + 1, - 0, - 0, - hinstance, - nullptr); - - if (!s_wcd.hWnd) - { - return; - } - - // create fonts - hdc = GetDC(s_wcd.hWnd); - nheight = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); - s_wcd.hfBufferFont = CreateFontA( - nheight, - 0, - 0, - 0, - FW_LIGHT, - 0, - 0, - 0, - DEFAULT_CHARSET, - OUT_DEFAULT_PRECIS, - CLIP_DEFAULT_PRECIS, - DEFAULT_QUALITY, - FF_MODERN | FIXED_PITCH, - "Courier New"); - ReleaseDC(s_wcd.hWnd, hdc); - - // create logo - if (logo) - { - s_wcd.codLogo = CreateWindowExA( - 0, - "Static", - 0, - WS_CHILDWINDOW | WS_VISIBLE | 0xE, - 5, - 5, - 0, - 0, - s_wcd.hWnd, - reinterpret_cast(1), - hinstance, - nullptr); - SendMessageA(s_wcd.codLogo, 0x172u, 0, reinterpret_cast(logo)); - } - - // create the input line - s_wcd.hwndInputLine = CreateWindowExA( - 0, - "edit", - 0, - WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL, - 6, - 426, - 608, - 20, - s_wcd.hWnd, - reinterpret_cast(0x65), - hinstance, - 0); - - // create the scrollbuffer - s_wcd.hwndBuffer = CreateWindowExA( - 0, - "edit", - 0, - WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, - 6, - 70, - 608, - 348, - s_wcd.hWnd, - reinterpret_cast(0x64), - hinstance, - 0); - - SendMessageA(s_wcd.hwndBuffer, WM_SETFONT, reinterpret_cast(s_wcd.hfBufferFont), 0); - SendMessageA(s_wcd.hwndBuffer, EM_LIMITTEXT, sizeof(s_wcd.cleanBuffer), 0); - s_wcd.SysInputLineWndProc = reinterpret_cast(SetWindowLongPtrA(s_wcd.hwndInputLine, -4, - reinterpret_cast(InputLineWndProc))); - SendMessageA(s_wcd.hwndInputLine, WM_SETFONT, reinterpret_cast(s_wcd.hfBufferFont), 0); - SetFocus(s_wcd.hwndInputLine); - SetWindowTextA(s_wcd.hwndBuffer, s_wcd.cleanBuffer); - InitializeCriticalSection(&s_wcd.critSect); - } - - void Sys_ShowConsole() - { - if (!console::is_enabled()) - { - return; - } - - if (!s_wcd.hWnd) - { - Sys_CreateConsole(GetModuleHandleA(reinterpret_cast(s_wcd.hWnd))); - } - if (s_wcd.hWnd) - { - ShowWindow(s_wcd.hWnd, TRUE); - SendMessageA(s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xFFFF); - } - } - - void Sys_DestroyConsole() - { - if (s_wcd.hWnd) - { - ShowWindow(s_wcd.hWnd, SW_HIDE); - CloseWindow(s_wcd.hWnd); - DestroyWindow(s_wcd.hWnd); - s_wcd.hWnd = nullptr; - DeleteCriticalSection(&s_wcd.critSect); - } - } - } - - std::string format(va_list* ap, const char* message) - { - static thread_local char buffer[0x1000]; - - const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap); - - if (count < 0) return {}; - return { buffer, static_cast(count) }; - } - - void dispatch_message(const int type, const std::string& message) - { - if (console::is_enabled()) - { - if (native::is_enabled()) - { - printf(message.data()); - } - else - { - syscon::Sys_Print(message.data()); - } - } - - game_console::print(type, message); - } - - void print(const int type, const char* fmt, ...) - { - va_list ap; - va_start(ap, fmt); - const auto result = format(&ap, fmt); - va_end(ap); - - dispatch_message(type, result); - } - - class component final : public component_interface - { - public: - component() - { - if (!console::is_enabled() || native::is_enabled()) return; - - syscon::bg_brush = CreateSolidBrush(CONSOLE_BACKGROUND_COLOR); - - const utils::nt::library self; - syscon::icon = LoadIconA(self.get_handle(), MAKEINTRESOURCEA(ID_ICON)); - syscon::logo = LoadImageA(self.get_handle(), MAKEINTRESOURCEA(IMAGE_LOGO), 0, 0, 0, LR_COPYFROMRESOURCE); - - (void)_pipe(this->handles_, 1024, _O_TEXT); - (void)_dup2(this->handles_[1], 1); - (void)_dup2(this->handles_[1], 2); - - //setvbuf(stdout, nullptr, _IONBF, 0); - //setvbuf(stderr, nullptr, _IONBF, 0); - } - - ~component() - { - if (!console::is_enabled() || native::is_enabled()) return; - - if (syscon::bg_brush) DeleteObject(syscon::bg_brush); - - if (syscon::icon) DestroyIcon(syscon::icon); - if (syscon::logo) DeleteObject(syscon::logo); - } - - void post_start() override - { - if (console::is_enabled()) - { - if (native::is_enabled()) - { - const auto handle = CreateThread(0, 0, native::console, 0, 0, 0); - utils::thread::set_name(handle, "Console"); - } - else - { - this->terminate_runner_ = false; - this->console_runner_ = utils::thread::create_named_thread("Console IO", [this]() - { - this->runner(); - }); - } - } - } - - void post_unpack() override - { - if (console::is_enabled()) - { - if (native::is_enabled()) - { - native::ingame = true; - } - else - { - this->initialize(); - } - } - } - - void pre_destroy() override - { - if (console::is_enabled()) - { - if (native::is_enabled()) - { - native::ingame = false; - native::exit = true; - } - else - { - this->destroy(); - } - } - } - - // only needed for sys_console - private: - volatile bool console_initialized_ = false; - volatile bool terminate_runner_ = false; - - std::thread console_runner_; - std::thread console_thread_; - - int handles_[2]{}; - - using message_queue = std::queue; - utils::concurrency::container messages; - - void initialize() - { - this->console_thread_ = utils::thread::create_named_thread("Console", [this]() - { - syscon::Sys_ShowConsole(); - - if (!game::environment::is_dedi()) - { - // Hide that shit - ShowWindow(syscon::s_wcd.hWnd, SW_MINIMIZE); - } - - { - messages.access([&](message_queue&) - { - this->console_initialized_ = true; - }); - } - - MSG msg; - while (!this->terminate_runner_) - { - if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - else - { - this->log_messages(); - std::this_thread::sleep_for(1ms); - } - } - }); - } - - void destroy() - { - syscon::Sys_DestroyConsole(); - - this->terminate_runner_ = true; - - printf("\r\n"); - _flushall(); - - if (this->console_runner_.joinable()) - { - this->console_runner_.join(); - } - - if (this->console_thread_.joinable()) - { - this->console_thread_.join(); - } - - _close(this->handles_[0]); - _close(this->handles_[1]); - - messages.access([&](message_queue& msgs) - { - msgs = {}; - }); - } - - void log_messages() - { - /*while*/ - if (this->console_initialized_ && !messages.get_raw().empty()) - { - std::queue message_queue_copy; - - { - messages.access([&](message_queue& msgs) - { - message_queue_copy = std::move(msgs); - msgs = {}; - }); - } - - while (!message_queue_copy.empty()) - { - log_message(message_queue_copy.front()); - message_queue_copy.pop(); - } - } - - fflush(stdout); - fflush(stderr); - } - - static void log_message(const std::string& message) - { - OutputDebugStringA(message.data()); - syscon::Conbuf_AppendText(message.data()); - } - - void runner() - { - char buffer[1024]; - - while (!this->terminate_runner_ && this->handles_[0]) - { - const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); - if (len > 0) - { - dispatch_message(con_type_info, std::string(buffer, len)); - } - else - { - std::this_thread::sleep_for(1ms); - } - } - - std::this_thread::yield(); - } - }; - - void set_title(std::string title) - { - if (console::is_enabled()) - { - if (native::is_enabled()) - { - SetConsoleTitleA(title.data()); - } - else if (syscon::s_wcd.hWnd) - { - SetWindowTextA(syscon::s_wcd.hWnd, title.data()); - } - } - } -} - -REGISTER_COMPONENT(console::component) diff --git a/src/client/component/console.hpp b/src/client/component/console.hpp deleted file mode 100644 index 0c3a6361..00000000 --- a/src/client/component/console.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -namespace console -{ - bool is_enabled(); - namespace native - { - bool is_enabled(); - } - - void set_title(std::string title); - - enum console_type - { - con_type_error = 1, - con_type_debug = 2, - con_type_warning = 3, - con_type_info = 7 - }; - - void print(int type, const char* fmt, ...); - - template - void error(const char* fmt, Args&&... args) - { - print(con_type_error, fmt, std::forward(args)...); - } - - template - void debug(const char* fmt, Args&&... args) - { -#ifdef DEBUG - print(con_type_debug, fmt, std::forward(args)...); -#endif - } - - template - void warn(const char* fmt, Args&&... args) - { - print(con_type_warning, fmt, std::forward(args)...); - } - - template - void info(const char* fmt, Args&&... args) - { - print(con_type_info, fmt, std::forward(args)...); - } -} \ No newline at end of file diff --git a/src/client/component/console/console.cpp b/src/client/component/console/console.cpp new file mode 100644 index 00000000..66fc0378 --- /dev/null +++ b/src/client/component/console/console.cpp @@ -0,0 +1,177 @@ +#include +#include "console.hpp" + +#include "game/game.hpp" + +#include + +#include "wincon.hpp" +#include "terminal.hpp" +#include "syscon.hpp" + +namespace game_console +{ + void print(int type, const std::string& data); +} + +namespace console +{ + enum console_type + { + con_type_none, + con_type_wincon, + con_type_terminal, + con_type_syscon, + con_type_game = con_type_syscon, + con_type_default = con_type_terminal, + } con_type; + + void init_console_type() + { + con_type = con_type_default; + + const auto noconsole_flag = utils::flags::has_flag("noconsole"); + if (!game::environment::is_dedi() && noconsole_flag) + { + con_type = con_type_none; + return; + } + + const auto wincon_flag = utils::flags::has_flag("wincon"); + if (wincon_flag) + { + con_type = con_type_wincon; + return; + } + + const auto terminal_flag = utils::flags::has_flag("terminal"); + if (terminal_flag) + { + con_type = con_type_terminal; + return; + } + + const auto syscon_flag = utils::flags::has_flag("syscon"); + if (syscon_flag) + { + con_type = con_type_syscon; + return; + } + } + + auto get_console_type() + { + return con_type; + } + + bool is_enabled() + { + return get_console_type() != console_type::con_type_none; + } + + namespace wincon + { + bool is_enabled() + { + return get_console_type() == console_type::con_type_wincon; + } + } + + namespace terminal + { + bool is_enabled() + { + return get_console_type() == console_type::con_type_terminal; + } + } + + namespace syscon + { + bool is_enabled() + { + return get_console_type() == console_type::con_type_syscon; + } + } + + std::string format(va_list* ap, const char* message) + { + static thread_local char buffer[0x1000]; + + const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap); + + if (count < 0) return {}; + return { buffer, static_cast(count) }; + } + + void dispatch_message(const int type, const std::string& message) + { + if (console::is_enabled()) + { + if (wincon::is_enabled()) + { + printf(message.data()); + } + else if (terminal::is_enabled()) + { + ::terminal::dispatch_message(type, message); + return; + } + else if (syscon::is_enabled()) + { + ::syscon::Sys_Print(message.data()); + } + } + + game_console::print(type, message); + } + + void print(const int type, const char* fmt, ...) + { + va_list ap; + va_start(ap, fmt); + const auto result = format(&ap, fmt); + va_end(ap); + + dispatch_message(type, result); + } + + void set_title(const std::string& title) + { + if (console::is_enabled()) + { + if (wincon::is_enabled() || terminal::is_enabled()) + { + SetConsoleTitleA(title.data()); + } + else if (syscon::is_enabled()) + { + ::syscon::set_title(title); + } + } + } + + void init() + { + static auto initialized = false; + if (initialized) return; + initialized = true; + + init_console_type(); + if (get_console_type() == con_type_none) + { + return; + } + else if (get_console_type() == con_type_wincon) + { + ::wincon::init(); + } + else if (get_console_type() == con_type_terminal) + { + ::terminal::init(); + } + else if (get_console_type() == con_type_syscon) + { + ::syscon::init(); + } + } +} \ No newline at end of file diff --git a/src/client/component/console/console.hpp b/src/client/component/console/console.hpp new file mode 100644 index 00000000..e9773858 --- /dev/null +++ b/src/client/component/console/console.hpp @@ -0,0 +1,46 @@ +#pragma once + +namespace console +{ + void set_title(const std::string& title); + + enum print_type + { + print_type_error = 1, + print_type_debug = 2, + print_type_warning = 3, + print_type_info = 7 + }; + + void dispatch_message(int type, const std::string& message); + + void print(int type, const char* fmt, ...); + + template + void error(const char* fmt, Args&&... args) + { + print(print_type_error, fmt, std::forward(args)...); + } + + template + void debug(const char* fmt, Args&&... args) + { +#ifdef DEBUG + print(print_type_debug, fmt, std::forward(args)...); +#endif + } + + template + void warn(const char* fmt, Args&&... args) + { + print(print_type_warning, fmt, std::forward(args)...); + } + + template + void info(const char* fmt, Args&&... args) + { + print(print_type_info, fmt, std::forward(args)...); + } + + void init(); +} \ No newline at end of file diff --git a/src/client/component/console/syscon.cpp b/src/client/component/console/syscon.cpp new file mode 100644 index 00000000..a3d72010 --- /dev/null +++ b/src/client/component/console/syscon.cpp @@ -0,0 +1,568 @@ +#include +#include "syscon.hpp" +#include "loader/component_loader.hpp" + +#include "console.hpp" + +#include "game/game.hpp" + +#include +#include + +namespace +{ + bool is_dark_mode_windows() + { + HKEY reg_key; + if (RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_QUERY_VALUE, + ®_key) == + ERROR_SUCCESS) + { + DWORD light_theme_value = 0x1; + DWORD length = sizeof(light_theme_value); + RegQueryValueExA(reg_key, "AppsUseLightTheme", nullptr, nullptr, reinterpret_cast(&light_theme_value), &length); + RegCloseKey(reg_key); + return light_theme_value == 0x0; + } + + return false; + } + + static bool darkmode = is_dark_mode_windows(); +} + +namespace syscon +{ +#define CONSOLE_BACKGROUND_COLOR darkmode ? RGB(50, 50, 50) : RGB(255, 255, 255) +#define CONSOLE_TEXT_COLOR darkmode ? RGB(232, 230, 227) : RGB(0, 0, 0) + + // todo: + // - history + // - resize + + struct WinConData + { + HWND hWnd; + HWND hwndBuffer; + HWND codLogo; + HFONT hfBufferFont; + HWND hwndInputLine; + char consoleText[512]; + int windowWidth; + int windowHeight; + WNDPROC SysInputLineWndProc; + char cleanBuffer[65536]; + _RTL_CRITICAL_SECTION critSect; + } s_wcd; + + HBRUSH bg_brush; + + HICON icon; + HANDLE logo; + + LRESULT ConWndProc(const HWND hwnd, const UINT umsg, const WPARAM wparam, const LPARAM lparam) + { + switch (umsg) + { + case WM_ACTIVATE: + if (LOWORD(wparam) != WA_INACTIVE) + { + SetFocus(s_wcd.hwndInputLine); + } + break; + case WM_CLOSE: + game::Cbuf_AddCall(0, game::Com_Quit_f); + DestroyWindow(hwnd); + return 0; + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: + SetBkColor(reinterpret_cast(wparam), CONSOLE_BACKGROUND_COLOR); + SetTextColor(reinterpret_cast(wparam), CONSOLE_TEXT_COLOR); + return reinterpret_cast(bg_brush); + } + + return DefWindowProcA(hwnd, umsg, wparam, lparam); + } + + unsigned int Conbuf_CleanText(const char* source, char* target, size_t size) + { + char* b = target; + int i = 0; + while (source && source[i] && (reinterpret_cast(b) - reinterpret_cast(target)) < (size - 1)) + { + if (source[i] == '\n' && source[i + 1] == '\r') + { + b[0] = '\r'; + b[1] = '\n'; + b += 2; + i++; + } + else if (source[i] == '\r' || source[i] == '\n') + { + b[0] = '\r'; + b[1] = '\n'; + b += 2; + } + else if (source[0] == '^' && source[1] && source[1] != '^' && source[1] >= 48 && source[1] <= 64) // Q_IsColorString + { + i++; + } + else + { + *b = source[i]; + b++; + } + i++; + } + + *b = '\0'; + return static_cast(b - target); + } + + void Conbuf_AppendText(const char* pmsg) + { + const char* msg; + unsigned int buf_len; + static unsigned int s_total_chars = 0; + + if (s_wcd.hwndBuffer) + { + // if the message is REALLY long, use just the last portion of it + if (strlen(pmsg) > ((sizeof(s_wcd.cleanBuffer) / 2) - 1)) + { + msg = pmsg + strlen(pmsg) - ((sizeof(s_wcd.cleanBuffer) / 2) + 1); + } + else + { + msg = pmsg; + } + + // copy into an intermediate buffer + buf_len = Conbuf_CleanText(msg, s_wcd.cleanBuffer, sizeof(s_wcd.cleanBuffer)); + + s_total_chars += buf_len; + + if (s_total_chars <= sizeof(s_wcd.cleanBuffer)) + { + SendMessageA(s_wcd.hwndBuffer, EM_SETSEL, 0xFFFF, 0xFFFF); + } + else + { + SendMessageA(s_wcd.hwndBuffer, EM_SETSEL, 0, -1); + s_total_chars = buf_len; + } + SendMessageA(s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xFFFF); + SendMessageA(s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0); + SendMessageA(s_wcd.hwndBuffer, EM_REPLACESEL, 0, reinterpret_cast(s_wcd.cleanBuffer)); + } + } + + void Sys_Print(const char* msg) + { + Conbuf_AppendText(msg); + } + + LRESULT InputLineWndProc(const HWND hwnd, const UINT umsg, const WPARAM wparam, const LPARAM lparam) + { + char dest[sizeof(s_wcd.consoleText) + 8]; + + switch (umsg) + { + case WM_KILLFOCUS: + if (reinterpret_cast(wparam) == s_wcd.hWnd) + { + SetFocus(hwnd); + return 0; + } + break; + case WM_CHAR: + const auto key = wparam; + + // enter the line + if (key == VK_RETURN) + { + memset(dest, 0, sizeof(dest)); + memset(s_wcd.consoleText, 0, sizeof(s_wcd.consoleText)); + + const auto length = GetWindowTextA(s_wcd.hwndInputLine, s_wcd.consoleText, sizeof(s_wcd.consoleText)); + if (length) + { + sprintf_s(dest, sizeof(dest), "]%s\n", s_wcd.consoleText); + SetWindowTextA(s_wcd.hwndInputLine, ""); + + Sys_Print(dest); + game::Cbuf_AddText(0, s_wcd.consoleText); + } + + return 0; + } + break; + } + + return CallWindowProcA(s_wcd.SysInputLineWndProc, hwnd, umsg, wparam, lparam); + } + + void Sys_CreateConsole(const HINSTANCE hinstance) + { + RECT rect; + WNDCLASSA wndclass; + HDC hdc; + int nheight; + int swidth, sheight; + DWORD DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX; + + const char* class_name = "IW7-Mod WinConsole"; + const char* window_name = "IW7-Mod Console"; + + memset(&rect, 0, sizeof(rect)); + memset(&wndclass, 0, sizeof(rect)); + memset(&hdc, 0, sizeof(hdc)); + + memset(&s_wcd, 0, sizeof(s_wcd)); + + wndclass.style = 0; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.lpfnWndProc = ConWndProc; + wndclass.hInstance = hinstance; + wndclass.hIcon = icon; + wndclass.hbrBackground = bg_brush; + wndclass.hCursor = LoadCursorA(0, IDC_ARROW); + wndclass.lpszMenuName = nullptr; + wndclass.lpszClassName = class_name; + + if (!RegisterClassA(&wndclass)) + { + return; + } + + rect.top = 0; + rect.left = 0; + rect.right = 620; + rect.bottom = 450; + AdjustWindowRect(&rect, DEDSTYLE, 0); + + hdc = GetDC(GetDesktopWindow()); + swidth = GetDeviceCaps(hdc, HORZRES); + sheight = GetDeviceCaps(hdc, VERTRES); + ReleaseDC(GetDesktopWindow(), hdc); + + s_wcd.windowHeight = rect.bottom - rect.top + 1; + s_wcd.windowWidth = rect.right - rect.left + 1; + + // create main window + s_wcd.hWnd = CreateWindowExA( + 0, + class_name, + window_name, + DEDSTYLE, + (swidth - 600) / 2, + (sheight - 450) / 2, + rect.right - rect.left + 1, + rect.bottom - rect.top + 1, + 0, + 0, + hinstance, + nullptr); + + if (!s_wcd.hWnd) + { + return; + } + + // create fonts + hdc = GetDC(s_wcd.hWnd); + nheight = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); + s_wcd.hfBufferFont = CreateFontA( + nheight, + 0, + 0, + 0, + FW_LIGHT, + 0, + 0, + 0, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_MODERN | FIXED_PITCH, + "Courier New"); + ReleaseDC(s_wcd.hWnd, hdc); + + // create logo + if (logo) + { + s_wcd.codLogo = CreateWindowExA( + 0, + "Static", + 0, + WS_CHILDWINDOW | WS_VISIBLE | 0xE, + 5, + 5, + 0, + 0, + s_wcd.hWnd, + reinterpret_cast(1), + hinstance, + nullptr); + SendMessageA(s_wcd.codLogo, 0x172u, 0, reinterpret_cast(logo)); + } + + // create the input line + s_wcd.hwndInputLine = CreateWindowExA( + 0, + "edit", + 0, + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL, + 6, + 426, + 608, + 20, + s_wcd.hWnd, + reinterpret_cast(0x65), + hinstance, + 0); + + // create the scrollbuffer + s_wcd.hwndBuffer = CreateWindowExA( + 0, + "edit", + 0, + WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, + 6, + 70, + 608, + 348, + s_wcd.hWnd, + reinterpret_cast(0x64), + hinstance, + 0); + + SendMessageA(s_wcd.hwndBuffer, WM_SETFONT, reinterpret_cast(s_wcd.hfBufferFont), 0); + SendMessageA(s_wcd.hwndBuffer, EM_LIMITTEXT, sizeof(s_wcd.cleanBuffer), 0); + s_wcd.SysInputLineWndProc = reinterpret_cast(SetWindowLongPtrA(s_wcd.hwndInputLine, -4, + reinterpret_cast(InputLineWndProc))); + SendMessageA(s_wcd.hwndInputLine, WM_SETFONT, reinterpret_cast(s_wcd.hfBufferFont), 0); + SetFocus(s_wcd.hwndInputLine); + SetWindowTextA(s_wcd.hwndBuffer, s_wcd.cleanBuffer); + InitializeCriticalSection(&s_wcd.critSect); + } + + void Sys_ShowConsole() + { + if (!s_wcd.hWnd) + { + Sys_CreateConsole(GetModuleHandleA(reinterpret_cast(s_wcd.hWnd))); + } + if (s_wcd.hWnd) + { + ShowWindow(s_wcd.hWnd, TRUE); + SendMessageA(s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xFFFF); + } + } + + void Sys_DestroyConsole() + { + if (s_wcd.hWnd) + { + ShowWindow(s_wcd.hWnd, SW_HIDE); + CloseWindow(s_wcd.hWnd); + DestroyWindow(s_wcd.hWnd); + s_wcd.hWnd = nullptr; + DeleteCriticalSection(&s_wcd.critSect); + } + } + + class component final : public component_interface + { + public: + component() + { + syscon::bg_brush = CreateSolidBrush(CONSOLE_BACKGROUND_COLOR); + + const utils::nt::library self; + syscon::icon = LoadIconA(self.get_handle(), MAKEINTRESOURCEA(ID_ICON)); + syscon::logo = LoadImageA(self.get_handle(), MAKEINTRESOURCEA(IMAGE_LOGO), 0, 0, 0, LR_COPYFROMRESOURCE); + + (void)_pipe(this->handles_, 1024, _O_TEXT); + (void)_dup2(this->handles_[1], 1); + (void)_dup2(this->handles_[1], 2); + + //setvbuf(stdout, nullptr, _IONBF, 0); + //setvbuf(stderr, nullptr, _IONBF, 0); + } + + ~component() + { + if (syscon::bg_brush) DeleteObject(syscon::bg_brush); + + if (syscon::icon) DestroyIcon(syscon::icon); + if (syscon::logo) DeleteObject(syscon::logo); + } + + void post_start() override + { + this->terminate_runner_ = false; + this->console_runner_ = utils::thread::create_named_thread("Console IO", [this]() + { + this->runner(); + }); + } + + void post_unpack() override + { + this->initialize(); + } + + void pre_destroy() override + { + this->destroy(); + } + + private: + volatile bool console_initialized_ = false; + volatile bool terminate_runner_ = false; + + std::thread console_runner_; + std::thread console_thread_; + + int handles_[2]{}; + + using message_queue = std::queue; + utils::concurrency::container messages; + + void initialize() + { + this->console_thread_ = utils::thread::create_named_thread("Console", [this]() + { + syscon::Sys_ShowConsole(); + + if (!game::environment::is_dedi()) + { + // Hide that shit + ShowWindow(syscon::s_wcd.hWnd, SW_MINIMIZE); + } + + { + messages.access([&](message_queue&) + { + this->console_initialized_ = true; + }); + } + + MSG msg; + while (!this->terminate_runner_) + { + if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + this->log_messages(); + std::this_thread::sleep_for(1ms); + } + } + }); + } + + void destroy() + { + syscon::Sys_DestroyConsole(); + + this->terminate_runner_ = true; + + printf("\r\n"); + _flushall(); + + if (this->console_runner_.joinable()) + { + this->console_runner_.join(); + } + + if (this->console_thread_.joinable()) + { + this->console_thread_.join(); + } + + _close(this->handles_[0]); + _close(this->handles_[1]); + + messages.access([&](message_queue& msgs) + { + msgs = {}; + }); + } + + void log_messages() + { + /*while*/ + if (this->console_initialized_ && !messages.get_raw().empty()) + { + std::queue message_queue_copy; + + { + messages.access([&](message_queue& msgs) + { + message_queue_copy = std::move(msgs); + msgs = {}; + }); + } + + while (!message_queue_copy.empty()) + { + log_message(message_queue_copy.front()); + message_queue_copy.pop(); + } + } + + fflush(stdout); + fflush(stderr); + } + + static void log_message(const std::string& message) + { + //OutputDebugStringA(message.data()); + syscon::Conbuf_AppendText(message.data()); + } + + void runner() + { + char buffer[1024]; + + while (!this->terminate_runner_ && this->handles_[0]) + { + const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); + if (len > 0) + { + console::dispatch_message(console::print_type_info, std::string(buffer, len)); + } + else + { + std::this_thread::sleep_for(1ms); + } + } + + std::this_thread::yield(); + } + }; + + void set_title(const std::string& title) + { + if (syscon::s_wcd.hWnd) + { + SetWindowTextA(syscon::s_wcd.hWnd, title.data()); + } + } +} + +namespace syscon +{ + void init() + { + // register component + static component_loader::installer __component = component_loader::installer("syscon::component"); + } +} \ No newline at end of file diff --git a/src/client/component/console/syscon.hpp b/src/client/component/console/syscon.hpp new file mode 100644 index 00000000..d4898ba1 --- /dev/null +++ b/src/client/component/console/syscon.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace syscon +{ + void init(); + + void set_title(const std::string& title); + + void Sys_Print(const char* msg); +} \ No newline at end of file diff --git a/src/client/component/console/terminal.cpp b/src/client/component/console/terminal.cpp new file mode 100644 index 00000000..35add923 --- /dev/null +++ b/src/client/component/console/terminal.cpp @@ -0,0 +1,422 @@ +#include +#include "terminal.hpp" +#include "loader/component_loader.hpp" + +#include "console.hpp" + +#include "game/game.hpp" + +#include "component/command.hpp" + +#include "version.h" + +#include +#include + +#define OUTPUT_HANDLE GetStdHandle(STD_OUTPUT_HANDLE) + +namespace game_console +{ + void print(int type, const std::string& data); +} + +namespace terminal +{ + namespace + { + utils::hook::detour printf_hook; + std::recursive_mutex print_mutex; + + struct + { + bool kill; + std::thread thread; + HANDLE kill_event; + char buffer[512]{}; + int cursor; + std::int32_t history_index = -1; + std::deque history{}; + } con{}; + + void set_cursor_pos(int x) + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + info.dwCursorPosition.X = static_cast(x); + SetConsoleCursorPosition(OUTPUT_HANDLE, info.dwCursorPosition); + } + + void show_cursor(const bool show) + { + CONSOLE_CURSOR_INFO info{}; + GetConsoleCursorInfo(OUTPUT_HANDLE, &info); + info.bVisible = show; + SetConsoleCursorInfo(OUTPUT_HANDLE, &info); + } + + template + int invoke_printf(const char* fmt, Args&&... args) + { + return printf_hook.invoke(fmt, std::forward(args)...); + } + + std::string format(va_list* ap, const char* message) + { + static thread_local char buffer[0x1000]; + + const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap); + if (count < 0) + { + return {}; + } + + return { buffer, static_cast(count) }; + } + + uint8_t get_attribute(const int type) + { + switch (type) + { + case console::print_type_info: + return 7; // white + case console::print_type_warning: + return 6; // yellow + case console::print_type_error: + return 4; // red + case console::print_type_debug: + return 3; // cyan + } + + return 7; + } + + void update() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + invoke_printf("%s", con.buffer); + set_cursor_pos(con.cursor); + show_cursor(true); + } + + void clear_output() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + + for (auto i = 0; i < std::strlen(con.buffer); i++) + { + invoke_printf(" "); + } + + set_cursor_pos(con.cursor); + show_cursor(true); + } + + void clear() + { + std::lock_guard _0(print_mutex); + + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + + con.cursor = 0; + set_cursor_pos(0); + } + + size_t get_max_input_length() + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + const auto columns = static_cast(info.srWindow.Right - info.srWindow.Left - 1); + return std::max(size_t(0), std::min(columns, sizeof(con.buffer))); + } + + void handle_resize() + { + clear(); + update(); + } + + void handle_input(const INPUT_RECORD record) + { + if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) + { + handle_resize(); + return; + } + + if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown) + { + return; + } + + std::lock_guard _0(print_mutex); + + const auto key = record.Event.KeyEvent.wVirtualKeyCode; + switch (key) + { + case VK_UP: + { + if (++con.history_index >= con.history.size()) + { + con.history_index = static_cast(con.history.size()) - 1; + } + + clear(); + + if (con.history_index != -1) + { + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } + + update(); + break; + } + case VK_DOWN: + { + if (--con.history_index < -1) + { + con.history_index = -1; + } + + clear(); + + if (con.history_index != -1) + { + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } + + update(); + break; + } + case VK_LEFT: + { + if (con.cursor > 0) + { + con.cursor--; + set_cursor_pos(con.cursor); + } + + break; + } + case VK_RIGHT: + { + if (con.cursor < std::strlen(con.buffer)) + { + con.cursor++; + set_cursor_pos(con.cursor); + } + + break; + } + case VK_RETURN: + { + if (con.history_index != -1) + { + const auto itr = con.history.begin() + con.history_index; + + if (*itr == con.buffer) + { + con.history.erase(con.history.begin() + con.history_index); + } + } + + if (con.buffer[0]) + { + con.history.push_front(con.buffer); + } + + if (con.history.size() > 10) + { + con.history.erase(con.history.begin() + 10); + } + + con.history_index = -1; + + game::Cbuf_AddText(0, con.buffer); + + con.cursor = 0; + + clear_output(); + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(7)); // green + invoke_printf("]%s\r\n", con.buffer); + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(console::print_type_info)); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + case VK_BACK: + { + if (con.cursor <= 0) + { + break; + } + + clear_output(); + + std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor, + strlen(con.buffer) + 1 - con.cursor); + con.cursor--; + + update(); + break; + } + case VK_ESCAPE: + { + con.cursor = 0; + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; + } + default: + { + const auto c = record.Event.KeyEvent.uChar.AsciiChar; + if (!c) + { + break; + } + + if (std::strlen(con.buffer) + 1 >= get_max_input_length()) + { + break; + } + + std::memmove(con.buffer + con.cursor + 1, + con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor); + con.buffer[con.cursor] = c; + con.cursor++; + + update(); + break; + } + } + } + + int __cdecl printf_stub(const char* fmt, ...) + { + va_list ap; + va_start(ap, fmt); + const auto result = format(&ap, fmt); + va_end(ap); + + return terminal::dispatch_message(console::print_type_info, result); + } + } + + int dispatch_message(const int type, const std::string& message) + { + std::lock_guard _0(print_mutex); + + clear_output(); + set_cursor_pos(0); + + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(type)); + const auto res = invoke_printf("%s", message.data()); + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(console::print_type_info)); + + game_console::print(type, message); + + if (message.size() <= 0 || message[message.size() - 1] != '\n') + { + invoke_printf("\n"); + } + + update(); + return res; + } + + class component final : public component_interface + { + public: + component() + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + } + + void post_unpack() override + { + printf_hook.create(printf, printf_stub); + + ShowWindow(GetConsoleWindow(), SW_SHOW); + SetConsoleTitle("IW7-Mod: " VERSION); + + con.kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + con.thread = utils::thread::create_named_thread("Console", []() + { + const auto handle = GetStdHandle(STD_INPUT_HANDLE); + HANDLE handles[2] = { handle, con.kill_event }; + MSG msg{}; + + INPUT_RECORD record{}; + DWORD num_events{}; + + while (!con.kill) + { + const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT); + if (con.kill) + { + return; + } + + switch (result) + { + case WAIT_OBJECT_0: + { + if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0) + { + break; + } + + handle_input(record); + break; + } + case WAIT_OBJECT_0 + 1: + { + if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE)) + { + break; + } + + if (msg.message == WM_QUIT) + { + command::execute("quit", false); + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + } + }); + } + + void pre_destroy() override + { + con.kill = true; + SetEvent(con.kill_event); + + if (con.thread.joinable()) + { + con.thread.join(); + } + } + }; +} + +namespace terminal +{ + void init() + { + // register component + static component_loader::installer __component = component_loader::installer("terminal::component"); + } +} \ No newline at end of file diff --git a/src/client/component/console/terminal.hpp b/src/client/component/console/terminal.hpp new file mode 100644 index 00000000..76cf7ab6 --- /dev/null +++ b/src/client/component/console/terminal.hpp @@ -0,0 +1,8 @@ +#pragma once + +namespace terminal +{ + void init(); + + int dispatch_message(const int type, const std::string& message); +} \ No newline at end of file diff --git a/src/client/component/console/wincon.cpp b/src/client/component/console/wincon.cpp new file mode 100644 index 00000000..ab6ec589 --- /dev/null +++ b/src/client/component/console/wincon.cpp @@ -0,0 +1,65 @@ +#include +#include "wincon.hpp" +#include "loader/component_loader.hpp" + +#include "console.hpp" + +#include "game/game.hpp" + +#include + +namespace wincon +{ + static std::atomic_bool ingame = false; + static std::atomic_bool exit = false; + + DWORD WINAPI console(LPVOID) + { + ShowWindow(GetConsoleWindow(), SW_SHOW); + SetConsoleTitleA("IW7-Mod Console"); + + std::string cmd; + exit = false; + + while (!exit) + { + std::getline(std::cin, cmd); + if (ingame) + { + game::Cbuf_AddText(0, cmd.data()); + } + } + + return 0; + } + + class component final : public component_interface + { + public: + void post_start() override + { + const auto handle = CreateThread(0, 0, wincon::console, 0, 0, 0); + utils::thread::set_name(handle, "Console"); + } + + void post_unpack() override + { + wincon::ingame = true; + } + + void pre_destroy() override + { + wincon::ingame = false; + wincon::exit = true; + } + }; +} + +namespace wincon +{ + void init() + { + // register component + static component_loader::installer __component = component_loader::installer("wincon::component"); + } +} \ No newline at end of file diff --git a/src/client/component/console/wincon.hpp b/src/client/component/console/wincon.hpp new file mode 100644 index 00000000..875549c7 --- /dev/null +++ b/src/client/component/console/wincon.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace wincon +{ + void init(); +} \ No newline at end of file diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index 40b15b33..19ec0f82 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -6,7 +6,7 @@ #include "dvars.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "scheduler.hpp" #include diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index ef78b49e..19a9e071 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -5,7 +5,7 @@ #include "game/game.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include #include diff --git a/src/client/component/game_console.cpp b/src/client/component/game_console.cpp index 3a1fc530..48489dda 100644 --- a/src/client/component/game_console.cpp +++ b/src/client/component/game_console.cpp @@ -2,7 +2,7 @@ #include "loader/component_loader.hpp" #include "game_console.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "scheduler.hpp" #include "game/game.hpp" @@ -471,7 +471,7 @@ namespace game_console const auto lines = utils::string::split(data, '\n'); for (const auto& line : lines) { - print_internal(type == console::con_type_info ? line : "^"s.append(std::to_string(type)).append(line)); + print_internal(type == console::print_type_info ? line : "^"s.append(std::to_string(type)).append(line)); } } diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index f0a1b5ae..e127c244 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -3,7 +3,7 @@ #include "game/game.hpp" -#include "console.hpp" +#include "console/console.hpp" #include diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp index e63c314a..855885f9 100644 --- a/src/client/component/lui.cpp +++ b/src/client/component/lui.cpp @@ -5,7 +5,7 @@ #include "game/dvars.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include #include diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index ffa957ff..f9aa1b4a 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -5,7 +5,7 @@ #include "game/game.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "dvars.hpp" #include diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 7f1a8106..409a36f6 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -8,7 +8,7 @@ #include "game/game.hpp" #include "command.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "network.hpp" #include "scheduler.hpp" diff --git a/src/client/component/stats.cpp b/src/client/component/stats.cpp index c78e0a89..b43931af 100644 --- a/src/client/component/stats.cpp +++ b/src/client/component/stats.cpp @@ -4,7 +4,7 @@ #include "game/game.hpp" #include "game/dvars.hpp" -#include "console.hpp" +#include "console/console.hpp" #include "command.hpp" #include "scheduler.hpp" diff --git a/src/client/main.cpp b/src/client/main.cpp index 79a24608..02d65ee2 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -3,6 +3,8 @@ #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "component/console/console.hpp" + #include #include @@ -106,6 +108,7 @@ void limit_parallel_dll_loading() int main() { ShowWindow(GetConsoleWindow(), SW_HIDE); + console::init(); FARPROC entry_point; enable_dpi_awareness();