summaryrefslogtreecommitdiffstats
path: root/src/cinput.cpp
blob: 7c40b3be263d6848594d45eab0ef689d585b10d6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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;
}