summaryrefslogtreecommitdiffstats
path: root/src/cinput.cpp
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-30 03:32:59 +0200
committersalmonik <salmonik@tuta.io>2026-08-30 03:32:59 +0200
commitaa096fe6886681d3338573cd3af779b57a5b56ab (patch)
tree14aee66fa1c3c168c140f02006aec246f9bbc23c /src/cinput.cpp
downloadhl2h-aa096fe6886681d3338573cd3af779b57a5b56ab.tar.gz
hl2h-aa096fe6886681d3338573cd3af779b57a5b56ab.zip
init
Diffstat (limited to 'src/cinput.cpp')
-rw-r--r--src/cinput.cpp142
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
8static uint32_t g_crc_table[256];
9static bool g_crc_inited = false;
10
11static 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
24uint32_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
49static constexpr size_t CMDOFF_M_PCOMMANDS = 0xE4;
50static constexpr size_t CMDOFF_M_PVERIFIED = 0xE8;
51static constexpr size_t VERIFIED_STRIDE = 0x58;
52static constexpr size_t VERIFIED_CRC_OFF = 0x54;
53
54CUserCmd *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
66void 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
79bool 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}