// cinput.cpp — CInput discovery, usercmd CRC, get/fix helpers #include "hl2h.h" // --------------------------------------------------------------------------- // CRC-32 (standard IEEE reflected, matches Valve's implementation) // --------------------------------------------------------------------------- static uint32_t g_crc_table[256]; static bool g_crc_inited = false; static void crc32_init_table() { if (g_crc_inited) return; for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320u ^ (c >> 1) : c >> 1; g_crc_table[i] = c; } g_crc_inited = true; } // field set verified from client.so GetChecksum disassembly uint32_t usercmd_checksum(const CUserCmd *c) { crc32_init_table(); uint32_t crc = 0xFFFFFFFFu; auto upd = [&crc](const void *p, size_t n) { const uint8_t *b = (const uint8_t *)p; while (n--) crc = g_crc_table[(crc ^ *b++) & 0xFF] ^ (crc >> 8); }; upd(&c->command_number, 4); upd(&c->tick_count, 4); upd(&c->viewangles, 12); upd(&c->forwardmove, 4); upd(&c->sidemove, 4); upd(&c->upmove, 4); upd(&c->buttons, 4); upd(&c->impulse, 1); upd(&c->weaponselect, 4); upd(&c->weaponsubtype, 4); upd(&c->random_seed, 4); upd(&c->mousedx, 2); upd(&c->mousedy, 2); return ~crc; } // CInput: +0xE4 = m_pCommands, +0xE8 = m_pVerifiedCommands static constexpr size_t CMDOFF_M_PCOMMANDS = 0xE4; static constexpr size_t CMDOFF_M_PVERIFIED = 0xE8; static constexpr size_t VERIFIED_STRIDE = 0x58; static constexpr size_t VERIFIED_CRC_OFF = 0x54; CUserCmd *get_usercmd(int seq) { if (!g_input) return nullptr; uintptr_t cmds = *(uintptr_t *)(g_input + CMDOFF_M_PCOMMANDS); if (!cmds) return nullptr; auto *cmd = (CUserCmd *)(cmds + (size_t)(seq % 90) * 0x54); if (cmd->command_number != seq) return nullptr; return cmd; } void fix_verified_crc(int seq, uint32_t crc) { if (!g_input) return; uintptr_t verified = *(uintptr_t *)(g_input + CMDOFF_M_PVERIFIED); if (verified) *(uint32_t *)(verified + (size_t)(seq % 90) * VERIFIED_STRIDE + VERIFIED_CRC_OFF) = crc; } // --------------------------------------------------------------------------- // CInput discovery: byte-scan of CHLClient::CreateMove prologue // --------------------------------------------------------------------------- bool find_input_global() { void **vt = VTABLE(g_iface.client); uintptr_t cm = (uintptr_t)vt[21]; // CreateMove slot if (!g_client.contains(cm)) { logf("CreateMove %p outside client module", (void *)cm); return false; } const uint8_t *b = (const uint8_t *)cm; // 1) PIC anchor: call __x86.get_pc_thunk + add $imm,%esi uintptr_t anchor = 0; for (int i = 0; i <= 0x100 - 11; i++) { if (b[i] == 0xE8 && b[i + 5] == 0x81 && b[i + 6] == 0xC6) { uint32_t imm; memcpy(&imm, b + i + 7, 4); anchor = cm + (uintptr_t)(i + 5) + imm; break; } } if (!anchor || !g_client.contains(anchor)) { logf("PIC anchor not found in CreateMove"); return false; } // 2) the input->CreateMove call: "ff 51 0c" preceded by "52", scan back // for "8d 86 imm32" with "8b 10" between for (int c = 3; c <= 0x100 - 3; c++) { if (!(b[c] == 0xFF && b[c + 1] == 0x51 && b[c + 2] == 0x0C && b[c - 1] == 0x52)) continue; for (int i = c - 8; i > c - 44 && i > 2; i--) { if (b[i] != 0x8D || b[i + 1] != 0x86) continue; bool has_mov = false; for (int j = i + 6; j < c - 1; j++) if (b[j] == 0x8B && b[j + 1] == 0x10) { has_mov = true; break; } if (!has_mov) continue; uint32_t disp; memcpy(&disp, b + i + 2, 4); uintptr_t var = anchor + disp; if (!g_client.contains(var)) continue; uintptr_t input = *(uintptr_t *)var; if (!input || !g_client.contains(input)) continue; uintptr_t vtbl = *(uintptr_t *)input; if (!vtbl || !g_client.contains(vtbl)) continue; uintptr_t f3 = *(uintptr_t *)(vtbl + 3 * sizeof(void *)); if (!g_client.contains(f3)) continue; logf("CInput* = %#lx (ptr var %#lx, vfunc[3] %#lx)", (unsigned long)input, (unsigned long)var, (unsigned long)f3); g_input = input; return true; } } logf("CInput pointer variable not found in CreateMove scan"); return false; }