1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // cl_parse.c  -- parse a message received from the server
21 
22 #include "client.h"
23 
24 int			serverPacketCount;
25 int			noFrameFromServerPacket;
26 
27 void CL_Reconnect_f (void);
28 
29 //=============================================================================
30 
CL_DownloadFileName(char * dest,int destlen,char * fn)31 void CL_DownloadFileName(char *dest, int destlen, char *fn)
32 {
33 	//if (strncmp(fn, "players", 7) == 0)
34 	//	Com_sprintf (dest, destlen, "%s/%s", BASEDIRNAME, fn);
35 	//else
36 	Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn);
37 }
38 
CL_FinishDownload(void)39 void CL_FinishDownload (void)
40 {
41 #ifdef _DEBUG
42 	clientinfo_t *ci;
43 #endif
44 
45 	int r;
46 	char	oldn[MAX_OSPATH];
47 	char	newn[MAX_OSPATH];
48 
49 	fclose (cls.download);
50 
51 	FS_FlushCache();
52 
53 	// rename the temp file to it's final name
54 	CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
55 	CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
56 
57 	r = rename (oldn, newn);
58 	if (r)
59 		Com_Printf ("failed to rename.\n", LOG_CLIENT);
60 
61 #ifdef _DEBUG
62 	if (cls.serverProtocol == PROTOCOL_R1Q2 && (strstr(newn, "players"))) {
63 		for (r = 0; r < cl.maxclients; r++) {
64 			ci = &cl.clientinfo[r];
65 			if (ci->deferred)
66 				CL_ParseClientinfo (r);
67 		}
68 	}
69 #endif
70 
71 	cls.failed_download = false;
72 	cls.downloadpending = false;
73 	cls.downloadname[0] = 0;
74 	cls.downloadposition = 0;
75 	cls.download = NULL;
76 	cls.downloadpercent = 0;
77 }
78 
79 /*
80 ===============
81 CL_CheckOrDownloadFile
82 
83 Returns true if the file exists, otherwise it attempts
84 to start a download from the server.
85 ===============
86 */
CL_CheckOrDownloadFile(const char * filename)87 qboolean	CL_CheckOrDownloadFile (const char *filename)
88 {
89 	FILE	*fp;
90 	int		length;
91 	char	*p;
92 	char	name[MAX_OSPATH];
93 	static char lastfilename[MAX_OSPATH] = {0};
94 
95 	//r1: don't attempt same file many times
96 	if (!strcmp (filename, lastfilename))
97 		return true;
98 
99 	strcpy (lastfilename, filename);
100 
101 	if (strstr (filename, ".."))
102 	{
103 		Com_Printf ("Refusing to check a path with .. (%s)\n", LOG_CLIENT, filename);
104 		return true;
105 	}
106 
107 	if (strchr (filename, ' '))
108 	{
109 		Com_Printf ("Refusing to check a path containing spaces (%s)\n", LOG_CLIENT, filename);
110 		return true;
111 	}
112 
113 	if (strchr (filename, ':'))
114 	{
115 		Com_Printf ("Refusing to check a path containing a colon (%s)\n", LOG_CLIENT, filename);
116 		return true;
117 	}
118 
119 	if (filename[0] == '/')
120 	{
121 		Com_Printf ("Refusing to check a path starting with / (%s)\n", LOG_CLIENT, filename);
122 		return true;
123 	}
124 
125 	if (FS_LoadFile (filename, NULL) != -1)
126 	{
127 		// it exists, no need to download
128 		return true;
129 	}
130 
131 #ifdef USE_CURL
132 	if (CL_QueueHTTPDownload (filename))
133 	{
134 		//we return true so that the precache check keeps feeding us more files.
135 		//since we have multiple HTTP connections we want to minimize latency
136 		//and be constantly sending requests, not one at a time.
137 		return true;
138 	}
139 	else
140 #endif
141 	{
142 		strcpy (cls.downloadname, filename);
143 
144 		//r1: fix \ to /
145 		p = cls.downloadname;
146 		while ((p = strchr(p, '\\')))
147 			p[0] = '/';
148 
149 		length = (int)strlen(cls.downloadname);
150 
151 		//normalize path
152 		p = cls.downloadname;
153 		while ((p = strstr (p, "./")))
154 		{
155 			memmove (p, p+2, length - (p - cls.downloadname) - 1);
156 			length -= 2;
157 		}
158 
159 		//r1: verify we are giving the server a legal path
160 		if (cls.downloadname[length-1] == '/')
161 		{
162 			Com_Printf ("Refusing to download bad path (%s)\n", LOG_CLIENT, filename);
163 			return true;
164 		}
165 
166 		// download to a temp name, and only rename
167 		// to the real name when done, so if interrupted
168 		// a runt file wont be left
169 		COM_StripExtension (cls.downloadname, cls.downloadtempname);
170 		strcat (cls.downloadtempname, ".tmp");
171 
172 	//ZOID
173 		// check to see if we already have a tmp for this file, if so, try to resume
174 		// open the file if not opened yet
175 		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
176 
177 	//	FS_CreatePath (name);
178 
179 		fp = fopen (name, "r+b");
180 		if (fp)
181 		{
182 			// it exists
183 			int len;
184 
185 			fseek(fp, 0, SEEK_END);
186 			len = ftell(fp);
187 
188 			cls.download = fp;
189 
190 			// give the server an offset to start the download
191 			Com_Printf ("Resuming %s\n", LOG_CLIENT, cls.downloadname);
192 
193 			MSG_WriteByte (clc_stringcmd);
194 			if (cls.serverProtocol == PROTOCOL_R1Q2)
195 				MSG_WriteString (va("download \"%s\" %i udp-zlib", cls.downloadname, len));
196 			else
197 				MSG_WriteString (va("download \"%s\" %i", cls.downloadname, len));
198 		}
199 		else
200 		{
201 			Com_Printf ("Downloading %s\n", LOG_CLIENT, cls.downloadname);
202 
203 			MSG_WriteByte (clc_stringcmd);
204 			if (cls.serverProtocol == PROTOCOL_R1Q2)
205 				MSG_WriteString (va("download \"%s\" 0 udp-zlib", cls.downloadname));
206 			else
207 				MSG_WriteString (va("download \"%s\"", cls.downloadname));
208 		}
209 
210 		MSG_EndWriting (&cls.netchan.message);
211 
212 		send_packet_now = true;
213 		cls.downloadpending = true;
214 
215 		return false;
216 	}
217 }
218 
219 /*
220 ===============
221 CL_Download_f
222 
223 Request a download from the server
224 ===============
225 */
CL_Download_f(void)226 void CL_Download_f (void)
227 {
228 	//char	name[MAX_OSPATH];
229 	//FILE	*fp;
230 //	char	*p;
231 	char	*filename;
232 
233 	if (Cmd_Argc() != 2) {
234 		Com_Printf("Usage: download <filename>\n", LOG_CLIENT);
235 		return;
236 	}
237 
238 	if (cls.state < ca_connected)
239 	{
240 		Com_Printf ("Not connected.\n", LOG_CLIENT);
241 		return;
242 	}
243 
244 	//Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
245 	filename = Cmd_Argv(1);
246 
247 	if (FS_LoadFile (filename, NULL) != -1)
248 	{
249 		// it exists, no need to download
250 		Com_Printf("File already exists.\n", LOG_CLIENT);
251 		return;
252 	}
253 
254 	CL_CheckOrDownloadFile (filename);
255 
256 	/*if (strstr (filename, ".."))
257 	{
258 		Com_Printf ("Refusing to download a path with .. (%s)\n", LOG_CLIENT, filename);
259 		return;
260 	}
261 
262 	if (FS_LoadFile (filename, NULL) != -1)
263 	{	// it exists, no need to download
264 		Com_Printf("File already exists.\n", LOG_CLIENT);
265 		return;
266 	}
267 
268 	strncpy (cls.downloadname, filename, sizeof(cls.downloadname)-1);
269 
270 
271 	// download to a temp name, and only rename
272 	// to the real name when done, so if interrupted
273 	// a runt file wont be left
274 	COM_StripExtension (cls.downloadname, cls.downloadtempname);
275 	strcat (cls.downloadtempname, ".tmp");
276 
277 //ZOID
278 	// check to see if we already have a tmp for this file, if so, try to resume
279 	// open the file if not opened yet
280 	CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
281 
282 	fp = fopen (name, "r+b");
283 	if (fp) { // it exists
284 		int len;
285 
286 		fseek(fp, 0, SEEK_END);
287 		len = ftell(fp);
288 
289 		cls.download = fp;
290 
291 		// give the server an offset to start the download
292 		Com_Printf ("Resuming %s\n", LOG_CLIENT, cls.downloadname);
293 		MSG_WriteByte (clc_stringcmd);
294 		if (cls.serverProtocol == PROTOCOL_R1Q2) {
295 			MSG_WriteString (va("download \"%s\" %i udp-zlib", cls.downloadname, len));
296 		} else {
297 			MSG_WriteString (va("download \"%s\" %i", cls.downloadname, len));
298 		}
299 	} else {
300 		Com_Printf ("Downloading %s\n", LOG_CLIENT, cls.downloadname);
301 
302 		MSG_WriteByte (clc_stringcmd);
303 		if (cls.serverProtocol == PROTOCOL_R1Q2) {
304 			MSG_WriteString (va("download \"%s\" 0 udp-zlib", cls.downloadname));
305 		} else {
306 			MSG_WriteString (va("download \"%s\" 0", cls.downloadname));
307 		}
308 	}
309 	MSG_EndWriting (&cls.netchan.message);
310 
311 	send_packet_now = true;*/
312 }
313 
CL_Passive_f(void)314 void CL_Passive_f (void)
315 {
316 	if (cls.state != ca_disconnected) {
317 		Com_Printf ("Passive mode can only be modified when you are disconnected.\n", LOG_CLIENT);
318 	} else {
319 		cls.passivemode = !cls.passivemode;
320 
321 		if (cls.passivemode) {
322 			NET_Config (NET_CLIENT);
323 			Com_Printf ("Listening for passive connections on port %d\n", LOG_CLIENT, Cvar_IntValue ("ip_clientport"));
324 		} else {
325 			Com_Printf ("No longer listening for passive connections.\n", LOG_CLIENT);
326 		}
327 	}
328 }
329 
330 /*
331 ======================
332 CL_RegisterSounds
333 ======================
334 */
CL_RegisterSounds(void)335 void CL_RegisterSounds (void)
336 {
337 	int		i;
338 
339 	S_BeginRegistration ();
340 	CL_RegisterTEntSounds ();
341 	for (i=1 ; i<MAX_SOUNDS ; i++)
342 	{
343 		if (!cl.configstrings[CS_SOUNDS+i][0])
344 			break;
345 		cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
346 		Sys_SendKeyEvents ();	// pump message loop
347 	}
348 	S_EndRegistration ();
349 }
350 
351 /*
352 =====================
353 CL_ParseDownload
354 
355 A download message has been received from the server
356 =====================
357 */
358 
CL_ParseDownload(qboolean dataIsCompressed)359 void CL_ParseDownload (qboolean dataIsCompressed)
360 {
361 	int		size, percent;
362 	char	name[MAX_OSPATH];
363 
364 	// read the data
365 	size = MSG_ReadShort (&net_message);
366 	percent = MSG_ReadByte (&net_message);
367 
368 	if (size < 0)
369 	{
370 		if (size == -1)
371 			Com_Printf ("Server does not have this file.\n", LOG_CLIENT);
372 		else
373 			Com_Printf ("Bad download data from server.\n", LOG_CLIENT);
374 
375 		//r1: nuke the temp filename
376 		cls.downloadtempname[0] = 0;
377 		cls.downloadname[0] = 0;
378 		cls.failed_download = true;
379 
380 		if (cls.download)
381 		{
382 			// if here, we tried to resume a file but the server said no
383 			fclose (cls.download);
384 			cls.download = NULL;
385 		}
386 
387 		cls.downloadpending = false;
388 		CL_RequestNextDownload ();
389 		return;
390 	}
391 
392 	// open the file if not opened yet
393 	if (!cls.download)
394 	{
395 		if (!cls.downloadtempname[0])
396 		{
397 			Com_Printf ("Received download packet without request. Ignored.\n", LOG_CLIENT);
398 			net_message.readcount += size;
399 			return;
400 		}
401 		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
402 
403 		FS_CreatePath (name);
404 
405 		cls.download = fopen (name, "wb");
406 		if (!cls.download)
407 		{
408 			net_message.readcount += size;
409 			Com_Printf ("Failed to open %s\n", LOG_CLIENT, cls.downloadtempname);
410 			cls.downloadpending = false;
411 			CL_RequestNextDownload ();
412 			return;
413 		}
414 	}
415 
416 	//r1: downloading something, drop to console to show status bar
417 	SCR_EndLoadingPlaque();
418 
419 	//r1: if we're stuck with udp, may as well make best use of the bandwidth...
420 	if (dataIsCompressed)
421 	{
422 #ifndef NO_ZLIB
423 		uint16		uncompressedLen;
424 		byte		uncompressed[0xFFFF];
425 
426 		uncompressedLen = MSG_ReadShort (&net_message);
427 
428 		if (!uncompressedLen)
429 			Com_Error (ERR_DROP, "uncompressedLen == 0");
430 
431 		ZLibDecompress (net_message_buffer + net_message.readcount, size, uncompressed, uncompressedLen, -15);
432 		fwrite (uncompressed, 1, uncompressedLen, cls.download);
433 		Com_DPrintf ("svc_zdownload(%s): %d -> %d\n", cls.downloadname, size, uncompressedLen);
434 #else
435 		Com_Error (ERR_DROP, "Received a unrequested compressed download");
436 #endif
437 	}
438 	else
439 	{
440 		fwrite (net_message_buffer + net_message.readcount, 1, size, cls.download);
441 	}
442 
443 	net_message.readcount += size;
444 
445 	if (percent != 100)
446 	{
447 		cls.downloadpercent = percent;
448 
449 		MSG_WriteByte (clc_stringcmd);
450 		MSG_Print ("nextdl");
451 		MSG_EndWriting (&cls.netchan.message);
452 		send_packet_now = true;
453 	}
454 	else
455 	{
456 		CL_FinishDownload ();
457 
458 		// get another file if needed
459 		CL_RequestNextDownload ();
460 	}
461 }
462 
463 
464 /*
465 =====================================================================
466 
467   SERVER CONNECTING MESSAGES
468 
469 =====================================================================
470 */
471 
472 /*
473 ==================
474 CL_ParseServerData
475 ==================
476 */
CL_ParseServerData(void)477 qboolean CL_ParseServerData (void)
478 {
479 	char	*str;
480 	int		i;
481 	int		newVersion;
482 	cvar_t	*gameDirHack;
483 //
484 // wipe the client_state_t struct
485 //
486 	CL_ClearState ();
487 	cls.state = ca_connected;
488 
489 // parse protocol version number
490 	i = MSG_ReadLong (&net_message);
491 	cls.serverProtocol = i;
492 
493 	cl.servercount = MSG_ReadLong (&net_message);
494 	cl.attractloop = MSG_ReadByte (&net_message);
495 
496 	if (i != PROTOCOL_ORIGINAL && i != PROTOCOL_R1Q2 && !cl.attractloop)
497 		Com_Error (ERR_HARD, "Server is using unknown protocol %d.", i);
498 
499 	// game directory
500 	str = MSG_ReadString (&net_message);
501 	strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
502 
503 	// set gamedir, fucking christ this is messy!
504 	if ((str[0] && (!fs_gamedirvar->string || !fs_gamedirvar->string[0] || strcmp(fs_gamedirvar->string, str))) ||
505 		(!str[0] && (fs_gamedirvar->string || fs_gamedirvar->string[0])))
506 	{
507 		if (strcmp(fs_gamedirvar->string, str))
508 		{
509 			if (cl.attractloop)
510 			{
511 				Cvar_ForceSet ("game", str);
512 				FS_SetGamedir (str);
513 			}
514 			else
515 			{
516 				Cvar_Set("game", str);
517 			}
518 		}
519 	}
520 
521 	Cvar_ForceSet ("$game", str);
522 
523 	gameDirHack = Cvar_FindVar ("game");
524 	gameDirHack->flags |= CVAR_NOSET;
525 
526 	// parse player entity number
527 	cl.playernum = MSG_ReadShort (&net_message);
528 
529 	// get the full level name
530 	str = MSG_ReadString (&net_message);
531 
532 	if (cls.serverProtocol == PROTOCOL_R1Q2)
533 	{
534 		cl.enhancedServer = MSG_ReadByte (&net_message);
535 
536 		newVersion = MSG_ReadShort (&net_message);
537 		if (newVersion != MINOR_VERSION_R1Q2)
538 		{
539 			if (cl.attractloop)
540 			{
541 				if (newVersion < MINOR_VERSION_R1Q2)
542 					Com_Printf ("This demo was recorded with an earlier version of the R1Q2 protocol. It may not play back properly.\n", LOG_CLIENT);
543 				else
544 					Com_Printf ("This demo was recorded with a later version of the R1Q2 protocol. It may not play back properly. Please update your R1Q2 client.\n", LOG_CLIENT);
545 			}
546 			else
547 			{
548 				if (newVersion > MINOR_VERSION_R1Q2)
549 					Com_Printf ("Server reports a higher R1Q2 protocol number than your client supports. Some features will be unavailable until you update your R1Q2 client.\n", LOG_CLIENT);
550 				else
551 					Com_Printf ("Server reports a lower R1Q2 protocol number. The server admin needs to update their server!\n", LOG_CLIENT);
552 			}
553 
554 			//cap if server is above us just to be safe
555 			if (newVersion > MINOR_VERSION_R1Q2)
556 				newVersion = MINOR_VERSION_R1Q2;
557 		}
558 
559 		if (newVersion >= 1903)
560 		{
561 			MSG_ReadByte (&net_message);	//was ad
562 			cl.strafeHack = MSG_ReadByte (&net_message);
563 		}
564 		else
565 		{
566 			cl.strafeHack = false;
567 		}
568 
569 		cls.protocolVersion = newVersion;
570 	}
571 	else
572 	{
573 		cl.enhancedServer = false;
574 		cl.strafeHack = false;
575 		cls.protocolVersion = 0;
576 	}
577 
578 	Com_DPrintf ("Serverdata packet received. protocol=%d, servercount=%d, attractloop=%d, clnum=%d, game=%s, map=%s, enhanced=%d\n", cls.serverProtocol, cl.servercount, cl.attractloop, cl.playernum, cl.gamedir, str, cl.enhancedServer);
579 
580 	if (cl.playernum == -1)
581 	{	// playing a cinematic or showing a pic, not a level
582 		//SCR_PlayCinematic (str);
583 		// tell the server to advance to the next map / cinematic
584 		MSG_WriteByte (clc_stringcmd);
585 		MSG_Print (va("nextserver %i\n", cl.servercount));
586 		MSG_EndWriting (&cls.netchan.message);
587 	}
588 	else
589 	{
590 		// seperate the printfs so the server message can have a color
591 		Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n", LOG_CLIENT);
592 		Com_Printf ("\2%s\n", LOG_CLIENT, str);
593 
594 		// need to prep refresh at next oportunity
595 		cl.refresh_prepped = false;
596 	}
597 
598 	//CL_FixCvarCheats();
599 	return true;
600 }
601 /*
602 ==================
603 CL_ParseBaseline
604 ==================
605 */
CL_ParseBaseline(void)606 void CL_ParseBaseline (void)
607 {
608 	entity_state_t	*es;
609 	uint32			bits;
610 	int				newnum;
611 
612 
613 	newnum = CL_ParseEntityBits (&bits);
614 	es = &cl_entities[newnum].baseline;
615 	CL_ParseDelta (&null_entity_state, es, newnum, bits);
616 }
617 
CL_ParseZPacket(void)618 void CL_ParseZPacket (void)
619 {
620 #ifndef NO_ZLIB
621 	byte buff_in[MAX_MSGLEN];
622 	byte buff_out[0xFFFF];
623 
624 	sizebuf_t sb, old;
625 
626 	int16 compressed_len = MSG_ReadShort (&net_message);
627 	int16 uncompressed_len = MSG_ReadShort (&net_message);
628 
629 	if (uncompressed_len <= 0)
630 		Com_Error (ERR_DROP, "CL_ParseZPacket: uncompressed_len <= 0");
631 
632 	if (compressed_len <= 0)
633 		Com_Error (ERR_DROP, "CL_ParseZPacket: compressed_len <= 0");
634 
635 	MSG_ReadData (&net_message, buff_in, compressed_len);
636 
637 	SZ_Init (&sb, buff_out, uncompressed_len);
638 	sb.cursize = ZLibDecompress (buff_in, compressed_len, buff_out, uncompressed_len, -15);
639 
640 	old = net_message;
641 	net_message = sb;
642 	CL_ParseServerMessage ();
643 	net_message = old;
644 
645 	Com_DPrintf ("Got a ZPacket, %d->%d\n", uncompressed_len + 4, compressed_len);
646 #else
647 	Com_Error (ERR_DROP, "Receied a zPacket but no zlib in this binary");
648 #endif
649 }
650 
651 
652 /*
653 ================
654 CL_LoadClientinfo
655 
656 ================
657 */
CL_LoadClientinfo(clientinfo_t * ci,char * s)658 void CL_LoadClientinfo (clientinfo_t *ci, char *s)
659 {
660 	int i;
661 	char		*t;
662 	//char		original_model_name[MAX_QPATH];
663 	//char		original_skin_name[MAX_QPATH];
664 
665 	char		model_name[MAX_QPATH];
666 	char		skin_name[MAX_QPATH];
667 	char		model_filename[MAX_QPATH];
668 	char		skin_filename[MAX_QPATH];
669 	char		weapon_filename[MAX_QPATH];
670 
671 	Q_strncpy(ci->cinfo, s, sizeof(ci->cinfo)-1);
672 
673 	ci->deferred = false;
674 
675 	// isolate the player's name
676 	Q_strncpy(ci->name, s, sizeof(ci->name)-1);
677 
678 	i = 0;
679 
680 	t = strchr (s, '\\');
681 	if (t)
682 	{
683 		if (t - s >= sizeof(ci->name)-1)
684 		{
685 			i = -1;
686 		}
687 		else
688 		{
689 			ci->name[t-s] = 0;
690 			s = t+1;
691 		}
692 	}
693 
694 	//r1ch: check sanity of paths: only allow printable data
695 	t = s;
696 	while (*t)
697 	{
698 		//if (!isprint (*t))
699 		if (*t <= 32)
700 		{
701 			i = -1;
702 			break;
703 		}
704 		t++;
705 	}
706 
707 	if (cl_noskins->intvalue || s[0] == 0 || i == -1)
708 	{
709 badskin:
710 		//strcpy (model_filename, "players/male/tris.md2");
711 		//strcpy (weapon_filename, "players/male/weapon.md2");
712 		//strcpy (skin_filename, "players/male/grunt.pcx");
713 		strcpy (ci->iconname, "/players/male/grunt_i.pcx");
714 		strcpy (model_name, "male");
715 		ci->model = re.RegisterModel ("players/male/tris.md2");
716 		//memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
717 		//ci->weaponmodel[0] = re.RegisterModel (weapon_filename);
718 		ci->skin = re.RegisterSkin ("players/male/grunt.pcx");
719 		ci->icon = re.RegisterPic (ci->iconname);
720 	}
721 	else
722 	{
723 		int		length;
724 		int		j;
725 
726 		Q_strncpy (model_name, s, sizeof(model_name)-1);
727 
728 		t = strchr(model_name, '/');
729 		if (!t)
730 			t = strchr(model_name, '\\');
731 
732 		if (!t)
733 		{
734 			memcpy (model_name, "male\0grunt\0\0\0\0\0\0", 16);
735 			s = "male\0grunt";
736 		}
737 		else
738 		{
739 			t[0] = 0;
740 		}
741 
742 		//strcpy (original_model_name, model_name);
743 
744 		// isolate the skin name
745 		Q_strncpy (skin_name, s + strlen(model_name) + 1, sizeof(skin_name)-1);
746 		//strcpy (original_skin_name, s + strlen(model_name) + 1);
747 
748 		length = (int)strlen (model_name);
749 		for (j = 0; j < length; j++)
750 		{
751 			if (!isvalidchar(model_name[j]))
752 			{
753 				Com_DPrintf ("Bad character '%c' in playermodel '%s'\n", model_name[j], model_name);
754 				goto badskin;
755 			}
756 		}
757 
758 		length = (int)strlen (skin_name);
759 		for (j = 0; j < length; j++)
760 		{
761 			if (!isvalidchar(skin_name[j]))
762 			{
763 				Com_DPrintf ("Bad character '%c' in playerskin '%s'\n", skin_name[j], skin_name);
764 				goto badskin;
765 			}
766 		}
767 
768 		// model file
769 		Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
770 		ci->model = re.RegisterModel (model_filename);
771 		if (!ci->model)
772 		{
773 			ci->deferred = true;
774 			//if (!CL_CheckOrDownloadFile (model_filename))
775 			//	return;
776 
777 			strcpy(model_name, "male");
778 			//Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
779 			strcpy (model_filename, "players/male/tris.md2");
780 			ci->model = re.RegisterModel (model_filename);
781 		}
782 
783 		// skin file
784 		Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
785 		ci->skin = re.RegisterSkin (skin_filename);
786 
787 		if (!ci->skin)
788 		{
789 			//Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", original_model_name, original_skin_name);
790 			ci->deferred = true;
791 			//CL_CheckOrDownloadFile (skin_filename);
792 		}
793 
794 		// if we don't have the skin and the model wasn't male,
795 		// see if the male has it (this is for CTF's skins)
796  		if (!ci->skin && Q_stricmp(model_name, "male"))
797 		{
798 			// change model to male
799 			strcpy(model_name, "male");
800 			strcpy (model_filename, "players/male/tris.md2");
801 			ci->model = re.RegisterModel (model_filename);
802 
803 			// see if the skin exists for the male model
804 			Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
805 			ci->skin = re.RegisterSkin (skin_filename);
806 		}
807 
808 		// if we still don't have a skin, it means that the male model didn't have
809 		// it, so default to grunt
810 		if (!ci->skin) {
811 			// see if the skin exists for the male model
812 			Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name);
813 			ci->skin = re.RegisterSkin (skin_filename);
814 		}
815 
816 		// icon file
817 		Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
818 		ci->icon = re.RegisterPic (ci->iconname);
819 
820 		if (!ci->icon) {
821 			//Com_sprintf (ci->iconname, sizeof(ci->iconname), "players/%s/%s_i.pcx", original_model_name, original_skin_name);
822 			ci->deferred = true;
823 			//ci->icon = re.RegisterPic ("/players/male/grunt_i.pcx");
824 		}
825 	}
826 
827 	// weapon file
828 	for (i = 0; i < num_cl_weaponmodels; i++)
829 	{
830 		Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
831 		ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
832 
833 		if (!ci->weaponmodel[i])
834 		{
835 			//Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", original_model_name, cl_weaponmodels[i]);
836 			ci->deferred = true;
837 		}
838 
839 		if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0)
840 		{
841 			// try male
842 			Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
843 			ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
844 		}
845 
846 		if (!cl_vwep->intvalue)
847 			break; // only one when vwep is off
848 	}
849 
850 	// must have loaded all data types to be valud
851 	if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
852 	{
853 		ci->skin = NULL;
854 		ci->icon = NULL;
855 		ci->model = NULL;
856 		ci->weaponmodel[0] = NULL;
857 		return;
858 	}
859 }
860 
861 /*
862 ================
863 CL_ParseClientinfo
864 
865 Load the skin, icon, and model for a client
866 ================
867 */
CL_ParseClientinfo(int player)868 void CL_ParseClientinfo (int player)
869 {
870 	char			*s;
871 	clientinfo_t	*ci;
872 
873 	s = cl.configstrings[player+CS_PLAYERSKINS];
874 
875 	ci = &cl.clientinfo[player];
876 
877 	CL_LoadClientinfo (ci, s);
878 }
879 
880 
881 /*
882 ================
883 CL_ParseConfigString
884 ================
885 */
CL_ParseConfigString(void)886 void CL_ParseConfigString (void)
887 {
888 	size_t	length;
889 	int		i;
890 	char	*s;
891 	char	olds[MAX_QPATH];
892 
893 	i = MSG_ReadShort (&net_message);
894 	if (i < 0 || i >= MAX_CONFIGSTRINGS)
895 		Com_Error (ERR_DROP, "CL_ParseConfigString: configstring %d >= MAX_CONFIGSTRINGS", i);
896 	s = MSG_ReadString(&net_message);
897 
898 	Q_strncpy (olds, cl.configstrings[i], sizeof(olds)-1);
899 
900 	//Com_Printf ("cs: %i=%s\n", LOG_GENERAL, i, MakePrintable (s));
901 
902 	//r1ch: only allow statusbar to overflow
903 	/*if (i >= CS_STATUSBAR && i < CS_AIRACCEL)
904 		strncpy (cl.configstrings[i], s, (sizeof(cl.configstrings[i]) * (CS_AIRACCEL - i))-1);
905 	else
906 		Q_strncpy (cl.configstrings[i], s, sizeof(cl.configstrings[i])-1);*/
907 
908 	//r1: overflow may be desired by some mods in stats programs for example. who knows.
909 	length = strlen(s);
910 
911 	if (length >= (sizeof(cl.configstrings[0]) * (MAX_CONFIGSTRINGS-i)) - 1)
912 		Com_Error (ERR_DROP, "CL_ParseConfigString: configstring %d exceeds available space", i);
913 
914 	//r1: don't allow basic things to overflow
915 	if (i != CS_NAME && i < CS_GENERAL)
916 	{
917 		if (i >= CS_STATUSBAR && i < CS_AIRACCEL)
918 		{
919 			strncpy (cl.configstrings[i], s, (sizeof(cl.configstrings[i]) * (CS_AIRACCEL - i))-1);
920 		}
921 		else
922 		{
923 			if (length >= MAX_QPATH)
924 				Com_Printf ("WARNING: Configstring %d of length %d exceeds MAX_QPATH.\n", LOG_CLIENT|LOG_WARNING, i, (int)length);
925 			Q_strncpy (cl.configstrings[i], s, sizeof(cl.configstrings[i])-1);
926 		}
927 	}
928 	else
929 	{
930 		strcpy (cl.configstrings[i], s);
931 	}
932 
933 	// do something apropriate
934 	if (i == CS_AIRACCEL)
935 	{
936 		pm_airaccelerate = (qboolean)atoi(cl.configstrings[CS_AIRACCEL]);
937 	}
938 	else if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES)
939 	{
940 		CL_SetLightstyle (i - CS_LIGHTS);
941 	}
942 #ifdef CD_AUDIO
943 	else if (i == CS_CDTRACK)
944 	{
945 		if (cl.refresh_prepped)
946 			CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
947 	}
948 #endif
949 	else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS)
950 	{
951 		if (cl.refresh_prepped)
952 		{
953 			cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]);
954 			if (cl.configstrings[i][0] == '*')
955 				cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
956 			else
957 				cl.model_clip[i-CS_MODELS] = NULL;
958 		}
959 
960 		//r1: load map whilst connecting to save a bit of time
961 		/*if (i == CS_MODELS + 1)
962 		{
963 			CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &i);
964 			if (i && i != atoi(cl.configstrings[CS_MAPCHECKSUM]))
965 				Com_Error (ERR_DROP, "Local map version differs from server: 0x%.8x != 0x%.8x\n",
966 					i, atoi(cl.configstrings[CS_MAPCHECKSUM]));
967 		}*/
968 	}
969 	else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS)
970 	{
971 		if (cl.refresh_prepped)
972 			cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]);
973 	}
974 	else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS)
975 	{
976 		if (cl.refresh_prepped)
977 			re.RegisterPic (cl.configstrings[i]);
978 	}
979 	else if (i == CS_MAXCLIENTS)
980 	{
981 		if (!cl.attractloop)
982 			cl.maxclients = atoi(cl.configstrings[CS_MAXCLIENTS]);
983 	}
984 	else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS)
985 	{
986 		//r1: hack to avoid parsing non-skins from mods that overload CS_PLAYERSKINS
987 		//FIXME: how reliable is CS_MAXCLIENTS?
988 		i -= CS_PLAYERSKINS;
989 		if (i < cl.maxclients)
990 		{
991 			if (cl.refresh_prepped && strcmp(olds, s))
992 				CL_ParseClientinfo (i);
993 		}
994 		else
995 		{
996 			Com_DPrintf ("CL_ParseConfigString: Ignoring out-of-range playerskin %d (%s)\n", i, MakePrintable(s, 0));
997 		}
998 	}
999 }
1000 
1001 /*
1002 =====================================================================
1003 
1004 ACTION MESSAGES
1005 
1006 =====================================================================
1007 */
1008 
1009 /*
1010 ==================
1011 CL_ParseStartSoundPacket
1012 ==================
1013 */
CL_ParseStartSoundPacket(void)1014 void CL_ParseStartSoundPacket(void)
1015 {
1016     vec3_t  pos_v;
1017 	float	*pos;
1018     int 	channel, ent;
1019     int 	sound_num;
1020     float 	volume;
1021     float 	attenuation;
1022 	int		flags;
1023 	float	ofs;
1024 
1025 	flags = MSG_ReadByte (&net_message);
1026 	if (flags == -1)
1027 		Com_Error (ERR_DROP, "CL_ParseStartSoundPacket: End of message while reading flags");
1028 
1029 	sound_num = MSG_ReadByte (&net_message);
1030 	if (sound_num == -1)
1031 		Com_Error (ERR_DROP, "CL_ParseStartSoundPacket: End of message while reading sound_num");
1032 
1033     if (flags & SND_VOLUME)
1034 		volume = MSG_ReadByte (&net_message) / 255.0f;
1035 	else
1036 		volume = DEFAULT_SOUND_PACKET_VOLUME;
1037 
1038     if (flags & SND_ATTENUATION)
1039 	{
1040 		int	attn;
1041 		attn = MSG_ReadByte (&net_message);
1042 		if (attn == -1)
1043 			Com_Error (ERR_DROP, "CL_ParseStartSoundPacket: End of message while reading attenuation");
1044 		attenuation = attn / 64.0f;
1045 	}
1046 	else
1047 		attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
1048 
1049     if (flags & SND_OFFSET)
1050 	{
1051 		int	offset;
1052 		offset = MSG_ReadByte (&net_message);
1053 		if (offset == -1)
1054 			Com_Error (ERR_DROP, "CL_ParseStartSoundPacket: End of message while reading offset");
1055 
1056 		ofs = offset / 1000.0f;
1057 	}
1058 	else
1059 		ofs = 0;
1060 
1061 	if (flags & SND_ENT)
1062 	{	// entity reletive
1063 		channel = MSG_ReadShort(&net_message);
1064 		if (channel == -1)
1065 			Com_Error (ERR_DROP, "CL_ParseStartSoundPacket: End of message while reading channel");
1066 
1067 		ent = channel>>3;
1068 
1069 		if (ent < 0 || ent > MAX_EDICTS)
1070 			Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
1071 
1072 		channel &= 7;
1073 	}
1074 	else
1075 	{
1076 		ent = 0;
1077 		channel = 0;
1078 	}
1079 
1080 	if (flags & SND_POS)
1081 	{	// positioned in space
1082 		MSG_ReadPos (&net_message, pos_v);
1083 
1084 		pos = pos_v;
1085 	}
1086 	else	// use entity number
1087 		pos = NULL;
1088 
1089 	if (!cl.sound_precache[sound_num])
1090 		return;
1091 
1092 	S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
1093 }
1094 
CL_ServerFPSChanged(void)1095 static void CL_ServerFPSChanged (void)
1096 {
1097 	centity_t	*cent;
1098 	int			i;
1099 
1100 	cl.gunlerp_start = cl.gunlerp_end = 0;
1101 
1102 	for (i = 0; i < MAX_ENTITIES; i++)
1103 	{
1104 		cent = &cl_entities[i];
1105 		cent->lerp_time = 0;
1106 	}
1107 }
1108 
CL_ParseSetting(void)1109 static void CL_ParseSetting (void)
1110 {
1111 	uint32	setting, value;
1112 
1113 	setting = MSG_ReadLong (&net_message);
1114 	value = MSG_ReadLong (&net_message);
1115 
1116 	if (setting >= SVSET_MAX)
1117 		return;
1118 
1119 	cl.settings[setting] = value;
1120 
1121 	//if FPS changed, reset some internal lerp variables
1122 	if (setting == SVSET_FPS)
1123 		CL_ServerFPSChanged ();
1124 }
1125 
CL_CheckForIP(const char * s)1126 static void CL_CheckForIP (const char *s)
1127 {
1128 	unsigned int	ip1, ip2, ip3, ip4;
1129 	unsigned int	port;
1130 
1131 	port = 0;
1132 
1133 	while (s[0])
1134 	{
1135 		if (sscanf (s, "%u.%u.%u.%u", &ip1, &ip2, &ip3, &ip4) == 4)
1136 		{
1137 			if (ip1 < 256 && ip2 < 256 && ip3 < 256 && ip4 < 256)
1138 			{
1139 				const char *p;
1140 				p = strrchr (s, ':');
1141 
1142 				if (p)
1143 				{
1144 					p++;
1145 					port = strtoul (p, NULL, 10);
1146 					if (port <= 1024 || port > 65535)
1147 						break;
1148 				}
1149 
1150 				if (port == 0)
1151 					port = PORT_SERVER;
1152 
1153 				Com_sprintf (cls.followHost, sizeof(cls.followHost), "%u.%u.%u.%u:%u", ip1, ip2, ip3, ip4, port);
1154 				break;
1155 			}
1156 		}
1157 		s++;
1158 	}
1159 }
1160 
CL_CheckForURL(const char * s)1161 static void CL_CheckForURL (const char *s)
1162 {
1163 	char	followURL[1024];
1164 	char	*p;
1165 
1166 	p = strstr (s, "http://");
1167 	if (p)
1168 	{
1169 		Q_strncpy (followURL, p, sizeof(followURL)-1);
1170 		StripHighBits (followURL, 1);
1171 		p = strchr (followURL, ' ');
1172 		if (p)
1173 			p[0] = '\0';
1174 
1175 		Sys_UpdateURLMenu (followURL);
1176 	}
1177 }
1178 
SHOWNET(const char * s)1179 void SHOWNET(const char *s)
1180 {
1181 	if (cl_shownet->intvalue>=2)
1182 		Com_Printf ("%3i:%s\n", LOG_CLIENT, net_message.readcount-1, s);
1183 }
1184 
CL_ParsePrint(void)1185 void CL_ParsePrint (void)
1186 {
1187 	int		i;
1188 	char	*s;
1189 
1190 	i = MSG_ReadByte (&net_message);
1191 	s = MSG_ReadString (&net_message);
1192 
1193 	if (i == PRINT_CHAT)
1194 	{
1195 		if (CL_IgnoreMatch (s))
1196 			return;
1197 
1198 		S_StartLocalSound ("misc/talk.wav");
1199 		if (cl_filterchat->intvalue)
1200 		{
1201 			StripHighBits(s, (int)cl_filterchat->intvalue == 2);
1202 			strcat (s, "\n");
1203 		}
1204 		con.ormask = 128;
1205 
1206 		CL_CheckForIP (s);
1207 		CL_CheckForURL (s);
1208 		SCR_AddChatMessage (s);
1209 
1210 		//r1: change !p_version to !version since p is for proxies
1211 		if ((strstr (s, "!r1q2_version") || strstr (s, "!version")) &&
1212 			(cls.lastSpamTime == 0 || cls.realtime > cls.lastSpamTime + 300000))
1213 			cls.spamTime = cls.realtime + (int)(random() * 1500);
1214 
1215 		Com_Printf ("%s", LOG_CLIENT|LOG_CHAT, s);
1216 	}
1217 	else
1218 	{
1219 		int		len;
1220 
1221 		Com_Printf ("%s", LOG_CLIENT, s);
1222 
1223 		//strip newline for trigger match
1224 		len = strlen(s);
1225 		if (s[len-1] == '\n')
1226 			s[len-1] = '\0';
1227 
1228 		Cmd_ExecTrigger (s); //Triggers
1229 	}
1230 
1231 	con.ormask = 0;
1232 }
1233 
1234 /*
1235 =====================
1236 CL_ParseServerMessage
1237 =====================
1238 */
CL_ParseServerMessage(void)1239 qboolean CL_ParseServerMessage (void)
1240 {
1241 	int			cmd, extrabits;
1242 	char		*s;
1243 	int			oldReadCount;
1244 	qboolean	gotFrame, ret;
1245 
1246 //
1247 // if recording demos, copy the message out
1248 //
1249 	if (cl_shownet->intvalue == 1)
1250 		Com_Printf ("%i ", LOG_CLIENT, net_message.cursize);
1251 	else if (cl_shownet->intvalue >= 2)
1252 		Com_Printf ("------------------\n", LOG_CLIENT);
1253 
1254 	serverPacketCount++;
1255 	gotFrame = false;
1256 	ret = true;
1257 
1258 //
1259 // parse the message
1260 //
1261 	for (;;)
1262 	{
1263 		if (net_message.readcount > net_message.cursize)
1264 		{
1265 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message (%d>%d)", net_message.readcount, net_message.cursize);
1266 			break;
1267 		}
1268 
1269 		oldReadCount = net_message.readcount;
1270 
1271 		cmd = MSG_ReadByte (&net_message);
1272 
1273 		if (cmd == -1)
1274 		{
1275 			SHOWNET("END OF MESSAGE");
1276 			break;
1277 		}
1278 
1279 #ifdef _DEBUG
1280 		if (cmd == 31)
1281 			Sys_DebugBreak ();
1282 #endif
1283 
1284 		//r1: more hacky bit stealing in the name of bandwidth
1285 		extrabits = cmd & 0xE0;
1286 		cmd &= 0x1F;
1287 
1288 		if (cl_shownet->intvalue>=2)
1289 		{
1290 			if (cmd >= svc_max_enttypes)
1291 				Com_Printf ("%3i:BAD CMD %i\n", LOG_CLIENT, net_message.readcount-1,cmd);
1292 			else
1293 				SHOWNET(svc_strings[cmd]);
1294 		}
1295 
1296 	// other commands
1297 		switch (cmd)
1298 		{
1299 		case svc_muzzleflash:
1300 			CL_ParseMuzzleFlash ();
1301 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1302 			break;
1303 
1304 		case svc_muzzleflash2:
1305 			CL_ParseMuzzleFlash2 ();
1306 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1307 			break;
1308 
1309 		case svc_temp_entity:
1310 			CL_ParseTEnt ();
1311 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1312 			break;
1313 
1314 		case svc_layout:
1315 			s = MSG_ReadString (&net_message);
1316 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1317 			strncpy (cl.layout, s, sizeof(cl.layout)-1);
1318 			break;
1319 
1320 		case svc_inventory:
1321 			CL_ParseInventory ();
1322 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1323 			break;
1324 
1325 		case svc_nop:
1326 			break;
1327 
1328 		case svc_disconnect:
1329 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1330 			Com_Error (ERR_DISCONNECT, "Server disconnected\n");
1331 			break;
1332 
1333 		case svc_reconnect:
1334 			Com_Printf ("Server disconnected, reconnecting\n", LOG_CLIENT);
1335 			if (cls.download) {
1336 				//ZOID, close download
1337 				fclose (cls.download);
1338 				cls.download = NULL;
1339 			}
1340 			cls.downloadname[0] = 0;
1341 			cls.state = ca_connecting;
1342 			cls.connect_time = -99999;	// CL_CheckForResend() will fire immediately
1343 			break;
1344 
1345 		case svc_sound:
1346 			CL_ParseStartSoundPacket();
1347 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1348 			break;
1349 
1350 		case svc_print:
1351 			CL_ParsePrint ();
1352 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1353 
1354 			break;
1355 
1356 		case svc_stufftext:
1357 			s = MSG_ReadString (&net_message);
1358 			Com_DPrintf ("stufftext: %s\n", s);
1359 
1360 			//ugly, but necessary :(
1361 			if (!cl.attractloop || !strcmp(s, "precache\n"))
1362 				Cbuf_AddText (s);
1363 			else
1364 				Com_DPrintf ("WARNING: Demo tried to execute command '%s', ignored.\n", MakePrintable(s, 0));
1365 			break;
1366 
1367 		case svc_serverdata:
1368 			Cbuf_Execute ();		// make sure any stuffed commands are done
1369 			if (!CL_ParseServerData ())
1370 				return true;
1371 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1372 			break;
1373 
1374 		case svc_configstring:
1375 			CL_ParseConfigString ();
1376 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1377 			break;
1378 
1379 		case svc_spawnbaseline:
1380 			CL_ParseBaseline ();
1381 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1382 			break;
1383 
1384 		case svc_centerprint:
1385 			SCR_CenterPrint (MSG_ReadString (&net_message));
1386 			CL_WriteDemoMessage (net_message.data + oldReadCount, net_message.readcount - oldReadCount, false);
1387 			break;
1388 
1389 		case svc_download:
1390 			CL_ParseDownload (false);
1391 			break;
1392 
1393 		case svc_playerinfo:
1394 		case svc_packetentities:
1395 		case svc_deltapacketentities:
1396 #ifdef _DEBUG
1397 			Sys_DebugBreak ();
1398 #endif
1399 			Com_Error (ERR_DROP, "Out of place frame data");
1400 			break;
1401 
1402 		case svc_frame:
1403 			//note, frame is written to demo stream in a special way (see cl_ents.c)
1404 			CL_ParseFrame (extrabits);
1405 			gotFrame = true;
1406 			break;
1407 
1408 		// ************** r1q2 specific BEGIN ****************
1409 		case svc_zpacket:
1410 			//contents of zpackets are written to demo implicity on decompress
1411 			CL_ParseZPacket();
1412 			break;
1413 
1414 		case svc_zdownload:
1415 			CL_ParseDownload(true);
1416 			break;
1417 
1418 		case svc_playerupdate:
1419 			gotFrame = true;
1420 			ret = false;
1421 			CL_ParsePlayerUpdate ();
1422 			break;
1423 
1424 		case svc_setting:
1425 			CL_ParseSetting ();
1426 			break;
1427 		// ************** r1q2 specific END ******************
1428 
1429 		default:
1430 #ifdef _DEBUG
1431 			//Sys_DebugBreak ();
1432 #endif
1433 			if (developer->intvalue)
1434 			{
1435 				Com_Printf ("Unknown command char %d, ignoring!!\n", LOG_CLIENT, cmd);
1436 			}
1437 			else
1438 			{
1439 				/*if (cls.serverProtocol != PROTOCOL_ORIGINAL && cls.realtime - cls.connect_time < 30000)
1440 				{
1441 					Com_Printf ("Unknown command byte %d, assuming protocol mismatch. Reconnecting with protocol 34.\nPlease be sure that you and the server are using the latest build of R1Q2.\n", LOG_CLIENT, cmd);
1442 					CL_Disconnect(false);
1443 					cls.serverProtocol = PROTOCOL_ORIGINAL;
1444 					CL_Reconnect_f ();
1445 					return;
1446 				}*/
1447 				Com_Error (ERR_DROP,"CL_ParseServerMessage: Unknown command byte %d (0x%.2x)", cmd, cmd);
1448 			}
1449 			break;
1450 
1451 		}
1452 	}
1453 
1454 	if (!gotFrame)
1455 		noFrameFromServerPacket++;
1456 	else
1457 		noFrameFromServerPacket = 0;
1458 
1459 	//flush this frame
1460 	CL_WriteDemoMessage (NULL, 0, true);
1461 
1462 	return ret;
1463 }
1464