1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 // cl_parse.c  -- parse a message received from the server
26 
27 #include "client.h"
28 #include "cl_cgameapi.h"
29 #include "qcommon/stringed_ingame.h"
30 
31 #ifdef USE_INTERNAL_ZLIB
32 #include "zlib/zlib.h"
33 #else
34 #include <zlib.h>
35 #endif
36 
37 static char hiddenCvarVal[128];
38 
39 char *svc_strings[256] = {
40 	"svc_bad",
41 
42 	"svc_nop",
43 	"svc_gamestate",
44 	"svc_configstring",
45 	"svc_baseline",
46 	"svc_serverCommand",
47 	"svc_download",
48 	"svc_snapshot",
49 	"svc_setgame",
50 	"svc_mapchange",
51 };
52 
SHOWNET(msg_t * msg,char * s)53 void SHOWNET( msg_t *msg, char *s) {
54 	if ( cl_shownet->integer >= 2) {
55 		Com_Printf ("%3i:%s\n", msg->readcount-1, s);
56 	}
57 }
58 
59 /*
60 =========================================================================
61 
62 MESSAGE PARSING
63 
64 =========================================================================
65 */
66 
67 /*
68 ==================
69 CL_DeltaEntity
70 
71 Parses deltas from the given base and adds the resulting entity
72 to the current frame
73 ==================
74 */
CL_DeltaEntity(msg_t * msg,clSnapshot_t * frame,int newnum,entityState_t * old,qboolean unchanged)75 void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
76 					 qboolean unchanged) {
77 	entityState_t	*state;
78 
79 	// save the parsed entity state into the big circular buffer so
80 	// it can be used as the source for a later delta
81 	state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
82 
83 	if ( unchanged )
84 	{
85 		*state = *old;
86 	}
87 	else
88 	{
89 		MSG_ReadDeltaEntity( msg, old, state, newnum );
90 	}
91 
92 	if ( state->number == (MAX_GENTITIES-1) ) {
93 		return;		// entity was delta removed
94 	}
95 	cl.parseEntitiesNum++;
96 	frame->numEntities++;
97 }
98 
99 /*
100 ==================
101 CL_ParsePacketEntities
102 
103 ==================
104 */
CL_ParsePacketEntities(msg_t * msg,clSnapshot_t * oldframe,clSnapshot_t * newframe)105 void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
106 	int			newnum;
107 	entityState_t	*oldstate;
108 	int			oldindex, oldnum;
109 
110 	newframe->parseEntitiesNum = cl.parseEntitiesNum;
111 	newframe->numEntities = 0;
112 
113 	// delta from the entities present in oldframe
114 	oldindex = 0;
115 	oldstate = NULL;
116 	if (!oldframe) {
117 		oldnum = 99999;
118 	} else {
119 		if ( oldindex >= oldframe->numEntities ) {
120 			oldnum = 99999;
121 		} else {
122 			oldstate = &cl.parseEntities[
123 				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
124 			oldnum = oldstate->number;
125 		}
126 	}
127 
128 	while ( 1 ) {
129 		// read the entity index number
130 		newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
131 
132 		if ( newnum == (MAX_GENTITIES-1) ) {
133 			break;
134 		}
135 
136 		if ( msg->readcount > msg->cursize ) {
137 			Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
138 		}
139 
140 		while ( oldnum < newnum ) {
141 			// one or more entities from the old packet are unchanged
142 			if ( cl_shownet->integer == 3 ) {
143 				Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
144 			}
145 			CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
146 
147 			oldindex++;
148 
149 			if ( oldindex >= oldframe->numEntities ) {
150 				oldnum = 99999;
151 			} else {
152 				oldstate = &cl.parseEntities[
153 					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
154 				oldnum = oldstate->number;
155 			}
156 		}
157 		if (oldnum == newnum) {
158 			// delta from previous state
159 			if ( cl_shownet->integer == 3 ) {
160 				Com_Printf ("%3i:  delta: %i\n", msg->readcount, newnum);
161 			}
162 			CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
163 
164 			oldindex++;
165 
166 			if ( oldindex >= oldframe->numEntities ) {
167 				oldnum = 99999;
168 			} else {
169 				oldstate = &cl.parseEntities[
170 					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
171 				oldnum = oldstate->number;
172 			}
173 			continue;
174 		}
175 
176 		if ( oldnum > newnum ) {
177 			// delta from baseline
178 			if ( cl_shownet->integer == 3 ) {
179 				Com_Printf ("%3i:  baseline: %i\n", msg->readcount, newnum);
180 			}
181 			CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
182 			continue;
183 		}
184 
185 	}
186 
187 	// any remaining entities in the old frame are copied over
188 	while ( oldnum != 99999 ) {
189 		// one or more entities from the old packet are unchanged
190 		if ( cl_shownet->integer == 3 ) {
191 			Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
192 		}
193 		CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
194 
195 		oldindex++;
196 
197 		if ( oldindex >= oldframe->numEntities ) {
198 			oldnum = 99999;
199 		} else {
200 			oldstate = &cl.parseEntities[
201 				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
202 			oldnum = oldstate->number;
203 		}
204 	}
205 }
206 
207 
208 /*
209 ================
210 CL_ParseSnapshot
211 
212 If the snapshot is parsed properly, it will be copied to
213 cl.snap and saved in cl.snapshots[].  If the snapshot is invalid
214 for any reason, no changes to the state will be made at all.
215 ================
216 */
CL_ParseSnapshot(msg_t * msg)217 void CL_ParseSnapshot( msg_t *msg ) {
218 	int			len;
219 	clSnapshot_t	*old;
220 	clSnapshot_t	newSnap;
221 	int			deltaNum;
222 	int			oldMessageNum;
223 	int			i, packetNum;
224 
225 	// get the reliable sequence acknowledge number
226 	// NOTE: now sent with all server to client messages
227 	//clc.reliableAcknowledge = MSG_ReadLong( msg );
228 
229 	// read in the new snapshot to a temporary buffer
230 	// we will only copy to cl.snap if it is valid
231 	Com_Memset (&newSnap, 0, sizeof(newSnap));
232 
233 	// we will have read any new server commands in this
234 	// message before we got to svc_snapshot
235 	newSnap.serverCommandNum = clc.serverCommandSequence;
236 
237 	newSnap.serverTime = MSG_ReadLong( msg );
238 
239 	// if we were just unpaused, we can only *now* really let the
240 	// change come into effect or the client hangs.
241 	cl_paused->modified = qfalse;
242 
243 	newSnap.messageNum = clc.serverMessageSequence;
244 
245 	deltaNum = MSG_ReadByte( msg );
246 	if ( !deltaNum ) {
247 		newSnap.deltaNum = -1;
248 	} else {
249 		newSnap.deltaNum = newSnap.messageNum - deltaNum;
250 	}
251 	newSnap.snapFlags = MSG_ReadByte( msg );
252 
253 	// If the frame is delta compressed from data that we
254 	// no longer have available, we must suck up the rest of
255 	// the frame, but not use it, then ask for a non-compressed
256 	// message
257 	if ( newSnap.deltaNum <= 0 ) {
258 		newSnap.valid = qtrue;		// uncompressed frame
259 		old = NULL;
260 		clc.demowaiting = qfalse;	// we can start recording now
261 	} else {
262 		old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
263 		if ( !old->valid ) {
264 			// should never happen
265 			Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
266 			while ( ( newSnap.deltaNum & PACKET_MASK ) != ( newSnap.messageNum & PACKET_MASK ) && !old->valid ) {
267 				newSnap.deltaNum++;
268 				old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
269 			}
270 			if ( old->valid ) {
271 				Com_Printf ("Found more recent frame to delta from.\n");
272 			}
273 		}
274 		if ( !old->valid ) {
275 			Com_Printf ("Failed to find more recent frame to delta from.\n");
276 		} else if ( old->messageNum != newSnap.deltaNum ) {
277 			// The frame that the server did the delta from
278 			// is too old, so we can't reconstruct it properly.
279 			Com_Printf ("Delta frame too old.\n");
280 		} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
281 			Com_DPrintf ("Delta parseEntitiesNum too old.\n");
282 		} else {
283 			newSnap.valid = qtrue;	// valid delta parse
284 		}
285 	}
286 
287 	// read areamask
288 	len = MSG_ReadByte( msg );
289 
290 	if((unsigned)len > sizeof(newSnap.areamask))
291 	{
292 		Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len);
293 		return;
294 	}
295 
296 	MSG_ReadData( msg, &newSnap.areamask, len);
297 
298 	// read playerinfo
299 	SHOWNET( msg, "playerstate" );
300 	if ( old ) {
301 		MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
302 		if (newSnap.ps.m_iVehicleNum)
303 		{ //this means we must have written our vehicle's ps too
304 			MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue );
305 		}
306 	} else {
307 		MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
308 		if (newSnap.ps.m_iVehicleNum)
309 		{ //this means we must have written our vehicle's ps too
310 			MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue );
311 		}
312 	}
313 
314 	// read packet entities
315 	SHOWNET( msg, "packet entities" );
316 	CL_ParsePacketEntities( msg, old, &newSnap );
317 
318 	// if not valid, dump the entire thing now that it has
319 	// been properly read
320 	if ( !newSnap.valid ) {
321 		return;
322 	}
323 
324 	// clear the valid flags of any snapshots between the last
325 	// received and this one, so if there was a dropped packet
326 	// it won't look like something valid to delta from next
327 	// time we wrap around in the buffer
328 	oldMessageNum = cl.snap.messageNum + 1;
329 
330 	if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
331 		oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
332 	}
333 	for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
334 		cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
335 	}
336 
337 	// copy to the current good spot
338 	cl.snap = newSnap;
339 	cl.snap.ping = 999;
340 	// calculate ping time
341 	for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
342 		packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
343 		if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
344 			cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
345 			break;
346 		}
347 	}
348 	// save the frame off in the backup array for later delta comparisons
349 	cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
350 
351 	if (cl_shownet->integer == 3) {
352 		Com_Printf( "   snapshot:%i  delta:%i  ping:%i\n", cl.snap.messageNum,
353 		cl.snap.deltaNum, cl.snap.ping );
354 	}
355 
356 	cl.newSnapshots = qtrue;
357 }
358 
359 
360 /*
361 ================
362 CL_ParseSetGame
363 
364 rww - Update fs_game, this message is so we can use the ext_data
365 *_overrides.txt files for mods.
366 ================
367 */
368 void MSG_CheckNETFPSFOverrides(qboolean psfOverrides);
369 void FS_UpdateGamedir(void);
CL_ParseSetGame(msg_t * msg)370 void CL_ParseSetGame( msg_t *msg )
371 {
372 	char newGameDir[MAX_QPATH];
373 	int i = 0;
374 	char next;
375 
376 	while (i < MAX_QPATH)
377 	{
378 		next = MSG_ReadByte( msg );
379 
380 		if (next)
381 		{ //if next is 0 then we have finished reading to the end of the message
382 			newGameDir[i] = next;
383 		}
384 		else
385 		{
386 			break;
387 		}
388 		i++;
389 	}
390 	newGameDir[i] = 0;
391 
392 	if(FS_CheckDirTraversal(newGameDir))
393 	{
394 		Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", newGameDir);
395 		return;
396 	}
397 
398 	if(!FS_FilenameCompare(newGameDir, BASEGAME))
399 		Cvar_Set("fs_game", "");
400 	else
401 		Cvar_Set("fs_game", newGameDir);
402 
403 	if(!(Cvar_Flags("fs_game") & CVAR_MODIFIED))
404 		return;
405 
406 	//Update the search path for the mod dir
407 	FS_UpdateGamedir();
408 
409 	//Now update the overrides manually
410 	MSG_CheckNETFPSFOverrides(qfalse);
411 	MSG_CheckNETFPSFOverrides(qtrue);
412 }
413 
414 
415 //=====================================================================
416 
417 int cl_connectedToPureServer;
418 int cl_connectedToCheatServer;
419 
420 /*
421 ==================
422 CL_SystemInfoChanged
423 
424 The systeminfo configstring has been changed, so parse
425 new information out of it.  This will happen at every
426 gamestate, and possibly during gameplay.
427 ==================
428 */
CL_SystemInfoChanged(void)429 void CL_SystemInfoChanged( void ) {
430 	char			*systemInfo;
431 	const char		*s, *t;
432 	char			key[BIG_INFO_KEY];
433 	char			value[BIG_INFO_VALUE];
434 	qboolean		gameSet;
435 
436 	systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
437 	// NOTE TTimo:
438 	// when the serverId changes, any further messages we send to the server will use this new serverId
439 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
440 	// in some cases, outdated cp commands might get sent with this new serverId
441 	cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
442 
443 	// don't set any vars when playing a demo
444 	if ( clc.demoplaying ) {
445 		return;
446 	}
447 
448 	s = Info_ValueForKey( systemInfo, "sv_cheats" );
449 	cl_connectedToCheatServer = atoi( s );
450 	if ( !cl_connectedToCheatServer )
451 	{
452 		Cvar_SetCheatState();
453 	}
454 
455 	// check pure server string
456 	s = Info_ValueForKey( systemInfo, "sv_paks" );
457 	t = Info_ValueForKey( systemInfo, "sv_pakNames" );
458 	FS_PureServerSetLoadedPaks( s, t );
459 
460 	s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
461 	t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
462 	FS_PureServerSetReferencedPaks( s, t );
463 
464 	gameSet = qfalse;
465 	// scan through all the variables in the systeminfo and locally set cvars to match
466 	s = systemInfo;
467 	while ( s ) {
468 		Info_NextPair( &s, key, value );
469 		if ( !key[0] ) {
470 			break;
471 		}
472 		// ehw!
473 		if ( !Q_stricmp( key, "fs_game" ) ) {
474 			if(FS_CheckDirTraversal(value))
475 			{
476 				Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value);
477 				continue;
478 			}
479 
480 			if(!FS_FilenameCompare(value, BASEGAME))
481 			{
482 				Q_strncpyz(value, "", sizeof(value));
483 			}
484 
485 			gameSet = qtrue;
486 		}
487 		Cvar_Server_Set( key, value );
488 	}
489 	// if game folder should not be set and it is set at the client side
490 	if ( !gameSet && *Cvar_VariableString("fs_game") ) {
491 		Cvar_Set( "fs_game", "" );
492 	}
493 	cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
494 }
495 
496 /*
497 ==================
498 CL_ParseGamestate
499 ==================
500 */
CL_ParseGamestate(msg_t * msg)501 void CL_ParseGamestate( msg_t *msg ) {
502 	int				i;
503 	entityState_t	*es;
504 	int				newnum;
505 	entityState_t	nullstate;
506 	int				cmd;
507 	char			*s;
508 
509 	Con_Close();
510 
511 	clc.connectPacketCount = 0;
512 
513 	// wipe local client state
514 	CL_ClearState();
515 
516 	// a gamestate always marks a server command sequence
517 	clc.serverCommandSequence = MSG_ReadLong( msg );
518 
519 	// parse all the configstrings and baselines
520 	cl.gameState.dataCount = 1;	// leave a 0 at the beginning for uninitialized configstrings
521 	while ( 1 ) {
522 		cmd = MSG_ReadByte( msg );
523 
524 		if ( cmd == svc_EOF ) {
525 			break;
526 		}
527 
528 		if ( cmd == svc_configstring ) {
529 			int		len, start;
530 
531 			start = msg->readcount;
532 
533 			i = MSG_ReadShort( msg );
534 			if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
535 				Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
536 			}
537 			s = MSG_ReadBigString( msg );
538 
539 			if (cl_shownet->integer >= 2)
540 			{
541 				Com_Printf("%3i: %d: %s\n", start, i, s);
542 			}
543 
544 			/*
545 			if (i == CS_SERVERINFO)
546 			{ //get the special value here
547 				char *f = strstr(s, "g_debugMelee");
548 				if (f)
549 				{
550 					while (*f && *f != '\\')
551 					{ //find the \ after it
552 						f++;
553 					}
554 					if (*f == '\\')
555 					{ //got it
556 						int i = 0;
557 
558 						f++;
559 						while (*f && *f != '\\' && i < 128)
560 						{
561 							hiddenCvarVal[i] = *f;
562 							i++;
563 							f++;
564 						}
565 						hiddenCvarVal[i] = 0;
566 
567 						//resume here
568 						s = f;
569 					}
570 				}
571 			}
572 			*/
573 
574 			len = strlen( s );
575 
576 			if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
577 				Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
578 			}
579 
580 			// append it to the gameState string buffer
581 			cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
582 			Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
583 			cl.gameState.dataCount += len + 1;
584 		} else if ( cmd == svc_baseline ) {
585 			newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
586 			if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
587 				Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
588 			}
589 			Com_Memset (&nullstate, 0, sizeof(nullstate));
590 			es = &cl.entityBaselines[ newnum ];
591 			MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
592 		} else {
593 			Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
594 		}
595 	}
596 
597 	clc.clientNum = MSG_ReadLong(msg);
598 	// read the checksum feed
599 	clc.checksumFeed = MSG_ReadLong( msg );
600 
601 	// Throw away the info for the old RMG system.
602 	MSG_ReadShort (msg);
603 
604 
605 	// parse serverId and other cvars
606 	CL_SystemInfoChanged();
607 
608 	// reinitialize the filesystem if the game directory has changed
609 	if( FS_ConditionalRestart( clc.checksumFeed ) ) {
610 		// don't set to true because we yet have to start downloading
611 		// enabling this can cause double loading of a map when connecting to
612 		// a server which has a different game directory set
613 		//clc.downloadRestart = qtrue;
614 	}
615 
616 	// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
617 	// cgame
618 	CL_InitDownloads();
619 
620 	// make sure the game starts
621 	Cvar_Set( "cl_paused", "0" );
622 }
623 
624 
625 //=====================================================================
626 
627 /*
628 =====================
629 CL_ParseDownload
630 
631 A download message has been received from the server
632 =====================
633 */
CL_ParseDownload(msg_t * msg)634 void CL_ParseDownload ( msg_t *msg ) {
635 	int		size;
636 	unsigned char data[MAX_MSGLEN];
637 	uint16_t block;
638 
639 	if (!*clc.downloadTempName) {
640 		Com_Printf("Server sending download, but no download was requested\n");
641 		CL_AddReliableCommand("stopdl", qfalse);
642 		return;
643 	}
644 
645 	// read the data
646 	block = MSG_ReadShort ( msg );
647 
648 	if ( !block && !clc.downloadBlock )
649 	{
650 		// block zero is special, contains file size
651 		clc.downloadSize = MSG_ReadLong ( msg );
652 
653 		Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
654 
655 		if (clc.downloadSize < 0)
656 		{
657 			Com_Error(ERR_DROP, "%s", MSG_ReadString( msg ) );
658 			return;
659 		}
660 	}
661 
662 	size = /*(unsigned short)*/MSG_ReadShort ( msg );
663 	if (size < 0 || size > (int)sizeof(data))
664 	{
665 		Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size);
666 		return;
667 	}
668 
669 	MSG_ReadData( msg, data, size );
670 
671 	if((clc.downloadBlock & 0xFFFF) != block)
672 	{
673 		Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", (clc.downloadBlock & 0xFFFF), block);
674 		return;
675 	}
676 
677 	// open the file if not opened yet
678 	if (!clc.download)
679 	{
680 		clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
681 
682 		if (!clc.download) {
683 			Com_Printf( "Could not create %s\n", clc.downloadTempName );
684 			CL_AddReliableCommand( "stopdl", qfalse );
685 			CL_NextDownload();
686 			return;
687 		}
688 	}
689 
690 	if (size)
691 		FS_Write( data, size, clc.download );
692 
693 	CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock), qfalse );
694 	clc.downloadBlock++;
695 
696 	clc.downloadCount += size;
697 
698 	// So UI gets access to it
699 	Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
700 
701 	if (!size) { // A zero length block means EOF
702 		if (clc.download) {
703 			FS_FCloseFile( clc.download );
704 			clc.download = 0;
705 
706 			// rename the file
707 			FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse );
708 		}
709 
710 		// send intentions now
711 		// We need this because without it, we would hold the last nextdl and then start
712 		// loading right away.  If we take a while to load, the server is happily trying
713 		// to send us that last block over and over.
714 		// Write it twice to help make sure we acknowledge the download
715 		CL_WritePacket();
716 		CL_WritePacket();
717 
718 		// get another file if needed
719 		CL_NextDownload ();
720 	}
721 }
722 
CL_GetValueForHidden(const char * s)723 int CL_GetValueForHidden(const char *s)
724 { //string arg here just in case I want to add more sometime and make a lookup table
725 	return atoi(hiddenCvarVal);
726 }
727 
728 /*
729 =====================
730 CL_ParseCommandString
731 
732 Command strings are just saved off until cgame asks for them
733 when it transitions a snapshot
734 =====================
735 */
CL_ParseCommandString(msg_t * msg)736 void CL_ParseCommandString( msg_t *msg ) {
737 	char	*s;
738 	int		seq;
739 	int		index;
740 
741 	seq = MSG_ReadLong( msg );
742 	s = MSG_ReadString( msg );
743 
744 	// see if we have already executed stored it off
745 	if ( clc.serverCommandSequence >= seq ) {
746 		return;
747 	}
748 	clc.serverCommandSequence = seq;
749 
750 	index = seq & (MAX_RELIABLE_COMMANDS-1);
751 	/*
752 	if (s[0] == 'c' && s[1] == 's' && s[2] == ' ' && s[3] == '0' && s[4] == ' ')
753 	{ //yes.. we seem to have an incoming server info.
754 		char *f = strstr(s, "g_debugMelee");
755 		if (f)
756 		{
757 			while (*f && *f != '\\')
758 			{ //find the \ after it
759 				f++;
760 			}
761 			if (*f == '\\')
762 			{ //got it
763 				int i = 0;
764 
765 				f++;
766 				while (*f && *f != '\\' && i < 128)
767 				{
768 					hiddenCvarVal[i] = *f;
769 					i++;
770 					f++;
771 				}
772 				hiddenCvarVal[i] = 0;
773 
774 				//don't worry about backing over beginning of string I guess,
775 				//we already know we successfully strstr'd the initial string
776 				//which exceeds this length.
777 				//MSG_ReadString appears to just return a static buffer so I
778 				//can stomp over its contents safely.
779 				f--;
780 				*f = '\"';
781 				f--;
782 				*f = ' ';
783 				f--;
784 				*f = '0';
785 				f--;
786 				*f = ' ';
787 				f--;
788 				*f = 's';
789 				f--;
790 				*f = 'c';
791 
792 				//the normal configstring gets to start here...
793 				s = f;
794 			}
795 		}
796 	}
797 	*/
798 	Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
799 }
800 
801 
802 /*
803 =====================
804 CL_ParseServerMessage
805 =====================
806 */
CL_ParseServerMessage(msg_t * msg)807 void CL_ParseServerMessage( msg_t *msg ) {
808 	int			cmd;
809 
810 	if ( cl_shownet->integer == 1 ) {
811 		Com_Printf ("%i ",msg->cursize);
812 	} else if ( cl_shownet->integer >= 2 ) {
813 		Com_Printf ("------------------\n");
814 	}
815 
816 	MSG_Bitstream(msg);
817 
818 	// get the reliable sequence acknowledge number
819 	clc.reliableAcknowledge = MSG_ReadLong( msg );
820 	//
821 	if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
822 		clc.reliableAcknowledge = clc.reliableSequence;
823 	}
824 
825 	//
826 	// parse the message
827 	//
828 	while ( 1 ) {
829 		if ( msg->readcount > msg->cursize ) {
830 			Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
831 			break;
832 		}
833 
834 		cmd = MSG_ReadByte( msg );
835 
836 		if ( cmd == svc_EOF) {
837 			SHOWNET( msg, "END OF MESSAGE" );
838 			break;
839 		}
840 
841 		if ( cl_shownet->integer >= 2 ) {
842 			if ( !svc_strings[cmd] ) {
843 				Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
844 			} else {
845 				SHOWNET( msg, svc_strings[cmd] );
846 			}
847 		}
848 
849 	// other commands
850 		switch ( cmd ) {
851 		default:
852 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
853 			break;
854 		case svc_nop:
855 			break;
856 		case svc_serverCommand:
857 			CL_ParseCommandString( msg );
858 			break;
859 		case svc_gamestate:
860 			CL_ParseGamestate( msg );
861 			break;
862 		case svc_snapshot:
863 			CL_ParseSnapshot( msg );
864 			break;
865 		case svc_setgame:
866 			CL_ParseSetGame( msg );
867 			break;
868 		case svc_download:
869 			CL_ParseDownload( msg );
870 			break;
871 		case svc_mapchange:
872 			if ( cls.cgameStarted )
873 				CGVM_MapChange();
874 			break;
875 		}
876 	}
877 }
878