feature: binds

This commit is contained in:
FutureRave 2023-02-10 16:45:11 +00:00
parent 6158430274
commit 847b6090b2
No known key found for this signature in database
GPG Key ID: 22F9079C86CFAB31
4 changed files with 198 additions and 1 deletions

View File

@ -118,6 +118,8 @@ namespace game
Win_LocalizeRef_t Win_LocalizeRef;
Key_KeynumToString_t Key_KeynumToString;
decltype(longjmp)* _longjmp;
CmdArgs* sv_cmd_args;
@ -174,6 +176,8 @@ namespace game
const char** g_assetNames;
const char** command_whitelist;
int Vec4Compare(const float* a, const float* b)
{
return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
@ -842,6 +846,8 @@ namespace game
native::Win_LocalizeRef = native::Win_LocalizeRef_t(SELECT_VALUE(0x49D7E0, 0x5CBE90));
native::Key_KeynumToString = native::Key_KeynumToString_t(SELECT_VALUE(0x4BB000, 0x48C080));
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC));
native::sv_cmd_args = reinterpret_cast<native::CmdArgs*>(SELECT_VALUE(0x1757218, 0x1CAA998));
@ -903,5 +909,7 @@ namespace game
native::db_hashCritSect = reinterpret_cast<native::FastCriticalSection*>(SELECT_VALUE(0xFA9E7C, 0x18596E4));
native::g_assetNames = reinterpret_cast<const char**>(SELECT_VALUE(0x92A688, 0x8AAB30));
native::command_whitelist = reinterpret_cast<const char**>(SELECT_VALUE(0x929FA0, 0x8AA3B8));
}
}

View File

@ -247,7 +247,10 @@ namespace game
extern LargeLocalResetToMark_t LargeLocalResetToMark;
typedef const char* (*Win_LocalizeRef_t)(const char* ref);
extern Win_LocalizeRef_t Win_LocalizeRer;
extern Win_LocalizeRef_t Win_LocalizeRef;
typedef const char* (*Key_KeynumToString_t)(int keynum, int translate);
extern Key_KeynumToString_t Key_KeynumToString;
extern decltype(longjmp)* _longjmp;
@ -308,6 +311,8 @@ namespace game
extern const char** g_assetNames;
extern const char** command_whitelist;
// Global Definitions & Functions
constexpr auto JUMP_LAND_SLOWDOWN_TIME = 1800;

View File

@ -1399,6 +1399,43 @@ namespace game
static_assert(sizeof(fileHandleData_t) == 0x11C);
struct field_t
{
int cursor;
int scroll;
int drawWidth;
int widthInPixels;
float charHeight;
int fixedSize;
char buffer[256];
};
enum LocSelInputState
{
LOC_SEL_INPUT_NONE = 0x0,
LOC_SEL_INPUT_CONFIRM = 0x1,
LOC_SEL_INPUT_CANCEL = 0x2,
};
struct KeyState
{
int down;
int repeats;
int binding;
};
struct PlayerKeyState
{
field_t chatField;
int chat_team;
int overstrikeMode;
int anyKeyDown;
KeyState keys[256];
LocSelInputState locSelInputState;
};
static_assert(sizeof(PlayerKeyState) == 0xD28);
namespace mp
{
enum ConfigString

147
src/module/binding.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace
{
std::vector<std::string> custom_binds;
utils::hook::detour key_get_binding_for_cmd_hook;
utils::hook::detour cl_execute_key_hook;
game::native::KeyState* keys;
int key_write_bindings_to_buffer([[maybe_unused]] int local_client_num, char* buffer, int buffer_size)
{
buffer_size = buffer_size - 4;
auto bytes_used = 0;
for (auto keyIndex = 0; keyIndex < 256; ++keyIndex)
{
if (keys[keyIndex].binding && keys[keyIndex].binding < 91)
{
auto len = sprintf_s(&buffer[bytes_used], buffer_size - bytes_used, "bind %s \"%s\"\n",
game::native::Key_KeynumToString(keyIndex, false), game::native::command_whitelist[keys[keyIndex].binding]);
if (len < 0)
{
return bytes_used;
}
bytes_used += len;
}
else if (keys[keyIndex].binding >= 91)
{
auto value = keys[keyIndex].binding - 91;
if (static_cast<std::size_t>(value) < custom_binds.size() && !custom_binds[value].empty())
{
auto len = sprintf_s(&buffer[bytes_used], buffer_size - bytes_used, "bind %s \"%s\"\n",
game::native::Key_KeynumToString(keyIndex, false), custom_binds[value].data());
if (len < 0)
{
return bytes_used;
}
bytes_used += len;
}
}
}
buffer[bytes_used] = '\0';
return bytes_used;
}
__declspec(naked) void key_write_bindings_to_buffer_stub()
{
__asm
{
pushad
push [esp + 0x20 + 0x8] // bufferSize
push [esp + 0x20 + 0x8] // buffer
push eax // localClientNum
call key_write_bindings_to_buffer
add esp, 0xC
popad
ret
}
}
int get_binding_for_custom_command(const char* command)
{
auto index = 0;
for (const auto& bind : custom_binds)
{
if (bind == command)
{
return index;
}
++index;
}
custom_binds.emplace_back(command);
index = static_cast<int>(custom_binds.size()) - 1;
return index;
}
int key_get_binding_for_cmd_stub(const char* command)
{
for (auto i = 0; i <= 91; i++)
{
if (game::native::command_whitelist[i] && !std::strcmp(command, game::native::command_whitelist[i]))
{
return i;
}
}
return 91 + get_binding_for_custom_command(command);
}
void cl_execute_key_stub(game::native::LocalClientNum_t local_client_num, int key, int down)
{
if (key >= 91)
{
key -= 91;
if (static_cast<std::uint32_t>(key) < custom_binds.size() && !custom_binds[key].empty())
{
game::native::Cbuf_AddText(local_client_num, utils::string::va("%s\n", custom_binds[key].data()));
}
return;
}
cl_execute_key_hook.invoke<void>(local_client_num, key, down);
}
}
class binding final : public module
{
public:
void post_load() override
{
if (game::is_sp())
{
return;
}
keys = reinterpret_cast<game::native::KeyState*>(0xB3C760);
utils::hook(0x48C53A, key_write_bindings_to_buffer_stub, HOOK_CALL).install()->quick();
key_get_binding_for_cmd_hook.create(0x48C1C0, &key_get_binding_for_cmd_stub);
cl_execute_key_hook.create(0x48AF00, cl_execute_key_stub);
}
};
REGISTER_MODULE(binding)