diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-30 03:32:59 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-30 03:32:59 +0200 |
| commit | aa096fe6886681d3338573cd3af779b57a5b56ab (patch) | |
| tree | 14aee66fa1c3c168c140f02006aec246f9bbc23c /src/cinput.cpp | |
| download | hl2h-aa096fe6886681d3338573cd3af779b57a5b56ab.tar.gz hl2h-aa096fe6886681d3338573cd3af779b57a5b56ab.zip | |
init
Diffstat (limited to 'src/cinput.cpp')
| -rw-r--r-- | src/cinput.cpp | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/cinput.cpp b/src/cinput.cpp new file mode 100644 index 0000000..7c40b3b --- /dev/null +++ b/src/cinput.cpp | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | // cinput.cpp — CInput discovery, usercmd CRC, get/fix helpers | ||
| 2 | #include "hl2h.h" | ||
| 3 | |||
| 4 | // --------------------------------------------------------------------------- | ||
| 5 | // CRC-32 (standard IEEE reflected, matches Valve's implementation) | ||
| 6 | // --------------------------------------------------------------------------- | ||
| 7 | |||
| 8 | static uint32_t g_crc_table[256]; | ||
| 9 | static bool g_crc_inited = false; | ||
| 10 | |||
| 11 | static void crc32_init_table() { | ||
| 12 | if (g_crc_inited) | ||
| 13 | return; | ||
| 14 | for (uint32_t i = 0; i < 256; i++) { | ||
| 15 | uint32_t c = i; | ||
| 16 | for (int k = 0; k < 8; k++) | ||
| 17 | c = (c & 1) ? 0xEDB88320u ^ (c >> 1) : c >> 1; | ||
| 18 | g_crc_table[i] = c; | ||
| 19 | } | ||
| 20 | g_crc_inited = true; | ||
| 21 | } | ||
| 22 | |||
| 23 | // field set verified from client.so GetChecksum disassembly | ||
| 24 | uint32_t usercmd_checksum(const CUserCmd *c) { | ||
| 25 | crc32_init_table(); | ||
| 26 | uint32_t crc = 0xFFFFFFFFu; | ||
| 27 | auto upd = [&crc](const void *p, size_t n) { | ||
| 28 | const uint8_t *b = (const uint8_t *)p; | ||
| 29 | while (n--) | ||
| 30 | crc = g_crc_table[(crc ^ *b++) & 0xFF] ^ (crc >> 8); | ||
| 31 | }; | ||
| 32 | upd(&c->command_number, 4); | ||
| 33 | upd(&c->tick_count, 4); | ||
| 34 | upd(&c->viewangles, 12); | ||
| 35 | upd(&c->forwardmove, 4); | ||
| 36 | upd(&c->sidemove, 4); | ||
| 37 | upd(&c->upmove, 4); | ||
| 38 | upd(&c->buttons, 4); | ||
| 39 | upd(&c->impulse, 1); | ||
| 40 | upd(&c->weaponselect, 4); | ||
| 41 | upd(&c->weaponsubtype, 4); | ||
| 42 | upd(&c->random_seed, 4); | ||
| 43 | upd(&c->mousedx, 2); | ||
| 44 | upd(&c->mousedy, 2); | ||
| 45 | return ~crc; | ||
| 46 | } | ||
| 47 | |||
| 48 | // CInput: +0xE4 = m_pCommands, +0xE8 = m_pVerifiedCommands | ||
| 49 | static constexpr size_t CMDOFF_M_PCOMMANDS = 0xE4; | ||
| 50 | static constexpr size_t CMDOFF_M_PVERIFIED = 0xE8; | ||
| 51 | static constexpr size_t VERIFIED_STRIDE = 0x58; | ||
| 52 | static constexpr size_t VERIFIED_CRC_OFF = 0x54; | ||
| 53 | |||
| 54 | CUserCmd *get_usercmd(int seq) { | ||
| 55 | if (!g_input) | ||
| 56 | return nullptr; | ||
| 57 | uintptr_t cmds = *(uintptr_t *)(g_input + CMDOFF_M_PCOMMANDS); | ||
| 58 | if (!cmds) | ||
| 59 | return nullptr; | ||
| 60 | auto *cmd = (CUserCmd *)(cmds + (size_t)(seq % 90) * 0x54); | ||
| 61 | if (cmd->command_number != seq) | ||
| 62 | return nullptr; | ||
| 63 | return cmd; | ||
| 64 | } | ||
| 65 | |||
| 66 | void fix_verified_crc(int seq, uint32_t crc) { | ||
| 67 | if (!g_input) | ||
| 68 | return; | ||
| 69 | uintptr_t verified = *(uintptr_t *)(g_input + CMDOFF_M_PVERIFIED); | ||
| 70 | if (verified) | ||
| 71 | *(uint32_t *)(verified + (size_t)(seq % 90) * VERIFIED_STRIDE + | ||
| 72 | VERIFIED_CRC_OFF) = crc; | ||
| 73 | } | ||
| 74 | |||
| 75 | // --------------------------------------------------------------------------- | ||
| 76 | // CInput discovery: byte-scan of CHLClient::CreateMove prologue | ||
| 77 | // --------------------------------------------------------------------------- | ||
| 78 | |||
| 79 | bool find_input_global() { | ||
| 80 | void **vt = VTABLE(g_iface.client); | ||
| 81 | uintptr_t cm = (uintptr_t)vt[21]; // CreateMove slot | ||
| 82 | if (!g_client.contains(cm)) { | ||
| 83 | logf("CreateMove %p outside client module", (void *)cm); | ||
| 84 | return false; | ||
| 85 | } | ||
| 86 | const uint8_t *b = (const uint8_t *)cm; | ||
| 87 | |||
| 88 | // 1) PIC anchor: call __x86.get_pc_thunk + add $imm,%esi | ||
| 89 | uintptr_t anchor = 0; | ||
| 90 | for (int i = 0; i <= 0x100 - 11; i++) { | ||
| 91 | if (b[i] == 0xE8 && b[i + 5] == 0x81 && b[i + 6] == 0xC6) { | ||
| 92 | uint32_t imm; | ||
| 93 | memcpy(&imm, b + i + 7, 4); | ||
| 94 | anchor = cm + (uintptr_t)(i + 5) + imm; | ||
| 95 | break; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | if (!anchor || !g_client.contains(anchor)) { | ||
| 99 | logf("PIC anchor not found in CreateMove"); | ||
| 100 | return false; | ||
| 101 | } | ||
| 102 | |||
| 103 | // 2) the input->CreateMove call: "ff 51 0c" preceded by "52", scan back | ||
| 104 | // for "8d 86 imm32" with "8b 10" between | ||
| 105 | for (int c = 3; c <= 0x100 - 3; c++) { | ||
| 106 | if (!(b[c] == 0xFF && b[c + 1] == 0x51 && b[c + 2] == 0x0C && | ||
| 107 | b[c - 1] == 0x52)) | ||
| 108 | continue; | ||
| 109 | for (int i = c - 8; i > c - 44 && i > 2; i--) { | ||
| 110 | if (b[i] != 0x8D || b[i + 1] != 0x86) | ||
| 111 | continue; | ||
| 112 | bool has_mov = false; | ||
| 113 | for (int j = i + 6; j < c - 1; j++) | ||
| 114 | if (b[j] == 0x8B && b[j + 1] == 0x10) { | ||
| 115 | has_mov = true; | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | if (!has_mov) | ||
| 119 | continue; | ||
| 120 | uint32_t disp; | ||
| 121 | memcpy(&disp, b + i + 2, 4); | ||
| 122 | uintptr_t var = anchor + disp; | ||
| 123 | if (!g_client.contains(var)) | ||
| 124 | continue; | ||
| 125 | uintptr_t input = *(uintptr_t *)var; | ||
| 126 | if (!input || !g_client.contains(input)) | ||
| 127 | continue; | ||
| 128 | uintptr_t vtbl = *(uintptr_t *)input; | ||
| 129 | if (!vtbl || !g_client.contains(vtbl)) | ||
| 130 | continue; | ||
| 131 | uintptr_t f3 = *(uintptr_t *)(vtbl + 3 * sizeof(void *)); | ||
| 132 | if (!g_client.contains(f3)) | ||
| 133 | continue; | ||
| 134 | logf("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, | ||
| 135 | (unsigned long)var, (unsigned long)f3); | ||
| 136 | g_input = input; | ||
| 137 | return true; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | logf("CInput pointer variable not found in CreateMove scan"); | ||
| 141 | return false; | ||
| 142 | } | ||
