├── .gitignore ├── LICENSE ├── README.md ├── cgame.cpp ├── cl_chat.cpp ├── cl_input.cpp ├── cl_main.cpp ├── client.h ├── cod.bmp ├── codextended.cpp ├── common.cpp ├── common.h ├── console.cpp ├── crypt.cpp ├── cvar.cpp ├── discord.cpp ├── dl_main.cpp ├── dl_public.h ├── dllmain.cpp ├── exmaster_client.cpp ├── exmaster_client.h ├── files.cpp ├── fixes.cpp ├── game.cpp ├── hdd_serial_number.h ├── hook.cpp ├── keycodes.h ├── keys.h ├── lib_export.def ├── librarymodule.cpp ├── libs ├── detours │ ├── detours.h │ └── detours.lib ├── discord │ ├── discord-rpc.lib │ ├── discord_register.h │ └── discord_rpc.h └── libcurl │ ├── curl.h │ ├── curlver.h │ ├── easy.h │ ├── header.h │ ├── libcurl.lib │ ├── libcurl_a.lib │ ├── mprintf.h │ ├── multi.h │ ├── options.h │ ├── stdcheaders.h │ ├── system.h │ ├── typecheck-gcc.h │ ├── urlapi.h │ └── websockets.h ├── mem_util.h ├── menudef.h ├── mss32.VC.db ├── mss32.cpp ├── mss32.filters ├── mss32.rc ├── mss32.sln ├── mss32.user ├── mss32.vcxproj ├── mss32.vcxproj.user ├── net.cpp ├── render.cpp ├── render.h ├── resource.h ├── shared.cpp ├── shared.h ├── stdafx.cpp ├── stdafx.h ├── stockmapinfo.h ├── targetver.h ├── ui.cpp ├── version.h ├── version_info.h ├── win_authority.cpp ├── win_main.cpp ├── xm_phandler.cpp ├── xuid.cpp └── xuidtool.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | Debug/ 3 | Release/ 4 | Dev/ 5 | EXE/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Call of Duty Extended [Client] 2 | 3 | CoDExtended Client (1.1x), is a client-side modification for Call of Duty 1 1.1. 4 | 5 | ## Requirements 6 | - At least PC with Windows XP SP3 Update, but recommended is Windows 10. (Supported OS'es: Win XP SP3,Win 7,Win 10, Win 11), non tested OS'es: Win Vista, Win 8 7 | - Disabled VirtualStore 8 | 9 | ## Changes/additions 10 | 11 | - server list name cleanup (removed invalid characters which pushed servers to top) 12 | - fixed "invalid CD-Key" when using fs_game / mods 13 | - cURL (HTTP/fast) download 14 | - new UI elements (cg_xui_scoreboard, cg_xui_connect, cg_xui_fps) 15 | - Discord Rich Presence 16 | - unlocked FOV 17 | - bugfixes 18 | 19 | ## Building from source 20 | 21 | Build with Visual Studio
22 | Rename old mss32.dll to miles32.dll
23 | Copy newly compiled DLL to game directory and rename it to mss32.dll
24 | ???
25 | PROFIT
26 | 27 | ## Notice 28 | 29 | The official 1.1x source code is private. This build will not let you play on 1.1x-enforced servers. 30 | If you wish to add your own features, add your features with a pull request so we can include them in the next official release. 31 | 32 | Forum: http://xtnded.org 33 | Discord: http://discord.cod1x.eu 34 | 35 | ## License 36 | 37 | CoDExtended Client is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 38 | -------------------------------------------------------------------------------- /cgame.cpp: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include "client.h" 3 | #include "render.h" 4 | #include 5 | #pragma comment(lib, "winmm.lib") 6 | 7 | typedef enum { 8 | CG_R_ADDREFENTITYTOSCREEN = 61, 9 | 10 | CG_GETDOBJ = 162, 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | e_cgame_trap_end 21 | } e_cgame_trap; 22 | 23 | /* notepad ++ search for regex for e.g more parms with specific syscall num with 24 | _30074898\([^,]*, 61 25 | */ 26 | 27 | typedef void(*CG_ServerCommand_t)(); 28 | CG_ServerCommand_t CG_ServerCommand; 29 | 30 | const char* disliked_vars[] = { 31 | "r_showimages", 32 | "name", 33 | "cl_allowdownload", 34 | "version", 35 | "cg_norender", 36 | "cl_avidemo", 37 | NULL }; 38 | 39 | #define M_DrawShadowString(x,y,font,fontscale,color,text,a,b,c) \ 40 | RE_SetColor(vColorBlack); \ 41 | SCR_DrawString(x + 1,y + 1,font,fontscale,vColorBlack,text,a,b,c); \ 42 | RE_SetColor(color); \ 43 | SCR_DrawString(x,y,font,fontscale,color,text,a,b,c); \ 44 | RE_SetColor(NULL); 45 | 46 | DWORD cgame_mp; 47 | 48 | class ChatMessage { 49 | public: 50 | std::string msg; 51 | time_t addtime; 52 | 53 | ChatMessage() { 54 | addtime = time(NULL); 55 | } 56 | }; 57 | 58 | std::vector chatmessages; 59 | 60 | 61 | void CG_RemoveChatEscapeChar(char *s) { 62 | char *src, *dest; 63 | for (src = s, dest = s; *src; src++) { 64 | if( *src == '^' && isdigit(*(src + 1)) || isdigit(*src) && src != s && *(src - 1) == '^') 65 | continue; 66 | if (*src != 25 && *src >= 32 && *src <= 126) 67 | *dest++ = *src; 68 | } 69 | *dest = 0; 70 | } 71 | 72 | char *(*CG_Argv)(int) = nullptr; 73 | 74 | void myCG_ServerCommand(void) { 75 | int argc = Cmd_Argc(); 76 | #if 0 77 | Com_Printf("^2CG_ServerCommand: "); 78 | for (int i = 0; i < argc; i++) 79 | Com_Printf("%s ", Cmd_Argv(i)); 80 | Com_Printf("\n"); 81 | #endif 82 | 83 | if (argc > 0) { 84 | char* cmd = Cmd_Argv(0); 85 | if (strlen(cmd) > 0) { 86 | if ((*cmd == 'h' || *cmd == 'i')) { 87 | } else if (*cmd == 'b') { 88 | Com_DPrintf("[CG_ParseScores] b "); 89 | for (size_t i = 0; i < argc; i++) { 90 | Com_DPrintf("%s ", Cmd_Argv(i)); 91 | } 92 | Com_DPrintf("\n"); 93 | } else if (*cmd == 'v') { 94 | if (argc > 1) { 95 | char* var = Cmd_Argv(1); 96 | 97 | for (int i = 0; disliked_vars[i]; i++) { 98 | if (!strcmp(disliked_vars[i], var)) 99 | return; // kindly fuck off please (c) php 100 | } 101 | } 102 | 103 | } 104 | } 105 | } 106 | CG_ServerCommand(); 107 | } 108 | 109 | void pm_aimflag() { 110 | int *pm = (int*)(cgame_mp + 0x19D570); 111 | int *ps = (int*)*pm; 112 | int *gclient = (int*)*ps; 113 | 114 | int *v4 = (int *)(ps + 12); 115 | 116 | int val = *(int*)(gclient + 21); //336? 84*4=336 /84/4=21?? 117 | 118 | if (val == 1023) { 119 | *v4 |= 0x20; 120 | return; 121 | } 122 | 123 | void(*call)(); 124 | *(int*)&call = CGAME_OFF(0x3000FB80); 125 | call(); 126 | } 127 | 128 | #define cg_crosshairClientNum (*(int*)CGAME_OFF(0x3020C8C8)) 129 | #define cg_renderingThirdPerson (*(int*)CGAME_OFF(0x30207158)) 130 | 131 | typedef enum { 132 | TEAM_FREE, 133 | TEAM_AXIS, 134 | TEAM_ALLIES, 135 | TEAM_SPECTATOR, 136 | 137 | TEAM_NUM_TEAMS 138 | } team_t; 139 | 140 | static const char *teamStrings[] = { "TEAM_FREE", "Axis", "Allies", "Spectator" }; 141 | 142 | typedef struct { 143 | int snapsFlags; 144 | int ping; 145 | int serverTime; 146 | //rest 147 | } snapshot_t; 148 | 149 | typedef struct { 150 | int unk; 151 | int infoValid; 152 | int clientNum; 153 | char name[32]; 154 | team_t team; 155 | //down here model names and attachments and rest of client info 156 | } clientInfo_t; 157 | 158 | typedef struct { 159 | float alpha; 160 | int clientNum; 161 | time_t time; 162 | team_t team; 163 | } fadeStatus_t; 164 | 165 | #define MAX_LAST_NUMS 5 166 | static fadeStatus_t lastNums[MAX_LAST_NUMS] = { 0 }; 167 | static int lastNumsIndex = 0; 168 | 169 | static clientInfo_t *other_info = NULL, *info = NULL; 170 | static int *cent; 171 | 172 | #define CLIENTINFO_SIZE 0x448 173 | 174 | void R_AddDebugString(float *origin, float *color, float fontScale, const char* str) { 175 | __asm { 176 | push str 177 | push fontScale 178 | mov ebx, origin 179 | mov edi, color 180 | mov eax, 0x4E0A40 181 | call eax 182 | add esp, 8 183 | } 184 | } 185 | 186 | void CG_DrawCrosshairNames() { 187 | cg_crosshairClientNum = 65; 188 | float(*CG_ScanForCrosshairEntity)(); 189 | *(int*)&CG_ScanForCrosshairEntity = CGAME_OFF(0x30016E50); 190 | CG_ScanForCrosshairEntity(); 191 | 192 | if (cg_crosshairClientNum > 64) 193 | return; 194 | 195 | other_info = (clientInfo_t*)(0x448 * cg_crosshairClientNum + CGAME_OFF(0x3018BC0C)); 196 | info = (clientInfo_t*)(0x448 * *(int*)(*(int*)CGAME_OFF(0x301E2160) + 184) + CGAME_OFF(0x3018BC0C)); 197 | 198 | if (other_info->team == TEAM_ALLIES || other_info->team == TEAM_AXIS) { 199 | if (!*(int*)CGAME_OFF(0x3020BA24) && !*(int*)CGAME_OFF(0x3020BA28)) { 200 | return; 201 | } 202 | else if (other_info->team == info->team || info->team == TEAM_SPECTATOR) { 203 | for (int i = 0; i < MAX_LAST_NUMS; i++) { 204 | if (lastNums[i].alpha > 0 && lastNums[i].clientNum == cg_crosshairClientNum) { 205 | lastNums[i].alpha = 1; 206 | return; 207 | } 208 | } 209 | 210 | lastNumsIndex = (lastNumsIndex + 1) % MAX_LAST_NUMS; 211 | fadeStatus_t *st = &lastNums[lastNumsIndex]; 212 | st->clientNum = cg_crosshairClientNum; 213 | st->time = *cls_realtime; 214 | st->alpha = 1; 215 | st->team = other_info->team; 216 | 217 | //float *origin = (float*)FILE_OFF(0x301E2188); //my origin 218 | //R_AddDebugString(origin,color,2,other_info->name); 219 | } 220 | } 221 | } 222 | 223 | void __cdecl cg_playersprites_sub() { 224 | 225 | other_info = NULL; 226 | 227 | for (int i = 0; i < MAX_LAST_NUMS; i++) { 228 | fadeStatus_t *st = &lastNums[i]; 229 | 230 | if (st->clientNum != *(int*)((int)cent + 144)) 231 | continue; 232 | 233 | if (st->alpha <= 0) { 234 | st->time = 0; 235 | st->alpha = 0; 236 | st->clientNum = 65; 237 | continue; 238 | } 239 | 240 | if (*cls_realtime - st->time > 350) { 241 | st->alpha -= 0.1; 242 | st->time = *cls_realtime; 243 | } 244 | 245 | other_info = (clientInfo_t*)(0x448 * st->clientNum + CGAME_OFF(0x3018BC0C)); 246 | 247 | #ifndef SMALLCHAR_WIDTH 248 | #define SMALLCHAR_WIDTH 48 249 | #endif 250 | vec4_t color = { 1, 1, 1, st->alpha }; 251 | vec3_t org = { 0 }; 252 | org[0] = *(float*)((int)cent + 504); 253 | org[1] = *(float*)((int)cent + 508); 254 | org[2] = *(float*)((int)cent + 512) + 80; 255 | char tmp_name[64] = { 0 }; 256 | strcpy(tmp_name, other_info->name); 257 | R_AddDebugString(org, color, .5, Q_CleanStr(tmp_name)); 258 | } 259 | } 260 | 261 | void(*CG_PlayerSprites)(); 262 | void _CG_PlayerSprites() { 263 | __asm mov cent, eax 264 | CG_PlayerSprites(); 265 | cg_playersprites_sub(); 266 | } 267 | 268 | void CG_SetHeadNames(int flag) { 269 | if (!cgame_mp) 270 | return; 271 | 272 | if (*cls_state != 6) // CA_ACTIVE 273 | return; 274 | 275 | static char org_bytes[2][5] = { 276 | { 0xA1, 0x2C, 0x90, 0x1D, 0x30 }, 277 | { 0xE8, 0xED, 0xF1, 0xFF, 0xFF } 278 | }; 279 | if (!flag) { 280 | _memcpy((void*)CGAME_OFF(0x30016F70), &org_bytes[0], 5); 281 | _memcpy((void*)CGAME_OFF(0x300282DE), &org_bytes[1], 5); 282 | return; 283 | } 284 | __jmp(CGAME_OFF(0x30016F70), (int)CG_DrawCrosshairNames); 285 | __call(CGAME_OFF(0x300282DE), (int)_CG_PlayerSprites); 286 | } 287 | 288 | void ParseVector(char *s, vec3_t out) { 289 | if (!s || !*s) 290 | return; 291 | 292 | if (strlen(s) > 200) 293 | return; 294 | char b[128] = { 0 }; 295 | int outN = 0, bi = 0; 296 | for (int i = 0; s[i] != '\0'; i++) { 297 | if ((i != 0 && s[i] == ' ') || s[i + 1] == '\0') { 298 | out[outN++] = atof(b); 299 | memset(b, 0, sizeof(b)); 300 | bi = 0; 301 | } 302 | else { 303 | b[bi++] = s[i]; 304 | } 305 | 306 | } 307 | } 308 | 309 | char *PrintVector(vec3_t v) { 310 | return va("x: %f, y: %f, z: %f", v[0], v[1], v[2]); 311 | } 312 | 313 | #define M_DrawShadowedString(x, y, fontscale, fontcolor, text) \ 314 | SCR_DrawString(x+2,y+2, 1, fontscale, vColorBlack, text, NULL, NULL, NULL); \ 315 | SCR_DrawString(x,y,1,fontscale,fontcolor,text,NULL,NULL,NULL); 316 | 317 | 318 | #pragma pack(push, 1) 319 | typedef struct { 320 | int clientNum; 321 | int score; 322 | int ping; 323 | int deaths; 324 | char *clientName; 325 | int statusicon; 326 | } clientScoreInfo_t; 327 | #pragma pack(pop) 328 | 329 | #include 330 | 331 | void CG_KeyEvent(int key, int down, unsigned time) { 332 | if (keys[key].repeats > 1) 333 | return; 334 | if (!down) 335 | return; 336 | 337 | if (!cgame_mp) 338 | return; 339 | } 340 | 341 | extern cvar_t *cg_drawheadnames; 342 | 343 | void CG_SCR_DrawScreenField(int stereoFrame) { 344 | if (cg_drawheadnames->modified) { 345 | void CG_SetHeadNames(int flag); 346 | CG_SetHeadNames(cg_drawheadnames->integer); 347 | } 348 | } 349 | 350 | void CG_DrawDisconnect() { 351 | cvar_t* xui_interrupted = Cvar_Get("cg_xui_interrupted", "0", CVAR_ARCHIVE); 352 | if (xui_interrupted->integer) { 353 | void(*call)(); 354 | *(int*)&call = CGAME_OFF(0x30015450); 355 | call(); 356 | } 357 | } 358 | 359 | void CG_DrawFPS(float y) { 360 | cvar_t* xui_fps = Cvar_Get("cg_xui_fps", "1", CVAR_ARCHIVE); 361 | 362 | if (xui_fps->integer) { 363 | cvar_t* x = Cvar_Get("cg_xui_fps_x", "597", CVAR_ARCHIVE); // uh this x y values just look good with my hp bar 364 | cvar_t* y = Cvar_Get("cg_xui_fps_y", "8", CVAR_ARCHIVE); 365 | 366 | #define FPS_FRAMES 4 367 | static int previousTimes[FPS_FRAMES]; 368 | static int index; 369 | int i, total; 370 | int fps; 371 | static int previous; 372 | int t, frameTime; 373 | 374 | t = timeGetTime(); 375 | frameTime = t - previous; 376 | previous = t; 377 | previousTimes[index % FPS_FRAMES] = frameTime; 378 | index++; 379 | 380 | if (index > FPS_FRAMES) { 381 | total = 0; 382 | for (i = 0; i < FPS_FRAMES; i++) { 383 | total += previousTimes[i]; 384 | } 385 | if (!total) { 386 | total = 1; 387 | } 388 | fps = 1000 * FPS_FRAMES / total; 389 | 390 | M_DrawShadowString(x->integer, y->integer, 1, .20, vColorWhite, va("FPS: %d", fps), NULL, NULL, NULL); 391 | } 392 | } else { 393 | void(*call)(float); 394 | *(int*)&call = CGAME_OFF(0x30014A00); 395 | call(y); 396 | } 397 | } 398 | 399 | void CG_Obituary(int ent) { 400 | if (!Cvar_VariableIntegerValue("cg_x_obituary")) return; 401 | 402 | void(*call)(int); 403 | *(int*)(&call) = CGAME_OFF(0x3001D6C0); 404 | call(ent); 405 | } 406 | 407 | void PM_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out) { 408 | float backoff; 409 | float change; 410 | int i; 411 | float overbounce = 1.001f; 412 | 413 | backoff = DotProduct(in, normal); 414 | 415 | if (backoff < 0) { 416 | backoff *= overbounce; 417 | } 418 | else { 419 | backoff /= overbounce; 420 | } 421 | 422 | for (i = 0; i < 3; i++) { 423 | change = normal[i] * backoff; 424 | out[i] = in[i] - change; 425 | } 426 | } 427 | 428 | // xoxor4d 429 | void PM_ProjectVelocity(vec3_t in, vec3_t normal, vec3_t out) { 430 | float speedXY, DotNormalXY, normalisedNormalXY, projectionZ, projectionXYZ; 431 | 432 | speedXY = in[1] * in[1] + in[0] * in[0]; 433 | 434 | if ((normal[2]) < 0.001f || (speedXY == 0.0f)) { 435 | VectorCopy(in, out); 436 | } 437 | else { 438 | DotNormalXY = normal[1] * in[1] + in[0] * normal[0]; 439 | normalisedNormalXY = -DotNormalXY / normal[2]; 440 | 441 | projectionZ = in[2] * in[2] + speedXY; 442 | 443 | projectionXYZ = sqrtf((projectionZ / (speedXY + normalisedNormalXY * normalisedNormalXY))); 444 | 445 | if (projectionXYZ < 1.0f || normalisedNormalXY < 0.0f || in[2] > 0.0f) { 446 | out[0] = projectionXYZ * in[0]; 447 | out[1] = projectionXYZ * in[1]; 448 | out[2] = projectionXYZ * normalisedNormalXY; 449 | } 450 | } 451 | } 452 | 453 | uint32_t PM_Bounce(vec3_t in, vec3_t normal, vec3_t out) { 454 | int x_cl_bounce = atoi(Info_ValueForKey(cs1, "x_cl_bounce")); 455 | 456 | if (x_cl_bounce) { 457 | PM_ProjectVelocity(in, normal, out); 458 | } 459 | else { 460 | PM_ClipVelocity(in, normal, out); 461 | } 462 | 463 | return CGAME_OFF(0x3000D830); 464 | } 465 | 466 | __declspec(naked) void PM_Bounce_Stub() 467 | { 468 | __asm 469 | { 470 | push esi; // out 471 | push ecx; // normal 472 | push edx; // in 473 | call PM_Bounce; 474 | add esp, 12; 475 | 476 | push eax 477 | retn; 478 | } 479 | } 480 | 481 | static void (*PM_CheckForChangeWeapon)(); 482 | static void (*PM_BeginWeaponChange)(int, int); 483 | static void (*PM_FinishWeaponChange)(); 484 | void _PM_CheckForChangeWeapon() 485 | { 486 | int* pm = (int*)(cgame_mp + 0x19D570); 487 | pmove_t* xm = *(pmove_t**)(int)pm; 488 | 489 | if ((xm->ps->pm_flags & 0x20000)) 490 | { 491 | int* weapon = (int*)((int)xm->ps + 176); 492 | if (*weapon) 493 | { 494 | PM_BeginWeaponChange(*weapon, 0); 495 | } 496 | return; 497 | } 498 | PM_CheckForChangeWeapon(); 499 | } 500 | 501 | void _PM_FinishWeaponChange() 502 | { 503 | int* pm = (int*)(cgame_mp + 0x19D570); 504 | pmove_t* xm = *(pmove_t**)(int)pm; 505 | 506 | if ((xm->ps->pm_flags & 0x20000)) 507 | { 508 | int* weapon = (int*)((int)xm->ps + 176); 509 | if (*weapon) 510 | { 511 | *weapon = 0; 512 | } 513 | return; 514 | } 515 | PM_FinishWeaponChange(); 516 | } 517 | 518 | void CG_Init(DWORD base) { 519 | cgame_mp = base; 520 | CG_ServerCommand = (CG_ServerCommand_t)(cgame_mp + 0x2E0D0); 521 | CG_Argv = (char*(*)(int))CGAME_OFF(0x30020960); 522 | 523 | XUNLOCK((void*)CGAME_OFF(0x3020C8C8), sizeof(int)); 524 | 525 | XUNLOCK((void*)CGAME_OFF(0x3020C8C8), sizeof(int)); //crosshairnumber 526 | 527 | __call(CGAME_OFF(0x3002E5A6), (int)myCG_ServerCommand); 528 | __call(CGAME_OFF(0x3000C799), (int)pm_aimflag); 529 | __call(CGAME_OFF(0x3000C7B8), (int)pm_aimflag); 530 | __call(CGAME_OFF(0x3000C7D2), (int)pm_aimflag); 531 | __call(CGAME_OFF(0x3000C7FF), (int)pm_aimflag); 532 | __call(CGAME_OFF(0x3000C858), (int)pm_aimflag); 533 | __call(CGAME_OFF(0x3000C893), (int)pm_aimflag); 534 | CG_PlayerSprites = (void(*)())CGAME_OFF(0x300274D0); 535 | 536 | __call(CGAME_OFF(0x300159CC), (int)CG_DrawDisconnect); 537 | __call(CGAME_OFF(0x300159D4), (int)CG_DrawDisconnect); 538 | 539 | __call(CGAME_OFF(0x3001509E), (int)CG_DrawFPS); 540 | 541 | __call(CGAME_OFF(0x3001E6A1), (int)CG_Obituary); 542 | 543 | *(UINT32*)CGAME_OFF(0x300749EC) = CVAR_ARCHIVE; // Enable cg_fov 544 | *(UINT32*)CGAME_OFF(0x30074EBC) = CVAR_ARCHIVE; // Enable cg_thirdperson 545 | 546 | void CG_SetHeadNames(int flag); 547 | CG_SetHeadNames(cg_drawheadnames->integer); 548 | 549 | __jmp(CGAME_OFF(0x3000D82B), (int)PM_Bounce_Stub); 550 | 551 | __nop(CGAME_OFF(0x30065550), 1); //weapon 32 fix 552 | 553 | __call(CGAME_OFF(0x30011C25), (int)_PM_CheckForChangeWeapon); 554 | __call(CGAME_OFF(0x30011CB4), (int)_PM_FinishWeaponChange); 555 | PM_CheckForChangeWeapon = (void(*)())CGAME_OFF(0x300112E0); 556 | PM_BeginWeaponChange = (void(*)(int, int))CGAME_OFF(0x30010570); 557 | PM_FinishWeaponChange = (void(*)())CGAME_OFF(0x300107c0); 558 | } 559 | -------------------------------------------------------------------------------- /cl_chat.cpp: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include "render.h" 3 | #include "client.h" 4 | 5 | #define GCHAT_BASE_X 50 6 | #define GCHAT_BASE_Y 20 7 | #define GCHAT_FONT_TYPE 2 8 | 9 | static const vec4_t gChatColor = {1,1,1,1}; 10 | 11 | struct chatmsg { 12 | std::string name; 13 | std::string message; 14 | time_t time; 15 | float opacity; 16 | int clearTimer; 17 | }; 18 | 19 | #define MAX_DISPLAY_MSG 15 20 | 21 | class UIGlobalChatMessageWindow : public UIObject { 22 | public: 23 | 24 | std::vector messages; 25 | 26 | UIGlobalChatMessageWindow(int a, int b, int c, int d) : UIObject(a, b, c, d) { 27 | classType = UIOT_UIGlobalChatMessageWindow; 28 | shader = whiteShader; 29 | } 30 | 31 | virtual void Render() { 32 | 33 | vec4_t bg_color_lower = { 0, 0, 0, .5 }; 34 | vec4_t bg_color = { 0, 0, 0, .85 }; 35 | RE_SetColor(bg_color); 36 | SCR_DrawPic(x, y, width, height, *whiteShader); 37 | RE_SetColor(NULL); 38 | 39 | const char *sMessage, *sName; 40 | int name_len, msg_count = 0; 41 | 42 | time_t curTime = time(NULL); 43 | vec4_t per_color = { gChatColor[0], gChatColor[1], gChatColor[2], 1 }; 44 | 45 | static bool reset_other_faders = false; 46 | int faders = 0; 47 | 48 | for (std::vector::iterator it = messages.begin(); it != messages.end();) { 49 | name_len = 0; 50 | sMessage = it->message.c_str(); 51 | per_color[3] = it->opacity; 52 | 53 | if (it->name.size() > 0) { 54 | //sName = va("(difftime = %f and %d) %s: ", difftime(curTime, it->time), *cls_realtime - it->clearTimer, it->name.c_str()); 55 | sName = va("%s: ", it->name.c_str()); 56 | name_len = SMALLCHAR_WIDTH * CG_DrawStrlen(sName); 57 | SCR_DrawString(x, y + SMALLCHAR_HEIGHT * (msg_count+1), GCHAT_FONT_TYPE, .3, (float*)per_color, sName, NULL, NULL, NULL); 58 | } 59 | SCR_DrawString(x + name_len, y + SMALLCHAR_HEIGHT * (msg_count+1), GCHAT_FONT_TYPE, .3, (float*)per_color, sMessage, NULL, NULL, NULL); 60 | 61 | msg_count++; 62 | 63 | if (difftime(curTime, it->time) >= 1 && ((messages.size() > MAX_DISPLAY_MSG && !reset_other_faders) || messages.size() - faders > MAX_DISPLAY_MSG)) { 64 | if (!it->clearTimer) { 65 | it->clearTimer = *cls_realtime; 66 | reset_other_faders = true; 67 | } 68 | } 69 | 70 | if (it->clearTimer) { 71 | if (*cls_realtime - it->clearTimer > 500) 72 | it->opacity -= 0.007f; 73 | faders++; 74 | } 75 | 76 | if (it->opacity <= 0) { 77 | reset_other_faders = false; 78 | it = messages.erase(it); 79 | continue; 80 | } 81 | 82 | ++it; 83 | } 84 | } 85 | 86 | virtual ~UIGlobalChatMessageWindow() {} 87 | }; 88 | 89 | UIGlobalChatMessageWindow *gwindow; 90 | 91 | bool xui_txt_box_OnEnter(UITextbox *o) { 92 | 93 | void Enc_SendTalkMessage(const char *chatmsg); 94 | Enc_SendTalkMessage(o->text.c_str()); 95 | 96 | return true; 97 | } 98 | 99 | void CM_CreateWindow(UIMenu *m) { 100 | int X_POS = 105; 101 | int X_WIDTH = 640; 102 | gwindow = xui->createMenuItem(m, X_POS, 0, X_WIDTH, 430); 103 | UITextbox *txt_box = xui->createMenuItem(m, X_POS, 440, 200, 30); 104 | txt_box->SetBackgroundColor(.7, .7, .7, 1); 105 | txt_box->m_OnEnter = xui_txt_box_OnEnter; 106 | } 107 | 108 | void CM_ReceiveHandler(const char *name, const char *message) { 109 | //if (gChatMessages.size() > 10) 110 | //gChatMessages.clear(); 111 | 112 | chatmsg msg; 113 | msg.message = message; 114 | if (name != NULL) //it's a global broadcast 115 | msg.name = name; 116 | msg.time = time(NULL); 117 | msg.opacity = 1; 118 | msg.clearTimer = 0; 119 | gwindow->messages.push_back(msg); 120 | } -------------------------------------------------------------------------------- /cl_input.cpp: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include "render.h" 3 | #include "client.h" 4 | #include "exmaster_client.h" 5 | 6 | qkey_t* keys = (qkey_t*)0x142F780; 7 | 8 | static cvar_t* cl_bypassMouseInput = (cvar_t*)0x142F604; 9 | 10 | #define KEY_ENTER 13 //KEY_RETURN? 11 | #define KEY_BACKSPACE 8 12 | 13 | void CL_CharEvent_(int key) { 14 | if (key == (unsigned char)'`' || key == (unsigned char)'~') { 15 | return; 16 | } 17 | if (*cls_keyCatchers & KEYCATCH_CONSOLE) // some day use the field_t types :D:D:DD but for now custom (c) php 18 | return; 19 | } 20 | 21 | #define CALL_CL_KEYEVENT(key) \ 22 | __asm{push ebx} \ 23 | __asm{mov ebx, 40E200h} \ 24 | __asm{mov eax, key} \ 25 | __asm{call ebx} \ 26 | __asm{pop ebx} 27 | 28 | UINT_PTR pcl_charevent = (UINT_PTR)0x40E200; 29 | 30 | void __declspec(naked) CL_CharEvent() { 31 | __asm 32 | { 33 | push eax 34 | call pcl_charevent 35 | call CL_CharEvent_ 36 | add esp, 4 37 | retn 38 | } 39 | } 40 | 41 | #define SHIFT_KEY 160 42 | #define SPACE_KEY 32 43 | #define KEY_ESCAPE 27 44 | 45 | #define IS_TOGGLING_OVERLAY (keys[SHIFT_KEY].down && keys[SPACE_KEY].repeats <= 1 && keys[SPACE_KEY].down && key == SPACE_KEY) 46 | qkey_t qeys[MAX_KEYS] = { 0 }; 47 | 48 | void CL_KeyEvent(int key, int down, unsigned int time) { 49 | call(0x40DC30, key, down, time); 50 | 51 | if (!key) 52 | return; 53 | 54 | if (ui_cursor == nullptr) 55 | return; 56 | 57 | void CG_KeyEvent(int, int, unsigned); 58 | CG_KeyEvent(key, down, time); 59 | 60 | if (*cls_keyCatchers & KEYCATCH_CONSOLE) { 61 | 62 | if (key == 'a' && keys[K_LCTRL].down) { 63 | char* consoleBuffer = (char*)0x142F65C; 64 | if (CopyToClipboard(consoleBuffer)); 65 | } 66 | return; 67 | 68 | } 69 | 70 | if (!down || !keys[key].down || keys[key].repeats > 1) 71 | return; 72 | } 73 | 74 | UINT_PTR pfield_charevent_return = (UINT_PTR)0x40CB77; 75 | UINT_PTR pfield_charevent_continue = (UINT_PTR)0x40CB23; 76 | 77 | __declspec(naked) void Field_CharEvent_IgnoreTilde() 78 | { 79 | __asm 80 | { 81 | cmp ebx, 20h 82 | jge check 83 | jmp pfield_charevent_return 84 | 85 | check : 86 | cmp ebx, 126 87 | jl checked 88 | jmp pfield_charevent_return 89 | 90 | checked : 91 | jmp pfield_charevent_continue 92 | } 93 | } 94 | 95 | // cmp ebx, 20h is 3 bytes, we need 5 for a jmp... 96 | // jl ... is 2 bytes 7c54 (assuming when subtracing the addresses) 97 | // so it works out 98 | // - Richard -------------------------------------------------------------------------------- /cl_main.cpp: -------------------------------------------------------------------------------- 1 | #include "shared.h" 2 | #include "client.h" 3 | #include "render.h" 4 | #include "version_info.h" 5 | #include "dl_public.h" 6 | 7 | #pragma comment(lib, "psapi.lib") 8 | 9 | #include "Shlwapi.h" 10 | 11 | cvar_t *cl_running; 12 | cvar_t *clientname; 13 | cvar_t *cl_wwwDownload; 14 | cvar_t *cl_allowDownload; 15 | cvar_t *cl_console_fraction; 16 | cvar_t *cl_findshader; 17 | 18 | cvar_t *cg_drawheadnames; 19 | cvar_t *cg_fov; 20 | cvar_t* cg_x_discord; 21 | 22 | cvar_t* g_bounce; 23 | 24 | cvar_t* com_hunkmegs; 25 | cvar_t* x_master; 26 | 27 | cvar_t* r_borderless; 28 | 29 | DWORD __glob_wd_threadid; 30 | HANDLE __glob_wd_threadhandle; 31 | std::string res; 32 | 33 | #include 34 | 35 | void CL_Connect_f() { 36 | void(*o)() = (void(*)())0x40F6A0; 37 | 38 | o(); 39 | 40 | if (*cls_state == CA_CONNECTING || *cls_state == CA_CHALLENGING) { 41 | Cvar_Set("cl_allowDownload", "0"); 42 | } 43 | 44 | char* info = clc_stringData + clc_stringOffsets[1]; 45 | char *fs_game = Info_ValueForKey(info, "fs_game"); // Reset client fs_game if loaded & server doesn't use it. 46 | if (fs_game == "") { 47 | Cvar_Set("fs_game", ""); 48 | } 49 | } 50 | 51 | void(*CL_DownloadsComplete)(void) = (void(*)())0x40FFB0; 52 | 53 | void DL_Name(const char *localName, char* remoteName) { 54 | char *downloadName = Cvar_VariableString("cl_downloadName"); 55 | Cvar_Set("cl_downloadName", va(" %s", remoteName)); // Enough spaces to render name fully. :P 56 | } 57 | 58 | static int use_regular_dl = 0; 59 | 60 | int dl_files_count = 0; 61 | 62 | void WWW_BeginDownload(void) { 63 | char localTempName[MAX_PATH]; 64 | char remoteTempName[MAX_PATH]; 65 | 66 | if (clc_bWWWDl) 67 | return; 68 | 69 | char *s; 70 | char *remoteName, *localName; 71 | 72 | char* info = clc_stringData + clc_stringOffsets[1]; 73 | char *url = Info_ValueForKey(info, "sv_wwwBaseURL"); 74 | 75 | if (*clc_downloadList) { 76 | s = clc_downloadList; 77 | 78 | dl_files_count = 0; 79 | int i; 80 | for (i = 0; i < strlen(clc_downloadList); i++) 81 | if (clc_downloadList[i] == '@') 82 | dl_files_count++; 83 | 84 | // @remotename@localname@remotename@localname, etc. 85 | 86 | if (*s == '@') { 87 | s++; 88 | } 89 | remoteName = s; 90 | 91 | if ((s = strchr(s, '@')) == NULL) { 92 | CL_DownloadsComplete(); 93 | return; 94 | } 95 | 96 | *s++ = 0; 97 | localName = s; 98 | if ((s = strchr(s, '@')) != NULL) { 99 | *s++ = 0; 100 | } else { 101 | s = localName + strlen(localName); // point at the nul byte 102 | } 103 | 104 | int tmp = use_regular_dl; 105 | use_regular_dl = 0; 106 | if (cl_wwwDownload->integer && url && *url && !tmp) { 107 | Com_Printf("***** WWW_BeginDownload *****\n" 108 | "Localname: %s\n" 109 | "Remotename: %s\n" 110 | "****************************\n", localName, remoteName); 111 | 112 | Cvar_Set("cl_downloadSize", "0"); 113 | Cvar_Set("cl_downloadCount", "0"); 114 | Cvar_Set("cl_downloadTime", va("%i", *cls_realtime)); 115 | 116 | Q_strncpyz(localTempName, FS_BuildOSPath(Cvar_VariableString("fs_homepath"), localName, ""), sizeof(localTempName)); 117 | localTempName[strlen(localTempName) - 1] = '\0'; 118 | 119 | Q_strncpyz(remoteTempName, FS_BuildOSPath(url, remoteName, ""), sizeof(remoteTempName)); 120 | remoteTempName[strlen(remoteTempName) - 1] = '\0'; 121 | 122 | if (!DL_BeginDownload(localTempName, remoteTempName, 1)) { 123 | clc_bWWWDl = false; 124 | char *error = va("Download failure while getting '%s'\n", remoteTempName); // get the msg before clearing structs 125 | 126 | Com_Error(ERR_DROP, error); 127 | return; 128 | } 129 | 130 | clc_bWWWDl = true; 131 | } 132 | 133 | *cls_downloadRestart = qtrue; 134 | 135 | memmove(clc_downloadList, s, strlen(s) + 1); 136 | 137 | return; 138 | } 139 | 140 | CL_DownloadsComplete(); 141 | } 142 | 143 | void X_CL_NextDownload(void) { 144 | char* info = clc_stringData + clc_stringOffsets[1]; 145 | char* url = Info_ValueForKey(info, "sv_wwwBaseURL"); 146 | int argc = Cmd_Argc(); 147 | 148 | if (argc > 1) { 149 | const char* arg1 = Info_ValueForKey(info, "sv_referencedPakNames"); 150 | 151 | if (strstr(arg1, ".pk3") != NULL) { //so if extension is not pk3 but is (exe,bat and any other) kick player (So you can't dl .exe,.bat,.cfg) 152 | Com_Error(ERR_DROP, "It's likely that this server will infect your computer with malware. \n We do not permit connections to such servers in order to protect your security."); 153 | return; 154 | } 155 | } 156 | 157 | if (cl_wwwDownload->integer && *url) 158 | WWW_BeginDownload(); 159 | else 160 | CL_NextDownload(); 161 | } 162 | 163 | void CL_WWWDownload() { 164 | dlStatus_t ret = DL_DownloadLoop(); 165 | 166 | if (ret == DL_CONTINUE) 167 | return; 168 | 169 | if (ret == DL_DONE) { 170 | Cvar_Set("cl_downloadName", ""); 171 | clc_bWWWDl = false; 172 | *cls_downloadRestart = 1; 173 | CL_DownloadsComplete(); 174 | } else if (ret == DL_FAILED) { 175 | // Perhaps actually check the response? Invalid URL, forbidden, etc? 176 | const char* error = va("Download failure while getting %s.\nURL might be invalid.", Cvar_VariableString("dlname_error")); 177 | Com_Error(ERR_DROP, error); 178 | return; 179 | } 180 | } 181 | 182 | void CL_InitDownloads() { 183 | if (clc_bWWWDl) 184 | return; 185 | 186 | if (cl_allowDownload->integer && FS_ComparePaks(clc_downloadList, 1024, qtrue)) { 187 | Com_Printf("Need paks: %s\n", clc_downloadList); 188 | if (*clc_downloadList) { // if autodownloading is not enabled on the server 189 | *cls_state = 3; 190 | CL_NextDownload(); 191 | return; 192 | } 193 | } 194 | 195 | CL_DownloadsComplete(); 196 | } 197 | 198 | void CL_FOVLimit() { 199 | int fov = Cvar_VariableIntegerValue("cg_fov"); 200 | int cheats = atoi(Info_ValueForKey(cs1, "sv_cheats")); 201 | 202 | char* sv_fov_min = Info_ValueForKey(cs1, "sv_fov_min"); 203 | char* sv_fov_max = Info_ValueForKey(cs1, "sv_fov_max"); 204 | int fovMin = strlen(sv_fov_min) ? atoi(sv_fov_min) : 80; 205 | int fovMax = strlen(sv_fov_max) ? atoi(sv_fov_max) : 95; 206 | 207 | if ((fov < fovMin || fov > fovMax) && cheats != 1) { 208 | Com_Printf("cg_fov \"%d\" is invalid. Allowed values: \"%d\" - \"%d\".\n", fov, fovMin, fovMax); 209 | Cvar_Set("cg_fov", "80"); 210 | } 211 | } 212 | 213 | void CL_FPSLimit() { 214 | int fps = Cvar_VariableIntegerValue("com_maxfps"); 215 | int cheats = atoi(Info_ValueForKey(cs1, "sv_cheats")); 216 | 217 | char* sv_fps_min = Info_ValueForKey(cs1, "sv_fps_min"); 218 | char* sv_fps_max = Info_ValueForKey(cs1, "sv_fps_max"); 219 | int fpsMin = strlen(sv_fps_min) ? atoi(sv_fps_min) : 30; 220 | int fpsMax = strlen(sv_fps_max) ? atoi(sv_fps_max) : 333; 221 | 222 | if ((fps < fpsMin || fps > fpsMax) && cheats != 1) { 223 | Com_Printf("com_maxfps \"%d\" is invalid. Allowed values: \"%d\" - \"%d\".\n", fps, fpsMin, fpsMax); 224 | Cvar_Set("com_maxfps", "85"); 225 | } 226 | } 227 | 228 | void Disconnect_IfEsc() { 229 | if (*cls_state == CA_CONNECTING || *cls_state == CA_CHALLENGING || *cls_state == CA_CONNECTED) { 230 | if (GetFocus() && GetKeyState(VK_ESCAPE) & 0x8000) 231 | { 232 | ((void(*)())0x40F5F0)(); //CL_Disconnnect 233 | } 234 | } 235 | } 236 | 237 | void CL_Frame(int msec) { 238 | void(*call)(int); 239 | *(DWORD*)&call = 0x411280; 240 | 241 | if (!cl_running->integer) 242 | return; 243 | 244 | void Enc_SendHeartbeat(); 245 | Enc_SendHeartbeat(); 246 | 247 | if (clc_bWWWDl) 248 | CL_WWWDownload(); 249 | 250 | void CL_DiscordFrame(); 251 | if (cg_x_discord->integer) 252 | CL_DiscordFrame(); 253 | 254 | CL_FOVLimit(); 255 | CL_FPSLimit(); 256 | Disconnect_IfEsc(); 257 | 258 | call(msec); 259 | } 260 | 261 | void Cmd_Borderless() { 262 | if (Cmd_Argc() > 1) { 263 | Cvar_Set("r_borderless", Cmd_Argv(1)); 264 | 265 | if (Cvar_VariableIntegerValue("r_borderless")) { 266 | Cvar_Set("r_fullscreen", "0"); 267 | Cvar_Set("r_mode", "-1"); 268 | Cvar_Set("com_introplayed", "1"); 269 | } 270 | 271 | void(*Cbuf_ExecuteText)(const char*); 272 | *(UINT32*)&Cbuf_ExecuteText = 0x428A80; 273 | char cmd[10]; 274 | sprintf(cmd, "vid_restart", cmd); 275 | Cbuf_ExecuteText(cmd); 276 | } 277 | else { 278 | Com_Printf("\"r_borderless\" is:\"%d\" default: \"0\"\n", Cvar_VariableIntegerValue("r_borderless")); 279 | } 280 | } 281 | 282 | void Cmd_Minimize() { 283 | ShowWindow(*gameWindow, SW_MINIMIZE); 284 | } 285 | 286 | int *whiteShader = (int*)0x15CA630; 287 | 288 | const char* CL_GetConfigString(int index) { 289 | // Check if the index is within bounds 290 | if (index < 0 || index >= 2048) { 291 | Com_Error(ERR_FATAL, "GetConfigString index out of bounds"); 292 | } 293 | 294 | // Calculate the offset based on the index 295 | int offset = clc_stringOffsets[index]; 296 | 297 | // Check if the offset is zero (empty string) 298 | if (!offset) { 299 | return ""; 300 | } 301 | 302 | // Return the actual config string 303 | return clc_stringData + offset; 304 | } 305 | 306 | void CL_OpenScriptMenu(void) 307 | { 308 | char* parentMenuName; 309 | const char* scriptMenuResponse; 310 | int menuIndex; 311 | char* command; 312 | int* dword_163B3E0 = (int*)(0x163B3E0); 313 | int* dword_161747C = (int*)(0x161747C); 314 | 315 | if (Cmd_Argc() == 3) 316 | { 317 | if (*(dword_163B3E0 + 32)) 318 | { 319 | if (dword_161747C) 320 | { 321 | parentMenuName = Cmd_Argv(1); 322 | scriptMenuResponse = Cmd_Argv(2); 323 | 324 | if (parentMenuName && scriptMenuResponse) 325 | { 326 | menuIndex = -1; 327 | for (int i = 0; i < 32; ++i) { 328 | const char* configString = CL_GetConfigString(i + 1180); 329 | 330 | if (*configString && !I_stricmp(parentMenuName, configString)) { 331 | menuIndex = i; 332 | break; 333 | } 334 | } 335 | 336 | if (menuIndex != -1) { 337 | int serverId = Cvar_VariableIntegerValue("sv_serverId"); 338 | command = va("cmd mr %i %i %s\n", serverId, menuIndex, scriptMenuResponse); 339 | void(*Cbuf_ExecuteText)(const char*); 340 | *(UINT32*)&Cbuf_ExecuteText = 0x428A80; 341 | Cbuf_ExecuteText(command); 342 | } 343 | else { 344 | Com_Printf("Menu '%s' not found!\n", parentMenuName); 345 | } 346 | } 347 | } 348 | } 349 | } 350 | else 351 | { 352 | Com_Printf("USAGE: openScriptMenu