├── .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