feature: binds
This commit is contained in:
parent
6158430274
commit
847b6090b2
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
147
src/module/binding.cpp
Normal 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)
|
Loading…
Reference in New Issue
Block a user