1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // cl_main.c  -- client main loop
30 
31 #include "client.h"
32 #include <limits.h>
33 
34 #ifdef __linux__
35 #include <sys/stat.h>
36 #endif
37 
38 #include "../sys/sys_local.h"
39 #include "../sys/sys_loadlib.h"
40 
41 #ifdef USE_MUMBLE
42 #include "libmumblelink.h"
43 #endif
44 
45 #ifdef USE_MUMBLE
46 cvar_t	*cl_useMumble;
47 cvar_t	*cl_mumbleScale;
48 #endif
49 
50 #ifdef USE_VOIP
51 cvar_t	*cl_voipUseVAD;
52 cvar_t	*cl_voipVADThreshold;
53 cvar_t	*cl_voipSend;
54 cvar_t	*cl_voipSendTarget;
55 cvar_t	*cl_voipGainDuringCapture;
56 cvar_t	*cl_voipCaptureMult;
57 cvar_t	*cl_voipShowMeter;
58 cvar_t	*cl_voipProtocol;
59 cvar_t	*cl_voip;
60 #endif
61 
62 #ifdef USE_RENDERER_DLOPEN
63 cvar_t	*cl_renderer;
64 #endif
65 
66 cvar_t  *cl_wavefilerecord;
67 cvar_t  *cl_nodelta;
68 cvar_t  *cl_debugMove;
69 
70 cvar_t  *cl_noprint;
71 #ifdef UPDATE_SERVER_NAME
72 cvar_t  *cl_motd;
73 #endif
74 cvar_t  *cl_autoupdate;         // DHM - Nerve
75 
76 cvar_t  *rcon_client_password;
77 cvar_t  *rconAddress;
78 
79 cvar_t  *cl_timeout;
80 cvar_t  *cl_maxpackets;
81 cvar_t  *cl_packetdup;
82 cvar_t  *cl_timeNudge;
83 cvar_t  *cl_showTimeDelta;
84 cvar_t  *cl_freezeDemo;
85 
86 cvar_t  *cl_showPing;
87 
88 cvar_t  *cl_shownet = NULL;     // NERVE - SMF - This is referenced in msg.c and we need to make sure it is NULL
89 cvar_t  *cl_shownuments;        // DHM - Nerve
90 cvar_t  *cl_visibleClients;     // DHM - Nerve
91 cvar_t  *cl_showSend;
92 cvar_t  *cl_showServerCommands; // NERVE - SMF
93 cvar_t  *cl_timedemo;
94 cvar_t	*cl_timedemoLog;
95 cvar_t	*cl_autoRecordDemo;
96 cvar_t	*cl_aviFrameRate;
97 cvar_t	*cl_aviMotionJpeg;
98 cvar_t  *cl_avidemo;
99 cvar_t  *cl_forceavidemo;
100 
101 cvar_t  *cl_freelook;
102 cvar_t  *cl_sensitivity;
103 
104 cvar_t  *cl_mouseAccel;
105 cvar_t	*cl_mouseAccelOffset;
106 cvar_t	*cl_mouseAccelStyle;
107 cvar_t  *cl_showMouseRate;
108 
109 cvar_t  *m_pitch;
110 cvar_t  *m_yaw;
111 cvar_t  *m_forward;
112 cvar_t  *m_side;
113 cvar_t  *m_filter;
114 
115 cvar_t	*j_pitch;
116 cvar_t	*j_yaw;
117 cvar_t	*j_forward;
118 cvar_t	*j_side;
119 cvar_t	*j_up;
120 cvar_t	*j_pitch_axis;
121 cvar_t	*j_yaw_axis;
122 cvar_t	*j_forward_axis;
123 cvar_t	*j_side_axis;
124 cvar_t	*j_up_axis;
125 
126 cvar_t  *cl_activeAction;
127 
128 cvar_t  *cl_motdString;
129 
130 cvar_t  *cl_allowDownload;
131 cvar_t  *cl_conXOffset;
132 cvar_t  *cl_inGameVideo;
133 
134 cvar_t  *cl_serverStatusResendTime;
135 cvar_t  *cl_missionStats;
136 cvar_t  *cl_waitForFire;
137 
138 // NERVE - SMF - localization
139 cvar_t  *cl_language;
140 cvar_t  *cl_debugTranslation;
141 // -NERVE - SMF
142 // DHM - Nerve :: Auto-Update
143 cvar_t  *cl_updateavailable;
144 cvar_t  *cl_updatefiles;
145 // DHM - Nerve
146 
147 cvar_t	*cl_lanForcePackets;
148 
149 cvar_t	*cl_guid;
150 cvar_t	*cl_guidServerUniq;
151 
152 cvar_t	*cl_consoleKeys;
153 
154 cvar_t	*cl_rate;
155 
156 clientActive_t cl;
157 clientConnection_t clc;
158 clientStatic_t cls;
159 vm_t                *cgvm;
160 
161 char				cl_reconnectArgs[MAX_OSPATH];
162 char				cl_oldGame[MAX_QPATH];
163 qboolean			cl_oldGameSet;
164 
165 // Structure containing functions exported from refresh DLL
166 refexport_t re;
167 #ifdef USE_RENDERER_DLOPEN
168 static void	*rendererLib = NULL;
169 #endif
170 
171 ping_t cl_pinglist[MAX_PINGREQUESTS];
172 
173 typedef struct serverStatus_s
174 {
175 	char string[BIG_INFO_STRING];
176 	netadr_t address;
177 	int time, startTime;
178 	qboolean pending;
179 	qboolean print;
180 	qboolean retrieved;
181 } serverStatus_t;
182 
183 serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
184 
185 // DHM - Nerve :: Have we heard from the auto-update server this session?
186 qboolean autoupdateChecked;
187 qboolean autoupdateStarted;
188 // TTimo : moved from char* to array (was getting the char* from va(), broke on big downloads)
189 char autoupdateFilename[MAX_QPATH];
190 // "updates" shifted from -7
191 #define AUTOUPDATE_DIR "ni]Zm^l"
192 #define AUTOUPDATE_DIR_SHIFT 7
193 
194 static int noGameRestart = qfalse;
195 
196 extern void SV_BotFrame( int time );
197 void CL_CheckForResend( void );
198 void CL_ShowIP_f( void );
199 void CL_ServerStatus_f( void );
200 void CL_ServerStatusResponse( netadr_t from, msg_t *msg );
201 void CL_SaveTranslations_f( void );
202 void CL_LoadTranslations_f( void );
203 
204 /*
205 ===============
206 CL_CDDialog
207 
208 Called by Com_Error when a cd is needed
209 ===============
210 */
CL_CDDialog(void)211 void CL_CDDialog( void ) {
212 	cls.cddialog = qtrue;   // start it next frame
213 }
214 
215 #ifdef USE_MUMBLE
216 static
CL_UpdateMumble(void)217 void CL_UpdateMumble(void)
218 {
219 	vec3_t pos, forward, up;
220 	float scale = cl_mumbleScale->value;
221 	float tmp;
222 
223 	if(!cl_useMumble->integer)
224 		return;
225 
226 	// !!! FIXME: not sure if this is even close to correct.
227 	AngleVectors( cl.snap.ps.viewangles, forward, NULL, up);
228 
229 	pos[0] = cl.snap.ps.origin[0] * scale;
230 	pos[1] = cl.snap.ps.origin[2] * scale;
231 	pos[2] = cl.snap.ps.origin[1] * scale;
232 
233 	tmp = forward[1];
234 	forward[1] = forward[2];
235 	forward[2] = tmp;
236 
237 	tmp = up[1];
238 	up[1] = up[2];
239 	up[2] = tmp;
240 
241 	if(cl_useMumble->integer > 1) {
242 		fprintf(stderr, "%f %f %f, %f %f %f, %f %f %f\n",
243 			pos[0], pos[1], pos[2],
244 			forward[0], forward[1], forward[2],
245 			up[0], up[1], up[2]);
246 	}
247 
248 	mumble_update_coordinates(pos, forward, up);
249 }
250 #endif
251 
252 
253 #ifdef USE_VOIP
254 static
CL_UpdateVoipIgnore(const char * idstr,qboolean ignore)255 void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore)
256 {
257 	if ((*idstr >= '0') && (*idstr <= '9')) {
258 		const int id = atoi(idstr);
259 		if ((id >= 0) && (id < MAX_CLIENTS)) {
260 			clc.voipIgnore[id] = ignore;
261 			CL_AddReliableCommand(va("voip %s %d",
262 			                         ignore ? "ignore" : "unignore", id), qfalse);
263 			Com_Printf("VoIP: %s ignoring player #%d\n",
264 			            ignore ? "Now" : "No longer", id);
265 			return;
266 		}
267 	}
268 	Com_Printf("VoIP: invalid player ID#\n");
269 }
270 
271 static
CL_UpdateVoipGain(const char * idstr,float gain)272 void CL_UpdateVoipGain(const char *idstr, float gain)
273 {
274 	if ((*idstr >= '0') && (*idstr <= '9')) {
275 		const int id = atoi(idstr);
276 		if (gain < 0.0f)
277 			gain = 0.0f;
278 		if ((id >= 0) && (id < MAX_CLIENTS)) {
279 			clc.voipGain[id] = gain;
280 			Com_Printf("VoIP: player #%d gain now set to %f\n", id, gain);
281 		}
282 	}
283 }
284 
CL_Voip_f(void)285 void CL_Voip_f( void )
286 {
287 	const char *cmd = Cmd_Argv(1);
288 	const char *reason = NULL;
289 
290 	if (clc.state != CA_ACTIVE)
291 		reason = "Not connected to a server";
292 	else if (!clc.voipCodecInitialized)
293 		reason = "Voip codec not initialized";
294 	else if (!clc.voipEnabled)
295 		reason = "Server doesn't support VoIP";
296 	else if (!clc.demoplaying && (Cvar_VariableValue("g_gametype") == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")))
297 		reason = "running in single-player mode";
298 
299 	if (reason != NULL) {
300 		Com_Printf("VoIP: command ignored: %s\n", reason);
301 		return;
302 	}
303 
304 	if (strcmp(cmd, "ignore") == 0) {
305 		CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue);
306 	} else if (strcmp(cmd, "unignore") == 0) {
307 		CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse);
308 	} else if (strcmp(cmd, "gain") == 0) {
309 		if (Cmd_Argc() > 3) {
310 			CL_UpdateVoipGain(Cmd_Argv(2), atof(Cmd_Argv(3)));
311 		} else if (Q_isanumber(Cmd_Argv(2))) {
312 			int id = atoi(Cmd_Argv(2));
313 			if (id >= 0 && id < MAX_CLIENTS) {
314 				Com_Printf("VoIP: current gain for player #%d "
315 					"is %f\n", id, clc.voipGain[id]);
316 			} else {
317 				Com_Printf("VoIP: invalid player ID#\n");
318 			}
319 		} else {
320 			Com_Printf("usage: voip gain <playerID#> [value]\n");
321 		}
322 	} else if (strcmp(cmd, "muteall") == 0) {
323 		Com_Printf("VoIP: muting incoming voice\n");
324 		CL_AddReliableCommand("voip muteall", qfalse);
325 		clc.voipMuteAll = qtrue;
326 	} else if (strcmp(cmd, "unmuteall") == 0) {
327 		Com_Printf("VoIP: unmuting incoming voice\n");
328 		CL_AddReliableCommand("voip unmuteall", qfalse);
329 		clc.voipMuteAll = qfalse;
330 	} else {
331 		Com_Printf("usage: voip [un]ignore <playerID#>\n"
332 		           "       voip [un]muteall\n"
333 		           "       voip gain <playerID#> [value]\n");
334 	}
335 }
336 
337 
338 static
CL_VoipNewGeneration(void)339 void CL_VoipNewGeneration(void)
340 {
341 	// don't have a zero generation so new clients won't match, and don't
342 	//  wrap to negative so MSG_ReadLong() doesn't "fail."
343 	clc.voipOutgoingGeneration++;
344 	if (clc.voipOutgoingGeneration <= 0)
345 		clc.voipOutgoingGeneration = 1;
346 	clc.voipPower = 0.0f;
347 	clc.voipOutgoingSequence = 0;
348 
349 	opus_encoder_ctl(clc.opusEncoder, OPUS_RESET_STATE);
350 }
351 
352 /*
353 ===============
354 CL_VoipParseTargets
355 
356 sets clc.voipTargets according to cl_voipSendTarget
357 Generally we don't want who's listening to change during a transmission,
358 so this is only called when the key is first pressed
359 ===============
360 */
CL_VoipParseTargets(void)361 void CL_VoipParseTargets(void)
362 {
363 	const char *target = cl_voipSendTarget->string;
364 	char *end;
365 	int val;
366 
367 	Com_Memset(clc.voipTargets, 0, sizeof(clc.voipTargets));
368 	clc.voipFlags &= ~VOIP_SPATIAL;
369 
370 	while(target)
371 	{
372 		while(*target == ',' || *target == ' ')
373 			target++;
374 
375 		if(!*target)
376 			break;
377 
378 		if(isdigit(*target))
379 		{
380 			val = strtol(target, &end, 10);
381 			target = end;
382 		}
383 		else
384 		{
385 			if(!Q_stricmpn(target, "all", 3))
386 			{
387 				Com_Memset(clc.voipTargets, ~0, sizeof(clc.voipTargets));
388 				return;
389 			}
390 			if(!Q_stricmpn(target, "spatial", 7))
391 			{
392 				clc.voipFlags |= VOIP_SPATIAL;
393 				target += 7;
394 				continue;
395 			}
396 			else
397 			{
398 				if(!Q_stricmpn(target, "attacker", 8))
399 				{
400 					val = VM_Call(cgvm, CG_LAST_ATTACKER);
401 					target += 8;
402 				}
403 				else if(!Q_stricmpn(target, "crosshair", 9))
404 				{
405 					val = VM_Call(cgvm, CG_CROSSHAIR_PLAYER);
406 					target += 9;
407 				}
408 				else
409 				{
410 					while(*target && *target != ',' && *target != ' ')
411 						target++;
412 
413 					continue;
414 				}
415 
416 				if(val < 0)
417 					continue;
418 			}
419 		}
420 
421 		if(val < 0 || val >= MAX_CLIENTS)
422 		{
423 			Com_Printf(S_COLOR_YELLOW "WARNING: VoIP "
424 				   "target %d is not a valid client "
425 				   "number\n", val);
426 
427 			continue;
428 		}
429 
430 		clc.voipTargets[val / 8] |= 1 << (val % 8);
431 	}
432 }
433 
434 /*
435 ===============
436 CL_CaptureVoip
437 
438 Record more audio from the hardware if required and encode it into Opus
439  data for later transmission.
440 ===============
441 */
442 static
CL_CaptureVoip(void)443 void CL_CaptureVoip(void)
444 {
445 	const float audioMult = cl_voipCaptureMult->value;
446 	const qboolean useVad = (cl_voipUseVAD->integer != 0);
447 	qboolean initialFrame = qfalse;
448 	qboolean finalFrame = qfalse;
449 
450 #if USE_MUMBLE
451 	// if we're using Mumble, don't try to handle VoIP transmission ourselves.
452 	if (cl_useMumble->integer)
453 		return;
454 #endif
455 
456 	// If your data rate is too low, you'll get Connection Interrupted warnings
457 	//  when VoIP packets arrive, even if you have a broadband connection.
458 	//  This might work on rates lower than 25000, but for safety's sake, we'll
459 	//  just demand it. Who doesn't have at least a DSL line now, anyhow? If
460 	//  you don't, you don't need VoIP.  :)
461 	if (cl_voip->modified || cl_rate->modified) {
462 		if ((cl_voip->integer) && (cl_rate->integer < 25000)) {
463 			Com_Printf(S_COLOR_YELLOW "Your network rate is too slow for VoIP.\n");
464 			Com_Printf("Set 'Data Rate' to 'LAN/Cable/xDSL' in 'Setup/System/Network'.\n");
465 			Com_Printf("Until then, VoIP is disabled.\n");
466 			Cvar_Set("cl_voip", "0");
467 		}
468 		Cvar_Set("cl_voipProtocol", cl_voip->integer ? "opus" : "");
469 		cl_voip->modified = qfalse;
470 		cl_rate->modified = qfalse;
471 	}
472 
473 	if (!clc.voipCodecInitialized)
474 		return;  // just in case this gets called at a bad time.
475 
476 	if (clc.voipOutgoingDataSize > 0)
477 		return;  // packet is pending transmission, don't record more yet.
478 
479 	if (cl_voipUseVAD->modified) {
480 		Cvar_Set("cl_voipSend", (useVad) ? "1" : "0");
481 		cl_voipUseVAD->modified = qfalse;
482 	}
483 
484 	if ((useVad) && (!cl_voipSend->integer))
485 		Cvar_Set("cl_voipSend", "1");  // lots of things reset this.
486 
487 	if (cl_voipSend->modified) {
488 		qboolean dontCapture = qfalse;
489 		if (clc.state != CA_ACTIVE)
490 			dontCapture = qtrue;  // not connected to a server.
491 		else if (!clc.voipEnabled)
492 			dontCapture = qtrue;  // server doesn't support VoIP.
493 		else if (clc.demoplaying)
494 			dontCapture = qtrue;  // playing back a demo.
495 		else if ( cl_voip->integer == 0 )
496 			dontCapture = qtrue;  // client has VoIP support disabled.
497 		else if ( audioMult == 0.0f )
498 			dontCapture = qtrue;  // basically silenced incoming audio.
499 
500 		cl_voipSend->modified = qfalse;
501 
502 		if(dontCapture)
503 		{
504 			Cvar_Set("cl_voipSend", "0");
505 			return;
506 		}
507 
508 		if (cl_voipSend->integer) {
509 			initialFrame = qtrue;
510 		} else {
511 			finalFrame = qtrue;
512 		}
513 	}
514 
515 	// try to get more audio data from the sound card...
516 
517 	if (initialFrame) {
518 		S_MasterGain(Com_Clamp(0.0f, 1.0f, cl_voipGainDuringCapture->value));
519 		S_StartCapture();
520 		CL_VoipNewGeneration();
521 		CL_VoipParseTargets();
522 	}
523 
524 	if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
525 		int samples = S_AvailableCaptureSamples();
526 		const int packetSamples = (finalFrame) ? VOIP_MAX_FRAME_SAMPLES : VOIP_MAX_PACKET_SAMPLES;
527 
528 		// enough data buffered in audio hardware to process yet?
529 		if (samples >= packetSamples) {
530 			// audio capture is always MONO16.
531 			static int16_t sampbuffer[VOIP_MAX_PACKET_SAMPLES];
532 			float voipPower = 0.0f;
533 			int voipFrames;
534 			int i, bytes;
535 
536 			if (samples > VOIP_MAX_PACKET_SAMPLES)
537 				samples = VOIP_MAX_PACKET_SAMPLES;
538 
539 			// !!! FIXME: maybe separate recording from encoding, so voipPower
540 			// !!! FIXME:  updates faster than 4Hz?
541 
542 			samples -= samples % VOIP_MAX_FRAME_SAMPLES;
543 			if (samples != 120 && samples != 240 && samples != 480 && samples != 960 && samples != 1920 && samples != 2880 ) {
544 				Com_Printf("Voip: bad number of samples %d\n", samples);
545 				return;
546 			}
547 			voipFrames = samples / VOIP_MAX_FRAME_SAMPLES;
548 
549 			S_Capture(samples, (byte *) sampbuffer);  // grab from audio card.
550 
551 			// check the "power" of this packet...
552 			for (i = 0; i < samples; i++) {
553 				const float flsamp = (float) sampbuffer[i];
554 				const float s = fabs(flsamp);
555 				voipPower += s * s;
556 				sampbuffer[i] = (int16_t) ((flsamp) * audioMult);
557 			}
558 
559 			// encode raw audio samples into Opus data...
560 			bytes = opus_encode(clc.opusEncoder, sampbuffer, samples,
561 									(unsigned char *) clc.voipOutgoingData,
562 									sizeof (clc.voipOutgoingData));
563 			if ( bytes <= 0 ) {
564 				Com_DPrintf("VoIP: Error encoding %d samples\n", samples);
565 				bytes = 0;
566 			}
567 
568 			clc.voipPower = (voipPower / (32768.0f * 32768.0f *
569 			                 ((float) samples))) * 100.0f;
570 
571 			if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
572 				CL_VoipNewGeneration();  // no "talk" for at least 1/4 second.
573 			} else {
574 				clc.voipOutgoingDataSize = bytes;
575 				clc.voipOutgoingDataFrames = voipFrames;
576 
577 				Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
578 				            voipFrames, bytes, clc.voipPower);
579 
580 				#if 0
581 				static FILE *encio = NULL;
582 				if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb");
583 				if (encio != NULL) { fwrite(clc.voipOutgoingData, bytes, 1, encio); fflush(encio); }
584 				static FILE *decio = NULL;
585 				if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb");
586 				if (decio != NULL) { fwrite(sampbuffer, voipFrames * VOIP_MAX_FRAME_SAMPLES * 2, 1, decio); fflush(decio); }
587 				#endif
588 			}
589 		}
590 	}
591 
592 	// User requested we stop recording, and we've now processed the last of
593 	//  any previously-buffered data. Pause the capture device, etc.
594 	if (finalFrame) {
595 		S_StopCapture();
596 		S_MasterGain(1.0f);
597 		clc.voipPower = 0.0f;  // force this value so it doesn't linger.
598 	}
599 }
600 #endif
601 
602 /*
603 =======================================================================
604 
605 CLIENT RELIABLE COMMAND COMMUNICATION
606 
607 =======================================================================
608 */
609 
610 /*
611 ======================
612 CL_AddReliableCommand
613 
614 The given command will be transmitted to the server, and is gauranteed to
615 not have future usercmd_t executed before it is executed
616 ======================
617 */
CL_AddReliableCommand(const char * cmd,qboolean isDisconnectCmd)618 void CL_AddReliableCommand(const char *cmd, qboolean isDisconnectCmd)
619 {
620 	int unacknowledged = clc.reliableSequence - clc.reliableAcknowledge;
621 
622 	// if we would be losing an old command that hasn't been acknowledged,
623 	// we must drop the connection
624 	// also leave one slot open for the disconnect command in this case.
625 
626 	if ((isDisconnectCmd && unacknowledged > MAX_RELIABLE_COMMANDS) ||
627 	    (!isDisconnectCmd && unacknowledged >= MAX_RELIABLE_COMMANDS))
628 	{
629 		if(com_errorEntered)
630 			return;
631 		else
632 			Com_Error(ERR_DROP, "Client command overflow");
633 	}
634 
635 	Q_strncpyz(clc.reliableCommands[++clc.reliableSequence & (MAX_RELIABLE_COMMANDS - 1)],
636 		   cmd, sizeof(*clc.reliableCommands));
637 }
638 
639 /*
640 =======================================================================
641 
642 CLIENT SIDE DEMO RECORDING
643 
644 =======================================================================
645 */
646 
647 /*
648 ====================
649 CL_WriteDemoMessage
650 
651 Dumps the current net message, prefixed by the length
652 ====================
653 */
CL_WriteDemoMessage(msg_t * msg,int headerBytes)654 void CL_WriteDemoMessage( msg_t *msg, int headerBytes ) {
655 	int len, swlen;
656 
657 	// write the packet sequence
658 	len = clc.serverMessageSequence;
659 	swlen = LittleLong( len );
660 	FS_Write( &swlen, 4, clc.demofile );
661 
662 	// skip the packet sequencing information
663 	len = msg->cursize - headerBytes;
664 	swlen = LittleLong( len );
665 	FS_Write( &swlen, 4, clc.demofile );
666 	FS_Write( msg->data + headerBytes, len, clc.demofile );
667 }
668 
669 
670 /*
671 ====================
672 CL_StopRecording_f
673 
674 stop recording a demo
675 ====================
676 */
CL_StopRecord_f(void)677 void CL_StopRecord_f( void ) {
678 	int len;
679 
680 	if ( !clc.demorecording ) {
681 		Com_Printf( "Not recording a demo.\n" );
682 		return;
683 	}
684 
685 	// finish up
686 	len = -1;
687 	FS_Write( &len, 4, clc.demofile );
688 	FS_Write( &len, 4, clc.demofile );
689 	FS_FCloseFile( clc.demofile );
690 	clc.demofile = 0;
691 	clc.demorecording = qfalse;
692 	Com_Printf( "Stopped demo.\n" );
693 }
694 
695 /*
696 ==================
697 CL_DemoFilename
698 ==================
699 */
CL_DemoFilename(int number,char * fileName,int fileNameSize)700 void CL_DemoFilename( int number, char *fileName, int fileNameSize ) {
701 	int a,b,c,d;
702 
703 	if(number < 0 || number > 9999)
704 		number = 9999;
705 
706 	a = number / 1000;
707 	number -= a * 1000;
708 	b = number / 100;
709 	number -= b * 100;
710 	c = number / 10;
711 	number -= c * 10;
712 	d = number;
713 
714 	Com_sprintf( fileName, fileNameSize, "demo%i%i%i%i"
715 				 , a, b, c, d );
716 }
717 
718 /*
719 ====================
720 CL_Record_f
721 
722 record <demoname>
723 
724 Begins recording a demo from the current position
725 ====================
726 */
727 static char	demoName[MAX_QPATH];	// compiler bug workaround
CL_Record_f(void)728 void CL_Record_f( void ) {
729 	char	name[MAX_OSPATH];
730 	byte	bufData[MAX_MSGLEN];
731 	msg_t	buf;
732 	int	i;
733 	int	len;
734 	entityState_t	*ent;
735 	entityState_t	nullstate;
736 	char	*s;
737 
738 	if ( Cmd_Argc() > 2 ) {
739 		Com_Printf( "record <demoname>\n" );
740 		return;
741 	}
742 
743 	if ( clc.demorecording ) {
744 		Com_Printf( "Already recording.\n" );
745 		return;
746 	}
747 
748 	if ( clc.state != CA_ACTIVE ) {
749 		Com_Printf( "You must be in a level to record.\n" );
750 		return;
751 	}
752 
753 	// sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 ..
754 	if ( NET_IsLocalAddress( clc.serverAddress ) && !Cvar_VariableValue( "g_synchronousClients" ) ) {
755 		Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n");
756 	}
757 
758 	if ( Cmd_Argc() == 2 ) {
759 		s = Cmd_Argv( 1 );
760 		Q_strncpyz( demoName, s, sizeof( demoName ) );
761 #ifdef LEGACY_PROTOCOL
762 		if(clc.compat)
763 			Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_legacyprotocol->integer);
764 		else
765 #endif
766 			Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);
767 	} else {
768 		int	number;
769 
770 		// scan for a free demo name
771 		for ( number = 0 ; number <= 9999 ; number++ ) {
772 			CL_DemoFilename( number, demoName, sizeof( demoName ) );
773 #ifdef LEGACY_PROTOCOL
774 			if(clc.compat)
775 				Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_legacyprotocol->integer);
776 			else
777 #endif
778 				Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);
779 
780 			if (!FS_FileExists(name))
781 				break;	// file doesn't exist
782 		}
783 	}
784 
785 	// open the demo file
786 	Com_Printf( "recording to %s.\n", name );
787 	clc.demofile = FS_FOpenFileWrite( name );
788 	if ( !clc.demofile ) {
789 		Com_Printf( "ERROR: couldn't open.\n" );
790 		return;
791 	}
792 	clc.demorecording = qtrue;
793 	Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
794 
795 	// don't start saving messages until a non-delta compressed message is received
796 	clc.demowaiting = qtrue;
797 
798 	// write out the gamestate message
799 	MSG_Init( &buf, bufData, sizeof( bufData ) );
800 	MSG_Bitstream( &buf );
801 
802 	// NOTE, MRE: all server->client messages now acknowledge
803 	MSG_WriteLong( &buf, clc.reliableSequence );
804 
805 	MSG_WriteByte( &buf, svc_gamestate );
806 	MSG_WriteLong( &buf, clc.serverCommandSequence );
807 
808 	// configstrings
809 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
810 		if ( !cl.gameState.stringOffsets[i] ) {
811 			continue;
812 		}
813 		s = cl.gameState.stringData + cl.gameState.stringOffsets[i];
814 		MSG_WriteByte( &buf, svc_configstring );
815 		MSG_WriteShort( &buf, i );
816 		MSG_WriteBigString( &buf, s );
817 	}
818 
819 	// baselines
820 	Com_Memset( &nullstate, 0, sizeof( nullstate ) );
821 	for ( i = 0; i < MAX_GENTITIES ; i++ ) {
822 		ent = &cl.entityBaselines[i];
823 		if ( !ent->number ) {
824 			continue;
825 		}
826 		MSG_WriteByte( &buf, svc_baseline );
827 		MSG_WriteDeltaEntity( &buf, &nullstate, ent, qtrue );
828 	}
829 
830 	MSG_WriteByte( &buf, svc_EOF );
831 
832 	// finished writing the gamestate stuff
833 
834 	// write the client num
835 	MSG_WriteLong( &buf, clc.clientNum );
836 	// write the checksum feed
837 	MSG_WriteLong( &buf, clc.checksumFeed );
838 
839 	// finished writing the client packet
840 	MSG_WriteByte( &buf, svc_EOF );
841 
842 	// write it to the demo file
843 	len = LittleLong( clc.serverMessageSequence - 1 );
844 	FS_Write( &len, 4, clc.demofile );
845 
846 	len = LittleLong( buf.cursize );
847 	FS_Write( &len, 4, clc.demofile );
848 	FS_Write( buf.data, buf.cursize, clc.demofile );
849 
850 	// the rest of the demo file will be copied from net messages
851 }
852 
853 /*
854 =======================================================================
855 
856 CLIENT SIDE DEMO PLAYBACK
857 
858 =======================================================================
859 */
860 
861 /*
862 =================
863 CL_DemoFrameDurationSDev
864 =================
865 */
CL_DemoFrameDurationSDev(void)866 static float CL_DemoFrameDurationSDev( void )
867 {
868 	int i;
869 	int numFrames;
870 	float mean = 0.0f;
871 	float variance = 0.0f;
872 
873 	if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS )
874 		numFrames = MAX_TIMEDEMO_DURATIONS;
875 	else
876 		numFrames = clc.timeDemoFrames - 1;
877 
878 	for( i = 0; i < numFrames; i++ )
879 		mean += clc.timeDemoDurations[ i ];
880 	mean /= numFrames;
881 
882 	for( i = 0; i < numFrames; i++ )
883 	{
884 		float x = clc.timeDemoDurations[ i ];
885 
886 		variance += ( ( x - mean ) * ( x - mean ) );
887 	}
888 	variance /= numFrames;
889 
890 	return sqrt( variance );
891 }
892 
893 /*
894 =================
895 CL_DemoCompleted
896 =================
897 */
CL_DemoCompleted(void)898 void CL_DemoCompleted( void )
899 {
900 	char buffer[ MAX_STRING_CHARS ];
901 
902 	if( cl_timedemo && cl_timedemo->integer )
903 	{
904 		int	time;
905 
906 		time = Sys_Milliseconds() - clc.timeDemoStart;
907 		if( time > 0 )
908 		{
909 			// Millisecond times are frame durations:
910 			// minimum/average/maximum/std deviation
911 			Com_sprintf( buffer, sizeof( buffer ),
912 					"%i frames %3.1f seconds %3.1f fps %d.0/%.1f/%d.0/%.1f ms\n",
913 					clc.timeDemoFrames,
914 					time/1000.0,
915 					clc.timeDemoFrames*1000.0 / time,
916 					clc.timeDemoMinDuration,
917 					time / (float)clc.timeDemoFrames,
918 					clc.timeDemoMaxDuration,
919 					CL_DemoFrameDurationSDev( ) );
920 			Com_Printf( "%s", buffer );
921 
922 			// Write a log of all the frame durations
923 			if( cl_timedemoLog && strlen( cl_timedemoLog->string ) > 0 )
924 			{
925 				int i;
926 				int numFrames;
927 				fileHandle_t f;
928 
929 				if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS )
930 					numFrames = MAX_TIMEDEMO_DURATIONS;
931 				else
932 					numFrames = clc.timeDemoFrames - 1;
933 
934 				f = FS_FOpenFileWrite( cl_timedemoLog->string );
935 				if( f )
936 				{
937 					FS_Printf( f, "# %s", buffer );
938 
939 					for( i = 0; i < numFrames; i++ )
940 						FS_Printf( f, "%d\n", clc.timeDemoDurations[ i ] );
941 
942 					FS_FCloseFile( f );
943 					Com_Printf( "%s written\n", cl_timedemoLog->string );
944 				}
945 				else
946 				{
947 					Com_Printf( "Couldn't open %s for writing\n",
948 							cl_timedemoLog->string );
949 				}
950 			}
951 		}
952 	}
953 
954 	CL_Disconnect( qtrue );
955 	CL_NextDemo();
956 }
957 
958 /*
959 =================
960 CL_ReadDemoMessage
961 =================
962 */
CL_ReadDemoMessage(void)963 void CL_ReadDemoMessage( void ) {
964 	int	r;
965 	msg_t	buf;
966 	byte	bufData[ MAX_MSGLEN ];
967 	int	s;
968 
969 	if ( !clc.demofile ) {
970 		CL_DemoCompleted();
971 		return;
972 	}
973 
974 	// get the sequence number
975 	r = FS_Read( &s, 4, clc.demofile );
976 	if ( r != 4 ) {
977 		CL_DemoCompleted();
978 		return;
979 	}
980 	clc.serverMessageSequence = LittleLong( s );
981 
982 	// init the message
983 	MSG_Init( &buf, bufData, sizeof( bufData ) );
984 
985 	// get the length
986 	r = FS_Read( &buf.cursize, 4, clc.demofile );
987 	if ( r != 4 ) {
988 		CL_DemoCompleted();
989 		return;
990 	}
991 	buf.cursize = LittleLong( buf.cursize );
992 	if ( buf.cursize == -1 ) {
993 		CL_DemoCompleted();
994 		return;
995 	}
996 	if ( buf.cursize > buf.maxsize ) {
997 		Com_Error( ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN" );
998 	}
999 	r = FS_Read( buf.data, buf.cursize, clc.demofile );
1000 	if ( r != buf.cursize ) {
1001 		Com_Printf( "Demo file was truncated.\n" );
1002 		CL_DemoCompleted();
1003 		return;
1004 	}
1005 
1006 	clc.lastPacketTime = cls.realtime;
1007 	buf.readcount = 0;
1008 	CL_ParseServerMessage( &buf );
1009 }
1010 
1011 /*
1012 ====================
1013 CL_WalkDemoExt
1014 ====================
1015 */
CL_WalkDemoExt(char * arg,char * name,int * demofile)1016 static int CL_WalkDemoExt(char *arg, char *name, int *demofile)
1017 {
1018 	int i = 0;
1019 	*demofile = 0;
1020 
1021 #ifdef LEGACY_PROTOCOL
1022 	if(com_legacyprotocol->integer > 0)
1023 	{
1024 		Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_legacyprotocol->integer);
1025 		FS_FOpenFileRead(name, demofile, qtrue);
1026 
1027 		if (*demofile)
1028 		{
1029 			Com_Printf("Demo file: %s\n", name);
1030 			return com_legacyprotocol->integer;
1031 		}
1032 	}
1033 
1034 	if(com_protocol->integer != com_legacyprotocol->integer)
1035 #endif
1036 	{
1037 		Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_protocol->integer);
1038 		FS_FOpenFileRead(name, demofile, qtrue);
1039 
1040 		if (*demofile)
1041 		{
1042 			Com_Printf("Demo file: %s\n", name);
1043 			return com_protocol->integer;
1044 		}
1045 	}
1046 
1047 	Com_Printf("Not found: %s\n", name);
1048 
1049 	while(demo_protocols[i])
1050 	{
1051 #ifdef LEGACY_PROTOCOL
1052 		if(demo_protocols[i] == com_legacyprotocol->integer)
1053 			continue;
1054 #endif
1055 		if(demo_protocols[i] == com_protocol->integer)
1056 			continue;
1057 
1058 		Com_sprintf (name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, demo_protocols[i]);
1059 		FS_FOpenFileRead( name, demofile, qtrue );
1060 		if (*demofile)
1061 		{
1062 			Com_Printf("Demo file: %s\n", name);
1063 
1064 			return demo_protocols[i];
1065 		}
1066 		else
1067 			Com_Printf("Not found: %s\n", name);
1068 		i++;
1069 	}
1070 
1071 	return -1;
1072 }
1073 
1074 /*
1075 ====================
1076 CL_CompleteDemoName
1077 ====================
1078 */
CL_CompleteDemoName(char * args,int argNum)1079 static void CL_CompleteDemoName( char *args, int argNum )
1080 {
1081 	if( argNum == 2 )
1082 	{
1083 		char demoExt[ 16 ];
1084 
1085 		Com_sprintf(demoExt, sizeof(demoExt), ".%s%d", DEMOEXT, com_protocol->integer);
1086 		Field_CompleteFilename( "demos", demoExt, qtrue, qtrue );
1087 	}
1088 }
1089 
1090 /*
1091 ====================
1092 CL_PlayDemo_f
1093 
1094 demo <demoname>
1095 
1096 ====================
1097 */
CL_PlayDemo_f(void)1098 void CL_PlayDemo_f( void ) {
1099 	char		name[MAX_OSPATH];
1100 	char		arg[MAX_OSPATH];
1101 	char		*ext_test;
1102 	int		protocol, i;
1103 	char		retry[MAX_OSPATH];
1104 
1105 	if (Cmd_Argc() != 2) {
1106 		Com_Printf ("demo <demoname>\n");
1107 		return;
1108 	}
1109 
1110 	// make sure a local server is killed
1111 	// 2 means don't force disconnect of local client
1112 	Cvar_Set( "sv_killserver", "2" );
1113 
1114 	// open the demo file
1115 	Q_strncpyz( arg, Cmd_Argv(1), sizeof( arg ) );
1116 
1117 	CL_Disconnect( qtrue );
1118 
1119 	// check for an extension .DEMOEXT_?? (?? is protocol)
1120 	ext_test = strrchr(arg, '.');
1121 
1122 	if(ext_test && !Q_stricmpn(ext_test + 1, DEMOEXT, ARRAY_LEN(DEMOEXT) - 1))
1123 	{
1124 		protocol = atoi(ext_test + ARRAY_LEN(DEMOEXT));
1125 
1126 		for(i = 0; demo_protocols[i]; i++)
1127 		{
1128 			if(demo_protocols[i] == protocol)
1129 				break;
1130 		}
1131 
1132 		if(demo_protocols[i] || protocol == com_protocol->integer
1133 #ifdef LEGACY_PROTOCOL
1134 		   || protocol == com_legacyprotocol->integer
1135 #endif
1136 		  )
1137 		{
1138 			Com_sprintf(name, sizeof(name), "demos/%s", arg);
1139 			FS_FOpenFileRead(name, &clc.demofile, qtrue);
1140 		}
1141 		else
1142 		{
1143 			int len;
1144 
1145 			Com_Printf("Protocol %d not supported for demos\n", protocol);
1146 			len = ext_test - arg;
1147 
1148 			if(len >= ARRAY_LEN(retry))
1149 				len = ARRAY_LEN(retry) - 1;
1150 
1151 			Q_strncpyz(retry, arg, len + 1);
1152 			retry[len] = '\0';
1153 			protocol = CL_WalkDemoExt(retry, name, &clc.demofile);
1154 		}
1155 	}
1156 	else
1157 		protocol = CL_WalkDemoExt(arg, name, &clc.demofile);
1158 
1159 	if (!clc.demofile) {
1160 		Com_Error( ERR_DROP, "couldn't open %s", name);
1161 		return;
1162 	}
1163 	Q_strncpyz( clc.demoName, arg, sizeof( clc.demoName ) );
1164 
1165 	Con_Close();
1166 
1167 	clc.state = CA_CONNECTED;
1168 	clc.demoplaying = qtrue;
1169 	Q_strncpyz( clc.servername, arg, sizeof( clc.servername ) );
1170 
1171 #ifdef LEGACY_PROTOCOL
1172 	if(protocol <= com_legacyprotocol->integer)
1173 		clc.compat = qtrue;
1174 	else
1175 		clc.compat = qfalse;
1176 #endif
1177 
1178 	// read demo messages until connected
1179 	while ( clc.state >= CA_CONNECTED && clc.state < CA_PRIMED ) {
1180 		CL_ReadDemoMessage();
1181 	}
1182 	// don't get the first snapshot this frame, to prevent the long
1183 	// time from the gamestate load from messing causing a time skip
1184 	clc.firstDemoFrameSkipped = qfalse;
1185 }
1186 
1187 /*
1188 ====================
1189 CL_StartDemoLoop
1190 
1191 Closing the main menu will restart the demo loop
1192 ====================
1193 */
CL_StartDemoLoop(void)1194 void CL_StartDemoLoop( void ) {
1195 	// start the demo loop again
1196 	Cbuf_AddText( "d1\n" );
1197 	Key_SetCatcher( 0 );
1198 }
1199 
1200 /*
1201 ==================
1202 CL_NextDemo
1203 
1204 Called when a demo or cinematic finishes
1205 If the "nextdemo" cvar is set, that command will be issued
1206 ==================
1207 */
CL_NextDemo(void)1208 void CL_NextDemo( void ) {
1209 	char	v[MAX_STRING_CHARS];
1210 
1211 	Q_strncpyz( v, Cvar_VariableString( "nextdemo" ), sizeof( v ) );
1212 	v[MAX_STRING_CHARS - 1] = 0;
1213 	Com_DPrintf( "CL_NextDemo: %s\n", v );
1214 	if ( !v[0] ) {
1215 		return;
1216 	}
1217 
1218 	Cvar_Set( "nextdemo","" );
1219 	Cbuf_AddText( v );
1220 	Cbuf_AddText( "\n" );
1221 	Cbuf_Execute();
1222 }
1223 
1224 /*
1225 ====================
1226 
1227   Wave file saving functions
1228 
1229 ====================
1230 
1231 
1232 void CL_WriteWaveOpen() {
1233 	// we will just save it as a 16bit stereo 22050kz pcm file
1234 	clc.wavefile = FS_FOpenFileWrite( "demodata.pcm" );
1235 	clc.wavetime = -1;
1236 }
1237 
1238 void CL_WriteWaveClose() {
1239 	// and we're outta here
1240 	FS_FCloseFile( clc.wavefile );
1241 }
1242 
1243 extern int s_soundtime;
1244 extern portable_samplepair_t *paintbuffer;
1245 
1246 void CL_WriteWaveFilePacket() {
1247 	int total, i;
1248 	if ( clc.wavetime == -1 ) {
1249 		clc.wavetime = s_soundtime;
1250 		return;
1251 	}
1252 
1253 	total = s_soundtime - clc.wavetime;
1254 	clc.wavetime = s_soundtime;
1255 
1256 	for ( i = 0; i < total; i++ ) {
1257 		int parm;
1258 		short out;
1259 		parm =  ( paintbuffer[i].left ) >> 8;
1260 		if ( parm > 32767 ) {
1261 			parm = 32767;
1262 		}
1263 		if ( parm < -32768 ) {
1264 			parm = -32768;
1265 		}
1266 		out = parm;
1267 		FS_Write( &out, 2, clc.wavefile );
1268 		parm =  ( paintbuffer[i].right ) >> 8;
1269 		if ( parm > 32767 ) {
1270 			parm = 32767;
1271 		}
1272 		if ( parm < -32768 ) {
1273 			parm = -32768;
1274 		}
1275 		out = parm;
1276 		FS_Write( &out, 2, clc.wavefile );
1277 	}
1278 }
1279 */
1280 
1281 
1282 //======================================================================
1283 
1284 /*
1285 =====================
1286 CL_ShutdownAll
1287 =====================
1288 */
CL_ShutdownAll(qboolean shutdownRef)1289 void CL_ShutdownAll(qboolean shutdownRef)
1290 {
1291 	if(CL_VideoRecording())
1292 		CL_CloseAVI();
1293 
1294 	if(clc.demorecording)
1295 		CL_StopRecord_f();
1296 
1297 #ifdef USE_CURL
1298 	CL_cURL_Shutdown();
1299 #endif
1300 	// clear sounds
1301 	S_DisableSounds();
1302 	// shutdown CGame
1303 	CL_ShutdownCGame();
1304 	// shutdown UI
1305 	CL_ShutdownUI();
1306 
1307 	// shutdown the renderer
1308 	if(shutdownRef)
1309 		CL_ShutdownRef();
1310 	else if(re.Shutdown)
1311 		re.Shutdown(qfalse);		// don't destroy window or context
1312 
1313 	cls.uiStarted = qfalse;
1314 	cls.cgameStarted = qfalse;
1315 	cls.rendererStarted = qfalse;
1316 	cls.soundRegistered = qfalse;
1317 }
1318 
1319 /*
1320 =================
1321 CL_ClearMemory
1322 
1323 Called by Com_GameRestart
1324 =================
1325 */
CL_ClearMemory(qboolean shutdownRef)1326 void CL_ClearMemory(qboolean shutdownRef)
1327 {
1328 	// shutdown all the client stuff
1329 	CL_ShutdownAll(shutdownRef);
1330 
1331 	// if not running a server clear the whole hunk
1332 	if ( !com_sv_running || !com_sv_running->integer ) {
1333 		// clear the whole hunk
1334 		Hunk_Clear();
1335 		// clear collision map data
1336 		CM_ClearMap();
1337 	} else {
1338 		// clear all the client data on the hunk
1339 		Hunk_ClearToMark();
1340 	}
1341 }
1342 
1343 /*
1344 =================
1345 CL_FlushMemory
1346 
1347 Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only
1348 ways a client gets into a game
1349 Also called by Com_Error
1350 =================
1351 */
CL_FlushMemory(void)1352 void CL_FlushMemory(void)
1353 {
1354 	CL_ClearMemory(qfalse);
1355 	CL_StartHunkUsers(qfalse);
1356 }
1357 
1358 /*
1359 =====================
1360 CL_MapLoading
1361 
1362 A local server is starting to load a map, so update the
1363 screen to let the user know about it, then dump all client
1364 memory on the hunk from cgame, ui, and renderer
1365 =====================
1366 */
CL_MapLoading(void)1367 void CL_MapLoading( void ) {
1368 	if ( com_dedicated->integer ) {
1369 		clc.state = CA_DISCONNECTED;
1370 		Key_SetCatcher( KEYCATCH_CONSOLE );
1371 		return;
1372 	}
1373 
1374 	if ( !com_cl_running->integer ) {
1375 		return;
1376 	}
1377 
1378 	Con_Close();
1379 	Key_SetCatcher( 0 );
1380 
1381 	// if we are already connected to the local host, stay connected
1382 	if ( clc.state >= CA_CONNECTED && !Q_stricmp( clc.servername, "localhost" ) ) {
1383 		clc.state = CA_CONNECTED;		// so the connect screen is drawn
1384 		Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) );
1385 		Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) );
1386 		Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
1387 		clc.lastPacketSentTime = -9999;
1388 		SCR_UpdateScreen();
1389 	} else {
1390 		// clear nextmap so the cinematic shutdown doesn't execute it
1391 		Cvar_Set( "nextmap", "" );
1392 		CL_Disconnect( qtrue );
1393 		Q_strncpyz( clc.servername, "localhost", sizeof(clc.servername) );
1394 		clc.state = CA_CHALLENGING;		// so the connect screen is drawn
1395 		Key_SetCatcher( 0 );
1396 		SCR_UpdateScreen();
1397 		clc.connectTime = -RETRANSMIT_TIMEOUT;
1398 		NET_StringToAdr( clc.servername, &clc.serverAddress, NA_UNSPEC);
1399 		// we don't need a challenge on the localhost
1400 
1401 		CL_CheckForResend();
1402 	}
1403 }
1404 
1405 /*
1406 =====================
1407 CL_ClearState
1408 
1409 Called before parsing a gamestate
1410 =====================
1411 */
CL_ClearState(void)1412 void CL_ClearState( void ) {
1413 
1414 //	S_StopAllSounds();
1415 
1416 	memset( &cl, 0, sizeof( cl ) );
1417 }
1418 
1419 /*
1420 ====================
1421 CL_UpdateGUID
1422 
1423 update cl_guid using QKEY_FILE and optional prefix
1424 ====================
1425 */
CL_UpdateGUID(const char * prefix,int prefix_len)1426 static void CL_UpdateGUID( const char *prefix, int prefix_len )
1427 {
1428 #if !defined( USE_PBMD5 )
1429 	fileHandle_t f;
1430 	int len;
1431 
1432 	len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
1433 	FS_FCloseFile( f );
1434 
1435 	if( len != QKEY_SIZE )
1436 		Cvar_Set( "cl_guid", "" );
1437 	else
1438 		Cvar_Set( "cl_guid", Com_MD5File( QKEY_FILE, QKEY_SIZE,
1439 			prefix, prefix_len ) );
1440 #else
1441 	if ( !Q_stricmp( cl_cdkey, "                " )  )
1442 	{
1443 		Cvar_Set( "cl_guid", "NO_GUID" );
1444 
1445 		return;
1446 	}
1447 
1448 	if ( !Q_stricmp( cl_guid->string, "unknown" ) )
1449 		Cvar_Set( "cl_guid", Com_PBMD5File( cl_cdkey ) );
1450 	else
1451 		return;
1452 #endif
1453 }
1454 
CL_OldGame(void)1455 static void CL_OldGame(void)
1456 {
1457 	if(cl_oldGameSet)
1458 	{
1459 		// change back to previous fs_game
1460 		cl_oldGameSet = qfalse;
1461 		Cvar_Set2("fs_game", cl_oldGame, qtrue);
1462 		FS_ConditionalRestart(clc.checksumFeed, qfalse);
1463 	}
1464 }
1465 
1466 /*
1467 =====================
1468 CL_Disconnect
1469 
1470 Called when a connection, demo, or cinematic is being terminated.
1471 Goes from a connected state to either a menu state or a console state
1472 Sends a disconnect message to the server
1473 This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors
1474 =====================
1475 */
CL_Disconnect(qboolean showMainMenu)1476 void CL_Disconnect( qboolean showMainMenu ) {
1477 	if ( !com_cl_running || !com_cl_running->integer ) {
1478 		return;
1479 	}
1480 
1481 	// shutting down the client so enter full screen ui mode
1482 	Cvar_Set( "r_uiFullScreen", "1" );
1483 
1484 	if ( clc.demorecording ) {
1485 		CL_StopRecord_f();
1486 	}
1487 
1488 	if ( clc.download ) {
1489 		FS_FCloseFile( clc.download );
1490 		clc.download = 0;
1491 	}
1492 	*clc.downloadTempName = *clc.downloadName = 0;
1493 	Cvar_Set( "cl_downloadName", "" );
1494 
1495 #ifdef USE_MUMBLE
1496 	if (cl_useMumble->integer && mumble_islinked()) {
1497 		Com_Printf("Mumble: Unlinking from Mumble application\n");
1498 		mumble_unlink();
1499 	}
1500 #endif
1501 
1502 #ifdef USE_VOIP
1503 	if (cl_voipSend->integer) {
1504 		int tmp = cl_voipUseVAD->integer;
1505 		cl_voipUseVAD->integer = 0;  // disable this for a moment.
1506 		clc.voipOutgoingDataSize = 0;  // dump any pending VoIP transmission.
1507 		Cvar_Set("cl_voipSend", "0");
1508 		CL_CaptureVoip();  // clean up any state...
1509 		cl_voipUseVAD->integer = tmp;
1510 	}
1511 
1512 	if (clc.voipCodecInitialized) {
1513 		int i;
1514 		opus_encoder_destroy(clc.opusEncoder);
1515 		for (i = 0; i < MAX_CLIENTS; i++) {
1516 			opus_decoder_destroy(clc.opusDecoder[i]);
1517 		}
1518 		clc.voipCodecInitialized = qfalse;
1519 	}
1520 	Cmd_RemoveCommand ("voip");
1521 #endif
1522 
1523 	if ( clc.demofile ) {
1524 		FS_FCloseFile( clc.demofile );
1525 		clc.demofile = 0;
1526 	}
1527 
1528 	if ( uivm && showMainMenu ) {
1529 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
1530 	}
1531 
1532 	SCR_StopCinematic();
1533 	S_ClearSoundBuffer();
1534 
1535 	// send a disconnect message to the server
1536 	// send it a few times in case one is dropped
1537 	if ( clc.state >= CA_CONNECTED ) {
1538 		CL_AddReliableCommand("disconnect", qtrue);
1539 		CL_WritePacket();
1540 		CL_WritePacket();
1541 		CL_WritePacket();
1542 	}
1543 
1544 	// Remove pure paks
1545 	FS_PureServerSetLoadedPaks("", "");
1546 	FS_PureServerSetReferencedPaks( "", "" );
1547 
1548 	CL_ClearState();
1549 
1550 	// wipe the client connection
1551 	Com_Memset( &clc, 0, sizeof( clc ) );
1552 
1553 	clc.state = CA_DISCONNECTED;
1554 
1555 	// allow cheats locally
1556 	Cvar_Set( "sv_cheats", "1" );
1557 
1558 	// not connected to a pure server anymore
1559 	cl_connectedToPureServer = qfalse;
1560 
1561 #ifdef USE_VOIP
1562 	// not connected to voip server anymore.
1563 	clc.voipEnabled = qfalse;
1564 #endif
1565 
1566 	// Stop recording any video
1567 	if( CL_VideoRecording( ) ) {
1568 		// Finish rendering current frame
1569 		SCR_UpdateScreen( );
1570 		CL_CloseAVI( );
1571 	}
1572 
1573 	CL_UpdateGUID( NULL, 0 );
1574 
1575 	if(!noGameRestart)
1576 		CL_OldGame();
1577 	else
1578 		noGameRestart = qfalse;
1579 }
1580 
1581 /*
1582 ===================
1583 CL_ForwardCommandToServer
1584 
1585 adds the current command line as a clientCommand
1586 things like godmode, noclip, etc, are commands directed to the server,
1587 so when they are typed in at the console, they will need to be forwarded.
1588 ===================
1589 */
CL_ForwardCommandToServer(const char * string)1590 void CL_ForwardCommandToServer( const char *string ) {
1591 	char	*cmd;
1592 
1593 	cmd = Cmd_Argv( 0 );
1594 
1595 	// ignore key up commands
1596 	if ( cmd[0] == '-' ) {
1597 		return;
1598 	}
1599 
1600 	if ( clc.demoplaying || clc.state < CA_CONNECTED || cmd[0] == '+' ) {
1601 		Com_Printf ("Unknown command \"%s" S_COLOR_WHITE "\"\n", cmd);
1602 		return;
1603 	}
1604 
1605 	if ( Cmd_Argc() > 1 ) {
1606 		CL_AddReliableCommand(string, qfalse);
1607 	} else {
1608 		CL_AddReliableCommand(cmd, qfalse);
1609 	}
1610 }
1611 
1612 /*
1613 ===================
1614 CL_RequestMotd
1615 
1616 ===================
1617 */
CL_RequestMotd(void)1618 void CL_RequestMotd( void ) {
1619 #ifdef UPDATE_SERVER_NAME
1620 	char	info[MAX_INFO_STRING];
1621 
1622 	if ( !cl_motd->integer ) {
1623 		return;
1624 	}
1625 	Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME );
1626 	if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer, NA_IP ) ) {
1627 		Com_Printf( "Couldn't resolve address\n" );
1628 		return;
1629 	}
1630 	cls.updateServer.port = BigShort( PORT_UPDATE );
1631 	Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME,
1632 				cls.updateServer.ip[0], cls.updateServer.ip[1],
1633 				cls.updateServer.ip[2], cls.updateServer.ip[3],
1634 				BigShort( cls.updateServer.port ) );
1635 
1636 	info[0] = 0;
1637 	Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", (int)( ( ( (unsigned int)rand() << 16 ) ^ (unsigned int)rand() ) ^ Com_Milliseconds() ) );
1638 
1639 	Info_SetValueForKey( info, "challenge", cls.updateChallenge );
1640 	Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string );
1641 	Info_SetValueForKey( info, "version", com_version->string );
1642 
1643 	NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info );
1644 #endif
1645 }
1646 
1647 /*
1648 ===================
1649 CL_RequestAuthorization
1650 
1651 Authorization server protocol
1652 -----------------------------
1653 
1654 All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff).
1655 
1656 Whenever the client tries to get a challenge from the server it wants to
1657 connect to, it also blindly fires off a packet to the authorize server:
1658 
1659 getKeyAuthorize <challenge> <cdkey>
1660 
1661 cdkey may be "demo"
1662 
1663 
1664 #OLD The authorize server returns a:
1665 #OLD
1666 #OLD keyAthorize <challenge> <accept | deny>
1667 #OLD
1668 #OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP
1669 #OLD address in the last 15 minutes.
1670 
1671 
1672 The server sends a:
1673 
1674 getIpAuthorize <challenge> <ip>
1675 
1676 The authorize server returns a:
1677 
1678 ipAuthorize <challenge> <accept | deny | demo | unknown >
1679 
1680 A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes.
1681 If no response is received from the authorize server after two tries, the client will be let
1682 in anyway.
1683 ===================
1684 */
1685 #ifndef STANDALONE
1686 #ifdef USE_AUTHORIZE_SERVER
CL_RequestAuthorization(void)1687 void CL_RequestAuthorization( void ) {
1688 	char	nums[64];
1689 	int	i, j, l;
1690 	cvar_t 	*fs;
1691 
1692 	if ( !cls.authorizeServer.port ) {
1693 		Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
1694 		if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer, NA_IP ) ) {
1695 			Com_Printf( "Couldn't resolve address\n" );
1696 			return;
1697 		}
1698 
1699 		cls.authorizeServer.port = BigShort( PORT_AUTHORIZE );
1700 		Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
1701 					cls.authorizeServer.ip[0], cls.authorizeServer.ip[1],
1702 					cls.authorizeServer.ip[2], cls.authorizeServer.ip[3],
1703 					BigShort( cls.authorizeServer.port ) );
1704 	}
1705 	if ( cls.authorizeServer.type == NA_BAD ) {
1706 		return;
1707 	}
1708 
1709 	// only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces
1710 	j = 0;
1711 	l = strlen( cl_cdkey );
1712 	if ( l > 32 ) {
1713 		l = 32;
1714 	}
1715 	for ( i = 0 ; i < l ; i++ ) {
1716 		if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' )
1717 				|| ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' )
1718 				|| ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) ) {
1719 			nums[j] = cl_cdkey[i];
1720 			j++;
1721 		}
1722 	}
1723 	nums[j] = 0;
1724 
1725 	fs = Cvar_Get( "cl_anonymous", "0", CVAR_INIT | CVAR_SYSTEMINFO );
1726 
1727 	NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, "getKeyAuthorize %i %s", fs->integer, nums );
1728 }
1729 #endif
1730 #endif
1731 
1732 /*
1733 ======================================================================
1734 
1735 CONSOLE COMMANDS
1736 
1737 ======================================================================
1738 */
1739 
1740 /*
1741 ==================
1742 CL_ForwardToServer_f
1743 ==================
1744 */
CL_ForwardToServer_f(void)1745 void CL_ForwardToServer_f( void ) {
1746 	if ( clc.state != CA_ACTIVE || clc.demoplaying ) {
1747 		Com_Printf( "Not connected to a server.\n" );
1748 		return;
1749 	}
1750 
1751 	// don't forward the first argument
1752 	if ( Cmd_Argc() > 1 ) {
1753 		CL_AddReliableCommand(Cmd_Args(), qfalse);
1754 	}
1755 }
1756 
1757 /*
1758 ==================
1759 CL_Disconnect_f
1760 ==================
1761 */
CL_Disconnect_f(void)1762 void CL_Disconnect_f( void ) {
1763 	SCR_StopCinematic();
1764 	Cvar_Set("ui_singlePlayerActive", "0");
1765 	if ( clc.state != CA_DISCONNECTED && clc.state != CA_CINEMATIC ) {
1766 		Com_Error( ERR_DISCONNECT, "Disconnected from server" );
1767 	}
1768 }
1769 
1770 
1771 /*
1772 ================
1773 CL_Reconnect_f
1774 
1775 ================
1776 */
CL_Reconnect_f(void)1777 void CL_Reconnect_f( void ) {
1778 	if ( !strlen( cl_reconnectArgs ) )
1779 		return;
1780 	Cvar_Set("ui_singlePlayerActive", "0");
1781 	Cbuf_AddText( va("connect %s\n", cl_reconnectArgs ) );
1782 }
1783 
1784 /*
1785 ================
1786 CL_Connect_f
1787 
1788 ================
1789 */
CL_Connect_f(void)1790 void CL_Connect_f( void ) {
1791 	char	server[MAX_OSPATH];
1792 	const char	*serverString;
1793 	int argc = Cmd_Argc();
1794 	netadrtype_t family = NA_UNSPEC;
1795 
1796 	if ( argc != 2 && argc != 3 ) {
1797 		Com_Printf( "usage: connect [-4|-6] server\n");
1798 		return;
1799 	}
1800 
1801 	if(argc == 2)
1802 		Q_strncpyz( server, Cmd_Argv(1), sizeof( server ) );
1803 	else
1804 	{
1805 		if(!strcmp(Cmd_Argv(1), "-4"))
1806 			family = NA_IP;
1807 		else if(!strcmp(Cmd_Argv(1), "-6"))
1808 			family = NA_IP6;
1809 		else
1810 			Com_Printf( "warning: only -4 or -6 as address type understood.\n");
1811 
1812 		Q_strncpyz( server, Cmd_Argv(2), sizeof( server ) );
1813 	}
1814 
1815 	// save arguments for reconnect
1816 	Q_strncpyz( cl_reconnectArgs, Cmd_Args(), sizeof( cl_reconnectArgs ) );
1817 
1818 	Cvar_Set("ui_singlePlayerActive", "0");
1819 
1820 	S_StopAllSounds();      // NERVE - SMF
1821 
1822 	// starting to load a map so we get out of full screen ui mode
1823 	Cvar_Set( "r_uiFullScreen", "0" );
1824 
1825 	// fire a message off to the motd server
1826 	CL_RequestMotd();
1827 
1828 	// clear any previous "server full" type messages
1829 	clc.serverMessage[0] = 0;
1830 
1831 	if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
1832 		// if running a local server, kill it
1833 		SV_Shutdown( "Server quit" );
1834 	}
1835 
1836 	// make sure a local server is killed
1837 	Cvar_Set( "sv_killserver", "1" );
1838 	SV_Frame( 0 );
1839 
1840 	noGameRestart = qtrue;
1841 	CL_Disconnect( qtrue );
1842 	Con_Close();
1843 
1844 	Q_strncpyz( clc.servername, server, sizeof(clc.servername) );
1845 
1846 	if (!NET_StringToAdr(clc.servername, &clc.serverAddress, family) ) {
1847 		Com_Printf( "Bad server address\n" );
1848 		clc.state = CA_DISCONNECTED;
1849 		return;
1850 	}
1851 	if ( clc.serverAddress.port == 0 ) {
1852 		clc.serverAddress.port = BigShort( PORT_SERVER );
1853 	}
1854 
1855 	serverString = NET_AdrToStringwPort(clc.serverAddress);
1856 
1857 	Com_Printf( "%s resolved to %s\n", clc.servername, serverString);
1858 
1859 	if( cl_guidServerUniq->integer )
1860 		CL_UpdateGUID( serverString, strlen( serverString ) );
1861 	else
1862 		CL_UpdateGUID( NULL, 0 );
1863 
1864 	// if we aren't playing on a lan, we need to authenticate
1865 	// with the cd key
1866 	if(NET_IsLocalAddress(clc.serverAddress))
1867 		clc.state = CA_CHALLENGING;
1868 	else
1869 	{
1870 		clc.state = CA_CONNECTING;
1871 
1872 		// Set a client challenge number that ideally is mirrored back by the server.
1873 		clc.challenge = ( ( (unsigned int)rand() << 16 ) ^ (unsigned int)rand() ) ^ Com_Milliseconds();
1874 	}
1875 
1876 	// show_bug.cgi?id=507
1877 	// prepare to catch a connection process that would turn bad
1878 	Cvar_Set( "com_errorDiagnoseIP", NET_AdrToString( clc.serverAddress ) );
1879 	// ATVI Wolfenstein Misc #439
1880 	// we need to setup a correct default for this, otherwise the first val we set might reappear
1881 	Cvar_Set( "com_errorMessage", "" );
1882 
1883 	Key_SetCatcher( 0 );
1884 	clc.connectTime = -99999;	// CL_CheckForResend() will fire immediately
1885 	clc.connectPacketCount = 0;
1886 
1887 	// server connection string
1888 	Cvar_Set( "cl_currentServerAddress", server );
1889 
1890 	// NERVE - SMF - reset some cvars
1891 	Cvar_Set( "mp_playerType", "0" );
1892 	Cvar_Set( "mp_currentPlayerType", "0" );
1893 	Cvar_Set( "mp_weapon", "0" );
1894 	Cvar_Set( "mp_team", "0" );
1895 	Cvar_Set( "mp_currentTeam", "0" );
1896 
1897 	Cvar_Set( "ui_limboOptions", "0" );
1898 	Cvar_Set( "ui_limboPrevOptions", "0" );
1899 	Cvar_Set( "ui_limboObjective", "0" );
1900 	// -NERVE - SMF
1901 
1902 }
1903 
1904 #define MAX_RCON_MESSAGE 1024
1905 
1906 /*
1907 ==================
1908 CL_CompleteRcon
1909 ==================
1910 */
CL_CompleteRcon(char * args,int argNum)1911 static void CL_CompleteRcon( char *args, int argNum )
1912 {
1913 	if( argNum == 2 )
1914 	{
1915 		// Skip "rcon "
1916 		char *p = Com_SkipTokens( args, 1, " " );
1917 
1918 		if( p > args )
1919 			Field_CompleteCommand( p, qtrue, qtrue );
1920 	}
1921 }
1922 
1923 /*
1924 =====================
1925 CL_Rcon_f
1926 
1927   Send the rest of the command line over as
1928   an unconnected command.
1929 =====================
1930 */
CL_Rcon_f(void)1931 void CL_Rcon_f( void ) {
1932 	char	message[MAX_RCON_MESSAGE];
1933 	netadr_t	to;
1934 
1935 	if ( !rcon_client_password->string[0] ) {
1936 		Com_Printf( "You must set 'rconpassword' before\n"
1937 					"issuing an rcon command.\n" );
1938 		return;
1939 	}
1940 
1941 	message[0] = -1;
1942 	message[1] = -1;
1943 	message[2] = -1;
1944 	message[3] = -1;
1945 	message[4] = 0;
1946 
1947 	Q_strcat (message, MAX_RCON_MESSAGE, "rcon ");
1948 
1949 	Q_strcat (message, MAX_RCON_MESSAGE, rcon_client_password->string);
1950 	Q_strcat (message, MAX_RCON_MESSAGE, " ");
1951 
1952 	Q_strcat (message, MAX_RCON_MESSAGE, Cmd_Cmd()+5);
1953 
1954 	if ( clc.state >= CA_CONNECTED ) {
1955 		to = clc.netchan.remoteAddress;
1956 	} else {
1957 		if ( !strlen( rconAddress->string ) ) {
1958 			Com_Printf( "You must either be connected,\n"
1959 						"or set the 'rconAddress' cvar\n"
1960 						"to issue rcon commands\n" );
1961 
1962 			return;
1963 		}
1964 		NET_StringToAdr (rconAddress->string, &to, NA_UNSPEC);
1965 		if ( to.port == 0 ) {
1966 			to.port = BigShort( PORT_SERVER );
1967 		}
1968 	}
1969 
1970 	NET_SendPacket( NS_CLIENT, strlen( message ) + 1, message, to );
1971 	cls.rconAddress = to;
1972 }
1973 
1974 /*
1975 =================
1976 CL_SendPureChecksums
1977 =================
1978 */
CL_SendPureChecksums(void)1979 void CL_SendPureChecksums( void ) {
1980 	char cMsg[MAX_INFO_VALUE];
1981 
1982 	// if we are pure we need to send back a command with our referenced pk3 checksums
1983 	Com_sprintf(cMsg, sizeof(cMsg), "cp %d %s", cl.serverId, FS_ReferencedPakPureChecksums());
1984 
1985 	CL_AddReliableCommand(cMsg, qfalse);
1986 }
1987 
1988 /*
1989 =================
1990 CL_ResetPureClientAtServer
1991 =================
1992 */
CL_ResetPureClientAtServer(void)1993 void CL_ResetPureClientAtServer( void ) {
1994 	CL_AddReliableCommand("vdr", qfalse);
1995 }
1996 
1997 /*
1998 =================
1999 CL_Vid_Restart_f
2000 
2001 Restart the video subsystem
2002 
2003 we also have to reload the UI and CGame because the renderer
2004 doesn't know what graphics to reload
2005 =================
2006 */
CL_Vid_Restart_f(void)2007 void CL_Vid_Restart_f( void ) {
2008 
2009 	// RF, don't show percent bar, since the memory usage will just sit at the same level anyway
2010 	Cvar_Set( "com_expectedhunkusage", "-1" );
2011 
2012 	// Settings may have changed so stop recording now
2013 	if( CL_VideoRecording( ) ) {
2014 		CL_CloseAVI( );
2015 	}
2016 
2017 	if(clc.demorecording)
2018 		CL_StopRecord_f();
2019 
2020 	// don't let them loop during the restart
2021 	S_StopAllSounds();
2022 
2023 	if(!FS_ConditionalRestart(clc.checksumFeed, qtrue))
2024 	{
2025 		// if not running a server clear the whole hunk
2026 		if(com_sv_running->integer)
2027 		{
2028 			// clear all the client data on the hunk
2029 			Hunk_ClearToMark();
2030 		}
2031 		else
2032 		{
2033 			// clear the whole hunk
2034 			Hunk_Clear();
2035 		}
2036 
2037 		// shutdown the UI
2038 		CL_ShutdownUI();
2039 		// shutdown the CGame
2040 		CL_ShutdownCGame();
2041 		// shutdown the renderer and clear the renderer interface
2042 		CL_ShutdownRef();
2043 		// client is no longer pure untill new checksums are sent
2044 		CL_ResetPureClientAtServer();
2045 		// clear pak references
2046 		FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
2047 		// reinitialize the filesystem if the game directory or checksum has changed
2048 
2049 		S_BeginRegistration();  // all sound handles are now invalid
2050 
2051 		cls.rendererStarted = qfalse;
2052 		cls.uiStarted = qfalse;
2053 		cls.cgameStarted = qfalse;
2054 		cls.soundRegistered = qfalse;
2055 		autoupdateChecked = qfalse;
2056 
2057 		// unpause so the cgame definately gets a snapshot and renders a frame
2058 		Cvar_Set( "cl_paused", "0" );
2059 
2060 		// initialize the renderer interface
2061 		CL_InitRef();
2062 
2063 		// startup all the client stuff
2064 		CL_StartHunkUsers(qfalse);
2065 
2066 		// start the cgame if connected
2067 		if(clc.state > CA_CONNECTED && clc.state != CA_CINEMATIC)
2068 		{
2069 			cls.cgameStarted = qtrue;
2070 			CL_InitCGame();
2071 			// send pure checksums
2072 			CL_SendPureChecksums();
2073 		}
2074 
2075 	}
2076 }
2077 
2078 /*
2079 =================
2080 CL_UI_Restart_f
2081 
2082 Restart the ui subsystem
2083 =================
2084 */
CL_UI_Restart_f(void)2085 void CL_UI_Restart_f( void ) {          // NERVE - SMF
2086 	// shutdown the UI
2087 	CL_ShutdownUI();
2088 
2089 	autoupdateChecked = qfalse;
2090 
2091 	// init the UI
2092 	CL_InitUI();
2093 }
2094 
2095 /*
2096 =================
2097 CL_Snd_Shutdown
2098 
2099 Shut down the sound subsystem
2100 =================
2101 */
CL_Snd_Shutdown(void)2102 void CL_Snd_Shutdown(void)
2103 {
2104 	S_Shutdown();
2105 	cls.soundStarted = qfalse;
2106 }
2107 
2108 /*
2109 =================
2110 CL_Snd_Restart_f
2111 
2112 Restart the sound subsystem
2113 The cgame and game must also be forced to restart because
2114 handles will be invalid
2115 =================
2116 */
CL_Snd_Restart_f(void)2117 void CL_Snd_Restart_f(void)
2118 {
2119 	CL_Snd_Shutdown();
2120 	// sound will be reinitialized by vid_restart
2121 	CL_Vid_Restart_f();
2122 }
2123 
2124 /*
2125 ==================
2126 CL_PK3List_f
2127 ==================
2128 */
CL_OpenedPK3List_f(void)2129 void CL_OpenedPK3List_f( void ) {
2130 	Com_Printf( "Opened PK3 Names: %s\n", FS_LoadedPakNames() );
2131 }
2132 
2133 /*
2134 ==================
2135 CL_PureList_f
2136 ==================
2137 */
CL_ReferencedPK3List_f(void)2138 void CL_ReferencedPK3List_f( void ) {
2139 	Com_Printf( "Referenced PK3 Names: %s\n", FS_ReferencedPakNames() );
2140 }
2141 
2142 /*
2143 ==================
2144 CL_Configstrings_f
2145 ==================
2146 */
CL_Configstrings_f(void)2147 void CL_Configstrings_f( void ) {
2148 	int	i;
2149 	int	ofs;
2150 
2151 	if ( clc.state != CA_ACTIVE ) {
2152 		Com_Printf( "Not connected to a server.\n" );
2153 		return;
2154 	}
2155 
2156 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
2157 		ofs = cl.gameState.stringOffsets[ i ];
2158 		if ( !ofs ) {
2159 			continue;
2160 		}
2161 		Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
2162 	}
2163 }
2164 
2165 /*
2166 ==============
2167 CL_Clientinfo_f
2168 ==============
2169 */
CL_Clientinfo_f(void)2170 void CL_Clientinfo_f( void ) {
2171 	Com_Printf( "--------- Client Information ---------\n" );
2172 	Com_Printf( "state: %i\n", clc.state );
2173 	Com_Printf( "Server: %s\n", clc.servername );
2174 	Com_Printf( "User info settings:\n" );
2175 	Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
2176 	Com_Printf( "--------------------------------------\n" );
2177 }
2178 
2179 
2180 //====================================================================
2181 
2182 /*
2183 =================
2184 CL_DownloadsComplete
2185 
2186 Called when all downloading has been completed
2187 =================
2188 */
CL_DownloadsComplete(void)2189 void CL_DownloadsComplete( void ) {
2190 
2191 #ifndef _WIN32
2192 	char    *fs_write_path;
2193 #endif
2194 	char    *fn;
2195 
2196 	// DHM - Nerve :: Auto-update (not finished yet)
2197 	if ( autoupdateStarted ) {
2198 
2199 		if ( strlen( autoupdateFilename ) > 4 )  {
2200 #ifdef _WIN32
2201 			// win32's Sys_StartProcess prepends the current dir
2202 			fn = va( "%s/%s", FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT ), autoupdateFilename );
2203 #else
2204 			fs_write_path = Cvar_VariableString( "fs_homepath" );
2205 			fn = FS_BuildOSPath( fs_write_path, FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT ), autoupdateFilename );
2206 #ifdef __linux__
2207 			Sys_Chmod( fn, S_IXUSR );
2208 #endif
2209 #endif
2210 			Sys_StartProcess( fn, qtrue );
2211 		}
2212 
2213 		autoupdateStarted = qfalse;
2214 		CL_Disconnect( qtrue );
2215 		return;
2216 	}
2217 
2218 #ifdef USE_CURL
2219 	// if we downloaded with cURL
2220 	if(clc.cURLUsed) {
2221 		clc.cURLUsed = qfalse;
2222 		CL_cURL_Shutdown();
2223 		if( clc.cURLDisconnected ) {
2224 			if(clc.downloadRestart) {
2225 				FS_Restart(clc.checksumFeed);
2226 				clc.downloadRestart = qfalse;
2227 			}
2228 			clc.cURLDisconnected = qfalse;
2229 			CL_Reconnect_f();
2230 			return;
2231 		}
2232 	}
2233 #endif
2234 
2235 	// if we downloaded files we need to restart the file system
2236 	if ( clc.downloadRestart ) {
2237 		clc.downloadRestart = qfalse;
2238 
2239 		FS_Restart( clc.checksumFeed ); // We possibly downloaded a pak, restart the file system to load it
2240 
2241 		// inform the server so we get new gamestate info
2242 		CL_AddReliableCommand( "donedl", qfalse );
2243 
2244 		// by sending the donedl command we request a new gamestate
2245 		// so we don't want to load stuff yet
2246 		return;
2247 	}
2248 
2249 	// let the client game init and load data
2250 	clc.state = CA_LOADING;
2251 
2252 	Com_EventLoop();
2253 
2254 	// if the gamestate was changed by calling Com_EventLoop
2255 	// then we loaded everything already and we don't want to do it again.
2256 	if ( clc.state != CA_LOADING ) {
2257 		return;
2258 	}
2259 
2260 	// starting to load a map so we get out of full screen ui mode
2261 	Cvar_Set( "r_uiFullScreen", "0" );
2262 
2263 	// flush client memory and start loading stuff
2264 	// this will also (re)load the UI
2265 	// if this is a local client then only the client part of the hunk
2266 	// will be cleared, note that this is done after the hunk mark has been set
2267 	CL_FlushMemory();
2268 
2269 	// initialize the CGame
2270 	cls.cgameStarted = qtrue;
2271 	CL_InitCGame();
2272 
2273 	// set pure checksums
2274 	CL_SendPureChecksums();
2275 
2276 	CL_WritePacket();
2277 	CL_WritePacket();
2278 	CL_WritePacket();
2279 }
2280 
2281 /*
2282 =================
2283 CL_BeginDownload
2284 
2285 Requests a file to download from the server.  Stores it in the current
2286 game directory.
2287 =================
2288 */
CL_BeginDownload(const char * localName,const char * remoteName)2289 void CL_BeginDownload( const char *localName, const char *remoteName ) {
2290 
2291 	Com_DPrintf( "***** CL_BeginDownload *****\n"
2292 				 "Localname: %s\n"
2293 				 "Remotename: %s\n"
2294 				 "****************************\n", localName, remoteName );
2295 
2296 	Q_strncpyz( clc.downloadName, localName, sizeof( clc.downloadName ) );
2297 	Com_sprintf( clc.downloadTempName, sizeof( clc.downloadTempName ), "%s.tmp", localName );
2298 
2299 	// Set so UI gets access to it
2300 	Cvar_Set( "cl_downloadName", remoteName );
2301 	Cvar_Set( "cl_downloadSize", "0" );
2302 	Cvar_Set( "cl_downloadCount", "0" );
2303 	Cvar_SetValue( "cl_downloadTime", cls.realtime );
2304 
2305 	clc.downloadBlock = 0; // Starting new file
2306 	clc.downloadCount = 0;
2307 
2308 	CL_AddReliableCommand( va( "download %s", remoteName ), qfalse );
2309 }
2310 
2311 /*
2312 =================
2313 CL_NextDownload
2314 
2315 A download completed or failed
2316 =================
2317 */
CL_NextDownload(void)2318 void CL_NextDownload( void ) {
2319 	char *s;
2320 	char *remoteName, *localName;
2321 	qboolean useCURL = qfalse;
2322 
2323 	// A download has finished, check whether this matches a referenced checksum
2324 	if( *clc.downloadName && !autoupdateStarted ) {
2325 		char *zippath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), clc.downloadName, "");
2326 		zippath[strlen(zippath)-1] = '\0';
2327 
2328 		if(!FS_CompareZipChecksum(zippath))
2329 			Com_Error(ERR_DROP, "Incorrect checksum for file: %s", clc.downloadName);
2330 	}
2331 
2332 	*clc.downloadTempName = *clc.downloadName = 0;
2333 	Cvar_Set("cl_downloadName", "");
2334 
2335 	// We are looking to start a download here
2336 	if ( *clc.downloadList ) {
2337 		s = clc.downloadList;
2338 
2339 		// format is:
2340 		//  @remotename@localname@remotename@localname, etc.
2341 
2342 		if ( *s == '@' ) {
2343 			s++;
2344 		}
2345 		remoteName = s;
2346 
2347 		if ( ( s = strchr( s, '@' ) ) == NULL ) {
2348 			CL_DownloadsComplete();
2349 			return;
2350 		}
2351 
2352 		*s++ = 0;
2353 		localName = s;
2354 		if ( ( s = strchr( s, '@' ) ) != NULL ) {
2355 			*s++ = 0;
2356 		} else {
2357 			s = localName + strlen( localName ); // point at the nul byte
2358 
2359 		}
2360 
2361 #ifdef USE_CURL
2362 		if(!(cl_allowDownload->integer & DLF_NO_REDIRECT)) {
2363 			if(clc.sv_allowDownload & DLF_NO_REDIRECT) {
2364 				Com_Printf("WARNING: server does not "
2365 					"allow download redirection "
2366 					"(sv_allowDownload is %d)\n",
2367 					clc.sv_allowDownload);
2368 			}
2369 			else if(!*clc.sv_dlURL) {
2370 				Com_Printf("WARNING: server allows "
2371 					"download redirection, but does not "
2372 					"have sv_dlURL set\n");
2373 			}
2374 			else if(!CL_cURL_Init()) {
2375 				Com_Printf("WARNING: could not load "
2376 					"cURL library\n");
2377 			}
2378 			else {
2379 				CL_cURL_BeginDownload(localName, va("%s/%s",
2380 					clc.sv_dlURL, remoteName));
2381 				useCURL = qtrue;
2382 			}
2383 		}
2384 		else if(!(clc.sv_allowDownload & DLF_NO_REDIRECT)) {
2385 			Com_Printf("WARNING: server allows download "
2386 				"redirection, but it disabled by client "
2387 				"configuration (cl_allowDownload is %d)\n",
2388 				cl_allowDownload->integer);
2389 		}
2390 #endif /* USE_CURL */
2391 		if(!useCURL) {
2392 			if((cl_allowDownload->integer & DLF_NO_UDP)) {
2393 				Com_Error(ERR_DROP, "UDP Downloads are "
2394 					"disabled on your client. "
2395 					"(cl_allowDownload is %d)",
2396 					cl_allowDownload->integer);
2397 				return;
2398 			}
2399 			else {
2400 				CL_BeginDownload( localName, remoteName );
2401 			}
2402 		}
2403 		clc.downloadRestart = qtrue;
2404 
2405 		// move over the rest
2406 		memmove( clc.downloadList, s, strlen( s ) + 1 );
2407 
2408 		return;
2409 	}
2410 
2411 	CL_DownloadsComplete();
2412 }
2413 
2414 /*
2415 =================
2416 CL_InitDownloads
2417 
2418 After receiving a valid game state, we valid the cgame and local zip files here
2419 and determine if we need to download them
2420 =================
2421 */
CL_InitDownloads(void)2422 void CL_InitDownloads( void ) {
2423 #ifndef PRE_RELEASE_DEMO
2424 	char missingfiles[1024];
2425 	char *dir = FS_ShiftStr( AUTOUPDATE_DIR, AUTOUPDATE_DIR_SHIFT );
2426 
2427 	if ( autoupdateStarted && NET_CompareAdr( cls.autoupdateServer, clc.serverAddress ) ) {
2428 		if ( strlen( cl_updatefiles->string ) > 4 ) {
2429 			Q_strncpyz( autoupdateFilename, cl_updatefiles->string, sizeof( autoupdateFilename ) );
2430 			Q_strncpyz( clc.downloadList, va( "@%s/%s@%s/%s", dir, cl_updatefiles->string, dir, cl_updatefiles->string ), MAX_INFO_STRING );
2431 			clc.state = CA_CONNECTED;
2432 			CL_NextDownload();
2433 			return;
2434 		}
2435 	} else {
2436 		if ( !(cl_allowDownload->integer & DLF_ENABLE) ) {
2437 			// autodownload is disabled on the client
2438 			// but it's possible that some referenced files on the server are missing
2439 
2440 			// whatever autodownlad configuration, store missing files in a cvar, use later in the ui maybe
2441 			if ( FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) ) {
2442 				Cvar_Set( "com_missingFiles", missingfiles );
2443 			} else {
2444 				Cvar_Set( "com_missingFiles", "" );
2445 			}
2446 			Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
2447 						"You might not be able to join the game\n"
2448 						"Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
2449 		}
2450 		else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ), qtrue ) ) {
2451 			// this gets printed to UI, i18n
2452 			Com_Printf( CL_TranslateStringBuf( "Need paks: %s\n" ), clc.downloadList );
2453 
2454 			if ( *clc.downloadList ) {
2455 				// if autodownloading is not enabled on the server
2456 				clc.state = CA_CONNECTED;
2457 
2458 				*clc.downloadTempName = *clc.downloadName = 0;
2459 				Cvar_Set( "cl_downloadName", "" );
2460 
2461 				CL_NextDownload();
2462 				return;
2463 			}
2464 		}
2465 	}
2466 #endif
2467 
2468 	CL_DownloadsComplete();
2469 }
2470 
2471 /*
2472 =================
2473 CL_CheckForResend
2474 
2475 Resend a connect message if the last one has timed out
2476 =================
2477 */
CL_CheckForResend(void)2478 void CL_CheckForResend( void ) {
2479 	int		port;
2480 	char	info[MAX_INFO_STRING];
2481 	char	data[MAX_INFO_STRING + 10];
2482 
2483 	// don't send anything if playing back a demo
2484 	if ( clc.demoplaying ) {
2485 		return;
2486 	}
2487 
2488 	// resend if we haven't gotten a reply yet
2489 	if ( clc.state != CA_CONNECTING && clc.state != CA_CHALLENGING ) {
2490 		return;
2491 	}
2492 
2493 	if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
2494 		return;
2495 	}
2496 
2497 	clc.connectTime = cls.realtime;	// for retransmit requests
2498 	clc.connectPacketCount++;
2499 
2500 
2501 	switch ( clc.state ) {
2502 	case CA_CONNECTING:
2503 		// requesting a challenge .. IPv6 users always get in as authorize server supports no ipv6.
2504 #ifndef STANDALONE
2505 #ifdef USE_AUTHORIZE_SERVER
2506 		if (!com_standalone->integer && clc.serverAddress.type == NA_IP && !Sys_IsLANAddress( clc.serverAddress ) )
2507 			CL_RequestAuthorization();
2508 #endif
2509 #endif
2510 
2511 		// The challenge request shall be followed by a client challenge so no malicious server can hijack this connection.
2512 		// Add the gamename so the server knows we're running the correct game or can reject the client
2513 		// with a meaningful message
2514 		Com_sprintf(data, sizeof(data), "getchallenge %d %s", clc.challenge, com_gamename->string);
2515 
2516 		NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "%s", data);
2517 		break;
2518 
2519 	case CA_CHALLENGING:
2520 		// sending back the challenge
2521 		port = Cvar_VariableValue( "net_qport" );
2522 
2523 		Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
2524 
2525 #ifdef LEGACY_PROTOCOL
2526 		if(com_legacyprotocol->integer == com_protocol->integer)
2527 			clc.compat = qtrue;
2528 
2529 		if(clc.compat)
2530 			Info_SetValueForKey(info, "protocol", va("%i", com_legacyprotocol->integer));
2531 		else
2532 #endif
2533 		Info_SetValueForKey( info, "protocol", va("%i", com_protocol->integer ) );
2534 		Info_SetValueForKey( info, "qport", va( "%i", port ) );
2535 		Info_SetValueForKey( info, "challenge", va( "%i", clc.challenge ) );
2536 
2537 		Com_sprintf( data, sizeof(data), "connect \"%s\"", info );
2538 		NET_OutOfBandData( NS_CLIENT, clc.serverAddress, (byte *) data, strlen ( data ) );
2539 		// the most current userinfo has been sent, so watch for any
2540 		// newer changes to userinfo variables
2541 		cvar_modifiedFlags &= ~CVAR_USERINFO;
2542 		break;
2543 
2544 	default:
2545 		Com_Error( ERR_FATAL, "CL_CheckForResend: bad clc.state" );
2546 	}
2547 }
2548 
2549 /*
2550 ===================
2551 CL_MotdPacket
2552 
2553 ===================
2554 */
CL_MotdPacket(netadr_t from)2555 void CL_MotdPacket( netadr_t from ) {
2556 #ifdef UPDATE_SERVER_NAME
2557 	char	*challenge;
2558 	char	*info;
2559 
2560 	// if not from our server, ignore it
2561 	if ( !NET_CompareAdr( from, cls.updateServer ) ) {
2562 		return;
2563 	}
2564 
2565 	info = Cmd_Argv( 1 );
2566 
2567 	// check challenge
2568 	challenge = Info_ValueForKey( info, "challenge" );
2569 	if ( strcmp( challenge, cls.updateChallenge ) ) {
2570 		return;
2571 	}
2572 
2573 	challenge = Info_ValueForKey( info, "motd" );
2574 
2575 	Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) );
2576 	Cvar_Set( "cl_motdString", challenge );
2577 #endif
2578 }
2579 
2580 /*
2581 ===================
2582 CL_PrintPackets
2583 an OOB message from server, with potential markups
2584 print OOB are the only messages we handle markups in
2585 [err_dialog]: used to indicate that the connection should be aborted
2586   no further information, just do an error diagnostic screen afterwards
2587 [err_prot]: HACK. This is a protocol error. The client uses a custom
2588   protocol error message (client sided) in the diagnostic window.
2589   The space for the error message on the connection screen is limited
2590   to 256 chars.
2591 ===================
2592 */
CL_PrintPacket(netadr_t from,msg_t * msg)2593 void CL_PrintPacket( netadr_t from, msg_t *msg ) {
2594 	char *s;
2595 	s = MSG_ReadBigString( msg );
2596 	if ( !Q_stricmpn( s, "[err_dialog]", 12 ) ) {
2597 		Q_strncpyz( clc.serverMessage, s + 12, sizeof( clc.serverMessage ) );
2598 		Cvar_Set( "com_errorMessage", clc.serverMessage );
2599 	} else if ( !Q_stricmpn( s, "[err_prot]", 10 ) )    {
2600 		Q_strncpyz( clc.serverMessage, s + 10, sizeof( clc.serverMessage ) );
2601 		Cvar_Set( "com_errorMessage", CL_TranslateStringBuf( PROTOCOL_MISMATCH_ERROR_LONG ) );
2602 	} else {
2603 		Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
2604 	}
2605 	Com_Printf( "%s", clc.serverMessage );
2606 }
2607 
2608 /*
2609 ===================
2610 CL_InitServerInfo
2611 ===================
2612 */
CL_InitServerInfo(serverInfo_t * server,netadr_t * address)2613 void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
2614 	server->adr = *address;
2615 	server->clients = 0;
2616 	server->hostName[0] = '\0';
2617 	server->mapName[0] = '\0';
2618 	server->maxClients = 0;
2619 	server->maxPing = 0;
2620 	server->minPing = 0;
2621 	server->ping = -1;
2622 	server->game[0] = '\0';
2623 	server->gameType = 0;
2624 	server->netType = 0;
2625 	server->allowAnonymous = 0;
2626 	server->friendlyFire = 0;           // NERVE - SMF
2627 	server->maxlives = 0;               // NERVE - SMF
2628 	server->tourney = 0;                // NERVE - SMF
2629 	server->punkbuster = 0;             // DHM - Nerve
2630 	server->gameName[0] = '\0';           // Arnout
2631 	server->antilag = 0;
2632 	server->g_humanplayers = 0;
2633 	server->g_needpass = 0;
2634 }
2635 
2636 #define MAX_SERVERSPERPACKET    256
2637 
2638 /*
2639 ===================
2640 CL_ServersResponsePacket
2641 ===================
2642 */
CL_ServersResponsePacket(const netadr_t * from,msg_t * msg,qboolean extended)2643 void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extended ) {
2644 	int				i, j, count, total;
2645 	netadr_t addresses[MAX_SERVERSPERPACKET];
2646 	int	numservers;
2647 	byte*	buffptr;
2648 	byte*	buffend;
2649 
2650 	Com_Printf( "CL_ServersResponsePacket from %s\n", NET_AdrToStringwPort( *from ) );
2651 
2652 	if ( cls.numglobalservers == -1 ) {
2653 		// state to detect lack of servers or lack of response
2654 		cls.numglobalservers = 0;
2655 		cls.numGlobalServerAddresses = 0;
2656 	}
2657 
2658 	// parse through server response string
2659 	numservers = 0;
2660 	buffptr    = msg->data;
2661 	buffend    = buffptr + msg->cursize;
2662 
2663 	// advance to initial token
2664 	do
2665 	{
2666 		if(*buffptr == '\\' || (extended && *buffptr == '/'))
2667 			break;
2668 
2669 		buffptr++;
2670 	} while (buffptr < buffend);
2671 
2672 	while (buffptr + 1 < buffend)
2673 	{
2674 		// IPv4 address
2675 		if (*buffptr == '\\')
2676 		{
2677 			buffptr++;
2678 
2679 			if (buffend - buffptr < sizeof(addresses[numservers].ip) + sizeof(addresses[numservers].port) + 1)
2680 				break;
2681 
2682 			for(i = 0; i < sizeof(addresses[numservers].ip); i++)
2683 				addresses[numservers].ip[i] = *buffptr++;
2684 
2685 			addresses[numservers].type = NA_IP;
2686 		}
2687 		// IPv6 address, if it's an extended response
2688 		else if (extended && *buffptr == '/')
2689 		{
2690 			buffptr++;
2691 
2692 			if (buffend - buffptr < sizeof(addresses[numservers].ip6) + sizeof(addresses[numservers].port) + 1)
2693 				break;
2694 
2695 			for(i = 0; i < sizeof(addresses[numservers].ip6); i++)
2696 				addresses[numservers].ip6[i] = *buffptr++;
2697 
2698 			addresses[numservers].type = NA_IP6;
2699 			addresses[numservers].scope_id = from->scope_id;
2700 		}
2701 		else
2702 			// syntax error!
2703 			break;
2704 
2705 		// parse out port
2706 		addresses[numservers].port = (*buffptr++) << 8;
2707 		addresses[numservers].port += *buffptr++;
2708 		addresses[numservers].port = BigShort( addresses[numservers].port );
2709 
2710 		// syntax check
2711 		if (*buffptr != '\\' && *buffptr != '/')
2712 			break;
2713 
2714 		numservers++;
2715 
2716 		if (numservers >= MAX_SERVERSPERPACKET)
2717 			break;
2718 	}
2719 
2720 	count = cls.numglobalservers;
2721 
2722 	for (i = 0; i < numservers && count < MAX_GLOBAL_SERVERS; i++) {
2723 		// build net address
2724 		serverInfo_t *server = &cls.globalServers[count];
2725 
2726 		// Tequila: It's possible to have sent many master server requests. Then
2727 		// we may receive many times the same addresses from the master server.
2728 		// We just avoid to add a server if it is still in the global servers list.
2729 		for (j = 0; j < count; j++)
2730 		{
2731 			if (NET_CompareAdr(cls.globalServers[j].adr, addresses[i]))
2732 				break;
2733 		}
2734 
2735 		if (j < count)
2736 			continue;
2737 
2738 		CL_InitServerInfo( server, &addresses[i] );
2739 		// advance to next slot
2740 		count++;
2741 	}
2742 
2743 	// if getting the global list
2744 	if ( count >= MAX_GLOBAL_SERVERS && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS )
2745 	{
2746 		// if we couldn't store the servers in the main list anymore
2747 		for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++)
2748 		{
2749 			// just store the addresses in an additional list
2750 			cls.globalServerAddresses[cls.numGlobalServerAddresses++] = addresses[i];
2751 		}
2752 	}
2753 
2754 	cls.numglobalservers = count;
2755 	total = count + cls.numGlobalServerAddresses;
2756 
2757 	Com_Printf( "%d servers parsed (total %d)\n", numservers, total );
2758 }
2759 
2760 /*
2761 =================
2762 CL_ConnectionlessPacket
2763 
2764 Responses to broadcasts, etc
2765 =================
2766 */
CL_ConnectionlessPacket(netadr_t from,msg_t * msg)2767 void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
2768 	char	*s;
2769 	char	*c;
2770 	int challenge = 0;
2771 
2772 	MSG_BeginReadingOOB( msg );
2773 	MSG_ReadLong( msg );	// skip the -1
2774 
2775 	s = MSG_ReadStringLine( msg );
2776 
2777 	Cmd_TokenizeString( s );
2778 
2779 	c = Cmd_Argv( 0 );
2780 
2781 	Com_DPrintf ("CL packet %s: %s\n", NET_AdrToStringwPort(from), c);
2782 
2783 	// challenge from the server we are connecting to
2784 	if (!Q_stricmp(c, "challengeResponse"))
2785 	{
2786 		char *strver;
2787 		int ver;
2788 
2789 		if (clc.state != CA_CONNECTING)
2790 		{
2791 			Com_DPrintf("Unwanted challenge response received. Ignored.\n");
2792 			return;
2793 		}
2794 
2795 		c = Cmd_Argv( 3 );
2796 		if(*c)
2797 			challenge = atoi(c);
2798 
2799 		strver = Cmd_Argv( 4 );
2800 		if(*strver)
2801 		{
2802 			ver = atoi(strver);
2803 
2804 			if(ver != com_protocol->integer)
2805 			{
2806 #ifdef LEGACY_PROTOCOL
2807 				if(com_legacyprotocol->integer > 0)
2808 				{
2809 					// Server is ioq3 but has a different protocol than we do.
2810 					// Fall back to idq3 protocol.
2811 					clc.compat = qtrue;
2812 
2813 					Com_Printf(S_COLOR_YELLOW "Warning: Server reports protocol version %d, "
2814 						   "we have %d. Trying legacy protocol %d.\n",
2815 						   ver, com_protocol->integer, com_legacyprotocol->integer);
2816 				}
2817 				else
2818 #endif
2819 				{
2820 					Com_Printf(S_COLOR_YELLOW "Warning: Server reports protocol version %d, we have %d. "
2821 						   "Trying anyways.\n", ver, com_protocol->integer);
2822 				}
2823 			}
2824 		}
2825 #ifdef LEGACY_PROTOCOL
2826 		else
2827 			clc.compat = qtrue;
2828 
2829 		if(clc.compat)
2830 		{
2831 			if(!NET_CompareAdr(from, clc.serverAddress))
2832 			{
2833 				// This challenge response is not coming from the expected address.
2834 				// Check whether we have a matching client challenge to prevent
2835 				// connection hi-jacking.
2836 
2837 				if(!*c || challenge != clc.challenge)
2838 				{
2839 					Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
2840 					return;
2841 				}
2842 			}
2843 		}
2844 		else
2845 #endif
2846 		{
2847 			if(!*c || challenge != clc.challenge)
2848 			{
2849 				Com_Printf("Bad challenge for challengeResponse. Ignored.\n");
2850 				return;
2851 			}
2852 		}
2853 
2854 		// start sending challenge response instead of challenge request packets
2855 		clc.challenge = atoi(Cmd_Argv(1));
2856 		if ( Cmd_Argc() > 2 ) {
2857 			clc.onlyVisibleClients = atoi( Cmd_Argv( 2 ) );         // DHM - Nerve
2858 		} else {
2859 			clc.onlyVisibleClients = 0;
2860 		}
2861 		clc.state = CA_CHALLENGING;
2862 		clc.connectPacketCount = 0;
2863 		clc.connectTime = -99999;
2864 
2865 		// take this address as the new server address.  This allows
2866 		// a server proxy to hand off connections to multiple servers
2867 		clc.serverAddress = from;
2868 		Com_DPrintf ("challengeResponse: %d\n", clc.challenge);
2869 		return;
2870 	}
2871 
2872 	// server connection
2873 	if ( !Q_stricmp( c, "connectResponse" ) ) {
2874 		if ( clc.state >= CA_CONNECTED ) {
2875 			Com_Printf( "Dup connect received. Ignored.\n" );
2876 			return;
2877 		}
2878 		if ( clc.state != CA_CHALLENGING ) {
2879 			Com_Printf( "connectResponse packet while not connecting. Ignored.\n" );
2880 			return;
2881 		}
2882 		if ( !NET_CompareAdr( from, clc.serverAddress ) ) {
2883 			Com_Printf( "connectResponse from wrong address. Ignored.\n" );
2884 			return;
2885 		}
2886 
2887 #ifdef LEGACY_PROTOCOL
2888 		if(!clc.compat)
2889 #endif
2890 		{
2891 			c = Cmd_Argv(1);
2892 
2893 			if(*c)
2894 				challenge = atoi(c);
2895 			else
2896 			{
2897 				Com_Printf("Bad connectResponse received. Ignored.\n");
2898 				return;
2899 			}
2900 
2901 			if(challenge != clc.challenge)
2902 			{
2903 				Com_Printf("ConnectResponse with bad challenge received. Ignored.\n");
2904 				return;
2905 			}
2906 		}
2907 
2908 		// DHM - Nerve :: If we have completed a connection to the Auto-Update server...
2909 		if ( autoupdateChecked && NET_CompareAdr( cls.autoupdateServer, clc.serverAddress ) ) {
2910 			// Mark the client as being in the process of getting an update
2911 			if ( cl_updateavailable->integer ) {
2912 				autoupdateStarted = qtrue;
2913 			}
2914 		}
2915 
2916 #ifdef LEGACY_PROTOCOL
2917 		Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
2918 			      clc.challenge, clc.compat);
2919 #else
2920 		Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
2921 			      clc.challenge, qfalse);
2922 #endif
2923 
2924 		clc.state = CA_CONNECTED;
2925 		clc.lastPacketSentTime = -9999;	// send first packet immediately
2926 		return;
2927 	}
2928 
2929 	// server responding to an info broadcast
2930 	if ( !Q_stricmp( c, "infoResponse" ) ) {
2931 		CL_ServerInfoPacket( from, msg );
2932 		return;
2933 	}
2934 
2935 	// server responding to a get playerlist
2936 	if ( !Q_stricmp( c, "statusResponse" ) ) {
2937 		CL_ServerStatusResponse( from, msg );
2938 		return;
2939 	}
2940 
2941 	// echo request from server
2942 	if ( !Q_stricmp( c, "echo" ) ) {
2943 #ifdef UPDATE_SERVER
2944 		NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv( 1 ) );
2945 #else
2946 		// NOTE: we may have to add exceptions for auth and update servers
2947 		if ( NET_CompareAdr( from, clc.serverAddress ) || NET_CompareAdr( from, cls.rconAddress ) ) {
2948 			NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
2949 		}
2950 #endif
2951 		return;
2952 	}
2953 
2954 	// cd check
2955 	if ( !Q_stricmp( c, "keyAuthorize" ) ) {
2956 		// we don't use these now, so dump them on the floor
2957 		return;
2958 	}
2959 
2960 	// global MOTD from id
2961 	if ( !Q_stricmp( c, "motd" ) ) {
2962 		CL_MotdPacket( from );
2963 		return;
2964 	}
2965 
2966 	// echo request from server
2967 	if ( !Q_stricmp( c, "print" ) ) {
2968 #ifdef UPDATE_SERVER
2969 		s = MSG_ReadString( msg );
2970 
2971 		Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
2972 		Com_Printf( "%s", s );
2973 #else
2974 		// NOTE: we may have to add exceptions for auth and update servers
2975 		if ( NET_CompareAdr( from, clc.serverAddress ) || NET_CompareAdr( from, cls.rconAddress ) ) {
2976 			s = MSG_ReadString( msg );
2977 
2978 			Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
2979 			Com_Printf( "%s", s );
2980 		}
2981 #endif
2982 		return;
2983 	}
2984 
2985 	// DHM - Nerve :: Auto-update server response message
2986 	if ( !Q_stricmp( c, "updateResponse" ) ) {
2987 		CL_UpdateInfoPacket( from );
2988 		return;
2989 	}
2990 
2991 	// list of servers sent back by a master server (classic)
2992 	if ( !Q_strncmp( c, "getserversResponse", 18 ) ) {
2993 		CL_ServersResponsePacket( &from, msg, qfalse );
2994 		return;
2995 	}
2996 
2997 	// list of servers sent back by a master server (extended)
2998 	if ( !Q_strncmp(c, "getserversExtResponse", 21) ) {
2999 		CL_ServersResponsePacket( &from, msg, qtrue );
3000 		return;
3001 	}
3002 
3003 	Com_DPrintf( "Unknown connectionless packet command.\n" );
3004 }
3005 
3006 /*
3007 =================
3008 CL_PacketEvent
3009 
3010 A packet has arrived from the main event loop
3011 =================
3012 */
CL_PacketEvent(netadr_t from,msg_t * msg)3013 void CL_PacketEvent( netadr_t from, msg_t *msg ) {
3014 	int	headerBytes;
3015 
3016 	clc.lastPacketTime = cls.realtime;
3017 
3018 	if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
3019 		CL_ConnectionlessPacket( from, msg );
3020 		return;
3021 	}
3022 
3023 	if ( clc.state < CA_CONNECTED ) {
3024 		return;	// can't be a valid sequenced packet
3025 	}
3026 
3027 	if ( msg->cursize < 4 ) {
3028 		Com_Printf ("%s: Runt packet\n", NET_AdrToStringwPort( from ));
3029 		return;
3030 	}
3031 
3032 	//
3033 	// packet from server
3034 	//
3035 	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
3036 		Com_DPrintf( "%s:sequenced packet without connection\n"
3037 			, NET_AdrToStringwPort( from ) );
3038 		// FIXME: send a client disconnect?
3039 		return;
3040 	}
3041 
3042 	if ( !CL_Netchan_Process( &clc.netchan, msg ) ) {
3043 		return;	// out of order, duplicated, etc
3044 	}
3045 
3046 	// the header is different lengths for reliable and unreliable messages
3047 	headerBytes = msg->readcount;
3048 
3049 	// track the last message received so it can be returned in
3050 	// client messages, allowing the server to detect a dropped
3051 	// gamestate
3052 	clc.serverMessageSequence = LittleLong( *(int *)msg->data );
3053 
3054 	clc.lastPacketTime = cls.realtime;
3055 	CL_ParseServerMessage( msg );
3056 
3057 	//
3058 	// we don't know if it is ok to save a demo message until
3059 	// after we have parsed the frame
3060 	//
3061 	if ( clc.demorecording && !clc.demowaiting ) {
3062 		CL_WriteDemoMessage( msg, headerBytes );
3063 	}
3064 }
3065 
3066 /*
3067 ==================
3068 CL_CheckTimeout
3069 
3070 ==================
3071 */
CL_CheckTimeout(void)3072 void CL_CheckTimeout( void ) {
3073 	//
3074 	// check timeout
3075 	//
3076 	if ( ( !CL_CheckPaused() || !sv_paused->integer )
3077 		&& clc.state >= CA_CONNECTED && clc.state != CA_CINEMATIC
3078 		&& cls.realtime - clc.lastPacketTime > cl_timeout->value * 1000 ) {
3079 		if ( ++cl.timeoutcount > 5 ) {	// timeoutcount saves debugger
3080 			Com_Printf( "\nServer connection timed out.\n" );
3081 			CL_Disconnect( qtrue );
3082 			return;
3083 		}
3084 	} else {
3085 		cl.timeoutcount = 0;
3086 	}
3087 }
3088 
3089 /*
3090 ==================
3091 CL_CheckPaused
3092 Check whether client has been paused.
3093 ==================
3094 */
CL_CheckPaused(void)3095 qboolean CL_CheckPaused(void)
3096 {
3097 	// if cl_paused->modified is set, the cvar has only been changed in
3098 	// this frame. Keep paused in this frame to ensure the server doesn't
3099 	// lag behind.
3100 	if(cl_paused->integer || cl_paused->modified)
3101 		return qtrue;
3102 
3103 	return qfalse;
3104 }
3105 
3106 
3107 //============================================================================
3108 
3109 /*
3110 ==================
3111 CL_CheckUserinfo
3112 
3113 ==================
3114 */
CL_CheckUserinfo(void)3115 void CL_CheckUserinfo( void ) {
3116 	// don't add reliable commands when not yet connected
3117 	if(clc.state < CA_CONNECTED)
3118 		return;
3119 
3120 	// don't overflow the reliable command buffer when paused
3121 	if(CL_CheckPaused())
3122 		return;
3123 
3124 	// send a reliable userinfo update if needed
3125 	if ( cvar_modifiedFlags & CVAR_USERINFO ) {
3126 		cvar_modifiedFlags &= ~CVAR_USERINFO;
3127 		CL_AddReliableCommand(va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ), qfalse);
3128 	}
3129 }
3130 
3131 /*
3132 ==================
3133 CL_Frame
3134 
3135 ==================
3136 */
CL_Frame(int msec)3137 void CL_Frame( int msec ) {
3138 
3139 	if ( !com_cl_running->integer ) {
3140 		return;
3141 	}
3142 
3143 #ifdef USE_CURL
3144 	if(clc.downloadCURLM) {
3145 		CL_cURL_PerformDownload();
3146 		// we can't process frames normally when in disconnected
3147 		// download mode since the ui vm expects clc.state to be
3148 		// CA_CONNECTED
3149 		if(clc.cURLDisconnected) {
3150 			cls.realFrametime = msec;
3151 			cls.frametime = msec;
3152 			cls.realtime += cls.frametime;
3153 			SCR_UpdateScreen();
3154 			S_Update();
3155 			Con_RunConsole();
3156 			cls.framecount++;
3157 			return;
3158 		}
3159 	}
3160 #endif
3161 
3162 	if ( cls.cddialog ) {
3163 		// bring up the cd error dialog if needed
3164 		cls.cddialog = qfalse;
3165 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD );
3166 	} else	if ( clc.state == CA_DISCONNECTED && !( Key_GetCatcher( ) & KEYCATCH_UI )
3167 		&& !com_sv_running->integer && uivm ) {
3168 		// if disconnected, bring up the menu
3169 		S_StopAllSounds();
3170 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
3171 	}
3172 
3173 	// if recording an avi, lock to a fixed fps
3174 	if ( ( CL_VideoRecording( ) && cl_aviFrameRate->integer && msec ) || ( cl_avidemo->integer && msec ) ) {
3175 		// save the current screen
3176 		if ( clc.state == CA_ACTIVE || cl_forceavidemo->integer ) {
3177 			if ( cl_avidemo->integer ) {	// Legacy (screenshot) method
3178 				Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" );
3179 
3180 				// fixed time for next frame
3181 				msec = ( 1000 / cl_avidemo->integer ) * com_timescale->value;
3182 				if ( msec == 0 ) {
3183 					msec = 1;
3184 				}
3185 			} else {			// ioquake3 method
3186 				float fps = MIN(cl_aviFrameRate->value * com_timescale->value, 1000.0f);
3187 				float frameDuration = MAX(1000.0f / fps, 1.0f) + clc.aviVideoFrameRemainder;
3188 
3189 				CL_TakeVideoFrame( );
3190 
3191 				msec = (int)frameDuration;
3192 				clc.aviVideoFrameRemainder = frameDuration - msec;
3193 			}
3194 		}
3195 	}
3196 
3197 	if( cl_autoRecordDemo->integer ) {
3198 		if( clc.state == CA_ACTIVE && !clc.demorecording && !clc.demoplaying ) {
3199 			// If not recording a demo, and we should be, start one
3200 			qtime_t	now;
3201 			char		*nowString;
3202 			char		*p;
3203 			char		mapName[ MAX_QPATH ];
3204 			char		serverName[ MAX_OSPATH ];
3205 
3206 			Com_RealTime( &now );
3207 			nowString = va( "%04d%02d%02d%02d%02d%02d",
3208 					1900 + now.tm_year,
3209 					1 + now.tm_mon,
3210 					now.tm_mday,
3211 					now.tm_hour,
3212 					now.tm_min,
3213 					now.tm_sec );
3214 
3215 			Q_strncpyz( serverName, clc.servername, MAX_OSPATH );
3216 			// Replace the ":" in the address as it is not a valid
3217 			// file name character
3218 			p = strstr( serverName, ":" );
3219 			if( p ) {
3220 				*p = '.';
3221 			}
3222 
3223 			Q_strncpyz( mapName, COM_SkipPath( cl.mapname ), sizeof( cl.mapname ) );
3224 			COM_StripExtension(mapName, mapName, sizeof(mapName));
3225 
3226 			Cbuf_ExecuteText( EXEC_NOW,
3227 					va( "record %s-%s-%s", nowString, serverName, mapName ) );
3228 		}
3229 		else if( clc.state != CA_ACTIVE && clc.demorecording ) {
3230 			// Recording, but not CA_ACTIVE, so stop recording
3231 			CL_StopRecord_f( );
3232 		}
3233 	}
3234 
3235 	// save the msec before checking pause
3236 	cls.realFrametime = msec;
3237 
3238 	// decide the simulation time
3239 	cls.frametime = msec;
3240 
3241 	cls.realtime += cls.frametime;
3242 
3243 	if ( cl_timegraph->integer ) {
3244 		SCR_DebugGraph ( cls.realFrametime * 0.25 );
3245 	}
3246 
3247 	// see if we need to update any userinfo
3248 	CL_CheckUserinfo();
3249 
3250 	// if we haven't gotten a packet in a long time,
3251 	// drop the connection
3252 	CL_CheckTimeout();
3253 
3254 	// send intentions now
3255 	CL_SendCmd();
3256 
3257 	// resend a connection request if necessary
3258 	CL_CheckForResend();
3259 
3260 	// decide on the serverTime to render
3261 	CL_SetCGameTime();
3262 
3263 	// update the screen
3264 	SCR_UpdateScreen();
3265 
3266 	// update audio
3267 	S_Update();
3268 
3269 #ifdef USE_VOIP
3270 	CL_CaptureVoip();
3271 #endif
3272 
3273 #ifdef USE_MUMBLE
3274 	CL_UpdateMumble();
3275 #endif
3276 
3277 	// advance local effects for next frame
3278 	SCR_RunCinematic();
3279 
3280 	Con_RunConsole();
3281 
3282 	cls.framecount++;
3283 }
3284 
3285 
3286 //============================================================================
3287 // Ridah, startup-caching system
3288 typedef struct {
3289 	char name[MAX_QPATH];
3290 	int hits;
3291 	int lastSetIndex;
3292 } cacheItem_t;
3293 typedef enum {
3294 	CACHE_SOUNDS,
3295 	CACHE_MODELS,
3296 	CACHE_IMAGES,
3297 
3298 	CACHE_NUMGROUPS
3299 } cacheGroup_t;
3300 static cacheItem_t cacheGroups[CACHE_NUMGROUPS] = {
3301 	{{'s','o','u','n','d',0}, CACHE_SOUNDS},
3302 	{{'m','o','d','e','l',0}, CACHE_MODELS},
3303 	{{'i','m','a','g','e',0}, CACHE_IMAGES},
3304 };
3305 #define MAX_CACHE_ITEMS     4096
3306 #define CACHE_HIT_RATIO     0.75        // if hit on this percentage of maps, it'll get cached
3307 
3308 static int cacheIndex;
3309 static cacheItem_t cacheItems[CACHE_NUMGROUPS][MAX_CACHE_ITEMS];
3310 
CL_Cache_StartGather_f(void)3311 static void CL_Cache_StartGather_f( void ) {
3312 	cacheIndex = 0;
3313 	memset( cacheItems, 0, sizeof( cacheItems ) );
3314 
3315 	Cvar_Set( "cl_cacheGathering", "1" );
3316 }
3317 
CL_Cache_UsedFile_f(void)3318 static void CL_Cache_UsedFile_f( void ) {
3319 	char groupStr[MAX_QPATH];
3320 	char itemStr[MAX_QPATH];
3321 	int i,group;
3322 	cacheItem_t *item;
3323 
3324 	if ( Cmd_Argc() < 2 ) {
3325 		Com_Error( ERR_DROP, "usedfile without enough parameters\n" );
3326 		return;
3327 	}
3328 
3329 	strcpy( groupStr, Cmd_Argv( 1 ) );
3330 
3331 	strcpy( itemStr, Cmd_Argv( 2 ) );
3332 	for ( i = 3; i < Cmd_Argc(); i++ ) {
3333 		strcat( itemStr, " " );
3334 		strcat( itemStr, Cmd_Argv( i ) );
3335 	}
3336 	Q_strlwr( itemStr );
3337 
3338 	// find the cache group
3339 	for ( i = 0; i < CACHE_NUMGROUPS; i++ ) {
3340 		if ( !Q_strncmp( groupStr, cacheGroups[i].name, MAX_QPATH ) ) {
3341 			break;
3342 		}
3343 	}
3344 	if ( i == CACHE_NUMGROUPS ) {
3345 		Com_Error( ERR_DROP, "usedfile without a valid cache group\n" );
3346 		return;
3347 	}
3348 
3349 	// see if it's already there
3350 	group = i;
3351 	for ( i = 0, item = cacheItems[group]; i < MAX_CACHE_ITEMS; i++, item++ ) {
3352 		if ( !item->name[0] ) {
3353 			// didn't find it, so add it here
3354 			Q_strncpyz( item->name, itemStr, MAX_QPATH );
3355 			if ( cacheIndex > 9999 ) { // hack, but yeh
3356 				item->hits = cacheIndex;
3357 			} else {
3358 				item->hits++;
3359 			}
3360 			item->lastSetIndex = cacheIndex;
3361 			break;
3362 		}
3363 		if ( item->name[0] == itemStr[0] && !Q_strncmp( item->name, itemStr, MAX_QPATH ) ) {
3364 			if ( item->lastSetIndex != cacheIndex ) {
3365 				item->hits++;
3366 				item->lastSetIndex = cacheIndex;
3367 			}
3368 			break;
3369 		}
3370 	}
3371 }
3372 
CL_Cache_SetIndex_f(void)3373 static void CL_Cache_SetIndex_f( void ) {
3374 	if ( Cmd_Argc() < 2 ) {
3375 		Com_Error( ERR_DROP, "setindex needs an index\n" );
3376 		return;
3377 	}
3378 
3379 	cacheIndex = atoi( Cmd_Argv( 1 ) );
3380 }
3381 
CL_Cache_MapChange_f(void)3382 static void CL_Cache_MapChange_f( void ) {
3383 	cacheIndex++;
3384 }
3385 
CL_Cache_EndGather_f(void)3386 static void CL_Cache_EndGather_f( void ) {
3387 	// save the frequently used files to the cache list file
3388 	int i, j, handle, cachePass;
3389 	char filename[MAX_QPATH];
3390 
3391 	cachePass = (int)floor( (float)cacheIndex * CACHE_HIT_RATIO );
3392 
3393 	for ( i = 0; i < CACHE_NUMGROUPS; i++ ) {
3394 		Q_strncpyz( filename, cacheGroups[i].name, MAX_QPATH );
3395 		Q_strcat( filename, MAX_QPATH, ".cache" );
3396 
3397 		handle = FS_FOpenFileWrite( filename );
3398 
3399 		for ( j = 0; j < MAX_CACHE_ITEMS; j++ ) {
3400 			// if it's a valid filename, and it's been hit enough times, cache it
3401 			if ( cacheItems[i][j].hits >= cachePass && strstr( cacheItems[i][j].name, "/" ) ) {
3402 				FS_Write( cacheItems[i][j].name, strlen( cacheItems[i][j].name ), handle );
3403 				FS_Write( "\n", 1, handle );
3404 			}
3405 		}
3406 
3407 		FS_FCloseFile( handle );
3408 	}
3409 
3410 	Cvar_Set( "cl_cacheGathering", "0" );
3411 }
3412 
3413 // done.
3414 //============================================================================
3415 
3416 /*
3417 ================
3418 CL_SetRecommended_f
3419 ================
3420 */
CL_SetRecommended_f(void)3421 void CL_SetRecommended_f( void ) {
3422 	Com_SetRecommended();
3423 }
3424 
3425 /*
3426 ================
3427 CL_RefPrintf
3428 
3429 DLL glue
3430 ================
3431 */
CL_RefPrintf(int print_level,const char * fmt,...)3432 static __attribute__ ((format (printf, 2, 3))) void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) {
3433 	va_list	argptr;
3434 	char	msg[MAXPRINTMSG];
3435 
3436 	va_start( argptr,fmt );
3437 	Q_vsnprintf( msg, sizeof ( msg ), fmt, argptr );
3438 	va_end( argptr );
3439 
3440 	if ( print_level == PRINT_ALL ) {
3441 		Com_Printf( "%s", msg );
3442 	} else if ( print_level == PRINT_WARNING ) {
3443 		Com_Printf( S_COLOR_YELLOW "%s", msg );		// yellow
3444 	} else if ( print_level == PRINT_DEVELOPER ) {
3445 		Com_DPrintf( S_COLOR_RED "%s", msg );		// red
3446 	}
3447 }
3448 
3449 /*
3450 ============
3451 CL_ShutdownRef
3452 ============
3453 */
CL_ShutdownRef(void)3454 void CL_ShutdownRef( void ) {
3455 	if ( re.Shutdown ) {
3456 		re.Shutdown( qtrue );
3457 	}
3458 
3459 	memset( &re, 0, sizeof( re ) );
3460 
3461 #ifdef USE_RENDERER_DLOPEN
3462 	if ( rendererLib ) {
3463 		Sys_UnloadLibrary( rendererLib );
3464 		rendererLib = NULL;
3465 	}
3466 #endif
3467 }
3468 
3469 /*
3470 ============
3471 CL_InitRenderer
3472 ============
3473 */
CL_InitRenderer(void)3474 void CL_InitRenderer( void ) {
3475 	// this sets up the renderer and calls R_Init
3476 	re.BeginRegistration( &cls.glconfig );
3477 
3478 	// load character sets
3479 	cls.charSetShader = re.RegisterShader( "gfx/2d/hudchars" );
3480 	cls.whiteShader = re.RegisterShader( "white" );
3481 	cls.consoleShader = re.RegisterShader( "console-16bit" ); // JPW NERVE shader works with 16bit
3482 	cls.consoleShader2 = re.RegisterShader( "console2-16bit" ); // JPW NERVE same
3483 	g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2;
3484 	g_consoleField.widthInChars = g_console_field_width;
3485 }
3486 
3487 /*
3488 ============================
3489 CL_StartHunkUsers
3490 
3491 After the server has cleared the hunk, these will need to be restarted
3492 This is the only place that any of these functions are called from
3493 ============================
3494 */
CL_StartHunkUsers(qboolean rendererOnly)3495 void CL_StartHunkUsers( qboolean rendererOnly ) {
3496 	if ( !com_cl_running ) {
3497 		return;
3498 	}
3499 
3500 	if ( !com_cl_running->integer ) {
3501 		return;
3502 	}
3503 
3504 	if ( !cls.rendererStarted ) {
3505 		cls.rendererStarted = qtrue;
3506 		CL_InitRenderer();
3507 	}
3508 
3509 	if ( rendererOnly ) {
3510 		return;
3511 	}
3512 
3513 	if ( !cls.soundStarted ) {
3514 		cls.soundStarted = qtrue;
3515 		S_Init();
3516 	}
3517 
3518 	if ( !cls.soundRegistered ) {
3519 		cls.soundRegistered = qtrue;
3520 		S_BeginRegistration();
3521 	}
3522 
3523 	if( com_dedicated->integer ) {
3524 		return;
3525 	}
3526 
3527 	if ( !cls.uiStarted ) {
3528 		cls.uiStarted = qtrue;
3529 		CL_InitUI();
3530 	}
3531 }
3532 
CL_ScaledMilliseconds(void)3533 int CL_ScaledMilliseconds( void ) {
3534 	return Sys_Milliseconds() * com_timescale->value;
3535 }
3536 
3537 // DHM - Nerve
CL_CheckAutoUpdate(void)3538 void CL_CheckAutoUpdate( void ) {
3539 	int validServerNum = 0;
3540 	int i = 0, rnd = 0;
3541 	netadr_t temp;
3542 	char        *servername;
3543 
3544 	if ( !cl_autoupdate->integer ) {
3545 		return;
3546 	}
3547 
3548 	// Only check once per session
3549 	if ( autoupdateChecked ) {
3550 		return;
3551 	}
3552 
3553 	srand( Com_Milliseconds() );
3554 
3555 	// Find out how many update servers have valid DNS listings
3556 	for ( i = 0; i < MAX_AUTOUPDATE_SERVERS; i++ ) {
3557 		if ( NET_StringToAdr( cls.autoupdateServerNames[i], &temp, NA_UNSPEC ) ) {
3558 			validServerNum++;
3559 		}
3560 	}
3561 
3562 	// Pick a random server
3563 	if ( validServerNum > 1 ) {
3564 		rnd = rand() % validServerNum;
3565 	} else {
3566 		rnd = 0;
3567 	}
3568 
3569 	servername = cls.autoupdateServerNames[rnd];
3570 
3571 	Com_DPrintf( "Resolving AutoUpdate Server... " );
3572 	if ( !NET_StringToAdr( servername, &cls.autoupdateServer, NA_UNSPEC  ) ) {
3573 		Com_DPrintf( "Couldn't resolve first address, trying default..." );
3574 
3575 		// Fall back to the first one
3576 		if ( !NET_StringToAdr( cls.autoupdateServerNames[0], &cls.autoupdateServer, NA_UNSPEC  ) ) {
3577 			Com_DPrintf( "Failed to resolve any Auto-update servers.\n" );
3578 			autoupdateChecked = qtrue;
3579 			return;
3580 		}
3581 	}
3582 	cls.autoupdateServer.port = BigShort( PORT_SERVER );
3583 	Com_DPrintf( "%i.%i.%i.%i:%i\n", cls.autoupdateServer.ip[0], cls.autoupdateServer.ip[1],
3584 				 cls.autoupdateServer.ip[2], cls.autoupdateServer.ip[3],
3585 				 BigShort( cls.autoupdateServer.port ) );
3586 
3587 	NET_OutOfBandPrint( NS_CLIENT, cls.autoupdateServer, "getUpdateInfo \"%s\" \"%s\"-\"%s\"\n", Q3_VERSION, OS_STRING, ARCH_STRING );
3588 
3589 	CL_RequestMotd();
3590 
3591 	autoupdateChecked = qtrue;
3592 }
3593 
CL_GetAutoUpdate(void)3594 void CL_GetAutoUpdate( void ) {
3595 
3596 	// Don't try and get an update if we haven't checked for one
3597 	if ( !autoupdateChecked ) {
3598 		return;
3599 	}
3600 
3601 	// Make sure there's a valid update file to request
3602 	if ( strlen( cl_updatefiles->string ) < 5 ) {
3603 		return;
3604 	}
3605 
3606 	Com_DPrintf( "Connecting to auto-update server...\n" );
3607 
3608 	S_StopAllSounds();      // NERVE - SMF
3609 
3610 	// starting to load a map so we get out of full screen ui mode
3611 	Cvar_Set( "r_uiFullScreen", "0" );
3612 
3613 	// clear any previous "server full" type messages
3614 	clc.serverMessage[0] = 0;
3615 
3616 	if ( com_sv_running->integer ) {
3617 		// if running a local server, kill it
3618 		SV_Shutdown( "Server quit\n" );
3619 	}
3620 
3621 	// make sure a local server is killed
3622 	Cvar_Set( "sv_killserver", "1" );
3623 	SV_Frame( 0 );
3624 
3625 	CL_Disconnect( qtrue );
3626 	Con_Close();
3627 
3628 	Q_strncpyz( clc.servername, "Auto-Updater", sizeof( clc.servername ) );
3629 
3630 	if ( cls.autoupdateServer.type == NA_BAD ) {
3631 		Com_Printf( "Bad server address\n" );
3632 		clc.state = CA_DISCONNECTED;
3633 		return;
3634 	}
3635 
3636 	// Copy auto-update server address to Server connect address
3637 	memcpy( &clc.serverAddress, &cls.autoupdateServer, sizeof( netadr_t ) );
3638 
3639 	Com_DPrintf( "%s resolved to %i.%i.%i.%i:%i\n", clc.servername,
3640 				 clc.serverAddress.ip[0], clc.serverAddress.ip[1],
3641 				 clc.serverAddress.ip[2], clc.serverAddress.ip[3],
3642 				 BigShort( clc.serverAddress.port ) );
3643 
3644 	clc.state = CA_CONNECTING;
3645 
3646 	Key_SetCatcher( 0 );
3647 	clc.connectTime = -99999;   // CL_CheckForResend() will fire immediately
3648 	clc.connectPacketCount = 0;
3649 
3650 	// server connection string
3651 	Cvar_Set( "cl_currentServerAddress", "Auto-Updater" );
3652 }
3653 // DHM - Nerve
3654 
3655 /*
3656 ============
3657 CL_RefMalloc
3658 ============
3659 */
3660 #ifdef ZONE_DEBUG
CL_RefMallocDebug(int size,char * label,char * file,int line)3661 void *CL_RefMallocDebug( int size, char *label, char *file, int line ) {
3662 	return Z_TagMallocDebug( size, TAG_RENDERER, label, file, line );
3663 }
3664 #else
CL_RefMalloc(int size)3665 void *CL_RefMalloc( int size ) {
3666 	return Z_TagMalloc( size, TAG_RENDERER );
3667 }
3668 #endif
3669 
3670 /*
3671 ============
3672 CL_RefTagFree
3673 ============
3674 */
CL_RefTagFree(void)3675 void CL_RefTagFree( void ) {
3676 	Z_FreeTags( TAG_RENDERER );
3677 	return;
3678 }
3679 
3680 /*
3681 ============
3682 CL_InitRef
3683 ============
3684 */
CL_InitRef(void)3685 void CL_InitRef( void ) {
3686 	refimport_t ri;
3687 	refexport_t *ret;
3688 #ifdef USE_RENDERER_DLOPEN
3689 	GetRefAPI_t		GetRefAPI;
3690 	char			dllName[MAX_OSPATH];
3691 #endif
3692 
3693 	Com_Printf( "----- Initializing Renderer ----\n" );
3694 
3695 #ifdef USE_RENDERER_DLOPEN
3696 	cl_renderer = Cvar_Get("cl_renderer", "opengl1", CVAR_ARCHIVE | CVAR_LATCH);
3697 
3698 	Com_sprintf(dllName, sizeof(dllName), "renderer_mp_%s_" ARCH_STRING DLL_EXT, cl_renderer->string);
3699 
3700 	if(!(rendererLib = Sys_LoadDll(dllName, qfalse)) && strcmp(cl_renderer->string, cl_renderer->resetString))
3701 	{
3702 		Com_Printf("failed:\n\"%s\"\n", Sys_LibraryError());
3703 		Cvar_ForceReset("cl_renderer");
3704 
3705 		Com_sprintf(dllName, sizeof(dllName), "renderer_mp_opengl1_" ARCH_STRING DLL_EXT);
3706 		rendererLib = Sys_LoadDll(dllName, qfalse);
3707 	}
3708 
3709 	if(!rendererLib)
3710 	{
3711 		Com_Printf("failed:\n\"%s\"\n", Sys_LibraryError());
3712 		Com_Error(ERR_FATAL, "Failed to load renderer");
3713 	}
3714 
3715 	GetRefAPI = Sys_LoadFunction(rendererLib, "GetRefAPI");
3716 	if(!GetRefAPI)
3717 	{
3718 		Com_Error(ERR_FATAL, "Can't load symbol GetRefAPI: '%s'",  Sys_LibraryError());
3719 	}
3720 #endif
3721 
3722 	ri.Cmd_AddCommand = Cmd_AddCommand;
3723 	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
3724 	ri.Cmd_Argc = Cmd_Argc;
3725 	ri.Cmd_Argv = Cmd_Argv;
3726 	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
3727 	ri.Printf = CL_RefPrintf;
3728 	ri.Error = Com_Error;
3729 	ri.Milliseconds = CL_ScaledMilliseconds;
3730 #ifdef ZONE_DEBUG
3731 	ri.Z_MallocDebug = CL_RefMallocDebug;
3732 #else
3733 	ri.Z_Malloc = CL_RefMalloc;
3734 #endif
3735 	ri.Free = Z_Free;
3736 	ri.Tag_Free = CL_RefTagFree;
3737 	ri.Hunk_Clear = Hunk_ClearToMark;
3738 #ifdef HUNK_DEBUG
3739 	ri.Hunk_AllocDebug = Hunk_AllocDebug;
3740 #else
3741 	ri.Hunk_Alloc = Hunk_Alloc;
3742 #endif
3743 	ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
3744 	ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
3745 
3746 	ri.CM_ClusterPVS = CM_ClusterPVS;
3747 	ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
3748 	ri.FS_ReadFile = FS_ReadFile;
3749 	ri.FS_FreeFile = FS_FreeFile;
3750 	ri.FS_WriteFile = FS_WriteFile;
3751 	ri.FS_FreeFileList = FS_FreeFileList;
3752 	ri.FS_ListFiles = FS_ListFiles;
3753 	ri.FS_FileIsInPAK = FS_FileIsInPAK;
3754 	ri.FS_FileExists = FS_FileExists;
3755 	ri.Cvar_Get = Cvar_Get;
3756 	ri.Cvar_Set = Cvar_Set;
3757 	ri.Cvar_SetValue = Cvar_SetValue;
3758 	ri.Cvar_CheckRange = Cvar_CheckRange;
3759 	ri.Cvar_VariableIntegerValue = Cvar_VariableIntegerValue;
3760 
3761 	// cinematic stuff
3762 
3763 	ri.CIN_UploadCinematic = CIN_UploadCinematic;
3764 	ri.CIN_PlayCinematic = CIN_PlayCinematic;
3765 	ri.CIN_RunCinematic = CIN_RunCinematic;
3766 
3767 	ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame;
3768 
3769 	ri.IN_Init = IN_Init;
3770 	ri.IN_Shutdown = IN_Shutdown;
3771 	ri.IN_Restart = IN_Restart;
3772 
3773 	ri.ftol = Q_ftol;
3774 
3775 	ri.Sys_SetEnv = Sys_SetEnv;
3776 	ri.Sys_GLimpSafeInit = Sys_GLimpSafeInit;
3777 	ri.Sys_GLimpInit = Sys_GLimpInit;
3778 	ri.Sys_LowPhysicalMemory = Sys_LowPhysicalMemory;
3779 
3780 	ret = GetRefAPI( REF_API_VERSION, &ri );
3781 
3782 	if ( !ret ) {
3783 		Com_Error( ERR_FATAL, "Couldn't initialize refresh" );
3784 	}
3785 
3786 	re = *ret;
3787 
3788 	Com_Printf( "---- Renderer Initialization Complete ----\n" );
3789 
3790 	// unpause so the cgame definately gets a snapshot and renders a frame
3791 	Cvar_Set( "cl_paused", "0" );
3792 }
3793 
3794 // RF, trap manual client damage commands so users can't issue them manually
CL_ClientDamageCommand(void)3795 void CL_ClientDamageCommand( void ) {
3796 	// do nothing
3797 }
3798 
3799 #if defined (__i386__)
3800 #define BIN_STRING "x86"
3801 #endif
3802 
3803 // NERVE - SMF
CL_startSingleplayer_f(void)3804 void CL_startSingleplayer_f( void ) {
3805 	char binName[MAX_OSPATH];
3806 
3807 #if defined(_WIN64) || defined(__WIN64__)
3808 	Com_sprintf(binName, sizeof(binName), "ioWolfSP." ARCH_STRING ".exe");
3809 	Sys_StartProcess( binName, qtrue );
3810 #elif defined(_WIN32) || defined(__WIN32__)
3811 	Com_sprintf(binName, sizeof(binName), "ioWolfSP." BIN_STRING ".exe");
3812 	Sys_StartProcess( binName, qtrue );
3813 #elif defined(__i386__) && (!defined(_WIN32) || !defined(__WIN32__))
3814 	Com_sprintf(binName, sizeof(binName), "./iowolfsp." BIN_STRING );
3815 	Sys_StartProcess( binName, qtrue );
3816 #else
3817 	Com_sprintf(binName, sizeof(binName), "./iowolfsp." ARCH_STRING );
3818 	Sys_StartProcess( binName, qtrue );
3819 #endif
3820 }
3821 
CL_SaveTranslations_f(void)3822 void CL_SaveTranslations_f( void ) {
3823 	CL_SaveTransTable( "scripts/translation.cfg", qfalse );
3824 }
3825 
CL_SaveNewTranslations_f(void)3826 void CL_SaveNewTranslations_f( void ) {
3827 	char fileName[512];
3828 
3829 	if ( Cmd_Argc() != 2 ) {
3830 		Com_Printf( "usage: SaveNewTranslations <filename>\n" );
3831 		return;
3832 	}
3833 
3834 	strcpy( fileName, va( "translations/%s.cfg", Cmd_Argv( 1 ) ) );
3835 
3836 	CL_SaveTransTable( fileName, qtrue );
3837 }
3838 
CL_LoadTranslations_f(void)3839 void CL_LoadTranslations_f( void ) {
3840 	CL_ReloadTranslation();
3841 }
3842 // -NERVE - SMF
3843 
3844 //===========================================================================================
3845 
3846 
3847 /*
3848 ===============
3849 CL_Video_f
3850 
3851 video
3852 video [filename]
3853 ===============
3854 */
CL_Video_f(void)3855 void CL_Video_f( void )
3856 {
3857 	char  filename[ MAX_OSPATH ];
3858 	int   i, last;
3859 
3860 	if( !clc.demoplaying )
3861 	{
3862 		Com_Printf( "The video command can only be used when playing back demos\n" );
3863 		return;
3864 	}
3865 
3866 	if( Cmd_Argc( ) == 2 )
3867 	{
3868 		// explicit filename
3869 		Com_sprintf( filename, MAX_OSPATH, "videos/%s.avi", Cmd_Argv( 1 ) );
3870 	}
3871 	else
3872 	{
3873 		// scan for a free filename
3874 		for( i = 0; i <= 9999; i++ )
3875 		{
3876 			int a, b, c, d;
3877 
3878 			last = i;
3879 
3880 			a = last / 1000;
3881 			last -= a * 1000;
3882 			b = last / 100;
3883 			last -= b * 100;
3884 			c = last / 10;
3885 			last -= c * 10;
3886 			d = last;
3887 
3888 			Com_sprintf( filename, MAX_OSPATH, "videos/video%d%d%d%d.avi", a, b, c, d );
3889 
3890 			if( !FS_FileExists( filename ) )
3891 			break; // file doesn't exist
3892 		}
3893 
3894 		if( i > 9999 )
3895 		{
3896 			Com_Printf( S_COLOR_RED "ERROR: no free file names to create video\n" );
3897 			return;
3898 		}
3899 	}
3900 
3901 	CL_OpenAVIForWriting( filename );
3902 }
3903 
3904 /*
3905 ===============
3906 CL_StopVideo_f
3907 ===============
3908 */
CL_StopVideo_f(void)3909 void CL_StopVideo_f( void )
3910 {
3911 	CL_CloseAVI( );
3912 }
3913 
3914 /*
3915 ===============
3916 CL_GenerateQKey
3917 
3918 test to see if a valid QKEY_FILE exists.  If one does not, try to generate
3919 it by filling it with 2048 bytes of random data.
3920 ===============
3921 */
CL_GenerateQKey(void)3922 static void CL_GenerateQKey(void)
3923 {
3924 	int len = 0;
3925 	unsigned char buff[ QKEY_SIZE ];
3926 	fileHandle_t f;
3927 
3928 	len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
3929 	FS_FCloseFile( f );
3930 	if( len == QKEY_SIZE ) {
3931 		Com_Printf( "RTCWKEY found.\n" );
3932 		return;
3933 	}
3934 	else {
3935 		if( len > 0 ) {
3936 			Com_Printf( "RTCWKEY file size != %d, regenerating\n",
3937 				QKEY_SIZE );
3938 		}
3939 
3940 		Com_Printf( "RTCWKEY building random string\n" );
3941 		Com_RandomBytes( buff, sizeof(buff) );
3942 
3943 		f = FS_SV_FOpenFileWrite( QKEY_FILE );
3944 		if( !f ) {
3945 			Com_Printf( "RTCWKEY could not open %s for write\n",
3946 				QKEY_FILE );
3947 			return;
3948 		}
3949 		FS_Write( buff, sizeof(buff), f );
3950 		FS_FCloseFile( f );
3951 		Com_Printf( "RTCWKEY generated\n" );
3952 	}
3953 }
3954 
3955 /*
3956 ====================
3957 CL_Init
3958 ====================
3959 */
CL_Init(void)3960 void CL_Init( void ) {
3961 	Com_Printf( "----- Client Initialization -----\n" );
3962 
3963 	Con_Init();
3964 
3965 	if(!com_fullyInitialized)
3966 	{
3967 		CL_ClearState();
3968 		clc.state = CA_DISCONNECTED;	// no longer CA_UNINITIALIZED
3969 		cl_oldGameSet = qfalse;
3970 	}
3971 
3972 	cls.realtime = 0;
3973 
3974 	CL_InitInput();
3975 
3976 	//
3977 	// register our variables
3978 	//
3979 	cl_noprint = Cvar_Get( "cl_noprint", "0", 0 );
3980 #ifdef UPDATE_SERVER_NAME
3981 	cl_motd = Cvar_Get( "cl_motd", "1", 0 );
3982 #endif
3983 	cl_autoupdate = Cvar_Get( "cl_autoupdate", "0", CVAR_ARCHIVE );
3984 
3985 	cl_timeout = Cvar_Get( "cl_timeout", "200", 0 );
3986 
3987 	cl_wavefilerecord = Cvar_Get( "cl_wavefilerecord", "0", CVAR_TEMP );
3988 
3989 	cl_timeNudge = Cvar_Get( "cl_timeNudge", "0", CVAR_TEMP );
3990 	cl_shownet = Cvar_Get( "cl_shownet", "0", CVAR_TEMP );
3991 	cl_shownuments = Cvar_Get( "cl_shownuments", "0", CVAR_TEMP );
3992 	cl_visibleClients = Cvar_Get( "cl_visibleClients", "0", CVAR_TEMP );
3993 	cl_showServerCommands = Cvar_Get( "cl_showServerCommands", "0", 0 );
3994 	cl_showSend = Cvar_Get( "cl_showSend", "0", CVAR_TEMP );
3995 	cl_showTimeDelta = Cvar_Get( "cl_showTimeDelta", "0", CVAR_TEMP );
3996 	cl_freezeDemo = Cvar_Get( "cl_freezeDemo", "0", CVAR_TEMP );
3997 	rcon_client_password = Cvar_Get( "rconPassword", "", CVAR_TEMP );
3998 	cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP );
3999 
4000 	cl_timedemo = Cvar_Get( "timedemo", "0", 0 );
4001 	cl_timedemoLog = Cvar_Get ("cl_timedemoLog", "", CVAR_ARCHIVE);
4002 	cl_autoRecordDemo = Cvar_Get ("cl_autoRecordDemo", "0", CVAR_ARCHIVE);
4003 	cl_aviFrameRate = Cvar_Get ("cl_aviFrameRate", "25", CVAR_ARCHIVE);
4004 	cl_aviMotionJpeg = Cvar_Get ("cl_aviMotionJpeg", "1", CVAR_ARCHIVE);
4005 	cl_avidemo = Cvar_Get( "cl_avidemo", "0", 0 );
4006 	cl_forceavidemo = Cvar_Get( "cl_forceavidemo", "0", 0 );
4007 
4008 	rconAddress = Cvar_Get( "rconAddress", "", 0 );
4009 
4010 	cl_yawspeed = Cvar_Get( "cl_yawspeed", "140", CVAR_ARCHIVE );
4011 	cl_pitchspeed = Cvar_Get( "cl_pitchspeed", "140", CVAR_ARCHIVE );
4012 	cl_anglespeedkey = Cvar_Get( "cl_anglespeedkey", "1.5", 0 );
4013 
4014 	cl_maxpackets = Cvar_Get( "cl_maxpackets", "38", CVAR_ARCHIVE );
4015 	cl_packetdup = Cvar_Get( "cl_packetdup", "1", CVAR_ARCHIVE );
4016 
4017 	cl_showPing = Cvar_Get( "cl_showPing", "0", CVAR_ARCHIVE );
4018 
4019 	cl_run = Cvar_Get( "cl_run", "1", CVAR_ARCHIVE );
4020 	cl_sensitivity = Cvar_Get( "sensitivity", "5", CVAR_ARCHIVE );
4021 	cl_mouseAccel = Cvar_Get( "cl_mouseAccel", "0", CVAR_ARCHIVE );
4022 	cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE );
4023 
4024 	// 0: legacy mouse acceleration
4025 	// 1: new implementation
4026 	cl_mouseAccelStyle = Cvar_Get( "cl_mouseAccelStyle", "0", CVAR_ARCHIVE );
4027 	// offset for the power function (for style 1, ignored otherwise)
4028 	// this should be set to the max rate value
4029 	cl_mouseAccelOffset = Cvar_Get( "cl_mouseAccelOffset", "5", CVAR_ARCHIVE );
4030 	Cvar_CheckRange(cl_mouseAccelOffset, 0.001f, 50000.0f, qfalse);
4031 
4032 	cl_showMouseRate = Cvar_Get( "cl_showmouserate", "0", 0 );
4033 
4034 	cl_allowDownload = Cvar_Get( "cl_allowDownload", "1", CVAR_ARCHIVE );
4035 #ifdef USE_CURL_DLOPEN
4036 	cl_cURLLib = Cvar_Get("cl_cURLLib", DEFAULT_CURL_LIB, CVAR_ARCHIVE | CVAR_PROTECTED);
4037 #endif
4038 
4039 	// init autoswitch so the ui will have it correctly even
4040 	// if the cgame hasn't been started
4041 	// -NERVE - SMF - disabled autoswitch by default
4042 	Cvar_Get( "cg_autoswitch", "0", CVAR_ARCHIVE );
4043 
4044 	// Rafael - particle switch
4045 	Cvar_Get( "cg_wolfparticles", "1", CVAR_ARCHIVE );
4046 	// done
4047 
4048 	cl_conXOffset = Cvar_Get( "cl_conXOffset", "0", 0 );
4049 	cl_inGameVideo = Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE );
4050 
4051 	cl_serverStatusResendTime = Cvar_Get( "cl_serverStatusResendTime", "750", 0 );
4052 
4053 	// RF
4054 	cl_recoilPitch = Cvar_Get( "cg_recoilPitch", "0", CVAR_ROM );
4055 
4056 	cl_bypassMouseInput = Cvar_Get( "cl_bypassMouseInput", "0", 0 ); //CVAR_ROM );			// NERVE - SMF
4057 
4058 	m_pitch = Cvar_Get( "m_pitch", "0.022", CVAR_ARCHIVE );
4059 	m_yaw = Cvar_Get( "m_yaw", "0.022", CVAR_ARCHIVE );
4060 	m_forward = Cvar_Get( "m_forward", "0.25", CVAR_ARCHIVE );
4061 	m_side = Cvar_Get( "m_side", "0.25", CVAR_ARCHIVE );
4062 	m_filter = Cvar_Get( "m_filter", "0", CVAR_ARCHIVE );
4063 
4064 	j_pitch =        Cvar_Get ("j_pitch",        "0.022", CVAR_ARCHIVE);
4065 	j_yaw =          Cvar_Get ("j_yaw",          "-0.022", CVAR_ARCHIVE);
4066 	j_forward =      Cvar_Get ("j_forward",      "-0.25", CVAR_ARCHIVE);
4067 	j_side =         Cvar_Get ("j_side",         "0.25", CVAR_ARCHIVE);
4068 	j_up =           Cvar_Get ("j_up",           "0", CVAR_ARCHIVE);
4069 
4070 	j_pitch_axis =   Cvar_Get ("j_pitch_axis",   "3", CVAR_ARCHIVE);
4071 	j_yaw_axis =     Cvar_Get ("j_yaw_axis",     "2", CVAR_ARCHIVE);
4072 	j_forward_axis = Cvar_Get ("j_forward_axis", "1", CVAR_ARCHIVE);
4073 	j_side_axis =    Cvar_Get ("j_side_axis",    "0", CVAR_ARCHIVE);
4074 	j_up_axis =      Cvar_Get ("j_up_axis",      "4", CVAR_ARCHIVE);
4075 
4076 	Cvar_CheckRange(j_pitch_axis, 0, MAX_JOYSTICK_AXIS-1, qtrue);
4077 	Cvar_CheckRange(j_yaw_axis, 0, MAX_JOYSTICK_AXIS-1, qtrue);
4078 	Cvar_CheckRange(j_forward_axis, 0, MAX_JOYSTICK_AXIS-1, qtrue);
4079 	Cvar_CheckRange(j_side_axis, 0, MAX_JOYSTICK_AXIS-1, qtrue);
4080 	Cvar_CheckRange(j_up_axis, 0, MAX_JOYSTICK_AXIS-1, qtrue);
4081 
4082 	cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM );
4083 
4084 	Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE );
4085 
4086 	cl_lanForcePackets = Cvar_Get ("cl_lanForcePackets", "1", CVAR_ARCHIVE);
4087 
4088 	cl_guid = Cvar_Get( "cl_guid", "unknown", CVAR_USERINFO | CVAR_ROM );
4089 
4090 	cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE);
4091 
4092 	// ~ and `, as keys and characters
4093 	cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE);
4094 
4095 	// NERVE - SMF
4096 	Cvar_Get( "cg_drawCompass", "1", CVAR_ARCHIVE );
4097 	Cvar_Get( "cg_drawNotifyText", "1", CVAR_ARCHIVE );
4098 	Cvar_Get( "cg_quickMessageAlt", "1", CVAR_ARCHIVE );
4099 	Cvar_Get( "cg_popupLimboMenu", "1", CVAR_ARCHIVE );
4100 	Cvar_Get( "cg_descriptiveText", "1", CVAR_ARCHIVE );
4101 	Cvar_Get( "cg_drawTeamOverlay", "2", CVAR_ARCHIVE );
4102 	Cvar_Get( "cg_uselessNostalgia", "0", CVAR_ARCHIVE ); // JPW NERVE
4103 	Cvar_Get( "cg_drawGun", "1", CVAR_ARCHIVE );
4104 	Cvar_Get( "cg_cursorHints", "1", CVAR_ARCHIVE );
4105 	Cvar_Get( "cg_voiceSpriteTime", "6000", CVAR_ARCHIVE );
4106 	Cvar_Get( "cg_teamChatsOnly", "0", CVAR_ARCHIVE );
4107 	Cvar_Get( "cg_noVoiceChats", "0", CVAR_ARCHIVE );
4108 	Cvar_Get( "cg_noVoiceText", "0", CVAR_ARCHIVE );
4109 	Cvar_Get( "cg_crosshairSize", "48", CVAR_ARCHIVE );
4110 	Cvar_Get( "cg_drawCrosshair", "1", CVAR_ARCHIVE );
4111 	Cvar_Get( "cg_zoomDefaultSniper", "20", CVAR_ARCHIVE );
4112 	Cvar_Get( "cg_zoomstepsniper", "2", CVAR_ARCHIVE );
4113 
4114 	Cvar_Get( "mp_playerType", "0", 0 );
4115 	Cvar_Get( "mp_currentPlayerType", "0", 0 );
4116 	Cvar_Get( "mp_weapon", "0", 0 );
4117 	Cvar_Get( "mp_team", "0", 0 );
4118 	Cvar_Get( "mp_currentTeam", "0", 0 );
4119 	// -NERVE - SMF
4120 
4121 	// userinfo
4122 	Cvar_Get( "name", "WolfPlayer", CVAR_USERINFO | CVAR_ARCHIVE );
4123 	cl_rate = Cvar_Get( "rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE );     // NERVE - SMF - changed from 3000
4124 	Cvar_Get( "snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE );
4125 	Cvar_Get( "model", "multi", CVAR_USERINFO | CVAR_ARCHIVE );
4126 	Cvar_Get( "head", "default", CVAR_USERINFO | CVAR_ARCHIVE );
4127 	Cvar_Get( "color", "4", CVAR_USERINFO | CVAR_ARCHIVE );
4128 	Cvar_Get( "handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE );
4129 	Cvar_Get( "sex", "male", CVAR_USERINFO | CVAR_ARCHIVE );
4130 	Cvar_Get( "cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE );
4131 
4132 	Cvar_Get( "password", "", CVAR_USERINFO );
4133 	Cvar_Get( "cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE );
4134 
4135 #ifdef USE_MUMBLE
4136 	cl_useMumble = Cvar_Get ("cl_useMumble", "0", CVAR_ARCHIVE | CVAR_LATCH);
4137 	cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE);
4138 #endif
4139 
4140 #ifdef USE_VOIP
4141 	cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0);
4142 	cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "spatial", 0);
4143 	cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE);
4144 	cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE);
4145 	cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE);
4146 	cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
4147 	cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE);
4148 
4149 	cl_voip = Cvar_Get ("cl_voip", "1", CVAR_ARCHIVE);
4150 	Cvar_CheckRange( cl_voip, 0, 1, qtrue );
4151 	cl_voipProtocol = Cvar_Get ("cl_voipProtocol", cl_voip->integer ? "opus" : "", CVAR_USERINFO | CVAR_ROM);
4152 #endif
4153 
4154 //----(SA) added
4155 	Cvar_Get( "cg_autoactivate", "1", CVAR_USERINFO | CVAR_ARCHIVE );
4156 //----(SA) end
4157 
4158 	// cgame might not be initialized before menu is used
4159 	Cvar_Get( "cg_viewsize", "100", CVAR_ARCHIVE );
4160 	// Make sure cg_stereoSeparation is zero as that variable is deprecated and should not be used anymore.
4161 	Cvar_Get ("cg_stereoSeparation", "0", CVAR_ROM);
4162 
4163 	Cvar_Get( "cg_autoReload", "1", CVAR_ARCHIVE | CVAR_USERINFO );
4164 
4165 	cl_missionStats = Cvar_Get( "g_missionStats", "0", CVAR_ROM );
4166 	cl_waitForFire = Cvar_Get( "cl_waitForFire", "0", CVAR_ROM );
4167 
4168 	// NERVE - SMF - localization
4169 	cl_language = Cvar_Get( "cl_language", "0", CVAR_ARCHIVE );
4170 	cl_debugTranslation = Cvar_Get( "cl_debugTranslation", "0", 0 );
4171 	// -NERVE - SMF
4172 
4173 	// DHM - Nerve :: Auto-update
4174 	cl_updateavailable = Cvar_Get( "cl_updateavailable", "0", CVAR_ROM );
4175 	cl_updatefiles = Cvar_Get( "cl_updatefiles", "", CVAR_ROM );
4176 
4177 	Q_strncpyz( cls.autoupdateServerNames[0], AUTOUPDATE_SERVER1_NAME, MAX_QPATH );
4178 	Q_strncpyz( cls.autoupdateServerNames[1], AUTOUPDATE_SERVER2_NAME, MAX_QPATH );
4179 	Q_strncpyz( cls.autoupdateServerNames[2], AUTOUPDATE_SERVER3_NAME, MAX_QPATH );
4180 	Q_strncpyz( cls.autoupdateServerNames[3], AUTOUPDATE_SERVER4_NAME, MAX_QPATH );
4181 	Q_strncpyz( cls.autoupdateServerNames[4], AUTOUPDATE_SERVER5_NAME, MAX_QPATH );
4182 	// DHM - Nerve
4183 
4184 	//
4185 	// register our commands
4186 	//
4187 	Cmd_AddCommand( "cmd", CL_ForwardToServer_f );
4188 	Cmd_AddCommand( "configstrings", CL_Configstrings_f );
4189 	Cmd_AddCommand( "clientinfo", CL_Clientinfo_f );
4190 	Cmd_AddCommand( "snd_restart", CL_Snd_Restart_f );
4191 	Cmd_AddCommand( "vid_restart", CL_Vid_Restart_f );
4192 	Cmd_AddCommand( "ui_restart", CL_UI_Restart_f );          // NERVE - SMF
4193 	Cmd_AddCommand( "disconnect", CL_Disconnect_f );
4194 	Cmd_AddCommand( "record", CL_Record_f );
4195 	Cmd_AddCommand( "demo", CL_PlayDemo_f );
4196 	Cmd_SetCommandCompletionFunc( "demo", CL_CompleteDemoName );
4197 	Cmd_AddCommand( "cinematic", CL_PlayCinematic_f );
4198 	Cmd_AddCommand( "stoprecord", CL_StopRecord_f );
4199 	Cmd_AddCommand( "connect", CL_Connect_f );
4200 	Cmd_AddCommand( "reconnect", CL_Reconnect_f );
4201 	Cmd_AddCommand( "localservers", CL_LocalServers_f );
4202 	Cmd_AddCommand( "globalservers", CL_GlobalServers_f );
4203 	Cmd_AddCommand( "rcon", CL_Rcon_f );
4204 	Cmd_SetCommandCompletionFunc( "rcon", CL_CompleteRcon );
4205 	Cmd_AddCommand( "ping", CL_Ping_f );
4206 	Cmd_AddCommand( "serverstatus", CL_ServerStatus_f );
4207 	Cmd_AddCommand( "showip", CL_ShowIP_f );
4208 	Cmd_AddCommand( "fs_openedList", CL_OpenedPK3List_f );
4209 	Cmd_AddCommand( "fs_referencedList", CL_ReferencedPK3List_f );
4210 	Cmd_AddCommand ("video", CL_Video_f );
4211 	Cmd_AddCommand ("stopvideo", CL_StopVideo_f );
4212 
4213 	// Ridah, startup-caching system
4214 	Cmd_AddCommand( "cache_startgather", CL_Cache_StartGather_f );
4215 	Cmd_AddCommand( "cache_usedfile", CL_Cache_UsedFile_f );
4216 	Cmd_AddCommand( "cache_setindex", CL_Cache_SetIndex_f );
4217 	Cmd_AddCommand( "cache_mapchange", CL_Cache_MapChange_f );
4218 	Cmd_AddCommand( "cache_endgather", CL_Cache_EndGather_f );
4219 
4220 	Cmd_AddCommand( "updatehunkusage", CL_UpdateLevelHunkUsage );
4221 	Cmd_AddCommand( "updatescreen", SCR_UpdateScreen );
4222 	// done.
4223 	Cmd_AddCommand( "SaveTranslations", CL_SaveTranslations_f );     // NERVE - SMF - localization
4224 	Cmd_AddCommand( "SaveNewTranslations", CL_SaveNewTranslations_f );   // NERVE - SMF - localization
4225 	Cmd_AddCommand( "LoadTranslations", CL_LoadTranslations_f );     // NERVE - SMF - localization
4226 	// NERVE - SMF - don't do this in multiplayer
4227 	// RF, add this command so clients can't bind a key to send client damage commands to the server
4228 //	Cmd_AddCommand( "cld", CL_ClientDamageCommand );
4229 
4230 	Cmd_AddCommand( "startSingleplayer", CL_startSingleplayer_f );      // NERVE - SMF
4231 
4232 	Cmd_AddCommand( "setRecommended", CL_SetRecommended_f );
4233 
4234 	CL_InitRef();
4235 
4236 	SCR_Init();
4237 
4238 //	Cbuf_Execute();
4239 
4240 	Cvar_Set( "cl_running", "1" );
4241 
4242 	// DHM - Nerve
4243 	autoupdateChecked = qfalse;
4244 	autoupdateStarted = qfalse;
4245 
4246 	CL_InitTranslation();   // NERVE - SMF - localization
4247 
4248 	CL_GenerateQKey();
4249 	CL_UpdateGUID( NULL, 0 );
4250 
4251 	Com_Printf( "----- Client Initialization Complete -----\n" );
4252 }
4253 
4254 
4255 /*
4256 ===============
4257 CL_Shutdown
4258 
4259 ===============
4260 */
CL_Shutdown(char * finalmsg,qboolean disconnect,qboolean quit)4261 void CL_Shutdown( char *finalmsg, qboolean disconnect, qboolean quit ) {
4262 	static qboolean recursive = qfalse;
4263 
4264 	// check whether the client is running at all.
4265 	if(!(com_cl_running && com_cl_running->integer))
4266 		return;
4267 
4268 	Com_Printf( "----- Client Shutdown (%s) -----\n", finalmsg );
4269 
4270 	if ( recursive ) {
4271 		Com_Printf( "WARNING: Recursive shutdown\n" );
4272 		return;
4273 	}
4274 	recursive = qtrue;
4275 
4276 	noGameRestart = quit;
4277 
4278 	if(disconnect)
4279 		CL_Disconnect(qtrue);
4280 
4281 	CL_ClearMemory(qtrue);
4282 	CL_Snd_Shutdown();
4283 
4284 	Cmd_RemoveCommand( "cmd" );
4285 	Cmd_RemoveCommand( "configstrings" );
4286 	Cmd_RemoveCommand ("clientinfo");
4287 	Cmd_RemoveCommand( "snd_restart" );
4288 	Cmd_RemoveCommand( "vid_restart" );
4289 	Cmd_RemoveCommand( "ui_restart" );
4290 	Cmd_RemoveCommand( "disconnect" );
4291 	Cmd_RemoveCommand( "record" );
4292 	Cmd_RemoveCommand( "demo" );
4293 	Cmd_RemoveCommand( "cinematic" );
4294 	Cmd_RemoveCommand( "stoprecord" );
4295 	Cmd_RemoveCommand( "connect" );
4296 	Cmd_RemoveCommand ("reconnect");
4297 	Cmd_RemoveCommand( "localservers" );
4298 	Cmd_RemoveCommand( "globalservers" );
4299 	Cmd_RemoveCommand( "rcon" );
4300 	Cmd_RemoveCommand( "ping" );
4301 	Cmd_RemoveCommand( "serverstatus" );
4302 	Cmd_RemoveCommand( "showip" );
4303 	Cmd_RemoveCommand ("fs_openedList");
4304 	Cmd_RemoveCommand ("fs_referencedList");
4305 	Cmd_RemoveCommand( "model" );
4306 	Cmd_RemoveCommand ("video");
4307 	Cmd_RemoveCommand ("stopvideo");
4308 
4309 	// Ridah, startup-caching system
4310 	Cmd_RemoveCommand( "cache_startgather" );
4311 	Cmd_RemoveCommand( "cache_usedfile" );
4312 	Cmd_RemoveCommand( "cache_setindex" );
4313 	Cmd_RemoveCommand( "cache_mapchange" );
4314 	Cmd_RemoveCommand( "cache_endgather" );
4315 
4316 	Cmd_RemoveCommand( "updatehunkusage" );
4317 	// done.
4318 
4319 	Cmd_RemoveCommand( "updatescreen" );
4320 	Cmd_RemoveCommand( "SaveTranslations" );     // NERVE - SMF - localization
4321 	Cmd_RemoveCommand( "SaveNewTranslations" );   // NERVE - SMF - localization
4322 	Cmd_RemoveCommand( "LoadTranslations" );     // NERVE - SMF - localization
4323 	Cmd_RemoveCommand( "startSingleplayer" );      // NERVE - SMF
4324 	Cmd_RemoveCommand( "setRecommended" );
4325 
4326 	CL_ShutdownInput();
4327 	Con_Shutdown();
4328 
4329 	Cvar_Set( "cl_running", "0" );
4330 
4331 	recursive = qfalse;
4332 
4333 	memset( &cls, 0, sizeof( cls ) );
4334 	Key_SetCatcher( 0 );
4335 
4336 	Com_Printf( "-----------------------\n" );
4337 }
4338 
4339 
CL_SetServerInfo(serverInfo_t * server,const char * info,int ping)4340 static void CL_SetServerInfo( serverInfo_t *server, const char *info, int ping ) {
4341 	if ( server ) {
4342 		if ( info ) {
4343 			server->clients = atoi( Info_ValueForKey( info, "clients" ) );
4344 			Q_strncpyz( server->hostName,Info_ValueForKey( info, "hostname" ), MAX_NAME_LENGTH );
4345 			Q_strncpyz( server->mapName, Info_ValueForKey( info, "mapname" ), MAX_NAME_LENGTH );
4346 			server->maxClients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
4347 			Q_strncpyz( server->game,Info_ValueForKey( info, "game" ), MAX_NAME_LENGTH );
4348 			server->gameType = atoi( Info_ValueForKey( info, "gametype" ) );
4349 			server->netType = atoi( Info_ValueForKey( info, "nettype" ) );
4350 			server->minPing = atoi( Info_ValueForKey( info, "minping" ) );
4351 			server->maxPing = atoi( Info_ValueForKey( info, "maxping" ) );
4352 			server->allowAnonymous = atoi( Info_ValueForKey( info, "sv_allowAnonymous" ) );
4353 			server->friendlyFire = atoi( Info_ValueForKey( info, "friendlyFire" ) );         // NERVE - SMF
4354 			server->maxlives = atoi( Info_ValueForKey( info, "maxlives" ) );                 // NERVE - SMF
4355 			server->tourney = atoi( Info_ValueForKey( info, "tourney" ) );                       // NERVE - SMF
4356 			server->punkbuster = atoi( Info_ValueForKey( info, "punkbuster" ) );             // DHM - Nerve
4357 			Q_strncpyz( server->gameName, Info_ValueForKey( info, "gamename" ), MAX_NAME_LENGTH );   // Arnout
4358 			server->antilag = atoi( Info_ValueForKey( info, "g_antilag" ) );
4359 			server->g_humanplayers = atoi( Info_ValueForKey( info, "g_humanplayers" ) );
4360 			server->g_needpass = atoi( Info_ValueForKey( info, "g_needpass" ) );
4361 		}
4362 		server->ping = ping;
4363 	}
4364 }
4365 
CL_SetServerInfoByAddress(netadr_t from,const char * info,int ping)4366 static void CL_SetServerInfoByAddress( netadr_t from, const char *info, int ping ) {
4367 	int i;
4368 
4369 	for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) {
4370 		if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
4371 			CL_SetServerInfo( &cls.localServers[i], info, ping );
4372 		}
4373 	}
4374 
4375 	for ( i = 0; i < MAX_GLOBAL_SERVERS; i++ ) {
4376 		if ( NET_CompareAdr( from, cls.globalServers[i].adr ) ) {
4377 			CL_SetServerInfo( &cls.globalServers[i], info, ping );
4378 		}
4379 	}
4380 
4381 	for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) {
4382 		if ( NET_CompareAdr( from, cls.favoriteServers[i].adr ) ) {
4383 			CL_SetServerInfo( &cls.favoriteServers[i], info, ping );
4384 		}
4385 	}
4386 
4387 }
4388 
4389 /*
4390 ===================
4391 CL_ServerInfoPacket
4392 ===================
4393 */
CL_ServerInfoPacket(netadr_t from,msg_t * msg)4394 void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
4395 	int	i, type;
4396 	char	info[MAX_INFO_STRING];
4397 	char	*infoString;
4398 	int	prot;
4399 	char	*gamename;
4400 	qboolean gameMismatch;
4401 
4402 	infoString = MSG_ReadString( msg );
4403 
4404 	// if this isn't the correct gamename, ignore it
4405 	gamename = Info_ValueForKey( infoString, "gamename" );
4406 
4407 #ifdef LEGACY_PROTOCOL
4408 	// gamename is optional for legacy protocol
4409 	if (com_legacyprotocol->integer && !*gamename)
4410 		gameMismatch = qfalse;
4411 	else
4412 #endif
4413 		gameMismatch = !*gamename || strcmp(gamename, com_gamename->string) != 0;
4414 
4415 	if (gameMismatch)
4416 	{
4417 		Com_DPrintf( "Game mismatch in info packet: %s\n", infoString );
4418 		return;
4419 	}
4420 
4421 	// if this isn't the correct protocol version, ignore it
4422 	prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
4423 
4424 	if(prot != com_protocol->integer
4425 #ifdef LEGACY_PROTOCOL
4426 	   && prot != com_legacyprotocol->integer
4427 #endif
4428 	  )
4429 	{
4430 		Com_DPrintf( "Different protocol info packet: %s\n", infoString );
4431 		return;
4432 	}
4433 
4434 	// iterate servers waiting for ping response
4435 	for ( i = 0; i < MAX_PINGREQUESTS; i++ )
4436 	{
4437 		if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) ) {
4438 			// calc ping time
4439 			cl_pinglist[i].time = Sys_Milliseconds() - cl_pinglist[i].start;
4440 			Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
4441 
4442 			// save of info
4443 			Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
4444 
4445 			// tack on the net type
4446 			// NOTE: make sure these types are in sync with the netnames strings in the UI
4447 			switch ( from.type )
4448 			{
4449 			case NA_BROADCAST:
4450 			case NA_IP:
4451 				type = 1;
4452 				break;
4453 			case NA_IP6:
4454 				type = 2;
4455 				break;
4456 			default:
4457 				type = 0;
4458 				break;
4459 			}
4460 			Info_SetValueForKey( cl_pinglist[i].info, "nettype", va( "%d", type ) );
4461 			CL_SetServerInfoByAddress( from, infoString, cl_pinglist[i].time );
4462 
4463 			return;
4464 		}
4465 	}
4466 
4467 	// if not just sent a local broadcast or pinging local servers
4468 	if ( cls.pingUpdateSource != AS_LOCAL ) {
4469 		return;
4470 	}
4471 
4472 	for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
4473 		// empty slot
4474 		if ( cls.localServers[i].adr.port == 0 ) {
4475 			break;
4476 		}
4477 
4478 		// avoid duplicate
4479 		if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
4480 			return;
4481 		}
4482 	}
4483 
4484 	if ( i == MAX_OTHER_SERVERS ) {
4485 		Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
4486 		return;
4487 	}
4488 
4489 	// add this to the list
4490 	cls.numlocalservers = i + 1;
4491 	CL_InitServerInfo( &cls.localServers[i], &from );
4492 
4493 	Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
4494 	if ( strlen( info ) ) {
4495 		if ( info[strlen( info ) - 1] != '\n' ) {
4496 			Q_strcat( info, sizeof(info), "\n" );
4497 		}
4498 		Com_Printf( "%s: %s", NET_AdrToStringwPort( from ), info );
4499 	}
4500 }
4501 
4502 /*
4503 ===================
4504 CL_UpdateInfoPacket
4505 ===================
4506 */
CL_UpdateInfoPacket(netadr_t from)4507 void CL_UpdateInfoPacket( netadr_t from ) {
4508 
4509 	if ( cls.autoupdateServer.type == NA_BAD ) {
4510 		Com_DPrintf( "CL_UpdateInfoPacket:  Auto-Updater has bad address\n" );
4511 		return;
4512 	}
4513 
4514 	Com_DPrintf( "Auto-Updater resolved to %i.%i.%i.%i:%i\n",
4515 				 cls.autoupdateServer.ip[0], cls.autoupdateServer.ip[1],
4516 				 cls.autoupdateServer.ip[2], cls.autoupdateServer.ip[3],
4517 				 BigShort( cls.autoupdateServer.port ) );
4518 
4519 	if ( !NET_CompareAdr( from, cls.autoupdateServer ) ) {
4520 		Com_DPrintf( "CL_UpdateInfoPacket:  Received packet from %i.%i.%i.%i:%i\n",
4521 					 from.ip[0], from.ip[1], from.ip[2], from.ip[3],
4522 					 BigShort( from.port ) );
4523 		return;
4524 	}
4525 
4526 	Cvar_Set( "cl_updateavailable", Cmd_Argv( 1 ) );
4527 
4528 	if ( !Q_stricmp( cl_updateavailable->string, "1" ) ) {
4529 		Cvar_Set( "cl_updatefiles", Cmd_Argv( 2 ) );
4530 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_WM_AUTOUPDATE );
4531 	}
4532 }
4533 // DHM - Nerve
4534 
4535 /*
4536 ===================
4537 CL_GetServerStatus
4538 ===================
4539 */
CL_GetServerStatus(netadr_t from)4540 serverStatus_t *CL_GetServerStatus( netadr_t from ) {
4541 	int i, oldest, oldestTime;
4542 
4543 	for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) {
4544 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
4545 			return &cl_serverStatusList[i];
4546 		}
4547 	}
4548 	for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) {
4549 		if ( cl_serverStatusList[i].retrieved ) {
4550 			return &cl_serverStatusList[i];
4551 		}
4552 	}
4553 	oldest = -1;
4554 	oldestTime = 0;
4555 	for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) {
4556 		if ( oldest == -1 || cl_serverStatusList[i].startTime < oldestTime ) {
4557 			oldest = i;
4558 			oldestTime = cl_serverStatusList[i].startTime;
4559 		}
4560 	}
4561 	return &cl_serverStatusList[oldest];
4562 }
4563 
4564 /*
4565 ===================
4566 CL_ServerStatus
4567 ===================
4568 */
CL_ServerStatus(char * serverAddress,char * serverStatusString,int maxLen)4569 int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) {
4570 	int i;
4571 	netadr_t	to;
4572 	serverStatus_t *serverStatus;
4573 
4574 	// if no server address then reset all server status requests
4575 	if ( !serverAddress ) {
4576 		for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) {
4577 			cl_serverStatusList[i].address.port = 0;
4578 			cl_serverStatusList[i].retrieved = qtrue;
4579 		}
4580 		return qfalse;
4581 	}
4582 	// get the address
4583 	if ( !NET_StringToAdr( serverAddress, &to, NA_UNSPEC) ) {
4584 		return qfalse;
4585 	}
4586 	serverStatus = CL_GetServerStatus( to );
4587 	// if no server status string then reset the server status request for this address
4588 	if ( !serverStatusString ) {
4589 		serverStatus->retrieved = qtrue;
4590 		return qfalse;
4591 	}
4592 
4593 	// if this server status request has the same address
4594 	if ( NET_CompareAdr( to, serverStatus->address ) ) {
4595 		// if we received a response for this server status request
4596 		if ( !serverStatus->pending ) {
4597 			Q_strncpyz( serverStatusString, serverStatus->string, maxLen );
4598 			serverStatus->retrieved = qtrue;
4599 			serverStatus->startTime = 0;
4600 			return qtrue;
4601 		}
4602 		// resend the request regularly
4603 		else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
4604 			serverStatus->print = qfalse;
4605 			serverStatus->pending = qtrue;
4606 			serverStatus->retrieved = qfalse;
4607 			serverStatus->time = 0;
4608 			serverStatus->startTime = Com_Milliseconds();
4609 			NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
4610 			return qfalse;
4611 		}
4612 	}
4613 	// if retrieved
4614 	else if ( serverStatus->retrieved ) {
4615 		serverStatus->address = to;
4616 		serverStatus->print = qfalse;
4617 		serverStatus->pending = qtrue;
4618 		serverStatus->retrieved = qfalse;
4619 		serverStatus->startTime = Com_Milliseconds();
4620 		serverStatus->time = 0;
4621 		NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
4622 		return qfalse;
4623 	}
4624 	return qfalse;
4625 }
4626 
4627 /*
4628 ===================
4629 CL_ServerStatusResponse
4630 ===================
4631 */
CL_ServerStatusResponse(netadr_t from,msg_t * msg)4632 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) {
4633 	char	*s;
4634 	char	info[MAX_INFO_STRING];
4635 	int	i, l, score, ping;
4636 	int	len;
4637 	serverStatus_t *serverStatus;
4638 
4639 	serverStatus = NULL;
4640 	for ( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ ) {
4641 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
4642 			serverStatus = &cl_serverStatusList[i];
4643 			break;
4644 		}
4645 	}
4646 	// if we didn't request this server status
4647 	if ( !serverStatus ) {
4648 		return;
4649 	}
4650 
4651 	s = MSG_ReadStringLine( msg );
4652 
4653 	len = 0;
4654 	Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "%s", s );
4655 
4656 	if ( serverStatus->print ) {
4657 		Com_Printf( "Server settings:\n" );
4658 		// print cvars
4659 		while ( *s ) {
4660 			for ( i = 0; i < 2 && *s; i++ ) {
4661 				if ( *s == '\\' ) {
4662 					s++;
4663 				}
4664 				l = 0;
4665 				while ( *s ) {
4666 					info[l++] = *s;
4667 					if ( l >= MAX_INFO_STRING - 1 ) {
4668 						break;
4669 					}
4670 					s++;
4671 					if ( *s == '\\' ) {
4672 						break;
4673 					}
4674 				}
4675 				info[l] = '\0';
4676 				if ( i ) {
4677 					Com_Printf( "%s\n", info );
4678 				} else {
4679 					Com_Printf( "%-24s", info );
4680 				}
4681 			}
4682 		}
4683 	}
4684 
4685 	len = strlen( serverStatus->string );
4686 	Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\" );
4687 
4688 	if ( serverStatus->print ) {
4689 		Com_Printf( "\nPlayers:\n" );
4690 		Com_Printf( "num: score: ping: name:\n" );
4691 	}
4692 	for ( i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++ ) {
4693 
4694 		len = strlen( serverStatus->string );
4695 		Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\%s", s );
4696 
4697 		if ( serverStatus->print ) {
4698 			score = ping = 0;
4699 			sscanf( s, "%d %d", &score, &ping );
4700 			s = strchr( s, ' ' );
4701 			if ( s ) {
4702 				s = strchr( s + 1, ' ' );
4703 			}
4704 			if ( s ) {
4705 				s++;
4706 			} else {
4707 				s = "unknown";
4708 			}
4709 			Com_Printf( "%-2d   %-3d    %-3d   %s\n", i, score, ping, s );
4710 		}
4711 	}
4712 	len = strlen( serverStatus->string );
4713 	Com_sprintf( &serverStatus->string[len], sizeof( serverStatus->string ) - len, "\\" );
4714 
4715 	serverStatus->time = Com_Milliseconds();
4716 	serverStatus->address = from;
4717 	serverStatus->pending = qfalse;
4718 	if (serverStatus->print) {
4719 		serverStatus->retrieved = qtrue;
4720 	}
4721 }
4722 
4723 /*
4724 ==================
4725 CL_LocalServers_f
4726 ==================
4727 */
CL_LocalServers_f(void)4728 void CL_LocalServers_f( void ) {
4729 	char	*message;
4730 	int	i, j;
4731 	netadr_t	to;
4732 
4733 	Com_Printf( "Scanning for servers on the local network...\n" );
4734 
4735 	// reset the list, waiting for response
4736 	cls.numlocalservers = 0;
4737 	cls.pingUpdateSource = AS_LOCAL;
4738 
4739 	for ( i = 0; i < MAX_OTHER_SERVERS; i++ ) {
4740 		qboolean b = cls.localServers[i].visible;
4741 		Com_Memset( &cls.localServers[i], 0, sizeof( cls.localServers[i] ) );
4742 		cls.localServers[i].visible = b;
4743 	}
4744 	Com_Memset( &to, 0, sizeof( to ) );
4745 
4746 	// The 'xxx' in the message is a challenge that will be echoed back
4747 	// by the server.  We don't care about that here, but master servers
4748 	// can use that to prevent spoofed server responses from invalid ip
4749 	message = "\377\377\377\377getinfo xxx";
4750 
4751 	// send each message twice in case one is dropped
4752 	for ( i = 0 ; i < 2 ; i++ ) {
4753 		// send a broadcast packet on each server port
4754 		// we support multiple server ports so a single machine
4755 		// can nicely run multiple servers
4756 		for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
4757 			to.port = BigShort( (short)( PORT_SERVER + j ) );
4758 
4759 			to.type = NA_BROADCAST;
4760 			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
4761 			to.type = NA_MULTICAST6;
4762 			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
4763 		}
4764 	}
4765 }
4766 
4767 /*
4768 ==================
4769 CL_GlobalServers_f
4770 
4771 ioquake3 2008; added support for requesting five separate master servers using 0-4.
4772 ioquake3 2017; made master 0 fetch all master servers and 1-5 request a single master server.
4773 ==================
4774 */
CL_GlobalServers_f(void)4775 void CL_GlobalServers_f( void ) {
4776 	netadr_t	to;
4777 	int			count, i, masterNum;
4778 	char		command[1024], *masteraddress;
4779 
4780 	if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > MAX_MASTER_SERVERS)
4781 	{
4782 		Com_Printf("usage: globalservers <master# 0-%d> <protocol> [keywords]\n", MAX_MASTER_SERVERS);
4783 		return;
4784 	}
4785 
4786 	// request from all master servers
4787 	if ( masterNum == 0 ) {
4788 		int numAddress = 0;
4789 
4790 		for ( i = 1; i <= MAX_MASTER_SERVERS; i++ ) {
4791 			sprintf(command, "sv_master%d", i);
4792 			masteraddress = Cvar_VariableString(command);
4793 
4794 			if(!*masteraddress)
4795 				continue;
4796 
4797 			numAddress++;
4798 
4799 			Com_sprintf(command, sizeof(command), "globalservers %d %s %s\n", i, Cmd_Argv(2), Cmd_ArgsFrom(3));
4800 			Cbuf_AddText(command);
4801 		}
4802 
4803 		if ( !numAddress ) {
4804 			Com_Printf( "CL_GlobalServers_f: Error: No master server addresses.\n");
4805 		}
4806 		return;
4807 	}
4808 
4809 	sprintf(command, "sv_master%d", masterNum);
4810 	masteraddress = Cvar_VariableString(command);
4811 
4812 	if(!*masteraddress)
4813 	{
4814 		Com_Printf( "CL_GlobalServers_f: Error: No master server address given.\n");
4815 		return;
4816 	}
4817 
4818 	// reset the list, waiting for response
4819 	// -1 is used to distinguish a "no response"
4820 
4821 	i = NET_StringToAdr(masteraddress, &to, NA_UNSPEC);
4822 
4823 	if(!i)
4824 	{
4825 		Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress);
4826 		return;
4827 	}
4828 	else if(i == 2)
4829 		to.port = BigShort(PORT_MASTER);
4830 
4831 	Com_Printf("Requesting servers from %s (%s)...\n", masteraddress, NET_AdrToStringwPort(to));
4832 
4833 	cls.numglobalservers = -1;
4834 	cls.pingUpdateSource = AS_GLOBAL;
4835 
4836 	// Use the extended query for IPv6 masters
4837 	if (to.type == NA_IP6 || to.type == NA_MULTICAST6)
4838 	{
4839 		int v4enabled = Cvar_VariableIntegerValue("net_enabled") & NET_ENABLEV4;
4840 
4841 		if(v4enabled)
4842 		{
4843 			Com_sprintf(command, sizeof(command), "getserversExt %s %s",
4844 				com_gamename->string, Cmd_Argv(2));
4845 		}
4846 		else
4847 		{
4848 			Com_sprintf(command, sizeof(command), "getserversExt %s %s ipv6",
4849 				com_gamename->string, Cmd_Argv(2));
4850 		}
4851 	}
4852 	else if ( !Q_stricmp( com_gamename->string, LEGACY_MASTER_GAMENAME ) )
4853 		Com_sprintf(command, sizeof(command), "getservers %s",
4854 			Cmd_Argv(2));
4855 	else
4856 		Com_sprintf(command, sizeof(command), "getservers %s %s",
4857 			com_gamename->string, Cmd_Argv(2));
4858 
4859 	for (i=3; i < count; i++)
4860 	{
4861 		Q_strcat(command, sizeof(command), " ");
4862 		Q_strcat(command, sizeof(command), Cmd_Argv(i));
4863 	}
4864 
4865 	NET_OutOfBandPrint( NS_SERVER, to, "%s", command );
4866 }
4867 
4868 /*
4869 ==================
4870 CL_GetPing
4871 ==================
4872 */
CL_GetPing(int n,char * buf,int buflen,int * pingtime)4873 void CL_GetPing( int n, char *buf, int buflen, int *pingtime ) {
4874 	const char 	*str;
4875 	int	time;
4876 	int	maxPing;
4877 
4878 	if (n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port)
4879 	{
4880 		// empty or invalid slot
4881 		buf[0]    = '\0';
4882 		*pingtime = 0;
4883 		return;
4884 	}
4885 
4886 	str = NET_AdrToStringwPort( cl_pinglist[n].adr );
4887 	Q_strncpyz( buf, str, buflen );
4888 
4889 	time = cl_pinglist[n].time;
4890 	if ( !time ) {
4891 		// check for timeout
4892 		time = Sys_Milliseconds() - cl_pinglist[n].start;
4893 		maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
4894 		if ( maxPing < 100 ) {
4895 			maxPing = 100;
4896 		}
4897 		if ( time < maxPing ) {
4898 			// not timed out yet
4899 			time = 0;
4900 		}
4901 	}
4902 
4903 	CL_SetServerInfoByAddress( cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time );
4904 
4905 	*pingtime = time;
4906 }
4907 
4908 /*
4909 ==================
4910 CL_GetPingInfo
4911 ==================
4912 */
CL_GetPingInfo(int n,char * buf,int buflen)4913 void CL_GetPingInfo( int n, char *buf, int buflen ) {
4914 	if ( n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port ) {
4915 		// empty or invalid slot
4916 		if ( buflen ) {
4917 			buf[0] = '\0';
4918 		}
4919 		return;
4920 	}
4921 
4922 	Q_strncpyz( buf, cl_pinglist[n].info, buflen );
4923 }
4924 
4925 /*
4926 ==================
4927 CL_ClearPing
4928 ==================
4929 */
CL_ClearPing(int n)4930 void CL_ClearPing( int n ) {
4931 	if ( n < 0 || n >= MAX_PINGREQUESTS ) {
4932 		return;
4933 	}
4934 
4935 	cl_pinglist[n].adr.port = 0;
4936 }
4937 
4938 /*
4939 ==================
4940 CL_GetPingQueueCount
4941 ==================
4942 */
CL_GetPingQueueCount(void)4943 int CL_GetPingQueueCount( void ) {
4944 	int	i;
4945 	int	count;
4946 	ping_t*	pingptr;
4947 
4948 	count   = 0;
4949 	pingptr = cl_pinglist;
4950 
4951 	for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ ) {
4952 		if ( pingptr->adr.port ) {
4953 			count++;
4954 		}
4955 	}
4956 
4957 	return ( count );
4958 }
4959 
4960 /*
4961 ==================
4962 CL_GetFreePing
4963 ==================
4964 */
CL_GetFreePing(void)4965 ping_t* CL_GetFreePing( void ) {
4966 	ping_t*	pingptr;
4967 	ping_t*	best;
4968 	int	oldest;
4969 	int	i;
4970 	int	time;
4971 
4972 	pingptr = cl_pinglist;
4973 	for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ )
4974 	{
4975 		// find free ping slot
4976 		if ( pingptr->adr.port ) {
4977 			if ( !pingptr->time ) {
4978 				if (Sys_Milliseconds() - pingptr->start < 500)
4979 				{
4980 					// still waiting for response
4981 					continue;
4982 				}
4983 			} else if ( pingptr->time < 500 ) {
4984 				// results have not been queried
4985 				continue;
4986 			}
4987 		}
4988 
4989 		// clear it
4990 		pingptr->adr.port = 0;
4991 		return ( pingptr );
4992 	}
4993 
4994 	// use oldest entry
4995 	pingptr = cl_pinglist;
4996 	best    = cl_pinglist;
4997 	oldest  = INT_MIN;
4998 	for ( i = 0; i < MAX_PINGREQUESTS; i++, pingptr++ )
4999 	{
5000 		// scan for oldest
5001 		time = Sys_Milliseconds() - pingptr->start;
5002 		if ( time > oldest ) {
5003 			oldest = time;
5004 			best   = pingptr;
5005 		}
5006 	}
5007 
5008 	return ( best );
5009 }
5010 
5011 /*
5012 ==================
5013 CL_Ping_f
5014 ==================
5015 */
CL_Ping_f(void)5016 void CL_Ping_f( void ) {
5017 	netadr_t	to;
5018 	ping_t*	pingptr;
5019 	char*	server;
5020 	int			argc;
5021 	netadrtype_t	family = NA_UNSPEC;
5022 
5023 	argc = Cmd_Argc();
5024 
5025 	if ( argc != 2 && argc != 3 ) {
5026 		Com_Printf( "usage: ping [-4|-6] server\n");
5027 		return;
5028 	}
5029 
5030 	if(argc == 2)
5031 		server = Cmd_Argv(1);
5032 	else
5033 	{
5034 		if(!strcmp(Cmd_Argv(1), "-4"))
5035 			family = NA_IP;
5036 		else if(!strcmp(Cmd_Argv(1), "-6"))
5037 			family = NA_IP6;
5038 		else
5039 			Com_Printf( "warning: only -4 or -6 as address type understood.\n");
5040 
5041 		server = Cmd_Argv(2);
5042 	}
5043 
5044 	Com_Memset( &to, 0, sizeof( netadr_t ) );
5045 
5046 	if ( !NET_StringToAdr( server, &to, family ) ) {
5047 		return;
5048 	}
5049 
5050 	pingptr = CL_GetFreePing();
5051 
5052 	memcpy( &pingptr->adr, &to, sizeof( netadr_t ) );
5053 	pingptr->start = Sys_Milliseconds();
5054 	pingptr->time  = 0;
5055 
5056 	CL_SetServerInfoByAddress( pingptr->adr, NULL, 0 );
5057 
5058 	NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
5059 }
5060 
5061 /*
5062 ==================
5063 CL_UpdateVisiblePings_f
5064 ==================
5065 */
CL_UpdateVisiblePings_f(int source)5066 qboolean CL_UpdateVisiblePings_f( int source ) {
5067 	int	slots, i;
5068 	char	buff[MAX_STRING_CHARS];
5069 	int	pingTime;
5070 	int	max;
5071 	qboolean status = qfalse;
5072 
5073 	if ( source < 0 || source > AS_FAVORITES ) {
5074 		return qfalse;
5075 	}
5076 
5077 	cls.pingUpdateSource = source;
5078 
5079 	slots = CL_GetPingQueueCount();
5080 	if ( slots < MAX_PINGREQUESTS ) {
5081 		serverInfo_t *server = NULL;
5082 
5083 		switch ( source ) {
5084 		case AS_LOCAL:
5085 			server = &cls.localServers[0];
5086 			max = cls.numlocalservers;
5087 			break;
5088 		case AS_GLOBAL:
5089 			server = &cls.globalServers[0];
5090 			max = cls.numglobalservers;
5091 			break;
5092 		case AS_FAVORITES:
5093 			server = &cls.favoriteServers[0];
5094 			max = cls.numfavoriteservers;
5095 			break;
5096 			default:
5097 				return qfalse;
5098 		}
5099 		for ( i = 0; i < max; i++ ) {
5100 			if ( server[i].visible ) {
5101 				if ( server[i].ping == -1 ) {
5102 					int j;
5103 
5104 					if ( slots >= MAX_PINGREQUESTS ) {
5105 						break;
5106 					}
5107 					for ( j = 0; j < MAX_PINGREQUESTS; j++ ) {
5108 						if ( !cl_pinglist[j].adr.port ) {
5109 							continue;
5110 						}
5111 						if ( NET_CompareAdr( cl_pinglist[j].adr, server[i].adr ) ) {
5112 							// already on the list
5113 							break;
5114 						}
5115 					}
5116 					if ( j >= MAX_PINGREQUESTS ) {
5117 						status = qtrue;
5118 						for ( j = 0; j < MAX_PINGREQUESTS; j++ ) {
5119 							if ( !cl_pinglist[j].adr.port ) {
5120 								break;
5121 							}
5122 						}
5123 						memcpy( &cl_pinglist[j].adr, &server[i].adr, sizeof( netadr_t ) );
5124 						cl_pinglist[j].start = Sys_Milliseconds();
5125 						cl_pinglist[j].time = 0;
5126 						NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
5127 						slots++;
5128 					}
5129 				}
5130 				// if the server has a ping higher than cl_maxPing or
5131 				// the ping packet got lost
5132 				else if ( server[i].ping == 0 ) {
5133 					// if we are updating global servers
5134 					if ( source == AS_GLOBAL ) {
5135 						//
5136 						if ( cls.numGlobalServerAddresses > 0 ) {
5137 							// overwrite this server with one from the additional global servers
5138 							cls.numGlobalServerAddresses--;
5139 							CL_InitServerInfo( &server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses] );
5140 							// NOTE: the server[i].visible flag stays untouched
5141 						}
5142 					}
5143 				}
5144 			}
5145 		}
5146 	}
5147 
5148 	if ( slots ) {
5149 		status = qtrue;
5150 	}
5151 	for ( i = 0; i < MAX_PINGREQUESTS; i++ ) {
5152 		if ( !cl_pinglist[i].adr.port ) {
5153 			continue;
5154 		}
5155 		CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
5156 		if ( pingTime != 0 ) {
5157 			CL_ClearPing( i );
5158 			status = qtrue;
5159 		}
5160 	}
5161 
5162 	return status;
5163 }
5164 
5165 /*
5166 ==================
5167 CL_UpdateServerInfo
5168 ==================
5169 */
CL_UpdateServerInfo(int n)5170 void CL_UpdateServerInfo( int n ) {
5171 	if ( !cl_pinglist[n].adr.port ) {
5172 		return;
5173 	}
5174 
5175 	CL_SetServerInfoByAddress( cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time );
5176 }
5177 
5178 /*
5179 ==================
5180 CL_ServerStatus_f
5181 ==================
5182 */
CL_ServerStatus_f(void)5183 void CL_ServerStatus_f( void ) {
5184 	netadr_t	to, *toptr = NULL;
5185 	char	*server;
5186 	serverStatus_t *serverStatus;
5187 	int			argc;
5188 	netadrtype_t	family = NA_UNSPEC;
5189 
5190 	argc = Cmd_Argc();
5191 
5192 	if ( argc != 2 && argc != 3 )
5193 	{
5194 		if (clc.state != CA_ACTIVE || clc.demoplaying)
5195 		{
5196 			Com_Printf( "Not connected to a server.\n" );
5197 			Com_Printf( "usage: serverstatus [-4|-6] server\n");
5198 			return;
5199 		}
5200 
5201 		toptr = &clc.serverAddress;
5202 	}
5203 
5204 	if(!toptr)
5205 	{
5206 		Com_Memset( &to, 0, sizeof(netadr_t) );
5207 
5208 		if(argc == 2)
5209 			server = Cmd_Argv(1);
5210 		else
5211 		{
5212 			if(!strcmp(Cmd_Argv(1), "-4"))
5213 				family = NA_IP;
5214 			else if(!strcmp(Cmd_Argv(1), "-6"))
5215 				family = NA_IP6;
5216 			else
5217 				Com_Printf( "warning: only -4 or -6 as address type understood.\n");
5218 
5219 			server = Cmd_Argv(2);
5220 		}
5221 
5222 		toptr = &to;
5223 		if ( !NET_StringToAdr( server, toptr, family ) )
5224 			return;
5225 	}
5226 
5227 	NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" );
5228 
5229 	serverStatus = CL_GetServerStatus( *toptr );
5230 	serverStatus->address = *toptr;
5231 	serverStatus->print = qtrue;
5232 	serverStatus->pending = qtrue;
5233 }
5234 
5235 /*
5236 ==================
5237 CL_ShowIP_f
5238 ==================
5239 */
CL_ShowIP_f(void)5240 void CL_ShowIP_f( void ) {
5241 	Sys_ShowIP();
5242 }
5243 
5244 /*
5245 =================
5246 CL_CDKeyValidate
5247 =================
5248 */
CL_CDKeyValidate(const char * key,const char * checksum)5249 qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
5250 #ifdef STANDALONE
5251 	return qtrue;
5252 #else
5253 	char ch;
5254 	byte sum;
5255 	char chs[3];
5256 	int i, len;
5257 
5258 	len = strlen( key );
5259 	if ( len != CDKEY_LEN ) {
5260 		return qfalse;
5261 	}
5262 
5263 	if ( checksum && strlen( checksum ) != CDCHKSUM_LEN ) {
5264 		return qfalse;
5265 	}
5266 
5267 	sum = 0;
5268 	// for loop gets rid of conditional assignment warning
5269 	for ( i = 0; i < len; i++ ) {
5270 		ch = *key++;
5271 		if ( ch >= 'a' && ch <= 'z' ) {
5272 			ch -= 32;
5273 		}
5274 		switch ( ch ) {
5275 		case '2':
5276 		case '3':
5277 		case '7':
5278 		case 'A':
5279 		case 'B':
5280 		case 'C':
5281 		case 'D':
5282 		case 'G':
5283 		case 'H':
5284 		case 'J':
5285 		case 'L':
5286 		case 'P':
5287 		case 'R':
5288 		case 'S':
5289 		case 'T':
5290 		case 'W':
5291 			sum = ( sum << 1 ) ^ ch;
5292 			continue;
5293 		default:
5294 			return qfalse;
5295 		}
5296 	}
5297 
5298 	Com_sprintf( chs, sizeof( chs ), "%02x", sum );
5299 
5300 	if ( checksum && !Q_stricmp( chs, checksum ) ) {
5301 		return qtrue;
5302 	}
5303 
5304 	if ( !checksum ) {
5305 		return qtrue;
5306 	}
5307 
5308 	return qfalse;
5309 #endif
5310 }
5311 
5312 // NERVE - SMF
5313 /*
5314 =======================
5315 CL_AddToLimboChat
5316 
5317 =======================
5318 */
CL_AddToLimboChat(const char * str)5319 void CL_AddToLimboChat( const char *str ) {
5320 	int len = 0;
5321 	char *p;
5322 	int i;
5323 
5324 	cl.limboChatPos = LIMBOCHAT_HEIGHT - 1;
5325 
5326 	// copy old strings
5327 	for ( i = cl.limboChatPos; i > 0; i-- ) {
5328 		strcpy( cl.limboChatMsgs[i], cl.limboChatMsgs[i - 1] );
5329 	}
5330 
5331 	// copy new string
5332 	p = cl.limboChatMsgs[0];
5333 	*p = 0;
5334 
5335 	while ( *str ) {
5336 		if ( len > LIMBOCHAT_WIDTH - 1 ) {
5337 			break;
5338 		}
5339 
5340 		if ( Q_IsColorString( str ) ) {
5341 			*p++ = *str++;
5342 			*p++ = *str++;
5343 			continue;
5344 		}
5345 		*p++ = *str++;
5346 		len++;
5347 	}
5348 	*p = 0;
5349 }
5350 
5351 /*
5352 =======================
5353 CL_GetLimboString
5354 
5355 =======================
5356 */
CL_GetLimboString(int index,char * buf)5357 qboolean CL_GetLimboString( int index, char *buf ) {
5358 	if ( index >= LIMBOCHAT_HEIGHT ) {
5359 		return qfalse;
5360 	}
5361 
5362 	strncpy( buf, cl.limboChatMsgs[index], 140 );
5363 	return qtrue;
5364 }
5365 // -NERVE - SMF
5366 
5367 // NERVE - SMF - Localization code
5368 #define FILE_HASH_SIZE      1024
5369 #define MAX_VA_STRING       32000
5370 #define MAX_TRANS_STRING    4096
5371 
5372 typedef struct trans_s {
5373 	char original[MAX_TRANS_STRING];
5374 	char translated[MAX_LANGUAGES][MAX_TRANS_STRING];
5375 	struct      trans_s *next;
5376 	float x_offset;
5377 	float y_offset;
5378 	qboolean fromFile;
5379 } trans_t;
5380 
5381 static trans_t* transTable[FILE_HASH_SIZE];
5382 
5383 /*
5384 =======================
5385 AllocTrans
5386 =======================
5387 */
AllocTrans(char * original,char * translated[MAX_LANGUAGES])5388 static trans_t* AllocTrans( char *original, char *translated[MAX_LANGUAGES] ) {
5389 	trans_t *t;
5390 	int i;
5391 
5392 	t = malloc( sizeof( trans_t ) );
5393 	memset( t, 0, sizeof( trans_t ) );
5394 
5395 	if ( original ) {
5396 		strncpy( t->original, original, MAX_TRANS_STRING );
5397 	}
5398 
5399 	if ( translated ) {
5400 		for ( i = 0; i < MAX_LANGUAGES; i++ )
5401 			strncpy( t->translated[i], translated[i], MAX_TRANS_STRING );
5402 	}
5403 
5404 	return t;
5405 }
5406 
5407 /*
5408 =======================
5409 generateHashValue
5410 =======================
5411 */
generateHashValue(const char * fname)5412 static long generateHashValue( const char *fname ) {
5413 	int i;
5414 	long hash;
5415 	char letter;
5416 
5417 	hash = 0;
5418 	i = 0;
5419 	while ( fname[i] != '\0' ) {
5420 		letter = tolower( fname[i] );
5421 		hash += (long)( letter ) * ( i + 119 );
5422 		i++;
5423 	}
5424 	hash &= ( FILE_HASH_SIZE - 1 );
5425 	return hash;
5426 }
5427 
5428 /*
5429 =======================
5430 LookupTrans
5431 =======================
5432 */
LookupTrans(char * original,char * translated[MAX_LANGUAGES],qboolean isLoading)5433 static trans_t* LookupTrans( char *original, char *translated[MAX_LANGUAGES], qboolean isLoading ) {
5434 	trans_t *t, *newt, *prev = NULL;
5435 	long hash;
5436 
5437 	hash = generateHashValue( original );
5438 
5439 	for ( t = transTable[hash]; t; prev = t, t = t->next ) {
5440 		if ( !Q_stricmp( original, t->original ) ) {
5441 			if ( isLoading ) {
5442 				Com_DPrintf( S_COLOR_YELLOW "WARNING: Duplicate string found: \"%s\"\n", original );
5443 			}
5444 			return t;
5445 		}
5446 	}
5447 
5448 	newt = AllocTrans( original, translated );
5449 
5450 	if ( prev ) {
5451 		prev->next = newt;
5452 	} else {
5453 		transTable[hash] = newt;
5454 	}
5455 
5456 	if ( cl_debugTranslation->integer >= 1 && !isLoading ) {
5457 		Com_Printf( "Missing translation: \'%s\'\n", original );
5458 	}
5459 
5460 	// see if we want to save out the translation table everytime a string is added
5461 //	if ( cl_debugTranslation->integer == 2 && !isLoading ) {
5462 //		CL_SaveTransTable();
5463 //	}
5464 
5465 	return newt;
5466 }
5467 
5468 /*
5469 =======================
5470 CL_SaveTransTable
5471 =======================
5472 */
CL_SaveTransTable(const char * fileName,qboolean newOnly)5473 void CL_SaveTransTable( const char *fileName, qboolean newOnly ) {
5474 	int bucketlen, bucketnum, maxbucketlen, avebucketlen;
5475 	int untransnum, transnum;
5476 	const char *buf;
5477 	fileHandle_t f;
5478 	trans_t *t;
5479 	int i, j, len;
5480 
5481 	if ( cl.corruptedTranslationFile ) {
5482 		Com_Printf( S_COLOR_YELLOW "WARNING: Cannot save corrupted translation file. Please reload first." );
5483 		return;
5484 	}
5485 
5486 	FS_FOpenFileByMode( fileName, &f, FS_WRITE );
5487 
5488 	bucketnum = 0;
5489 	maxbucketlen = 0;
5490 	avebucketlen = 0;
5491 	transnum = 0;
5492 	untransnum = 0;
5493 
5494 	// write out version, if one
5495 	if ( strlen( cl.translationVersion ) ) {
5496 		buf = va( "#version\t\t\"%s\"\n", cl.translationVersion );
5497 	} else {
5498 		buf = va( "#version\t\t\"1.0 01/01/01\"\n" );
5499 	}
5500 
5501 	len = strlen( buf );
5502 	FS_Write( buf, len, f );
5503 
5504 	// write out translated strings
5505 	for ( j = 0; j < 2; j++ ) {
5506 
5507 		for ( i = 0; i < FILE_HASH_SIZE; i++ ) {
5508 			t = transTable[i];
5509 
5510 			if ( !t || ( newOnly && t->fromFile ) ) {
5511 				continue;
5512 			}
5513 
5514 			bucketlen = 0;
5515 
5516 			for ( ; t; t = t->next ) {
5517 				bucketlen++;
5518 
5519 				if ( strlen( t->translated[0] ) ) {
5520 					if ( j ) {
5521 						continue;
5522 					}
5523 					transnum++;
5524 				} else {
5525 					if ( !j ) {
5526 						continue;
5527 					}
5528 					untransnum++;
5529 				}
5530 
5531 				buf = va( "{\n\tenglish\t\t\"%s\"\n", t->original );
5532 				len = strlen( buf );
5533 				FS_Write( buf, len, f );
5534 
5535 				buf = va( "\tfrench\t\t\"%s\"\n", t->translated[LANGUAGE_FRENCH] );
5536 				len = strlen( buf );
5537 				FS_Write( buf, len, f );
5538 
5539 				buf = va( "\tgerman\t\t\"%s\"\n", t->translated[LANGUAGE_GERMAN] );
5540 				len = strlen( buf );
5541 				FS_Write( buf, len, f );
5542 
5543 				buf = va( "\titalian\t\t\"%s\"\n", t->translated[LANGUAGE_ITALIAN] );
5544 				len = strlen( buf );
5545 				FS_Write( buf, len, f );
5546 
5547 				buf = va( "\tspanish\t\t\"%s\"\n", t->translated[LANGUAGE_SPANISH] );
5548 				len = strlen( buf );
5549 				FS_Write( buf, len, f );
5550 
5551 				buf = va( "}\n" );
5552 				len = strlen( buf );
5553 				FS_Write( buf, len, f );
5554 			}
5555 
5556 			if ( bucketlen > maxbucketlen ) {
5557 				maxbucketlen = bucketlen;
5558 			}
5559 
5560 			if ( bucketlen ) {
5561 				bucketnum++;
5562 				avebucketlen += bucketlen;
5563 			}
5564 		}
5565 	}
5566 
5567 	Com_Printf( "Saved translation table.\nTotal = %i, Translated = %i, Untranslated = %i, aveblen = %2.2f, maxblen = %i\n",
5568 				transnum + untransnum, transnum, untransnum, (float)avebucketlen / bucketnum, maxbucketlen );
5569 
5570 	FS_FCloseFile( f );
5571 }
5572 
5573 /*
5574 =======================
5575 CL_CheckTranslationString
5576 
5577 NERVE - SMF - compare formatting characters
5578 =======================
5579 */
CL_CheckTranslationString(char * original,char * translated)5580 qboolean CL_CheckTranslationString( char *original, char *translated ) {
5581 	char format_org[128], format_trans[128];
5582 	int len, i;
5583 
5584 	memset( format_org, 0, 128 );
5585 	memset( format_trans, 0, 128 );
5586 
5587 	// generate formatting string for original
5588 	len = strlen( original );
5589 
5590 	for ( i = 0; i < len; i++ ) {
5591 		if ( original[i] != '%' ) {
5592 			continue;
5593 		}
5594 
5595 		strcat( format_org, va( "%c%c ", '%', original[i + 1] ) );
5596 	}
5597 
5598 	// generate formatting string for translated
5599 	len = strlen( translated );
5600 	if ( !len ) {
5601 		return qtrue;
5602 	}
5603 
5604 	for ( i = 0; i < len; i++ ) {
5605 		if ( translated[i] != '%' ) {
5606 			continue;
5607 		}
5608 
5609 		strcat( format_trans, va( "%c%c ", '%', translated[i + 1] ) );
5610 	}
5611 
5612 	// compare
5613 	len = strlen( format_org );
5614 
5615 	if ( len != strlen( format_trans ) ) {
5616 		return qfalse;
5617 	}
5618 
5619 	for ( i = 0; i < len; i++ ) {
5620 		if ( format_org[i] != format_trans[i] ) {
5621 			return qfalse;
5622 		}
5623 	}
5624 
5625 	return qtrue;
5626 }
5627 
5628 /*
5629 =======================
5630 CL_LoadTransTable
5631 =======================
5632 */
CL_LoadTransTable(const char * fileName)5633 void CL_LoadTransTable( const char *fileName ) {
5634 	char translated[MAX_LANGUAGES][MAX_VA_STRING];
5635 	char original[MAX_VA_STRING];
5636 	qboolean aborted;
5637 	char *text;
5638 	fileHandle_t f;
5639 	char *text_p;
5640 	char *token;
5641 	int len, i;
5642 	trans_t *t;
5643 	int count;
5644 
5645 	count = 0;
5646 	aborted = qfalse;
5647 	cl.corruptedTranslationFile = qfalse;
5648 
5649 	len = FS_FOpenFileByMode( fileName, &f, FS_READ );
5650 	if ( len <= 0 ) {
5651 		return;
5652 	}
5653 
5654 	text = malloc( len + 1 );
5655 	if ( !text ) {
5656 		return;
5657 	}
5658 
5659 	FS_Read( text, len, f );
5660 	text[len] = 0;
5661 	FS_FCloseFile( f );
5662 
5663 	// parse the text
5664 	text_p = text;
5665 
5666 	do {
5667 		token = COM_Parse( &text_p );
5668 		if ( Q_stricmp( "{", token ) ) {
5669 			// parse version number
5670 			if ( !Q_stricmp( "#version", token ) ) {
5671 				token = COM_Parse( &text_p );
5672 				strcpy( cl.translationVersion, token );
5673 				continue;
5674 			}
5675 
5676 			break;
5677 		}
5678 
5679 		// english
5680 		token = COM_Parse( &text_p );
5681 		if ( Q_stricmp( "english", token ) ) {
5682 			aborted = qtrue;
5683 			break;
5684 		}
5685 
5686 		token = COM_Parse( &text_p );
5687 		strcpy( original, token );
5688 
5689 		if ( cl_debugTranslation->integer == 3 ) {
5690 			Com_Printf( "%i Loading: \"%s\"\n", count, original );
5691 		}
5692 
5693 		// french
5694 		token = COM_Parse( &text_p );
5695 		if ( Q_stricmp( "french", token ) ) {
5696 			aborted = qtrue;
5697 			break;
5698 		}
5699 
5700 		token = COM_Parse( &text_p );
5701 		strcpy( translated[LANGUAGE_FRENCH], token );
5702 		if ( !CL_CheckTranslationString( original, translated[LANGUAGE_FRENCH] ) ) {
5703 			Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" );
5704 			aborted = qtrue;
5705 			break;
5706 		}
5707 
5708 		// german
5709 		token = COM_Parse( &text_p );
5710 		if ( Q_stricmp( "german", token ) ) {
5711 			aborted = qtrue;
5712 			break;
5713 		}
5714 
5715 		token = COM_Parse( &text_p );
5716 		strcpy( translated[LANGUAGE_GERMAN], token );
5717 		if ( !CL_CheckTranslationString( original, translated[LANGUAGE_GERMAN] ) ) {
5718 			Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" );
5719 			aborted = qtrue;
5720 			break;
5721 		}
5722 
5723 		// italian
5724 		token = COM_Parse( &text_p );
5725 		if ( Q_stricmp( "italian", token ) ) {
5726 			aborted = qtrue;
5727 			break;
5728 		}
5729 
5730 		token = COM_Parse( &text_p );
5731 		strcpy( translated[LANGUAGE_ITALIAN], token );
5732 		if ( !CL_CheckTranslationString( original, translated[LANGUAGE_ITALIAN] ) ) {
5733 			Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" );
5734 			aborted = qtrue;
5735 			break;
5736 		}
5737 
5738 		// spanish
5739 		token = COM_Parse( &text_p );
5740 		if ( Q_stricmp( "spanish", token ) ) {
5741 			aborted = qtrue;
5742 			break;
5743 		}
5744 
5745 		token = COM_Parse( &text_p );
5746 		strcpy( translated[LANGUAGE_SPANISH], token );
5747 		if ( !CL_CheckTranslationString( original, translated[LANGUAGE_SPANISH] ) ) {
5748 			Com_Printf( S_COLOR_YELLOW "WARNING: Translation formatting doesn't match up with English version!\n" );
5749 			aborted = qtrue;
5750 			break;
5751 		}
5752 
5753 		// do lookup
5754 		t = LookupTrans( original, NULL, qtrue );
5755 
5756 		if ( t ) {
5757 			t->fromFile = qtrue;
5758 
5759 			for ( i = 0; i < MAX_LANGUAGES; i++ )
5760 				strncpy( t->translated[i], translated[i], MAX_TRANS_STRING );
5761 		}
5762 
5763 		token = COM_Parse( &text_p );
5764 
5765 		// set offset if we have one
5766 		if ( !Q_stricmp( "offset", token ) ) {
5767 			if ( t )
5768 			{
5769 				token = COM_Parse( &text_p );
5770 				t->x_offset = atof( token );
5771 
5772 				token = COM_Parse( &text_p );
5773 				t->y_offset = atof( token );
5774 
5775 				token = COM_Parse( &text_p );
5776 			}
5777 		}
5778 
5779 		if ( Q_stricmp( "}", token ) ) {
5780 			aborted = qtrue;
5781 			break;
5782 		}
5783 
5784 		count++;
5785 	} while ( token );
5786 
5787 	if ( aborted ) {
5788 		int i, line = 1;
5789 
5790 		for ( i = 0; i < len && ( text + i ) < text_p; i++ ) {
5791 			if ( text[i] == '\n' ) {
5792 				line++;
5793 			}
5794 		}
5795 
5796 		Com_Printf( S_COLOR_YELLOW "WARNING: Problem loading %s on line %i\n", fileName, line );
5797 		cl.corruptedTranslationFile = qtrue;
5798 	} else {
5799 		Com_Printf( "Loaded %i translation strings from %s\n", count, fileName );
5800 	}
5801 
5802 	// cleanup
5803 	free( text );
5804 }
5805 
5806 /*
5807 =======================
5808 CL_ReloadTranslation
5809 =======================
5810 */
CL_ReloadTranslation(void)5811 void CL_ReloadTranslation( void ) {
5812 	char    **fileList;
5813 	int numFiles, i;
5814 	char fileName[MAX_QPATH];
5815 
5816 	for ( i = 0; i < FILE_HASH_SIZE; i++ ) {
5817 		if ( transTable[i] ) {
5818 			free( transTable[i] );
5819 		}
5820 	}
5821 
5822 	memset( transTable, 0, sizeof( trans_t* ) * FILE_HASH_SIZE );
5823 	CL_LoadTransTable( "scripts/translation.cfg" );
5824 
5825 	fileList = FS_ListFiles( "translations", ".cfg", &numFiles );
5826 
5827 	for ( i = 0; i < numFiles; i++ ) {
5828 		Com_sprintf( fileName, sizeof (fileName), "translations/%s", fileList[i] );
5829 		CL_LoadTransTable( fileName );
5830 	}
5831 }
5832 
5833 /*
5834 =======================
5835 CL_InitTranslation
5836 =======================
5837 */
CL_InitTranslation(void)5838 void CL_InitTranslation( void ) {
5839 	char    **fileList;
5840 	int numFiles, i;
5841 	char fileName[MAX_QPATH];
5842 
5843 	memset( transTable, 0, sizeof( trans_t* ) * FILE_HASH_SIZE );
5844 	CL_LoadTransTable( "scripts/translation.cfg" );
5845 
5846 	fileList = FS_ListFiles( "translations", ".cfg", &numFiles );
5847 
5848 	for ( i = 0; i < numFiles; i++ ) {
5849 		Com_sprintf( fileName, sizeof (fileName), "translations/%s", fileList[i] );
5850 		CL_LoadTransTable( fileName );
5851 	}
5852 }
5853 
5854 /*
5855 =======================
5856 CL_TranslateString
5857 =======================
5858 */
CL_TranslateString(const char * string,char * dest_buffer)5859 void CL_TranslateString( const char *string, char *dest_buffer ) {
5860 	int i, count, currentLanguage;
5861 	trans_t *t;
5862 	qboolean newline = qfalse;
5863 	char *buf;
5864 
5865 	buf = dest_buffer;
5866 	currentLanguage = cl_language->integer - 1;
5867 
5868 	// early bail if we only want english or bad language type
5869 	if ( !string ) {
5870 		strcpy( buf, "(null)" );
5871 		return;
5872 	} else if ( currentLanguage == -1 || currentLanguage >= MAX_LANGUAGES || !strlen( string ) )   {
5873 		strcpy( buf, string );
5874 		return;
5875 	}
5876 	// ignore newlines
5877 	if ( string[strlen( string ) - 1] == '\n' ) {
5878 		newline = qtrue;
5879 	}
5880 
5881 	for ( i = 0, count = 0; string[i] != '\0'; i++ ) {
5882 		if ( string[i] != '\n' ) {
5883 			buf[count++] = string[i];
5884 		}
5885 	}
5886 	buf[count] = '\0';
5887 
5888 	t = LookupTrans( buf, NULL, qfalse );
5889 
5890 	if ( t && strlen( t->translated[currentLanguage] ) ) {
5891 		int offset = 0;
5892 
5893 		if ( cl_debugTranslation->integer >= 1 ) {
5894 			buf[0] = '^';
5895 			buf[1] = '1';
5896 			buf[2] = '[';
5897 			offset = 3;
5898 		}
5899 
5900 		strcpy( buf + offset, t->translated[currentLanguage] );
5901 
5902 		if ( cl_debugTranslation->integer >= 1 ) {
5903 			int len2 = strlen( buf );
5904 
5905 			buf[len2] = ']';
5906 			buf[len2 + 1] = '^';
5907 			buf[len2 + 2] = '7';
5908 			buf[len2 + 3] = '\0';
5909 		}
5910 
5911 		if ( newline ) {
5912 			int len2 = strlen( buf );
5913 
5914 			buf[len2] = '\n';
5915 			buf[len2 + 1] = '\0';
5916 		}
5917 	} else {
5918 		int offset = 0;
5919 
5920 		if ( cl_debugTranslation->integer >= 1 ) {
5921 			buf[0] = '^';
5922 			buf[1] = '1';
5923 			buf[2] = '[';
5924 			offset = 3;
5925 		}
5926 
5927 		strcpy( buf + offset, string );
5928 
5929 		if ( cl_debugTranslation->integer >= 1 ) {
5930 			int len2 = strlen( buf );
5931 			qboolean addnewline = qfalse;
5932 
5933 			if ( buf[len2 - 1] == '\n' ) {
5934 				len2--;
5935 				addnewline = qtrue;
5936 			}
5937 
5938 			buf[len2] = ']';
5939 			buf[len2 + 1] = '^';
5940 			buf[len2 + 2] = '7';
5941 			buf[len2 + 3] = '\0';
5942 
5943 			if ( addnewline ) {
5944 				buf[len2 + 3] = '\n';
5945 				buf[len2 + 4] = '\0';
5946 			}
5947 		}
5948 	}
5949 }
5950 
5951 /*
5952 =======================
5953 CL_TranslateStringBuf
5954 TTimo - handy, stores in a static buf, converts \n to chr(13)
5955 =======================
5956 */
CL_TranslateStringBuf(const char * string)5957 const char* CL_TranslateStringBuf( const char *string ) {
5958 	char *p;
5959 	int i,l;
5960 	static char buf[MAX_VA_STRING];
5961 	CL_TranslateString( string, buf );
5962 	while ( ( p = strstr( buf, "\\n" ) ) )
5963 	{
5964 		*p = '\n';
5965 		p++;
5966 		// Com_Memcpy(p, p+1, strlen(p) ); b0rks on win32
5967 		l = strlen( p );
5968 		for ( i = 0; i < l; i++ )
5969 		{
5970 			*p = *( p + 1 );
5971 			p++;
5972 		}
5973 	}
5974 	return buf;
5975 }
5976 
5977 /*
5978 =======================
5979 CL_OpenURLForCvar
5980 =======================
5981 */
CL_OpenURL(const char * url)5982 void CL_OpenURL( const char *url ) {
5983 	if ( !url || !strlen( url ) ) {
5984 		Com_Printf( "%s", CL_TranslateStringBuf( "invalid/empty URL\n" ) );
5985 		return;
5986 	}
5987 	Sys_OpenURL( url, qtrue );
5988 }
5989