diff --git a/src/client/component/colors.cpp b/src/client/component/colors.cpp new file mode 100644 index 00000000..8ecbfbf1 --- /dev/null +++ b/src/client/component/colors.cpp @@ -0,0 +1,183 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include +#include + +namespace colors +{ + struct hsv_color + { + unsigned char h; + unsigned char s; + unsigned char v; + }; + + namespace + { + std::vector color_table; + + DWORD hsv_to_rgb(const hsv_color hsv) + { + DWORD rgb; + + if (hsv.s == 0) + { + return RGB(hsv.v, hsv.v, hsv.v); + } + + // converting to 16 bit to prevent overflow + const unsigned int h = hsv.h; + const unsigned int s = hsv.s; + const unsigned int v = hsv.v; + + const auto region = static_cast(h / 43); + const auto remainder = (h - (region * 43)) * 6; + + const auto p = static_cast((v * (255 - s)) >> 8); + const auto q = static_cast( + (v * (255 - ((s * remainder) >> 8))) >> 8); + const auto t = static_cast( + (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8); + + switch (region) + { + case 0: + rgb = RGB(v, t, p); + break; + case 1: + rgb = RGB(q, v, p); + break; + case 2: + rgb = RGB(p, v, t); + break; + case 3: + rgb = RGB(p, q, v); + break; + case 4: + rgb = RGB(t, p, v); + break; + default: + rgb = RGB(v, p, q); + break; + } + + return rgb; + } + + int color_index(const char c) + { + const auto index = c - 48; + return index >= 0xC ? 7 : index; + } + + char add(const uint8_t r, const uint8_t g, const uint8_t b) + { + const char index = '0' + static_cast(color_table.size()); + color_table.push_back(RGB(r, g, b)); + return index; + } + + void com_clean_name_stub(const char* in, char* out, const int out_size) + { + strncpy_s(out, out_size, in, _TRUNCATE); + } + + char* i_clean_str_stub(char* string) + { + utils::string::strip(string, string, static_cast(strlen(string)) + 1); + + return string; + } + + size_t get_client_name_stub(const int local_client_num, const int index, char* buf, const int size, + const size_t unk, const size_t unk2) + { + // CL_GetClientName (CL_GetClientNameAndClantag?) + const auto result = reinterpret_cast(0x14025BAA0)( // H1 (1.4) + local_client_num, index, buf, size, unk, unk2); + + utils::string::strip(buf, buf, size); + + return result; + } + + void rb_lookup_color_stub(const char index, DWORD* color) + { + *color = RGB(255, 255, 255); + + if (index == '8') + { + *color = *reinterpret_cast(SELECT_VALUE(0x14F142FF8, 0x14FE70634)); // H1(1.4) + } + else if (index == '9') + { + *color = *reinterpret_cast(SELECT_VALUE(0x14F142FFC, 0x14FE70638)); // H1(1.4) + } + else if (index == ':') + { + *color = hsv_to_rgb({ static_cast((game::Sys_Milliseconds() / 100) % 256), 255, 255 }); + } + else if (index == ';') + { + *color = *reinterpret_cast(SELECT_VALUE(0x14F143004, 0x14FE70640)); // H1(1.4) + } + else if (index == '<') + { + *color = 0xFFFCFF80; + } + else + { + *color = color_table[color_index(index)]; + } + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + if (game::environment::is_dedi()) + { + return; + } + + if (!game::environment::is_sp()) + { + // allows colored name in-game + utils::hook::jump(0x140503810, com_clean_name_stub); // H1(1.4) + + // don't apply colors to overhead names + utils::hook::call(0x1400AB416, get_client_name_stub); // H1(1.4) + + // patch I_CleanStr + utils::hook::jump(0x140503D00, i_clean_str_stub); // H1(1.4) + } + + // force new colors + utils::hook::jump(SELECT_VALUE(0x140524BD0, 0x1406206A0), rb_lookup_color_stub); // H1(1.4) + + // add colors + add(0, 0, 0); // 0 - Black + add(255, 49, 49); // 1 - Red + add(134, 192, 0); // 2 - Green + add(255, 173, 34); // 3 - Yellow + add(0, 135, 193); // 4 - Blue + add(32, 197, 255); // 5 - Light Blue + add(151, 80, 221); // 6 - Pink + add(255, 255, 255); // 7 - White + + add(0, 0, 0); // 8 - Team color (axis?) + add(0, 0, 0); // 9 - Team color (allies?) + + add(0, 0, 0); // 10 - Rainbow (:) + add(0, 0, 0); + // 11 - Server color (;) - using that color in infostrings (e.g. your name) fails, ';' is an illegal character! + } + }; +} + +REGISTER_COMPONENT(colors::component) \ No newline at end of file diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 0c9ea785..971f551b 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -267,7 +267,7 @@ namespace command static void add_commands_generic() { add("quit", game::Com_Quit_f); - add("quit_hard", utils::nt::raise_hard_exception); + //add("quit_hard", utils::nt::raise_hard_exception); /* this command delivers you to a windows blue screen, its quit hard from windows xD */ add("crash", []() { *reinterpret_cast(1) = 0; @@ -294,7 +294,7 @@ namespace command } console::info("Total %i matches\n", matches.size()); - }); + });*/ add("dvarDump", [](const params& argument) { @@ -302,7 +302,7 @@ namespace command std::string filename; if (argument.size() == 2) { - filename = "s1x/"; + filename = "h1-mod/"; filename.append(argument[1]); if (!filename.ends_with(".txt")) { @@ -317,17 +317,17 @@ namespace command if (!filename.empty()) { const auto line = std::format("{} \"{}\"\r\n", dvar->hash, - game::Dvar_ValueToString(dvar, dvar->current, 0)); + game::Dvar_ValueToString(dvar, dvar->current)); utils::io::write_file(filename, line, i != 0); } console::info("%s \"%s\"\n", dvar->hash, - game::Dvar_ValueToString(dvar, dvar->current, 0)); + game::Dvar_ValueToString(dvar, dvar->current)); } } console::info("\n%i dvars\n", *game::dvarCount); console::info("================================ END DVAR DUMP ====================================\n"); }); - + add("commandDump", [](const params& argument) { console::info("================================ COMMAND DUMP =====================================\n"); @@ -335,7 +335,7 @@ namespace command std::string filename; if (argument.size() == 2) { - filename = "s1x/"; + filename = "h1-mod/"; filename.append(argument[1]); if (!filename.ends_with(".txt")) { @@ -360,7 +360,7 @@ namespace command console::info("\n%i commands\n", i); console::info("================================ END COMMAND DUMP =================================\n"); }); - + /* add("listassetpool", [](const params& params) { if (params.size() < 2) @@ -446,7 +446,7 @@ namespace command : "^1off")); }); - /*add("demigod", []() + add("demigod", []() { if (!game::SV_Loaded()) { @@ -502,7 +502,7 @@ namespace command : "^1off")); }); - add("give", [](const params& params) + /*add("give", [](const params& params) { if (!game::SV_Loaded()) { diff --git a/src/client/component/fps.cpp b/src/client/component/fps.cpp index dd56f071..652ce7ee 100644 --- a/src/client/component/fps.cpp +++ b/src/client/component/fps.cpp @@ -156,7 +156,7 @@ namespace fps // fps setup cg_perf.perf_start = std::chrono::high_resolution_clock::now(); - utils::hook::call(SELECT_VALUE(0x14018D261, 0x140213B27), &perf_update); //h1sp + utils::hook::call(SELECT_VALUE(0x14018D261, 0x14025B747), &perf_update); // H1(1.4) // change cg_drawfps flags to saved utils::hook::call(SELECT_VALUE(0x140139F48, 0x1401A4B8E), &cg_draw_fps_register_stub); //h1sp diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp new file mode 100644 index 00000000..c1284942 --- /dev/null +++ b/src/client/component/lui.cpp @@ -0,0 +1,60 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include "command.hpp" +#include "console.hpp" + +#include + +namespace lui +{ + class component final : public component_interface + { + public: + void post_unpack() override + { + if (!game::environment::is_mp()) return; + + // Don't show create cod account popup + //utils::hook::set(0x14017C957, 0); // H1(1.4) + +//#ifdef _DEBUG + // Enable development menus (causes issues in sp) + //utils::hook::set(SELECT_VALUE(0x1400B4ABC, 0x1401AB779), 1); +//#endif + + command::add("lui_open", [](const command::params& params) + { + if (params.size() <= 1) + { + console::info("usage: lui_open \n"); + return; + } + + game::LUI_OpenMenu(0, params[1], 0, 0, 0); + }); + + command::add("lui_open_popup", [](const command::params& params) + { + if (params.size() <= 1) + { + console::info("usage: lui_open_popup \n"); + return; + } + + game::LUI_OpenMenu(0, params[1], 1, 0, 0); + }); + + command::add("runMenuScript", [](const command::params& params) + { + const auto args_str = params.join(1); + const auto* args = args_str.data(); + game::UI_RunMenuScript(0, &args); + }); + } + }; +} + +REGISTER_COMPONENT(lui::component) \ No newline at end of file diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 790bb125..5543db08 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -3,13 +3,11 @@ #include "command.hpp" #include "network.hpp" -#include "game_console.hpp" -#include "../game/game.hpp" +#include "console.hpp" +#include "game/dvars.hpp" #include #include -#include -#include namespace network { @@ -56,11 +54,11 @@ namespace network // Command handled a.popad64(); a.mov(al, 1); - a.jmp(0x140252AF8); //H1MP64(1.4) + a.jmp(0x140252AF8); // H1MP64(1.4) a.bind(return_unhandled); a.popad64(); - a.jmp(0x14025234C); //H1MP64(1.4) + a.jmp(0x14025234C); // H1MP64(1.4) } int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2) @@ -105,11 +103,11 @@ namespace network get_callbacks()[utils::string::to_lower(command)] = callback; } - void dw_send_to_stub(const unsigned int size, const char* src, game::netadr_s* a3) + int dw_send_to_stub(const int size, const char* src, game::netadr_s* a3) { sockaddr s = {}; - game::NetadrToSockadr(a3, &s); //0x1404F62F0 - sendto(*game::query_socket, src, size - 2, 0, &s, 16); + game::NetadrToSockadr(a3, &s); + return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0; } void send(const game::netadr_s& address, const std::string& command, const std::string& data, const char separator) @@ -122,16 +120,23 @@ namespace network send_data(address, packet); } - void send_data(const game::netadr_s& address, const std::string& data) { + auto size = static_cast(data.size()); if (address.type == game::NA_LOOPBACK) { - game::NET_SendLoopPacket(game::NS_CLIENT1, static_cast(data.size()), data.data(), &address); + // TODO: Fix this for loopback + if (size > 1280) + { + console::error("Packet was too long. Truncated!\n"); + size = 1280; + } + + game::NET_SendLoopPacket(game::NS_CLIENT1, size, data.data(), &address); } else { - game::Sys_SendPacket(static_cast(data.size()), data.data(), &address); + game::Sys_SendPacket(size, data.data(), &address); } } @@ -168,7 +173,8 @@ namespace network game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags, const char* description) { - auto dvar = dvars::register_int("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port"); + game::dvar_t* dvar; + dvar = dvars::register_int("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED); // read net_port from command line command::read_startup_variable("net_port"); @@ -190,7 +196,7 @@ namespace network // redirect dw_sendto to raw socket //utils::hook::jump(0x1404D850A, reinterpret_cast(0x1404D849A)); utils::hook::call(0x140513467, dw_send_to_stub); // H1MP64(1.4) - utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub); + utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub); // H1MP64(1.4) // intercept command handling utils::hook::jump(0x140252327, utils::hook::assemble(handle_command_stub), true); // H1MP64(1.4) @@ -213,64 +219,66 @@ namespace network // disable xuid verification utils::hook::set(0x14005B62D, 0xEB); // H1MP64(1.4) - utils::hook::set(0x14005B649, 0xEB); // H1MP64(1.4) NOT_SURE SHOULD JZ BUT LEA + utils::hook::set(0x14005B649, 0xEB); // H1MP64(1.4) // disable xuid verification - utils::hook::nop(0x14048382C, 2); // H1MP64(1.4) - utils::hook::set(0x140483889, 0xEB); // H1MP64(1.4) NOT_SURE + utils::hook::nop(0x14048382C, 2); + utils::hook::set(0x140483889, 0xEB); // H1MP64(1.4) // ignore configstring mismatch utils::hook::set(0x1402591C9, 0xEB); // H1MP64(1.4) // ignore dw handle in SV_PacketEvent - utils::hook::set(0x1404898E2, 0xEB); // H1MP64(1.4) + utils::hook::set(0x1404898E2, 0xEB); utils::hook::call(0x1404898D6, &net_compare_address); // H1MP64(1.4) // ignore dw handle in SV_FindClientByAddress - utils::hook::set(0x140488EFD, 0xEB); // H1MP64(1.4) + utils::hook::set(0x140488EFD, 0xEB); utils::hook::call(0x140488EF1, &net_compare_address); // H1MP64(1.4) // ignore dw handle in SV_DirectConnect utils::hook::set(0x140480C58, 0xEB); // H1MP64(1.4) - utils::hook::set(0x140480CF2, 0xEB); // H1MP64(1.4) NOT_SURE + utils::hook::set(0x140480CF2, 0xEB); // H1MP64(1.4) utils::hook::call(0x140480C4B, &net_compare_address); // H1MP64(1.4) utils::hook::call(0x140480E62, &net_compare_address); // H1MP64(1.4) // increase cl_maxpackets - //dvars::override::Dvar_RegisterInt("cl_maxpackets", 1000, 1, 1000, 0x1); - dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, 0x1, true); + dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED); + + // increase snaps + dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE); // ignore impure client utils::hook::jump(0x140481B58, reinterpret_cast(0x140481BEE)); // H1MP64(1.4) // don't send checksum utils::hook::set(0x140513433, 0); // H1MP64(1.4) mov: r8d, edi ; LEN + utils::hook::set(0x14051345A, 0); // H1MP64(1.4) // don't read checksum - utils::hook::jump(0x140513389, 0x14051339F); // H1MP64(1.4) + utils::hook::jump(0x140513389, 0x14051339F); // don't try to reconnect client - utils::hook::call(0x140480DFF, reconnect_migratated_client); // H1MP64(1.4) + utils::hook::call(0x140480DFF, reconnect_migratated_client); utils::hook::nop(0x140480DDB, 4); // H1MP64(1.4) this crashes when reconnecting for some reason // allow server owner to modify net_port before the socket bind utils::hook::call(0x140512BE5, register_netport_stub); // H1MP64(1.4) utils::hook::call(0x140512D20, register_netport_stub); // H1MP64(1.4) + // increase allowed packet size + const auto max_packet_size = 0x20000; + utils::hook::set(0x1404255F0, max_packet_size); // H1MP64(1.4) + utils::hook::set(0x14042562E, max_packet_size); // H1MP64(1.4) + utils::hook::set(0x140425521, max_packet_size); // H1MP64(1.4) + utils::hook::set(0x140425549, max_packet_size); // H1MP64(1.4) + // ignore built in "print" oob command and add in our own utils::hook::set(0x14025280E, 0xEB); // H1MP64(1.4) - on("print", [](const game::netadr_s& addr, const std::string_view& data) + on("print", [](const game::netadr_s&, const std::string_view& data) { const std::string message{ data }; - - if (game::environment::is_dedi()) - { - printf("%s\n", message.data()); - } - else - { - console::info("%s\n", message.data()); //test - } + console::info(message.data()); }); } } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index d1c7a01d..db1ad670 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -148,7 +148,8 @@ namespace game WEAK symbol Image_Setup{ 0, 0x14074B2A0 }; - WEAK symbol LUI_OpenMenu{ 0, 0x1405F0EE0 }; + WEAK symbol LUI_OpenMenu{ 0, 0x14048E450 }; // + WEAK symbol Menu_IsMenuOpenAndVisible{ 0, 0x1405EE1A0 }; WEAK symbol Scr_AllocVector{ 0, 0x1405C3220 }; @@ -178,6 +179,8 @@ namespace game WEAK symbol UI_SafeTranslateString{ 0x140350430, 0x1405A2930 }; // H1(1.4) + WEAK symbol UI_RunMenuScript{ 0, 0x1404CFE60 }; // H1(1.4) + WEAK symbol longjmp{ 0x140648FD4, 0x14089EED0 }; // H1(1.4) WEAK symbol _setjmp{ 0x1406BFDD0, 0x1408EC2E0 }; // H1(1.4) @@ -192,8 +195,8 @@ namespace game WEAK symbol gfxDrawMethod{ 0,0x14EDF9E00 }; - WEAK symbol dvarCount{ 0, 0x14BFBB310 }; - WEAK symbol sortedDvars{ 0,0x14BFBB320 }; + WEAK symbol dvarCount{ 0, 0x14D064CF4 }; //h1mp + WEAK symbol sortedDvars{ 0,0x14D064D00 }; //h1mp WEAK symbol levelEntityId{ 0,0x14B5E0B30 }; WEAK symbol g_script_error_level{ 0,0x14BA9CC24 };