diff options
Diffstat (limited to 'code/client')
| -rwxr-xr-x | code/client/cl_cgame.c | 1029 | ||||
| -rwxr-xr-x | code/client/cl_cin.c | 1740 | ||||
| -rwxr-xr-x | code/client/cl_console.c | 786 | ||||
| -rwxr-xr-x | code/client/cl_input.c | 901 | ||||
| -rwxr-xr-x | code/client/cl_keys.c | 1252 | ||||
| -rwxr-xr-x | code/client/cl_main.c | 3324 | ||||
| -rwxr-xr-x | code/client/cl_net_chan.c | 167 | ||||
| -rwxr-xr-x | code/client/cl_parse.c | 655 | ||||
| -rwxr-xr-x | code/client/cl_scrn.c | 547 | ||||
| -rwxr-xr-x | code/client/cl_ui.c | 1200 | ||||
| -rwxr-xr-x | code/client/client.h | 519 | ||||
| -rwxr-xr-x | code/client/keys.h | 57 | ||||
| -rwxr-xr-x | code/client/snd_adpcm.c | 330 | ||||
| -rwxr-xr-x | code/client/snd_dma.c | 1636 | ||||
| -rwxr-xr-x | code/client/snd_local.h | 204 | ||||
| -rwxr-xr-x | code/client/snd_mem.c | 404 | ||||
| -rwxr-xr-x | code/client/snd_mix.c | 681 | ||||
| -rwxr-xr-x | code/client/snd_public.h | 72 | ||||
| -rwxr-xr-x | code/client/snd_wavelet.c | 253 | 
19 files changed, 15757 insertions, 0 deletions
diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c new file mode 100755 index 0000000..0b259d3 --- /dev/null +++ b/code/client/cl_cgame.c @@ -0,0 +1,1029 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// cl_cgame.c  -- client system interaction with client game
 +
 +#include "client.h"
 +
 +#include "../game/botlib.h"
 +
 +extern	botlib_export_t	*botlib_export;
 +
 +extern qboolean loadCamera(const char *name);
 +extern void startCamera(int time);
 +extern qboolean getCameraInfo(int time, vec3_t *origin, vec3_t *angles);
 +
 +/*
 +====================
 +CL_GetGameState
 +====================
 +*/
 +void CL_GetGameState( gameState_t *gs ) {
 +	*gs = cl.gameState;
 +}
 +
 +/*
 +====================
 +CL_GetGlconfig
 +====================
 +*/
 +void CL_GetGlconfig( glconfig_t *glconfig ) {
 +	*glconfig = cls.glconfig;
 +}
 +
 +
 +/*
 +====================
 +CL_GetUserCmd
 +====================
 +*/
 +qboolean CL_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
 +	// cmds[cmdNumber] is the last properly generated command
 +
 +	// can't return anything that we haven't created yet
 +	if ( cmdNumber > cl.cmdNumber ) {
 +		Com_Error( ERR_DROP, "CL_GetUserCmd: %i >= %i", cmdNumber, cl.cmdNumber );
 +	}
 +
 +	// the usercmd has been overwritten in the wrapping
 +	// buffer because it is too far out of date
 +	if ( cmdNumber <= cl.cmdNumber - CMD_BACKUP ) {
 +		return qfalse;
 +	}
 +
 +	*ucmd = cl.cmds[ cmdNumber & CMD_MASK ];
 +
 +	return qtrue;
 +}
 +
 +int CL_GetCurrentCmdNumber( void ) {
 +	return cl.cmdNumber;
 +}
 +
 +
 +/*
 +====================
 +CL_GetParseEntityState
 +====================
 +*/
 +qboolean	CL_GetParseEntityState( int parseEntityNumber, entityState_t *state ) {
 +	// can't return anything that hasn't been parsed yet
 +	if ( parseEntityNumber >= cl.parseEntitiesNum ) {
 +		Com_Error( ERR_DROP, "CL_GetParseEntityState: %i >= %i",
 +			parseEntityNumber, cl.parseEntitiesNum );
 +	}
 +
 +	// can't return anything that has been overwritten in the circular buffer
 +	if ( parseEntityNumber <= cl.parseEntitiesNum - MAX_PARSE_ENTITIES ) {
 +		return qfalse;
 +	}
 +
 +	*state = cl.parseEntities[ parseEntityNumber & ( MAX_PARSE_ENTITIES - 1 ) ];
 +	return qtrue;
 +}
 +
 +/*
 +====================
 +CL_GetCurrentSnapshotNumber
 +====================
 +*/
 +void	CL_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
 +	*snapshotNumber = cl.snap.messageNum;
 +	*serverTime = cl.snap.serverTime;
 +}
 +
 +/*
 +====================
 +CL_GetSnapshot
 +====================
 +*/
 +qboolean	CL_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
 +	clSnapshot_t	*clSnap;
 +	int				i, count;
 +
 +	if ( snapshotNumber > cl.snap.messageNum ) {
 +		Com_Error( ERR_DROP, "CL_GetSnapshot: snapshotNumber > cl.snapshot.messageNum" );
 +	}
 +
 +	// if the frame has fallen out of the circular buffer, we can't return it
 +	if ( cl.snap.messageNum - snapshotNumber >= PACKET_BACKUP ) {
 +		return qfalse;
 +	}
 +
 +	// if the frame is not valid, we can't return it
 +	clSnap = &cl.snapshots[snapshotNumber & PACKET_MASK];
 +	if ( !clSnap->valid ) {
 +		return qfalse;
 +	}
 +
 +	// if the entities in the frame have fallen out of their
 +	// circular buffer, we can't return it
 +	if ( cl.parseEntitiesNum - clSnap->parseEntitiesNum >= MAX_PARSE_ENTITIES ) {
 +		return qfalse;
 +	}
 +
 +	// write the snapshot
 +	snapshot->snapFlags = clSnap->snapFlags;
 +	snapshot->serverCommandSequence = clSnap->serverCommandNum;
 +	snapshot->ping = clSnap->ping;
 +	snapshot->serverTime = clSnap->serverTime;
 +	Com_Memcpy( snapshot->areamask, clSnap->areamask, sizeof( snapshot->areamask ) );
 +	snapshot->ps = clSnap->ps;
 +	count = clSnap->numEntities;
 +	if ( count > MAX_ENTITIES_IN_SNAPSHOT ) {
 +		Com_DPrintf( "CL_GetSnapshot: truncated %i entities to %i\n", count, MAX_ENTITIES_IN_SNAPSHOT );
 +		count = MAX_ENTITIES_IN_SNAPSHOT;
 +	}
 +	snapshot->numEntities = count;
 +	for ( i = 0 ; i < count ; i++ ) {
 +		snapshot->entities[i] = 
 +			cl.parseEntities[ ( clSnap->parseEntitiesNum + i ) & (MAX_PARSE_ENTITIES-1) ];
 +	}
 +
 +	// FIXME: configstring changes and server commands!!!
 +
 +	return qtrue;
 +}
 +
 +/*
 +=====================
 +CL_SetUserCmdValue
 +=====================
 +*/
 +void CL_SetUserCmdValue( int userCmdValue, float sensitivityScale ) {
 +	cl.cgameUserCmdValue = userCmdValue;
 +	cl.cgameSensitivity = sensitivityScale;
 +}
 +
 +/*
 +=====================
 +CL_AddCgameCommand
 +=====================
 +*/
 +void CL_AddCgameCommand( const char *cmdName ) {
 +	Cmd_AddCommand( cmdName, NULL );
 +}
 +
 +/*
 +=====================
 +CL_CgameError
 +=====================
 +*/
 +void CL_CgameError( const char *string ) {
 +	Com_Error( ERR_DROP, "%s", string );
 +}
 +
 +
 +/*
 +=====================
 +CL_ConfigstringModified
 +=====================
 +*/
 +void CL_ConfigstringModified( void ) {
 +	char		*old, *s;
 +	int			i, index;
 +	char		*dup;
 +	gameState_t	oldGs;
 +	int			len;
 +
 +	index = atoi( Cmd_Argv(1) );
 +	if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
 +		Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
 +	}
 +	// get everything after "cs <num>"
 +	s = Cmd_ArgsFrom(2);
 +
 +	old = cl.gameState.stringData + cl.gameState.stringOffsets[ index ];
 +	if ( !strcmp( old, s ) ) {
 +		return;		// unchanged
 +	}
 +
 +	// build the new gameState_t
 +	oldGs = cl.gameState;
 +
 +	Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
 +
 +	// leave the first 0 for uninitialized strings
 +	cl.gameState.dataCount = 1;
 +		
 +	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
 +		if ( i == index ) {
 +			dup = s;
 +		} else {
 +			dup = oldGs.stringData + oldGs.stringOffsets[ i ];
 +		}
 +		if ( !dup[0] ) {
 +			continue;		// leave with the default empty string
 +		}
 +
 +		len = strlen( dup );
 +
 +		if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
 +			Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
 +		}
 +
 +		// append it to the gameState string buffer
 +		cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
 +		Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, dup, len + 1 );
 +		cl.gameState.dataCount += len + 1;
 +	}
 +
 +	if ( index == CS_SYSTEMINFO ) {
 +		// parse serverId and other cvars
 +		CL_SystemInfoChanged();
 +	}
 +
 +}
 +
 +
 +/*
 +===================
 +CL_GetServerCommand
 +
 +Set up argc/argv for the given command
 +===================
 +*/
 +qboolean CL_GetServerCommand( int serverCommandNumber ) {
 +	char	*s;
 +	char	*cmd;
 +	static char bigConfigString[BIG_INFO_STRING];
 +	int argc;
 +
 +	// if we have irretrievably lost a reliable command, drop the connection
 +	if ( serverCommandNumber <= clc.serverCommandSequence - MAX_RELIABLE_COMMANDS ) {
 +		// when a demo record was started after the client got a whole bunch of
 +		// reliable commands then the client never got those first reliable commands
 +		if ( clc.demoplaying )
 +			return qfalse;
 +		Com_Error( ERR_DROP, "CL_GetServerCommand: a reliable command was cycled out" );
 +		return qfalse;
 +	}
 +
 +	if ( serverCommandNumber > clc.serverCommandSequence ) {
 +		Com_Error( ERR_DROP, "CL_GetServerCommand: requested a command not received" );
 +		return qfalse;
 +	}
 +
 +	s = clc.serverCommands[ serverCommandNumber & ( MAX_RELIABLE_COMMANDS - 1 ) ];
 +	clc.lastExecutedServerCommand = serverCommandNumber;
 +
 +	Com_DPrintf( "serverCommand: %i : %s\n", serverCommandNumber, s );
 +
 +rescan:
 +	Cmd_TokenizeString( s );
 +	cmd = Cmd_Argv(0);
 +	argc = Cmd_Argc();
 +
 +	if ( !strcmp( cmd, "disconnect" ) ) {
 +		// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=552
 +		// allow server to indicate why they were disconnected
 +		if ( argc >= 2 )
 +			Com_Error (ERR_SERVERDISCONNECT, va( "Server Disconnected - %s", Cmd_Argv( 1 ) ) );
 +		else
 +			Com_Error (ERR_SERVERDISCONNECT,"Server disconnected\n");
 +	}
 +
 +	if ( !strcmp( cmd, "bcs0" ) ) {
 +		Com_sprintf( bigConfigString, BIG_INFO_STRING, "cs %s \"%s", Cmd_Argv(1), Cmd_Argv(2) );
 +		return qfalse;
 +	}
 +
 +	if ( !strcmp( cmd, "bcs1" ) ) {
 +		s = Cmd_Argv(2);
 +		if( strlen(bigConfigString) + strlen(s) >= BIG_INFO_STRING ) {
 +			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
 +		}
 +		strcat( bigConfigString, s );
 +		return qfalse;
 +	}
 +
 +	if ( !strcmp( cmd, "bcs2" ) ) {
 +		s = Cmd_Argv(2);
 +		if( strlen(bigConfigString) + strlen(s) + 1 >= BIG_INFO_STRING ) {
 +			Com_Error( ERR_DROP, "bcs exceeded BIG_INFO_STRING" );
 +		}
 +		strcat( bigConfigString, s );
 +		strcat( bigConfigString, "\"" );
 +		s = bigConfigString;
 +		goto rescan;
 +	}
 +
 +	if ( !strcmp( cmd, "cs" ) ) {
 +		CL_ConfigstringModified();
 +		// reparse the string, because CL_ConfigstringModified may have done another Cmd_TokenizeString()
 +		Cmd_TokenizeString( s );
 +		return qtrue;
 +	}
 +
 +	if ( !strcmp( cmd, "map_restart" ) ) {
 +		// clear notify lines and outgoing commands before passing
 +		// the restart to the cgame
 +		Con_ClearNotify();
 +		Com_Memset( cl.cmds, 0, sizeof( cl.cmds ) );
 +		return qtrue;
 +	}
 +
 +	// the clientLevelShot command is used during development
 +	// to generate 128*128 screenshots from the intermission
 +	// point of levels for the menu system to use
 +	// we pass it along to the cgame to make apropriate adjustments,
 +	// but we also clear the console and notify lines here
 +	if ( !strcmp( cmd, "clientLevelShot" ) ) {
 +		// don't do it if we aren't running the server locally,
 +		// otherwise malicious remote servers could overwrite
 +		// the existing thumbnails
 +		if ( !com_sv_running->integer ) {
 +			return qfalse;
 +		}
 +		// close the console
 +		Con_Close();
 +		// take a special screenshot next frame
 +		Cbuf_AddText( "wait ; wait ; wait ; wait ; screenshot levelshot\n" );
 +		return qtrue;
 +	}
 +
 +	// we may want to put a "connect to other server" command here
 +
 +	// cgame can now act on the command
 +	return qtrue;
 +}
 +
 +
 +/*
 +====================
 +CL_CM_LoadMap
 +
 +Just adds default parameters that cgame doesn't need to know about
 +====================
 +*/
 +void CL_CM_LoadMap( const char *mapname ) {
 +	int		checksum;
 +
 +	CM_LoadMap( mapname, qtrue, &checksum );
 +}
 +
 +/*
 +====================
 +CL_ShutdonwCGame
 +
 +====================
 +*/
 +void CL_ShutdownCGame( void ) {
 +	cls.keyCatchers &= ~KEYCATCH_CGAME;
 +	cls.cgameStarted = qfalse;
 +	if ( !cgvm ) {
 +		return;
 +	}
 +	VM_Call( cgvm, CG_SHUTDOWN );
 +	VM_Free( cgvm );
 +	cgvm = NULL;
 +}
 +
 +static int	FloatAsInt( float f ) {
 +	int		temp;
 +
 +	*(float *)&temp = f;
 +
 +	return temp;
 +}
 +
 +/*
 +====================
 +CL_CgameSystemCalls
 +
 +The cgame module is making a system call
 +====================
 +*/
 +#define	VMA(x) VM_ArgPtr(args[x])
 +#define	VMF(x)	((float *)args)[x]
 +int CL_CgameSystemCalls( int *args ) {
 +	switch( args[0] ) {
 +	case CG_PRINT:
 +		Com_Printf( "%s", VMA(1) );
 +		return 0;
 +	case CG_ERROR:
 +		Com_Error( ERR_DROP, "%s", VMA(1) );
 +		return 0;
 +	case CG_MILLISECONDS:
 +		return Sys_Milliseconds();
 +	case CG_CVAR_REGISTER:
 +		Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] ); 
 +		return 0;
 +	case CG_CVAR_UPDATE:
 +		Cvar_Update( VMA(1) );
 +		return 0;
 +	case CG_CVAR_SET:
 +		Cvar_Set( VMA(1), VMA(2) );
 +		return 0;
 +	case CG_CVAR_VARIABLESTRINGBUFFER:
 +		Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] );
 +		return 0;
 +	case CG_ARGC:
 +		return Cmd_Argc();
 +	case CG_ARGV:
 +		Cmd_ArgvBuffer( args[1], VMA(2), args[3] );
 +		return 0;
 +	case CG_ARGS:
 +		Cmd_ArgsBuffer( VMA(1), args[2] );
 +		return 0;
 +	case CG_FS_FOPENFILE:
 +		return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] );
 +	case CG_FS_READ:
 +		FS_Read2( VMA(1), args[2], args[3] );
 +		return 0;
 +	case CG_FS_WRITE:
 +		FS_Write( VMA(1), args[2], args[3] );
 +		return 0;
 +	case CG_FS_FCLOSEFILE:
 +		FS_FCloseFile( args[1] );
 +		return 0;
 +	case CG_FS_SEEK:
 +		return FS_Seek( args[1], args[2], args[3] );
 +	case CG_SENDCONSOLECOMMAND:
 +		Cbuf_AddText( VMA(1) );
 +		return 0;
 +	case CG_ADDCOMMAND:
 +		CL_AddCgameCommand( VMA(1) );
 +		return 0;
 +	case CG_REMOVECOMMAND:
 +		Cmd_RemoveCommand( VMA(1) );
 +		return 0;
 +	case CG_SENDCLIENTCOMMAND:
 +		CL_AddReliableCommand( VMA(1) );
 +		return 0;
 +	case CG_UPDATESCREEN:
 +		// this is used during lengthy level loading, so pump message loop
 +//		Com_EventLoop();	// FIXME: if a server restarts here, BAD THINGS HAPPEN!
 +// We can't call Com_EventLoop here, a restart will crash and this _does_ happen
 +// if there is a map change while we are downloading at pk3.
 +// ZOID
 +		SCR_UpdateScreen();
 +		return 0;
 +	case CG_CM_LOADMAP:
 +		CL_CM_LoadMap( VMA(1) );
 +		return 0;
 +	case CG_CM_NUMINLINEMODELS:
 +		return CM_NumInlineModels();
 +	case CG_CM_INLINEMODEL:
 +		return CM_InlineModel( args[1] );
 +	case CG_CM_TEMPBOXMODEL:
 +		return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qfalse );
 +	case CG_CM_TEMPCAPSULEMODEL:
 +		return CM_TempBoxModel( VMA(1), VMA(2), /*int capsule*/ qtrue );
 +	case CG_CM_POINTCONTENTS:
 +		return CM_PointContents( VMA(1), args[2] );
 +	case CG_CM_TRANSFORMEDPOINTCONTENTS:
 +		return CM_TransformedPointContents( VMA(1), args[2], VMA(3), VMA(4) );
 +	case CG_CM_BOXTRACE:
 +		CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qfalse );
 +		return 0;
 +	case CG_CM_CAPSULETRACE:
 +		CM_BoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qtrue );
 +		return 0;
 +	case CG_CM_TRANSFORMEDBOXTRACE:
 +		CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qfalse );
 +		return 0;
 +	case CG_CM_TRANSFORMEDCAPSULETRACE:
 +		CM_TransformedBoxTrace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], VMA(8), VMA(9), /*int capsule*/ qtrue );
 +		return 0;
 +	case CG_CM_MARKFRAGMENTS:
 +		return re.MarkFragments( args[1], VMA(2), VMA(3), args[4], VMA(5), args[6], VMA(7) );
 +	case CG_S_STARTSOUND:
 +		S_StartSound( VMA(1), args[2], args[3], args[4] );
 +		return 0;
 +	case CG_S_STARTLOCALSOUND:
 +		S_StartLocalSound( args[1], args[2] );
 +		return 0;
 +	case CG_S_CLEARLOOPINGSOUNDS:
 +		S_ClearLoopingSounds(args[1]);
 +		return 0;
 +	case CG_S_ADDLOOPINGSOUND:
 +		S_AddLoopingSound( args[1], VMA(2), VMA(3), args[4] );
 +		return 0;
 +	case CG_S_ADDREALLOOPINGSOUND:
 +		S_AddRealLoopingSound( args[1], VMA(2), VMA(3), args[4] );
 +		return 0;
 +	case CG_S_STOPLOOPINGSOUND:
 +		S_StopLoopingSound( args[1] );
 +		return 0;
 +	case CG_S_UPDATEENTITYPOSITION:
 +		S_UpdateEntityPosition( args[1], VMA(2) );
 +		return 0;
 +	case CG_S_RESPATIALIZE:
 +		S_Respatialize( args[1], VMA(2), VMA(3), args[4] );
 +		return 0;
 +	case CG_S_REGISTERSOUND:
 +		return S_RegisterSound( VMA(1), args[2] );
 +	case CG_S_STARTBACKGROUNDTRACK:
 +		S_StartBackgroundTrack( VMA(1), VMA(2) );
 +		return 0;
 +	case CG_R_LOADWORLDMAP:
 +		re.LoadWorld( VMA(1) );
 +		return 0; 
 +	case CG_R_REGISTERMODEL:
 +		return re.RegisterModel( VMA(1) );
 +	case CG_R_REGISTERSKIN:
 +		return re.RegisterSkin( VMA(1) );
 +	case CG_R_REGISTERSHADER:
 +		return re.RegisterShader( VMA(1) );
 +	case CG_R_REGISTERSHADERNOMIP:
 +		return re.RegisterShaderNoMip( VMA(1) );
 +	case CG_R_REGISTERFONT:
 +		re.RegisterFont( VMA(1), args[2], VMA(3));
 +	case CG_R_CLEARSCENE:
 +		re.ClearScene();
 +		return 0;
 +	case CG_R_ADDREFENTITYTOSCENE:
 +		re.AddRefEntityToScene( VMA(1) );
 +		return 0;
 +	case CG_R_ADDPOLYTOSCENE:
 +		re.AddPolyToScene( args[1], args[2], VMA(3), 1 );
 +		return 0;
 +	case CG_R_ADDPOLYSTOSCENE:
 +		re.AddPolyToScene( args[1], args[2], VMA(3), args[4] );
 +		return 0;
 +	case CG_R_LIGHTFORPOINT:
 +		return re.LightForPoint( VMA(1), VMA(2), VMA(3), VMA(4) );
 +	case CG_R_ADDLIGHTTOSCENE:
 +		re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
 +		return 0;
 +	case CG_R_ADDADDITIVELIGHTTOSCENE:
 +		re.AddAdditiveLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
 +		return 0;
 +	case CG_R_RENDERSCENE:
 +		re.RenderScene( VMA(1) );
 +		return 0;
 +	case CG_R_SETCOLOR:
 +		re.SetColor( VMA(1) );
 +		return 0;
 +	case CG_R_DRAWSTRETCHPIC:
 +		re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] );
 +		return 0;
 +	case CG_R_MODELBOUNDS:
 +		re.ModelBounds( args[1], VMA(2), VMA(3) );
 +		return 0;
 +	case CG_R_LERPTAG:
 +		return re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) );
 +	case CG_GETGLCONFIG:
 +		CL_GetGlconfig( VMA(1) );
 +		return 0;
 +	case CG_GETGAMESTATE:
 +		CL_GetGameState( VMA(1) );
 +		return 0;
 +	case CG_GETCURRENTSNAPSHOTNUMBER:
 +		CL_GetCurrentSnapshotNumber( VMA(1), VMA(2) );
 +		return 0;
 +	case CG_GETSNAPSHOT:
 +		return CL_GetSnapshot( args[1], VMA(2) );
 +	case CG_GETSERVERCOMMAND:
 +		return CL_GetServerCommand( args[1] );
 +	case CG_GETCURRENTCMDNUMBER:
 +		return CL_GetCurrentCmdNumber();
 +	case CG_GETUSERCMD:
 +		return CL_GetUserCmd( args[1], VMA(2) );
 +	case CG_SETUSERCMDVALUE:
 +		CL_SetUserCmdValue( args[1], VMF(2) );
 +		return 0;
 +	case CG_MEMORY_REMAINING:
 +		return Hunk_MemoryRemaining();
 +  case CG_KEY_ISDOWN:
 +		return Key_IsDown( args[1] );
 +  case CG_KEY_GETCATCHER:
 +		return Key_GetCatcher();
 +  case CG_KEY_SETCATCHER:
 +		Key_SetCatcher( args[1] );
 +    return 0;
 +  case CG_KEY_GETKEY:
 +		return Key_GetKey( VMA(1) );
 +
 +
 +
 +	case CG_MEMSET:
 +		Com_Memset( VMA(1), args[2], args[3] );
 +		return 0;
 +	case CG_MEMCPY:
 +		Com_Memcpy( VMA(1), VMA(2), args[3] );
 +		return 0;
 +	case CG_STRNCPY:
 +		return (int)strncpy( VMA(1), VMA(2), args[3] );
 +	case CG_SIN:
 +		return FloatAsInt( sin( VMF(1) ) );
 +	case CG_COS:
 +		return FloatAsInt( cos( VMF(1) ) );
 +	case CG_ATAN2:
 +		return FloatAsInt( atan2( VMF(1), VMF(2) ) );
 +	case CG_SQRT:
 +		return FloatAsInt( sqrt( VMF(1) ) );
 +	case CG_FLOOR:
 +		return FloatAsInt( floor( VMF(1) ) );
 +	case CG_CEIL:
 +		return FloatAsInt( ceil( VMF(1) ) );
 +	case CG_ACOS:
 +		return FloatAsInt( Q_acos( VMF(1) ) );
 +
 +	case CG_PC_ADD_GLOBAL_DEFINE:
 +		return botlib_export->PC_AddGlobalDefine( VMA(1) );
 +	case CG_PC_LOAD_SOURCE:
 +		return botlib_export->PC_LoadSourceHandle( VMA(1) );
 +	case CG_PC_FREE_SOURCE:
 +		return botlib_export->PC_FreeSourceHandle( args[1] );
 +	case CG_PC_READ_TOKEN:
 +		return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) );
 +	case CG_PC_SOURCE_FILE_AND_LINE:
 +		return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) );
 +
 +	case CG_S_STOPBACKGROUNDTRACK:
 +		S_StopBackgroundTrack();
 +		return 0;
 +
 +	case CG_REAL_TIME:
 +		return Com_RealTime( VMA(1) );
 +	case CG_SNAPVECTOR:
 +		Sys_SnapVector( VMA(1) );
 +		return 0;
 +
 +	case CG_CIN_PLAYCINEMATIC:
 +	  return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]);
 +
 +	case CG_CIN_STOPCINEMATIC:
 +	  return CIN_StopCinematic(args[1]);
 +
 +	case CG_CIN_RUNCINEMATIC:
 +	  return CIN_RunCinematic(args[1]);
 +
 +	case CG_CIN_DRAWCINEMATIC:
 +	  CIN_DrawCinematic(args[1]);
 +	  return 0;
 +
 +	case CG_CIN_SETEXTENTS:
 +	  CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]);
 +	  return 0;
 +
 +	case CG_R_REMAP_SHADER:
 +		re.RemapShader( VMA(1), VMA(2), VMA(3) );
 +		return 0;
 +
 +/*
 +	case CG_LOADCAMERA:
 +		return loadCamera(VMA(1));
 +
 +	case CG_STARTCAMERA:
 +		startCamera(args[1]);
 +		return 0;
 +
 +	case CG_GETCAMERAINFO:
 +		return getCameraInfo(args[1], VMA(2), VMA(3));
 +*/
 +	case CG_GET_ENTITY_TOKEN:
 +		return re.GetEntityToken( VMA(1), args[2] );
 +	case CG_R_INPVS:
 +		return re.inPVS( VMA(1), VMA(2) );
 +
 +	default:
 +	        assert(0); // bk010102
 +		Com_Error( ERR_DROP, "Bad cgame system trap: %i", args[0] );
 +	}
 +	return 0;
 +}
 +
 +
 +/*
 +====================
 +CL_InitCGame
 +
 +Should only be called by CL_StartHunkUsers
 +====================
 +*/
 +void CL_InitCGame( void ) {
 +	const char			*info;
 +	const char			*mapname;
 +	int					t1, t2;
 +	vmInterpret_t		interpret;
 +
 +	t1 = Sys_Milliseconds();
 +
 +	// put away the console
 +	Con_Close();
 +
 +	// find the current mapname
 +	info = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SERVERINFO ];
 +	mapname = Info_ValueForKey( info, "mapname" );
 +	Com_sprintf( cl.mapname, sizeof( cl.mapname ), "maps/%s.bsp", mapname );
 +
 +	// load the dll or bytecode
 +	if ( cl_connectedToPureServer != 0 ) {
 +		// if sv_pure is set we only allow qvms to be loaded
 +		interpret = VMI_COMPILED;
 +	}
 +	else {
 +		interpret = Cvar_VariableValue( "vm_cgame" );
 +	}
 +	cgvm = VM_Create( "cgame", CL_CgameSystemCalls, interpret );
 +	if ( !cgvm ) {
 +		Com_Error( ERR_DROP, "VM_Create on cgame failed" );
 +	}
 +	cls.state = CA_LOADING;
 +
 +	// init for this gamestate
 +	// use the lastExecutedServerCommand instead of the serverCommandSequence
 +	// otherwise server commands sent just before a gamestate are dropped
 +	VM_Call( cgvm, CG_INIT, clc.serverMessageSequence, clc.lastExecutedServerCommand, clc.clientNum );
 +
 +	// we will send a usercmd this frame, which
 +	// will cause the server to send us the first snapshot
 +	cls.state = CA_PRIMED;
 +
 +	t2 = Sys_Milliseconds();
 +
 +	Com_Printf( "CL_InitCGame: %5.2f seconds\n", (t2-t1)/1000.0 );
 +
 +	// have the renderer touch all its images, so they are present
 +	// on the card even if the driver does deferred loading
 +	re.EndRegistration();
 +
 +	// make sure everything is paged in
 +	if (!Sys_LowPhysicalMemory()) {
 +		Com_TouchMemory();
 +	}
 +
 +	// clear anything that got printed
 +	Con_ClearNotify ();
 +}
 +
 +
 +/*
 +====================
 +CL_GameCommand
 +
 +See if the current console command is claimed by the cgame
 +====================
 +*/
 +qboolean CL_GameCommand( void ) {
 +	if ( !cgvm ) {
 +		return qfalse;
 +	}
 +
 +	return VM_Call( cgvm, CG_CONSOLE_COMMAND );
 +}
 +
 +
 +
 +/*
 +=====================
 +CL_CGameRendering
 +=====================
 +*/
 +void CL_CGameRendering( stereoFrame_t stereo ) {
 +	VM_Call( cgvm, CG_DRAW_ACTIVE_FRAME, cl.serverTime, stereo, clc.demoplaying );
 +	VM_Debug( 0 );
 +}
 +
 +
 +/*
 +=================
 +CL_AdjustTimeDelta
 +
 +Adjust the clients view of server time.
 +
 +We attempt to have cl.serverTime exactly equal the server's view
 +of time plus the timeNudge, but with variable latencies over
 +the internet it will often need to drift a bit to match conditions.
 +
 +Our ideal time would be to have the adjusted time approach, but not pass,
 +the very latest snapshot.
 +
 +Adjustments are only made when a new snapshot arrives with a rational
 +latency, which keeps the adjustment process framerate independent and
 +prevents massive overadjustment during times of significant packet loss
 +or bursted delayed packets.
 +=================
 +*/
 +
 +#define	RESET_TIME	500
 +
 +void CL_AdjustTimeDelta( void ) {
 +	int		resetTime;
 +	int		newDelta;
 +	int		deltaDelta;
 +
 +	cl.newSnapshots = qfalse;
 +
 +	// the delta never drifts when replaying a demo
 +	if ( clc.demoplaying ) {
 +		return;
 +	}
 +
 +	// if the current time is WAY off, just correct to the current value
 +	if ( com_sv_running->integer ) {
 +		resetTime = 100;
 +	} else {
 +		resetTime = RESET_TIME;
 +	}
 +
 +	newDelta = cl.snap.serverTime - cls.realtime;
 +	deltaDelta = abs( newDelta - cl.serverTimeDelta );
 +
 +	if ( deltaDelta > RESET_TIME ) {
 +		cl.serverTimeDelta = newDelta;
 +		cl.oldServerTime = cl.snap.serverTime;	// FIXME: is this a problem for cgame?
 +		cl.serverTime = cl.snap.serverTime;
 +		if ( cl_showTimeDelta->integer ) {
 +			Com_Printf( "<RESET> " );
 +		}
 +	} else if ( deltaDelta > 100 ) {
 +		// fast adjust, cut the difference in half
 +		if ( cl_showTimeDelta->integer ) {
 +			Com_Printf( "<FAST> " );
 +		}
 +		cl.serverTimeDelta = ( cl.serverTimeDelta + newDelta ) >> 1;
 +	} else {
 +		// slow drift adjust, only move 1 or 2 msec
 +
 +		// if any of the frames between this and the previous snapshot
 +		// had to be extrapolated, nudge our sense of time back a little
 +		// the granularity of +1 / -2 is too high for timescale modified frametimes
 +		if ( com_timescale->value == 0 || com_timescale->value == 1 ) {
 +			if ( cl.extrapolatedSnapshot ) {
 +				cl.extrapolatedSnapshot = qfalse;
 +				cl.serverTimeDelta -= 2;
 +			} else {
 +				// otherwise, move our sense of time forward to minimize total latency
 +				cl.serverTimeDelta++;
 +			}
 +		}
 +	}
 +
 +	if ( cl_showTimeDelta->integer ) {
 +		Com_Printf( "%i ", cl.serverTimeDelta );
 +	}
 +}
 +
 +
 +/*
 +==================
 +CL_FirstSnapshot
 +==================
 +*/
 +void CL_FirstSnapshot( void ) {
 +	// ignore snapshots that don't have entities
 +	if ( cl.snap.snapFlags & SNAPFLAG_NOT_ACTIVE ) {
 +		return;
 +	}
 +	cls.state = CA_ACTIVE;
 +
 +	// set the timedelta so we are exactly on this first frame
 +	cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
 +	cl.oldServerTime = cl.snap.serverTime;
 +
 +	clc.timeDemoBaseTime = cl.snap.serverTime;
 +
 +	// if this is the first frame of active play,
 +	// execute the contents of activeAction now
 +	// this is to allow scripting a timedemo to start right
 +	// after loading
 +	if ( cl_activeAction->string[0] ) {
 +		Cbuf_AddText( cl_activeAction->string );
 +		Cvar_Set( "activeAction", "" );
 +	}
 +	
 +	Sys_BeginProfiling();
 +}
 +
 +/*
 +==================
 +CL_SetCGameTime
 +==================
 +*/
 +void CL_SetCGameTime( void ) {
 +	// getting a valid frame message ends the connection process
 +	if ( cls.state != CA_ACTIVE ) {
 +		if ( cls.state != CA_PRIMED ) {
 +			return;
 +		}
 +		if ( clc.demoplaying ) {
 +			// we shouldn't get the first snapshot on the same frame
 +			// as the gamestate, because it causes a bad time skip
 +			if ( !clc.firstDemoFrameSkipped ) {
 +				clc.firstDemoFrameSkipped = qtrue;
 +				return;
 +			}
 +			CL_ReadDemoMessage();
 +		}
 +		if ( cl.newSnapshots ) {
 +			cl.newSnapshots = qfalse;
 +			CL_FirstSnapshot();
 +		}
 +		if ( cls.state != CA_ACTIVE ) {
 +			return;
 +		}
 +	}	
 +
 +	// if we have gotten to this point, cl.snap is guaranteed to be valid
 +	if ( !cl.snap.valid ) {
 +		Com_Error( ERR_DROP, "CL_SetCGameTime: !cl.snap.valid" );
 +	}
 +
 +	// allow pause in single player
 +	if ( sv_paused->integer && cl_paused->integer && com_sv_running->integer ) {
 +		// paused
 +		return;
 +	}
 +
 +	if ( cl.snap.serverTime < cl.oldFrameServerTime ) {
 +		Com_Error( ERR_DROP, "cl.snap.serverTime < cl.oldFrameServerTime" );
 +	}
 +	cl.oldFrameServerTime = cl.snap.serverTime;
 +
 +
 +	// get our current view of time
 +
 +	if ( clc.demoplaying && cl_freezeDemo->integer ) {
 +		// cl_freezeDemo is used to lock a demo in place for single frame advances
 +
 +	} else {
 +		// cl_timeNudge is a user adjustable cvar that allows more
 +		// or less latency to be added in the interest of better 
 +		// smoothness or better responsiveness.
 +		int tn;
 +		
 +		tn = cl_timeNudge->integer;
 +		if (tn<-30) {
 +			tn = -30;
 +		} else if (tn>30) {
 +			tn = 30;
 +		}
 +
 +		cl.serverTime = cls.realtime + cl.serverTimeDelta - tn;
 +
 +		// guarantee that time will never flow backwards, even if
 +		// serverTimeDelta made an adjustment or cl_timeNudge was changed
 +		if ( cl.serverTime < cl.oldServerTime ) {
 +			cl.serverTime = cl.oldServerTime;
 +		}
 +		cl.oldServerTime = cl.serverTime;
 +
 +		// note if we are almost past the latest frame (without timeNudge),
 +		// so we will try and adjust back a bit when the next snapshot arrives
 +		if ( cls.realtime + cl.serverTimeDelta >= cl.snap.serverTime - 5 ) {
 +			cl.extrapolatedSnapshot = qtrue;
 +		}
 +	}
 +
 +	// if we have gotten new snapshots, drift serverTimeDelta
 +	// don't do this every frame, or a period of packet loss would
 +	// make a huge adjustment
 +	if ( cl.newSnapshots ) {
 +		CL_AdjustTimeDelta();
 +	}
 +
 +	if ( !clc.demoplaying ) {
 +		return;
 +	}
 +
 +	// if we are playing a demo back, we can just keep reading
 +	// messages from the demo file until the cgame definately
 +	// has valid snapshots to interpolate between
 +
 +	// a timedemo will always use a deterministic set of time samples
 +	// no matter what speed machine it is run on,
 +	// while a normal demo may have different time samples
 +	// each time it is played back
 +	if ( cl_timedemo->integer ) {
 +		if (!clc.timeDemoStart) {
 +			clc.timeDemoStart = Sys_Milliseconds();
 +		}
 +		clc.timeDemoFrames++;
 +		cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
 +	}
 +
 +	while ( cl.serverTime >= cl.snap.serverTime ) {
 +		// feed another messag, which should change
 +		// the contents of cl.snap
 +		CL_ReadDemoMessage();
 +		if ( cls.state != CA_ACTIVE ) {
 +			return;		// end of demo
 +		}
 +	}
 +
 +}
 +
 +
 +
 diff --git a/code/client/cl_cin.c b/code/client/cl_cin.c new file mode 100755 index 0000000..7929624 --- /dev/null +++ b/code/client/cl_cin.c @@ -0,0 +1,1740 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +/*****************************************************************************
 + * name:		cl_cin.c
 + *
 + * desc:		video and cinematic playback
 + *
 + * $Archive: /MissionPack/code/client/cl_cin.c $
 + *
 + * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256
 + *
 + *****************************************************************************/
 +
 +#include "client.h"
 +#include "snd_local.h"
 +
 +#define MAXSIZE				8
 +#define MINSIZE				4
 +
 +#define DEFAULT_CIN_WIDTH	512
 +#define DEFAULT_CIN_HEIGHT	512
 +
 +#define ROQ_QUAD			0x1000
 +#define ROQ_QUAD_INFO		0x1001
 +#define ROQ_CODEBOOK		0x1002
 +#define ROQ_QUAD_VQ			0x1011
 +#define ROQ_QUAD_JPEG		0x1012
 +#define ROQ_QUAD_HANG		0x1013
 +#define ROQ_PACKET			0x1030
 +#define ZA_SOUND_MONO		0x1020
 +#define ZA_SOUND_STEREO		0x1021
 +
 +#define MAX_VIDEO_HANDLES	16
 +
 +extern glconfig_t glConfig;
 +extern	int		s_paintedtime;
 +extern	int		s_rawend;
 +
 +
 +static void RoQ_init( void );
 +
 +/******************************************************************************
 +*
 +* Class:		trFMV
 +*
 +* Description:	RoQ/RnR manipulation routines
 +*				not entirely complete for first run
 +*
 +******************************************************************************/
 +
 +static	long				ROQ_YY_tab[256];
 +static	long				ROQ_UB_tab[256];
 +static	long				ROQ_UG_tab[256];
 +static	long				ROQ_VG_tab[256];
 +static	long				ROQ_VR_tab[256];
 +static	unsigned short		vq2[256*16*4];
 +static	unsigned short		vq4[256*64*4];
 +static	unsigned short		vq8[256*256*4];
 +
 +
 +typedef struct {
 +	byte				linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2];
 +	byte				file[65536];
 +	short				sqrTable[256];
 +
 +	unsigned int		mcomp[256];
 +	byte				*qStatus[2][32768];
 +
 +	long				oldXOff, oldYOff, oldysize, oldxsize;
 +
 +	int					currentHandle;
 +} cinematics_t;
 +
 +typedef struct {
 +	char				fileName[MAX_OSPATH];
 +	int					CIN_WIDTH, CIN_HEIGHT;
 +	int					xpos, ypos, width, height;
 +	qboolean			looping, holdAtEnd, dirty, alterGameState, silent, shader;
 +	fileHandle_t		iFile;
 +	e_status			status;
 +	unsigned int		startTime;
 +	unsigned int		lastTime;
 +	long				tfps;
 +	long				RoQPlayed;
 +	long				ROQSize;
 +	unsigned int		RoQFrameSize;
 +	long				onQuad;
 +	long				numQuads;
 +	long				samplesPerLine;
 +	unsigned int		roq_id;
 +	long				screenDelta;
 +
 +	void ( *VQ0)(byte *status, void *qdata );
 +	void ( *VQ1)(byte *status, void *qdata );
 +	void ( *VQNormal)(byte *status, void *qdata );
 +	void ( *VQBuffer)(byte *status, void *qdata );
 +
 +	long				samplesPerPixel;				// defaults to 2
 +	byte*				gray;
 +	unsigned int		xsize, ysize, maxsize, minsize;
 +
 +	qboolean			half, smootheddouble, inMemory;
 +	long				normalBuffer0;
 +	long				roq_flags;
 +	long				roqF0;
 +	long				roqF1;
 +	long				t[2];
 +	long				roqFPS;
 +	int					playonwalls;
 +	byte*				buf;
 +	long				drawX, drawY;
 +} cin_cache;
 +
 +static cinematics_t		cin;
 +static cin_cache		cinTable[MAX_VIDEO_HANDLES];
 +static int				currentHandle = -1;
 +static int				CL_handle = -1;
 +
 +extern int				s_soundtime;		// sample PAIRS
 +extern int   			s_paintedtime; 		// sample PAIRS
 +
 +
 +void CIN_CloseAllVideos(void) {
 +	int		i;
 +
 +	for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
 +		if (cinTable[i].fileName[0] != 0 ) {
 +			CIN_StopCinematic(i);
 +		}
 +	}
 +}
 +
 +
 +static int CIN_HandleForVideo(void) {
 +	int		i;
 +
 +	for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
 +		if ( cinTable[i].fileName[0] == 0 ) {
 +			return i;
 +		}
 +	}
 +	Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" );
 +	return -1;
 +}
 +
 +
 +extern int CL_ScaledMilliseconds(void);
 +
 +//-----------------------------------------------------------------------------
 +// RllSetupTable
 +//
 +// Allocates and initializes the square table.
 +//
 +// Parameters:	None
 +//
 +// Returns:		Nothing
 +//-----------------------------------------------------------------------------
 +static void RllSetupTable()
 +{
 +	int z;
 +
 +	for (z=0;z<128;z++) {
 +		cin.sqrTable[z] = (short)(z*z);
 +		cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]);
 +	}
 +}
 +
 +
 +
 +//-----------------------------------------------------------------------------
 +// RllDecodeMonoToMono
 +//
 +// Decode mono source data into a mono buffer.
 +//
 +// Parameters:	from -> buffer holding encoded data
 +//				to ->	buffer to hold decoded data
 +//				size =	number of bytes of input (= # of shorts of output)
 +//				signedOutput = 0 for unsigned output, non-zero for signed output
 +//				flag = flags from asset header
 +//
 +// Returns:		Number of samples placed in output buffer
 +//-----------------------------------------------------------------------------
 +long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag)
 +{
 +	unsigned int z;
 +	int prev;
 +	
 +	if (signedOutput)	
 +		prev =  flag - 0x8000;
 +	else 
 +		prev = flag;
 +
 +	for (z=0;z<size;z++) {
 +		prev = to[z] = (short)(prev + cin.sqrTable[from[z]]); 
 +	}
 +	return size;	//*sizeof(short));
 +}
 +
 +
 +//-----------------------------------------------------------------------------
 +// RllDecodeMonoToStereo
 +//
 +// Decode mono source data into a stereo buffer. Output is 4 times the number
 +// of bytes in the input.
 +//
 +// Parameters:	from -> buffer holding encoded data
 +//				to ->	buffer to hold decoded data
 +//				size =	number of bytes of input (= 1/4 # of bytes of output)
 +//				signedOutput = 0 for unsigned output, non-zero for signed output
 +//				flag = flags from asset header
 +//
 +// Returns:		Number of samples placed in output buffer
 +//-----------------------------------------------------------------------------
 +long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag)
 +{
 +	unsigned int z;
 +	int prev;
 +	
 +	if (signedOutput)	
 +		prev =  flag - 0x8000;
 +	else 
 +		prev = flag;
 +
 +	for (z = 0; z < size; z++) {
 +		prev = (short)(prev + cin.sqrTable[from[z]]);
 +		to[z*2+0] = to[z*2+1] = (short)(prev);
 +	}
 +	
 +	return size;	// * 2 * sizeof(short));
 +}
 +
 +
 +//-----------------------------------------------------------------------------
 +// RllDecodeStereoToStereo
 +//
 +// Decode stereo source data into a stereo buffer.
 +//
 +// Parameters:	from -> buffer holding encoded data
 +//				to ->	buffer to hold decoded data
 +//				size =	number of bytes of input (= 1/2 # of bytes of output)
 +//				signedOutput = 0 for unsigned output, non-zero for signed output
 +//				flag = flags from asset header
 +//
 +// Returns:		Number of samples placed in output buffer
 +//-----------------------------------------------------------------------------
 +long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
 +{
 +	unsigned int z;
 +	unsigned char *zz = from;
 +	int	prevL, prevR;
 +
 +	if (signedOutput) {
 +		prevL = (flag & 0xff00) - 0x8000;
 +		prevR = ((flag & 0x00ff) << 8) - 0x8000;
 +	} else {
 +		prevL = flag & 0xff00;
 +		prevR = (flag & 0x00ff) << 8;
 +	}
 +
 +	for (z=0;z<size;z+=2) {
 +                prevL = (short)(prevL + cin.sqrTable[*zz++]); 
 +                prevR = (short)(prevR + cin.sqrTable[*zz++]);
 +                to[z+0] = (short)(prevL);
 +                to[z+1] = (short)(prevR);
 +	}
 +	
 +	return (size>>1);	//*sizeof(short));
 +}
 +
 +
 +//-----------------------------------------------------------------------------
 +// RllDecodeStereoToMono
 +//
 +// Decode stereo source data into a mono buffer.
 +//
 +// Parameters:	from -> buffer holding encoded data
 +//				to ->	buffer to hold decoded data
 +//				size =	number of bytes of input (= # of bytes of output)
 +//				signedOutput = 0 for unsigned output, non-zero for signed output
 +//				flag = flags from asset header
 +//
 +// Returns:		Number of samples placed in output buffer
 +//-----------------------------------------------------------------------------
 +long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
 +{
 +	unsigned int z;
 +	int prevL,prevR;
 +	
 +	if (signedOutput) {
 +		prevL = (flag & 0xff00) - 0x8000;
 +		prevR = ((flag & 0x00ff) << 8) -0x8000;
 +	} else {
 +		prevL = flag & 0xff00;
 +		prevR = (flag & 0x00ff) << 8;
 +	}
 +
 +	for (z=0;z<size;z+=1) {
 +		prevL= prevL + cin.sqrTable[from[z*2]];
 +		prevR = prevR + cin.sqrTable[from[z*2+1]];
 +		to[z] = (short)((prevL + prevR)/2);
 +	}
 +
 +	return size;
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void move8_32( byte *src, byte *dst, int spl )
 +{
 +	double *dsrc, *ddst;
 +	int dspl;
 +
 +	dsrc = (double *)src;
 +	ddst = (double *)dst;
 +	dspl = spl>>3;
 +
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void move4_32( byte *src, byte *dst, int spl  )
 +{
 +	double *dsrc, *ddst;
 +	int dspl;
 +
 +	dsrc = (double *)src;
 +	ddst = (double *)dst;
 +	dspl = spl>>3;
 +
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += dspl; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void blit8_32( byte *src, byte *dst, int spl  )
 +{
 +	double *dsrc, *ddst;
 +	int dspl;
 +
 +	dsrc = (double *)src;
 +	ddst = (double *)dst;
 +	dspl = spl>>3;
 +
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +	dsrc += 4; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3];
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +#define movs double
 +static void blit4_32( byte *src, byte *dst, int spl  )
 +{
 +	movs *dsrc, *ddst;
 +	int dspl;
 +
 +	dsrc = (movs *)src;
 +	ddst = (movs *)dst;
 +	dspl = spl>>3;
 +
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += 2; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += 2; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +	dsrc += 2; ddst += dspl;
 +	ddst[0] = dsrc[0]; ddst[1] = dsrc[1];
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void blit2_32( byte *src, byte *dst, int spl  )
 +{
 +	double *dsrc, *ddst;
 +	int dspl;
 +
 +	dsrc = (double *)src;
 +	ddst = (double *)dst;
 +	dspl = spl>>3;
 +
 +	ddst[0] = dsrc[0];
 +	ddst[dspl] = dsrc[1];
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void blitVQQuad32fs( byte **status, unsigned char *data )
 +{
 +unsigned short	newd, celdata, code;
 +unsigned int	index, i;
 +int		spl;
 +
 +	newd	= 0;
 +	celdata = 0;
 +	index	= 0;
 +	
 +        spl = cinTable[currentHandle].samplesPerLine;
 +        
 +	do {
 +		if (!newd) { 
 +			newd = 7;
 +			celdata = data[0] + data[1]*256;
 +			data += 2;
 +		} else {
 +			newd--;
 +		}
 +
 +		code = (unsigned short)(celdata&0xc000); 
 +		celdata <<= 2;
 +		
 +		switch (code) {
 +			case	0x8000:													// vq code
 +				blit8_32( (byte *)&vq8[(*data)*128], status[index], spl );
 +				data++;
 +				index += 5;
 +				break;
 +			case	0xc000:													// drop
 +				index++;													// skip 8x8
 +				for(i=0;i<4;i++) {
 +					if (!newd) { 
 +						newd = 7;
 +						celdata = data[0] + data[1]*256;
 +						data += 2;
 +					} else {
 +						newd--;
 +					}
 +						
 +					code = (unsigned short)(celdata&0xc000); celdata <<= 2; 
 +
 +					switch (code) {											// code in top two bits of code
 +						case	0x8000:										// 4x4 vq code
 +							blit4_32( (byte *)&vq4[(*data)*32], status[index], spl );
 +							data++;
 +							break;
 +						case	0xc000:										// 2x2 vq code
 +							blit2_32( (byte *)&vq2[(*data)*8], status[index], spl );
 +							data++;
 +							blit2_32( (byte *)&vq2[(*data)*8], status[index]+8, spl );
 +							data++;
 +							blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2, spl );
 +							data++;
 +							blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2+8, spl );
 +							data++;
 +							break;
 +						case	0x4000:										// motion compensation
 +							move4_32( status[index] + cin.mcomp[(*data)], status[index], spl );
 +							data++;
 +							break;
 +					}
 +					index++;
 +				}
 +				break;
 +			case	0x4000:													// motion compensation
 +				move8_32( status[index] + cin.mcomp[(*data)], status[index], spl );
 +				data++;
 +				index += 5;
 +				break;
 +			case	0x0000:
 +				index += 5;
 +				break;
 +		}
 +	} while ( status[index] != NULL );
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void ROQ_GenYUVTables( void )
 +{
 +	float t_ub,t_vr,t_ug,t_vg;
 +	long i;
 +
 +	t_ub = (1.77200f/2.0f) * (float)(1<<6) + 0.5f;
 +	t_vr = (1.40200f/2.0f) * (float)(1<<6) + 0.5f;
 +	t_ug = (0.34414f/2.0f) * (float)(1<<6) + 0.5f;
 +	t_vg = (0.71414f/2.0f) * (float)(1<<6) + 0.5f;
 +	for(i=0;i<256;i++) {
 +		float x = (float)(2 * i - 255);
 +	
 +		ROQ_UB_tab[i] = (long)( ( t_ub * x) + (1<<5));
 +		ROQ_VR_tab[i] = (long)( ( t_vr * x) + (1<<5));
 +		ROQ_UG_tab[i] = (long)( (-t_ug * x)		 );
 +		ROQ_VG_tab[i] = (long)( (-t_vg * x) + (1<<5));
 +		ROQ_YY_tab[i] = (long)( (i << 6) | (i >> 2) );
 +	}
 +}
 +
 +#define VQ2TO4(a,b,c,d) { \
 +    	*c++ = a[0];	\
 +	*d++ = a[0];	\
 +	*d++ = a[0];	\
 +	*c++ = a[1];	\
 +	*d++ = a[1];	\
 +	*d++ = a[1];	\
 +	*c++ = b[0];	\
 +	*d++ = b[0];	\
 +	*d++ = b[0];	\
 +	*c++ = b[1];	\
 +	*d++ = b[1];	\
 +	*d++ = b[1];	\
 +	*d++ = a[0];	\
 +	*d++ = a[0];	\
 +	*d++ = a[1];	\
 +	*d++ = a[1];	\
 +	*d++ = b[0];	\
 +	*d++ = b[0];	\
 +	*d++ = b[1];	\
 +	*d++ = b[1];	\
 +	a += 2; b += 2; }
 + 
 +#define VQ2TO2(a,b,c,d) { \
 +	*c++ = *a;	\
 +	*d++ = *a;	\
 +	*d++ = *a;	\
 +	*c++ = *b;	\
 +	*d++ = *b;	\
 +	*d++ = *b;	\
 +	*d++ = *a;	\
 +	*d++ = *a;	\
 +	*d++ = *b;	\
 +	*d++ = *b;	\
 +	a++; b++; }
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static unsigned short yuv_to_rgb( long y, long u, long v )
 +{ 
 +	long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
 +
 +	r = (YY + ROQ_VR_tab[v]) >> 9;
 +	g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8;
 +	b = (YY + ROQ_UB_tab[u]) >> 9;
 +	
 +	if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0;
 +	if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31;
 +
 +	return (unsigned short)((r<<11)+(g<<5)+(b));
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +#if defined(MACOS_X)
 +
 +static inline unsigned int yuv_to_rgb24( long y, long u, long v )
 +{ 
 +	long r,g,b,YY;
 +        
 +        YY = (long)(ROQ_YY_tab[(y)]);
 +
 +	r = (YY + ROQ_VR_tab[v]) >> 6;
 +	g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6;
 +	b = (YY + ROQ_UB_tab[u]) >> 6;
 +	
 +	if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0;
 +	if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255;
 +	
 +	return ((r<<24)|(g<<16)|(b<<8))|(255);	//+(255<<24));
 +}
 +
 +#else
 +static unsigned int yuv_to_rgb24( long y, long u, long v )
 +{ 
 +	long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
 +
 +	r = (YY + ROQ_VR_tab[v]) >> 6;
 +	g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6;
 +	b = (YY + ROQ_UB_tab[u]) >> 6;
 +	
 +	if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0;
 +	if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255;
 +	
 +	return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24));
 +}
 +#endif
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void decodeCodeBook( byte *input, unsigned short roq_flags )
 +{
 +	long	i, j, two, four;
 +	unsigned short	*aptr, *bptr, *cptr, *dptr;
 +	long	y0,y1,y2,y3,cr,cb;
 +	byte	*bbptr, *baptr, *bcptr, *bdptr;
 +	unsigned int *iaptr, *ibptr, *icptr, *idptr;
 +
 +	if (!roq_flags) {
 +		two = four = 256;
 +	} else {
 +		two  = roq_flags>>8;
 +		if (!two) two = 256;
 +		four = roq_flags&0xff;
 +	}
 +
 +	four *= 2;
 +
 +	bptr = (unsigned short *)vq2;
 +
 +	if (!cinTable[currentHandle].half) {
 +		if (!cinTable[currentHandle].smootheddouble) {
 +//
 +// normal height
 +//
 +			if (cinTable[currentHandle].samplesPerPixel==2) {
 +				for(i=0;i<two;i++) {
 +					y0 = (long)*input++;
 +					y1 = (long)*input++;
 +					y2 = (long)*input++;
 +					y3 = (long)*input++;
 +					cr = (long)*input++;
 +					cb = (long)*input++;
 +					*bptr++ = yuv_to_rgb( y0, cr, cb );
 +					*bptr++ = yuv_to_rgb( y1, cr, cb );
 +					*bptr++ = yuv_to_rgb( y2, cr, cb );
 +					*bptr++ = yuv_to_rgb( y3, cr, cb );
 +				}
 +
 +				cptr = (unsigned short *)vq4;
 +				dptr = (unsigned short *)vq8;
 +		
 +				for(i=0;i<four;i++) {
 +					aptr = (unsigned short *)vq2 + (*input++)*4;
 +					bptr = (unsigned short *)vq2 + (*input++)*4;
 +					for(j=0;j<2;j++)
 +						VQ2TO4(aptr,bptr,cptr,dptr);
 +				}
 +			} else if (cinTable[currentHandle].samplesPerPixel==4) {
 +				ibptr = (unsigned int *)bptr;
 +				for(i=0;i<two;i++) {
 +					y0 = (long)*input++;
 +					y1 = (long)*input++;
 +					y2 = (long)*input++;
 +					y3 = (long)*input++;
 +					cr = (long)*input++;
 +					cb = (long)*input++;
 +					*ibptr++ = yuv_to_rgb24( y0, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y1, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y2, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y3, cr, cb );
 +				}
 +
 +				icptr = (unsigned int *)vq4;
 +				idptr = (unsigned int *)vq8;
 +	
 +				for(i=0;i<four;i++) {
 +					iaptr = (unsigned int *)vq2 + (*input++)*4;
 +					ibptr = (unsigned int *)vq2 + (*input++)*4;
 +					for(j=0;j<2;j++) 
 +						VQ2TO4(iaptr, ibptr, icptr, idptr);
 +				}
 +			} else if (cinTable[currentHandle].samplesPerPixel==1) {
 +				bbptr = (byte *)bptr;
 +				for(i=0;i<two;i++) {
 +					*bbptr++ = cinTable[currentHandle].gray[*input++];
 +					*bbptr++ = cinTable[currentHandle].gray[*input++];
 +					*bbptr++ = cinTable[currentHandle].gray[*input++];
 +					*bbptr++ = cinTable[currentHandle].gray[*input]; input +=3;
 +				}
 +
 +				bcptr = (byte *)vq4;
 +				bdptr = (byte *)vq8;
 +	
 +				for(i=0;i<four;i++) {
 +					baptr = (byte *)vq2 + (*input++)*4;
 +					bbptr = (byte *)vq2 + (*input++)*4;
 +					for(j=0;j<2;j++) 
 +						VQ2TO4(baptr,bbptr,bcptr,bdptr);
 +				}
 +			}
 +		} else {
 +//
 +// double height, smoothed
 +//
 +			if (cinTable[currentHandle].samplesPerPixel==2) {
 +				for(i=0;i<two;i++) {
 +					y0 = (long)*input++;
 +					y1 = (long)*input++;
 +					y2 = (long)*input++;
 +					y3 = (long)*input++;
 +					cr = (long)*input++;
 +					cb = (long)*input++;
 +					*bptr++ = yuv_to_rgb( y0, cr, cb );
 +					*bptr++ = yuv_to_rgb( y1, cr, cb );
 +					*bptr++ = yuv_to_rgb( ((y0*3)+y2)/4, cr, cb );
 +					*bptr++ = yuv_to_rgb( ((y1*3)+y3)/4, cr, cb );
 +					*bptr++ = yuv_to_rgb( (y0+(y2*3))/4, cr, cb );
 +					*bptr++ = yuv_to_rgb( (y1+(y3*3))/4, cr, cb );
 +					*bptr++ = yuv_to_rgb( y2, cr, cb );
 +					*bptr++ = yuv_to_rgb( y3, cr, cb );
 +				}
 +
 +				cptr = (unsigned short *)vq4;
 +				dptr = (unsigned short *)vq8;
 +		
 +				for(i=0;i<four;i++) {
 +					aptr = (unsigned short *)vq2 + (*input++)*8;
 +					bptr = (unsigned short *)vq2 + (*input++)*8;
 +					for(j=0;j<2;j++) {
 +						VQ2TO4(aptr,bptr,cptr,dptr);
 +						VQ2TO4(aptr,bptr,cptr,dptr);
 +					}
 +				}
 +			} else if (cinTable[currentHandle].samplesPerPixel==4) {
 +				ibptr = (unsigned int *)bptr;
 +				for(i=0;i<two;i++) {
 +					y0 = (long)*input++;
 +					y1 = (long)*input++;
 +					y2 = (long)*input++;
 +					y3 = (long)*input++;
 +					cr = (long)*input++;
 +					cb = (long)*input++;
 +					*ibptr++ = yuv_to_rgb24( y0, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y1, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( ((y0*3)+y2)/4, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( ((y1*3)+y3)/4, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( (y0+(y2*3))/4, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( (y1+(y3*3))/4, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y2, cr, cb );
 +					*ibptr++ = yuv_to_rgb24( y3, cr, cb );
 +				}
 +
 +				icptr = (unsigned int *)vq4;
 +				idptr = (unsigned int *)vq8;
 +	
 +				for(i=0;i<four;i++) {
 +					iaptr = (unsigned int *)vq2 + (*input++)*8;
 +					ibptr = (unsigned int *)vq2 + (*input++)*8;
 +					for(j=0;j<2;j++) {
 +						VQ2TO4(iaptr, ibptr, icptr, idptr);
 +						VQ2TO4(iaptr, ibptr, icptr, idptr);
 +					}
 +				}
 +			} else if (cinTable[currentHandle].samplesPerPixel==1) {
 +				bbptr = (byte *)bptr;
 +				for(i=0;i<two;i++) {
 +					y0 = (long)*input++;
 +					y1 = (long)*input++;
 +					y2 = (long)*input++;
 +					y3 = (long)*input; input+= 3;
 +					*bbptr++ = cinTable[currentHandle].gray[y0];
 +					*bbptr++ = cinTable[currentHandle].gray[y1];
 +					*bbptr++ = cinTable[currentHandle].gray[((y0*3)+y2)/4];
 +					*bbptr++ = cinTable[currentHandle].gray[((y1*3)+y3)/4];
 +					*bbptr++ = cinTable[currentHandle].gray[(y0+(y2*3))/4];
 +					*bbptr++ = cinTable[currentHandle].gray[(y1+(y3*3))/4];						
 +					*bbptr++ = cinTable[currentHandle].gray[y2];
 +					*bbptr++ = cinTable[currentHandle].gray[y3];
 +				}
 +
 +				bcptr = (byte *)vq4;
 +				bdptr = (byte *)vq8;
 +	
 +				for(i=0;i<four;i++) {
 +					baptr = (byte *)vq2 + (*input++)*8;
 +					bbptr = (byte *)vq2 + (*input++)*8;
 +					for(j=0;j<2;j++) {
 +						VQ2TO4(baptr,bbptr,bcptr,bdptr);
 +						VQ2TO4(baptr,bbptr,bcptr,bdptr);
 +					}
 +				}
 +			}			
 +		}
 +	} else {
 +//
 +// 1/4 screen
 +//
 +		if (cinTable[currentHandle].samplesPerPixel==2) {
 +			for(i=0;i<two;i++) {
 +				y0 = (long)*input; input+=2;
 +				y2 = (long)*input; input+=2;
 +				cr = (long)*input++;
 +				cb = (long)*input++;
 +				*bptr++ = yuv_to_rgb( y0, cr, cb );
 +				*bptr++ = yuv_to_rgb( y2, cr, cb );
 +			}
 +
 +			cptr = (unsigned short *)vq4;
 +			dptr = (unsigned short *)vq8;
 +	
 +			for(i=0;i<four;i++) {
 +				aptr = (unsigned short *)vq2 + (*input++)*2;
 +				bptr = (unsigned short *)vq2 + (*input++)*2;
 +				for(j=0;j<2;j++) { 
 +					VQ2TO2(aptr,bptr,cptr,dptr);
 +				}
 +			}
 +		} else if (cinTable[currentHandle].samplesPerPixel == 1) {
 +			bbptr = (byte *)bptr;
 +				
 +			for(i=0;i<two;i++) {
 +				*bbptr++ = cinTable[currentHandle].gray[*input]; input+=2;
 +				*bbptr++ = cinTable[currentHandle].gray[*input]; input+=4;
 +			}
 +
 +			bcptr = (byte *)vq4;
 +			bdptr = (byte *)vq8;
 +	
 +			for(i=0;i<four;i++) {
 +				baptr = (byte *)vq2 + (*input++)*2;
 +				bbptr = (byte *)vq2 + (*input++)*2;
 +				for(j=0;j<2;j++) { 
 +					VQ2TO2(baptr,bbptr,bcptr,bdptr);
 +				}
 +			}			
 +		} else if (cinTable[currentHandle].samplesPerPixel == 4) {
 +			ibptr = (unsigned int *) bptr;
 +			for(i=0;i<two;i++) {
 +				y0 = (long)*input; input+=2;
 +				y2 = (long)*input; input+=2;
 +				cr = (long)*input++;
 +				cb = (long)*input++;
 +				*ibptr++ = yuv_to_rgb24( y0, cr, cb );
 +				*ibptr++ = yuv_to_rgb24( y2, cr, cb );
 +			}
 +
 +			icptr = (unsigned int *)vq4;
 +			idptr = (unsigned int *)vq8;
 +	
 +			for(i=0;i<four;i++) {
 +				iaptr = (unsigned int *)vq2 + (*input++)*2;
 +				ibptr = (unsigned int *)vq2 + (*input++)*2;
 +				for(j=0;j<2;j++) { 
 +					VQ2TO2(iaptr,ibptr,icptr,idptr);
 +				}
 +			}
 +		}
 +	}
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff )
 +{
 +	byte *scroff;
 +	long bigx, bigy, lowx, lowy, useY;
 +	long offset;
 +
 +	offset = cinTable[currentHandle].screenDelta;
 +	
 +	lowx = lowy = 0;
 +	bigx = cinTable[currentHandle].xsize;
 +	bigy = cinTable[currentHandle].ysize;
 +
 +	if (bigx > cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH;
 +	if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT;
 +
 +	if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) {
 +		useY = startY;
 +		scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel);
 +
 +		cin.qStatus[0][cinTable[currentHandle].onQuad  ] = scroff;
 +		cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset;
 +	}
 +
 +	if ( quadSize != MINSIZE ) {
 +		quadSize >>= 1;
 +		recurseQuad( startX,		  startY		  , quadSize, xOff, yOff );
 +		recurseQuad( startX+quadSize, startY		  , quadSize, xOff, yOff );
 +		recurseQuad( startX,		  startY+quadSize , quadSize, xOff, yOff );
 +		recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff );
 +	}
 +}
 +
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void setupQuad( long xOff, long yOff )
 +{
 +	long numQuadCels, i,x,y;
 +	byte *temp;
 +
 +	if (xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == cin.oldysize && cinTable[currentHandle].xsize == cin.oldxsize) {
 +		return;
 +	}
 +
 +	cin.oldXOff = xOff;
 +	cin.oldYOff = yOff;
 +	cin.oldysize = cinTable[currentHandle].ysize;
 +	cin.oldxsize = cinTable[currentHandle].xsize;
 +
 +	numQuadCels  = (cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].CIN_HEIGHT) / (16);
 +	numQuadCels += numQuadCels/4 + numQuadCels/16;
 +	numQuadCels += 64;							  // for overflow
 +
 +	numQuadCels  = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16);
 +	numQuadCels += numQuadCels/4;
 +	numQuadCels += 64;							  // for overflow
 +
 +	cinTable[currentHandle].onQuad = 0;
 +
 +	for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16) 
 +		for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16) 
 +			recurseQuad( x, y, 16, xOff, yOff );
 +
 +	temp = NULL;
 +
 +	for(i=(numQuadCels-64);i<numQuadCels;i++) {
 +		cin.qStatus[0][i] = temp;			  // eoq
 +		cin.qStatus[1][i] = temp;			  // eoq
 +	}
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void readQuadInfo( byte *qData )
 +{
 +	if (currentHandle < 0) return;
 +
 +	cinTable[currentHandle].xsize    = qData[0]+qData[1]*256;
 +	cinTable[currentHandle].ysize    = qData[2]+qData[3]*256;
 +	cinTable[currentHandle].maxsize  = qData[4]+qData[5]*256;
 +	cinTable[currentHandle].minsize  = qData[6]+qData[7]*256;
 +	
 +	cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize;
 +	cinTable[currentHandle].CIN_WIDTH  = cinTable[currentHandle].xsize;
 +
 +	cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].samplesPerPixel;
 +	cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT*cinTable[currentHandle].samplesPerLine;
 +
 +	cinTable[currentHandle].half = qfalse;
 +	cinTable[currentHandle].smootheddouble = qfalse;
 +	
 +	cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal;
 +	cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer;
 +
 +	cinTable[currentHandle].t[0] = (0 - (unsigned int)cin.linbuf)+(unsigned int)cin.linbuf+cinTable[currentHandle].screenDelta;
 +	cinTable[currentHandle].t[1] = (0 - ((unsigned int)cin.linbuf + cinTable[currentHandle].screenDelta))+(unsigned int)cin.linbuf;
 +
 +        cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH;
 +        cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT;
 +        
 +	// rage pro is very slow at 512 wide textures, voodoo can't do it at all
 +	if ( glConfig.hardwareType == GLHW_RAGEPRO || glConfig.maxTextureSize <= 256) {
 +                if (cinTable[currentHandle].drawX>256) {
 +                        cinTable[currentHandle].drawX = 256;
 +                }
 +                if (cinTable[currentHandle].drawY>256) {
 +                        cinTable[currentHandle].drawY = 256;
 +                }
 +		if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) {
 +			Com_Printf("HACK: approxmimating cinematic for Rage Pro or Voodoo\n");
 +		}
 +	}
 +#if defined(MACOS_X)
 +	cinTable[currentHandle].drawX = 256;
 +	cinTable[currentHandle].drawX = 256;
 +#endif
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void RoQPrepMcomp( long xoff, long yoff ) 
 +{
 +	long i, j, x, y, temp, temp2;
 +
 +	i=cinTable[currentHandle].samplesPerLine; j=cinTable[currentHandle].samplesPerPixel;
 +	if ( cinTable[currentHandle].xsize == (cinTable[currentHandle].ysize*4) && !cinTable[currentHandle].half ) { j = j+j; i = i+i; }
 +	
 +	for(y=0;y<16;y++) {
 +		temp2 = (y+yoff-8)*i;
 +		for(x=0;x<16;x++) {
 +			temp = (x+xoff-8)*j;
 +			cin.mcomp[(x*16)+y] = cinTable[currentHandle].normalBuffer0-(temp2+temp);
 +		}
 +	}
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void initRoQ() 
 +{
 +	if (currentHandle < 0) return;
 +
 +	cinTable[currentHandle].VQNormal = (void (*)(byte *, void *))blitVQQuad32fs;
 +	cinTable[currentHandle].VQBuffer = (void (*)(byte *, void *))blitVQQuad32fs;
 +	cinTable[currentHandle].samplesPerPixel = 4;
 +	ROQ_GenYUVTables();
 +	RllSetupTable();
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +/*
 +static byte* RoQFetchInterlaced( byte *source ) {
 +	int x, *src, *dst;
 +
 +	if (currentHandle < 0) return NULL;
 +
 +	src = (int *)source;
 +	dst = (int *)cinTable[currentHandle].buf2;
 +
 +	for(x=0;x<256*256;x++) {
 +		*dst = *src;
 +		dst++; src += 2;
 +	}
 +	return cinTable[currentHandle].buf2;
 +}
 +*/
 +static void RoQReset() {
 +	
 +	if (currentHandle < 0) return;
 +
 +	Sys_EndStreamedFile(cinTable[currentHandle].iFile);
 +	FS_FCloseFile( cinTable[currentHandle].iFile );
 +	FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
 +	// let the background thread start reading ahead
 +	Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 );
 +	Sys_StreamedRead (cin.file, 16, 1, cinTable[currentHandle].iFile);
 +	RoQ_init();
 +	cinTable[currentHandle].status = FMV_LOOPED;
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void RoQInterrupt(void)
 +{
 +	byte				*framedata;
 +        short		sbuf[32768];
 +        int		ssize;
 +        
 +	if (currentHandle < 0) return;
 +
 +	Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile );
 +	if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { 
 +		if (cinTable[currentHandle].holdAtEnd==qfalse) {
 +			if (cinTable[currentHandle].looping) {
 +				RoQReset();
 +			} else {
 +				cinTable[currentHandle].status = FMV_EOF;
 +			}
 +		} else {
 +			cinTable[currentHandle].status = FMV_IDLE;
 +		}
 +		return; 
 +	}
 +
 +	framedata = cin.file;
 +//
 +// new frame is ready
 +//
 +redump:
 +	switch(cinTable[currentHandle].roq_id) 
 +	{
 +		case	ROQ_QUAD_VQ:
 +			if ((cinTable[currentHandle].numQuads&1)) {
 +				cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1];
 +				RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
 +				cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata);
 +				cinTable[currentHandle].buf = 	cin.linbuf + cinTable[currentHandle].screenDelta;
 +			} else {
 +				cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0];
 +				RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
 +				cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata );
 +				cinTable[currentHandle].buf = 	cin.linbuf;
 +			}
 +			if (cinTable[currentHandle].numQuads == 0) {		// first frame
 +				Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize);
 +			}
 +			cinTable[currentHandle].numQuads++;
 +			cinTable[currentHandle].dirty = qtrue;
 +			break;
 +		case	ROQ_CODEBOOK:
 +			decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags );
 +			break;
 +		case	ZA_SOUND_MONO:
 +			if (!cinTable[currentHandle].silent) {
 +				ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
 +                                S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f );
 +			}
 +			break;
 +		case	ZA_SOUND_STEREO:
 +			if (!cinTable[currentHandle].silent) {
 +				if (cinTable[currentHandle].numQuads == -1) {
 +					S_Update();
 +					s_rawend = s_soundtime;
 +				}
 +				ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
 +                                S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f );
 +			}
 +			break;
 +		case	ROQ_QUAD_INFO:
 +			if (cinTable[currentHandle].numQuads == -1) {
 +				readQuadInfo( framedata );
 +				setupQuad( 0, 0 );
 +				// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
 +				cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value;
 +			}
 +			if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0;
 +			break;
 +		case	ROQ_PACKET:
 +			cinTable[currentHandle].inMemory = cinTable[currentHandle].roq_flags;
 +			cinTable[currentHandle].RoQFrameSize = 0;           // for header
 +			break;
 +		case	ROQ_QUAD_HANG:
 +			cinTable[currentHandle].RoQFrameSize = 0;
 +			break;
 +		case	ROQ_QUAD_JPEG:
 +			break;
 +		default:
 +			cinTable[currentHandle].status = FMV_EOF;
 +			break;
 +	}	
 +//
 +// read in next frame data
 +//
 +	if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { 
 +		if (cinTable[currentHandle].holdAtEnd==qfalse) {
 +			if (cinTable[currentHandle].looping) {
 +				RoQReset();
 +			} else {
 +				cinTable[currentHandle].status = FMV_EOF;
 +			}
 +		} else {
 +			cinTable[currentHandle].status = FMV_IDLE;
 +		}
 +		return; 
 +	}
 +	
 +	framedata		 += cinTable[currentHandle].RoQFrameSize;
 +	cinTable[currentHandle].roq_id		 = framedata[0] + framedata[1]*256;
 +	cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536;
 +	cinTable[currentHandle].roq_flags	 = framedata[6] + framedata[7]*256;
 +	cinTable[currentHandle].roqF0		 = (char)framedata[7];
 +	cinTable[currentHandle].roqF1		 = (char)framedata[6];
 +
 +	if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) {
 +		Com_DPrintf("roq_size>65536||roq_id==0x1084\n");
 +		cinTable[currentHandle].status = FMV_EOF;
 +		if (cinTable[currentHandle].looping) {
 +			RoQReset();
 +		}
 +		return;
 +	}
 +	if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF)) { cinTable[currentHandle].inMemory--; framedata += 8; goto redump; }
 +//
 +// one more frame hits the dust
 +//
 +//	assert(cinTable[currentHandle].RoQFrameSize <= 65536);
 +//	r = Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile );
 +	cinTable[currentHandle].RoQPlayed	+= cinTable[currentHandle].RoQFrameSize+8;
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void RoQ_init( void )
 +{
 +	// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
 +	cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value;
 +
 +	cinTable[currentHandle].RoQPlayed = 24;
 +
 +/*	get frame rate */	
 +	cinTable[currentHandle].roqFPS	 = cin.file[ 6] + cin.file[ 7]*256;
 +	
 +	if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30;
 +
 +	cinTable[currentHandle].numQuads = -1;
 +
 +	cinTable[currentHandle].roq_id		= cin.file[ 8] + cin.file[ 9]*256;
 +	cinTable[currentHandle].RoQFrameSize	= cin.file[10] + cin.file[11]*256 + cin.file[12]*65536;
 +	cinTable[currentHandle].roq_flags	= cin.file[14] + cin.file[15]*256;
 +
 +	if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) { 
 +		return;
 +	}
 +
 +}
 +
 +/******************************************************************************
 +*
 +* Function:		
 +*
 +* Description:	
 +*
 +******************************************************************************/
 +
 +static void RoQShutdown( void ) {
 +	const char *s;
 +
 +	if (!cinTable[currentHandle].buf) {
 +		return;
 +	}
 +
 +	if ( cinTable[currentHandle].status == FMV_IDLE ) {
 +		return;
 +	}
 +	Com_DPrintf("finished cinematic\n");
 +	cinTable[currentHandle].status = FMV_IDLE;
 +
 +	if (cinTable[currentHandle].iFile) {
 +		Sys_EndStreamedFile( cinTable[currentHandle].iFile );
 +		FS_FCloseFile( cinTable[currentHandle].iFile );
 +		cinTable[currentHandle].iFile = 0;
 +	}
 +
 +	if (cinTable[currentHandle].alterGameState) {
 +		cls.state = CA_DISCONNECTED;
 +		// we can't just do a vstr nextmap, because
 +		// if we are aborting the intro cinematic with
 +		// a devmap command, nextmap would be valid by
 +		// the time it was referenced
 +		s = Cvar_VariableString( "nextmap" );
 +		if ( s[0] ) {
 +			Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) );
 +			Cvar_Set( "nextmap", "" );
 +		}
 +		CL_handle = -1;
 +	}
 +	cinTable[currentHandle].fileName[0] = 0;
 +	currentHandle = -1;
 +}
 +
 +/*
 +==================
 +SCR_StopCinematic
 +==================
 +*/
 +e_status CIN_StopCinematic(int handle) {
 +	
 +	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
 +	currentHandle = handle;
 +
 +	Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName);
 +
 +	if (!cinTable[currentHandle].buf) {
 +		return FMV_EOF;
 +	}
 +
 +	if (cinTable[currentHandle].alterGameState) {
 +		if ( cls.state != CA_CINEMATIC ) {
 +			return cinTable[currentHandle].status;
 +		}
 +	}
 +	cinTable[currentHandle].status = FMV_EOF;
 +	RoQShutdown();
 +
 +	return FMV_EOF;
 +}
 +
 +/*
 +==================
 +SCR_RunCinematic
 +
 +Fetch and decompress the pending frame
 +==================
 +*/
 +
 +
 +e_status CIN_RunCinematic (int handle)
 +{
 +        // bk001204 - init
 +	int	start = 0;
 +	int     thisTime = 0;
 +
 +	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
 +
 +	if (cin.currentHandle != handle) {
 +		currentHandle = handle;
 +		cin.currentHandle = currentHandle;
 +		cinTable[currentHandle].status = FMV_EOF;
 +		RoQReset();
 +	}
 +
 +	if (cinTable[handle].playonwalls < -1)
 +	{
 +		return cinTable[handle].status;
 +	}
 +
 +	currentHandle = handle;
 +
 +	if (cinTable[currentHandle].alterGameState) {
 +		if ( cls.state != CA_CINEMATIC ) {
 +			return cinTable[currentHandle].status;
 +		}
 +	}
 +
 +	if (cinTable[currentHandle].status == FMV_IDLE) {
 +		return cinTable[currentHandle].status;
 +	}
 +
 +	// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
 +	thisTime = CL_ScaledMilliseconds()*com_timescale->value;
 +	if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime))>100) {
 +		cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime;
 +	}
 +	// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
 +	cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*3)/100);
 +
 +	start = cinTable[currentHandle].startTime;
 +	while(  (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads)
 +		&& (cinTable[currentHandle].status == FMV_PLAY) ) 
 +	{
 +		RoQInterrupt();
 +		if (start != cinTable[currentHandle].startTime) {
 +			// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
 +		  cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value)
 +							  - cinTable[currentHandle].startTime)*3)/100);
 +			start = cinTable[currentHandle].startTime;
 +		}
 +	}
 +
 +	cinTable[currentHandle].lastTime = thisTime;
 +
 +	if (cinTable[currentHandle].status == FMV_LOOPED) {
 +		cinTable[currentHandle].status = FMV_PLAY;
 +	}
 +
 +	if (cinTable[currentHandle].status == FMV_EOF) {
 +	  if (cinTable[currentHandle].looping) {
 +		RoQReset();
 +	  } else {
 +		RoQShutdown();
 +	  }
 +	}
 +
 +	return cinTable[currentHandle].status;
 +}
 +
 +/*
 +==================
 +CL_PlayCinematic
 +
 +==================
 +*/
 +int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) {
 +	unsigned short RoQID;
 +	char	name[MAX_OSPATH];
 +	int		i;
 +
 +	if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) {
 +		Com_sprintf (name, sizeof(name), "video/%s", arg);
 +	} else {
 +		Com_sprintf (name, sizeof(name), "%s", arg);
 +	}
 +
 +	if (!(systemBits & CIN_system)) {
 +		for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
 +			if (!strcmp(cinTable[i].fileName, name) ) {
 +				return i;
 +			}
 +		}
 +	}
 +
 +	Com_DPrintf("SCR_PlayCinematic( %s )\n", arg);
 +
 +	Com_Memset(&cin, 0, sizeof(cinematics_t) );
 +	currentHandle = CIN_HandleForVideo();
 +
 +	cin.currentHandle = currentHandle;
 +
 +	strcpy(cinTable[currentHandle].fileName, name);
 +
 +	cinTable[currentHandle].ROQSize = 0;
 +	cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
 +
 +	if (cinTable[currentHandle].ROQSize<=0) {
 +		Com_DPrintf("play(%s), ROQSize<=0\n", arg);
 +		cinTable[currentHandle].fileName[0] = 0;
 +		return -1;
 +	}
 +
 +	CIN_SetExtents(currentHandle, x, y, w, h);
 +	CIN_SetLooping(currentHandle, (systemBits & CIN_loop)!=0);
 +
 +	cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
 +	cinTable[currentHandle].CIN_WIDTH  =  DEFAULT_CIN_WIDTH;
 +	cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0;
 +	cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0;
 +	cinTable[currentHandle].playonwalls = 1;
 +	cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0;
 +	cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0;
 +
 +	if (cinTable[currentHandle].alterGameState) {
 +		// close the menu
 +		if ( uivm ) {
 +			VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
 +		}
 +	} else {
 +		cinTable[currentHandle].playonwalls = cl_inGameVideo->integer;
 +	}
 +
 +	initRoQ();
 +					
 +	FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
 +
 +	RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256;
 +	if (RoQID == 0x1084)
 +	{
 +		RoQ_init();
 +//		FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile);
 +		// let the background thread start reading ahead
 +		Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 );
 +
 +		cinTable[currentHandle].status = FMV_PLAY;
 +		Com_DPrintf("trFMV::play(), playing %s\n", arg);
 +
 +		if (cinTable[currentHandle].alterGameState) {
 +			cls.state = CA_CINEMATIC;
 +		}
 +		
 +		Con_Close();
 +
 +		s_rawend = s_soundtime;
 +
 +		return currentHandle;
 +	}
 +	Com_DPrintf("trFMV::play(), invalid RoQ ID\n");
 +
 +	RoQShutdown();
 +	return -1;
 +}
 +
 +void CIN_SetExtents (int handle, int x, int y, int w, int h) {
 +	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
 +	cinTable[handle].xpos = x;
 +	cinTable[handle].ypos = y;
 +	cinTable[handle].width = w;
 +	cinTable[handle].height = h;
 +	cinTable[handle].dirty = qtrue;
 +}
 +
 +void CIN_SetLooping(int handle, qboolean loop) {
 +	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
 +	cinTable[handle].looping = loop;
 +}
 +
 +/*
 +==================
 +SCR_DrawCinematic
 +
 +==================
 +*/
 +void CIN_DrawCinematic (int handle) {
 +	float	x, y, w, h;
 +	byte	*buf;
 +
 +	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
 +
 +	if (!cinTable[handle].buf) {
 +		return;
 +	}
 +
 +	x = cinTable[handle].xpos;
 +	y = cinTable[handle].ypos;
 +	w = cinTable[handle].width;
 +	h = cinTable[handle].height;
 +	buf = cinTable[handle].buf;
 +	SCR_AdjustFrom640( &x, &y, &w, &h );
 +
 +	if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) {
 +		int ix, iy, *buf2, *buf3, xm, ym, ll;
 +                
 +		xm = cinTable[handle].CIN_WIDTH/256;
 +		ym = cinTable[handle].CIN_HEIGHT/256;
 +                ll = 8;
 +                if (cinTable[handle].CIN_WIDTH==512) {
 +                    ll = 9;
 +                }
 +                
 +		buf3 = (int*)buf;
 +		buf2 = Hunk_AllocateTempMemory( 256*256*4 );
 +                if (xm==2 && ym==2) {
 +                    byte *bc2, *bc3;
 +                    int	ic, iiy;
 +                    
 +                    bc2 = (byte *)buf2;
 +                    bc3 = (byte *)buf3;
 +                    for (iy = 0; iy<256; iy++) {
 +                            iiy = iy<<12;
 +                            for (ix = 0; ix<2048; ix+=8) {
 +                                for(ic = ix;ic<(ix+4);ic++) {
 +                                    *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2;
 +                                    bc2++;
 +                                }
 +                            }
 +                    }
 +                } else if (xm==2 && ym==1) {
 +                    byte *bc2, *bc3;
 +                    int	ic, iiy;
 +                    
 +                    bc2 = (byte *)buf2;
 +                    bc3 = (byte *)buf3;
 +                    for (iy = 0; iy<256; iy++) {
 +                            iiy = iy<<11;
 +                            for (ix = 0; ix<2048; ix+=8) {
 +                                for(ic = ix;ic<(ix+4);ic++) {
 +                                    *bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1;
 +                                    bc2++;
 +                                }
 +                            }
 +                    }
 +                } else {
 +                    for (iy = 0; iy<256; iy++) {
 +                            for (ix = 0; ix<256; ix++) {
 +                                    buf2[(iy<<8)+ix] = buf3[((iy*ym)<<ll) + (ix*xm)];
 +                            }
 +                    }
 +                }
 +		re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue);
 +		cinTable[handle].dirty = qfalse;
 +		Hunk_FreeTempMemory(buf2);
 +		return;
 +	}
 +
 +	re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty);
 +	cinTable[handle].dirty = qfalse;
 +}
 +
 +void CL_PlayCinematic_f(void) {
 +	char	*arg, *s;
 +	qboolean	holdatend;
 +	int bits = CIN_system;
 +
 +	Com_DPrintf("CL_PlayCinematic_f\n");
 +	if (cls.state == CA_CINEMATIC) {
 +		SCR_StopCinematic();
 +	}
 +
 +	arg = Cmd_Argv( 1 );
 +	s = Cmd_Argv(2);
 +
 +	holdatend = qfalse;
 +	if ((s && s[0] == '1') || Q_stricmp(arg,"demoend.roq")==0 || Q_stricmp(arg,"end.roq")==0) {
 +		bits |= CIN_hold;
 +	}
 +	if (s && s[0] == '2') {
 +		bits |= CIN_loop;
 +	}
 +
 +	S_StopAllSounds ();
 +
 +	CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits );
 +	if (CL_handle >= 0) {
 +		do {
 +			SCR_RunCinematic();
 +		} while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY);		// wait for first frame (load codebook and sound)
 +	}
 +}
 +
 +
 +void SCR_DrawCinematic (void) {
 +	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
 +		CIN_DrawCinematic(CL_handle);
 +	}
 +}
 +
 +void SCR_RunCinematic (void)
 +{
 +	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
 +		CIN_RunCinematic(CL_handle);
 +	}
 +}
 +
 +void SCR_StopCinematic(void) {
 +	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
 +		CIN_StopCinematic(CL_handle);
 +		S_StopAllSounds ();
 +		CL_handle = -1;
 +	}
 +}
 +
 +void CIN_UploadCinematic(int handle) {
 +	if (handle >= 0 && handle < MAX_VIDEO_HANDLES) {
 +		if (!cinTable[handle].buf) {
 +			return;
 +		}
 +		if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) {
 +			if (cinTable[handle].playonwalls == 0) {
 +				cinTable[handle].playonwalls = -1;
 +			} else {
 +				if (cinTable[handle].playonwalls == -1) {
 +					cinTable[handle].playonwalls = -2;
 +				} else {
 +					cinTable[handle].dirty = qfalse;
 +				}
 +			}
 +		}
 +		re.UploadCinematic( 256, 256, 256, 256, cinTable[handle].buf, handle, cinTable[handle].dirty);
 +		if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) {
 +			cinTable[handle].playonwalls--;
 +		}
 +	}
 +}
 +
 diff --git a/code/client/cl_console.c b/code/client/cl_console.c new file mode 100755 index 0000000..50df0e2 --- /dev/null +++ b/code/client/cl_console.c @@ -0,0 +1,786 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// console.c
 +
 +#include "client.h"
 +
 +
 +int g_console_field_width = 78;
 +
 +
 +#define	NUM_CON_TIMES 4
 +
 +#define		CON_TEXTSIZE	32768
 +typedef struct {
 +	qboolean	initialized;
 +
 +	short	text[CON_TEXTSIZE];
 +	int		current;		// line where next message will be printed
 +	int		x;				// offset in current line for next print
 +	int		display;		// bottom of console displays this line
 +
 +	int 	linewidth;		// characters across screen
 +	int		totallines;		// total lines in console scrollback
 +
 +	float	xadjust;		// for wide aspect screens
 +
 +	float	displayFrac;	// aproaches finalFrac at scr_conspeed
 +	float	finalFrac;		// 0.0 to 1.0 lines of console to display
 +
 +	int		vislines;		// in scanlines
 +
 +	int		times[NUM_CON_TIMES];	// cls.realtime time the line was generated
 +								// for transparent notify lines
 +	vec4_t	color;
 +} console_t;
 +
 +extern	console_t	con;
 +
 +console_t	con;
 +
 +cvar_t		*con_conspeed;
 +cvar_t		*con_notifytime;
 +
 +#define	DEFAULT_CONSOLE_WIDTH	78
 +
 +vec4_t	console_color = {1.0, 1.0, 1.0, 1.0};
 +
 +
 +/*
 +================
 +Con_ToggleConsole_f
 +================
 +*/
 +void Con_ToggleConsole_f (void) {
 +	// closing a full screen console restarts the demo loop
 +	if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) {
 +		CL_StartDemoLoop();
 +		return;
 +	}
 +
 +	Field_Clear( &g_consoleField );
 +	g_consoleField.widthInChars = g_console_field_width;
 +
 +	Con_ClearNotify ();
 +	cls.keyCatchers ^= KEYCATCH_CONSOLE;
 +}
 +
 +/*
 +================
 +Con_MessageMode_f
 +================
 +*/
 +void Con_MessageMode_f (void) {
 +	chat_playerNum = -1;
 +	chat_team = qfalse;
 +	Field_Clear( &chatField );
 +	chatField.widthInChars = 30;
 +
 +	cls.keyCatchers ^= KEYCATCH_MESSAGE;
 +}
 +
 +/*
 +================
 +Con_MessageMode2_f
 +================
 +*/
 +void Con_MessageMode2_f (void) {
 +	chat_playerNum = -1;
 +	chat_team = qtrue;
 +	Field_Clear( &chatField );
 +	chatField.widthInChars = 25;
 +	cls.keyCatchers ^= KEYCATCH_MESSAGE;
 +}
 +
 +/*
 +================
 +Con_MessageMode3_f
 +================
 +*/
 +void Con_MessageMode3_f (void) {
 +	chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
 +	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
 +		chat_playerNum = -1;
 +		return;
 +	}
 +	chat_team = qfalse;
 +	Field_Clear( &chatField );
 +	chatField.widthInChars = 30;
 +	cls.keyCatchers ^= KEYCATCH_MESSAGE;
 +}
 +
 +/*
 +================
 +Con_MessageMode4_f
 +================
 +*/
 +void Con_MessageMode4_f (void) {
 +	chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER );
 +	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
 +		chat_playerNum = -1;
 +		return;
 +	}
 +	chat_team = qfalse;
 +	Field_Clear( &chatField );
 +	chatField.widthInChars = 30;
 +	cls.keyCatchers ^= KEYCATCH_MESSAGE;
 +}
 +
 +/*
 +================
 +Con_Clear_f
 +================
 +*/
 +void Con_Clear_f (void) {
 +	int		i;
 +
 +	for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
 +		con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
 +	}
 +
 +	Con_Bottom();		// go to end
 +}
 +
 +						
 +/*
 +================
 +Con_Dump_f
 +
 +Save the console contents out to a file
 +================
 +*/
 +void Con_Dump_f (void)
 +{
 +	int		l, x, i;
 +	short	*line;
 +	fileHandle_t	f;
 +	char	buffer[1024];
 +
 +	if (Cmd_Argc() != 2)
 +	{
 +		Com_Printf ("usage: condump <filename>\n");
 +		return;
 +	}
 +
 +	Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) );
 +
 +	f = FS_FOpenFileWrite( Cmd_Argv( 1 ) );
 +	if (!f)
 +	{
 +		Com_Printf ("ERROR: couldn't open.\n");
 +		return;
 +	}
 +
 +	// skip empty lines
 +	for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
 +	{
 +		line = con.text + (l%con.totallines)*con.linewidth;
 +		for (x=0 ; x<con.linewidth ; x++)
 +			if ((line[x] & 0xff) != ' ')
 +				break;
 +		if (x != con.linewidth)
 +			break;
 +	}
 +
 +	// write the remaining lines
 +	buffer[con.linewidth] = 0;
 +	for ( ; l <= con.current ; l++)
 +	{
 +		line = con.text + (l%con.totallines)*con.linewidth;
 +		for(i=0; i<con.linewidth; i++)
 +			buffer[i] = line[i] & 0xff;
 +		for (x=con.linewidth-1 ; x>=0 ; x--)
 +		{
 +			if (buffer[x] == ' ')
 +				buffer[x] = 0;
 +			else
 +				break;
 +		}
 +		strcat( buffer, "\n" );
 +		FS_Write(buffer, strlen(buffer), f);
 +	}
 +
 +	FS_FCloseFile( f );
 +}
 +
 +						
 +/*
 +================
 +Con_ClearNotify
 +================
 +*/
 +void Con_ClearNotify( void ) {
 +	int		i;
 +	
 +	for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
 +		con.times[i] = 0;
 +	}
 +}
 +
 +						
 +
 +/*
 +================
 +Con_CheckResize
 +
 +If the line width has changed, reformat the buffer.
 +================
 +*/
 +void Con_CheckResize (void)
 +{
 +	int		i, j, width, oldwidth, oldtotallines, numlines, numchars;
 +	MAC_STATIC short	tbuf[CON_TEXTSIZE];
 +
 +	width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2;
 +
 +	if (width == con.linewidth)
 +		return;
 +
 +	if (width < 1)			// video hasn't been initialized yet
 +	{
 +		width = DEFAULT_CONSOLE_WIDTH;
 +		con.linewidth = width;
 +		con.totallines = CON_TEXTSIZE / con.linewidth;
 +		for(i=0; i<CON_TEXTSIZE; i++)
 +
 +			con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
 +	}
 +	else
 +	{
 +		oldwidth = con.linewidth;
 +		con.linewidth = width;
 +		oldtotallines = con.totallines;
 +		con.totallines = CON_TEXTSIZE / con.linewidth;
 +		numlines = oldtotallines;
 +
 +		if (con.totallines < numlines)
 +			numlines = con.totallines;
 +
 +		numchars = oldwidth;
 +	
 +		if (con.linewidth < numchars)
 +			numchars = con.linewidth;
 +
 +		Com_Memcpy (tbuf, con.text, CON_TEXTSIZE * sizeof(short));
 +		for(i=0; i<CON_TEXTSIZE; i++)
 +
 +			con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
 +
 +
 +		for (i=0 ; i<numlines ; i++)
 +		{
 +			for (j=0 ; j<numchars ; j++)
 +			{
 +				con.text[(con.totallines - 1 - i) * con.linewidth + j] =
 +						tbuf[((con.current - i + oldtotallines) %
 +							  oldtotallines) * oldwidth + j];
 +			}
 +		}
 +
 +		Con_ClearNotify ();
 +	}
 +
 +	con.current = con.totallines - 1;
 +	con.display = con.current;
 +}
 +
 +
 +/*
 +================
 +Con_Init
 +================
 +*/
 +void Con_Init (void) {
 +	int		i;
 +
 +	con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
 +	con_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
 +
 +	Field_Clear( &g_consoleField );
 +	g_consoleField.widthInChars = g_console_field_width;
 +	for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
 +		Field_Clear( &historyEditLines[i] );
 +		historyEditLines[i].widthInChars = g_console_field_width;
 +	}
 +
 +	Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
 +	Cmd_AddCommand ("messagemode", Con_MessageMode_f);
 +	Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
 +	Cmd_AddCommand ("messagemode3", Con_MessageMode3_f);
 +	Cmd_AddCommand ("messagemode4", Con_MessageMode4_f);
 +	Cmd_AddCommand ("clear", Con_Clear_f);
 +	Cmd_AddCommand ("condump", Con_Dump_f);
 +}
 +
 +
 +/*
 +===============
 +Con_Linefeed
 +===============
 +*/
 +void Con_Linefeed (qboolean skipnotify)
 +{
 +	int		i;
 +
 +	// mark time for transparent overlay
 +	if (con.current >= 0)
 +	{
 +    if (skipnotify)
 +		  con.times[con.current % NUM_CON_TIMES] = 0;
 +    else
 +		  con.times[con.current % NUM_CON_TIMES] = cls.realtime;
 +	}
 +
 +	con.x = 0;
 +	if (con.display == con.current)
 +		con.display++;
 +	con.current++;
 +	for(i=0; i<con.linewidth; i++)
 +		con.text[(con.current%con.totallines)*con.linewidth+i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
 +}
 +
 +/*
 +================
 +CL_ConsolePrint
 +
 +Handles cursor positioning, line wrapping, etc
 +All console printing must go through this in order to be logged to disk
 +If no console is visible, the text will appear at the top of the game window
 +================
 +*/
 +void CL_ConsolePrint( char *txt ) {
 +	int		y;
 +	int		c, l;
 +	int		color;
 +	qboolean skipnotify = qfalse;		// NERVE - SMF
 +	int prev;							// NERVE - SMF
 +
 +	// TTimo - prefix for text that shows up in console but not in notify
 +	// backported from RTCW
 +	if ( !Q_strncmp( txt, "[skipnotify]", 12 ) ) {
 +		skipnotify = qtrue;
 +		txt += 12;
 +	}
 +	
 +	// for some demos we don't want to ever show anything on the console
 +	if ( cl_noprint && cl_noprint->integer ) {
 +		return;
 +	}
 +	
 +	if (!con.initialized) {
 +		con.color[0] = 
 +		con.color[1] = 
 +		con.color[2] =
 +		con.color[3] = 1.0f;
 +		con.linewidth = -1;
 +		Con_CheckResize ();
 +		con.initialized = qtrue;
 +	}
 +
 +	color = ColorIndex(COLOR_WHITE);
 +
 +	while ( (c = *txt) != 0 ) {
 +		if ( Q_IsColorString( txt ) ) {
 +			color = ColorIndex( *(txt+1) );
 +			txt += 2;
 +			continue;
 +		}
 +
 +		// count word length
 +		for (l=0 ; l< con.linewidth ; l++) {
 +			if ( txt[l] <= ' ') {
 +				break;
 +			}
 +
 +		}
 +
 +		// word wrap
 +		if (l != con.linewidth && (con.x + l >= con.linewidth) ) {
 +			Con_Linefeed(skipnotify);
 +
 +		}
 +
 +		txt++;
 +
 +		switch (c)
 +		{
 +		case '\n':
 +			Con_Linefeed (skipnotify);
 +			break;
 +		case '\r':
 +			con.x = 0;
 +			break;
 +		default:	// display character and advance
 +			y = con.current % con.totallines;
 +			con.text[y*con.linewidth+con.x] = (color << 8) | c;
 +			con.x++;
 +			if (con.x >= con.linewidth) {
 +				Con_Linefeed(skipnotify);
 +				con.x = 0;
 +			}
 +			break;
 +		}
 +	}
 +
 +
 +	// mark time for transparent overlay
 +	if (con.current >= 0) {
 +		// NERVE - SMF
 +		if ( skipnotify ) {
 +			prev = con.current % NUM_CON_TIMES - 1;
 +			if ( prev < 0 )
 +				prev = NUM_CON_TIMES - 1;
 +			con.times[prev] = 0;
 +		}
 +		else
 +		// -NERVE - SMF
 +			con.times[con.current % NUM_CON_TIMES] = cls.realtime;
 +	}
 +}
 +
 +
 +/*
 +==============================================================================
 +
 +DRAWING
 +
 +==============================================================================
 +*/
 +
 +
 +/*
 +================
 +Con_DrawInput
 +
 +Draw the editline after a ] prompt
 +================
 +*/
 +void Con_DrawInput (void) {
 +	int		y;
 +
 +	if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) {
 +		return;
 +	}
 +
 +	y = con.vislines - ( SMALLCHAR_HEIGHT * 2 );
 +
 +	re.SetColor( con.color );
 +
 +	SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' );
 +
 +	Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y,
 +		SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue );
 +}
 +
 +
 +/*
 +================
 +Con_DrawNotify
 +
 +Draws the last few lines of output transparently over the game top
 +================
 +*/
 +void Con_DrawNotify (void)
 +{
 +	int		x, v;
 +	short	*text;
 +	int		i;
 +	int		time;
 +	int		skip;
 +	int		currentColor;
 +
 +	currentColor = 7;
 +	re.SetColor( g_color_table[currentColor] );
 +
 +	v = 0;
 +	for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
 +	{
 +		if (i < 0)
 +			continue;
 +		time = con.times[i % NUM_CON_TIMES];
 +		if (time == 0)
 +			continue;
 +		time = cls.realtime - time;
 +		if (time > con_notifytime->value*1000)
 +			continue;
 +		text = con.text + (i % con.totallines)*con.linewidth;
 +
 +		if (cl.snap.ps.pm_type != PM_INTERMISSION && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
 +			continue;
 +		}
 +
 +		for (x = 0 ; x < con.linewidth ; x++) {
 +			if ( ( text[x] & 0xff ) == ' ' ) {
 +				continue;
 +			}
 +			if ( ( (text[x]>>8)&7 ) != currentColor ) {
 +				currentColor = (text[x]>>8)&7;
 +				re.SetColor( g_color_table[currentColor] );
 +			}
 +			SCR_DrawSmallChar( cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH, v, text[x] & 0xff );
 +		}
 +
 +		v += SMALLCHAR_HEIGHT;
 +	}
 +
 +	re.SetColor( NULL );
 +
 +	if (cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
 +		return;
 +	}
 +
 +	// draw the chat line
 +	if ( cls.keyCatchers & KEYCATCH_MESSAGE )
 +	{
 +		if (chat_team)
 +		{
 +			SCR_DrawBigString (8, v, "say_team:", 1.0f );
 +			skip = 11;
 +		}
 +		else
 +		{
 +			SCR_DrawBigString (8, v, "say:", 1.0f );
 +			skip = 5;
 +		}
 +
 +		Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v,
 +			SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue );
 +
 +		v += BIGCHAR_HEIGHT;
 +	}
 +
 +}
 +
 +/*
 +================
 +Con_DrawSolidConsole
 +
 +Draws the console with the solid background
 +================
 +*/
 +void Con_DrawSolidConsole( float frac ) {
 +	int				i, x, y;
 +	int				rows;
 +	short			*text;
 +	int				row;
 +	int				lines;
 +//	qhandle_t		conShader;
 +	int				currentColor;
 +	vec4_t			color;
 +
 +	lines = cls.glconfig.vidHeight * frac;
 +	if (lines <= 0)
 +		return;
 +
 +	if (lines > cls.glconfig.vidHeight )
 +		lines = cls.glconfig.vidHeight;
 +
 +	// on wide screens, we will center the text
 +	con.xadjust = 0;
 +	SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL );
 +
 +	// draw the background
 +	y = frac * SCREEN_HEIGHT - 2;
 +	if ( y < 1 ) {
 +		y = 0;
 +	}
 +	else {
 +		SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader );
 +	}
 +
 +	color[0] = 1;
 +	color[1] = 0;
 +	color[2] = 0;
 +	color[3] = 1;
 +	SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color );
 +
 +
 +	// draw the version number
 +
 +	re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
 +
 +	i = strlen( Q3_VERSION );
 +
 +	for (x=0 ; x<i ; x++) {
 +
 +		SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH, 
 +
 +			(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), Q3_VERSION[x] );
 +
 +	}
 +
 +
 +	// draw the text
 +	con.vislines = lines;
 +	rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH;		// rows of text to draw
 +
 +	y = lines - (SMALLCHAR_HEIGHT*3);
 +
 +	// draw from the bottom up
 +	if (con.display != con.current)
 +	{
 +	// draw arrows to show the buffer is backscrolled
 +		re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
 +		for (x=0 ; x<con.linewidth ; x+=4)
 +			SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, '^' );
 +		y -= SMALLCHAR_HEIGHT;
 +		rows--;
 +	}
 +	
 +	row = con.display;
 +
 +	if ( con.x == 0 ) {
 +		row--;
 +	}
 +
 +	currentColor = 7;
 +	re.SetColor( g_color_table[currentColor] );
 +
 +	for (i=0 ; i<rows ; i++, y -= SMALLCHAR_HEIGHT, row--)
 +	{
 +		if (row < 0)
 +			break;
 +		if (con.current - row >= con.totallines) {
 +			// past scrollback wrap point
 +			continue;	
 +		}
 +
 +		text = con.text + (row % con.totallines)*con.linewidth;
 +
 +		for (x=0 ; x<con.linewidth ; x++) {
 +			if ( ( text[x] & 0xff ) == ' ' ) {
 +				continue;
 +			}
 +
 +			if ( ( (text[x]>>8)&7 ) != currentColor ) {
 +				currentColor = (text[x]>>8)&7;
 +				re.SetColor( g_color_table[currentColor] );
 +			}
 +			SCR_DrawSmallChar(  con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff );
 +		}
 +	}
 +
 +	// draw the input prompt, user text, and cursor if desired
 +	Con_DrawInput ();
 +
 +	re.SetColor( NULL );
 +}
 +
 +
 +
 +/*
 +==================
 +Con_DrawConsole
 +==================
 +*/
 +void Con_DrawConsole( void ) {
 +	// check for console width changes from a vid mode change
 +	Con_CheckResize ();
 +
 +	// if disconnected, render console full screen
 +	if ( cls.state == CA_DISCONNECTED ) {
 +		if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) {
 +			Con_DrawSolidConsole( 1.0 );
 +			return;
 +		}
 +	}
 +
 +	if ( con.displayFrac ) {
 +		Con_DrawSolidConsole( con.displayFrac );
 +	} else {
 +		// draw notify lines
 +		if ( cls.state == CA_ACTIVE ) {
 +			Con_DrawNotify ();
 +		}
 +	}
 +}
 +
 +//================================================================
 +
 +/*
 +==================
 +Con_RunConsole
 +
 +Scroll it up or down
 +==================
 +*/
 +void Con_RunConsole (void) {
 +	// decide on the destination height of the console
 +	if ( cls.keyCatchers & KEYCATCH_CONSOLE )
 +		con.finalFrac = 0.5;		// half screen
 +	else
 +		con.finalFrac = 0;				// none visible
 +	
 +	// scroll towards the destination height
 +	if (con.finalFrac < con.displayFrac)
 +	{
 +		con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001;
 +		if (con.finalFrac > con.displayFrac)
 +			con.displayFrac = con.finalFrac;
 +
 +	}
 +	else if (con.finalFrac > con.displayFrac)
 +	{
 +		con.displayFrac += con_conspeed->value*cls.realFrametime*0.001;
 +		if (con.finalFrac < con.displayFrac)
 +			con.displayFrac = con.finalFrac;
 +	}
 +
 +}
 +
 +
 +void Con_PageUp( void ) {
 +	con.display -= 2;
 +	if ( con.current - con.display >= con.totallines ) {
 +		con.display = con.current - con.totallines + 1;
 +	}
 +}
 +
 +void Con_PageDown( void ) {
 +	con.display += 2;
 +	if (con.display > con.current) {
 +		con.display = con.current;
 +	}
 +}
 +
 +void Con_Top( void ) {
 +	con.display = con.totallines;
 +	if ( con.current - con.display >= con.totallines ) {
 +		con.display = con.current - con.totallines + 1;
 +	}
 +}
 +
 +void Con_Bottom( void ) {
 +	con.display = con.current;
 +}
 +
 +
 +void Con_Close( void ) {
 +	if ( !com_cl_running->integer ) {
 +		return;
 +	}
 +	Field_Clear( &g_consoleField );
 +	Con_ClearNotify ();
 +	cls.keyCatchers &= ~KEYCATCH_CONSOLE;
 +	con.finalFrac = 0;				// none visible
 +	con.displayFrac = 0;
 +}
 diff --git a/code/client/cl_input.c b/code/client/cl_input.c new file mode 100755 index 0000000..f450ee7 --- /dev/null +++ b/code/client/cl_input.c @@ -0,0 +1,901 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// cl.input.c  -- builds an intended movement command to send to the server
 +
 +#include "client.h"
 +
 +unsigned	frame_msec;
 +int			old_com_frameTime;
 +
 +/*
 +===============================================================================
 +
 +KEY BUTTONS
 +
 +Continuous button event tracking is complicated by the fact that two different
 +input sources (say, mouse button 1 and the control key) can both press the
 +same button, but the button should only be released when both of the
 +pressing key have been released.
 +
 +When a key event issues a button command (+forward, +attack, etc), it appends
 +its key number as argv(1) so it can be matched up with the release.
 +
 +argv(2) will be set to the time the event happened, which allows exact
 +control even at low framerates when the down and up events may both get qued
 +at the same time.
 +
 +===============================================================================
 +*/
 +
 +
 +kbutton_t	in_left, in_right, in_forward, in_back;
 +kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
 +kbutton_t	in_strafe, in_speed;
 +kbutton_t	in_up, in_down;
 +
 +kbutton_t	in_buttons[16];
 +
 +
 +qboolean	in_mlooking;
 +
 +
 +void IN_MLookDown( void ) {
 +	in_mlooking = qtrue;
 +}
 +
 +void IN_MLookUp( void ) {
 +	in_mlooking = qfalse;
 +	if ( !cl_freelook->integer ) {
 +		IN_CenterView ();
 +	}
 +}
 +
 +void IN_KeyDown( kbutton_t *b ) {
 +	int		k;
 +	char	*c;
 +	
 +	c = Cmd_Argv(1);
 +	if ( c[0] ) {
 +		k = atoi(c);
 +	} else {
 +		k = -1;		// typed manually at the console for continuous down
 +	}
 +
 +	if ( k == b->down[0] || k == b->down[1] ) {
 +		return;		// repeating key
 +	}
 +	
 +	if ( !b->down[0] ) {
 +		b->down[0] = k;
 +	} else if ( !b->down[1] ) {
 +		b->down[1] = k;
 +	} else {
 +		Com_Printf ("Three keys down for a button!\n");
 +		return;
 +	}
 +	
 +	if ( b->active ) {
 +		return;		// still down
 +	}
 +
 +	// save timestamp for partial frame summing
 +	c = Cmd_Argv(2);
 +	b->downtime = atoi(c);
 +
 +	b->active = qtrue;
 +	b->wasPressed = qtrue;
 +}
 +
 +void IN_KeyUp( kbutton_t *b ) {
 +	int		k;
 +	char	*c;
 +	unsigned	uptime;
 +
 +	c = Cmd_Argv(1);
 +	if ( c[0] ) {
 +		k = atoi(c);
 +	} else {
 +		// typed manually at the console, assume for unsticking, so clear all
 +		b->down[0] = b->down[1] = 0;
 +		b->active = qfalse;
 +		return;
 +	}
 +
 +	if ( b->down[0] == k ) {
 +		b->down[0] = 0;
 +	} else if ( b->down[1] == k ) {
 +		b->down[1] = 0;
 +	} else {
 +		return;		// key up without coresponding down (menu pass through)
 +	}
 +	if ( b->down[0] || b->down[1] ) {
 +		return;		// some other key is still holding it down
 +	}
 +
 +	b->active = qfalse;
 +
 +	// save timestamp for partial frame summing
 +	c = Cmd_Argv(2);
 +	uptime = atoi(c);
 +	if ( uptime ) {
 +		b->msec += uptime - b->downtime;
 +	} else {
 +		b->msec += frame_msec / 2;
 +	}
 +
 +	b->active = qfalse;
 +}
 +
 +
 +
 +/*
 +===============
 +CL_KeyState
 +
 +Returns the fraction of the frame that the key was down
 +===============
 +*/
 +float CL_KeyState( kbutton_t *key ) {
 +	float		val;
 +	int			msec;
 +
 +	msec = key->msec;
 +	key->msec = 0;
 +
 +	if ( key->active ) {
 +		// still down
 +		if ( !key->downtime ) {
 +			msec = com_frameTime;
 +		} else {
 +			msec += com_frameTime - key->downtime;
 +		}
 +		key->downtime = com_frameTime;
 +	}
 +
 +#if 0
 +	if (msec) {
 +		Com_Printf ("%i ", msec);
 +	}
 +#endif
 +
 +	val = (float)msec / frame_msec;
 +	if ( val < 0 ) {
 +		val = 0;
 +	}
 +	if ( val > 1 ) {
 +		val = 1;
 +	}
 +
 +	return val;
 +}
 +
 +
 +
 +void IN_UpDown(void) {IN_KeyDown(&in_up);}
 +void IN_UpUp(void) {IN_KeyUp(&in_up);}
 +void IN_DownDown(void) {IN_KeyDown(&in_down);}
 +void IN_DownUp(void) {IN_KeyUp(&in_down);}
 +void IN_LeftDown(void) {IN_KeyDown(&in_left);}
 +void IN_LeftUp(void) {IN_KeyUp(&in_left);}
 +void IN_RightDown(void) {IN_KeyDown(&in_right);}
 +void IN_RightUp(void) {IN_KeyUp(&in_right);}
 +void IN_ForwardDown(void) {IN_KeyDown(&in_forward);}
 +void IN_ForwardUp(void) {IN_KeyUp(&in_forward);}
 +void IN_BackDown(void) {IN_KeyDown(&in_back);}
 +void IN_BackUp(void) {IN_KeyUp(&in_back);}
 +void IN_LookupDown(void) {IN_KeyDown(&in_lookup);}
 +void IN_LookupUp(void) {IN_KeyUp(&in_lookup);}
 +void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);}
 +void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);}
 +void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);}
 +void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);}
 +void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);}
 +void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);}
 +
 +void IN_SpeedDown(void) {IN_KeyDown(&in_speed);}
 +void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
 +void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
 +void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
 +
 +void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);}
 +void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);}
 +void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);}
 +void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);}
 +void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);}
 +void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);}
 +void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);}
 +void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);}
 +void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);}
 +void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);}
 +void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);}
 +void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);}
 +void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);}
 +void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);}
 +void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);}
 +void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);}
 +void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);}
 +void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);}
 +void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);}
 +void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);}
 +void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);}
 +void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);}
 +void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);}
 +void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);}
 +void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);}
 +void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);}
 +void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);}
 +void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);}
 +void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);}
 +void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);}
 +void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);}
 +void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);}
 +
 +void IN_ButtonDown (void) {
 +	IN_KeyDown(&in_buttons[1]);}
 +void IN_ButtonUp (void) {
 +	IN_KeyUp(&in_buttons[1]);}
 +
 +void IN_CenterView (void) {
 +	cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]);
 +}
 +
 +
 +//==========================================================================
 +
 +cvar_t	*cl_upspeed;
 +cvar_t	*cl_forwardspeed;
 +cvar_t	*cl_sidespeed;
 +
 +cvar_t	*cl_yawspeed;
 +cvar_t	*cl_pitchspeed;
 +
 +cvar_t	*cl_run;
 +
 +cvar_t	*cl_anglespeedkey;
 +
 +
 +/*
 +================
 +CL_AdjustAngles
 +
 +Moves the local angle positions
 +================
 +*/
 +void CL_AdjustAngles( void ) {
 +	float	speed;
 +	
 +	if ( in_speed.active ) {
 +		speed = 0.001 * cls.frametime * cl_anglespeedkey->value;
 +	} else {
 +		speed = 0.001 * cls.frametime;
 +	}
 +
 +	if ( !in_strafe.active ) {
 +		cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
 +		cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
 +	}
 +
 +	cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup);
 +	cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown);
 +}
 +
 +/*
 +================
 +CL_KeyMove
 +
 +Sets the usercmd_t based on key states
 +================
 +*/
 +void CL_KeyMove( usercmd_t *cmd ) {
 +	int		movespeed;
 +	int		forward, side, up;
 +
 +	//
 +	// adjust for speed key / running
 +	// the walking flag is to keep animations consistant
 +	// even during acceleration and develeration
 +	//
 +	if ( in_speed.active ^ cl_run->integer ) {
 +		movespeed = 127;
 +		cmd->buttons &= ~BUTTON_WALKING;
 +	} else {
 +		cmd->buttons |= BUTTON_WALKING;
 +		movespeed = 64;
 +	}
 +
 +	forward = 0;
 +	side = 0;
 +	up = 0;
 +	if ( in_strafe.active ) {
 +		side += movespeed * CL_KeyState (&in_right);
 +		side -= movespeed * CL_KeyState (&in_left);
 +	}
 +
 +	side += movespeed * CL_KeyState (&in_moveright);
 +	side -= movespeed * CL_KeyState (&in_moveleft);
 +
 +
 +	up += movespeed * CL_KeyState (&in_up);
 +	up -= movespeed * CL_KeyState (&in_down);
 +
 +	forward += movespeed * CL_KeyState (&in_forward);
 +	forward -= movespeed * CL_KeyState (&in_back);
 +
 +	cmd->forwardmove = ClampChar( forward );
 +	cmd->rightmove = ClampChar( side );
 +	cmd->upmove = ClampChar( up );
 +}
 +
 +/*
 +=================
 +CL_MouseEvent
 +=================
 +*/
 +void CL_MouseEvent( int dx, int dy, int time ) {
 +	if ( cls.keyCatchers & KEYCATCH_UI ) {
 +		VM_Call( uivm, UI_MOUSE_EVENT, dx, dy );
 +	} else if (cls.keyCatchers & KEYCATCH_CGAME) {
 +		VM_Call (cgvm, CG_MOUSE_EVENT, dx, dy);
 +	} else {
 +		cl.mouseDx[cl.mouseIndex] += dx;
 +		cl.mouseDy[cl.mouseIndex] += dy;
 +	}
 +}
 +
 +/*
 +=================
 +CL_JoystickEvent
 +
 +Joystick values stay set until changed
 +=================
 +*/
 +void CL_JoystickEvent( int axis, int value, int time ) {
 +	if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) {
 +		Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis );
 +	}
 +	cl.joystickAxis[axis] = value;
 +}
 +
 +/*
 +=================
 +CL_JoystickMove
 +=================
 +*/
 +void CL_JoystickMove( usercmd_t *cmd ) {
 +	int		movespeed;
 +	float	anglespeed;
 +
 +	if ( in_speed.active ^ cl_run->integer ) {
 +		movespeed = 2;
 +	} else {
 +		movespeed = 1;
 +		cmd->buttons |= BUTTON_WALKING;
 +	}
 +
 +	if ( in_speed.active ) {
 +		anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value;
 +	} else {
 +		anglespeed = 0.001 * cls.frametime;
 +	}
 +
 +	if ( !in_strafe.active ) {
 +		cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE];
 +	} else {
 +		cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] );
 +	}
 +
 +	if ( in_mlooking ) {
 +		cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD];
 +	} else {
 +		cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] );
 +	}
 +
 +	cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] );
 +}
 +
 +/*
 +=================
 +CL_MouseMove
 +=================
 +*/
 +void CL_MouseMove( usercmd_t *cmd ) {
 +	float	mx, my;
 +	float	accelSensitivity;
 +	float	rate;
 +
 +	// allow mouse smoothing
 +	if ( m_filter->integer ) {
 +		mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5;
 +		my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5;
 +	} else {
 +		mx = cl.mouseDx[cl.mouseIndex];
 +		my = cl.mouseDy[cl.mouseIndex];
 +	}
 +	cl.mouseIndex ^= 1;
 +	cl.mouseDx[cl.mouseIndex] = 0;
 +	cl.mouseDy[cl.mouseIndex] = 0;
 +
 +	rate = sqrt( mx * mx + my * my ) / (float)frame_msec;
 +	accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value;
 +
 +	// scale by FOV
 +	accelSensitivity *= cl.cgameSensitivity;
 +
 +	if ( rate && cl_showMouseRate->integer ) {
 +		Com_Printf( "%f : %f\n", rate, accelSensitivity );
 +	}
 +
 +	mx *= accelSensitivity;
 +	my *= accelSensitivity;
 +
 +	if (!mx && !my) {
 +		return;
 +	}
 +
 +	// add mouse X/Y movement to cmd
 +	if ( in_strafe.active ) {
 +		cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx );
 +	} else {
 +		cl.viewangles[YAW] -= m_yaw->value * mx;
 +	}
 +
 +	if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) {
 +		cl.viewangles[PITCH] += m_pitch->value * my;
 +	} else {
 +		cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my );
 +	}
 +}
 +
 +
 +/*
 +==============
 +CL_CmdButtons
 +==============
 +*/
 +void CL_CmdButtons( usercmd_t *cmd ) {
 +	int		i;
 +
 +	//
 +	// figure button bits
 +	// send a button bit even if the key was pressed and released in
 +	// less than a frame
 +	//	
 +	for (i = 0 ; i < 15 ; i++) {
 +		if ( in_buttons[i].active || in_buttons[i].wasPressed ) {
 +			cmd->buttons |= 1 << i;
 +		}
 +		in_buttons[i].wasPressed = qfalse;
 +	}
 +
 +	if ( cls.keyCatchers ) {
 +		cmd->buttons |= BUTTON_TALK;
 +	}
 +
 +	// allow the game to know if any key at all is
 +	// currently pressed, even if it isn't bound to anything
 +	if ( anykeydown && !cls.keyCatchers ) {
 +		cmd->buttons |= BUTTON_ANY;
 +	}
 +}
 +
 +
 +/*
 +==============
 +CL_FinishMove
 +==============
 +*/
 +void CL_FinishMove( usercmd_t *cmd ) {
 +	int		i;
 +
 +	// copy the state that the cgame is currently sending
 +	cmd->weapon = cl.cgameUserCmdValue;
 +
 +	// send the current server time so the amount of movement
 +	// can be determined without allowing cheating
 +	cmd->serverTime = cl.serverTime;
 +
 +	for (i=0 ; i<3 ; i++) {
 +		cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
 +	}
 +}
 +
 +
 +/*
 +=================
 +CL_CreateCmd
 +=================
 +*/
 +usercmd_t CL_CreateCmd( void ) {
 +	usercmd_t	cmd;
 +	vec3_t		oldAngles;
 +
 +	VectorCopy( cl.viewangles, oldAngles );
 +
 +	// keyboard angle adjustment
 +	CL_AdjustAngles ();
 +	
 +	Com_Memset( &cmd, 0, sizeof( cmd ) );
 +
 +	CL_CmdButtons( &cmd );
 +
 +	// get basic movement from keyboard
 +	CL_KeyMove( &cmd );
 +
 +	// get basic movement from mouse
 +	CL_MouseMove( &cmd );
 +
 +	// get basic movement from joystick
 +	CL_JoystickMove( &cmd );
 +
 +	// check to make sure the angles haven't wrapped
 +	if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
 +		cl.viewangles[PITCH] = oldAngles[PITCH] + 90;
 +	} else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) {
 +		cl.viewangles[PITCH] = oldAngles[PITCH] - 90;
 +	} 
 +
 +	// store out the final values
 +	CL_FinishMove( &cmd );
 +
 +	// draw debug graphs of turning for mouse testing
 +	if ( cl_debugMove->integer ) {
 +		if ( cl_debugMove->integer == 1 ) {
 +			SCR_DebugGraph( abs(cl.viewangles[YAW] - oldAngles[YAW]), 0 );
 +		}
 +		if ( cl_debugMove->integer == 2 ) {
 +			SCR_DebugGraph( abs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 );
 +		}
 +	}
 +
 +	return cmd;
 +}
 +
 +
 +/*
 +=================
 +CL_CreateNewCommands
 +
 +Create a new usercmd_t structure for this frame
 +=================
 +*/
 +void CL_CreateNewCommands( void ) {
 +	usercmd_t	*cmd;
 +	int			cmdNum;
 +
 +	// no need to create usercmds until we have a gamestate
 +	if ( cls.state < CA_PRIMED ) {
 +		return;
 +	}
 +
 +	frame_msec = com_frameTime - old_com_frameTime;
 +
 +	// if running less than 5fps, truncate the extra time to prevent
 +	// unexpected moves after a hitch
 +	if ( frame_msec > 200 ) {
 +		frame_msec = 200;
 +	}
 +	old_com_frameTime = com_frameTime;
 +
 +
 +	// generate a command for this frame
 +	cl.cmdNumber++;
 +	cmdNum = cl.cmdNumber & CMD_MASK;
 +	cl.cmds[cmdNum] = CL_CreateCmd ();
 +	cmd = &cl.cmds[cmdNum];
 +}
 +
 +/*
 +=================
 +CL_ReadyToSendPacket
 +
 +Returns qfalse if we are over the maxpackets limit
 +and should choke back the bandwidth a bit by not sending
 +a packet this frame.  All the commands will still get
 +delivered in the next packet, but saving a header and
 +getting more delta compression will reduce total bandwidth.
 +=================
 +*/
 +qboolean CL_ReadyToSendPacket( void ) {
 +	int		oldPacketNum;
 +	int		delta;
 +
 +	// don't send anything if playing back a demo
 +	if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
 +		return qfalse;
 +	}
 +
 +	// If we are downloading, we send no less than 50ms between packets
 +	if ( *clc.downloadTempName &&
 +		cls.realtime - clc.lastPacketSentTime < 50 ) {
 +		return qfalse;
 +	}
 +
 +	// if we don't have a valid gamestate yet, only send
 +	// one packet a second
 +	if ( cls.state != CA_ACTIVE && 
 +		cls.state != CA_PRIMED && 
 +		!*clc.downloadTempName &&
 +		cls.realtime - clc.lastPacketSentTime < 1000 ) {
 +		return qfalse;
 +	}
 +
 +	// send every frame for loopbacks
 +	if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) {
 +		return qtrue;
 +	}
 +
 +	// send every frame for LAN
 +	if ( Sys_IsLANAddress( clc.netchan.remoteAddress ) ) {
 +		return qtrue;
 +	}
 +
 +	// check for exceeding cl_maxpackets
 +	if ( cl_maxpackets->integer < 15 ) {
 +		Cvar_Set( "cl_maxpackets", "15" );
 +	} else if ( cl_maxpackets->integer > 125 ) {
 +		Cvar_Set( "cl_maxpackets", "125" );
 +	}
 +	oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK;
 +	delta = cls.realtime -  cl.outPackets[ oldPacketNum ].p_realtime;
 +	if ( delta < 1000 / cl_maxpackets->integer ) {
 +		// the accumulated commands will go out in the next packet
 +		return qfalse;
 +	}
 +
 +	return qtrue;
 +}
 +
 +/*
 +===================
 +CL_WritePacket
 +
 +Create and send the command packet to the server
 +Including both the reliable commands and the usercmds
 +
 +During normal gameplay, a client packet will contain something like:
 +
 +4	sequence number
 +2	qport
 +4	serverid
 +4	acknowledged sequence number
 +4	clc.serverCommandSequence
 +<optional reliable commands>
 +1	clc_move or clc_moveNoDelta
 +1	command count
 +<count * usercmds>
 +
 +===================
 +*/
 +void CL_WritePacket( void ) {
 +	msg_t		buf;
 +	byte		data[MAX_MSGLEN];
 +	int			i, j;
 +	usercmd_t	*cmd, *oldcmd;
 +	usercmd_t	nullcmd;
 +	int			packetNum;
 +	int			oldPacketNum;
 +	int			count, key;
 +
 +	// don't send anything if playing back a demo
 +	if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
 +		return;
 +	}
 +
 +	Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
 +	oldcmd = &nullcmd;
 +
 +	MSG_Init( &buf, data, sizeof(data) );
 +
 +	MSG_Bitstream( &buf );
 +	// write the current serverId so the server
 +	// can tell if this is from the current gameState
 +	MSG_WriteLong( &buf, cl.serverId );
 +
 +	// write the last message we received, which can
 +	// be used for delta compression, and is also used
 +	// to tell if we dropped a gamestate
 +	MSG_WriteLong( &buf, clc.serverMessageSequence );
 +
 +	// write the last reliable message we received
 +	MSG_WriteLong( &buf, clc.serverCommandSequence );
 +
 +	// write any unacknowledged clientCommands
 +	for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) {
 +		MSG_WriteByte( &buf, clc_clientCommand );
 +		MSG_WriteLong( &buf, i );
 +		MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
 +	}
 +
 +	// we want to send all the usercmds that were generated in the last
 +	// few packet, so even if a couple packets are dropped in a row,
 +	// all the cmds will make it to the server
 +	if ( cl_packetdup->integer < 0 ) {
 +		Cvar_Set( "cl_packetdup", "0" );
 +	} else if ( cl_packetdup->integer > 5 ) {
 +		Cvar_Set( "cl_packetdup", "5" );
 +	}
 +	oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK;
 +	count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber;
 +	if ( count > MAX_PACKET_USERCMDS ) {
 +		count = MAX_PACKET_USERCMDS;
 +		Com_Printf("MAX_PACKET_USERCMDS\n");
 +	}
 +	if ( count >= 1 ) {
 +		if ( cl_showSend->integer ) {
 +			Com_Printf( "(%i)", count );
 +		}
 +
 +		// begin a client move command
 +		if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting
 +			|| clc.serverMessageSequence != cl.snap.messageNum ) {
 +			MSG_WriteByte (&buf, clc_moveNoDelta);
 +		} else {
 +			MSG_WriteByte (&buf, clc_move);
 +		}
 +
 +		// write the command count
 +		MSG_WriteByte( &buf, count );
 +
 +		// use the checksum feed in the key
 +		key = clc.checksumFeed;
 +		// also use the message acknowledge
 +		key ^= clc.serverMessageSequence;
 +		// also use the last acknowledged server command in the key
 +		key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32);
 +
 +		// write all the commands, including the predicted command
 +		for ( i = 0 ; i < count ; i++ ) {
 +			j = (cl.cmdNumber - count + i + 1) & CMD_MASK;
 +			cmd = &cl.cmds[j];
 +			MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd);
 +			oldcmd = cmd;
 +		}
 +	}
 +
 +	//
 +	// deliver the message
 +	//
 +	packetNum = clc.netchan.outgoingSequence & PACKET_MASK;
 +	cl.outPackets[ packetNum ].p_realtime = cls.realtime;
 +	cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime;
 +	cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber;
 +	clc.lastPacketSentTime = cls.realtime;
 +
 +	if ( cl_showSend->integer ) {
 +		Com_Printf( "%i ", buf.cursize );
 +	}
 +
 +	CL_Netchan_Transmit (&clc.netchan, &buf);	
 +
 +	// clients never really should have messages large enough
 +	// to fragment, but in case they do, fire them all off
 +	// at once
 +	// TTimo: this causes a packet burst, which is bad karma for winsock
 +	// added a WARNING message, we'll see if there are legit situations where this happens
 +	while ( clc.netchan.unsentFragments ) {
 +		Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" );
 +		CL_Netchan_TransmitNextFragment( &clc.netchan );
 +	}
 +}
 +
 +/*
 +=================
 +CL_SendCmd
 +
 +Called every frame to builds and sends a command packet to the server.
 +=================
 +*/
 +void CL_SendCmd( void ) {
 +	// don't send any message if not connected
 +	if ( cls.state < CA_CONNECTED ) {
 +		return;
 +	}
 +
 +	// don't send commands if paused
 +	if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) {
 +		return;
 +	}
 +
 +	// we create commands even if a demo is playing,
 +	CL_CreateNewCommands();
 +
 +	// don't send a packet if the last packet was sent too recently
 +	if ( !CL_ReadyToSendPacket() ) {
 +		if ( cl_showSend->integer ) {
 +			Com_Printf( ". " );
 +		}
 +		return;
 +	}
 +
 +	CL_WritePacket();
 +}
 +
 +/*
 +============
 +CL_InitInput
 +============
 +*/
 +void CL_InitInput( void ) {
 +	Cmd_AddCommand ("centerview",IN_CenterView);
 +
 +	Cmd_AddCommand ("+moveup",IN_UpDown);
 +	Cmd_AddCommand ("-moveup",IN_UpUp);
 +	Cmd_AddCommand ("+movedown",IN_DownDown);
 +	Cmd_AddCommand ("-movedown",IN_DownUp);
 +	Cmd_AddCommand ("+left",IN_LeftDown);
 +	Cmd_AddCommand ("-left",IN_LeftUp);
 +	Cmd_AddCommand ("+right",IN_RightDown);
 +	Cmd_AddCommand ("-right",IN_RightUp);
 +	Cmd_AddCommand ("+forward",IN_ForwardDown);
 +	Cmd_AddCommand ("-forward",IN_ForwardUp);
 +	Cmd_AddCommand ("+back",IN_BackDown);
 +	Cmd_AddCommand ("-back",IN_BackUp);
 +	Cmd_AddCommand ("+lookup", IN_LookupDown);
 +	Cmd_AddCommand ("-lookup", IN_LookupUp);
 +	Cmd_AddCommand ("+lookdown", IN_LookdownDown);
 +	Cmd_AddCommand ("-lookdown", IN_LookdownUp);
 +	Cmd_AddCommand ("+strafe", IN_StrafeDown);
 +	Cmd_AddCommand ("-strafe", IN_StrafeUp);
 +	Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
 +	Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
 +	Cmd_AddCommand ("+moveright", IN_MoverightDown);
 +	Cmd_AddCommand ("-moveright", IN_MoverightUp);
 +	Cmd_AddCommand ("+speed", IN_SpeedDown);
 +	Cmd_AddCommand ("-speed", IN_SpeedUp);
 +	Cmd_AddCommand ("+attack", IN_Button0Down);
 +	Cmd_AddCommand ("-attack", IN_Button0Up);
 +	Cmd_AddCommand ("+button0", IN_Button0Down);
 +	Cmd_AddCommand ("-button0", IN_Button0Up);
 +	Cmd_AddCommand ("+button1", IN_Button1Down);
 +	Cmd_AddCommand ("-button1", IN_Button1Up);
 +	Cmd_AddCommand ("+button2", IN_Button2Down);
 +	Cmd_AddCommand ("-button2", IN_Button2Up);
 +	Cmd_AddCommand ("+button3", IN_Button3Down);
 +	Cmd_AddCommand ("-button3", IN_Button3Up);
 +	Cmd_AddCommand ("+button4", IN_Button4Down);
 +	Cmd_AddCommand ("-button4", IN_Button4Up);
 +	Cmd_AddCommand ("+button5", IN_Button5Down);
 +	Cmd_AddCommand ("-button5", IN_Button5Up);
 +	Cmd_AddCommand ("+button6", IN_Button6Down);
 +	Cmd_AddCommand ("-button6", IN_Button6Up);
 +	Cmd_AddCommand ("+button7", IN_Button7Down);
 +	Cmd_AddCommand ("-button7", IN_Button7Up);
 +	Cmd_AddCommand ("+button8", IN_Button8Down);
 +	Cmd_AddCommand ("-button8", IN_Button8Up);
 +	Cmd_AddCommand ("+button9", IN_Button9Down);
 +	Cmd_AddCommand ("-button9", IN_Button9Up);
 +	Cmd_AddCommand ("+button10", IN_Button10Down);
 +	Cmd_AddCommand ("-button10", IN_Button10Up);
 +	Cmd_AddCommand ("+button11", IN_Button11Down);
 +	Cmd_AddCommand ("-button11", IN_Button11Up);
 +	Cmd_AddCommand ("+button12", IN_Button12Down);
 +	Cmd_AddCommand ("-button12", IN_Button12Up);
 +	Cmd_AddCommand ("+button13", IN_Button13Down);
 +	Cmd_AddCommand ("-button13", IN_Button13Up);
 +	Cmd_AddCommand ("+button14", IN_Button14Down);
 +	Cmd_AddCommand ("-button14", IN_Button14Up);
 +	Cmd_AddCommand ("+mlook", IN_MLookDown);
 +	Cmd_AddCommand ("-mlook", IN_MLookUp);
 +
 +	cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
 +	cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0);
 +}
 diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c new file mode 100755 index 0000000..7dcd7d8 --- /dev/null +++ b/code/client/cl_keys.c @@ -0,0 +1,1252 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +#include "client.h"
 +
 +/*
 +
 +key up events are sent even if in console mode
 +
 +*/
 +
 +field_t	historyEditLines[COMMAND_HISTORY];
 +
 +int			nextHistoryLine;		// the last line in the history buffer, not masked
 +int			historyLine;	// the line being displayed from history buffer
 +							// will be <= nextHistoryLine
 +
 +field_t		g_consoleField;
 +field_t		chatField;
 +qboolean	chat_team;
 +
 +int			chat_playerNum;
 +
 +
 +qboolean	key_overstrikeMode;
 +
 +qboolean	anykeydown;
 +qkey_t		keys[MAX_KEYS];
 +
 +
 +typedef struct {
 +	char	*name;
 +	int		keynum;
 +} keyname_t;
 +
 +
 +// names not in this list can either be lowercase ascii, or '0xnn' hex sequences
 +keyname_t keynames[] =
 +{
 +	{"TAB", K_TAB},
 +	{"ENTER", K_ENTER},
 +	{"ESCAPE", K_ESCAPE},
 +	{"SPACE", K_SPACE},
 +	{"BACKSPACE", K_BACKSPACE},
 +	{"UPARROW", K_UPARROW},
 +	{"DOWNARROW", K_DOWNARROW},
 +	{"LEFTARROW", K_LEFTARROW},
 +	{"RIGHTARROW", K_RIGHTARROW},
 +
 +	{"ALT", K_ALT},
 +	{"CTRL", K_CTRL},
 +	{"SHIFT", K_SHIFT},
 +
 +	{"COMMAND", K_COMMAND},
 +
 +	{"CAPSLOCK", K_CAPSLOCK},
 +
 +	
 +	{"F1", K_F1},
 +	{"F2", K_F2},
 +	{"F3", K_F3},
 +	{"F4", K_F4},
 +	{"F5", K_F5},
 +	{"F6", K_F6},
 +	{"F7", K_F7},
 +	{"F8", K_F8},
 +	{"F9", K_F9},
 +	{"F10", K_F10},
 +	{"F11", K_F11},
 +	{"F12", K_F12},
 +
 +	{"INS", K_INS},
 +	{"DEL", K_DEL},
 +	{"PGDN", K_PGDN},
 +	{"PGUP", K_PGUP},
 +	{"HOME", K_HOME},
 +	{"END", K_END},
 +
 +	{"MOUSE1", K_MOUSE1},
 +	{"MOUSE2", K_MOUSE2},
 +	{"MOUSE3", K_MOUSE3},
 +	{"MOUSE4", K_MOUSE4},
 +	{"MOUSE5", K_MOUSE5},
 +
 +	{"MWHEELUP",	K_MWHEELUP },
 +	{"MWHEELDOWN",	K_MWHEELDOWN },
 +
 +	{"JOY1", K_JOY1},
 +	{"JOY2", K_JOY2},
 +	{"JOY3", K_JOY3},
 +	{"JOY4", K_JOY4},
 +	{"JOY5", K_JOY5},
 +	{"JOY6", K_JOY6},
 +	{"JOY7", K_JOY7},
 +	{"JOY8", K_JOY8},
 +	{"JOY9", K_JOY9},
 +	{"JOY10", K_JOY10},
 +	{"JOY11", K_JOY11},
 +	{"JOY12", K_JOY12},
 +	{"JOY13", K_JOY13},
 +	{"JOY14", K_JOY14},
 +	{"JOY15", K_JOY15},
 +	{"JOY16", K_JOY16},
 +	{"JOY17", K_JOY17},
 +	{"JOY18", K_JOY18},
 +	{"JOY19", K_JOY19},
 +	{"JOY20", K_JOY20},
 +	{"JOY21", K_JOY21},
 +	{"JOY22", K_JOY22},
 +	{"JOY23", K_JOY23},
 +	{"JOY24", K_JOY24},
 +	{"JOY25", K_JOY25},
 +	{"JOY26", K_JOY26},
 +	{"JOY27", K_JOY27},
 +	{"JOY28", K_JOY28},
 +	{"JOY29", K_JOY29},
 +	{"JOY30", K_JOY30},
 +	{"JOY31", K_JOY31},
 +	{"JOY32", K_JOY32},
 +
 +	{"AUX1", K_AUX1},
 +	{"AUX2", K_AUX2},
 +	{"AUX3", K_AUX3},
 +	{"AUX4", K_AUX4},
 +	{"AUX5", K_AUX5},
 +	{"AUX6", K_AUX6},
 +	{"AUX7", K_AUX7},
 +	{"AUX8", K_AUX8},
 +	{"AUX9", K_AUX9},
 +	{"AUX10", K_AUX10},
 +	{"AUX11", K_AUX11},
 +	{"AUX12", K_AUX12},
 +	{"AUX13", K_AUX13},
 +	{"AUX14", K_AUX14},
 +	{"AUX15", K_AUX15},
 +	{"AUX16", K_AUX16},
 +
 +	{"KP_HOME",			K_KP_HOME },
 +	{"KP_UPARROW",		K_KP_UPARROW },
 +	{"KP_PGUP",			K_KP_PGUP },
 +	{"KP_LEFTARROW",	K_KP_LEFTARROW },
 +	{"KP_5",			K_KP_5 },
 +	{"KP_RIGHTARROW",	K_KP_RIGHTARROW },
 +	{"KP_END",			K_KP_END },
 +	{"KP_DOWNARROW",	K_KP_DOWNARROW },
 +	{"KP_PGDN",			K_KP_PGDN },
 +	{"KP_ENTER",		K_KP_ENTER },
 +	{"KP_INS",			K_KP_INS },
 +	{"KP_DEL",			K_KP_DEL },
 +	{"KP_SLASH",		K_KP_SLASH },
 +	{"KP_MINUS",		K_KP_MINUS },
 +	{"KP_PLUS",			K_KP_PLUS },
 +	{"KP_NUMLOCK",		K_KP_NUMLOCK },
 +	{"KP_STAR",			K_KP_STAR },
 +	{"KP_EQUALS",		K_KP_EQUALS },
 +
 +	{"PAUSE", K_PAUSE},
 +	
 +	{"SEMICOLON", ';'},	// because a raw semicolon seperates commands
 +
 +	{NULL,0}
 +};
 +
 +/*
 +=============================================================================
 +
 +EDIT FIELDS
 +
 +=============================================================================
 +*/
 +
 +
 +/*
 +===================
 +Field_Draw
 +
 +Handles horizontal scrolling and cursor blinking
 +x, y, amd width are in pixels
 +===================
 +*/
 +void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor ) {
 +	int		len;
 +	int		drawLen;
 +	int		prestep;
 +	int		cursorChar;
 +	char	str[MAX_STRING_CHARS];
 +	int		i;
 +
 +	drawLen = edit->widthInChars;
 +	len = strlen( edit->buffer ) + 1;
 +
 +	// guarantee that cursor will be visible
 +	if ( len <= drawLen ) {
 +		prestep = 0;
 +	} else {
 +		if ( edit->scroll + drawLen > len ) {
 +			edit->scroll = len - drawLen;
 +			if ( edit->scroll < 0 ) {
 +				edit->scroll = 0;
 +			}
 +		}
 +		prestep = edit->scroll;
 +
 +/*
 +		if ( edit->cursor < len - drawLen ) {
 +			prestep = edit->cursor;	// cursor at start
 +		} else {
 +			prestep = len - drawLen;
 +		}
 +*/
 +	}
 +
 +	if ( prestep + drawLen > len ) {
 +		drawLen = len - prestep;
 +	}
 +
 +	// extract <drawLen> characters from the field at <prestep>
 +	if ( drawLen >= MAX_STRING_CHARS ) {
 +		Com_Error( ERR_DROP, "drawLen >= MAX_STRING_CHARS" );
 +	}
 +
 +	Com_Memcpy( str, edit->buffer + prestep, drawLen );
 +	str[ drawLen ] = 0;
 +
 +	// draw it
 +	if ( size == SMALLCHAR_WIDTH ) {
 +		float	color[4];
 +
 +		color[0] = color[1] = color[2] = color[3] = 1.0;
 +		SCR_DrawSmallStringExt( x, y, str, color, qfalse );
 +	} else {
 +		// draw big string with drop shadow
 +		SCR_DrawBigString( x, y, str, 1.0 );
 +	}
 +
 +	// draw the cursor
 +	if ( !showCursor ) {
 +		return;
 +	}
 +
 +	if ( (int)( cls.realtime >> 8 ) & 1 ) {
 +		return;		// off blink
 +	}
 +
 +	if ( key_overstrikeMode ) {
 +		cursorChar = 11;
 +	} else {
 +		cursorChar = 10;
 +	}
 +
 +	i = drawLen - ( Q_PrintStrlen( str ) + 1 );
 +
 +	if ( size == SMALLCHAR_WIDTH ) {
 +		SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar );
 +	} else {
 +		str[0] = cursorChar;
 +		str[1] = 0;
 +		SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0 );
 +
 +	}
 +}
 +
 +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor ) 
 +{
 +	Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor );
 +}
 +
 +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor ) 
 +{
 +	Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor );
 +}
 +
 +/*
 +================
 +Field_Paste
 +================
 +*/
 +void Field_Paste( field_t *edit ) {
 +	char	*cbd;
 +	int		pasteLen, i;
 +
 +	cbd = Sys_GetClipboardData();
 +
 +	if ( !cbd ) {
 +		return;
 +	}
 +
 +	// send as if typed, so insert / overstrike works properly
 +	pasteLen = strlen( cbd );
 +	for ( i = 0 ; i < pasteLen ; i++ ) {
 +		Field_CharEvent( edit, cbd[i] );
 +	}
 +
 +	Z_Free( cbd );
 +}
 +
 +/*
 +=================
 +Field_KeyDownEvent
 +
 +Performs the basic line editing functions for the console,
 +in-game talk, and menu fields
 +
 +Key events are used for non-printable characters, others are gotten from char events.
 +=================
 +*/
 +void Field_KeyDownEvent( field_t *edit, int key ) {
 +	int		len;
 +
 +	// shift-insert is paste
 +	if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keys[K_SHIFT].down ) {
 +		Field_Paste( edit );
 +		return;
 +	}
 +
 +	len = strlen( edit->buffer );
 +
 +	if ( key == K_DEL ) {
 +		if ( edit->cursor < len ) {
 +			memmove( edit->buffer + edit->cursor, 
 +				edit->buffer + edit->cursor + 1, len - edit->cursor );
 +		}
 +		return;
 +	}
 +
 +	if ( key == K_RIGHTARROW ) 
 +	{
 +		if ( edit->cursor < len ) {
 +			edit->cursor++;
 +		}
 +
 +		if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
 +		{
 +			edit->scroll++;
 +		}
 +		return;
 +	}
 +
 +	if ( key == K_LEFTARROW ) 
 +	{
 +		if ( edit->cursor > 0 ) {
 +			edit->cursor--;
 +		}
 +		if ( edit->cursor < edit->scroll )
 +		{
 +			edit->scroll--;
 +		}
 +		return;
 +	}
 +
 +	if ( key == K_HOME || ( tolower(key) == 'a' && keys[K_CTRL].down ) ) {
 +		edit->cursor = 0;
 +		return;
 +	}
 +
 +	if ( key == K_END || ( tolower(key) == 'e' && keys[K_CTRL].down ) ) {
 +		edit->cursor = len;
 +		return;
 +	}
 +
 +	if ( key == K_INS ) {
 +		key_overstrikeMode = !key_overstrikeMode;
 +		return;
 +	}
 +}
 +
 +/*
 +==================
 +Field_CharEvent
 +==================
 +*/
 +void Field_CharEvent( field_t *edit, int ch ) {
 +	int		len;
 +
 +	if ( ch == 'v' - 'a' + 1 ) {	// ctrl-v is paste
 +		Field_Paste( edit );
 +		return;
 +	}
 +
 +	if ( ch == 'c' - 'a' + 1 ) {	// ctrl-c clears the field
 +		Field_Clear( edit );
 +		return;
 +	}
 +
 +	len = strlen( edit->buffer );
 +
 +	if ( ch == 'h' - 'a' + 1 )	{	// ctrl-h is backspace
 +		if ( edit->cursor > 0 ) {
 +			memmove( edit->buffer + edit->cursor - 1, 
 +				edit->buffer + edit->cursor, len + 1 - edit->cursor );
 +			edit->cursor--;
 +			if ( edit->cursor < edit->scroll )
 +			{
 +				edit->scroll--;
 +			}
 +		}
 +		return;
 +	}
 +
 +	if ( ch == 'a' - 'a' + 1 ) {	// ctrl-a is home
 +		edit->cursor = 0;
 +		edit->scroll = 0;
 +		return;
 +	}
 +
 +	if ( ch == 'e' - 'a' + 1 ) {	// ctrl-e is end
 +		edit->cursor = len;
 +		edit->scroll = edit->cursor - edit->widthInChars;
 +		return;
 +	}
 +
 +	//
 +	// ignore any other non printable chars
 +	//
 +	if ( ch < 32 ) {
 +		return;
 +	}
 +
 +	if ( key_overstrikeMode ) {	
 +		if ( edit->cursor == MAX_EDIT_LINE - 1 )
 +			return;
 +		edit->buffer[edit->cursor] = ch;
 +		edit->cursor++;
 +	} else {	// insert mode
 +		if ( len == MAX_EDIT_LINE - 1 ) {
 +			return; // all full
 +		}
 +		memmove( edit->buffer + edit->cursor + 1, 
 +			edit->buffer + edit->cursor, len + 1 - edit->cursor );
 +		edit->buffer[edit->cursor] = ch;
 +		edit->cursor++;
 +	}
 +
 +
 +	if ( edit->cursor >= edit->widthInChars ) {
 +		edit->scroll++;
 +	}
 +
 +	if ( edit->cursor == len + 1) {
 +		edit->buffer[edit->cursor] = 0;
 +	}
 +}
 +
 +/*
 +=============================================================================
 +
 +CONSOLE LINE EDITING
 +
 +==============================================================================
 +*/
 +
 +/*
 +====================
 +Console_Key
 +
 +Handles history and console scrollback
 +====================
 +*/
 +void Console_Key (int key) {
 +	// ctrl-L clears screen
 +	if ( key == 'l' && keys[K_CTRL].down ) {
 +		Cbuf_AddText ("clear\n");
 +		return;
 +	}
 +
 +	// enter finishes the line
 +	if ( key == K_ENTER || key == K_KP_ENTER ) {
 +		// if not in the game explicitly prepent a slash if needed
 +		if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\' 
 +			&& g_consoleField.buffer[0] != '/' ) {
 +			char	temp[MAX_STRING_CHARS];
 +
 +			Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) );
 +			Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp );
 +			g_consoleField.cursor++;
 +		}
 +
 +		Com_Printf ( "]%s\n", g_consoleField.buffer );
 +
 +		// leading slash is an explicit command
 +		if ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) {
 +			Cbuf_AddText( g_consoleField.buffer+1 );	// valid command
 +			Cbuf_AddText ("\n");
 +		} else {
 +			// other text will be chat messages
 +			if ( !g_consoleField.buffer[0] ) {
 +				return;	// empty lines just scroll the console without adding to history
 +			} else {
 +				Cbuf_AddText ("cmd say ");
 +				Cbuf_AddText( g_consoleField.buffer );
 +				Cbuf_AddText ("\n");
 +			}
 +		}
 +
 +		// copy line to history buffer
 +		historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField;
 +		nextHistoryLine++;
 +		historyLine = nextHistoryLine;
 +
 +		Field_Clear( &g_consoleField );
 +
 +		g_consoleField.widthInChars = g_console_field_width;
 +
 +		if ( cls.state == CA_DISCONNECTED ) {
 +			SCR_UpdateScreen ();	// force an update, because the command
 +		}							// may take some time
 +		return;
 +	}
 +
 +	// command completion
 +
 +	if (key == K_TAB) {
 +		Field_CompleteCommand(&g_consoleField);
 +		return;
 +	}
 +
 +	// command history (ctrl-p ctrl-n for unix style)
 +
 +	if ( (key == K_MWHEELUP && keys[K_SHIFT].down) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
 +		 ( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) {
 +		if ( nextHistoryLine - historyLine < COMMAND_HISTORY 
 +			&& historyLine > 0 ) {
 +			historyLine--;
 +		}
 +		g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
 +		return;
 +	}
 +
 +	if ( (key == K_MWHEELDOWN && keys[K_SHIFT].down) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
 +		 ( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) {
 +		if (historyLine == nextHistoryLine)
 +			return;
 +		historyLine++;
 +		g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
 +		return;
 +	}
 +
 +	// console scrolling
 +	if ( key == K_PGUP ) {
 +		Con_PageUp();
 +		return;
 +	}
 +
 +	if ( key == K_PGDN) {
 +		Con_PageDown();
 +		return;
 +	}
 +
 +	if ( key == K_MWHEELUP) {	//----(SA)	added some mousewheel functionality to the console
 +		Con_PageUp();
 +		if(keys[K_CTRL].down) {	// hold <ctrl> to accelerate scrolling
 +			Con_PageUp();
 +			Con_PageUp();
 +		}
 +		return;
 +	}
 +
 +	if ( key == K_MWHEELDOWN) {	//----(SA)	added some mousewheel functionality to the console
 +		Con_PageDown();
 +		if(keys[K_CTRL].down) {	// hold <ctrl> to accelerate scrolling
 +			Con_PageDown();
 +			Con_PageDown();
 +		}
 +		return;
 +	}
 +
 +	// ctrl-home = top of console
 +	if ( key == K_HOME && keys[K_CTRL].down ) {
 +		Con_Top();
 +		return;
 +	}
 +
 +	// ctrl-end = bottom of console
 +	if ( key == K_END && keys[K_CTRL].down ) {
 +		Con_Bottom();
 +		return;
 +	}
 +
 +	// pass to the normal editline routine
 +	Field_KeyDownEvent( &g_consoleField, key );
 +}
 +
 +//============================================================================
 +
 +
 +/*
 +================
 +Message_Key
 +
 +In game talk message
 +================
 +*/
 +void Message_Key( int key ) {
 +
 +	char	buffer[MAX_STRING_CHARS];
 +
 +
 +	if (key == K_ESCAPE) {
 +		cls.keyCatchers &= ~KEYCATCH_MESSAGE;
 +		Field_Clear( &chatField );
 +		return;
 +	}
 +
 +	if ( key == K_ENTER || key == K_KP_ENTER )
 +	{
 +		if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) {
 +			if (chat_playerNum != -1 )
 +
 +				Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer );
 +
 +			else if (chat_team)
 +
 +				Com_sprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", chatField.buffer );
 +			else
 +				Com_sprintf( buffer, sizeof( buffer ), "say \"%s\"\n", chatField.buffer );
 +
 +
 +
 +			CL_AddReliableCommand( buffer );
 +		}
 +		cls.keyCatchers &= ~KEYCATCH_MESSAGE;
 +		Field_Clear( &chatField );
 +		return;
 +	}
 +
 +	Field_KeyDownEvent( &chatField, key );
 +}
 +
 +//============================================================================
 +
 +
 +qboolean Key_GetOverstrikeMode( void ) {
 +	return key_overstrikeMode;
 +}
 +
 +
 +void Key_SetOverstrikeMode( qboolean state ) {
 +	key_overstrikeMode = state;
 +}
 +
 +
 +/*
 +===================
 +Key_IsDown
 +===================
 +*/
 +qboolean Key_IsDown( int keynum ) {
 +	if ( keynum == -1 ) {
 +		return qfalse;
 +	}
 +
 +	return keys[keynum].down;
 +}
 +
 +
 +/*
 +===================
 +Key_StringToKeynum
 +
 +Returns a key number to be used to index keys[] by looking at
 +the given string.  Single ascii characters return themselves, while
 +the K_* names are matched up.
 +
 +0x11 will be interpreted as raw hex, which will allow new controlers
 +
 +to be configured even if they don't have defined names.
 +===================
 +*/
 +int Key_StringToKeynum( char *str ) {
 +	keyname_t	*kn;
 +	
 +	if ( !str || !str[0] ) {
 +		return -1;
 +	}
 +	if ( !str[1] ) {
 +		return str[0];
 +	}
 +
 +	// check for hex code
 +	if ( str[0] == '0' && str[1] == 'x' && strlen( str ) == 4) {
 +		int		n1, n2;
 +		
 +		n1 = str[2];
 +		if ( n1 >= '0' && n1 <= '9' ) {
 +			n1 -= '0';
 +		} else if ( n1 >= 'a' && n1 <= 'f' ) {
 +			n1 = n1 - 'a' + 10;
 +		} else {
 +			n1 = 0;
 +		}
 +
 +		n2 = str[3];
 +		if ( n2 >= '0' && n2 <= '9' ) {
 +			n2 -= '0';
 +		} else if ( n2 >= 'a' && n2 <= 'f' ) {
 +			n2 = n2 - 'a' + 10;
 +		} else {
 +			n2 = 0;
 +		}
 +
 +		return n1 * 16 + n2;
 +	}
 +
 +	// scan for a text match
 +	for ( kn=keynames ; kn->name ; kn++ ) {
 +		if ( !Q_stricmp( str,kn->name ) )
 +			return kn->keynum;
 +	}
 +
 +	return -1;
 +}
 +
 +/*
 +===================
 +Key_KeynumToString
 +
 +Returns a string (either a single ascii char, a K_* name, or a 0x11 hex string) for the
 +given keynum.
 +===================
 +*/
 +char *Key_KeynumToString( int keynum ) {
 +	keyname_t	*kn;	
 +	static	char	tinystr[5];
 +	int			i, j;
 +
 +	if ( keynum == -1 ) {
 +		return "<KEY NOT FOUND>";
 +	}
 +
 +	if ( keynum < 0 || keynum > 255 ) {
 +		return "<OUT OF RANGE>";
 +	}
 +
 +	// check for printable ascii (don't use quote)
 +	if ( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' ) {
 +		tinystr[0] = keynum;
 +		tinystr[1] = 0;
 +		return tinystr;
 +	}
 +
 +	// check for a key string
 +	for ( kn=keynames ; kn->name ; kn++ ) {
 +		if (keynum == kn->keynum) {
 +			return kn->name;
 +		}
 +	}
 +
 +	// make a hex string
 +	i = keynum >> 4;
 +	j = keynum & 15;
 +
 +	tinystr[0] = '0';
 +	tinystr[1] = 'x';
 +	tinystr[2] = i > 9 ? i - 10 + 'a' : i + '0';
 +	tinystr[3] = j > 9 ? j - 10 + 'a' : j + '0';
 +	tinystr[4] = 0;
 +
 +	return tinystr;
 +}
 +
 +
 +/*
 +===================
 +Key_SetBinding
 +===================
 +*/
 +void Key_SetBinding( int keynum, const char *binding ) {
 +	if ( keynum == -1 ) {
 +		return;
 +	}
 +
 +	// free old bindings
 +	if ( keys[ keynum ].binding ) {
 +		Z_Free( keys[ keynum ].binding );
 +	}
 +		
 +	// allocate memory for new binding
 +	keys[keynum].binding = CopyString( binding );
 +
 +	// consider this like modifying an archived cvar, so the
 +	// file write will be triggered at the next oportunity
 +	cvar_modifiedFlags |= CVAR_ARCHIVE;
 +}
 +
 +
 +/*
 +===================
 +Key_GetBinding
 +===================
 +*/
 +char *Key_GetBinding( int keynum ) {
 +	if ( keynum == -1 ) {
 +		return "";
 +	}
 +
 +	return keys[ keynum ].binding;
 +}
 +
 +/* 
 +===================
 +Key_GetKey
 +===================
 +*/
 +
 +int Key_GetKey(const char *binding) {
 +  int i;
 +
 +  if (binding) {
 +  	for (i=0 ; i<256 ; i++) {
 +      if (keys[i].binding && Q_stricmp(binding, keys[i].binding) == 0) {
 +        return i;
 +      }
 +    }
 +  }
 +  return -1;
 +}
 +
 +/*
 +===================
 +Key_Unbind_f
 +===================
 +*/
 +void Key_Unbind_f (void)
 +{
 +	int		b;
 +
 +	if (Cmd_Argc() != 2)
 +	{
 +		Com_Printf ("unbind <key> : remove commands from a key\n");
 +		return;
 +	}
 +	
 +	b = Key_StringToKeynum (Cmd_Argv(1));
 +	if (b==-1)
 +	{
 +		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
 +		return;
 +	}
 +
 +	Key_SetBinding (b, "");
 +}
 +
 +/*
 +===================
 +Key_Unbindall_f
 +===================
 +*/
 +void Key_Unbindall_f (void)
 +{
 +	int		i;
 +	
 +	for (i=0 ; i<256 ; i++)
 +		if (keys[i].binding)
 +			Key_SetBinding (i, "");
 +}
 +
 +
 +/*
 +===================
 +Key_Bind_f
 +===================
 +*/
 +void Key_Bind_f (void)
 +{
 +	int			i, c, b;
 +	char		cmd[1024];
 +	
 +	c = Cmd_Argc();
 +
 +	if (c < 2)
 +	{
 +		Com_Printf ("bind <key> [command] : attach a command to a key\n");
 +		return;
 +	}
 +	b = Key_StringToKeynum (Cmd_Argv(1));
 +	if (b==-1)
 +	{
 +		Com_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1));
 +		return;
 +	}
 +
 +	if (c == 2)
 +	{
 +		if (keys[b].binding)
 +			Com_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keys[b].binding );
 +		else
 +			Com_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) );
 +		return;
 +	}
 +	
 +// copy the rest of the command line
 +	cmd[0] = 0;		// start out with a null string
 +	for (i=2 ; i< c ; i++)
 +	{
 +		strcat (cmd, Cmd_Argv(i));
 +		if (i != (c-1))
 +			strcat (cmd, " ");
 +	}
 +
 +	Key_SetBinding (b, cmd);
 +}
 +
 +/*
 +============
 +Key_WriteBindings
 +
 +Writes lines containing "bind key value"
 +============
 +*/
 +void Key_WriteBindings( fileHandle_t f ) {
 +	int		i;
 +
 +	FS_Printf (f, "unbindall\n" );
 +
 +	for (i=0 ; i<256 ; i++) {
 +		if (keys[i].binding && keys[i].binding[0] ) {
 +			FS_Printf (f, "bind %s \"%s\"\n", Key_KeynumToString(i), keys[i].binding);
 +
 +		}
 +
 +	}
 +}
 +
 +
 +/*
 +============
 +Key_Bindlist_f
 +
 +============
 +*/
 +void Key_Bindlist_f( void ) {
 +	int		i;
 +
 +	for ( i = 0 ; i < 256 ; i++ ) {
 +		if ( keys[i].binding && keys[i].binding[0] ) {
 +			Com_Printf( "%s \"%s\"\n", Key_KeynumToString(i), keys[i].binding );
 +		}
 +	}
 +}
 +
 +/*
 +===================
 +CL_InitKeyCommands
 +===================
 +*/
 +void CL_InitKeyCommands( void ) {
 +	// register our functions
 +	Cmd_AddCommand ("bind",Key_Bind_f);
 +	Cmd_AddCommand ("unbind",Key_Unbind_f);
 +	Cmd_AddCommand ("unbindall",Key_Unbindall_f);
 +	Cmd_AddCommand ("bindlist",Key_Bindlist_f);
 +}
 +
 +/*
 +===================
 +CL_AddKeyUpCommands
 +===================
 +*/
 +void CL_AddKeyUpCommands( int key, char *kb ) {
 +	int i;
 +	char button[1024], *buttonPtr;
 +	char	cmd[1024];
 +	qboolean keyevent;
 +
 +	if ( !kb ) {
 +		return;
 +	}
 +	keyevent = qfalse;
 +	buttonPtr = button;
 +	for ( i = 0; ; i++ ) {
 +		if ( kb[i] == ';' || !kb[i] ) {
 +			*buttonPtr = '\0';
 +			if ( button[0] == '+') {
 +				// button commands add keynum and time as parms so that multiple
 +				// sources can be discriminated and subframe corrected
 +				Com_sprintf (cmd, sizeof(cmd), "-%s %i %i\n", button+1, key, time);
 +				Cbuf_AddText (cmd);
 +				keyevent = qtrue;
 +			} else {
 +				if (keyevent) {
 +					// down-only command
 +					Cbuf_AddText (button);
 +					Cbuf_AddText ("\n");
 +				}
 +			}
 +			buttonPtr = button;
 +			while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) {
 +				i++;
 +			}
 +		}
 +		*buttonPtr++ = kb[i];
 +		if ( !kb[i] ) {
 +			break;
 +		}
 +	}
 +}
 +
 +/*
 +===================
 +CL_KeyEvent
 +
 +Called by the system for both key up and key down events
 +===================
 +*/
 +void CL_KeyEvent (int key, qboolean down, unsigned time) {
 +	char	*kb;
 +	char	cmd[1024];
 +
 +	// update auto-repeat status and BUTTON_ANY status
 +	keys[key].down = down;
 +
 +	if (down) {
 +		keys[key].repeats++;
 +		if ( keys[key].repeats == 1) {
 +			anykeydown++;
 +		}
 +	} else {
 +		keys[key].repeats = 0;
 +		anykeydown--;
 +		if (anykeydown < 0) {
 +			anykeydown = 0;
 +		}
 +	}
 +
 +#ifdef __linux__
 +  if (key == K_ENTER)
 +  {
 +    if (down)
 +    {
 +      if (keys[K_ALT].down)
 +      {
 +        Key_ClearStates();
 +        if (Cvar_VariableValue("r_fullscreen") == 0)
 +        {
 +          Com_Printf("Switching to fullscreen rendering\n");
 +          Cvar_Set("r_fullscreen", "1");
 +        }
 +        else
 +        {
 +          Com_Printf("Switching to windowed rendering\n");
 +          Cvar_Set("r_fullscreen", "0");
 +        }
 +        Cbuf_ExecuteText( EXEC_APPEND, "vid_restart\n");
 +        return;
 +      }
 +    }
 +  }
 +#endif
 +
 +	// console key is hardcoded, so the user can never unbind it
 +	if (key == '`' || key == '~') {
 +		if (!down) {
 +			return;
 +		}
 +    Con_ToggleConsole_f ();
 +		return;
 +	}
 +
 +
 +	// keys can still be used for bound actions
 +	if ( down && ( key < 128 || key == K_MOUSE1 ) && ( clc.demoplaying || cls.state == CA_CINEMATIC ) && !cls.keyCatchers) {
 +
 +		if (Cvar_VariableValue ("com_cameraMode") == 0) {
 +			Cvar_Set ("nextdemo","");
 +			key = K_ESCAPE;
 +		}
 +	}
 +
 +
 +	// escape is always handled special
 +	if ( key == K_ESCAPE && down ) {
 +		if ( cls.keyCatchers & KEYCATCH_MESSAGE ) {
 +			// clear message mode
 +			Message_Key( key );
 +			return;
 +		}
 +
 +		// escape always gets out of CGAME stuff
 +		if (cls.keyCatchers & KEYCATCH_CGAME) {
 +			cls.keyCatchers &= ~KEYCATCH_CGAME;
 +			VM_Call (cgvm, CG_EVENT_HANDLING, CGAME_EVENT_NONE);
 +			return;
 +		}
 +
 +		if ( !( cls.keyCatchers & KEYCATCH_UI ) ) {
 +			if ( cls.state == CA_ACTIVE && !clc.demoplaying ) {
 +				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_INGAME );
 +			}
 +			else {
 +				CL_Disconnect_f();
 +				S_StopAllSounds();
 +				VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
 +			}
 +			return;
 +		}
 +
 +		VM_Call( uivm, UI_KEY_EVENT, key, down );
 +		return;
 +	}
 +
 +	//
 +	// key up events only perform actions if the game key binding is
 +	// a button command (leading + sign).  These will be processed even in
 +	// console mode and menu mode, to keep the character from continuing 
 +	// an action started before a mode switch.
 +	//
 +	if (!down) {
 +		kb = keys[key].binding;
 +
 +		CL_AddKeyUpCommands( key, kb );
 +
 +		if ( cls.keyCatchers & KEYCATCH_UI && uivm ) {
 +			VM_Call( uivm, UI_KEY_EVENT, key, down );
 +		} else if ( cls.keyCatchers & KEYCATCH_CGAME && cgvm ) {
 +			VM_Call( cgvm, CG_KEY_EVENT, key, down );
 +		} 
 +
 +		return;
 +	}
 +
 +
 +	// distribute the key down event to the apropriate handler
 +	if ( cls.keyCatchers & KEYCATCH_CONSOLE ) {
 +		Console_Key( key );
 +	} else if ( cls.keyCatchers & KEYCATCH_UI ) {
 +		if ( uivm ) {
 +			VM_Call( uivm, UI_KEY_EVENT, key, down );
 +		} 
 +	} else if ( cls.keyCatchers & KEYCATCH_CGAME ) {
 +		if ( cgvm ) {
 +			VM_Call( cgvm, CG_KEY_EVENT, key, down );
 +		} 
 +	} else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) {
 +		Message_Key( key );
 +	} else if ( cls.state == CA_DISCONNECTED ) {
 +		Console_Key( key );
 +	} else {
 +		// send the bound action
 +		kb = keys[key].binding;
 +		if ( !kb ) {
 +			if (key >= 200) {
 +				Com_Printf ("%s is unbound, use controls menu to set.\n"
 +					, Key_KeynumToString( key ) );
 +			}
 +		} else if (kb[0] == '+') {	
 +			int i;
 +			char button[1024], *buttonPtr;
 +			buttonPtr = button;
 +			for ( i = 0; ; i++ ) {
 +				if ( kb[i] == ';' || !kb[i] ) {
 +					*buttonPtr = '\0';
 +					if ( button[0] == '+') {
 +						// button commands add keynum and time as parms so that multiple
 +						// sources can be discriminated and subframe corrected
 +						Com_sprintf (cmd, sizeof(cmd), "%s %i %i\n", button, key, time);
 +						Cbuf_AddText (cmd);
 +					} else {
 +						// down-only command
 +						Cbuf_AddText (button);
 +						Cbuf_AddText ("\n");
 +					}
 +					buttonPtr = button;
 +					while ( (kb[i] <= ' ' || kb[i] == ';') && kb[i] != 0 ) {
 +						i++;
 +					}
 +				}
 +				*buttonPtr++ = kb[i];
 +				if ( !kb[i] ) {
 +					break;
 +				}
 +			}
 +		} else {
 +			// down-only command
 +			Cbuf_AddText (kb);
 +			Cbuf_AddText ("\n");
 +		}
 +	}
 +}
 +
 +
 +/*
 +===================
 +CL_CharEvent
 +
 +Normal keyboard characters, already shifted / capslocked / etc
 +===================
 +*/
 +void CL_CharEvent( int key ) {
 +	// the console key should never be used as a char
 +	if ( key == '`' || key == '~' ) {
 +		return;
 +	}
 +
 +	// distribute the key down event to the apropriate handler
 +	if ( cls.keyCatchers & KEYCATCH_CONSOLE )
 +	{
 +		Field_CharEvent( &g_consoleField, key );
 +	}
 +	else if ( cls.keyCatchers & KEYCATCH_UI )
 +	{
 +		VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue );
 +	}
 +	else if ( cls.keyCatchers & KEYCATCH_MESSAGE ) 
 +	{
 +		Field_CharEvent( &chatField, key );
 +	}
 +	else if ( cls.state == CA_DISCONNECTED )
 +	{
 +		Field_CharEvent( &g_consoleField, key );
 +	}
 +}
 +
 +
 +/*
 +===================
 +Key_ClearStates
 +===================
 +*/
 +void Key_ClearStates (void)
 +{
 +	int		i;
 +
 +	anykeydown = qfalse;
 +
 +	for ( i=0 ; i < MAX_KEYS ; i++ ) {
 +		if ( keys[i].down ) {
 +			CL_KeyEvent( i, qfalse, 0 );
 +
 +		}
 +		keys[i].down = 0;
 +		keys[i].repeats = 0;
 +	}
 +}
 +
 diff --git a/code/client/cl_main.c b/code/client/cl_main.c new file mode 100755 index 0000000..89d3b48 --- /dev/null +++ b/code/client/cl_main.c @@ -0,0 +1,3324 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// cl_main.c  -- client main loop
 +
 +#include "client.h"
 +#include <limits.h>
 +
 +cvar_t	*cl_nodelta;
 +cvar_t	*cl_debugMove;
 +
 +cvar_t	*cl_noprint;
 +cvar_t	*cl_motd;
 +
 +cvar_t	*rcon_client_password;
 +cvar_t	*rconAddress;
 +
 +cvar_t	*cl_timeout;
 +cvar_t	*cl_maxpackets;
 +cvar_t	*cl_packetdup;
 +cvar_t	*cl_timeNudge;
 +cvar_t	*cl_showTimeDelta;
 +cvar_t	*cl_freezeDemo;
 +
 +cvar_t	*cl_shownet;
 +cvar_t	*cl_showSend;
 +cvar_t	*cl_timedemo;
 +cvar_t	*cl_avidemo;
 +cvar_t	*cl_forceavidemo;
 +
 +cvar_t	*cl_freelook;
 +cvar_t	*cl_sensitivity;
 +
 +cvar_t	*cl_mouseAccel;
 +cvar_t	*cl_showMouseRate;
 +
 +cvar_t	*m_pitch;
 +cvar_t	*m_yaw;
 +cvar_t	*m_forward;
 +cvar_t	*m_side;
 +cvar_t	*m_filter;
 +
 +cvar_t	*cl_activeAction;
 +
 +cvar_t	*cl_motdString;
 +
 +cvar_t	*cl_allowDownload;
 +cvar_t	*cl_conXOffset;
 +cvar_t	*cl_inGameVideo;
 +
 +cvar_t	*cl_serverStatusResendTime;
 +cvar_t	*cl_trn;
 +
 +clientActive_t		cl;
 +clientConnection_t	clc;
 +clientStatic_t		cls;
 +vm_t				*cgvm;
 +
 +// Structure containing functions exported from refresh DLL
 +refexport_t	re;
 +
 +ping_t	cl_pinglist[MAX_PINGREQUESTS];
 +
 +typedef struct serverStatus_s
 +{
 +	char string[BIG_INFO_STRING];
 +	netadr_t address;
 +	int time, startTime;
 +	qboolean pending;
 +	qboolean print;
 +	qboolean retrieved;
 +} serverStatus_t;
 +
 +serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
 +int serverStatusCount;
 +
 +#if defined __USEA3D && defined __A3D_GEOM
 +	void hA3Dg_ExportRenderGeom (refexport_t *incoming_re);
 +#endif
 +
 +extern void SV_BotFrame( int time );
 +void CL_CheckForResend( void );
 +void CL_ShowIP_f(void);
 +void CL_ServerStatus_f(void);
 +void CL_ServerStatusResponse( netadr_t from, msg_t *msg );
 +
 +/*
 +===============
 +CL_CDDialog
 +
 +Called by Com_Error when a cd is needed
 +===============
 +*/
 +void CL_CDDialog( void ) {
 +	cls.cddialog = qtrue;	// start it next frame
 +}
 +
 +
 +/*
 +=======================================================================
 +
 +CLIENT RELIABLE COMMAND COMMUNICATION
 +
 +=======================================================================
 +*/
 +
 +/*
 +======================
 +CL_AddReliableCommand
 +
 +The given command will be transmitted to the server, and is gauranteed to
 +not have future usercmd_t executed before it is executed
 +======================
 +*/
 +void CL_AddReliableCommand( const char *cmd ) {
 +	int		index;
 +
 +	// if we would be losing an old command that hasn't been acknowledged,
 +	// we must drop the connection
 +	if ( clc.reliableSequence - clc.reliableAcknowledge > MAX_RELIABLE_COMMANDS ) {
 +		Com_Error( ERR_DROP, "Client command overflow" );
 +	}
 +	clc.reliableSequence++;
 +	index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
 +	Q_strncpyz( clc.reliableCommands[ index ], cmd, sizeof( clc.reliableCommands[ index ] ) );
 +}
 +
 +/*
 +======================
 +CL_ChangeReliableCommand
 +======================
 +*/
 +void CL_ChangeReliableCommand( void ) {
 +	int r, index, l;
 +
 +	r = clc.reliableSequence - (random() * 5);
 +	index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
 +	l = strlen(clc.reliableCommands[ index ]);
 +	if ( l >= MAX_STRING_CHARS - 1 ) {
 +		l = MAX_STRING_CHARS - 2;
 +	}
 +	clc.reliableCommands[ index ][ l ] = '\n';
 +	clc.reliableCommands[ index ][ l+1 ] = '\0';
 +}
 +
 +/*
 +=======================================================================
 +
 +CLIENT SIDE DEMO RECORDING
 +
 +=======================================================================
 +*/
 +
 +/*
 +====================
 +CL_WriteDemoMessage
 +
 +Dumps the current net message, prefixed by the length
 +====================
 +*/
 +void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
 +	int		len, swlen;
 +
 +	// write the packet sequence
 +	len = clc.serverMessageSequence;
 +	swlen = LittleLong( len );
 +	FS_Write (&swlen, 4, clc.demofile);
 +
 +	// skip the packet sequencing information
 +	len = msg->cursize - headerBytes;
 +	swlen = LittleLong(len);
 +	FS_Write (&swlen, 4, clc.demofile);
 +	FS_Write ( msg->data + headerBytes, len, clc.demofile );
 +}
 +
 +
 +/*
 +====================
 +CL_StopRecording_f
 +
 +stop recording a demo
 +====================
 +*/
 +void CL_StopRecord_f( void ) {
 +	int		len;
 +
 +	if ( !clc.demorecording ) {
 +		Com_Printf ("Not recording a demo.\n");
 +		return;
 +	}
 +
 +	// finish up
 +	len = -1;
 +	FS_Write (&len, 4, clc.demofile);
 +	FS_Write (&len, 4, clc.demofile);
 +	FS_FCloseFile (clc.demofile);
 +	clc.demofile = 0;
 +	clc.demorecording = qfalse;
 +	clc.spDemoRecording = qfalse;
 +	Com_Printf ("Stopped demo.\n");
 +}
 +
 +/* 
 +================== 
 +CL_DemoFilename
 +================== 
 +*/  
 +void CL_DemoFilename( int number, char *fileName ) {
 +	int		a,b,c,d;
 +
 +	if ( number < 0 || number > 9999 ) {
 +		Com_sprintf( fileName, MAX_OSPATH, "demo9999.tga" );
 +		return;
 +	}
 +
 +	a = number / 1000;
 +	number -= a*1000;
 +	b = number / 100;
 +	number -= b*100;
 +	c = number / 10;
 +	number -= c*10;
 +	d = number;
 +
 +	Com_sprintf( fileName, MAX_OSPATH, "demo%i%i%i%i"
 +		, a, b, c, d );
 +}
 +
 +/*
 +====================
 +CL_Record_f
 +
 +record <demoname>
 +
 +Begins recording a demo from the current position
 +====================
 +*/
 +static char		demoName[MAX_QPATH];	// compiler bug workaround
 +void CL_Record_f( void ) {
 +	char		name[MAX_OSPATH];
 +	byte		bufData[MAX_MSGLEN];
 +	msg_t	buf;
 +	int			i;
 +	int			len;
 +	entityState_t	*ent;
 +	entityState_t	nullstate;
 +	char		*s;
 +
 +	if ( Cmd_Argc() > 2 ) {
 +		Com_Printf ("record <demoname>\n");
 +		return;
 +	}
 +
 +	if ( clc.demorecording ) {
 +		if (!clc.spDemoRecording) {
 +			Com_Printf ("Already recording.\n");
 +		}
 +		return;
 +	}
 +
 +	if ( cls.state != CA_ACTIVE ) {
 +		Com_Printf ("You must be in a level to record.\n");
 +		return;
 +	}
 +
 +  // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 ..
 +	if ( !Cvar_VariableValue( "g_synchronousClients" ) ) {
 +		Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n");
 +	}
 +
 +	if ( Cmd_Argc() == 2 ) {
 +		s = Cmd_Argv(1);
 +		Q_strncpyz( demoName, s, sizeof( demoName ) );
 +		Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
 +	} else {
 +		int		number;
 +
 +		// scan for a free demo name
 +		for ( number = 0 ; number <= 9999 ; number++ ) {
 +			CL_DemoFilename( number, demoName );
 +			Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
 +
 +			len = FS_ReadFile( name, NULL );
 +			if ( len <= 0 ) {
 +				break;	// file doesn't exist
 +			}
 +		}
 +	}
 +
 +	// open the demo file
 +
 +	Com_Printf ("recording to %s.\n", name);
 +	clc.demofile = FS_FOpenFileWrite( name );
 +	if ( !clc.demofile ) {
 +		Com_Printf ("ERROR: couldn't open.\n");
 +		return;
 +	}
 +	clc.demorecording = qtrue;
 +	if (Cvar_VariableValue("ui_recordSPDemo")) {
 +	  clc.spDemoRecording = qtrue;
 +	} else {
 +	  clc.spDemoRecording = qfalse;
 +	}
 +
 +
 +	Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
 +
 +	// don't start saving messages until a non-delta compressed message is received
 +	clc.demowaiting = qtrue;
 +
 +	// write out the gamestate message
 +	MSG_Init (&buf, bufData, sizeof(bufData));
 +	MSG_Bitstream(&buf);
 +
 +	// NOTE, MRE: all server->client messages now acknowledge
 +	MSG_WriteLong( &buf, clc.reliableSequence );
 +
 +	MSG_WriteByte (&buf, svc_gamestate);
 +	MSG_WriteLong (&buf, clc.serverCommandSequence );
 +
 +	// configstrings
 +	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
 +		if ( !cl.gameState.stringOffsets[i] ) {
 +			continue;
 +		}
 +		s = cl.gameState.stringData + cl.gameState.stringOffsets[i];
 +		MSG_WriteByte (&buf, svc_configstring);
 +		MSG_WriteShort (&buf, i);
 +		MSG_WriteBigString (&buf, s);
 +	}
 +
 +	// baselines
 +	Com_Memset (&nullstate, 0, sizeof(nullstate));
 +	for ( i = 0; i < MAX_GENTITIES ; i++ ) {
 +		ent = &cl.entityBaselines[i];
 +		if ( !ent->number ) {
 +			continue;
 +		}
 +		MSG_WriteByte (&buf, svc_baseline);		
 +		MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue );
 +	}
 +
 +	MSG_WriteByte( &buf, svc_EOF );
 +	
 +	// finished writing the gamestate stuff
 +
 +	// write the client num
 +	MSG_WriteLong(&buf, clc.clientNum);
 +	// write the checksum feed
 +	MSG_WriteLong(&buf, clc.checksumFeed);
 +
 +	// finished writing the client packet
 +	MSG_WriteByte( &buf, svc_EOF );
 +
 +	// write it to the demo file
 +	len = LittleLong( clc.serverMessageSequence - 1 );
 +	FS_Write (&len, 4, clc.demofile);
 +
 +	len = LittleLong (buf.cursize);
 +	FS_Write (&len, 4, clc.demofile);
 +	FS_Write (buf.data, buf.cursize, clc.demofile);
 +
 +	// the rest of the demo file will be copied from net messages
 +}
 +
 +/*
 +=======================================================================
 +
 +CLIENT SIDE DEMO PLAYBACK
 +
 +=======================================================================
 +*/
 +
 +/*
 +=================
 +CL_DemoCompleted
 +=================
 +*/
 +void CL_DemoCompleted( void ) {
 +	if (cl_timedemo && cl_timedemo->integer) {
 +		int	time;
 +		
 +		time = Sys_Milliseconds() - clc.timeDemoStart;
 +		if ( time > 0 ) {
 +			Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames,
 +			time/1000.0, clc.timeDemoFrames*1000.0 / time);
 +		}
 +	}
 +
 +	CL_Disconnect( qtrue );
 +	CL_NextDemo();
 +}
 +
 +/*
 +=================
 +CL_ReadDemoMessage
 +=================
 +*/
 +void CL_ReadDemoMessage( void ) {
 +	int			r;
 +	msg_t		buf;
 +	byte		bufData[ MAX_MSGLEN ];
 +	int			s;
 +
 +	if ( !clc.demofile ) {
 +		CL_DemoCompleted ();
 +		return;
 +	}
 +
 +	// get the sequence number
 +	r = FS_Read( &s, 4, clc.demofile);
 +	if ( r != 4 ) {
 +		CL_DemoCompleted ();
 +		return;
 +	}
 +	clc.serverMessageSequence = LittleLong( s );
 +
 +	// init the message
 +	MSG_Init( &buf, bufData, sizeof( bufData ) );
 +
 +	// get the length
 +	r = FS_Read (&buf.cursize, 4, clc.demofile);
 +	if ( r != 4 ) {
 +		CL_DemoCompleted ();
 +		return;
 +	}
 +	buf.cursize = LittleLong( buf.cursize );
 +	if ( buf.cursize == -1 ) {
 +		CL_DemoCompleted ();
 +		return;
 +	}
 +	if ( buf.cursize > buf.maxsize ) {
 +		Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN");
 +	}
 +	r = FS_Read( buf.data, buf.cursize, clc.demofile );
 +	if ( r != buf.cursize ) {
 +		Com_Printf( "Demo file was truncated.\n");
 +		CL_DemoCompleted ();
 +		return;
 +	}
 +
 +	clc.lastPacketTime = cls.realtime;
 +	buf.readcount = 0;
 +	CL_ParseServerMessage( &buf );
 +}
 +
 +/*
 +====================
 +CL_WalkDemoExt
 +====================
 +*/
 +static void CL_WalkDemoExt(char *arg, char *name, int *demofile)
 +{
 +	int i = 0;
 +	*demofile = 0;
 +	while(demo_protocols[i])
 +	{
 +		Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]);
 +		FS_FOpenFileRead( name, demofile, qtrue );
 +		if (*demofile)
 +		{
 +			Com_Printf("Demo file: %s\n", name);
 +			break;
 +		}
 +		else
 +			Com_Printf("Not found: %s\n", name);
 +		i++;
 +	}
 +}
 +
 +/*
 +====================
 +CL_PlayDemo_f
 +
 +demo <demoname>
 +
 +====================
 +*/
 +void CL_PlayDemo_f( void ) {
 +	char		name[MAX_OSPATH];
 +	char		*arg, *ext_test;
 +	int			protocol, i;
 +	char		retry[MAX_OSPATH];
 +
 +	if (Cmd_Argc() != 2) {
 +		Com_Printf ("playdemo <demoname>\n");
 +		return;
 +	}
 +
 +	// make sure a local server is killed
 +	Cvar_Set( "sv_killserver", "1" );
 +
 +	CL_Disconnect( qtrue );
 +
 +	// open the demo file
 +	arg = Cmd_Argv(1);
 +	
 +	// check for an extension .dm_?? (?? is protocol)
 +	ext_test = arg + strlen(arg) - 6;
 +	if ((strlen(arg) > 6) && (ext_test[0] == '.') && ((ext_test[1] == 'd') || (ext_test[1] == 'D')) && ((ext_test[2] == 'm') || (ext_test[2] == 'M')) && (ext_test[3] == '_'))
 +	{
 +		protocol = atoi(ext_test+4);
 +		i=0;
 +		while(demo_protocols[i])
 +		{
 +			if (demo_protocols[i] == protocol)
 +				break;
 +			i++;
 +		}
 +		if (demo_protocols[i])
 +		{
 +			Com_sprintf (name, sizeof(name), "demos/%s", arg);
 +			FS_FOpenFileRead( name, &clc.demofile, qtrue );
 +		} else {
 +			Com_Printf("Protocol %d not supported for demos\n", protocol);
 +			Q_strncpyz(retry, arg, sizeof(retry));
 +			retry[strlen(retry)-6] = 0;
 +			CL_WalkDemoExt( retry, name, &clc.demofile );
 +		}
 +	} else {
 +		CL_WalkDemoExt( arg, name, &clc.demofile );
 +	}
 +	
 +	if (!clc.demofile) {
 +		Com_Error( ERR_DROP, "couldn't open %s", name);
 +		return;
 +	}
 +	Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) );
 +
 +	Con_Close();
 +
 +	cls.state = CA_CONNECTED;
 +	clc.demoplaying = qtrue;
 +	Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );
 +
 +	// read demo messages until connected
 +	while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) {
 +		CL_ReadDemoMessage();
 +	}
 +	// don't get the first snapshot this frame, to prevent the long
 +	// time from the gamestate load from messing causing a time skip
 +	clc.firstDemoFrameSkipped = qfalse;
 +}
 +
 +
 +/*
 +====================
 +CL_StartDemoLoop
 +
 +Closing the main menu will restart the demo loop
 +====================
 +*/
 +void CL_StartDemoLoop( void ) {
 +	// start the demo loop again
 +	Cbuf_AddText ("d1\n");
 +	cls.keyCatchers = 0;
 +}
 +
 +/*
 +==================
 +CL_NextDemo
 +
 +Called when a demo or cinematic finishes
 +If the "nextdemo" cvar is set, that command will be issued
 +==================
 +*/
 +void CL_NextDemo( void ) {
 +	char	v[MAX_STRING_CHARS];
 +
 +	Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) );
 +	v[MAX_STRING_CHARS-1] = 0;
 +	Com_DPrintf("CL_NextDemo: %s\n", v );
 +	if (!v[0]) {
 +		return;
 +	}
 +
 +	Cvar_Set ("nextdemo","");
 +	Cbuf_AddText (v);
 +	Cbuf_AddText ("\n");
 +	Cbuf_Execute();
 +}
 +
 +
 +//======================================================================
 +
 +/*
 +=====================
 +CL_ShutdownAll
 +=====================
 +*/
 +void CL_ShutdownAll(void) {
 +
 +	// clear sounds
 +	S_DisableSounds();
 +	// shutdown CGame
 +	CL_ShutdownCGame();
 +	// shutdown UI
 +	CL_ShutdownUI();
 +
 +	// shutdown the renderer
 +	if ( re.Shutdown ) {
 +		re.Shutdown( qfalse );		// don't destroy window or context
 +	}
 +
 +	cls.uiStarted = qfalse;
 +	cls.cgameStarted = qfalse;
 +	cls.rendererStarted = qfalse;
 +	cls.soundRegistered = qfalse;
 +}
 +
 +/*
 +=================
 +CL_FlushMemory
 +
 +Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only
 +ways a client gets into a game
 +Also called by Com_Error
 +=================
 +*/
 +void CL_FlushMemory( void ) {
 +
 +	// shutdown all the client stuff
 +	CL_ShutdownAll();
 +
 +	// if not running a server clear the whole hunk
 +	if ( !com_sv_running->integer ) {
 +		// clear the whole hunk
 +		Hunk_Clear();
 +		// clear collision map data
 +		CM_ClearMap();
 +	}
 +	else {
 +		// clear all the client data on the hunk
 +		Hunk_ClearToMark();
 +	}
 +
 +	CL_StartHunkUsers();
 +}
 +
 +/*
 +=====================
 +CL_MapLoading
 +
 +A local server is starting to load a map, so update the
 +screen to let the user know about it, then dump all client
 +memory on the hunk from cgame, ui, and renderer
 +=====================
 +*/
 +void CL_MapLoading( void ) {
 +	if ( !com_cl_running->integer ) {
 +		return;
 +	}
 +
 +	Con_Close();
 +	cls.keyCatchers = 0;
 +
 +	// if we are already connected to the local host, stay connected
 +	if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) {
 +		cls.state = CA_CONNECTED;		// so the connect screen is drawn
 +		Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) );
 +		Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) );
 +		Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
 +		clc.lastPacketSentTime = -9999;
 +		SCR_UpdateScreen();
 +	} else {
 +		// clear nextmap so the cinematic shutdown doesn't execute it
 +		Cvar_Set( "nextmap", "" );
 +		CL_Disconnect( qtrue );
 +		Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) );
 +		cls.state = CA_CHALLENGING;		// so the connect screen is drawn
 +		cls.keyCatchers = 0;
 +		SCR_UpdateScreen();
 +		clc.connectTime = -RETRANSMIT_TIMEOUT;
 +		NET_StringToAdr( cls.servername, &clc.serverAddress);
 +		// we don't need a challenge on the localhost
 +
 +		CL_CheckForResend();
 +	}
 +}
 +
 +/*
 +=====================
 +CL_ClearState
 +
 +Called before parsing a gamestate
 +=====================
 +*/
 +void CL_ClearState (void) {
 +
 +//	S_StopAllSounds();
 +
 +	Com_Memset( &cl, 0, sizeof( cl ) );
 +}
 +
 +
 +/*
 +=====================
 +CL_Disconnect
 +
 +Called when a connection, demo, or cinematic is being terminated.
 +Goes from a connected state to either a menu state or a console state
 +Sends a disconnect message to the server
 +This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors
 +=====================
 +*/
 +void CL_Disconnect( qboolean showMainMenu ) {
 +	if ( !com_cl_running || !com_cl_running->integer ) {
 +		return;
 +	}
 +
 +	// shutting down the client so enter full screen ui mode
 +	Cvar_Set("r_uiFullScreen", "1");
 +
 +	if ( clc.demorecording ) {
 +		CL_StopRecord_f ();
 +	}
 +
 +	if (clc.download) {
 +		FS_FCloseFile( clc.download );
 +		clc.download = 0;
 +	}
 +	*clc.downloadTempName = *clc.downloadName = 0;
 +	Cvar_Set( "cl_downloadName", "" );
 +
 +	if ( clc.demofile ) {
 +		FS_FCloseFile( clc.demofile );
 +		clc.demofile = 0;
 +	}
 +
 +	if ( uivm && showMainMenu ) {
 +		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
 +	}
 +
 +	SCR_StopCinematic ();
 +	S_ClearSoundBuffer();
 +
 +	// send a disconnect message to the server
 +	// send it a few times in case one is dropped
 +	if ( cls.state >= CA_CONNECTED ) {
 +		CL_AddReliableCommand( "disconnect" );
 +		CL_WritePacket();
 +		CL_WritePacket();
 +		CL_WritePacket();
 +	}
 +	
 +	CL_ClearState ();
 +
 +	// wipe the client connection
 +	Com_Memset( &clc, 0, sizeof( clc ) );
 +
 +	cls.state = CA_DISCONNECTED;
 +
 +	// allow cheats locally
 +	Cvar_Set( "sv_cheats", "1" );
 +
 +	// not connected to a pure server anymore
 +	cl_connectedToPureServer = qfalse;
 +}
 +
 +
 +/*
 +===================
 +CL_ForwardCommandToServer
 +
 +adds the current command line as a clientCommand
 +things like godmode, noclip, etc, are commands directed to the server,
 +so when they are typed in at the console, they will need to be forwarded.
 +===================
 +*/
 +void CL_ForwardCommandToServer( const char *string ) {
 +	char	*cmd;
 +
 +	cmd = Cmd_Argv(0);
 +
 +	// ignore key up commands
 +	if ( cmd[0] == '-' ) {
 +		return;
 +	}
 +
 +	if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) {
 +		Com_Printf ("Unknown command \"%s\"\n", cmd);
 +		return;
 +	}
 +
 +	if ( Cmd_Argc() > 1 ) {
 +		CL_AddReliableCommand( string );
 +	} else {
 +		CL_AddReliableCommand( cmd );
 +	}
 +}
 +
 +/*
 +===================
 +CL_RequestMotd
 +
 +===================
 +*/
 +void CL_RequestMotd( void ) {
 +	char		info[MAX_INFO_STRING];
 +
 +	if ( !cl_motd->integer ) {
 +		return;
 +	}
 +	Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME );
 +	if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer  ) ) {
 +		Com_Printf( "Couldn't resolve address\n" );
 +		return;
 +	}
 +	cls.updateServer.port = BigShort( PORT_UPDATE );
 +	Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME,
 +		cls.updateServer.ip[0], cls.updateServer.ip[1],
 +		cls.updateServer.ip[2], cls.updateServer.ip[3],
 +		BigShort( cls.updateServer.port ) );
 +	
 +	info[0] = 0;
 +  // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization
 +  // only srand I could catch before here is tr_noise.c l:26 srand(1001)
 +  // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382
 +  // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word,
 +  //   but I decided it was enough randomization
 +	Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds());
 +
 +	Info_SetValueForKey( info, "challenge", cls.updateChallenge );
 +	Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string );
 +	Info_SetValueForKey( info, "version", com_version->string );
 +
 +	NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info );
 +}
 +
 +/*
 +===================
 +CL_RequestAuthorization
 +
 +Authorization server protocol
 +-----------------------------
 +
 +All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff).
 +
 +Whenever the client tries to get a challenge from the server it wants to
 +connect to, it also blindly fires off a packet to the authorize server:
 +
 +getKeyAuthorize <challenge> <cdkey>
 +
 +cdkey may be "demo"
 +
 +
 +#OLD The authorize server returns a:
 +#OLD 
 +#OLD keyAthorize <challenge> <accept | deny>
 +#OLD 
 +#OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP
 +#OLD address in the last 15 minutes.
 +
 +
 +The server sends a:
 +
 +getIpAuthorize <challenge> <ip>
 +
 +The authorize server returns a:
 +
 +ipAuthorize <challenge> <accept | deny | demo | unknown >
 +
 +A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes.
 +If no response is received from the authorize server after two tries, the client will be let
 +in anyway.
 +===================
 +*/
 +void CL_RequestAuthorization( void ) {
 +	char	nums[64];
 +	int		i, j, l;
 +	cvar_t	*fs;
 +
 +	if ( !cls.authorizeServer.port ) {
 +		Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
 +		if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer  ) ) {
 +			Com_Printf( "Couldn't resolve address\n" );
 +			return;
 +		}
 +
 +		cls.authorizeServer.port = BigShort( PORT_AUTHORIZE );
 +		Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
 +			cls.authorizeServer.ip[0], cls.authorizeServer.ip[1],
 +			cls.authorizeServer.ip[2], cls.authorizeServer.ip[3],
 +			BigShort( cls.authorizeServer.port ) );
 +	}
 +	if ( cls.authorizeServer.type == NA_BAD ) {
 +		return;
 +	}
 +
 +	if ( Cvar_VariableValue( "fs_restrict" ) ) {
 +		Q_strncpyz( nums, "demota", sizeof( nums ) );
 +	} else {
 +		// only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces
 +		j = 0;
 +		l = strlen( cl_cdkey );
 +		if ( l > 32 ) {
 +			l = 32;
 +		}
 +		for ( i = 0 ; i < l ; i++ ) {
 +			if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' )
 +				|| ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' )
 +				|| ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' )
 +				) {
 +				nums[j] = cl_cdkey[i];
 +				j++;
 +			}
 +		}
 +		nums[j] = 0;
 +	}
 +
 +	fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO );
 +
 +	NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, va("getKeyAuthorize %i %s", fs->integer, nums) );
 +}
 +
 +/*
 +======================================================================
 +
 +CONSOLE COMMANDS
 +
 +======================================================================
 +*/
 +
 +/*
 +==================
 +CL_ForwardToServer_f
 +==================
 +*/
 +void CL_ForwardToServer_f( void ) {
 +	if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
 +		Com_Printf ("Not connected to a server.\n");
 +		return;
 +	}
 +	
 +	// don't forward the first argument
 +	if ( Cmd_Argc() > 1 ) {
 +		CL_AddReliableCommand( Cmd_Args() );
 +	}
 +}
 +
 +/*
 +==================
 +CL_Setenv_f
 +
 +Mostly for controlling voodoo environment variables
 +==================
 +*/
 +void CL_Setenv_f( void ) {
 +	int argc = Cmd_Argc();
 +
 +	if ( argc > 2 ) {
 +		char buffer[1024];
 +		int i;
 +
 +		strcpy( buffer, Cmd_Argv(1) );
 +		strcat( buffer, "=" );
 +
 +		for ( i = 2; i < argc; i++ ) {
 +			strcat( buffer, Cmd_Argv( i ) );
 +			strcat( buffer, " " );
 +		}
 +
 +		putenv( buffer );
 +	} else if ( argc == 2 ) {
 +		char *env = getenv( Cmd_Argv(1) );
 +
 +		if ( env ) {
 +			Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
 +		} else {
 +			Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
 +		}
 +	}
 +}
 +
 +
 +/*
 +==================
 +CL_Disconnect_f
 +==================
 +*/
 +void CL_Disconnect_f( void ) {
 +	SCR_StopCinematic();
 +	Cvar_Set("ui_singlePlayerActive", "0");
 +	if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) {
 +		Com_Error (ERR_DISCONNECT, "Disconnected from server");
 +	}
 +}
 +
 +
 +/*
 +================
 +CL_Reconnect_f
 +
 +================
 +*/
 +void CL_Reconnect_f( void ) {
 +	if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) {
 +		Com_Printf( "Can't reconnect to localhost.\n" );
 +		return;
 +	}
 +	Cvar_Set("ui_singlePlayerActive", "0");
 +	Cbuf_AddText( va("connect %s\n", cls.servername ) );
 +}
 +
 +/*
 +================
 +CL_Connect_f
 +
 +================
 +*/
 +void CL_Connect_f( void ) {
 +	char	*server;
 +
 +	if ( Cmd_Argc() != 2 ) {
 +		Com_Printf( "usage: connect [server]\n");
 +		return;	
 +	}
 +
 +	Cvar_Set("ui_singlePlayerActive", "0");
 +
 +	// fire a message off to the motd server
 +	CL_RequestMotd();
 +
 +	// clear any previous "server full" type messages
 +	clc.serverMessage[0] = 0;
 +
 +	server = Cmd_Argv (1);
 +
 +	if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
 +		// if running a local server, kill it
 +		SV_Shutdown( "Server quit\n" );
 +	}
 +
 +	// make sure a local server is killed
 +	Cvar_Set( "sv_killserver", "1" );
 +	SV_Frame( 0 );
 +
 +	CL_Disconnect( qtrue );
 +	Con_Close();
 +
 +	/* MrE: 2000-09-13: now called in CL_DownloadsComplete
 +	CL_FlushMemory( );
 +	*/
 +
 +	Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
 +
 +	if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
 +		Com_Printf ("Bad server address\n");
 +		cls.state = CA_DISCONNECTED;
 +		return;
 +	}
 +	if (clc.serverAddress.port == 0) {
 +		clc.serverAddress.port = BigShort( PORT_SERVER );
 +	}
 +	Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername,
 +		clc.serverAddress.ip[0], clc.serverAddress.ip[1],
 +		clc.serverAddress.ip[2], clc.serverAddress.ip[3],
 +		BigShort( clc.serverAddress.port ) );
 +
 +	// if we aren't playing on a lan, we need to authenticate
 +	// with the cd key
 +	if ( NET_IsLocalAddress( clc.serverAddress ) ) {
 +		cls.state = CA_CHALLENGING;
 +	} else {
 +		cls.state = CA_CONNECTING;
 +	}
 +
 +	cls.keyCatchers = 0;
 +	clc.connectTime = -99999;	// CL_CheckForResend() will fire immediately
 +	clc.connectPacketCount = 0;
 +
 +	// server connection string
 +	Cvar_Set( "cl_currentServerAddress", server );
 +}
 +
 +
 +/*
 +=====================
 +CL_Rcon_f
 +
 +  Send the rest of the command line over as
 +  an unconnected command.
 +=====================
 +*/
 +void CL_Rcon_f( void ) {
 +	char	message[1024];
 +	netadr_t	to;
 +
 +	if ( !rcon_client_password->string ) {
 +		Com_Printf ("You must set 'rconpassword' before\n"
 +					"issuing an rcon command.\n");
 +		return;
 +	}
 +
 +	message[0] = -1;
 +	message[1] = -1;
 +	message[2] = -1;
 +	message[3] = -1;
 +	message[4] = 0;
 +
 +	strcat (message, "rcon ");
 +
 +	strcat (message, rcon_client_password->string);
 +	strcat (message, " ");
 +
 +	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
 +	strcat (message, Cmd_Cmd()+5);
 +
 +	if ( cls.state >= CA_CONNECTED ) {
 +		to = clc.netchan.remoteAddress;
 +	} else {
 +		if (!strlen(rconAddress->string)) {
 +			Com_Printf ("You must either be connected,\n"
 +						"or set the 'rconAddress' cvar\n"
 +						"to issue rcon commands\n");
 +
 +			return;
 +		}
 +		NET_StringToAdr (rconAddress->string, &to);
 +		if (to.port == 0) {
 +			to.port = BigShort (PORT_SERVER);
 +		}
 +	}
 +	
 +	NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
 +}
 +
 +/*
 +=================
 +CL_SendPureChecksums
 +=================
 +*/
 +void CL_SendPureChecksums( void ) {
 +	const char *pChecksums;
 +	char cMsg[MAX_INFO_VALUE];
 +	int i;
 +
 +	// if we are pure we need to send back a command with our referenced pk3 checksums
 +	pChecksums = FS_ReferencedPakPureChecksums();
 +
 +	// "cp"
 +	// "Yf"
 +	Com_sprintf(cMsg, sizeof(cMsg), "Yf ");
 +	Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) );
 +	Q_strcat(cMsg, sizeof(cMsg), pChecksums);
 +	for (i = 0; i < 2; i++) {
 +		cMsg[i] += 10;
 +	}
 +	CL_AddReliableCommand( cMsg );
 +}
 +
 +/*
 +=================
 +CL_ResetPureClientAtServer
 +=================
 +*/
 +void CL_ResetPureClientAtServer( void ) {
 +	CL_AddReliableCommand( va("vdr") );
 +}
 +
 +/*
 +=================
 +CL_Vid_Restart_f
 +
 +Restart the video subsystem
 +
 +we also have to reload the UI and CGame because the renderer
 +doesn't know what graphics to reload
 +=================
 +*/
 +void CL_Vid_Restart_f( void ) {
 +
 +	// don't let them loop during the restart
 +	S_StopAllSounds();
 +	// shutdown the UI
 +	CL_ShutdownUI();
 +	// shutdown the CGame
 +	CL_ShutdownCGame();
 +	// shutdown the renderer and clear the renderer interface
 +	CL_ShutdownRef();
 +	// client is no longer pure untill new checksums are sent
 +	CL_ResetPureClientAtServer();
 +	// clear pak references
 +	FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
 +	// reinitialize the filesystem if the game directory or checksum has changed
 +	FS_ConditionalRestart( clc.checksumFeed );
 +
 +	cls.rendererStarted = qfalse;
 +	cls.uiStarted = qfalse;
 +	cls.cgameStarted = qfalse;
 +	cls.soundRegistered = qfalse;
 +
 +	// unpause so the cgame definately gets a snapshot and renders a frame
 +	Cvar_Set( "cl_paused", "0" );
 +
 +	// if not running a server clear the whole hunk
 +	if ( !com_sv_running->integer ) {
 +		// clear the whole hunk
 +		Hunk_Clear();
 +	}
 +	else {
 +		// clear all the client data on the hunk
 +		Hunk_ClearToMark();
 +	}
 +
 +	// initialize the renderer interface
 +	CL_InitRef();
 +
 +	// startup all the client stuff
 +	CL_StartHunkUsers();
 +
 +	// start the cgame if connected
 +	if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) {
 +		cls.cgameStarted = qtrue;
 +		CL_InitCGame();
 +		// send pure checksums
 +		CL_SendPureChecksums();
 +	}
 +}
 +
 +/*
 +=================
 +CL_Snd_Restart_f
 +
 +Restart the sound subsystem
 +The cgame and game must also be forced to restart because
 +handles will be invalid
 +=================
 +*/
 +void CL_Snd_Restart_f( void ) {
 +	S_Shutdown();
 +	S_Init();
 +
 +	CL_Vid_Restart_f();
 +}
 +
 +
 +/*
 +==================
 +CL_PK3List_f
 +==================
 +*/
 +void CL_OpenedPK3List_f( void ) {
 +	Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames());
 +}
 +
 +/*
 +==================
 +CL_PureList_f
 +==================
 +*/
 +void CL_ReferencedPK3List_f( void ) {
 +	Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames());
 +}
 +
 +/*
 +==================
 +CL_Configstrings_f
 +==================
 +*/
 +void CL_Configstrings_f( void ) {
 +	int		i;
 +	int		ofs;
 +
 +	if ( cls.state != CA_ACTIVE ) {
 +		Com_Printf( "Not connected to a server.\n");
 +		return;
 +	}
 +
 +	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
 +		ofs = cl.gameState.stringOffsets[ i ];
 +		if ( !ofs ) {
 +			continue;
 +		}
 +		Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
 +	}
 +}
 +
 +/*
 +==============
 +CL_Clientinfo_f
 +==============
 +*/
 +void CL_Clientinfo_f( void ) {
 +	Com_Printf( "--------- Client Information ---------\n" );
 +	Com_Printf( "state: %i\n", cls.state );
 +	Com_Printf( "Server: %s\n", cls.servername );
 +	Com_Printf ("User info settings:\n");
 +	Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
 +	Com_Printf( "--------------------------------------\n" );
 +}
 +
 +
 +//====================================================================
 +
 +/*
 +=================
 +CL_DownloadsComplete
 +
 +Called when all downloading has been completed
 +=================
 +*/
 +void CL_DownloadsComplete( void ) {
 +
 +	// if we downloaded files we need to restart the file system
 +	if (clc.downloadRestart) {
 +		clc.downloadRestart = qfalse;
 +
 +		FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it
 +
 +		// inform the server so we get new gamestate info
 +		CL_AddReliableCommand( "donedl" );
 +
 +		// by sending the donedl command we request a new gamestate
 +		// so we don't want to load stuff yet
 +		return;
 +	}
 +
 +	// let the client game init and load data
 +	cls.state = CA_LOADING;
 +
 +	// Pump the loop, this may change gamestate!
 +	Com_EventLoop();
 +
 +	// if the gamestate was changed by calling Com_EventLoop
 +	// then we loaded everything already and we don't want to do it again.
 +	if ( cls.state != CA_LOADING ) {
 +		return;
 +	}
 +
 +	// starting to load a map so we get out of full screen ui mode
 +	Cvar_Set("r_uiFullScreen", "0");
 +
 +	// flush client memory and start loading stuff
 +	// this will also (re)load the UI
 +	// if this is a local client then only the client part of the hunk
 +	// will be cleared, note that this is done after the hunk mark has been set
 +	CL_FlushMemory();
 +
 +	// initialize the CGame
 +	cls.cgameStarted = qtrue;
 +	CL_InitCGame();
 +
 +	// set pure checksums
 +	CL_SendPureChecksums();
 +
 +	CL_WritePacket();
 +	CL_WritePacket();
 +	CL_WritePacket();
 +}
 +
 +/*
 +=================
 +CL_BeginDownload
 +
 +Requests a file to download from the server.  Stores it in the current
 +game directory.
 +=================
 +*/
 +void CL_BeginDownload( const char *localName, const char *remoteName ) {
 +
 +	Com_DPrintf("***** CL_BeginDownload *****\n"
 +				"Localname: %s\n"
 +				"Remotename: %s\n"
 +				"****************************\n", localName, remoteName);
 +
 +	Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) );
 +	Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName );
 +
 +	// Set so UI gets access to it
 +	Cvar_Set( "cl_downloadName", remoteName );
 +	Cvar_Set( "cl_downloadSize", "0" );
 +	Cvar_Set( "cl_downloadCount", "0" );
 +	Cvar_SetValue( "cl_downloadTime", cls.realtime );
 +
 +	clc.downloadBlock = 0; // Starting new file
 +	clc.downloadCount = 0;
 +
 +	CL_AddReliableCommand( va("download %s", remoteName) );
 +}
 +
 +/*
 +=================
 +CL_NextDownload
 +
 +A download completed or failed
 +=================
 +*/
 +void CL_NextDownload(void) {
 +	char *s;
 +	char *remoteName, *localName;
 +
 +	// We are looking to start a download here
 +	if (*clc.downloadList) {
 +		s = clc.downloadList;
 +
 +		// format is:
 +		//  @remotename@localname@remotename@localname, etc.
 +
 +		if (*s == '@')
 +			s++;
 +		remoteName = s;
 +		
 +		if ( (s = strchr(s, '@')) == NULL ) {
 +			CL_DownloadsComplete();
 +			return;
 +		}
 +
 +		*s++ = 0;
 +		localName = s;
 +		if ( (s = strchr(s, '@')) != NULL )
 +			*s++ = 0;
 +		else
 +			s = localName + strlen(localName); // point at the nul byte
 +
 +		CL_BeginDownload( localName, remoteName );
 +
 +		clc.downloadRestart = qtrue;
 +
 +		// move over the rest
 +		memmove( clc.downloadList, s, strlen(s) + 1);
 +
 +		return;
 +	}
 +
 +	CL_DownloadsComplete();
 +}
 +
 +/*
 +=================
 +CL_InitDownloads
 +
 +After receiving a valid game state, we valid the cgame and local zip files here
 +and determine if we need to download them
 +=================
 +*/
 +void CL_InitDownloads(void) {
 +  char missingfiles[1024];
 +
 +  if ( !cl_allowDownload->integer )
 +  {
 +    // autodownload is disabled on the client
 +    // but it's possible that some referenced files on the server are missing
 +    if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) )
 +    {      
 +      // NOTE TTimo I would rather have that printed as a modal message box
 +      //   but at this point while joining the game we don't know wether we will successfully join or not
 +      Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
 +                  "You might not be able to join the game\n"
 +                  "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
 +    }
 +  }
 +  else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) {
 +
 +    Com_Printf("Need paks: %s\n", clc.downloadList );
 +
 +		if ( *clc.downloadList ) {
 +			// if autodownloading is not enabled on the server
 +			cls.state = CA_CONNECTED;
 +			CL_NextDownload();
 +			return;
 +		}
 +
 +	}
 +		
 +	CL_DownloadsComplete();
 +}
 +
 +/*
 +=================
 +CL_CheckForResend
 +
 +Resend a connect message if the last one has timed out
 +=================
 +*/
 +void CL_CheckForResend( void ) {
 +	int		port, i;
 +	char	info[MAX_INFO_STRING];
 +	char	data[MAX_INFO_STRING];
 +
 +	// don't send anything if playing back a demo
 +	if ( clc.demoplaying ) {
 +		return;
 +	}
 +
 +	// resend if we haven't gotten a reply yet
 +	if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) {
 +		return;
 +	}
 +
 +	if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
 +		return;
 +	}
 +
 +	clc.connectTime = cls.realtime;	// for retransmit requests
 +	clc.connectPacketCount++;
 +
 +
 +	switch ( cls.state ) {
 +	case CA_CONNECTING:
 +		// requesting a challenge
 +		if ( !Sys_IsLANAddress( clc.serverAddress ) ) {
 +			CL_RequestAuthorization();
 +		}
 +		NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge");
 +		break;
 +		
 +	case CA_CHALLENGING:
 +		// sending back the challenge
 +		port = Cvar_VariableValue ("net_qport");
 +
 +		Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
 +		Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) );
 +		Info_SetValueForKey( info, "qport", va("%i", port ) );
 +		Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
 +		
 +		strcpy(data, "connect ");
 +    // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server
 +    //   (Com_TokenizeString tokenizes around spaces)
 +    data[8] = '"';
 +
 +		for(i=0;i<strlen(info);i++) {
 +			data[9+i] = info[i];	// + (clc.challenge)&0x3;
 +		}
 +    data[9+i] = '"';
 +		data[10+i] = 0;
 +
 +    // NOTE TTimo don't forget to set the right data length!
 +		NET_OutOfBandData( NS_CLIENT, clc.serverAddress, &data[0], i+10 );
 +		// the most current userinfo has been sent, so watch for any
 +		// newer changes to userinfo variables
 +		cvar_modifiedFlags &= ~CVAR_USERINFO;
 +		break;
 +
 +	default:
 +		Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" );
 +	}
 +}
 +
 +/*
 +===================
 +CL_DisconnectPacket
 +
 +Sometimes the server can drop the client and the netchan based
 +disconnect can be lost.  If the client continues to send packets
 +to the server, the server will send out of band disconnect packets
 +to the client so it doesn't have to wait for the full timeout period.
 +===================
 +*/
 +void CL_DisconnectPacket( netadr_t from ) {
 +	if ( cls.state < CA_AUTHORIZING ) {
 +		return;
 +	}
 +
 +	// if not from our server, ignore it
 +	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
 +		return;
 +	}
 +
 +	// if we have received packets within three seconds, ignore it
 +	// (it might be a malicious spoof)
 +	if ( cls.realtime - clc.lastPacketTime < 3000 ) {
 +		return;
 +	}
 +
 +	// drop the connection
 +	Com_Printf( "Server disconnected for unknown reason\n" );
 +	Cvar_Set("com_errorMessage", "Server disconnected for unknown reason\n" );
 +	CL_Disconnect( qtrue );
 +}
 +
 +
 +/*
 +===================
 +CL_MotdPacket
 +
 +===================
 +*/
 +void CL_MotdPacket( netadr_t from ) {
 +	char	*challenge;
 +	char	*info;
 +
 +	// if not from our server, ignore it
 +	if ( !NET_CompareAdr( from, cls.updateServer ) ) {
 +		return;
 +	}
 +
 +	info = Cmd_Argv(1);
 +
 +	// check challenge
 +	challenge = Info_ValueForKey( info, "challenge" );
 +	if ( strcmp( challenge, cls.updateChallenge ) ) {
 +		return;
 +	}
 +
 +	challenge = Info_ValueForKey( info, "motd" );
 +
 +	Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) );
 +	Cvar_Set( "cl_motdString", challenge );
 +}
 +
 +/*
 +===================
 +CL_InitServerInfo
 +===================
 +*/
 +void CL_InitServerInfo( serverInfo_t *server, serverAddress_t *address ) {
 +	server->adr.type  = NA_IP;
 +	server->adr.ip[0] = address->ip[0];
 +	server->adr.ip[1] = address->ip[1];
 +	server->adr.ip[2] = address->ip[2];
 +	server->adr.ip[3] = address->ip[3];
 +	server->adr.port  = address->port;
 +	server->clients = 0;
 +	server->hostName[0] = '\0';
 +	server->mapName[0] = '\0';
 +	server->maxClients = 0;
 +	server->maxPing = 0;
 +	server->minPing = 0;
 +	server->ping = -1;
 +	server->game[0] = '\0';
 +	server->gameType = 0;
 +	server->netType = 0;
 +}
 +
 +#define MAX_SERVERSPERPACKET	256
 +
 +/*
 +===================
 +CL_ServersResponsePacket
 +===================
 +*/
 +void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) {
 +	int				i, count, max, total;
 +	serverAddress_t addresses[MAX_SERVERSPERPACKET];
 +	int				numservers;
 +	byte*			buffptr;
 +	byte*			buffend;
 +	
 +	Com_Printf("CL_ServersResponsePacket\n");
 +
 +	if (cls.numglobalservers == -1) {
 +		// state to detect lack of servers or lack of response
 +		cls.numglobalservers = 0;
 +		cls.numGlobalServerAddresses = 0;
 +	}
 +
 +	if (cls.nummplayerservers == -1) {
 +		cls.nummplayerservers = 0;
 +	}
 +
 +	// parse through server response string
 +	numservers = 0;
 +	buffptr    = msg->data;
 +	buffend    = buffptr + msg->cursize;
 +	while (buffptr+1 < buffend) {
 +		// advance to initial token
 +		do {
 +			if (*buffptr++ == '\\')
 +				break;		
 +		}
 +		while (buffptr < buffend);
 +
 +		if ( buffptr >= buffend - 6 ) {
 +			break;
 +		}
 +
 +		// parse out ip
 +		addresses[numservers].ip[0] = *buffptr++;
 +		addresses[numservers].ip[1] = *buffptr++;
 +		addresses[numservers].ip[2] = *buffptr++;
 +		addresses[numservers].ip[3] = *buffptr++;
 +
 +		// parse out port
 +		addresses[numservers].port = (*buffptr++)<<8;
 +		addresses[numservers].port += *buffptr++;
 +		addresses[numservers].port = BigShort( addresses[numservers].port );
 +
 +		// syntax check
 +		if (*buffptr != '\\') {
 +			break;
 +		}
 +
 +		Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers,
 +				addresses[numservers].ip[0],
 +				addresses[numservers].ip[1],
 +				addresses[numservers].ip[2],
 +				addresses[numservers].ip[3],
 +				addresses[numservers].port );
 +
 +		numservers++;
 +		if (numservers >= MAX_SERVERSPERPACKET) {
 +			break;
 +		}
 +
 +		// parse out EOT
 +		if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') {
 +			break;
 +		}
 +	}
 +
 +	if (cls.masterNum == 0) {
 +		count = cls.numglobalservers;
 +		max = MAX_GLOBAL_SERVERS;
 +	} else {
 +		count = cls.nummplayerservers;
 +		max = MAX_OTHER_SERVERS;
 +	}
 +
 +	for (i = 0; i < numservers && count < max; i++) {
 +		// build net address
 +		serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count];
 +
 +		CL_InitServerInfo( server, &addresses[i] );
 +		// advance to next slot
 +		count++;
 +	}
 +
 +	// if getting the global list
 +	if (cls.masterNum == 0) {
 +		if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) {
 +			// if we couldn't store the servers in the main list anymore
 +			for (; i < numservers && count >= max; i++) {
 +				serverAddress_t *addr;
 +				// just store the addresses in an additional list
 +				addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++];
 +				addr->ip[0] = addresses[i].ip[0];
 +				addr->ip[1] = addresses[i].ip[1];
 +				addr->ip[2] = addresses[i].ip[2];
 +				addr->ip[3] = addresses[i].ip[3];
 +				addr->port  = addresses[i].port;
 +			}
 +		}
 +	}
 +
 +	if (cls.masterNum == 0) {
 +		cls.numglobalservers = count;
 +		total = count + cls.numGlobalServerAddresses;
 +	} else {
 +		cls.nummplayerservers = count;
 +		total = count;
 +	}
 +
 +	Com_Printf("%d servers parsed (total %d)\n", numservers, total);
 +}
 +
 +/*
 +=================
 +CL_ConnectionlessPacket
 +
 +Responses to broadcasts, etc
 +=================
 +*/
 +void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
 +	char	*s;
 +	char	*c;
 +
 +	MSG_BeginReadingOOB( msg );
 +	MSG_ReadLong( msg );	// skip the -1
 +
 +	s = MSG_ReadStringLine( msg );
 +
 +	Cmd_TokenizeString( s );
 +
 +	c = Cmd_Argv(0);
 +
 +	Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c);
 +
 +	// challenge from the server we are connecting to
 +	if ( !Q_stricmp(c, "challengeResponse") ) {
 +		if ( cls.state != CA_CONNECTING ) {
 +			Com_Printf( "Unwanted challenge response received.  Ignored.\n" );
 +		} else {
 +			// start sending challenge repsonse instead of challenge request packets
 +			clc.challenge = atoi(Cmd_Argv(1));
 +			cls.state = CA_CHALLENGING;
 +			clc.connectPacketCount = 0;
 +			clc.connectTime = -99999;
 +
 +			// take this address as the new server address.  This allows
 +			// a server proxy to hand off connections to multiple servers
 +			clc.serverAddress = from;
 +			Com_DPrintf ("challengeResponse: %d\n", clc.challenge);
 +		}
 +		return;
 +	}
 +
 +	// server connection
 +	if ( !Q_stricmp(c, "connectResponse") ) {
 +		if ( cls.state >= CA_CONNECTED ) {
 +			Com_Printf ("Dup connect received.  Ignored.\n");
 +			return;
 +		}
 +		if ( cls.state != CA_CHALLENGING ) {
 +			Com_Printf ("connectResponse packet while not connecting.  Ignored.\n");
 +			return;
 +		}
 +		if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) {
 +			Com_Printf( "connectResponse from a different address.  Ignored.\n" );
 +			Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), 
 +				NET_AdrToString( clc.serverAddress ) );
 +			return;
 +		}
 +		Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
 +		cls.state = CA_CONNECTED;
 +		clc.lastPacketSentTime = -9999;		// send first packet immediately
 +		return;
 +	}
 +
 +	// server responding to an info broadcast
 +	if ( !Q_stricmp(c, "infoResponse") ) {
 +		CL_ServerInfoPacket( from, msg );
 +		return;
 +	}
 +
 +	// server responding to a get playerlist
 +	if ( !Q_stricmp(c, "statusResponse") ) {
 +		CL_ServerStatusResponse( from, msg );
 +		return;
 +	}
 +
 +	// a disconnect message from the server, which will happen if the server
 +	// dropped the connection but it is still getting packets from us
 +	if (!Q_stricmp(c, "disconnect")) {
 +		CL_DisconnectPacket( from );
 +		return;
 +	}
 +
 +	// echo request from server
 +	if ( !Q_stricmp(c, "echo") ) {
 +		NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
 +		return;
 +	}
 +
 +	// cd check
 +	if ( !Q_stricmp(c, "keyAuthorize") ) {
 +		// we don't use these now, so dump them on the floor
 +		return;
 +	}
 +
 +	// global MOTD from id
 +	if ( !Q_stricmp(c, "motd") ) {
 +		CL_MotdPacket( from );
 +		return;
 +	}
 +
 +	// echo request from server
 +	if ( !Q_stricmp(c, "print") ) {
 +		s = MSG_ReadString( msg );
 +		Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
 +		Com_Printf( "%s", s );
 +		return;
 +	}
 +
 +	// echo request from server
 +	if ( !Q_strncmp(c, "getserversResponse", 18) ) {
 +		CL_ServersResponsePacket( from, msg );
 +		return;
 +	}
 +
 +	Com_DPrintf ("Unknown connectionless packet command.\n");
 +}
 +
 +
 +/*
 +=================
 +CL_PacketEvent
 +
 +A packet has arrived from the main event loop
 +=================
 +*/
 +void CL_PacketEvent( netadr_t from, msg_t *msg ) {
 +	int		headerBytes;
 +
 +	clc.lastPacketTime = cls.realtime;
 +
 +	if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
 +		CL_ConnectionlessPacket( from, msg );
 +		return;
 +	}
 +
 +	if ( cls.state < CA_CONNECTED ) {
 +		return;		// can't be a valid sequenced packet
 +	}
 +
 +	if ( msg->cursize < 4 ) {
 +		Com_Printf ("%s: Runt packet\n",NET_AdrToString( from ));
 +		return;
 +	}
 +
 +	//
 +	// packet from server
 +	//
 +	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
 +		Com_DPrintf ("%s:sequenced packet without connection\n"
 +			,NET_AdrToString( from ) );
 +		// FIXME: send a client disconnect?
 +		return;
 +	}
 +
 +	if (!CL_Netchan_Process( &clc.netchan, msg) ) {
 +		return;		// out of order, duplicated, etc
 +	}
 +
 +	// the header is different lengths for reliable and unreliable messages
 +	headerBytes = msg->readcount;
 +
 +	// track the last message received so it can be returned in 
 +	// client messages, allowing the server to detect a dropped
 +	// gamestate
 +	clc.serverMessageSequence = LittleLong( *(int *)msg->data );
 +
 +	clc.lastPacketTime = cls.realtime;
 +	CL_ParseServerMessage( msg );
 +
 +	//
 +	// we don't know if it is ok to save a demo message until
 +	// after we have parsed the frame
 +	//
 +	if ( clc.demorecording && !clc.demowaiting ) {
 +		CL_WriteDemoMessage( msg, headerBytes );
 +	}
 +}
 +
 +/*
 +==================
 +CL_CheckTimeout
 +
 +==================
 +*/
 +void CL_CheckTimeout( void ) {
 +	//
 +	// check timeout
 +	//
 +	if ( ( !cl_paused->integer || !sv_paused->integer ) 
 +		&& cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC
 +	    && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) {
 +		if (++cl.timeoutcount > 5) {	// timeoutcount saves debugger
 +			Com_Printf ("\nServer connection timed out.\n");
 +			CL_Disconnect( qtrue );
 +			return;
 +		}
 +	} else {
 +		cl.timeoutcount = 0;
 +	}
 +}
 +
 +
 +//============================================================================
 +
 +/*
 +==================
 +CL_CheckUserinfo
 +
 +==================
 +*/
 +void CL_CheckUserinfo( void ) {
 +	// don't add reliable commands when not yet connected
 +	if ( cls.state < CA_CHALLENGING ) {
 +		return;
 +	}
 +	// don't overflow the reliable command buffer when paused
 +	if ( cl_paused->integer ) {
 +		return;
 +	}
 +	// send a reliable userinfo update if needed
 +	if ( cvar_modifiedFlags & CVAR_USERINFO ) {
 +		cvar_modifiedFlags &= ~CVAR_USERINFO;
 +		CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) );
 +	}
 +
 +}
 +
 +/*
 +==================
 +CL_Frame
 +
 +==================
 +*/
 +void CL_Frame ( int msec ) {
 +
 +	if ( !com_cl_running->integer ) {
 +		return;
 +	}
 +
 +	if ( cls.cddialog ) {
 +		// bring up the cd error dialog if needed
 +		cls.cddialog = qfalse;
 +		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD );
 +	} else	if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI )
 +		&& !com_sv_running->integer ) {
 +		// if disconnected, bring up the menu
 +		S_StopAllSounds();
 +		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
 +	}
 +
 +	// if recording an avi, lock to a fixed fps
 +	if ( cl_avidemo->integer && msec) {
 +		// save the current screen
 +		if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) {
 +			Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" );
 +		}
 +		// fixed time for next frame'
 +		msec = (1000 / cl_avidemo->integer) * com_timescale->value;
 +		if (msec == 0) {
 +			msec = 1;
 +		}
 +	}
 +	
 +	// save the msec before checking pause
 +	cls.realFrametime = msec;
 +
 +	// decide the simulation time
 +	cls.frametime = msec;
 +
 +	cls.realtime += cls.frametime;
 +
 +	if ( cl_timegraph->integer ) {
 +		SCR_DebugGraph ( cls.realFrametime * 0.25, 0 );
 +	}
 +
 +	// see if we need to update any userinfo
 +	CL_CheckUserinfo();
 +
 +	// if we haven't gotten a packet in a long time,
 +	// drop the connection
 +	CL_CheckTimeout();
 +
 +	// send intentions now
 +	CL_SendCmd();
 +
 +	// resend a connection request if necessary
 +	CL_CheckForResend();
 +
 +	// decide on the serverTime to render
 +	CL_SetCGameTime();
 +
 +	// update the screen
 +	SCR_UpdateScreen();
 +
 +	// update audio
 +	S_Update();
 +
 +	// advance local effects for next frame
 +	SCR_RunCinematic();
 +
 +	Con_RunConsole();
 +
 +	cls.framecount++;
 +}
 +
 +
 +//============================================================================
 +
 +/*
 +================
 +CL_RefPrintf
 +
 +DLL glue
 +================
 +*/
 +void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) {
 +	va_list		argptr;
 +	char		msg[MAXPRINTMSG];
 +	
 +	va_start (argptr,fmt);
 +	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
 +	va_end (argptr);
 +
 +	if ( print_level == PRINT_ALL ) {
 +		Com_Printf ("%s", msg);
 +	} else if ( print_level == PRINT_WARNING ) {
 +		Com_Printf (S_COLOR_YELLOW "%s", msg);		// yellow
 +	} else if ( print_level == PRINT_DEVELOPER ) {
 +		Com_DPrintf (S_COLOR_RED "%s", msg);		// red
 +	}
 +}
 +
 +
 +
 +/*
 +============
 +CL_ShutdownRef
 +============
 +*/
 +void CL_ShutdownRef( void ) {
 +	if ( !re.Shutdown ) {
 +		return;
 +	}
 +	re.Shutdown( qtrue );
 +	Com_Memset( &re, 0, sizeof( re ) );
 +}
 +
 +/*
 +============
 +CL_InitRenderer
 +============
 +*/
 +void CL_InitRenderer( void ) {
 +	// this sets up the renderer and calls R_Init
 +	re.BeginRegistration( &cls.glconfig );
 +
 +	// load character sets
 +	cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" );
 +	cls.whiteShader = re.RegisterShader( "white" );
 +	cls.consoleShader = re.RegisterShader( "console" );
 +	g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2;
 +	g_consoleField.widthInChars = g_console_field_width;
 +}
 +
 +/*
 +============================
 +CL_StartHunkUsers
 +
 +After the server has cleared the hunk, these will need to be restarted
 +This is the only place that any of these functions are called from
 +============================
 +*/
 +void CL_StartHunkUsers( void ) {
 +	if (!com_cl_running) {
 +		return;
 +	}
 +
 +	if ( !com_cl_running->integer ) {
 +		return;
 +	}
 +
 +	if ( !cls.rendererStarted ) {
 +		cls.rendererStarted = qtrue;
 +		CL_InitRenderer();
 +	}
 +
 +	if ( !cls.soundStarted ) {
 +		cls.soundStarted = qtrue;
 +		S_Init();
 +	}
 +
 +	if ( !cls.soundRegistered ) {
 +		cls.soundRegistered = qtrue;
 +		S_BeginRegistration();
 +	}
 +
 +	if ( !cls.uiStarted ) {
 +		cls.uiStarted = qtrue;
 +		CL_InitUI();
 +	}
 +}
 +
 +/*
 +============
 +CL_RefMalloc
 +============
 +*/
 +void *CL_RefMalloc( int size ) {
 +	return Z_TagMalloc( size, TAG_RENDERER );
 +}
 +
 +int CL_ScaledMilliseconds(void) {
 +	return Sys_Milliseconds()*com_timescale->value;
 +}
 +
 +/*
 +============
 +CL_InitRef
 +============
 +*/
 +void CL_InitRef( void ) {
 +	refimport_t	ri;
 +	refexport_t	*ret;
 +
 +	Com_Printf( "----- Initializing Renderer ----\n" );
 +
 +	ri.Cmd_AddCommand = Cmd_AddCommand;
 +	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
 +	ri.Cmd_Argc = Cmd_Argc;
 +	ri.Cmd_Argv = Cmd_Argv;
 +	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
 +	ri.Printf = CL_RefPrintf;
 +	ri.Error = Com_Error;
 +	ri.Milliseconds = CL_ScaledMilliseconds;
 +	ri.Malloc = CL_RefMalloc;
 +	ri.Free = Z_Free;
 +#ifdef HUNK_DEBUG
 +	ri.Hunk_AllocDebug = Hunk_AllocDebug;
 +#else
 +	ri.Hunk_Alloc = Hunk_Alloc;
 +#endif
 +	ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
 +	ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
 +	ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
 +	ri.FS_ReadFile = FS_ReadFile;
 +	ri.FS_FreeFile = FS_FreeFile;
 +	ri.FS_WriteFile = FS_WriteFile;
 +	ri.FS_FreeFileList = FS_FreeFileList;
 +	ri.FS_ListFiles = FS_ListFiles;
 +	ri.FS_FileIsInPAK = FS_FileIsInPAK;
 +	ri.FS_FileExists = FS_FileExists;
 +	ri.Cvar_Get = Cvar_Get;
 +	ri.Cvar_Set = Cvar_Set;
 +
 +	// cinematic stuff
 +
 +	ri.CIN_UploadCinematic = CIN_UploadCinematic;
 +	ri.CIN_PlayCinematic = CIN_PlayCinematic;
 +	ri.CIN_RunCinematic = CIN_RunCinematic;
 +
 +	ret = GetRefAPI( REF_API_VERSION, &ri );
 +
 +#if defined __USEA3D && defined __A3D_GEOM
 +	hA3Dg_ExportRenderGeom (ret);
 +#endif
 +
 +	Com_Printf( "-------------------------------\n");
 +
 +	if ( !ret ) {
 +		Com_Error (ERR_FATAL, "Couldn't initialize refresh" );
 +	}
 +
 +	re = *ret;
 +
 +	// unpause so the cgame definately gets a snapshot and renders a frame
 +	Cvar_Set( "cl_paused", "0" );
 +}
 +
 +
 +//===========================================================================================
 +
 +
 +void CL_SetModel_f( void ) {
 +	char	*arg;
 +	char	name[256];
 +
 +	arg = Cmd_Argv( 1 );
 +	if (arg[0]) {
 +		Cvar_Set( "model", arg );
 +		Cvar_Set( "headmodel", arg );
 +	} else {
 +		Cvar_VariableStringBuffer( "model", name, sizeof(name) );
 +		Com_Printf("model is set to %s\n", name);
 +	}
 +}
 +
 +/*
 +====================
 +CL_Init
 +====================
 +*/
 +void CL_Init( void ) {
 +	Com_Printf( "----- Client Initialization -----\n" );
 +
 +	Con_Init ();	
 +
 +	CL_ClearState ();
 +
 +	cls.state = CA_DISCONNECTED;	// no longer CA_UNINITIALIZED
 +
 +	cls.realtime = 0;
 +
 +	CL_InitInput ();
 +
 +	//
 +	// register our variables
 +	//
 +	cl_noprint = Cvar_Get( "cl_noprint", "0", 0 );
 +	cl_motd = Cvar_Get ("cl_motd", "1", 0);
 +
 +	cl_timeout = Cvar_Get ("cl_timeout", "200", 0);
 +
 +	cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP );
 +	cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP );
 +	cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP );
 +	cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP );
 +	cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP );
 +	rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP );
 +	cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP );
 +
 +	cl_timedemo = Cvar_Get ("timedemo", "0", 0);
 +	cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0);
 +	cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0);
 +
 +	rconAddress = Cvar_Get ("rconAddress", "", 0);
 +
 +	cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE);
 +	cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE);
 +	cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
 +
 +	cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE );
 +	cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE );
 +
 +	cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE);
 +	cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE);
 +	cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE);
 +	cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE );
 +
 +	cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0);
 +
 +	cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE);
 +
 +	cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0);
 +#ifdef MACOS_X
 +        // In game video is REALLY slow in Mac OS X right now due to driver slowness
 +	cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE);
 +#else
 +	cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE);
 +#endif
 +
 +	cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0);
 +
 +	// init autoswitch so the ui will have it correctly even
 +	// if the cgame hasn't been started
 +	Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE);
 +
 +	m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
 +	m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE);
 +	m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE);
 +	m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE);
 +#ifdef MACOS_X
 +        // Input is jittery on OS X w/o this
 +	m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE);
 +#else
 +	m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE);
 +#endif
 +
 +	cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM );
 +
 +	Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE );
 +
 +
 +	// userinfo
 +	Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE);
 +	Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE);
 +	Cvar_Get ("color1",  "4", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("teamtask", "0", CVAR_USERINFO );
 +	Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE );
 +	Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE );
 +
 +	Cvar_Get ("password", "", CVAR_USERINFO);
 +	Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE );
 +
 +
 +	// cgame might not be initialized before menu is used
 +	Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE );
 +
 +	//
 +	// register our commands
 +	//
 +	Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
 +	Cmd_AddCommand ("configstrings", CL_Configstrings_f);
 +	Cmd_AddCommand ("clientinfo", CL_Clientinfo_f);
 +	Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
 +	Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f);
 +	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
 +	Cmd_AddCommand ("record", CL_Record_f);
 +	Cmd_AddCommand ("demo", CL_PlayDemo_f);
 +	Cmd_AddCommand ("cinematic", CL_PlayCinematic_f);
 +	Cmd_AddCommand ("stoprecord", CL_StopRecord_f);
 +	Cmd_AddCommand ("connect", CL_Connect_f);
 +	Cmd_AddCommand ("reconnect", CL_Reconnect_f);
 +	Cmd_AddCommand ("localservers", CL_LocalServers_f);
 +	Cmd_AddCommand ("globalservers", CL_GlobalServers_f);
 +	Cmd_AddCommand ("rcon", CL_Rcon_f);
 +	Cmd_AddCommand ("setenv", CL_Setenv_f );
 +	Cmd_AddCommand ("ping", CL_Ping_f );
 +	Cmd_AddCommand ("serverstatus", CL_ServerStatus_f );
 +	Cmd_AddCommand ("showip", CL_ShowIP_f );
 +	Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f );
 +	Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f );
 +	Cmd_AddCommand ("model", CL_SetModel_f );
 +	CL_InitRef();
 +
 +	SCR_Init ();
 +
 +	Cbuf_Execute ();
 +
 +	Cvar_Set( "cl_running", "1" );
 +
 +	Com_Printf( "----- Client Initialization Complete -----\n" );
 +}
 +
 +
 +/*
 +===============
 +CL_Shutdown
 +
 +===============
 +*/
 +void CL_Shutdown( void ) {
 +	static qboolean recursive = qfalse;
 +	
 +	Com_Printf( "----- CL_Shutdown -----\n" );
 +
 +	if ( recursive ) {
 +		printf ("recursive shutdown\n");
 +		return;
 +	}
 +	recursive = qtrue;
 +
 +	CL_Disconnect( qtrue );
 +
 +	S_Shutdown();
 +	CL_ShutdownRef();
 +	
 +	CL_ShutdownUI();
 +
 +	Cmd_RemoveCommand ("cmd");
 +	Cmd_RemoveCommand ("configstrings");
 +	Cmd_RemoveCommand ("userinfo");
 +	Cmd_RemoveCommand ("snd_restart");
 +	Cmd_RemoveCommand ("vid_restart");
 +	Cmd_RemoveCommand ("disconnect");
 +	Cmd_RemoveCommand ("record");
 +	Cmd_RemoveCommand ("demo");
 +	Cmd_RemoveCommand ("cinematic");
 +	Cmd_RemoveCommand ("stoprecord");
 +	Cmd_RemoveCommand ("connect");
 +	Cmd_RemoveCommand ("localservers");
 +	Cmd_RemoveCommand ("globalservers");
 +	Cmd_RemoveCommand ("rcon");
 +	Cmd_RemoveCommand ("setenv");
 +	Cmd_RemoveCommand ("ping");
 +	Cmd_RemoveCommand ("serverstatus");
 +	Cmd_RemoveCommand ("showip");
 +	Cmd_RemoveCommand ("model");
 +
 +	Cvar_Set( "cl_running", "0" );
 +
 +	recursive = qfalse;
 +
 +	Com_Memset( &cls, 0, sizeof( cls ) );
 +
 +	Com_Printf( "-----------------------\n" );
 +
 +}
 +
 +static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) {
 +	if (server) {
 +		if (info) {
 +			server->clients = atoi(Info_ValueForKey(info, "clients"));
 +			Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH);
 +			Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH);
 +			server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
 +			Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH);
 +			server->gameType = atoi(Info_ValueForKey(info, "gametype"));
 +			server->netType = atoi(Info_ValueForKey(info, "nettype"));
 +			server->minPing = atoi(Info_ValueForKey(info, "minping"));
 +			server->maxPing = atoi(Info_ValueForKey(info, "maxping"));
 +			server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster"));
 +		}
 +		server->ping = ping;
 +	}
 +}
 +
 +static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) {
 +	int i;
 +
 +	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
 +		if (NET_CompareAdr(from, cls.localServers[i].adr)) {
 +			CL_SetServerInfo(&cls.localServers[i], info, ping);
 +		}
 +	}
 +
 +	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
 +		if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) {
 +			CL_SetServerInfo(&cls.mplayerServers[i], info, ping);
 +		}
 +	}
 +
 +	for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
 +		if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
 +			CL_SetServerInfo(&cls.globalServers[i], info, ping);
 +		}
 +	}
 +
 +	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
 +		if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) {
 +			CL_SetServerInfo(&cls.favoriteServers[i], info, ping);
 +		}
 +	}
 +
 +}
 +
 +/*
 +===================
 +CL_ServerInfoPacket
 +===================
 +*/
 +void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
 +	int		i, type;
 +	char	info[MAX_INFO_STRING];
 +	char*	str;
 +	char	*infoString;
 +	int		prot;
 +
 +	infoString = MSG_ReadString( msg );
 +
 +	// if this isn't the correct protocol version, ignore it
 +	prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
 +	if ( prot != PROTOCOL_VERSION ) {
 +		Com_DPrintf( "Different protocol info packet: %s\n", infoString );
 +		return;
 +	}
 +
 +	// iterate servers waiting for ping response
 +	for (i=0; i<MAX_PINGREQUESTS; i++)
 +	{
 +		if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) )
 +		{
 +			// calc ping time
 +			cl_pinglist[i].time = cls.realtime - cl_pinglist[i].start + 1;
 +			Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
 +
 +			// save of info
 +			Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
 +
 +			// tack on the net type
 +			// NOTE: make sure these types are in sync with the netnames strings in the UI
 +			switch (from.type)
 +			{
 +				case NA_BROADCAST:
 +				case NA_IP:
 +					str = "udp";
 +					type = 1;
 +					break;
 +
 +				case NA_IPX:
 +				case NA_BROADCAST_IPX:
 +					str = "ipx";
 +					type = 2;
 +					break;
 +
 +				default:
 +					str = "???";
 +					type = 0;
 +					break;
 +			}
 +			Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) );
 +			CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time);
 +
 +			return;
 +		}
 +	}
 +
 +	// if not just sent a local broadcast or pinging local servers
 +	if (cls.pingUpdateSource != AS_LOCAL) {
 +		return;
 +	}
 +
 +	for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
 +		// empty slot
 +		if ( cls.localServers[i].adr.port == 0 ) {
 +			break;
 +		}
 +
 +		// avoid duplicate
 +		if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
 +			return;
 +		}
 +	}
 +
 +	if ( i == MAX_OTHER_SERVERS ) {
 +		Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
 +		return;
 +	}
 +
 +	// add this to the list
 +	cls.numlocalservers = i+1;
 +	cls.localServers[i].adr = from;
 +	cls.localServers[i].clients = 0;
 +	cls.localServers[i].hostName[0] = '\0';
 +	cls.localServers[i].mapName[0] = '\0';
 +	cls.localServers[i].maxClients = 0;
 +	cls.localServers[i].maxPing = 0;
 +	cls.localServers[i].minPing = 0;
 +	cls.localServers[i].ping = -1;
 +	cls.localServers[i].game[0] = '\0';
 +	cls.localServers[i].gameType = 0;
 +	cls.localServers[i].netType = from.type;
 +	cls.localServers[i].punkbuster = 0;
 +									 
 +	Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
 +	if (strlen(info)) {
 +		if (info[strlen(info)-1] != '\n') {
 +			strncat(info, "\n", sizeof(info));
 +		}
 +		Com_Printf( "%s: %s", NET_AdrToString( from ), info );
 +	}
 +}
 +
 +/*
 +===================
 +CL_GetServerStatus
 +===================
 +*/
 +serverStatus_t *CL_GetServerStatus( netadr_t from ) {
 +	serverStatus_t *serverStatus;
 +	int i, oldest, oldestTime;
 +
 +	serverStatus = NULL;
 +	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
 +			return &cl_serverStatusList[i];
 +		}
 +	}
 +	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +		if ( cl_serverStatusList[i].retrieved ) {
 +			return &cl_serverStatusList[i];
 +		}
 +	}
 +	oldest = -1;
 +	oldestTime = 0;
 +	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +		if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) {
 +			oldest = i;
 +			oldestTime = cl_serverStatusList[i].startTime;
 +		}
 +	}
 +	if (oldest != -1) {
 +		return &cl_serverStatusList[oldest];
 +	}
 +	serverStatusCount++;
 +	return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
 +}
 +
 +/*
 +===================
 +CL_ServerStatus
 +===================
 +*/
 +int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) {
 +	int i;
 +	netadr_t	to;
 +	serverStatus_t *serverStatus;
 +
 +	// if no server address then reset all server status requests
 +	if ( !serverAddress ) {
 +		for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +			cl_serverStatusList[i].address.port = 0;
 +			cl_serverStatusList[i].retrieved = qtrue;
 +		}
 +		return qfalse;
 +	}
 +	// get the address
 +	if ( !NET_StringToAdr( serverAddress, &to ) ) {
 +		return qfalse;
 +	}
 +	serverStatus = CL_GetServerStatus( to );
 +	// if no server status string then reset the server status request for this address
 +	if ( !serverStatusString ) {
 +		serverStatus->retrieved = qtrue;
 +		return qfalse;
 +	}
 +
 +	// if this server status request has the same address
 +	if ( NET_CompareAdr( to, serverStatus->address) ) {
 +		// if we recieved an response for this server status request
 +		if (!serverStatus->pending) {
 +			Q_strncpyz(serverStatusString, serverStatus->string, maxLen);
 +			serverStatus->retrieved = qtrue;
 +			serverStatus->startTime = 0;
 +			return qtrue;
 +		}
 +		// resend the request regularly
 +		else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
 +			serverStatus->print = qfalse;
 +			serverStatus->pending = qtrue;
 +			serverStatus->retrieved = qfalse;
 +			serverStatus->time = 0;
 +			serverStatus->startTime = Com_Milliseconds();
 +			NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
 +			return qfalse;
 +		}
 +	}
 +	// if retrieved
 +	else if ( serverStatus->retrieved ) {
 +		serverStatus->address = to;
 +		serverStatus->print = qfalse;
 +		serverStatus->pending = qtrue;
 +		serverStatus->retrieved = qfalse;
 +		serverStatus->startTime = Com_Milliseconds();
 +		serverStatus->time = 0;
 +		NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
 +		return qfalse;
 +	}
 +	return qfalse;
 +}
 +
 +/*
 +===================
 +CL_ServerStatusResponse
 +===================
 +*/
 +void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) {
 +	char	*s;
 +	char	info[MAX_INFO_STRING];
 +	int		i, l, score, ping;
 +	int		len;
 +	serverStatus_t *serverStatus;
 +
 +	serverStatus = NULL;
 +	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
 +			serverStatus = &cl_serverStatusList[i];
 +			break;
 +		}
 +	}
 +	// if we didn't request this server status
 +	if (!serverStatus) {
 +		return;
 +	}
 +
 +	s = MSG_ReadStringLine( msg );
 +
 +	len = 0;
 +	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s);
 +
 +	if (serverStatus->print) {
 +		Com_Printf("Server settings:\n");
 +		// print cvars
 +		while (*s) {
 +			for (i = 0; i < 2 && *s; i++) {
 +				if (*s == '\\')
 +					s++;
 +				l = 0;
 +				while (*s) {
 +					info[l++] = *s;
 +					if (l >= MAX_INFO_STRING-1)
 +						break;
 +					s++;
 +					if (*s == '\\') {
 +						break;
 +					}
 +				}
 +				info[l] = '\0';
 +				if (i) {
 +					Com_Printf("%s\n", info);
 +				}
 +				else {
 +					Com_Printf("%-24s", info);
 +				}
 +			}
 +		}
 +	}
 +
 +	len = strlen(serverStatus->string);
 +	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
 +
 +	if (serverStatus->print) {
 +		Com_Printf("\nPlayers:\n");
 +		Com_Printf("num: score: ping: name:\n");
 +	}
 +	for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) {
 +
 +		len = strlen(serverStatus->string);
 +		Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s);
 +
 +		if (serverStatus->print) {
 +			score = ping = 0;
 +			sscanf(s, "%d %d", &score, &ping);
 +			s = strchr(s, ' ');
 +			if (s)
 +				s = strchr(s+1, ' ');
 +			if (s)
 +				s++;
 +			else
 +				s = "unknown";
 +			Com_Printf("%-2d   %-3d    %-3d   %s\n", i, score, ping, s );
 +		}
 +	}
 +	len = strlen(serverStatus->string);
 +	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
 +
 +	serverStatus->time = Com_Milliseconds();
 +	serverStatus->address = from;
 +	serverStatus->pending = qfalse;
 +	if (serverStatus->print) {
 +		serverStatus->retrieved = qtrue;
 +	}
 +}
 +
 +/*
 +==================
 +CL_LocalServers_f
 +==================
 +*/
 +void CL_LocalServers_f( void ) {
 +	char		*message;
 +	int			i, j;
 +	netadr_t	to;
 +
 +	Com_Printf( "Scanning for servers on the local network...\n");
 +
 +	// reset the list, waiting for response
 +	cls.numlocalservers = 0;
 +	cls.pingUpdateSource = AS_LOCAL;
 +
 +	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
 +		qboolean b = cls.localServers[i].visible;
 +		Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i]));
 +		cls.localServers[i].visible = b;
 +	}
 +	Com_Memset( &to, 0, sizeof( to ) );
 +
 +	// The 'xxx' in the message is a challenge that will be echoed back
 +	// by the server.  We don't care about that here, but master servers
 +	// can use that to prevent spoofed server responses from invalid ip
 +	message = "\377\377\377\377getinfo xxx";
 +
 +	// send each message twice in case one is dropped
 +	for ( i = 0 ; i < 2 ; i++ ) {
 +		// send a broadcast packet on each server port
 +		// we support multiple server ports so a single machine
 +		// can nicely run multiple servers
 +		for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
 +			to.port = BigShort( (short)(PORT_SERVER + j) );
 +
 +			to.type = NA_BROADCAST;
 +			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
 +
 +			to.type = NA_BROADCAST_IPX;
 +			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
 +		}
 +	}
 +}
 +
 +/*
 +==================
 +CL_GlobalServers_f
 +==================
 +*/
 +void CL_GlobalServers_f( void ) {
 +	netadr_t	to;
 +	int			i;
 +	int			count;
 +	char		*buffptr;
 +	char		command[1024];
 +	
 +	if ( Cmd_Argc() < 3) {
 +		Com_Printf( "usage: globalservers <master# 0-1> <protocol> [keywords]\n");
 +		return;	
 +	}
 +
 +	cls.masterNum = atoi( Cmd_Argv(1) );
 +
 +	Com_Printf( "Requesting servers from the master...\n");
 +
 +	// reset the list, waiting for response
 +	// -1 is used to distinguish a "no response"
 +
 +	if( cls.masterNum == 1 ) {
 +		NET_StringToAdr( MASTER_SERVER_NAME, &to );
 +		cls.nummplayerservers = -1;
 +		cls.pingUpdateSource = AS_MPLAYER;
 +	}
 +	else {
 +		NET_StringToAdr( MASTER_SERVER_NAME, &to );
 +		cls.numglobalservers = -1;
 +		cls.pingUpdateSource = AS_GLOBAL;
 +	}
 +	to.type = NA_IP;
 +	to.port = BigShort(PORT_MASTER);
 +
 +	sprintf( command, "getservers %s", Cmd_Argv(2) );
 +
 +	// tack on keywords
 +	buffptr = command + strlen( command );
 +	count   = Cmd_Argc();
 +	for (i=3; i<count; i++)
 +		buffptr += sprintf( buffptr, " %s", Cmd_Argv(i) );
 +
 +	// if we are a demo, automatically add a "demo" keyword
 +	if ( Cvar_VariableValue( "fs_restrict" ) ) {
 +		buffptr += sprintf( buffptr, " demo" );
 +	}
 +
 +	NET_OutOfBandPrint( NS_SERVER, to, command );
 +}
 +
 +
 +/*
 +==================
 +CL_GetPing
 +==================
 +*/
 +void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
 +{
 +	const char	*str;
 +	int		time;
 +	int		maxPing;
 +
 +	if (!cl_pinglist[n].adr.port)
 +	{
 +		// empty slot
 +		buf[0]    = '\0';
 +		*pingtime = 0;
 +		return;
 +	}
 +
 +	str = NET_AdrToString( cl_pinglist[n].adr );
 +	Q_strncpyz( buf, str, buflen );
 +
 +	time = cl_pinglist[n].time;
 +	if (!time)
 +	{
 +		// check for timeout
 +		time = cls.realtime - cl_pinglist[n].start;
 +		maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
 +		if( maxPing < 100 ) {
 +			maxPing = 100;
 +		}
 +		if (time < maxPing)
 +		{
 +			// not timed out yet
 +			time = 0;
 +		}
 +	}
 +
 +	CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time);
 +
 +	*pingtime = time;
 +}
 +
 +/*
 +==================
 +CL_UpdateServerInfo
 +==================
 +*/
 +void CL_UpdateServerInfo( int n )
 +{
 +	if (!cl_pinglist[n].adr.port)
 +	{
 +		return;
 +	}
 +
 +	CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time );
 +}
 +
 +/*
 +==================
 +CL_GetPingInfo
 +==================
 +*/
 +void CL_GetPingInfo( int n, char *buf, int buflen )
 +{
 +	if (!cl_pinglist[n].adr.port)
 +	{
 +		// empty slot
 +		if (buflen)
 +			buf[0] = '\0';
 +		return;
 +	}
 +
 +	Q_strncpyz( buf, cl_pinglist[n].info, buflen );
 +}
 +
 +/*
 +==================
 +CL_ClearPing
 +==================
 +*/
 +void CL_ClearPing( int n )
 +{
 +	if (n < 0 || n >= MAX_PINGREQUESTS)
 +		return;
 +
 +	cl_pinglist[n].adr.port = 0;
 +}
 +
 +/*
 +==================
 +CL_GetPingQueueCount
 +==================
 +*/
 +int CL_GetPingQueueCount( void )
 +{
 +	int		i;
 +	int		count;
 +	ping_t*	pingptr;
 +
 +	count   = 0;
 +	pingptr = cl_pinglist;
 +
 +	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) {
 +		if (pingptr->adr.port) {
 +			count++;
 +		}
 +	}
 +
 +	return (count);
 +}
 +
 +/*
 +==================
 +CL_GetFreePing
 +==================
 +*/
 +ping_t* CL_GetFreePing( void )
 +{
 +	ping_t*	pingptr;
 +	ping_t*	best;	
 +	int		oldest;
 +	int		i;
 +	int		time;
 +
 +	pingptr = cl_pinglist;
 +	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
 +	{
 +		// find free ping slot
 +		if (pingptr->adr.port)
 +		{
 +			if (!pingptr->time)
 +			{
 +				if (cls.realtime - pingptr->start < 500)
 +				{
 +					// still waiting for response
 +					continue;
 +				}
 +			}
 +			else if (pingptr->time < 500)
 +			{
 +				// results have not been queried
 +				continue;
 +			}
 +		}
 +
 +		// clear it
 +		pingptr->adr.port = 0;
 +		return (pingptr);
 +	}
 +
 +	// use oldest entry
 +	pingptr = cl_pinglist;
 +	best    = cl_pinglist;
 +	oldest  = INT_MIN;
 +	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
 +	{
 +		// scan for oldest
 +		time = cls.realtime - pingptr->start;
 +		if (time > oldest)
 +		{
 +			oldest = time;
 +			best   = pingptr;
 +		}
 +	}
 +
 +	return (best);
 +}
 +
 +/*
 +==================
 +CL_Ping_f
 +==================
 +*/
 +void CL_Ping_f( void ) {
 +	netadr_t	to;
 +	ping_t*		pingptr;
 +	char*		server;
 +
 +	if ( Cmd_Argc() != 2 ) {
 +		Com_Printf( "usage: ping [server]\n");
 +		return;	
 +	}
 +
 +	Com_Memset( &to, 0, sizeof(netadr_t) );
 +
 +	server = Cmd_Argv(1);
 +
 +	if ( !NET_StringToAdr( server, &to ) ) {
 +		return;
 +	}
 +
 +	pingptr = CL_GetFreePing();
 +
 +	memcpy( &pingptr->adr, &to, sizeof (netadr_t) );
 +	pingptr->start = cls.realtime;
 +	pingptr->time  = 0;
 +
 +	CL_SetServerInfoByAddress(pingptr->adr, NULL, 0);
 +		
 +	NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
 +}
 +
 +/*
 +==================
 +CL_UpdateVisiblePings_f
 +==================
 +*/
 +qboolean CL_UpdateVisiblePings_f(int source) {
 +	int			slots, i;
 +	char		buff[MAX_STRING_CHARS];
 +	int			pingTime;
 +	int			max;
 +	qboolean status = qfalse;
 +
 +	if (source < 0 || source > AS_FAVORITES) {
 +		return qfalse;
 +	}
 +
 +	cls.pingUpdateSource = source;
 +
 +	slots = CL_GetPingQueueCount();
 +	if (slots < MAX_PINGREQUESTS) {
 +		serverInfo_t *server = NULL;
 +
 +		max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS;
 +		switch (source) {
 +			case AS_LOCAL :
 +				server = &cls.localServers[0];
 +				max = cls.numlocalservers;
 +			break;
 +			case AS_MPLAYER :
 +				server = &cls.mplayerServers[0];
 +				max = cls.nummplayerservers;
 +			break;
 +			case AS_GLOBAL :
 +				server = &cls.globalServers[0];
 +				max = cls.numglobalservers;
 +			break;
 +			case AS_FAVORITES :
 +				server = &cls.favoriteServers[0];
 +				max = cls.numfavoriteservers;
 +			break;
 +		}
 +		for (i = 0; i < max; i++) {
 +			if (server[i].visible) {
 +				if (server[i].ping == -1) {
 +					int j;
 +
 +					if (slots >= MAX_PINGREQUESTS) {
 +						break;
 +					}
 +					for (j = 0; j < MAX_PINGREQUESTS; j++) {
 +						if (!cl_pinglist[j].adr.port) {
 +							continue;
 +						}
 +						if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) {
 +							// already on the list
 +							break;
 +						}
 +					}
 +					if (j >= MAX_PINGREQUESTS) {
 +						status = qtrue;
 +						for (j = 0; j < MAX_PINGREQUESTS; j++) {
 +							if (!cl_pinglist[j].adr.port) {
 +								break;
 +							}
 +						}
 +						memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t));
 +						cl_pinglist[j].start = cls.realtime;
 +						cl_pinglist[j].time = 0;
 +						NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
 +						slots++;
 +					}
 +				}
 +				// if the server has a ping higher than cl_maxPing or
 +				// the ping packet got lost
 +				else if (server[i].ping == 0) {
 +					// if we are updating global servers
 +					if (source == AS_GLOBAL) {
 +						//
 +						if ( cls.numGlobalServerAddresses > 0 ) {
 +							// overwrite this server with one from the additional global servers
 +							cls.numGlobalServerAddresses--;
 +							CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]);
 +							// NOTE: the server[i].visible flag stays untouched
 +						}
 +					}
 +				}
 +			}
 +		}
 +	} 
 +
 +	if (slots) {
 +		status = qtrue;
 +	}
 +	for (i = 0; i < MAX_PINGREQUESTS; i++) {
 +		if (!cl_pinglist[i].adr.port) {
 +			continue;
 +		}
 +		CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
 +		if (pingTime != 0) {
 +			CL_ClearPing(i);
 +			status = qtrue;
 +		}
 +	}
 +
 +	return status;
 +}
 +
 +/*
 +==================
 +CL_ServerStatus_f
 +==================
 +*/
 +void CL_ServerStatus_f(void) {
 +	netadr_t	to;
 +	char		*server;
 +	serverStatus_t *serverStatus;
 +
 +	Com_Memset( &to, 0, sizeof(netadr_t) );
 +
 +	if ( Cmd_Argc() != 2 ) {
 +		if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
 +			Com_Printf ("Not connected to a server.\n");
 +			Com_Printf( "Usage: serverstatus [server]\n");
 +			return;	
 +		}
 +		server = cls.servername;
 +	}
 +	else {
 +		server = Cmd_Argv(1);
 +	}
 +
 +	if ( !NET_StringToAdr( server, &to ) ) {
 +		return;
 +	}
 +
 +	NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
 +
 +	serverStatus = CL_GetServerStatus( to );
 +	serverStatus->address = to;
 +	serverStatus->print = qtrue;
 +	serverStatus->pending = qtrue;
 +}
 +
 +/*
 +==================
 +CL_ShowIP_f
 +==================
 +*/
 +void CL_ShowIP_f(void) {
 +	Sys_ShowIP();
 +}
 +
 +/*
 +=================
 +bool CL_CDKeyValidate
 +=================
 +*/
 +qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
 +	char	ch;
 +	byte	sum;
 +	char	chs[3];
 +	int i, len;
 +
 +	len = strlen(key);
 +	if( len != CDKEY_LEN ) {
 +		return qfalse;
 +	}
 +
 +	if( checksum && strlen( checksum ) != CDCHKSUM_LEN ) {
 +		return qfalse;
 +	}
 +
 +	sum = 0;
 +	// for loop gets rid of conditional assignment warning
 +	for (i = 0; i < len; i++) {
 +		ch = *key++;
 +		if (ch>='a' && ch<='z') {
 +			ch -= 32;
 +		}
 +		switch( ch ) {
 +		case '2':
 +		case '3':
 +		case '7':
 +		case 'A':
 +		case 'B':
 +		case 'C':
 +		case 'D':
 +		case 'G':
 +		case 'H':
 +		case 'J':
 +		case 'L':
 +		case 'P':
 +		case 'R':
 +		case 'S':
 +		case 'T':
 +		case 'W':
 +			sum += ch;
 +			continue;
 +		default:
 +			return qfalse;
 +		}
 +	}
 +
 +	sprintf(chs, "%02x", sum);
 +	
 +	if (checksum && !Q_stricmp(chs, checksum)) {
 +		return qtrue;
 +	}
 +
 +	if (!checksum) {
 +		return qtrue;
 +	}
 +
 +	return qfalse;
 +}
 +
 +
 diff --git a/code/client/cl_net_chan.c b/code/client/cl_net_chan.c new file mode 100755 index 0000000..d1e0b08 --- /dev/null +++ b/code/client/cl_net_chan.c @@ -0,0 +1,167 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#include "../game/q_shared.h"
 +#include "../qcommon/qcommon.h"
 +#include "client.h"
 +
 +/*
 +==============
 +CL_Netchan_Encode
 +
 +	// first 12 bytes of the data are always:
 +	long serverId;
 +	long messageAcknowledge;
 +	long reliableAcknowledge;
 +
 +==============
 +*/
 +static void CL_Netchan_Encode( msg_t *msg ) {
 +	int serverId, messageAcknowledge, reliableAcknowledge;
 +	int i, index, srdc, sbit, soob;
 +	byte key, *string;
 +
 +	if ( msg->cursize <= CL_ENCODE_START ) {
 +		return;
 +	}
 +
 +        srdc = msg->readcount;
 +        sbit = msg->bit;
 +        soob = msg->oob;
 +        
 +        msg->bit = 0;
 +        msg->readcount = 0;
 +        msg->oob = 0;
 +        
 +        serverId = MSG_ReadLong(msg);
 +	messageAcknowledge = MSG_ReadLong(msg);
 +	reliableAcknowledge = MSG_ReadLong(msg);
 +
 +        msg->oob = soob;
 +        msg->bit = sbit;
 +        msg->readcount = srdc;
 +        
 +	string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
 +	index = 0;
 +	//
 +	key = clc.challenge ^ serverId ^ messageAcknowledge;
 +	for (i = CL_ENCODE_START; i < msg->cursize; i++) {
 +		// modify the key with the last received now acknowledged server command
 +		if (!string[index])
 +			index = 0;
 +		if (string[index] > 127 || string[index] == '%') {
 +			key ^= '.' << (i & 1);
 +		}
 +		else {
 +			key ^= string[index] << (i & 1);
 +		}
 +		index++;
 +		// encode the data with this key
 +		*(msg->data + i) = (*(msg->data + i)) ^ key;
 +	}
 +}
 +
 +/*
 +==============
 +CL_Netchan_Decode
 +
 +	// first four bytes of the data are always:
 +	long reliableAcknowledge;
 +
 +==============
 +*/
 +static void CL_Netchan_Decode( msg_t *msg ) {
 +	long reliableAcknowledge, i, index;
 +	byte key, *string;
 +        int	srdc, sbit, soob;
 +
 +        srdc = msg->readcount;
 +        sbit = msg->bit;
 +        soob = msg->oob;
 +        
 +        msg->oob = 0;
 +        
 +	reliableAcknowledge = MSG_ReadLong(msg);
 +
 +        msg->oob = soob;
 +        msg->bit = sbit;
 +        msg->readcount = srdc;
 +
 +	string = clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
 +	index = 0;
 +	// xor the client challenge with the netchan sequence number (need something that changes every message)
 +	key = clc.challenge ^ LittleLong( *(unsigned *)msg->data );
 +	for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) {
 +		// modify the key with the last sent and with this message acknowledged client command
 +		if (!string[index])
 +			index = 0;
 +		if (string[index] > 127 || string[index] == '%') {
 +			key ^= '.' << (i & 1);
 +		}
 +		else {
 +			key ^= string[index] << (i & 1);
 +		}
 +		index++;
 +		// decode the data with this key
 +		*(msg->data + i) = *(msg->data + i) ^ key;
 +	}
 +}
 +
 +/*
 +=================
 +CL_Netchan_TransmitNextFragment
 +=================
 +*/
 +void CL_Netchan_TransmitNextFragment( netchan_t *chan ) {
 +	Netchan_TransmitNextFragment( chan );
 +}
 +
 +/*
 +===============
 +CL_Netchan_Transmit
 +================
 +*/
 +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
 +	MSG_WriteByte( msg, clc_EOF );
 +
 +	CL_Netchan_Encode( msg );
 +	Netchan_Transmit( chan, msg->cursize, msg->data );
 +}
 +
 +extern 	int oldsize;
 +int newsize = 0;
 +
 +/*
 +=================
 +CL_Netchan_Process
 +=================
 +*/
 +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
 +	int ret;
 +
 +	ret = Netchan_Process( chan, msg );
 +	if (!ret)
 +		return qfalse;
 +	CL_Netchan_Decode( msg );
 +	newsize += msg->cursize;
 +	return qtrue;
 +}
 diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c new file mode 100755 index 0000000..9de3588 --- /dev/null +++ b/code/client/cl_parse.c @@ -0,0 +1,655 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// cl_parse.c  -- parse a message received from the server
 +
 +#include "client.h"
 +
 +char *svc_strings[256] = {
 +	"svc_bad",
 +
 +	"svc_nop",
 +	"svc_gamestate",
 +	"svc_configstring",
 +	"svc_baseline",	
 +	"svc_serverCommand",
 +	"svc_download",
 +	"svc_snapshot"
 +};
 +
 +void SHOWNET( msg_t *msg, char *s) {
 +	if ( cl_shownet->integer >= 2) {
 +		Com_Printf ("%3i:%s\n", msg->readcount-1, s);
 +	}
 +}
 +
 +
 +/*
 +=========================================================================
 +
 +MESSAGE PARSING
 +
 +=========================================================================
 +*/
 +
 +/*
 +==================
 +CL_DeltaEntity
 +
 +Parses deltas from the given base and adds the resulting entity
 +to the current frame
 +==================
 +*/
 +void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, 
 +					 qboolean unchanged) {
 +	entityState_t	*state;
 +
 +	// save the parsed entity state into the big circular buffer so
 +	// it can be used as the source for a later delta
 +	state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
 +
 +	if ( unchanged ) {
 +		*state = *old;
 +	} else {
 +		MSG_ReadDeltaEntity( msg, old, state, newnum );
 +	}
 +
 +	if ( state->number == (MAX_GENTITIES-1) ) {
 +		return;		// entity was delta removed
 +	}
 +	cl.parseEntitiesNum++;
 +	frame->numEntities++;
 +}
 +
 +/*
 +==================
 +CL_ParsePacketEntities
 +
 +==================
 +*/
 +void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
 +	int			newnum;
 +	entityState_t	*oldstate;
 +	int			oldindex, oldnum;
 +
 +	newframe->parseEntitiesNum = cl.parseEntitiesNum;
 +	newframe->numEntities = 0;
 +
 +	// delta from the entities present in oldframe
 +	oldindex = 0;
 +	oldstate = NULL;
 +	if (!oldframe) {
 +		oldnum = 99999;
 +	} else {
 +		if ( oldindex >= oldframe->numEntities ) {
 +			oldnum = 99999;
 +		} else {
 +			oldstate = &cl.parseEntities[
 +				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
 +			oldnum = oldstate->number;
 +		}
 +	}
 +
 +	while ( 1 ) {
 +		// read the entity index number
 +		newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
 +
 +		if ( newnum == (MAX_GENTITIES-1) ) {
 +			break;
 +		}
 +
 +		if ( msg->readcount > msg->cursize ) {
 +			Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
 +		}
 +
 +		while ( oldnum < newnum ) {
 +			// one or more entities from the old packet are unchanged
 +			if ( cl_shownet->integer == 3 ) {
 +				Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
 +			}
 +			CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
 +			
 +			oldindex++;
 +
 +			if ( oldindex >= oldframe->numEntities ) {
 +				oldnum = 99999;
 +			} else {
 +				oldstate = &cl.parseEntities[
 +					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
 +				oldnum = oldstate->number;
 +			}
 +		}
 +		if (oldnum == newnum) {
 +			// delta from previous state
 +			if ( cl_shownet->integer == 3 ) {
 +				Com_Printf ("%3i:  delta: %i\n", msg->readcount, newnum);
 +			}
 +			CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
 +
 +			oldindex++;
 +
 +			if ( oldindex >= oldframe->numEntities ) {
 +				oldnum = 99999;
 +			} else {
 +				oldstate = &cl.parseEntities[
 +					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
 +				oldnum = oldstate->number;
 +			}
 +			continue;
 +		}
 +
 +		if ( oldnum > newnum ) {
 +			// delta from baseline
 +			if ( cl_shownet->integer == 3 ) {
 +				Com_Printf ("%3i:  baseline: %i\n", msg->readcount, newnum);
 +			}
 +			CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
 +			continue;
 +		}
 +
 +	}
 +
 +	// any remaining entities in the old frame are copied over
 +	while ( oldnum != 99999 ) {
 +		// one or more entities from the old packet are unchanged
 +		if ( cl_shownet->integer == 3 ) {
 +			Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
 +		}
 +		CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
 +		
 +		oldindex++;
 +
 +		if ( oldindex >= oldframe->numEntities ) {
 +			oldnum = 99999;
 +		} else {
 +			oldstate = &cl.parseEntities[
 +				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
 +			oldnum = oldstate->number;
 +		}
 +	}
 +}
 +
 +
 +/*
 +================
 +CL_ParseSnapshot
 +
 +If the snapshot is parsed properly, it will be copied to
 +cl.snap and saved in cl.snapshots[].  If the snapshot is invalid
 +for any reason, no changes to the state will be made at all.
 +================
 +*/
 +void CL_ParseSnapshot( msg_t *msg ) {
 +	int			len;
 +	clSnapshot_t	*old;
 +	clSnapshot_t	newSnap;
 +	int			deltaNum;
 +	int			oldMessageNum;
 +	int			i, packetNum;
 +
 +	// get the reliable sequence acknowledge number
 +	// NOTE: now sent with all server to client messages
 +	//clc.reliableAcknowledge = MSG_ReadLong( msg );
 +
 +	// read in the new snapshot to a temporary buffer
 +	// we will only copy to cl.snap if it is valid
 +	Com_Memset (&newSnap, 0, sizeof(newSnap));
 +
 +	// we will have read any new server commands in this
 +	// message before we got to svc_snapshot
 +	newSnap.serverCommandNum = clc.serverCommandSequence;
 +
 +	newSnap.serverTime = MSG_ReadLong( msg );
 +
 +	newSnap.messageNum = clc.serverMessageSequence;
 +
 +	deltaNum = MSG_ReadByte( msg );
 +	if ( !deltaNum ) {
 +		newSnap.deltaNum = -1;
 +	} else {
 +		newSnap.deltaNum = newSnap.messageNum - deltaNum;
 +	}
 +	newSnap.snapFlags = MSG_ReadByte( msg );
 +
 +	// If the frame is delta compressed from data that we
 +	// no longer have available, we must suck up the rest of
 +	// the frame, but not use it, then ask for a non-compressed
 +	// message 
 +	if ( newSnap.deltaNum <= 0 ) {
 +		newSnap.valid = qtrue;		// uncompressed frame
 +		old = NULL;
 +		clc.demowaiting = qfalse;	// we can start recording now
 +	} else {
 +		old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
 +		if ( !old->valid ) {
 +			// should never happen
 +			Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
 +		} else if ( old->messageNum != newSnap.deltaNum ) {
 +			// The frame that the server did the delta from
 +			// is too old, so we can't reconstruct it properly.
 +			Com_Printf ("Delta frame too old.\n");
 +		} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
 +			Com_Printf ("Delta parseEntitiesNum too old.\n");
 +		} else {
 +			newSnap.valid = qtrue;	// valid delta parse
 +		}
 +	}
 +
 +	// read areamask
 +	len = MSG_ReadByte( msg );
 +	MSG_ReadData( msg, &newSnap.areamask, len);
 +
 +	// read playerinfo
 +	SHOWNET( msg, "playerstate" );
 +	if ( old ) {
 +		MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
 +	} else {
 +		MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
 +	}
 +
 +	// read packet entities
 +	SHOWNET( msg, "packet entities" );
 +	CL_ParsePacketEntities( msg, old, &newSnap );
 +
 +	// if not valid, dump the entire thing now that it has
 +	// been properly read
 +	if ( !newSnap.valid ) {
 +		return;
 +	}
 +
 +	// clear the valid flags of any snapshots between the last
 +	// received and this one, so if there was a dropped packet
 +	// it won't look like something valid to delta from next
 +	// time we wrap around in the buffer
 +	oldMessageNum = cl.snap.messageNum + 1;
 +
 +	if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
 +		oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
 +	}
 +	for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
 +		cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
 +	}
 +
 +	// copy to the current good spot
 +	cl.snap = newSnap;
 +	cl.snap.ping = 999;
 +	// calculate ping time
 +	for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
 +		packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
 +		if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
 +			cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
 +			break;
 +		}
 +	}
 +	// save the frame off in the backup array for later delta comparisons
 +	cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
 +
 +	if (cl_shownet->integer == 3) {
 +		Com_Printf( "   snapshot:%i  delta:%i  ping:%i\n", cl.snap.messageNum,
 +		cl.snap.deltaNum, cl.snap.ping );
 +	}
 +
 +	cl.newSnapshots = qtrue;
 +}
 +
 +
 +//=====================================================================
 +
 +int cl_connectedToPureServer;
 +
 +/*
 +==================
 +CL_SystemInfoChanged
 +
 +The systeminfo configstring has been changed, so parse
 +new information out of it.  This will happen at every
 +gamestate, and possibly during gameplay.
 +==================
 +*/
 +void CL_SystemInfoChanged( void ) {
 +	char			*systemInfo;
 +	const char		*s, *t;
 +	char			key[BIG_INFO_KEY];
 +	char			value[BIG_INFO_VALUE];
 +	qboolean		gameSet;
 +
 +	systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
 +	// NOTE TTimo:
 +	// when the serverId changes, any further messages we send to the server will use this new serverId
 +	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
 +	// in some cases, outdated cp commands might get sent with this news serverId
 +	cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
 +
 +	// don't set any vars when playing a demo
 +	if ( clc.demoplaying ) {
 +		return;
 +	}
 +
 +	s = Info_ValueForKey( systemInfo, "sv_cheats" );
 +	if ( atoi(s) == 0 ) {
 +		Cvar_SetCheatState();
 +	}
 +
 +	// check pure server string
 +	s = Info_ValueForKey( systemInfo, "sv_paks" );
 +	t = Info_ValueForKey( systemInfo, "sv_pakNames" );
 +	FS_PureServerSetLoadedPaks( s, t );
 +
 +	s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
 +	t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
 +	FS_PureServerSetReferencedPaks( s, t );
 +
 +	gameSet = qfalse;
 +	// scan through all the variables in the systeminfo and locally set cvars to match
 +	s = systemInfo;
 +	while ( s ) {
 +		Info_NextPair( &s, key, value );
 +		if ( !key[0] ) {
 +			break;
 +		}
 +		// ehw!
 +		if ( !Q_stricmp( key, "fs_game" ) ) {
 +			gameSet = qtrue;
 +		}
 +
 +		Cvar_Set( key, value );
 +	}
 +	// if game folder should not be set and it is set at the client side
 +	if ( !gameSet && *Cvar_VariableString("fs_game") ) {
 +		Cvar_Set( "fs_game", "" );
 +	}
 +	cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
 +}
 +
 +/*
 +==================
 +CL_ParseGamestate
 +==================
 +*/
 +void CL_ParseGamestate( msg_t *msg ) {
 +	int				i;
 +	entityState_t	*es;
 +	int				newnum;
 +	entityState_t	nullstate;
 +	int				cmd;
 +	char			*s;
 +
 +	Con_Close();
 +
 +	clc.connectPacketCount = 0;
 +
 +	// wipe local client state
 +	CL_ClearState();
 +
 +	// a gamestate always marks a server command sequence
 +	clc.serverCommandSequence = MSG_ReadLong( msg );
 +
 +	// parse all the configstrings and baselines
 +	cl.gameState.dataCount = 1;	// leave a 0 at the beginning for uninitialized configstrings
 +	while ( 1 ) {
 +		cmd = MSG_ReadByte( msg );
 +
 +		if ( cmd == svc_EOF ) {
 +			break;
 +		}
 +		
 +		if ( cmd == svc_configstring ) {
 +			int		len;
 +
 +			i = MSG_ReadShort( msg );
 +			if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
 +				Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
 +			}
 +			s = MSG_ReadBigString( msg );
 +			len = strlen( s );
 +
 +			if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
 +				Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
 +			}
 +
 +			// append it to the gameState string buffer
 +			cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
 +			Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
 +			cl.gameState.dataCount += len + 1;
 +		} else if ( cmd == svc_baseline ) {
 +			newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
 +			if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
 +				Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
 +			}
 +			Com_Memset (&nullstate, 0, sizeof(nullstate));
 +			es = &cl.entityBaselines[ newnum ];
 +			MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
 +		} else {
 +			Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
 +		}
 +	}
 +
 +	clc.clientNum = MSG_ReadLong(msg);
 +	// read the checksum feed
 +	clc.checksumFeed = MSG_ReadLong( msg );
 +
 +	// parse serverId and other cvars
 +	CL_SystemInfoChanged();
 +
 +	// reinitialize the filesystem if the game directory has changed
 +  FS_ConditionalRestart( clc.checksumFeed );
 +
 +	// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
 +	// cgame
 +	CL_InitDownloads();
 +
 +	// make sure the game starts
 +	Cvar_Set( "cl_paused", "0" );
 +}
 +
 +
 +//=====================================================================
 +
 +/*
 +=====================
 +CL_ParseDownload
 +
 +A download message has been received from the server
 +=====================
 +*/
 +void CL_ParseDownload ( msg_t *msg ) {
 +	int		size;
 +	unsigned char data[MAX_MSGLEN];
 +	int block;
 +
 +	// read the data
 +	block = MSG_ReadShort ( msg );
 +
 +	if ( !block )
 +	{
 +		// block zero is special, contains file size
 +		clc.downloadSize = MSG_ReadLong ( msg );
 +
 +		Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
 +
 +		if (clc.downloadSize < 0)
 +		{
 +			Com_Error(ERR_DROP, MSG_ReadString( msg ) );
 +			return;
 +		}
 +	}
 +
 +	size = MSG_ReadShort ( msg );
 +	if (size > 0)
 +		MSG_ReadData( msg, data, size );
 +
 +	if (clc.downloadBlock != block) {
 +		Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
 +		return;
 +	}
 +
 +	// open the file if not opened yet
 +	if (!clc.download)
 +	{
 +		if (!*clc.downloadTempName) {
 +			Com_Printf("Server sending download, but no download was requested\n");
 +			CL_AddReliableCommand( "stopdl" );
 +			return;
 +		}
 +
 +		clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
 +
 +		if (!clc.download) {
 +			Com_Printf( "Could not create %s\n", clc.downloadTempName );
 +			CL_AddReliableCommand( "stopdl" );
 +			CL_NextDownload();
 +			return;
 +		}
 +	}
 +
 +	if (size)
 +		FS_Write( data, size, clc.download );
 +
 +	CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
 +	clc.downloadBlock++;
 +
 +	clc.downloadCount += size;
 +
 +	// So UI gets access to it
 +	Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
 +
 +	if (!size) { // A zero length block means EOF
 +		if (clc.download) {
 +			FS_FCloseFile( clc.download );
 +			clc.download = 0;
 +
 +			// rename the file
 +			FS_SV_Rename ( clc.downloadTempName, clc.downloadName );
 +		}
 +		*clc.downloadTempName = *clc.downloadName = 0;
 +		Cvar_Set( "cl_downloadName", "" );
 +
 +		// send intentions now
 +		// We need this because without it, we would hold the last nextdl and then start
 +		// loading right away.  If we take a while to load, the server is happily trying
 +		// to send us that last block over and over.
 +		// Write it twice to help make sure we acknowledge the download
 +		CL_WritePacket();
 +		CL_WritePacket();
 +
 +		// get another file if needed
 +		CL_NextDownload ();
 +	}
 +}
 +
 +/*
 +=====================
 +CL_ParseCommandString
 +
 +Command strings are just saved off until cgame asks for them
 +when it transitions a snapshot
 +=====================
 +*/
 +void CL_ParseCommandString( msg_t *msg ) {
 +	char	*s;
 +	int		seq;
 +	int		index;
 +
 +	seq = MSG_ReadLong( msg );
 +	s = MSG_ReadString( msg );
 +
 +	// see if we have already executed stored it off
 +	if ( clc.serverCommandSequence >= seq ) {
 +		return;
 +	}
 +	clc.serverCommandSequence = seq;
 +
 +	index = seq & (MAX_RELIABLE_COMMANDS-1);
 +	Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
 +}
 +
 +
 +/*
 +=====================
 +CL_ParseServerMessage
 +=====================
 +*/
 +void CL_ParseServerMessage( msg_t *msg ) {
 +	int			cmd;
 +
 +	if ( cl_shownet->integer == 1 ) {
 +		Com_Printf ("%i ",msg->cursize);
 +	} else if ( cl_shownet->integer >= 2 ) {
 +		Com_Printf ("------------------\n");
 +	}
 +
 +	MSG_Bitstream(msg);
 +
 +	// get the reliable sequence acknowledge number
 +	clc.reliableAcknowledge = MSG_ReadLong( msg );
 +	// 
 +	if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
 +		clc.reliableAcknowledge = clc.reliableSequence;
 +	}
 +
 +	//
 +	// parse the message
 +	//
 +	while ( 1 ) {
 +		if ( msg->readcount > msg->cursize ) {
 +			Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
 +			break;
 +		}
 +
 +		cmd = MSG_ReadByte( msg );
 +
 +		if ( cmd == svc_EOF) {
 +			SHOWNET( msg, "END OF MESSAGE" );
 +			break;
 +		}
 +
 +		if ( cl_shownet->integer >= 2 ) {
 +			if ( !svc_strings[cmd] ) {
 +				Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
 +			} else {
 +				SHOWNET( msg, svc_strings[cmd] );
 +			}
 +		}
 +	
 +	// other commands
 +		switch ( cmd ) {
 +		default:
 +			Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
 +			break;			
 +		case svc_nop:
 +			break;
 +		case svc_serverCommand:
 +			CL_ParseCommandString( msg );
 +			break;
 +		case svc_gamestate:
 +			CL_ParseGamestate( msg );
 +			break;
 +		case svc_snapshot:
 +			CL_ParseSnapshot( msg );
 +			break;
 +		case svc_download:
 +			CL_ParseDownload( msg );
 +			break;
 +		}
 +	}
 +}
 +
 +
 diff --git a/code/client/cl_scrn.c b/code/client/cl_scrn.c new file mode 100755 index 0000000..c4ccb40 --- /dev/null +++ b/code/client/cl_scrn.c @@ -0,0 +1,547 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
 +
 +#include "client.h"
 +
 +qboolean	scr_initialized;		// ready to draw
 +
 +cvar_t		*cl_timegraph;
 +cvar_t		*cl_debuggraph;
 +cvar_t		*cl_graphheight;
 +cvar_t		*cl_graphscale;
 +cvar_t		*cl_graphshift;
 +
 +/*
 +================
 +SCR_DrawNamedPic
 +
 +Coordinates are 640*480 virtual values
 +=================
 +*/
 +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
 +	qhandle_t	hShader;
 +
 +	assert( width != 0 );
 +
 +	hShader = re.RegisterShader( picname );
 +	SCR_AdjustFrom640( &x, &y, &width, &height );
 +	re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
 +}
 +
 +
 +/*
 +================
 +SCR_AdjustFrom640
 +
 +Adjusted for resolution and screen aspect ratio
 +================
 +*/
 +void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) {
 +	float	xscale;
 +	float	yscale;
 +
 +#if 0
 +		// adjust for wide screens
 +		if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
 +			*x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) );
 +		}
 +#endif
 +
 +	// scale for screen sizes
 +	xscale = cls.glconfig.vidWidth / 640.0;
 +	yscale = cls.glconfig.vidHeight / 480.0;
 +	if ( x ) {
 +		*x *= xscale;
 +	}
 +	if ( y ) {
 +		*y *= yscale;
 +	}
 +	if ( w ) {
 +		*w *= xscale;
 +	}
 +	if ( h ) {
 +		*h *= yscale;
 +	}
 +}
 +
 +/*
 +================
 +SCR_FillRect
 +
 +Coordinates are 640*480 virtual values
 +=================
 +*/
 +void SCR_FillRect( float x, float y, float width, float height, const float *color ) {
 +	re.SetColor( color );
 +
 +	SCR_AdjustFrom640( &x, &y, &width, &height );
 +	re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader );
 +
 +	re.SetColor( NULL );
 +}
 +
 +
 +/*
 +================
 +SCR_DrawPic
 +
 +Coordinates are 640*480 virtual values
 +=================
 +*/
 +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
 +	SCR_AdjustFrom640( &x, &y, &width, &height );
 +	re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
 +}
 +
 +
 +
 +/*
 +** SCR_DrawChar
 +** chars are drawn at 640*480 virtual screen size
 +*/
 +static void SCR_DrawChar( int x, int y, float size, int ch ) {
 +	int row, col;
 +	float frow, fcol;
 +	float	ax, ay, aw, ah;
 +
 +	ch &= 255;
 +
 +	if ( ch == ' ' ) {
 +		return;
 +	}
 +
 +	if ( y < -size ) {
 +		return;
 +	}
 +
 +	ax = x;
 +	ay = y;
 +	aw = size;
 +	ah = size;
 +	SCR_AdjustFrom640( &ax, &ay, &aw, &ah );
 +
 +	row = ch>>4;
 +	col = ch&15;
 +
 +	frow = row*0.0625;
 +	fcol = col*0.0625;
 +	size = 0.0625;
 +
 +	re.DrawStretchPic( ax, ay, aw, ah,
 +					   fcol, frow, 
 +					   fcol + size, frow + size, 
 +					   cls.charSetShader );
 +}
 +
 +/*
 +** SCR_DrawSmallChar
 +** small chars are drawn at native screen resolution
 +*/
 +void SCR_DrawSmallChar( int x, int y, int ch ) {
 +	int row, col;
 +	float frow, fcol;
 +	float size;
 +
 +	ch &= 255;
 +
 +	if ( ch == ' ' ) {
 +		return;
 +	}
 +
 +	if ( y < -SMALLCHAR_HEIGHT ) {
 +		return;
 +	}
 +
 +	row = ch>>4;
 +	col = ch&15;
 +
 +	frow = row*0.0625;
 +	fcol = col*0.0625;
 +	size = 0.0625;
 +
 +	re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
 +					   fcol, frow, 
 +					   fcol + size, frow + size, 
 +					   cls.charSetShader );
 +}
 +
 +
 +/*
 +==================
 +SCR_DrawBigString[Color]
 +
 +Draws a multi-colored string with a drop shadow, optionally forcing
 +to a fixed color.
 +
 +Coordinates are at 640 by 480 virtual resolution
 +==================
 +*/
 +void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor ) {
 +	vec4_t		color;
 +	const char	*s;
 +	int			xx;
 +
 +	// draw the drop shadow
 +	color[0] = color[1] = color[2] = 0;
 +	color[3] = setColor[3];
 +	re.SetColor( color );
 +	s = string;
 +	xx = x;
 +	while ( *s ) {
 +		if ( Q_IsColorString( s ) ) {
 +			s += 2;
 +			continue;
 +		}
 +		SCR_DrawChar( xx+2, y+2, size, *s );
 +		xx += size;
 +		s++;
 +	}
 +
 +
 +	// draw the colored text
 +	s = string;
 +	xx = x;
 +	re.SetColor( setColor );
 +	while ( *s ) {
 +		if ( Q_IsColorString( s ) ) {
 +			if ( !forceColor ) {
 +				Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
 +				color[3] = setColor[3];
 +				re.SetColor( color );
 +			}
 +			s += 2;
 +			continue;
 +		}
 +		SCR_DrawChar( xx, y, size, *s );
 +		xx += size;
 +		s++;
 +	}
 +	re.SetColor( NULL );
 +}
 +
 +
 +void SCR_DrawBigString( int x, int y, const char *s, float alpha ) {
 +	float	color[4];
 +
 +	color[0] = color[1] = color[2] = 1.0;
 +	color[3] = alpha;
 +	SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse );
 +}
 +
 +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) {
 +	SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue );
 +}
 +
 +
 +/*
 +==================
 +SCR_DrawSmallString[Color]
 +
 +Draws a multi-colored string with a drop shadow, optionally forcing
 +to a fixed color.
 +
 +Coordinates are at 640 by 480 virtual resolution
 +==================
 +*/
 +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ) {
 +	vec4_t		color;
 +	const char	*s;
 +	int			xx;
 +
 +	// draw the colored text
 +	s = string;
 +	xx = x;
 +	re.SetColor( setColor );
 +	while ( *s ) {
 +		if ( Q_IsColorString( s ) ) {
 +			if ( !forceColor ) {
 +				Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
 +				color[3] = setColor[3];
 +				re.SetColor( color );
 +			}
 +			s += 2;
 +			continue;
 +		}
 +		SCR_DrawSmallChar( xx, y, *s );
 +		xx += SMALLCHAR_WIDTH;
 +		s++;
 +	}
 +	re.SetColor( NULL );
 +}
 +
 +
 +
 +/*
 +** SCR_Strlen -- skips color escape codes
 +*/
 +static int SCR_Strlen( const char *str ) {
 +	const char *s = str;
 +	int count = 0;
 +
 +	while ( *s ) {
 +		if ( Q_IsColorString( s ) ) {
 +			s += 2;
 +		} else {
 +			count++;
 +			s++;
 +		}
 +	}
 +
 +	return count;
 +}
 +
 +/*
 +** SCR_GetBigStringWidth
 +*/ 
 +int	SCR_GetBigStringWidth( const char *str ) {
 +	return SCR_Strlen( str ) * 16;
 +}
 +
 +
 +//===============================================================================
 +
 +/*
 +=================
 +SCR_DrawDemoRecording
 +=================
 +*/
 +void SCR_DrawDemoRecording( void ) {
 +	char	string[1024];
 +	int		pos;
 +
 +	if ( !clc.demorecording ) {
 +		return;
 +	}
 +	if ( clc.spDemoRecording ) {
 +		return;
 +	}
 +
 +	pos = FS_FTell( clc.demofile );
 +	sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 );
 +
 +	SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue );
 +}
 +
 +
 +/*
 +===============================================================================
 +
 +DEBUG GRAPH
 +
 +===============================================================================
 +*/
 +
 +typedef struct
 +{
 +	float	value;
 +	int		color;
 +} graphsamp_t;
 +
 +static	int			current;
 +static	graphsamp_t	values[1024];
 +
 +/*
 +==============
 +SCR_DebugGraph
 +==============
 +*/
 +void SCR_DebugGraph (float value, int color)
 +{
 +	values[current&1023].value = value;
 +	values[current&1023].color = color;
 +	current++;
 +}
 +
 +/*
 +==============
 +SCR_DrawDebugGraph
 +==============
 +*/
 +void SCR_DrawDebugGraph (void)
 +{
 +	int		a, x, y, w, i, h;
 +	float	v;
 +	int		color;
 +
 +	//
 +	// draw the graph
 +	//
 +	w = cls.glconfig.vidWidth;
 +	x = 0;
 +	y = cls.glconfig.vidHeight;
 +	re.SetColor( g_color_table[0] );
 +	re.DrawStretchPic(x, y - cl_graphheight->integer, 
 +		w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader );
 +	re.SetColor( NULL );
 +
 +	for (a=0 ; a<w ; a++)
 +	{
 +		i = (current-1-a+1024) & 1023;
 +		v = values[i].value;
 +		color = values[i].color;
 +		v = v * cl_graphscale->integer + cl_graphshift->integer;
 +		
 +		if (v < 0)
 +			v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer));
 +		h = (int)v % cl_graphheight->integer;
 +		re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader );
 +	}
 +}
 +
 +//=============================================================================
 +
 +/*
 +==================
 +SCR_Init
 +==================
 +*/
 +void SCR_Init( void ) {
 +	cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT);
 +	cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT);
 +	cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT);
 +	cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT);
 +	cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT);
 +
 +	scr_initialized = qtrue;
 +}
 +
 +
 +//=======================================================
 +
 +/*
 +==================
 +SCR_DrawScreenField
 +
 +This will be called twice if rendering in stereo mode
 +==================
 +*/
 +void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
 +	re.BeginFrame( stereoFrame );
 +
 +	// wide aspect ratio screens need to have the sides cleared
 +	// unless they are displaying game renderings
 +	if ( cls.state != CA_ACTIVE ) {
 +		if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
 +			re.SetColor( g_color_table[0] );
 +			re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader );
 +			re.SetColor( NULL );
 +		}
 +	}
 +
 +	if ( !uivm ) {
 +		Com_DPrintf("draw screen without UI loaded\n");
 +		return;
 +	}
 +
 +	// if the menu is going to cover the entire screen, we
 +	// don't need to render anything under it
 +	if ( !VM_Call( uivm, UI_IS_FULLSCREEN )) {
 +		switch( cls.state ) {
 +		default:
 +			Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" );
 +			break;
 +		case CA_CINEMATIC:
 +			SCR_DrawCinematic();
 +			break;
 +		case CA_DISCONNECTED:
 +			// force menu up
 +			S_StopAllSounds();
 +			VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
 +			break;
 +		case CA_CONNECTING:
 +		case CA_CHALLENGING:
 +		case CA_CONNECTED:
 +			// connecting clients will only show the connection dialog
 +			// refresh to update the time
 +			VM_Call( uivm, UI_REFRESH, cls.realtime );
 +			VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
 +			break;
 +		case CA_LOADING:
 +		case CA_PRIMED:
 +			// draw the game information screen and loading progress
 +			CL_CGameRendering( stereoFrame );
 +
 +			// also draw the connection information, so it doesn't
 +			// flash away too briefly on local or lan games
 +			// refresh to update the time
 +			VM_Call( uivm, UI_REFRESH, cls.realtime );
 +			VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
 +			break;
 +		case CA_ACTIVE:
 +			CL_CGameRendering( stereoFrame );
 +			SCR_DrawDemoRecording();
 +			break;
 +		}
 +	}
 +
 +	// the menu draws next
 +	if ( cls.keyCatchers & KEYCATCH_UI && uivm ) {
 +		VM_Call( uivm, UI_REFRESH, cls.realtime );
 +	}
 +
 +	// console draws next
 +	Con_DrawConsole ();
 +
 +	// debug graph can be drawn on top of anything
 +	if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) {
 +		SCR_DrawDebugGraph ();
 +	}
 +}
 +
 +/*
 +==================
 +SCR_UpdateScreen
 +
 +This is called every frame, and can also be called explicitly to flush
 +text to the screen.
 +==================
 +*/
 +void SCR_UpdateScreen( void ) {
 +	static int	recursive;
 +
 +	if ( !scr_initialized ) {
 +		return;				// not initialized yet
 +	}
 +
 +	if ( ++recursive > 2 ) {
 +		Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
 +	}
 +	recursive = 1;
 +
 +	// if running in stereo, we need to draw the frame twice
 +	if ( cls.glconfig.stereoEnabled ) {
 +		SCR_DrawScreenField( STEREO_LEFT );
 +		SCR_DrawScreenField( STEREO_RIGHT );
 +	} else {
 +		SCR_DrawScreenField( STEREO_CENTER );
 +	}
 +
 +	if ( com_speeds->integer ) {
 +		re.EndFrame( &time_frontend, &time_backend );
 +	} else {
 +		re.EndFrame( NULL, NULL );
 +	}
 +
 +	recursive = 0;
 +}
 +
 diff --git a/code/client/cl_ui.c b/code/client/cl_ui.c new file mode 100755 index 0000000..80040a8 --- /dev/null +++ b/code/client/cl_ui.c @@ -0,0 +1,1200 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#include "client.h"
 +
 +#include "../game/botlib.h"
 +
 +extern	botlib_export_t	*botlib_export;
 +
 +vm_t *uivm;
 +
 +/*
 +====================
 +GetClientState
 +====================
 +*/
 +static void GetClientState( uiClientState_t *state ) {
 +	state->connectPacketCount = clc.connectPacketCount;
 +	state->connState = cls.state;
 +	Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) );
 +	Q_strncpyz( state->updateInfoString, cls.updateInfoString, sizeof( state->updateInfoString ) );
 +	Q_strncpyz( state->messageString, clc.serverMessage, sizeof( state->messageString ) );
 +	state->clientNum = cl.snap.ps.clientNum;
 +}
 +
 +/*
 +====================
 +LAN_LoadCachedServers
 +====================
 +*/
 +void LAN_LoadCachedServers( ) {
 +	int size;
 +	fileHandle_t fileIn;
 +	cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0;
 +	cls.numGlobalServerAddresses = 0;
 +	if (FS_SV_FOpenFileRead("servercache.dat", &fileIn)) {
 +		FS_Read(&cls.numglobalservers, sizeof(int), fileIn);
 +		FS_Read(&cls.nummplayerservers, sizeof(int), fileIn);
 +		FS_Read(&cls.numfavoriteservers, sizeof(int), fileIn);
 +		FS_Read(&size, sizeof(int), fileIn);
 +		if (size == sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers)) {
 +			FS_Read(&cls.globalServers, sizeof(cls.globalServers), fileIn);
 +			FS_Read(&cls.mplayerServers, sizeof(cls.mplayerServers), fileIn);
 +			FS_Read(&cls.favoriteServers, sizeof(cls.favoriteServers), fileIn);
 +		} else {
 +			cls.numglobalservers = cls.nummplayerservers = cls.numfavoriteservers = 0;
 +			cls.numGlobalServerAddresses = 0;
 +		}
 +		FS_FCloseFile(fileIn);
 +	}
 +}
 +
 +/*
 +====================
 +LAN_SaveServersToCache
 +====================
 +*/
 +void LAN_SaveServersToCache( ) {
 +	int size;
 +	fileHandle_t fileOut = FS_SV_FOpenFileWrite("servercache.dat");
 +	FS_Write(&cls.numglobalservers, sizeof(int), fileOut);
 +	FS_Write(&cls.nummplayerservers, sizeof(int), fileOut);
 +	FS_Write(&cls.numfavoriteservers, sizeof(int), fileOut);
 +	size = sizeof(cls.globalServers) + sizeof(cls.favoriteServers) + sizeof(cls.mplayerServers);
 +	FS_Write(&size, sizeof(int), fileOut);
 +	FS_Write(&cls.globalServers, sizeof(cls.globalServers), fileOut);
 +	FS_Write(&cls.mplayerServers, sizeof(cls.mplayerServers), fileOut);
 +	FS_Write(&cls.favoriteServers, sizeof(cls.favoriteServers), fileOut);
 +	FS_FCloseFile(fileOut);
 +}
 +
 +
 +/*
 +====================
 +LAN_ResetPings
 +====================
 +*/
 +static void LAN_ResetPings(int source) {
 +	int count,i;
 +	serverInfo_t *servers = NULL;
 +	count = 0;
 +
 +	switch (source) {
 +		case AS_LOCAL :
 +			servers = &cls.localServers[0];
 +			count = MAX_OTHER_SERVERS;
 +			break;
 +		case AS_MPLAYER :
 +			servers = &cls.mplayerServers[0];
 +			count = MAX_OTHER_SERVERS;
 +			break;
 +		case AS_GLOBAL :
 +			servers = &cls.globalServers[0];
 +			count = MAX_GLOBAL_SERVERS;
 +			break;
 +		case AS_FAVORITES :
 +			servers = &cls.favoriteServers[0];
 +			count = MAX_OTHER_SERVERS;
 +			break;
 +	}
 +	if (servers) {
 +		for (i = 0; i < count; i++) {
 +			servers[i].ping = -1;
 +		}
 +	}
 +}
 +
 +/*
 +====================
 +LAN_AddServer
 +====================
 +*/
 +static int LAN_AddServer(int source, const char *name, const char *address) {
 +	int max, *count, i;
 +	netadr_t adr;
 +	serverInfo_t *servers = NULL;
 +	max = MAX_OTHER_SERVERS;
 +	count = 0;
 +
 +	switch (source) {
 +		case AS_LOCAL :
 +			count = &cls.numlocalservers;
 +			servers = &cls.localServers[0];
 +			break;
 +		case AS_MPLAYER :
 +			count = &cls.nummplayerservers;
 +			servers = &cls.mplayerServers[0];
 +			break;
 +		case AS_GLOBAL :
 +			max = MAX_GLOBAL_SERVERS;
 +			count = &cls.numglobalservers;
 +			servers = &cls.globalServers[0];
 +			break;
 +		case AS_FAVORITES :
 +			count = &cls.numfavoriteservers;
 +			servers = &cls.favoriteServers[0];
 +			break;
 +	}
 +	if (servers && *count < max) {
 +		NET_StringToAdr( address, &adr );
 +		for ( i = 0; i < *count; i++ ) {
 +			if (NET_CompareAdr(servers[i].adr, adr)) {
 +				break;
 +			}
 +		}
 +		if (i >= *count) {
 +			servers[*count].adr = adr;
 +			Q_strncpyz(servers[*count].hostName, name, sizeof(servers[*count].hostName));
 +			servers[*count].visible = qtrue;
 +			(*count)++;
 +			return 1;
 +		}
 +		return 0;
 +	}
 +	return -1;
 +}
 +
 +/*
 +====================
 +LAN_RemoveServer
 +====================
 +*/
 +static void LAN_RemoveServer(int source, const char *addr) {
 +	int *count, i;
 +	serverInfo_t *servers = NULL;
 +	count = 0;
 +	switch (source) {
 +		case AS_LOCAL :
 +			count = &cls.numlocalservers;
 +			servers = &cls.localServers[0];
 +			break;
 +		case AS_MPLAYER :
 +			count = &cls.nummplayerservers;
 +			servers = &cls.mplayerServers[0];
 +			break;
 +		case AS_GLOBAL :
 +			count = &cls.numglobalservers;
 +			servers = &cls.globalServers[0];
 +			break;
 +		case AS_FAVORITES :
 +			count = &cls.numfavoriteservers;
 +			servers = &cls.favoriteServers[0];
 +			break;
 +	}
 +	if (servers) {
 +		netadr_t comp;
 +		NET_StringToAdr( addr, &comp );
 +		for (i = 0; i < *count; i++) {
 +			if (NET_CompareAdr( comp, servers[i].adr)) {
 +				int j = i;
 +				while (j < *count - 1) {
 +					Com_Memcpy(&servers[j], &servers[j+1], sizeof(servers[j]));
 +					j++;
 +				}
 +				(*count)--;
 +				break;
 +			}
 +		}
 +	}
 +}
 +
 +
 +/*
 +====================
 +LAN_GetServerCount
 +====================
 +*/
 +static int LAN_GetServerCount( int source ) {
 +	switch (source) {
 +		case AS_LOCAL :
 +			return cls.numlocalservers;
 +			break;
 +		case AS_MPLAYER :
 +			return cls.nummplayerservers;
 +			break;
 +		case AS_GLOBAL :
 +			return cls.numglobalservers;
 +			break;
 +		case AS_FAVORITES :
 +			return cls.numfavoriteservers;
 +			break;
 +	}
 +	return 0;
 +}
 +
 +/*
 +====================
 +LAN_GetLocalServerAddressString
 +====================
 +*/
 +static void LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) {
 +	switch (source) {
 +		case AS_LOCAL :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				Q_strncpyz(buf, NET_AdrToString( cls.localServers[n].adr) , buflen );
 +				return;
 +			}
 +			break;
 +		case AS_MPLAYER :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				Q_strncpyz(buf, NET_AdrToString( cls.mplayerServers[n].adr) , buflen );
 +				return;
 +			}
 +			break;
 +		case AS_GLOBAL :
 +			if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +				Q_strncpyz(buf, NET_AdrToString( cls.globalServers[n].adr) , buflen );
 +				return;
 +			}
 +			break;
 +		case AS_FAVORITES :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				Q_strncpyz(buf, NET_AdrToString( cls.favoriteServers[n].adr) , buflen );
 +				return;
 +			}
 +			break;
 +	}
 +	buf[0] = '\0';
 +}
 +
 +/*
 +====================
 +LAN_GetServerInfo
 +====================
 +*/
 +static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) {
 +	char info[MAX_STRING_CHARS];
 +	serverInfo_t *server = NULL;
 +	info[0] = '\0';
 +	switch (source) {
 +		case AS_LOCAL :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.localServers[n];
 +			}
 +			break;
 +		case AS_MPLAYER :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.mplayerServers[n];
 +			}
 +			break;
 +		case AS_GLOBAL :
 +			if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +				server = &cls.globalServers[n];
 +			}
 +			break;
 +		case AS_FAVORITES :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.favoriteServers[n];
 +			}
 +			break;
 +	}
 +	if (server && buf) {
 +		buf[0] = '\0';
 +		Info_SetValueForKey( info, "hostname", server->hostName);
 +		Info_SetValueForKey( info, "mapname", server->mapName);
 +		Info_SetValueForKey( info, "clients", va("%i",server->clients));
 +		Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients));
 +		Info_SetValueForKey( info, "ping", va("%i",server->ping));
 +		Info_SetValueForKey( info, "minping", va("%i",server->minPing));
 +		Info_SetValueForKey( info, "maxping", va("%i",server->maxPing));
 +		Info_SetValueForKey( info, "game", server->game);
 +		Info_SetValueForKey( info, "gametype", va("%i",server->gameType));
 +		Info_SetValueForKey( info, "nettype", va("%i",server->netType));
 +		Info_SetValueForKey( info, "addr", NET_AdrToString(server->adr));
 +		Info_SetValueForKey( info, "punkbuster", va("%i", server->punkbuster));
 +		Q_strncpyz(buf, info, buflen);
 +	} else {
 +		if (buf) {
 +			buf[0] = '\0';
 +		}
 +	}
 +}
 +
 +/*
 +====================
 +LAN_GetServerPing
 +====================
 +*/
 +static int LAN_GetServerPing( int source, int n ) {
 +	serverInfo_t *server = NULL;
 +	switch (source) {
 +		case AS_LOCAL :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.localServers[n];
 +			}
 +			break;
 +		case AS_MPLAYER :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.mplayerServers[n];
 +			}
 +			break;
 +		case AS_GLOBAL :
 +			if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +				server = &cls.globalServers[n];
 +			}
 +			break;
 +		case AS_FAVORITES :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				server = &cls.favoriteServers[n];
 +			}
 +			break;
 +	}
 +	if (server) {
 +		return server->ping;
 +	}
 +	return -1;
 +}
 +
 +/*
 +====================
 +LAN_GetServerPtr
 +====================
 +*/
 +static serverInfo_t *LAN_GetServerPtr( int source, int n ) {
 +	switch (source) {
 +		case AS_LOCAL :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return &cls.localServers[n];
 +			}
 +			break;
 +		case AS_MPLAYER :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return &cls.mplayerServers[n];
 +			}
 +			break;
 +		case AS_GLOBAL :
 +			if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +				return &cls.globalServers[n];
 +			}
 +			break;
 +		case AS_FAVORITES :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return &cls.favoriteServers[n];
 +			}
 +			break;
 +	}
 +	return NULL;
 +}
 +
 +/*
 +====================
 +LAN_CompareServers
 +====================
 +*/
 +static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) {
 +	int res;
 +	serverInfo_t *server1, *server2;
 +
 +	server1 = LAN_GetServerPtr(source, s1);
 +	server2 = LAN_GetServerPtr(source, s2);
 +	if (!server1 || !server2) {
 +		return 0;
 +	}
 +
 +	res = 0;
 +	switch( sortKey ) {
 +		case SORT_HOST:
 +			res = Q_stricmp( server1->hostName, server2->hostName );
 +			break;
 +
 +		case SORT_MAP:
 +			res = Q_stricmp( server1->mapName, server2->mapName );
 +			break;
 +		case SORT_CLIENTS:
 +			if (server1->clients < server2->clients) {
 +				res = -1;
 +			}
 +			else if (server1->clients > server2->clients) {
 +				res = 1;
 +			}
 +			else {
 +				res = 0;
 +			}
 +			break;
 +		case SORT_GAME:
 +			if (server1->gameType < server2->gameType) {
 +				res = -1;
 +			}
 +			else if (server1->gameType > server2->gameType) {
 +				res = 1;
 +			}
 +			else {
 +				res = 0;
 +			}
 +			break;
 +		case SORT_PING:
 +			if (server1->ping < server2->ping) {
 +				res = -1;
 +			}
 +			else if (server1->ping > server2->ping) {
 +				res = 1;
 +			}
 +			else {
 +				res = 0;
 +			}
 +			break;
 +	}
 +
 +	if (sortDir) {
 +		if (res < 0)
 +			return 1;
 +		if (res > 0)
 +			return -1;
 +		return 0;
 +	}
 +	return res;
 +}
 +
 +/*
 +====================
 +LAN_GetPingQueueCount
 +====================
 +*/
 +static int LAN_GetPingQueueCount( void ) {
 +	return (CL_GetPingQueueCount());
 +}
 +
 +/*
 +====================
 +LAN_ClearPing
 +====================
 +*/
 +static void LAN_ClearPing( int n ) {
 +	CL_ClearPing( n );
 +}
 +
 +/*
 +====================
 +LAN_GetPing
 +====================
 +*/
 +static void LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) {
 +	CL_GetPing( n, buf, buflen, pingtime );
 +}
 +
 +/*
 +====================
 +LAN_GetPingInfo
 +====================
 +*/
 +static void LAN_GetPingInfo( int n, char *buf, int buflen ) {
 +	CL_GetPingInfo( n, buf, buflen );
 +}
 +
 +/*
 +====================
 +LAN_MarkServerVisible
 +====================
 +*/
 +static void LAN_MarkServerVisible(int source, int n, qboolean visible ) {
 +	if (n == -1) {
 +		int count = MAX_OTHER_SERVERS;
 +		serverInfo_t *server = NULL;
 +		switch (source) {
 +			case AS_LOCAL :
 +				server = &cls.localServers[0];
 +				break;
 +			case AS_MPLAYER :
 +				server = &cls.mplayerServers[0];
 +				break;
 +			case AS_GLOBAL :
 +				server = &cls.globalServers[0];
 +				count = MAX_GLOBAL_SERVERS;
 +				break;
 +			case AS_FAVORITES :
 +				server = &cls.favoriteServers[0];
 +				break;
 +		}
 +		if (server) {
 +			for (n = 0; n < count; n++) {
 +				server[n].visible = visible;
 +			}
 +		}
 +
 +	} else {
 +		switch (source) {
 +			case AS_LOCAL :
 +				if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +					cls.localServers[n].visible = visible;
 +				}
 +				break;
 +			case AS_MPLAYER :
 +				if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +					cls.mplayerServers[n].visible = visible;
 +				}
 +				break;
 +			case AS_GLOBAL :
 +				if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +					cls.globalServers[n].visible = visible;
 +				}
 +				break;
 +			case AS_FAVORITES :
 +				if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +					cls.favoriteServers[n].visible = visible;
 +				}
 +				break;
 +		}
 +	}
 +}
 +
 +
 +/*
 +=======================
 +LAN_ServerIsVisible
 +=======================
 +*/
 +static int LAN_ServerIsVisible(int source, int n ) {
 +	switch (source) {
 +		case AS_LOCAL :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return cls.localServers[n].visible;
 +			}
 +			break;
 +		case AS_MPLAYER :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return cls.mplayerServers[n].visible;
 +			}
 +			break;
 +		case AS_GLOBAL :
 +			if (n >= 0 && n < MAX_GLOBAL_SERVERS) {
 +				return cls.globalServers[n].visible;
 +			}
 +			break;
 +		case AS_FAVORITES :
 +			if (n >= 0 && n < MAX_OTHER_SERVERS) {
 +				return cls.favoriteServers[n].visible;
 +			}
 +			break;
 +	}
 +	return qfalse;
 +}
 +
 +/*
 +=======================
 +LAN_UpdateVisiblePings
 +=======================
 +*/
 +qboolean LAN_UpdateVisiblePings(int source ) {
 +	return CL_UpdateVisiblePings_f(source);
 +}
 +
 +/*
 +====================
 +LAN_GetServerStatus
 +====================
 +*/
 +int LAN_GetServerStatus( char *serverAddress, char *serverStatus, int maxLen ) {
 +	return CL_ServerStatus( serverAddress, serverStatus, maxLen );
 +}
 +
 +/*
 +====================
 +CL_GetGlConfig
 +====================
 +*/
 +static void CL_GetGlconfig( glconfig_t *config ) {
 +	*config = cls.glconfig;
 +}
 +
 +/*
 +====================
 +GetClipboardData
 +====================
 +*/
 +static void GetClipboardData( char *buf, int buflen ) {
 +	char	*cbd;
 +
 +	cbd = Sys_GetClipboardData();
 +
 +	if ( !cbd ) {
 +		*buf = 0;
 +		return;
 +	}
 +
 +	Q_strncpyz( buf, cbd, buflen );
 +
 +	Z_Free( cbd );
 +}
 +
 +/*
 +====================
 +Key_KeynumToStringBuf
 +====================
 +*/
 +static void Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) {
 +	Q_strncpyz( buf, Key_KeynumToString( keynum ), buflen );
 +}
 +
 +/*
 +====================
 +Key_GetBindingBuf
 +====================
 +*/
 +static void Key_GetBindingBuf( int keynum, char *buf, int buflen ) {
 +	char	*value;
 +
 +	value = Key_GetBinding( keynum );
 +	if ( value ) {
 +		Q_strncpyz( buf, value, buflen );
 +	}
 +	else {
 +		*buf = 0;
 +	}
 +}
 +
 +/*
 +====================
 +Key_GetCatcher
 +====================
 +*/
 +int Key_GetCatcher( void ) {
 +	return cls.keyCatchers;
 +}
 +
 +/*
 +====================
 +Ket_SetCatcher
 +====================
 +*/
 +void Key_SetCatcher( int catcher ) {
 +	cls.keyCatchers = catcher;
 +}
 +
 +
 +/*
 +====================
 +CLUI_GetCDKey
 +====================
 +*/
 +static void CLUI_GetCDKey( char *buf, int buflen ) {
 +	cvar_t	*fs;
 +	fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
 +	if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) {
 +		Com_Memcpy( buf, &cl_cdkey[16], 16);
 +		buf[16] = 0;
 +	} else {
 +		Com_Memcpy( buf, cl_cdkey, 16);
 +		buf[16] = 0;
 +	}
 +}
 +
 +
 +/*
 +====================
 +CLUI_SetCDKey
 +====================
 +*/
 +static void CLUI_SetCDKey( char *buf ) {
 +	cvar_t	*fs;
 +	fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
 +	if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) {
 +		Com_Memcpy( &cl_cdkey[16], buf, 16 );
 +		cl_cdkey[32] = 0;
 +		// set the flag so the fle will be written at the next opportunity
 +		cvar_modifiedFlags |= CVAR_ARCHIVE;
 +	} else {
 +		Com_Memcpy( cl_cdkey, buf, 16 );
 +		// set the flag so the fle will be written at the next opportunity
 +		cvar_modifiedFlags |= CVAR_ARCHIVE;
 +	}
 +}
 +
 +/*
 +====================
 +GetConfigString
 +====================
 +*/
 +static int GetConfigString(int index, char *buf, int size)
 +{
 +	int		offset;
 +
 +	if (index < 0 || index >= MAX_CONFIGSTRINGS)
 +		return qfalse;
 +
 +	offset = cl.gameState.stringOffsets[index];
 +	if (!offset) {
 +		if( size ) {
 +			buf[0] = 0;
 +		}
 +		return qfalse;
 +	}
 +
 +	Q_strncpyz( buf, cl.gameState.stringData+offset, size);
 + 
 +	return qtrue;
 +}
 +
 +/*
 +====================
 +FloatAsInt
 +====================
 +*/
 +static int FloatAsInt( float f ) {
 +	int		temp;
 +
 +	*(float *)&temp = f;
 +
 +	return temp;
 +}
 +
 +void *VM_ArgPtr( int intValue );
 +#define	VMA(x) VM_ArgPtr(args[x])
 +#define	VMF(x)	((float *)args)[x]
 +
 +/*
 +====================
 +CL_UISystemCalls
 +
 +The ui module is making a system call
 +====================
 +*/
 +int CL_UISystemCalls( int *args ) {
 +	switch( args[0] ) {
 +	case UI_ERROR:
 +		Com_Error( ERR_DROP, "%s", VMA(1) );
 +		return 0;
 +
 +	case UI_PRINT:
 +		Com_Printf( "%s", VMA(1) );
 +		return 0;
 +
 +	case UI_MILLISECONDS:
 +		return Sys_Milliseconds();
 +
 +	case UI_CVAR_REGISTER:
 +		Cvar_Register( VMA(1), VMA(2), VMA(3), args[4] ); 
 +		return 0;
 +
 +	case UI_CVAR_UPDATE:
 +		Cvar_Update( VMA(1) );
 +		return 0;
 +
 +	case UI_CVAR_SET:
 +		Cvar_Set( VMA(1), VMA(2) );
 +		return 0;
 +
 +	case UI_CVAR_VARIABLEVALUE:
 +		return FloatAsInt( Cvar_VariableValue( VMA(1) ) );
 +
 +	case UI_CVAR_VARIABLESTRINGBUFFER:
 +		Cvar_VariableStringBuffer( VMA(1), VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_CVAR_SETVALUE:
 +		Cvar_SetValue( VMA(1), VMF(2) );
 +		return 0;
 +
 +	case UI_CVAR_RESET:
 +		Cvar_Reset( VMA(1) );
 +		return 0;
 +
 +	case UI_CVAR_CREATE:
 +		Cvar_Get( VMA(1), VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_CVAR_INFOSTRINGBUFFER:
 +		Cvar_InfoStringBuffer( args[1], VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_ARGC:
 +		return Cmd_Argc();
 +
 +	case UI_ARGV:
 +		Cmd_ArgvBuffer( args[1], VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_CMD_EXECUTETEXT:
 +		Cbuf_ExecuteText( args[1], VMA(2) );
 +		return 0;
 +
 +	case UI_FS_FOPENFILE:
 +		return FS_FOpenFileByMode( VMA(1), VMA(2), args[3] );
 +
 +	case UI_FS_READ:
 +		FS_Read2( VMA(1), args[2], args[3] );
 +		return 0;
 +
 +	case UI_FS_WRITE:
 +		FS_Write( VMA(1), args[2], args[3] );
 +		return 0;
 +
 +	case UI_FS_FCLOSEFILE:
 +		FS_FCloseFile( args[1] );
 +		return 0;
 +
 +	case UI_FS_GETFILELIST:
 +		return FS_GetFileList( VMA(1), VMA(2), VMA(3), args[4] );
 +
 +	case UI_FS_SEEK:
 +		return FS_Seek( args[1], args[2], args[3] );
 +	
 +	case UI_R_REGISTERMODEL:
 +		return re.RegisterModel( VMA(1) );
 +
 +	case UI_R_REGISTERSKIN:
 +		return re.RegisterSkin( VMA(1) );
 +
 +	case UI_R_REGISTERSHADERNOMIP:
 +		return re.RegisterShaderNoMip( VMA(1) );
 +
 +	case UI_R_CLEARSCENE:
 +		re.ClearScene();
 +		return 0;
 +
 +	case UI_R_ADDREFENTITYTOSCENE:
 +		re.AddRefEntityToScene( VMA(1) );
 +		return 0;
 +
 +	case UI_R_ADDPOLYTOSCENE:
 +		re.AddPolyToScene( args[1], args[2], VMA(3), 1 );
 +		return 0;
 +
 +	case UI_R_ADDLIGHTTOSCENE:
 +		re.AddLightToScene( VMA(1), VMF(2), VMF(3), VMF(4), VMF(5) );
 +		return 0;
 +
 +	case UI_R_RENDERSCENE:
 +		re.RenderScene( VMA(1) );
 +		return 0;
 +
 +	case UI_R_SETCOLOR:
 +		re.SetColor( VMA(1) );
 +		return 0;
 +
 +	case UI_R_DRAWSTRETCHPIC:
 +		re.DrawStretchPic( VMF(1), VMF(2), VMF(3), VMF(4), VMF(5), VMF(6), VMF(7), VMF(8), args[9] );
 +		return 0;
 +
 +  case UI_R_MODELBOUNDS:
 +		re.ModelBounds( args[1], VMA(2), VMA(3) );
 +		return 0;
 +
 +	case UI_UPDATESCREEN:
 +		SCR_UpdateScreen();
 +		return 0;
 +
 +	case UI_CM_LERPTAG:
 +		re.LerpTag( VMA(1), args[2], args[3], args[4], VMF(5), VMA(6) );
 +		return 0;
 +
 +	case UI_S_REGISTERSOUND:
 +		return S_RegisterSound( VMA(1), args[2] );
 +
 +	case UI_S_STARTLOCALSOUND:
 +		S_StartLocalSound( args[1], args[2] );
 +		return 0;
 +
 +	case UI_KEY_KEYNUMTOSTRINGBUF:
 +		Key_KeynumToStringBuf( args[1], VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_KEY_GETBINDINGBUF:
 +		Key_GetBindingBuf( args[1], VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_KEY_SETBINDING:
 +		Key_SetBinding( args[1], VMA(2) );
 +		return 0;
 +
 +	case UI_KEY_ISDOWN:
 +		return Key_IsDown( args[1] );
 +
 +	case UI_KEY_GETOVERSTRIKEMODE:
 +		return Key_GetOverstrikeMode();
 +
 +	case UI_KEY_SETOVERSTRIKEMODE:
 +		Key_SetOverstrikeMode( args[1] );
 +		return 0;
 +
 +	case UI_KEY_CLEARSTATES:
 +		Key_ClearStates();
 +		return 0;
 +
 +	case UI_KEY_GETCATCHER:
 +		return Key_GetCatcher();
 +
 +	case UI_KEY_SETCATCHER:
 +		Key_SetCatcher( args[1] );
 +		return 0;
 +
 +	case UI_GETCLIPBOARDDATA:
 +		GetClipboardData( VMA(1), args[2] );
 +		return 0;
 +
 +	case UI_GETCLIENTSTATE:
 +		GetClientState( VMA(1) );
 +		return 0;		
 +
 +	case UI_GETGLCONFIG:
 +		CL_GetGlconfig( VMA(1) );
 +		return 0;
 +
 +	case UI_GETCONFIGSTRING:
 +		return GetConfigString( args[1], VMA(2), args[3] );
 +
 +	case UI_LAN_LOADCACHEDSERVERS:
 +		LAN_LoadCachedServers();
 +		return 0;
 +
 +	case UI_LAN_SAVECACHEDSERVERS:
 +		LAN_SaveServersToCache();
 +		return 0;
 +
 +	case UI_LAN_ADDSERVER:
 +		return LAN_AddServer(args[1], VMA(2), VMA(3));
 +
 +	case UI_LAN_REMOVESERVER:
 +		LAN_RemoveServer(args[1], VMA(2));
 +		return 0;
 +
 +	case UI_LAN_GETPINGQUEUECOUNT:
 +		return LAN_GetPingQueueCount();
 +
 +	case UI_LAN_CLEARPING:
 +		LAN_ClearPing( args[1] );
 +		return 0;
 +
 +	case UI_LAN_GETPING:
 +		LAN_GetPing( args[1], VMA(2), args[3], VMA(4) );
 +		return 0;
 +
 +	case UI_LAN_GETPINGINFO:
 +		LAN_GetPingInfo( args[1], VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_LAN_GETSERVERCOUNT:
 +		return LAN_GetServerCount(args[1]);
 +
 +	case UI_LAN_GETSERVERADDRESSSTRING:
 +		LAN_GetServerAddressString( args[1], args[2], VMA(3), args[4] );
 +		return 0;
 +
 +	case UI_LAN_GETSERVERINFO:
 +		LAN_GetServerInfo( args[1], args[2], VMA(3), args[4] );
 +		return 0;
 +
 +	case UI_LAN_GETSERVERPING:
 +		return LAN_GetServerPing( args[1], args[2] );
 +
 +	case UI_LAN_MARKSERVERVISIBLE:
 +		LAN_MarkServerVisible( args[1], args[2], args[3] );
 +		return 0;
 +
 +	case UI_LAN_SERVERISVISIBLE:
 +		return LAN_ServerIsVisible( args[1], args[2] );
 +
 +	case UI_LAN_UPDATEVISIBLEPINGS:
 +		return LAN_UpdateVisiblePings( args[1] );
 +
 +	case UI_LAN_RESETPINGS:
 +		LAN_ResetPings( args[1] );
 +		return 0;
 +
 +	case UI_LAN_SERVERSTATUS:
 +		return LAN_GetServerStatus( VMA(1), VMA(2), args[3] );
 +
 +	case UI_LAN_COMPARESERVERS:
 +		return LAN_CompareServers( args[1], args[2], args[3], args[4], args[5] );
 +
 +	case UI_MEMORY_REMAINING:
 +		return Hunk_MemoryRemaining();
 +
 +	case UI_GET_CDKEY:
 +		CLUI_GetCDKey( VMA(1), args[2] );
 +		return 0;
 +
 +	case UI_SET_CDKEY:
 +		CLUI_SetCDKey( VMA(1) );
 +		return 0;
 +	
 +	case UI_SET_PBCLSTATUS:
 +		return 0;	
 +
 +	case UI_R_REGISTERFONT:
 +		re.RegisterFont( VMA(1), args[2], VMA(3));
 +		return 0;
 +
 +	case UI_MEMSET:
 +		Com_Memset( VMA(1), args[2], args[3] );
 +		return 0;
 +
 +	case UI_MEMCPY:
 +		Com_Memcpy( VMA(1), VMA(2), args[3] );
 +		return 0;
 +
 +	case UI_STRNCPY:
 +		return (int)strncpy( VMA(1), VMA(2), args[3] );
 +
 +	case UI_SIN:
 +		return FloatAsInt( sin( VMF(1) ) );
 +
 +	case UI_COS:
 +		return FloatAsInt( cos( VMF(1) ) );
 +
 +	case UI_ATAN2:
 +		return FloatAsInt( atan2( VMF(1), VMF(2) ) );
 +
 +	case UI_SQRT:
 +		return FloatAsInt( sqrt( VMF(1) ) );
 +
 +	case UI_FLOOR:
 +		return FloatAsInt( floor( VMF(1) ) );
 +
 +	case UI_CEIL:
 +		return FloatAsInt( ceil( VMF(1) ) );
 +
 +	case UI_PC_ADD_GLOBAL_DEFINE:
 +		return botlib_export->PC_AddGlobalDefine( VMA(1) );
 +	case UI_PC_LOAD_SOURCE:
 +		return botlib_export->PC_LoadSourceHandle( VMA(1) );
 +	case UI_PC_FREE_SOURCE:
 +		return botlib_export->PC_FreeSourceHandle( args[1] );
 +	case UI_PC_READ_TOKEN:
 +		return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) );
 +	case UI_PC_SOURCE_FILE_AND_LINE:
 +		return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) );
 +
 +	case UI_S_STOPBACKGROUNDTRACK:
 +		S_StopBackgroundTrack();
 +		return 0;
 +	case UI_S_STARTBACKGROUNDTRACK:
 +		S_StartBackgroundTrack( VMA(1), VMA(2));
 +		return 0;
 +
 +	case UI_REAL_TIME:
 +		return Com_RealTime( VMA(1) );
 +
 +	case UI_CIN_PLAYCINEMATIC:
 +	  Com_DPrintf("UI_CIN_PlayCinematic\n");
 +	  return CIN_PlayCinematic(VMA(1), args[2], args[3], args[4], args[5], args[6]);
 +
 +	case UI_CIN_STOPCINEMATIC:
 +	  return CIN_StopCinematic(args[1]);
 +
 +	case UI_CIN_RUNCINEMATIC:
 +	  return CIN_RunCinematic(args[1]);
 +
 +	case UI_CIN_DRAWCINEMATIC:
 +	  CIN_DrawCinematic(args[1]);
 +	  return 0;
 +
 +	case UI_CIN_SETEXTENTS:
 +	  CIN_SetExtents(args[1], args[2], args[3], args[4], args[5]);
 +	  return 0;
 +
 +	case UI_R_REMAP_SHADER:
 +		re.RemapShader( VMA(1), VMA(2), VMA(3) );
 +		return 0;
 +
 +	case UI_VERIFY_CDKEY:
 +		return CL_CDKeyValidate(VMA(1), VMA(2));
 +
 +
 +		
 +	default:
 +		Com_Error( ERR_DROP, "Bad UI system trap: %i", args[0] );
 +
 +	}
 +
 +	return 0;
 +}
 +
 +/*
 +====================
 +CL_ShutdownUI
 +====================
 +*/
 +void CL_ShutdownUI( void ) {
 +	cls.keyCatchers &= ~KEYCATCH_UI;
 +	cls.uiStarted = qfalse;
 +	if ( !uivm ) {
 +		return;
 +	}
 +	VM_Call( uivm, UI_SHUTDOWN );
 +	VM_Free( uivm );
 +	uivm = NULL;
 +}
 +
 +/*
 +====================
 +CL_InitUI
 +====================
 +*/
 +#define UI_OLD_API_VERSION	4
 +
 +void CL_InitUI( void ) {
 +	int		v;
 +	vmInterpret_t		interpret;
 +
 +	// load the dll or bytecode
 +	if ( cl_connectedToPureServer != 0 ) {
 +		// if sv_pure is set we only allow qvms to be loaded
 +		interpret = VMI_COMPILED;
 +	}
 +	else {
 +		interpret = Cvar_VariableValue( "vm_ui" );
 +	}
 +	uivm = VM_Create( "ui", CL_UISystemCalls, interpret );
 +	if ( !uivm ) {
 +		Com_Error( ERR_FATAL, "VM_Create on UI failed" );
 +	}
 +
 +	// sanity check
 +	v = VM_Call( uivm, UI_GETAPIVERSION );
 +	if (v == UI_OLD_API_VERSION) {
 +//		Com_Printf(S_COLOR_YELLOW "WARNING: loading old Quake III Arena User Interface version %d\n", v );
 +		// init for this gamestate
 +		VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE));
 +	}
 +	else if (v != UI_API_VERSION) {
 +		Com_Error( ERR_DROP, "User Interface is version %d, expected %d", v, UI_API_VERSION );
 +		cls.uiStarted = qfalse;
 +	}
 +	else {
 +		// init for this gamestate
 +		VM_Call( uivm, UI_INIT, (cls.state >= CA_AUTHORIZING && cls.state < CA_ACTIVE) );
 +	}
 +}
 +
 +qboolean UI_usesUniqueCDKey() {
 +	if (uivm) {
 +		return (VM_Call( uivm, UI_HASUNIQUECDKEY) == qtrue);
 +	} else {
 +		return qfalse;
 +	}
 +}
 +
 +/*
 +====================
 +UI_GameCommand
 +
 +See if the current console command is claimed by the ui
 +====================
 +*/
 +qboolean UI_GameCommand( void ) {
 +	if ( !uivm ) {
 +		return qfalse;
 +	}
 +
 +	return VM_Call( uivm, UI_CONSOLE_COMMAND, cls.realtime );
 +}
 diff --git a/code/client/client.h b/code/client/client.h new file mode 100755 index 0000000..09e02a8 --- /dev/null +++ b/code/client/client.h @@ -0,0 +1,519 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// client.h -- primary header for client
 +
 +#include "../game/q_shared.h"
 +#include "../qcommon/qcommon.h"
 +#include "../renderer/tr_public.h"
 +#include "../ui/ui_public.h"
 +#include "keys.h"
 +#include "snd_public.h"
 +#include "../cgame/cg_public.h"
 +#include "../game/bg_public.h"
 +
 +#define	RETRANSMIT_TIMEOUT	3000	// time between connection packet retransmits
 +
 +
 +// snapshots are a view of the server at a given time
 +typedef struct {
 +	qboolean		valid;			// cleared if delta parsing was invalid
 +	int				snapFlags;		// rate delayed and dropped commands
 +
 +	int				serverTime;		// server time the message is valid for (in msec)
 +
 +	int				messageNum;		// copied from netchan->incoming_sequence
 +	int				deltaNum;		// messageNum the delta is from
 +	int				ping;			// time from when cmdNum-1 was sent to time packet was reeceived
 +	byte			areamask[MAX_MAP_AREA_BYTES];		// portalarea visibility bits
 +
 +	int				cmdNum;			// the next cmdNum the server is expecting
 +	playerState_t	ps;						// complete information about the current player at this time
 +
 +	int				numEntities;			// all of the entities that need to be presented
 +	int				parseEntitiesNum;		// at the time of this snapshot
 +
 +	int				serverCommandNum;		// execute all commands up to this before
 +											// making the snapshot current
 +} clSnapshot_t;
 +
 +
 +
 +/*
 +=============================================================================
 +
 +the clientActive_t structure is wiped completely at every
 +new gamestate_t, potentially several times during an established connection
 +
 +=============================================================================
 +*/
 +
 +typedef struct {
 +	int		p_cmdNumber;		// cl.cmdNumber when packet was sent
 +	int		p_serverTime;		// usercmd->serverTime when packet was sent
 +	int		p_realtime;			// cls.realtime when packet was sent
 +} outPacket_t;
 +
 +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of
 +// entities, so that when a delta compressed message arives from the server
 +// it can be un-deltad from the original 
 +#define	MAX_PARSE_ENTITIES	2048
 +
 +extern int g_console_field_width;
 +
 +typedef struct {
 +	int			timeoutcount;		// it requres several frames in a timeout condition
 +									// to disconnect, preventing debugging breaks from
 +									// causing immediate disconnects on continue
 +	clSnapshot_t	snap;			// latest received from server
 +
 +	int			serverTime;			// may be paused during play
 +	int			oldServerTime;		// to prevent time from flowing bakcwards
 +	int			oldFrameServerTime;	// to check tournament restarts
 +	int			serverTimeDelta;	// cl.serverTime = cls.realtime + cl.serverTimeDelta
 +									// this value changes as net lag varies
 +	qboolean	extrapolatedSnapshot;	// set if any cgame frame has been forced to extrapolate
 +									// cleared when CL_AdjustTimeDelta looks at it
 +	qboolean	newSnapshots;		// set on parse of any valid packet
 +
 +	gameState_t	gameState;			// configstrings
 +	char		mapname[MAX_QPATH];	// extracted from CS_SERVERINFO
 +
 +	int			parseEntitiesNum;	// index (not anded off) into cl_parse_entities[]
 +
 +	int			mouseDx[2], mouseDy[2];	// added to by mouse events
 +	int			mouseIndex;
 +	int			joystickAxis[MAX_JOYSTICK_AXIS];	// set by joystick events
 +
 +	// cgame communicates a few values to the client system
 +	int			cgameUserCmdValue;	// current weapon to add to usercmd_t
 +	float		cgameSensitivity;
 +
 +	// cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last
 +	// properly generated command
 +	usercmd_t	cmds[CMD_BACKUP];	// each mesage will send several old cmds
 +	int			cmdNumber;			// incremented each frame, because multiple
 +									// frames may need to be packed into a single packet
 +
 +	outPacket_t	outPackets[PACKET_BACKUP];	// information about each packet we have sent out
 +
 +	// the client maintains its own idea of view angles, which are
 +	// sent to the server each frame.  It is cleared to 0 upon entering each level.
 +	// the server sends a delta each frame which is added to the locally
 +	// tracked view angles to account for standing on rotating objects,
 +	// and teleport direction changes
 +	vec3_t		viewangles;
 +
 +	int			serverId;			// included in each client message so the server
 +												// can tell if it is for a prior map_restart
 +	// big stuff at end of structure so most offsets are 15 bits or less
 +	clSnapshot_t	snapshots[PACKET_BACKUP];
 +
 +	entityState_t	entityBaselines[MAX_GENTITIES];	// for delta compression when not in previous frame
 +
 +	entityState_t	parseEntities[MAX_PARSE_ENTITIES];
 +} clientActive_t;
 +
 +extern	clientActive_t		cl;
 +
 +/*
 +=============================================================================
 +
 +the clientConnection_t structure is wiped when disconnecting from a server,
 +either to go to a full screen console, play a demo, or connect to a different server
 +
 +A connection can be to either a server through the network layer or a
 +demo through a file.
 +
 +=============================================================================
 +*/
 +
 +
 +typedef struct {
 +
 +	int			clientNum;
 +	int			lastPacketSentTime;			// for retransmits during connection
 +	int			lastPacketTime;				// for timeouts
 +
 +	netadr_t	serverAddress;
 +	int			connectTime;				// for connection retransmits
 +	int			connectPacketCount;			// for display on connection dialog
 +	char		serverMessage[MAX_STRING_TOKENS];	// for display on connection dialog
 +
 +	int			challenge;					// from the server to use for connecting
 +	int			checksumFeed;				// from the server for checksum calculations
 +
 +	// these are our reliable messages that go to the server
 +	int			reliableSequence;
 +	int			reliableAcknowledge;		// the last one the server has executed
 +	char		reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
 +
 +	// server message (unreliable) and command (reliable) sequence
 +	// numbers are NOT cleared at level changes, but continue to
 +	// increase as long as the connection is valid
 +
 +	// message sequence is used by both the network layer and the
 +	// delta compression layer
 +	int			serverMessageSequence;
 +
 +	// reliable messages received from server
 +	int			serverCommandSequence;
 +	int			lastExecutedServerCommand;		// last server command grabbed or executed with CL_GetServerCommand
 +	char		serverCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
 +
 +	// file transfer from server
 +	fileHandle_t download;
 +	char		downloadTempName[MAX_OSPATH];
 +	char		downloadName[MAX_OSPATH];
 +	int			downloadNumber;
 +	int			downloadBlock;	// block we are waiting for
 +	int			downloadCount;	// how many bytes we got
 +	int			downloadSize;	// how many bytes we got
 +	char		downloadList[MAX_INFO_STRING]; // list of paks we need to download
 +	qboolean	downloadRestart;	// if true, we need to do another FS_Restart because we downloaded a pak
 +
 +	// demo information
 +	char		demoName[MAX_QPATH];
 +	qboolean	spDemoRecording;
 +	qboolean	demorecording;
 +	qboolean	demoplaying;
 +	qboolean	demowaiting;	// don't record until a non-delta message is received
 +	qboolean	firstDemoFrameSkipped;
 +	fileHandle_t	demofile;
 +
 +	int			timeDemoFrames;		// counter of rendered frames
 +	int			timeDemoStart;		// cls.realtime before first frame
 +	int			timeDemoBaseTime;	// each frame will be at this time + frameNum * 50
 +
 +	// big stuff at end of structure so most offsets are 15 bits or less
 +	netchan_t	netchan;
 +} clientConnection_t;
 +
 +extern	clientConnection_t clc;
 +
 +/*
 +==================================================================
 +
 +the clientStatic_t structure is never wiped, and is used even when
 +no client connection is active at all
 +
 +==================================================================
 +*/
 +
 +typedef struct {
 +	netadr_t	adr;
 +	int			start;
 +	int			time;
 +	char		info[MAX_INFO_STRING];
 +} ping_t;
 +
 +typedef struct {
 +	netadr_t	adr;
 +	char	  	hostName[MAX_NAME_LENGTH];
 +	char	  	mapName[MAX_NAME_LENGTH];
 +	char	  	game[MAX_NAME_LENGTH];
 +	int			netType;
 +	int			gameType;
 +	int		  	clients;
 +	int		  	maxClients;
 +	int			minPing;
 +	int			maxPing;
 +	int			ping;
 +	qboolean	visible;
 +	int			punkbuster;
 +} serverInfo_t;
 +
 +typedef struct {
 +	byte	ip[4];
 +	unsigned short	port;
 +} serverAddress_t;
 +
 +typedef struct {
 +	connstate_t	state;				// connection status
 +	int			keyCatchers;		// bit flags
 +
 +	qboolean	cddialog;			// bring up the cd needed dialog next frame
 +
 +	char		servername[MAX_OSPATH];		// name of server from original connect (used by reconnect)
 +
 +	// when the server clears the hunk, all of these must be restarted
 +	qboolean	rendererStarted;
 +	qboolean	soundStarted;
 +	qboolean	soundRegistered;
 +	qboolean	uiStarted;
 +	qboolean	cgameStarted;
 +
 +	int			framecount;
 +	int			frametime;			// msec since last frame
 +
 +	int			realtime;			// ignores pause
 +	int			realFrametime;		// ignoring pause, so console always works
 +
 +	int			numlocalservers;
 +	serverInfo_t	localServers[MAX_OTHER_SERVERS];
 +
 +	int			numglobalservers;
 +	serverInfo_t  globalServers[MAX_GLOBAL_SERVERS];
 +	// additional global servers
 +	int			numGlobalServerAddresses;
 +	serverAddress_t		globalServerAddresses[MAX_GLOBAL_SERVERS];
 +
 +	int			numfavoriteservers;
 +	serverInfo_t	favoriteServers[MAX_OTHER_SERVERS];
 +
 +	int			nummplayerservers;
 +	serverInfo_t	mplayerServers[MAX_OTHER_SERVERS];
 +
 +	int pingUpdateSource;		// source currently pinging or updating
 +
 +	int masterNum;
 +
 +	// update server info
 +	netadr_t	updateServer;
 +	char		updateChallenge[MAX_TOKEN_CHARS];
 +	char		updateInfoString[MAX_INFO_STRING];
 +
 +	netadr_t	authorizeServer;
 +
 +	// rendering info
 +	glconfig_t	glconfig;
 +	qhandle_t	charSetShader;
 +	qhandle_t	whiteShader;
 +	qhandle_t	consoleShader;
 +} clientStatic_t;
 +
 +extern	clientStatic_t		cls;
 +
 +//=============================================================================
 +
 +extern	vm_t			*cgvm;	// interface to cgame dll or vm
 +extern	vm_t			*uivm;	// interface to ui dll or vm
 +extern	refexport_t		re;		// interface to refresh .dll
 +
 +
 +//
 +// cvars
 +//
 +extern	cvar_t	*cl_nodelta;
 +extern	cvar_t	*cl_debugMove;
 +extern	cvar_t	*cl_noprint;
 +extern	cvar_t	*cl_timegraph;
 +extern	cvar_t	*cl_maxpackets;
 +extern	cvar_t	*cl_packetdup;
 +extern	cvar_t	*cl_shownet;
 +extern	cvar_t	*cl_showSend;
 +extern	cvar_t	*cl_timeNudge;
 +extern	cvar_t	*cl_showTimeDelta;
 +extern	cvar_t	*cl_freezeDemo;
 +
 +extern	cvar_t	*cl_yawspeed;
 +extern	cvar_t	*cl_pitchspeed;
 +extern	cvar_t	*cl_run;
 +extern	cvar_t	*cl_anglespeedkey;
 +
 +extern	cvar_t	*cl_sensitivity;
 +extern	cvar_t	*cl_freelook;
 +
 +extern	cvar_t	*cl_mouseAccel;
 +extern	cvar_t	*cl_showMouseRate;
 +
 +extern	cvar_t	*m_pitch;
 +extern	cvar_t	*m_yaw;
 +extern	cvar_t	*m_forward;
 +extern	cvar_t	*m_side;
 +extern	cvar_t	*m_filter;
 +
 +extern	cvar_t	*cl_timedemo;
 +
 +extern	cvar_t	*cl_activeAction;
 +
 +extern	cvar_t	*cl_allowDownload;
 +extern	cvar_t	*cl_conXOffset;
 +extern	cvar_t	*cl_inGameVideo;
 +
 +//=================================================
 +
 +//
 +// cl_main
 +//
 +
 +void CL_Init (void);
 +void CL_FlushMemory(void);
 +void CL_ShutdownAll(void);
 +void CL_AddReliableCommand( const char *cmd );
 +
 +void CL_StartHunkUsers( void );
 +
 +void CL_Disconnect_f (void);
 +void CL_GetChallengePacket (void);
 +void CL_Vid_Restart_f( void );
 +void CL_Snd_Restart_f (void);
 +void CL_StartDemoLoop( void );
 +void CL_NextDemo( void );
 +void CL_ReadDemoMessage( void );
 +
 +void CL_InitDownloads(void);
 +void CL_NextDownload(void);
 +
 +void CL_GetPing( int n, char *buf, int buflen, int *pingtime );
 +void CL_GetPingInfo( int n, char *buf, int buflen );
 +void CL_ClearPing( int n );
 +int CL_GetPingQueueCount( void );
 +
 +void CL_ShutdownRef( void );
 +void CL_InitRef( void );
 +qboolean CL_CDKeyValidate( const char *key, const char *checksum );
 +int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen );
 +
 +
 +//
 +// cl_input
 +//
 +typedef struct {
 +	int			down[2];		// key nums holding it down
 +	unsigned	downtime;		// msec timestamp
 +	unsigned	msec;			// msec down this frame if both a down and up happened
 +	qboolean	active;			// current state
 +	qboolean	wasPressed;		// set when down, not cleared when up
 +} kbutton_t;
 +
 +extern	kbutton_t	in_mlook, in_klook;
 +extern 	kbutton_t 	in_strafe;
 +extern 	kbutton_t 	in_speed;
 +
 +void CL_InitInput (void);
 +void CL_SendCmd (void);
 +void CL_ClearState (void);
 +void CL_ReadPackets (void);
 +
 +void CL_WritePacket( void );
 +void IN_CenterView (void);
 +
 +void CL_VerifyCode( void );
 +
 +float CL_KeyState (kbutton_t *key);
 +char *Key_KeynumToString (int keynum);
 +
 +//
 +// cl_parse.c
 +//
 +extern int cl_connectedToPureServer;
 +
 +void CL_SystemInfoChanged( void );
 +void CL_ParseServerMessage( msg_t *msg );
 +
 +//====================================================================
 +
 +void	CL_ServerInfoPacket( netadr_t from, msg_t *msg );
 +void	CL_LocalServers_f( void );
 +void	CL_GlobalServers_f( void );
 +void	CL_FavoriteServers_f( void );
 +void	CL_Ping_f( void );
 +qboolean CL_UpdateVisiblePings_f( int source );
 +
 +
 +//
 +// console
 +//
 +void Con_DrawCharacter (int cx, int line, int num);
 +
 +void Con_CheckResize (void);
 +void Con_Init (void);
 +void Con_Clear_f (void);
 +void Con_ToggleConsole_f (void);
 +void Con_DrawNotify (void);
 +void Con_ClearNotify (void);
 +void Con_RunConsole (void);
 +void Con_DrawConsole (void);
 +void Con_PageUp( void );
 +void Con_PageDown( void );
 +void Con_Top( void );
 +void Con_Bottom( void );
 +void Con_Close( void );
 +
 +
 +//
 +// cl_scrn.c
 +//
 +void	SCR_Init (void);
 +void	SCR_UpdateScreen (void);
 +
 +void	SCR_DebugGraph (float value, int color);
 +
 +int		SCR_GetBigStringWidth( const char *str );	// returns in virtual 640x480 coordinates
 +
 +void	SCR_AdjustFrom640( float *x, float *y, float *w, float *h );
 +void	SCR_FillRect( float x, float y, float width, float height, 
 +					 const float *color );
 +void	SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
 +void	SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname );
 +
 +void	SCR_DrawBigString( int x, int y, const char *s, float alpha );			// draws a string with embedded color control characters with fade
 +void	SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color );	// ignores embedded color control characters
 +void	SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor );
 +void	SCR_DrawSmallChar( int x, int y, int ch );
 +
 +
 +//
 +// cl_cin.c
 +//
 +
 +void CL_PlayCinematic_f( void );
 +void SCR_DrawCinematic (void);
 +void SCR_RunCinematic (void);
 +void SCR_StopCinematic (void);
 +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits);
 +e_status CIN_StopCinematic(int handle);
 +e_status CIN_RunCinematic (int handle);
 +void CIN_DrawCinematic (int handle);
 +void CIN_SetExtents (int handle, int x, int y, int w, int h);
 +void CIN_SetLooping (int handle, qboolean loop);
 +void CIN_UploadCinematic(int handle);
 +void CIN_CloseAllVideos(void);
 +
 +//
 +// cl_cgame.c
 +//
 +void CL_InitCGame( void );
 +void CL_ShutdownCGame( void );
 +qboolean CL_GameCommand( void );
 +void CL_CGameRendering( stereoFrame_t stereo );
 +void CL_SetCGameTime( void );
 +void CL_FirstSnapshot( void );
 +void CL_ShaderStateChanged(void);
 +
 +//
 +// cl_ui.c
 +//
 +void CL_InitUI( void );
 +void CL_ShutdownUI( void );
 +int Key_GetCatcher( void );
 +void Key_SetCatcher( int catcher );
 +void LAN_LoadCachedServers();
 +void LAN_SaveServersToCache();
 +
 +
 +//
 +// cl_net_chan.c
 +//
 +void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg);	//int length, const byte *data );
 +void CL_Netchan_TransmitNextFragment( netchan_t *chan );
 +qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg );
 diff --git a/code/client/keys.h b/code/client/keys.h new file mode 100755 index 0000000..bc598f4 --- /dev/null +++ b/code/client/keys.h @@ -0,0 +1,57 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +#include "../ui/keycodes.h"
 +
 +#define	MAX_KEYS		256
 +
 +typedef struct {
 +	qboolean	down;
 +	int			repeats;		// if > 1, it is autorepeating
 +	char		*binding;
 +} qkey_t;
 +
 +extern	qboolean	key_overstrikeMode;
 +extern	qkey_t		keys[MAX_KEYS];
 +
 +// NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h
 +void Field_KeyDownEvent( field_t *edit, int key );
 +void Field_CharEvent( field_t *edit, int ch );
 +void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor );
 +void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor );
 +
 +#define		COMMAND_HISTORY		32
 +extern	field_t	historyEditLines[COMMAND_HISTORY];
 +
 +extern	field_t	g_consoleField;
 +extern	field_t	chatField;
 +extern	qboolean	anykeydown;
 +extern	qboolean	chat_team;
 +extern	int			chat_playerNum;
 +
 +void Key_WriteBindings( fileHandle_t f );
 +void Key_SetBinding( int keynum, const char *binding );
 +char *Key_GetBinding( int keynum );
 +qboolean Key_IsDown( int keynum );
 +qboolean Key_GetOverstrikeMode( void );
 +void Key_SetOverstrikeMode( qboolean state );
 +void Key_ClearStates( void );
 +int Key_GetKey(const char *binding);
 diff --git a/code/client/snd_adpcm.c b/code/client/snd_adpcm.c new file mode 100755 index 0000000..bc52ad4 --- /dev/null +++ b/code/client/snd_adpcm.c @@ -0,0 +1,330 @@ +/***********************************************************
 +Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
 +Netherlands.
 +
 +                        All Rights Reserved
 +
 +Permission to use, copy, modify, and distribute this software and its 
 +documentation for any purpose and without fee is hereby granted, 
 +provided that the above copyright notice appear in all copies and that
 +both that copyright notice and this permission notice appear in 
 +supporting documentation, and that the names of Stichting Mathematisch
 +Centrum or CWI not be used in advertising or publicity pertaining to
 +distribution of the software without specific, written prior permission.
 +
 +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
 +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
 +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 +
 +******************************************************************/
 +
 +/*
 +** Intel/DVI ADPCM coder/decoder.
 +**
 +** The algorithm for this coder was taken from the IMA Compatability Project
 +** proceedings, Vol 2, Number 2; May 1992.
 +**
 +** Version 1.2, 18-Dec-92.
 +*/
 +
 +#include "snd_local.h"
 +
 +
 +/* Intel ADPCM step variation table */
 +static int indexTable[16] = {
 +    -1, -1, -1, -1, 2, 4, 6, 8,
 +    -1, -1, -1, -1, 2, 4, 6, 8,
 +};
 +
 +static int stepsizeTable[89] = {
 +    7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
 +    19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
 +    50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
 +    130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
 +    337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
 +    876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
 +    2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
 +    5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
 +    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
 +};
 +
 +   
 +void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) {
 +    short *inp;			/* Input buffer pointer */
 +    signed char *outp;		/* output buffer pointer */
 +    int val;			/* Current input sample value */
 +    int sign;			/* Current adpcm sign bit */
 +    int delta;			/* Current adpcm output value */
 +    int diff;			/* Difference between val and sample */
 +    int step;			/* Stepsize */
 +    int valpred;		/* Predicted output value */
 +    int vpdiff;			/* Current change to valpred */
 +    int index;			/* Current step change index */
 +    int outputbuffer;		/* place to keep previous 4-bit value */
 +    int bufferstep;		/* toggle between outputbuffer/output */
 +
 +    outp = (signed char *)outdata;
 +    inp = indata;
 +
 +    valpred = state->sample;
 +    index = state->index;
 +    step = stepsizeTable[index];
 +    
 +	outputbuffer = 0;	// quiet a compiler warning
 +    bufferstep = 1;
 +
 +    for ( ; len > 0 ; len-- ) {
 +		val = *inp++;
 +
 +		/* Step 1 - compute difference with previous value */
 +		diff = val - valpred;
 +		sign = (diff < 0) ? 8 : 0;
 +		if ( sign ) diff = (-diff);
 +
 +		/* Step 2 - Divide and clamp */
 +		/* Note:
 +		** This code *approximately* computes:
 +		**    delta = diff*4/step;
 +		**    vpdiff = (delta+0.5)*step/4;
 +		** but in shift step bits are dropped. The net result of this is
 +		** that even if you have fast mul/div hardware you cannot put it to
 +		** good use since the fixup would be too expensive.
 +		*/
 +		delta = 0;
 +		vpdiff = (step >> 3);
 +		
 +		if ( diff >= step ) {
 +			delta = 4;
 +			diff -= step;
 +			vpdiff += step;
 +		}
 +		step >>= 1;
 +		if ( diff >= step  ) {
 +			delta |= 2;
 +			diff -= step;
 +			vpdiff += step;
 +		}
 +		step >>= 1;
 +		if ( diff >= step ) {
 +			delta |= 1;
 +			vpdiff += step;
 +		}
 +
 +		/* Step 3 - Update previous value */
 +		if ( sign )
 +		  valpred -= vpdiff;
 +		else
 +		  valpred += vpdiff;
 +
 +		/* Step 4 - Clamp previous value to 16 bits */
 +		if ( valpred > 32767 )
 +		  valpred = 32767;
 +		else if ( valpred < -32768 )
 +		  valpred = -32768;
 +
 +		/* Step 5 - Assemble value, update index and step values */
 +		delta |= sign;
 +		
 +		index += indexTable[delta];
 +		if ( index < 0 ) index = 0;
 +		if ( index > 88 ) index = 88;
 +		step = stepsizeTable[index];
 +
 +		/* Step 6 - Output value */
 +		if ( bufferstep ) {
 +			outputbuffer = (delta << 4) & 0xf0;
 +		} else {
 +			*outp++ = (delta & 0x0f) | outputbuffer;
 +		}
 +		bufferstep = !bufferstep;
 +    }
 +
 +    /* Output last step, if needed */
 +    if ( !bufferstep )
 +      *outp++ = outputbuffer;
 +    
 +    state->sample = valpred;
 +    state->index = index;
 +}
 +
 +
 +/* static */ void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) {
 +    signed char *inp;		/* Input buffer pointer */
 +    int outp;			/* output buffer pointer */
 +    int sign;			/* Current adpcm sign bit */
 +    int delta;			/* Current adpcm output value */
 +    int step;			/* Stepsize */
 +    int valpred;		/* Predicted value */
 +    int vpdiff;			/* Current change to valpred */
 +    int index;			/* Current step change index */
 +    int inputbuffer;		/* place to keep next 4-bit value */
 +    int bufferstep;		/* toggle between inputbuffer/input */
 +
 +    outp = 0;
 +    inp = (signed char *)indata;
 +
 +    valpred = state->sample;
 +    index = state->index;
 +    step = stepsizeTable[index];
 +
 +    bufferstep = 0;
 +    inputbuffer = 0;	// quiet a compiler warning
 +    for ( ; len > 0 ; len-- ) {
 +		
 +		/* Step 1 - get the delta value */
 +		if ( bufferstep ) {
 +			delta = inputbuffer & 0xf;
 +		} else {
 +			inputbuffer = *inp++;
 +			delta = (inputbuffer >> 4) & 0xf;
 +		}
 +		bufferstep = !bufferstep;
 +
 +		/* Step 2 - Find new index value (for later) */
 +		index += indexTable[delta];
 +		if ( index < 0 ) index = 0;
 +		if ( index > 88 ) index = 88;
 +
 +		/* Step 3 - Separate sign and magnitude */
 +		sign = delta & 8;
 +		delta = delta & 7;
 +
 +		/* Step 4 - Compute difference and new predicted value */
 +		/*
 +		** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
 +		** in adpcm_coder.
 +		*/
 +		vpdiff = step >> 3;
 +		if ( delta & 4 ) vpdiff += step;
 +		if ( delta & 2 ) vpdiff += step>>1;
 +		if ( delta & 1 ) vpdiff += step>>2;
 +
 +		if ( sign )
 +		  valpred -= vpdiff;
 +		else
 +		  valpred += vpdiff;
 +
 +		/* Step 5 - clamp output value */
 +		if ( valpred > 32767 )
 +		  valpred = 32767;
 +		else if ( valpred < -32768 )
 +		  valpred = -32768;
 +
 +		/* Step 6 - Update step value */
 +		step = stepsizeTable[index];
 +
 +		/* Step 7 - Output value */
 +		outdata[outp] = valpred;
 +		outp++;
 +    }
 +
 +    state->sample = valpred;
 +    state->index = index;
 +}
 +
 +
 +/*
 +====================
 +S_AdpcmMemoryNeeded
 +
 +Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format
 +====================
 +*/
 +int S_AdpcmMemoryNeeded( const wavinfo_t *info ) {
 +	float	scale;
 +	int		scaledSampleCount;
 +	int		sampleMemory;
 +	int		blockCount;
 +	int		headerMemory;
 +
 +	// determine scale to convert from input sampling rate to desired sampling rate
 +	scale = (float)info->rate / dma.speed;
 +
 +	// calc number of samples at playback sampling rate
 +	scaledSampleCount = info->samples / scale;
 +
 +	// calc memory need to store those samples using ADPCM at 4 bits per sample
 +	sampleMemory = scaledSampleCount / 2;
 +
 +	// calc number of sample blocks needed of PAINTBUFFER_SIZE
 +	blockCount = scaledSampleCount / PAINTBUFFER_SIZE;
 +	if( scaledSampleCount % PAINTBUFFER_SIZE ) {
 +		blockCount++;
 +	}
 +
 +	// calc memory needed to store the block headers
 +	headerMemory = blockCount * sizeof(adpcm_state_t);
 +
 +	return sampleMemory + headerMemory;
 +}
 +
 +
 +/*
 +====================
 +S_AdpcmGetSamples
 +====================
 +*/
 +void S_AdpcmGetSamples(sndBuffer *chunk, short *to) {
 +	adpcm_state_t	state;
 +	byte			*out;
 +
 +	// get the starting state from the block header
 +	state.index = chunk->adpcm.index;
 +	state.sample = chunk->adpcm.sample;
 +
 +	out = (byte *)chunk->sndChunk;
 +	// get samples
 +	S_AdpcmDecode( out, to, SND_CHUNK_SIZE_BYTE*2, &state );
 +}
 +
 +
 +/*
 +====================
 +S_AdpcmEncodeSound
 +====================
 +*/
 +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
 +	adpcm_state_t	state;
 +	int				inOffset;
 +	int				count;
 +	int				n;
 +	sndBuffer		*newchunk, *chunk;
 +	byte			*out;
 +
 +	inOffset = 0;
 +	count = sfx->soundLength;
 +	state.index = 0;
 +	state.sample = samples[0];
 +
 +	chunk = NULL;
 +	while( count ) {
 +		n = count;
 +		if( n > SND_CHUNK_SIZE_BYTE*2 ) {
 +			n = SND_CHUNK_SIZE_BYTE*2;
 +		}
 +
 +		newchunk = SND_malloc();
 +		if (sfx->soundData == NULL) {
 +			sfx->soundData = newchunk;
 +		} else {
 +			chunk->next = newchunk;
 +		}
 +		chunk = newchunk;
 +
 +		// output the header
 +		chunk->adpcm.index  = state.index;
 +		chunk->adpcm.sample = state.sample;
 +
 +		out = (byte *)chunk->sndChunk;
 +
 +		// encode the samples
 +		S_AdpcmEncode( samples + inOffset, out, n, &state );
 +
 +		inOffset += n;
 +		count -= n;
 +	}
 +}
 diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c new file mode 100755 index 0000000..6ecff18 --- /dev/null +++ b/code/client/snd_dma.c @@ -0,0 +1,1636 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +/*****************************************************************************
 + * name:		snd_dma.c
 + *
 + * desc:		main control for any streaming sound output device
 + *
 + * $Archive: /MissionPack/code/client/snd_dma.c $
 + *
 + *****************************************************************************/
 +
 +#include "snd_local.h"
 +#include "client.h"
 +
 +void S_Play_f(void);
 +void S_SoundList_f(void);
 +void S_Music_f(void);
 +
 +void S_Update_();
 +void S_StopAllSounds(void);
 +void S_UpdateBackgroundTrack( void );
 +
 +static fileHandle_t s_backgroundFile;
 +static wavinfo_t	s_backgroundInfo;
 +//int			s_nextWavChunk;
 +static int			s_backgroundSamples;
 +static char		s_backgroundLoop[MAX_QPATH];
 +//static char		s_backgroundMusic[MAX_QPATH]; //TTimo: unused
 +
 +
 +// =======================================================================
 +// Internal sound data & structures
 +// =======================================================================
 +
 +// only begin attenuating sound volumes when outside the FULLVOLUME range
 +#define		SOUND_FULLVOLUME	80
 +
 +#define		SOUND_ATTENUATE		0.0008f
 +
 +channel_t   s_channels[MAX_CHANNELS];
 +channel_t   loop_channels[MAX_CHANNELS];
 +int			numLoopChannels;
 +
 +static int	s_soundStarted;
 +static		qboolean	s_soundMuted;
 +
 +dma_t		dma;
 +
 +static int			listener_number;
 +static vec3_t		listener_origin;
 +static vec3_t		listener_axis[3];
 +
 +int			s_soundtime;		// sample PAIRS
 +int   		s_paintedtime; 		// sample PAIRS
 +
 +// MAX_SFX may be larger than MAX_SOUNDS because
 +// of custom player sounds
 +#define		MAX_SFX			4096
 +sfx_t		s_knownSfx[MAX_SFX];
 +int			s_numSfx = 0;
 +
 +#define		LOOP_HASH		128
 +static	sfx_t		*sfxHash[LOOP_HASH];
 +
 +cvar_t		*s_volume;
 +cvar_t		*s_testsound;
 +cvar_t		*s_khz;
 +cvar_t		*s_show;
 +cvar_t		*s_mixahead;
 +cvar_t		*s_mixPreStep;
 +cvar_t		*s_musicVolume;
 +cvar_t		*s_separation;
 +cvar_t		*s_doppler;
 +
 +static loopSound_t		loopSounds[MAX_GENTITIES];
 +static	channel_t		*freelist = NULL;
 +
 +int						s_rawend;
 +portable_samplepair_t	s_rawsamples[MAX_RAW_SAMPLES];
 +
 +
 +// ====================================================================
 +// User-setable variables
 +// ====================================================================
 +
 +
 +void S_SoundInfo_f(void) {	
 +	Com_Printf("----- Sound Info -----\n" );
 +	if (!s_soundStarted) {
 +		Com_Printf ("sound system not started\n");
 +	} else {
 +		if ( s_soundMuted ) {
 +			Com_Printf ("sound system is muted\n");
 +		}
 +
 +		Com_Printf("%5d stereo\n", dma.channels - 1);
 +		Com_Printf("%5d samples\n", dma.samples);
 +		Com_Printf("%5d samplebits\n", dma.samplebits);
 +		Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
 +		Com_Printf("%5d speed\n", dma.speed);
 +		Com_Printf("0x%x dma buffer\n", dma.buffer);
 +		if ( s_backgroundFile ) {
 +			Com_Printf("Background file: %s\n", s_backgroundLoop );
 +		} else {
 +			Com_Printf("No background file.\n" );
 +		}
 +
 +	}
 +	Com_Printf("----------------------\n" );
 +}
 +
 +
 +
 +/*
 +================
 +S_Init
 +================
 +*/
 +void S_Init( void ) {
 +	cvar_t	*cv;
 +	qboolean	r;
 +
 +	Com_Printf("\n------- sound initialization -------\n");
 +
 +	s_volume = Cvar_Get ("s_volume", "0.8", CVAR_ARCHIVE);
 +	s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE);
 +	s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE);
 +	s_doppler = Cvar_Get ("s_doppler", "1", CVAR_ARCHIVE);
 +	s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
 +	s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
 +
 +	s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
 +	s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
 +	s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
 +
 +	cv = Cvar_Get ("s_initsound", "1", 0);
 +	if ( !cv->integer ) {
 +		Com_Printf ("not initializing.\n");
 +		Com_Printf("------------------------------------\n");
 +		return;
 +	}
 +
 +	Cmd_AddCommand("play", S_Play_f);
 +	Cmd_AddCommand("music", S_Music_f);
 +	Cmd_AddCommand("s_list", S_SoundList_f);
 +	Cmd_AddCommand("s_info", S_SoundInfo_f);
 +	Cmd_AddCommand("s_stop", S_StopAllSounds);
 +
 +	r = SNDDMA_Init();
 +	Com_Printf("------------------------------------\n");
 +
 +	if ( r ) {
 +		s_soundStarted = 1;
 +		s_soundMuted = 1;
 +//		s_numSfx = 0;
 +
 +		Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
 +
 +		s_soundtime = 0;
 +		s_paintedtime = 0;
 +
 +		S_StopAllSounds ();
 +
 +		S_SoundInfo_f();
 +	}
 +
 +}
 +
 +
 +void S_ChannelFree(channel_t *v) {
 +	v->thesfx = NULL;
 +	*(channel_t **)v = freelist;
 +	freelist = (channel_t*)v;
 +}
 +
 +channel_t*	S_ChannelMalloc() {
 +	channel_t *v;
 +	if (freelist == NULL) {
 +		return NULL;
 +	}
 +	v = freelist;
 +	freelist = *(channel_t **)freelist;
 +	v->allocTime = Com_Milliseconds();
 +	return v;
 +}
 +
 +void S_ChannelSetup() {
 +	channel_t *p, *q;
 +
 +	// clear all the sounds so they don't
 +	Com_Memset( s_channels, 0, sizeof( s_channels ) );
 +
 +	p = s_channels;;
 +	q = p + MAX_CHANNELS;
 +	while (--q > p) {
 +		*(channel_t **)q = q-1;
 +	}
 +	
 +	*(channel_t **)q = NULL;
 +	freelist = p + MAX_CHANNELS - 1;
 +	Com_DPrintf("Channel memory manager started\n");
 +}
 +
 +// =======================================================================
 +// Shutdown sound engine
 +// =======================================================================
 +
 +void S_Shutdown( void ) {
 +	if ( !s_soundStarted ) {
 +		return;
 +	}
 +
 +	SNDDMA_Shutdown();
 +
 +	s_soundStarted = 0;
 +
 +    Cmd_RemoveCommand("play");
 +	Cmd_RemoveCommand("music");
 +	Cmd_RemoveCommand("stopsound");
 +	Cmd_RemoveCommand("soundlist");
 +	Cmd_RemoveCommand("soundinfo");
 +}
 +
 +
 +// =======================================================================
 +// Load a sound
 +// =======================================================================
 +
 +/*
 +================
 +return a hash value for the sfx name
 +================
 +*/
 +static long S_HashSFXName(const char *name) {
 +	int		i;
 +	long	hash;
 +	char	letter;
 +
 +	hash = 0;
 +	i = 0;
 +	while (name[i] != '\0') {
 +		letter = tolower(name[i]);
 +		if (letter =='.') break;				// don't include extension
 +		if (letter =='\\') letter = '/';		// damn path names
 +		hash+=(long)(letter)*(i+119);
 +		i++;
 +	}
 +	hash &= (LOOP_HASH-1);
 +	return hash;
 +}
 +
 +/*
 +==================
 +S_FindName
 +
 +Will allocate a new sfx if it isn't found
 +==================
 +*/
 +static sfx_t *S_FindName( const char *name ) {
 +	int		i;
 +	int		hash;
 +
 +	sfx_t	*sfx;
 +
 +	if (!name) {
 +		Com_Error (ERR_FATAL, "S_FindName: NULL\n");
 +	}
 +	if (!name[0]) {
 +		Com_Error (ERR_FATAL, "S_FindName: empty name\n");
 +	}
 +
 +	if (strlen(name) >= MAX_QPATH) {
 +		Com_Error (ERR_FATAL, "Sound name too long: %s", name);
 +	}
 +
 +	hash = S_HashSFXName(name);
 +
 +	sfx = sfxHash[hash];
 +	// see if already loaded
 +	while (sfx) {
 +		if (!Q_stricmp(sfx->soundName, name) ) {
 +			return sfx;
 +		}
 +		sfx = sfx->next;
 +	}
 +
 +	// find a free sfx
 +	for (i=0 ; i < s_numSfx ; i++) {
 +		if (!s_knownSfx[i].soundName[0]) {
 +			break;
 +		}
 +	}
 +
 +	if (i == s_numSfx) {
 +		if (s_numSfx == MAX_SFX) {
 +			Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
 +		}
 +		s_numSfx++;
 +	}
 +	
 +	sfx = &s_knownSfx[i];
 +	Com_Memset (sfx, 0, sizeof(*sfx));
 +	strcpy (sfx->soundName, name);
 +
 +	sfx->next = sfxHash[hash];
 +	sfxHash[hash] = sfx;
 +
 +	return sfx;
 +}
 +
 +/*
 +=================
 +S_DefaultSound
 +=================
 +*/
 +void S_DefaultSound( sfx_t *sfx ) {
 +	
 +	int		i;
 +
 +	sfx->soundLength = 512;
 +	sfx->soundData = SND_malloc();
 +	sfx->soundData->next = NULL;
 +
 +
 +	for ( i = 0 ; i < sfx->soundLength ; i++ ) {
 +		sfx->soundData->sndChunk[i] = i;
 +	}
 +}
 +
 +/*
 +===================
 +S_DisableSounds
 +
 +Disables sounds until the next S_BeginRegistration.
 +This is called when the hunk is cleared and the sounds
 +are no longer valid.
 +===================
 +*/
 +void S_DisableSounds( void ) {
 +	S_StopAllSounds();
 +	s_soundMuted = qtrue;
 +}
 +
 +/*
 +=====================
 +S_BeginRegistration
 +
 +=====================
 +*/
 +void S_BeginRegistration( void ) {
 +	s_soundMuted = qfalse;		// we can play again
 +
 +	if (s_numSfx == 0) {
 +		SND_setup();
 +
 +		s_numSfx = 0;
 +		Com_Memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
 +		Com_Memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
 +
 +		S_RegisterSound("sound/feedback/hit.wav", qfalse);		// changed to a sound in baseq3
 +	}
 +}
 +
 +
 +/*
 +==================
 +S_RegisterSound
 +
 +Creates a default buzz sound if the file can't be loaded
 +==================
 +*/
 +sfxHandle_t	S_RegisterSound( const char *name, qboolean compressed ) {
 +	sfx_t	*sfx;
 +
 +	compressed = qfalse;
 +	if (!s_soundStarted) {
 +		return 0;
 +	}
 +
 +	if ( strlen( name ) >= MAX_QPATH ) {
 +		Com_Printf( "Sound name exceeds MAX_QPATH\n" );
 +		return 0;
 +	}
 +
 +	sfx = S_FindName( name );
 +	if ( sfx->soundData ) {
 +		if ( sfx->defaultSound ) {
 +			Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
 +			return 0;
 +		}
 +		return sfx - s_knownSfx;
 +	}
 +
 +	sfx->inMemory = qfalse;
 +	sfx->soundCompressed = compressed;
 +
 +  S_memoryLoad(sfx);
 +
 +	if ( sfx->defaultSound ) {
 +		Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->soundName );
 +		return 0;
 +	}
 +
 +	return sfx - s_knownSfx;
 +}
 +
 +void S_memoryLoad(sfx_t	*sfx) {
 +	// load the sound file
 +	if ( !S_LoadSound ( sfx ) ) {
 +//		Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->soundName );
 +		sfx->defaultSound = qtrue;
 +	}
 +	sfx->inMemory = qtrue;
 +}
 +
 +//=============================================================================
 +
 +/*
 +=================
 +S_SpatializeOrigin
 +
 +Used for spatializing s_channels
 +=================
 +*/
 +void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *right_vol)
 +{
 +    vec_t		dot;
 +    vec_t		dist;
 +    vec_t		lscale, rscale, scale;
 +    vec3_t		source_vec;
 +    vec3_t		vec;
 +
 +	const float dist_mult = SOUND_ATTENUATE;
 +	
 +	// calculate stereo seperation and distance attenuation
 +	VectorSubtract(origin, listener_origin, source_vec);
 +
 +	dist = VectorNormalize(source_vec);
 +	dist -= SOUND_FULLVOLUME;
 +	if (dist < 0)
 +		dist = 0;			// close enough to be at full volume
 +	dist *= dist_mult;		// different attenuation levels
 +	
 +	VectorRotate( source_vec, listener_axis, vec );
 +
 +	dot = -vec[1];
 +
 +	if (dma.channels == 1)
 +	{ // no attenuation = no spatialization
 +		rscale = 1.0;
 +		lscale = 1.0;
 +	}
 +	else
 +	{
 +		rscale = 0.5 * (1.0 + dot);
 +		lscale = 0.5 * (1.0 - dot);
 +		//rscale = s_separation->value + ( 1.0 - s_separation->value ) * dot;
 +		//lscale = s_separation->value - ( 1.0 - s_separation->value ) * dot;
 +		if ( rscale < 0 ) {
 +			rscale = 0;
 +		}
 +		if ( lscale < 0 ) {
 +			lscale = 0;
 +		}
 +	}
 +
 +	// add in distance effect
 +	scale = (1.0 - dist) * rscale;
 +	*right_vol = (master_vol * scale);
 +	if (*right_vol < 0)
 +		*right_vol = 0;
 +
 +	scale = (1.0 - dist) * lscale;
 +	*left_vol = (master_vol * scale);
 +	if (*left_vol < 0)
 +		*left_vol = 0;
 +}
 +
 +// =======================================================================
 +// Start a sound effect
 +// =======================================================================
 +
 +/*
 +====================
 +S_StartSound
 +
 +Validates the parms and ques the sound up
 +if pos is NULL, the sound will be dynamically sourced from the entity
 +Entchannel 0 will never override a playing sound
 +====================
 +*/
 +void S_StartSound(vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle ) {
 +	channel_t	*ch;
 +	sfx_t		*sfx;
 +  int i, oldest, chosen, time;
 +  int	inplay, allowed;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	if ( !origin && ( entityNum < 0 || entityNum > MAX_GENTITIES ) ) {
 +		Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum );
 +	}
 +
 +	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
 +		Com_Printf( S_COLOR_YELLOW, "S_StartSound: handle %i out of range\n", sfxHandle );
 +		return;
 +	}
 +
 +	sfx = &s_knownSfx[ sfxHandle ];
 +
 +	if (sfx->inMemory == qfalse) {
 +		S_memoryLoad(sfx);
 +	}
 +
 +	if ( s_show->integer == 1 ) {
 +		Com_Printf( "%i : %s\n", s_paintedtime, sfx->soundName );
 +	}
 +
 +	time = Com_Milliseconds();
 +
 +//	Com_Printf("playing %s\n", sfx->soundName);
 +	// pick a channel to play on
 +
 +	allowed = 4;
 +	if (entityNum == listener_number) {
 +		allowed = 8;
 +	}
 +
 +	ch = s_channels;
 +	inplay = 0;
 +	for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {		
 +		if (ch[i].entnum == entityNum && ch[i].thesfx == sfx) {
 +			if (time - ch[i].allocTime < 50) {
 +//				if (Cvar_VariableValue( "cg_showmiss" )) {
 +//					Com_Printf("double sound start\n");
 +//				}
 +				return;
 +			}
 +			inplay++;
 +		}
 +	}
 +
 +	if (inplay>allowed) {
 +		return;
 +	}
 +
 +	sfx->lastTimeUsed = time;
 +
 +	ch = S_ChannelMalloc();	// entityNum, entchannel);
 +	if (!ch) {
 +		ch = s_channels;
 +
 +		oldest = sfx->lastTimeUsed;
 +		chosen = -1;
 +		for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
 +			if (ch->entnum != listener_number && ch->entnum == entityNum && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
 +				oldest = ch->allocTime;
 +				chosen = i;
 +			}
 +		}
 +		if (chosen == -1) {
 +			ch = s_channels;
 +			for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
 +				if (ch->entnum != listener_number && ch->allocTime<oldest && ch->entchannel != CHAN_ANNOUNCER) {
 +					oldest = ch->allocTime;
 +					chosen = i;
 +				}
 +			}
 +			if (chosen == -1) {
 +				if (ch->entnum == listener_number) {
 +					for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
 +						if (ch->allocTime<oldest) {
 +							oldest = ch->allocTime;
 +							chosen = i;
 +						}
 +					}
 +				}
 +				if (chosen == -1) {
 +					Com_Printf("dropping sound\n");
 +					return;
 +				}
 +			}
 +		}
 +		ch = &s_channels[chosen];
 +		ch->allocTime = sfx->lastTimeUsed;
 +	}
 +
 +	if (origin) {
 +		VectorCopy (origin, ch->origin);
 +		ch->fixed_origin = qtrue;
 +	} else {
 +		ch->fixed_origin = qfalse;
 +	}
 +
 +	ch->master_vol = 127;
 +	ch->entnum = entityNum;
 +	ch->thesfx = sfx;
 +	ch->startSample = START_SAMPLE_IMMEDIATE;
 +	ch->entchannel = entchannel;
 +	ch->leftvol = ch->master_vol;		// these will get calced at next spatialize
 +	ch->rightvol = ch->master_vol;		// unless the game isn't running
 +	ch->doppler = qfalse;
 +}
 +
 +
 +/*
 +==================
 +S_StartLocalSound
 +==================
 +*/
 +void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
 +		Com_Printf( S_COLOR_YELLOW, "S_StartLocalSound: handle %i out of range\n", sfxHandle );
 +		return;
 +	}
 +
 +	S_StartSound (NULL, listener_number, channelNum, sfxHandle );
 +}
 +
 +
 +/*
 +==================
 +S_ClearSoundBuffer
 +
 +If we are about to perform file access, clear the buffer
 +so sound doesn't stutter.
 +==================
 +*/
 +void S_ClearSoundBuffer( void ) {
 +	int		clear;
 +		
 +	if (!s_soundStarted)
 +		return;
 +
 +	// stop looping sounds
 +	Com_Memset(loopSounds, 0, MAX_GENTITIES*sizeof(loopSound_t));
 +	Com_Memset(loop_channels, 0, MAX_CHANNELS*sizeof(channel_t));
 +	numLoopChannels = 0;
 +
 +	S_ChannelSetup();
 +
 +	s_rawend = 0;
 +
 +	if (dma.samplebits == 8)
 +		clear = 0x80;
 +	else
 +		clear = 0;
 +
 +	SNDDMA_BeginPainting ();
 +	if (dma.buffer)
 +    // TTimo: due to a particular bug workaround in linux sound code,
 +    //   have to optionally use a custom C implementation of Com_Memset
 +    //   not affecting win32, we have #define Snd_Memset Com_Memset
 +    // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
 +		Snd_Memset(dma.buffer, clear, dma.samples * dma.samplebits/8);
 +	SNDDMA_Submit ();
 +}
 +
 +/*
 +==================
 +S_StopAllSounds
 +==================
 +*/
 +void S_StopAllSounds(void) {
 +	if ( !s_soundStarted ) {
 +		return;
 +	}
 +
 +	// stop the background music
 +	S_StopBackgroundTrack();
 +
 +	S_ClearSoundBuffer ();
 +}
 +
 +/*
 +==============================================================
 +
 +continuous looping sounds are added each frame
 +
 +==============================================================
 +*/
 +
 +void S_StopLoopingSound(int entityNum) {
 +	loopSounds[entityNum].active = qfalse;
 +//	loopSounds[entityNum].sfx = 0;
 +	loopSounds[entityNum].kill = qfalse;
 +}
 +
 +/*
 +==================
 +S_ClearLoopingSounds
 +
 +==================
 +*/
 +void S_ClearLoopingSounds( qboolean killall ) {
 +	int i;
 +	for ( i = 0 ; i < MAX_GENTITIES ; i++) {
 +		if (killall || loopSounds[i].kill == qtrue || (loopSounds[i].sfx && loopSounds[i].sfx->soundLength == 0)) {
 +			loopSounds[i].kill = qfalse;
 +			S_StopLoopingSound(i);
 +		}
 +	}
 +	numLoopChannels = 0;
 +}
 +
 +/*
 +==================
 +S_AddLoopingSound
 +
 +Called during entity generation for a frame
 +Include velocity in case I get around to doing doppler...
 +==================
 +*/
 +void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
 +	sfx_t *sfx;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
 +		Com_Printf( S_COLOR_YELLOW, "S_AddLoopingSound: handle %i out of range\n", sfxHandle );
 +		return;
 +	}
 +
 +	sfx = &s_knownSfx[ sfxHandle ];
 +
 +	if (sfx->inMemory == qfalse) {
 +		S_memoryLoad(sfx);
 +	}
 +
 +	if ( !sfx->soundLength ) {
 +		Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
 +	}
 +
 +	VectorCopy( origin, loopSounds[entityNum].origin );
 +	VectorCopy( velocity, loopSounds[entityNum].velocity );
 +	loopSounds[entityNum].active = qtrue;
 +	loopSounds[entityNum].kill = qtrue;
 +	loopSounds[entityNum].doppler = qfalse;
 +	loopSounds[entityNum].oldDopplerScale = 1.0;
 +	loopSounds[entityNum].dopplerScale = 1.0;
 +	loopSounds[entityNum].sfx = sfx;
 +
 +	if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) {
 +		vec3_t	out;
 +		float	lena, lenb;
 +
 +		loopSounds[entityNum].doppler = qtrue;
 +		lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin);
 +		VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out);
 +		lenb = DistanceSquared(loopSounds[listener_number].origin, out);
 +		if ((loopSounds[entityNum].framenum+1) != cls.framecount) {
 +			loopSounds[entityNum].oldDopplerScale = 1.0;
 +		} else {
 +			loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale;
 +		}
 +		loopSounds[entityNum].dopplerScale = lenb/(lena*100);
 +		if (loopSounds[entityNum].dopplerScale<=1.0) {
 +			loopSounds[entityNum].doppler = qfalse;			// don't bother doing the math
 +		}
 +	}
 +
 +	loopSounds[entityNum].framenum = cls.framecount;
 +}
 +
 +/*
 +==================
 +S_AddLoopingSound
 +
 +Called during entity generation for a frame
 +Include velocity in case I get around to doing doppler...
 +==================
 +*/
 +void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
 +	sfx_t *sfx;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
 +		Com_Printf( S_COLOR_YELLOW, "S_AddRealLoopingSound: handle %i out of range\n", sfxHandle );
 +		return;
 +	}
 +
 +	sfx = &s_knownSfx[ sfxHandle ];
 +
 +	if (sfx->inMemory == qfalse) {
 +		S_memoryLoad(sfx);
 +	}
 +
 +	if ( !sfx->soundLength ) {
 +		Com_Error( ERR_DROP, "%s has length 0", sfx->soundName );
 +	}
 +	VectorCopy( origin, loopSounds[entityNum].origin );
 +	VectorCopy( velocity, loopSounds[entityNum].velocity );
 +	loopSounds[entityNum].sfx = sfx;
 +	loopSounds[entityNum].active = qtrue;
 +	loopSounds[entityNum].kill = qfalse;
 +	loopSounds[entityNum].doppler = qfalse;
 +}
 +
 +
 +
 +/*
 +==================
 +S_AddLoopSounds
 +
 +Spatialize all of the looping sounds.
 +All sounds are on the same cycle, so any duplicates can just
 +sum up the channel multipliers.
 +==================
 +*/
 +void S_AddLoopSounds (void) {
 +	int			i, j, time;
 +	int			left_total, right_total, left, right;
 +	channel_t	*ch;
 +	loopSound_t	*loop, *loop2;
 +	static int	loopFrame;
 +
 +
 +	numLoopChannels = 0;
 +
 +	time = Com_Milliseconds();
 +
 +	loopFrame++;
 +	for ( i = 0 ; i < MAX_GENTITIES ; i++) {
 +		loop = &loopSounds[i];
 +		if ( !loop->active || loop->mergeFrame == loopFrame ) {
 +			continue;	// already merged into an earlier sound
 +		}
 +
 +		if (loop->kill) {
 +			S_SpatializeOrigin( loop->origin, 127, &left_total, &right_total);			// 3d
 +		} else {
 +			S_SpatializeOrigin( loop->origin, 90,  &left_total, &right_total);			// sphere
 +		}
 +
 +		loop->sfx->lastTimeUsed = time;
 +
 +		for (j=(i+1); j< MAX_GENTITIES ; j++) {
 +			loop2 = &loopSounds[j];
 +			if ( !loop2->active || loop2->doppler || loop2->sfx != loop->sfx) {
 +				continue;
 +			}
 +			loop2->mergeFrame = loopFrame;
 +
 +			if (loop2->kill) {
 +				S_SpatializeOrigin( loop2->origin, 127, &left, &right);				// 3d
 +			} else {
 +				S_SpatializeOrigin( loop2->origin, 90,  &left, &right);				// sphere
 +			}
 +
 +			loop2->sfx->lastTimeUsed = time;
 +			left_total += left;
 +			right_total += right;
 +		}
 +		if (left_total == 0 && right_total == 0) {
 +			continue;		// not audible
 +		}
 +
 +		// allocate a channel
 +		ch = &loop_channels[numLoopChannels];
 +		
 +		if (left_total > 255) {
 +			left_total = 255;
 +		}
 +		if (right_total > 255) {
 +			right_total = 255;
 +		}
 +		
 +		ch->master_vol = 127;
 +		ch->leftvol = left_total;
 +		ch->rightvol = right_total;
 +		ch->thesfx = loop->sfx;
 +		ch->doppler = loop->doppler;
 +		ch->dopplerScale = loop->dopplerScale;
 +		ch->oldDopplerScale = loop->oldDopplerScale;
 +		numLoopChannels++;
 +		if (numLoopChannels == MAX_CHANNELS) {
 +			return;
 +		}
 +	}
 +}
 +
 +//=============================================================================
 +
 +/*
 +=================
 +S_ByteSwapRawSamples
 +
 +If raw data has been loaded in little endien binary form, this must be done.
 +If raw data was calculated, as with ADPCM, this should not be called.
 +=================
 +*/
 +void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
 +	int		i;
 +
 +	if ( width != 2 ) {
 +		return;
 +	}
 +	if ( LittleShort( 256 ) == 256 ) {
 +		return;
 +	}
 +
 +	if ( s_channels == 2 ) {
 +		samples <<= 1;
 +	}
 +	for ( i = 0 ; i < samples ; i++ ) {
 +		((short *)data)[i] = LittleShort( ((short *)data)[i] );
 +	}
 +}
 +
 +portable_samplepair_t *S_GetRawSamplePointer() {
 +	return s_rawsamples;
 +}
 +
 +/*
 +============
 +S_RawSamples
 +
 +Music streaming
 +============
 +*/
 +void S_RawSamples( int samples, int rate, int width, int s_channels, const byte *data, float volume ) {
 +	int		i;
 +	int		src, dst;
 +	float	scale;
 +	int		intVolume;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	intVolume = 256 * volume;
 +
 +	if ( s_rawend < s_soundtime ) {
 +		Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime );
 +		s_rawend = s_soundtime;
 +	}
 +
 +	scale = (float)rate / dma.speed;
 +
 +//Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend);
 +	if (s_channels == 2 && width == 2)
 +	{
 +		if (scale == 1.0)
 +		{	// optimized case
 +			for (i=0 ; i<samples ; i++)
 +			{
 +				dst = s_rawend&(MAX_RAW_SAMPLES-1);
 +				s_rawend++;
 +				s_rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
 +				s_rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
 +			}
 +		}
 +		else
 +		{
 +			for (i=0 ; ; i++)
 +			{
 +				src = i*scale;
 +				if (src >= samples)
 +					break;
 +				dst = s_rawend&(MAX_RAW_SAMPLES-1);
 +				s_rawend++;
 +				s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
 +				s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
 +			}
 +		}
 +	}
 +	else if (s_channels == 1 && width == 2)
 +	{
 +		for (i=0 ; ; i++)
 +		{
 +			src = i*scale;
 +			if (src >= samples)
 +				break;
 +			dst = s_rawend&(MAX_RAW_SAMPLES-1);
 +			s_rawend++;
 +			s_rawsamples[dst].left = ((short *)data)[src] * intVolume;
 +			s_rawsamples[dst].right = ((short *)data)[src] * intVolume;
 +		}
 +	}
 +	else if (s_channels == 2 && width == 1)
 +	{
 +		intVolume *= 256;
 +
 +		for (i=0 ; ; i++)
 +		{
 +			src = i*scale;
 +			if (src >= samples)
 +				break;
 +			dst = s_rawend&(MAX_RAW_SAMPLES-1);
 +			s_rawend++;
 +			s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
 +			s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
 +		}
 +	}
 +	else if (s_channels == 1 && width == 1)
 +	{
 +		intVolume *= 256;
 +
 +		for (i=0 ; ; i++)
 +		{
 +			src = i*scale;
 +			if (src >= samples)
 +				break;
 +			dst = s_rawend&(MAX_RAW_SAMPLES-1);
 +			s_rawend++;
 +			s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
 +			s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
 +		}
 +	}
 +
 +	if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) {
 +		Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime );
 +	}
 +}
 +
 +//=============================================================================
 +
 +/*
 +=====================
 +S_UpdateEntityPosition
 +
 +let the sound system know where an entity currently is
 +======================
 +*/
 +void S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
 +	if ( entityNum < 0 || entityNum > MAX_GENTITIES ) {
 +		Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
 +	}
 +	VectorCopy( origin, loopSounds[entityNum].origin );
 +}
 +
 +
 +/*
 +============
 +S_Respatialize
 +
 +Change the volumes of all the playing sounds for changes in their positions
 +============
 +*/
 +void S_Respatialize( int entityNum, const vec3_t head, vec3_t axis[3], int inwater ) {
 +	int			i;
 +	channel_t	*ch;
 +	vec3_t		origin;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	listener_number = entityNum;
 +	VectorCopy(head, listener_origin);
 +	VectorCopy(axis[0], listener_axis[0]);
 +	VectorCopy(axis[1], listener_axis[1]);
 +	VectorCopy(axis[2], listener_axis[2]);
 +
 +	// update spatialization for dynamic sounds	
 +	ch = s_channels;
 +	for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
 +		if ( !ch->thesfx ) {
 +			continue;
 +		}
 +		// anything coming from the view entity will always be full volume
 +		if (ch->entnum == listener_number) {
 +			ch->leftvol = ch->master_vol;
 +			ch->rightvol = ch->master_vol;
 +		} else {
 +			if (ch->fixed_origin) {
 +				VectorCopy( ch->origin, origin );
 +			} else {
 +				VectorCopy( loopSounds[ ch->entnum ].origin, origin );
 +			}
 +
 +			S_SpatializeOrigin (origin, ch->master_vol, &ch->leftvol, &ch->rightvol);
 +		}
 +	}
 +
 +	// add loopsounds
 +	S_AddLoopSounds ();
 +}
 +
 +
 +/*
 +========================
 +S_ScanChannelStarts
 +
 +Returns qtrue if any new sounds were started since the last mix
 +========================
 +*/
 +qboolean S_ScanChannelStarts( void ) {
 +	channel_t		*ch;
 +	int				i;
 +	qboolean		newSamples;
 +
 +	newSamples = qfalse;
 +	ch = s_channels;
 +
 +	for (i=0; i<MAX_CHANNELS ; i++, ch++) {
 +		if ( !ch->thesfx ) {
 +			continue;
 +		}
 +		// if this channel was just started this frame,
 +		// set the sample count to it begins mixing
 +		// into the very first sample
 +		if ( ch->startSample == START_SAMPLE_IMMEDIATE ) {
 +			ch->startSample = s_paintedtime;
 +			newSamples = qtrue;
 +			continue;
 +		}
 +
 +		// if it is completely finished by now, clear it
 +		if ( ch->startSample + (ch->thesfx->soundLength) <= s_paintedtime ) {
 +			S_ChannelFree(ch);
 +		}
 +	}
 +
 +	return newSamples;
 +}
 +
 +/*
 +============
 +S_Update
 +
 +Called once each time through the main loop
 +============
 +*/
 +void S_Update( void ) {
 +	int			i;
 +	int			total;
 +	channel_t	*ch;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		Com_DPrintf ("not started or muted\n");
 +		return;
 +	}
 +
 +	//
 +	// debugging output
 +	//
 +	if ( s_show->integer == 2 ) {
 +		total = 0;
 +		ch = s_channels;
 +		for (i=0 ; i<MAX_CHANNELS; i++, ch++) {
 +			if (ch->thesfx && (ch->leftvol || ch->rightvol) ) {
 +				Com_Printf ("%f %f %s\n", ch->leftvol, ch->rightvol, ch->thesfx->soundName);
 +				total++;
 +			}
 +		}
 +		
 +		Com_Printf ("----(%i)---- painted: %i\n", total, s_paintedtime);
 +	}
 +
 +	// add raw data from streamed samples
 +	S_UpdateBackgroundTrack();
 +
 +	// mix some sound
 +	S_Update_();
 +}
 +
 +void S_GetSoundtime(void)
 +{
 +	int		samplepos;
 +	static	int		buffers;
 +	static	int		oldsamplepos;
 +	int		fullsamples;
 +	
 +	fullsamples = dma.samples / dma.channels;
 +
 +	// it is possible to miscount buffers if it has wrapped twice between
 +	// calls to S_Update.  Oh well.
 +	samplepos = SNDDMA_GetDMAPos();
 +	if (samplepos < oldsamplepos)
 +	{
 +		buffers++;					// buffer wrapped
 +		
 +		if (s_paintedtime > 0x40000000)
 +		{	// time to chop things off to avoid 32 bit limits
 +			buffers = 0;
 +			s_paintedtime = fullsamples;
 +			S_StopAllSounds ();
 +		}
 +	}
 +	oldsamplepos = samplepos;
 +
 +	s_soundtime = buffers*fullsamples + samplepos/dma.channels;
 +
 +#if 0
 +// check to make sure that we haven't overshot
 +	if (s_paintedtime < s_soundtime)
 +	{
 +		Com_DPrintf ("S_Update_ : overflow\n");
 +		s_paintedtime = s_soundtime;
 +	}
 +#endif
 +
 +	if ( dma.submission_chunk < 256 ) {
 +		s_paintedtime = s_soundtime + s_mixPreStep->value * dma.speed;
 +	} else {
 +		s_paintedtime = s_soundtime + dma.submission_chunk;
 +	}
 +}
 +
 +
 +void S_Update_(void) {
 +	unsigned        endtime;
 +	int				samps;
 +	static			float	lastTime = 0.0f;
 +	float			ma, op;
 +	float			thisTime, sane;
 +	static			int ot = -1;
 +
 +	if ( !s_soundStarted || s_soundMuted ) {
 +		return;
 +	}
 +
 +	thisTime = Com_Milliseconds();
 +
 +	// Updates s_soundtime
 +	S_GetSoundtime();
 +
 +	if (s_soundtime == ot) {
 +		return;
 +	}
 +	ot = s_soundtime;
 +
 +	// clear any sound effects that end before the current time,
 +	// and start any new sounds
 +	S_ScanChannelStarts();
 +
 +	sane = thisTime - lastTime;
 +	if (sane<11) {
 +		sane = 11;			// 85hz
 +	}
 +
 +	ma = s_mixahead->value * dma.speed;
 +	op = s_mixPreStep->value + sane*dma.speed*0.01;
 +
 +	if (op < ma) {
 +		ma = op;
 +	}
 +
 +	// mix ahead of current position
 +	endtime = s_soundtime + ma;
 +
 +	// mix to an even submission block size
 +	endtime = (endtime + dma.submission_chunk-1)
 +		& ~(dma.submission_chunk-1);
 +
 +	// never mix more than the complete buffer
 +	samps = dma.samples >> (dma.channels-1);
 +	if (endtime - s_soundtime > samps)
 +		endtime = s_soundtime + samps;
 +
 +
 +
 +	SNDDMA_BeginPainting ();
 +
 +	S_PaintChannels (endtime);
 +
 +	SNDDMA_Submit ();
 +
 +	lastTime = thisTime;
 +}
 +
 +/*
 +===============================================================================
 +
 +console functions
 +
 +===============================================================================
 +*/
 +
 +void S_Play_f( void ) {
 +	int 		i;
 +	sfxHandle_t	h;
 +	char		name[256];
 +	
 +	i = 1;
 +	while ( i<Cmd_Argc() ) {
 +		if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
 +			Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
 +		} else {
 +			Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
 +		}
 +		h = S_RegisterSound( name, qfalse );
 +		if( h ) {
 +			S_StartLocalSound( h, CHAN_LOCAL_SOUND );
 +		}
 +		i++;
 +	}
 +}
 +
 +void S_Music_f( void ) {
 +	int		c;
 +
 +	c = Cmd_Argc();
 +
 +	if ( c == 2 ) {
 +		S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1) );
 +		s_backgroundLoop[0] = 0;
 +	} else if ( c == 3 ) {
 +		S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
 +	} else {
 +		Com_Printf ("music <musicfile> [loopfile]\n");
 +		return;
 +	}
 +
 +}
 +
 +void S_SoundList_f( void ) {
 +	int		i;
 +	sfx_t	*sfx;
 +	int		size, total;
 +	char	type[4][16];
 +	char	mem[2][16];
 +
 +	strcpy(type[0], "16bit");
 +	strcpy(type[1], "adpcm");
 +	strcpy(type[2], "daub4");
 +	strcpy(type[3], "mulaw");
 +	strcpy(mem[0], "paged out");
 +	strcpy(mem[1], "resident ");
 +	total = 0;
 +	for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++) {
 +		size = sfx->soundLength;
 +		total += size;
 +		Com_Printf("%6i[%s] : %s[%s]\n", size, type[sfx->soundCompressionMethod], sfx->soundName, mem[sfx->inMemory] );
 +	}
 +	Com_Printf ("Total resident: %i\n", total);
 +	S_DisplayFreeMemory();
 +}
 +
 +
 +/*
 +===============================================================================
 +
 +background music functions
 +
 +===============================================================================
 +*/
 +
 +int	FGetLittleLong( fileHandle_t f ) {
 +	int		v;
 +
 +	FS_Read( &v, sizeof(v), f );
 +
 +	return LittleLong( v);
 +}
 +
 +int	FGetLittleShort( fileHandle_t f ) {
 +	short	v;
 +
 +	FS_Read( &v, sizeof(v), f );
 +
 +	return LittleShort( v);
 +}
 +
 +// returns the length of the data in the chunk, or 0 if not found
 +int S_FindWavChunk( fileHandle_t f, char *chunk ) {
 +	char	name[5];
 +	int		len;
 +	int		r;
 +
 +	name[4] = 0;
 +	len = 0;
 +	r = FS_Read( name, 4, f );
 +	if ( r != 4 ) {
 +		return 0;
 +	}
 +	len = FGetLittleLong( f );
 +	if ( len < 0 || len > 0xfffffff ) {
 +		len = 0;
 +		return 0;
 +	}
 +	len = (len + 1 ) & ~1;		// pad to word boundary
 +//	s_nextWavChunk += len + 8;
 +
 +	if ( strcmp( name, chunk ) ) {
 +		return 0;
 +	}
 +
 +	return len;
 +}
 +
 +/*
 +======================
 +S_StopBackgroundTrack
 +======================
 +*/
 +void S_StopBackgroundTrack( void ) {
 +	if ( !s_backgroundFile ) {
 +		return;
 +	}
 +	Sys_EndStreamedFile( s_backgroundFile );
 +	FS_FCloseFile( s_backgroundFile );
 +	s_backgroundFile = 0;
 +	s_rawend = 0;
 +}
 +
 +/*
 +======================
 +S_StartBackgroundTrack
 +======================
 +*/
 +void S_StartBackgroundTrack( const char *intro, const char *loop ){
 +	int		len;
 +	char	dump[16];
 +	char	name[MAX_QPATH];
 +
 +	if ( !intro ) {
 +		intro = "";
 +	}
 +	if ( !loop || !loop[0] ) {
 +		loop = intro;
 +	}
 +	Com_DPrintf( "S_StartBackgroundTrack( %s, %s )\n", intro, loop );
 +
 +	Q_strncpyz( name, intro, sizeof( name ) - 4 );
 +	COM_DefaultExtension( name, sizeof( name ), ".wav" );
 +
 +	if ( !intro[0] ) {
 +		return;
 +	}
 +
 +	Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
 +
 +	// close the background track, but DON'T reset s_rawend
 +	// if restarting the same back ground track
 +	if ( s_backgroundFile ) {
 +		Sys_EndStreamedFile( s_backgroundFile );
 +		FS_FCloseFile( s_backgroundFile );
 +		s_backgroundFile = 0;
 +	}
 +
 +	//
 +	// open up a wav file and get all the info
 +	//
 +	FS_FOpenFileRead( name, &s_backgroundFile, qtrue );
 +	if ( !s_backgroundFile ) {
 +		Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name );
 +		return;
 +	}
 +
 +	// skip the riff wav header
 +
 +	FS_Read(dump, 12, s_backgroundFile);
 +
 +	if ( !S_FindWavChunk( s_backgroundFile, "fmt " ) ) {
 +		Com_Printf( "No fmt chunk in %s\n", name );
 +		FS_FCloseFile( s_backgroundFile );
 +		s_backgroundFile = 0;
 +		return;
 +	}
 +
 +	// save name for soundinfo
 +	s_backgroundInfo.format = FGetLittleShort( s_backgroundFile );
 +	s_backgroundInfo.channels = FGetLittleShort( s_backgroundFile );
 +	s_backgroundInfo.rate = FGetLittleLong( s_backgroundFile );
 +	FGetLittleLong(  s_backgroundFile );
 +	FGetLittleShort(  s_backgroundFile );
 +	s_backgroundInfo.width = FGetLittleShort( s_backgroundFile ) / 8;
 +
 +	if ( s_backgroundInfo.format != WAV_FORMAT_PCM ) {
 +		FS_FCloseFile( s_backgroundFile );
 +		s_backgroundFile = 0;
 +		Com_Printf("Not a microsoft PCM format wav: %s\n", name);
 +		return;
 +	}
 +
 +	if ( s_backgroundInfo.channels != 2 || s_backgroundInfo.rate != 22050 ) {
 +		Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name );
 +	}
 +
 +	if ( ( len = S_FindWavChunk( s_backgroundFile, "data" ) ) == 0 ) {
 +		FS_FCloseFile( s_backgroundFile );
 +		s_backgroundFile = 0;
 +		Com_Printf("No data chunk in %s\n", name);
 +		return;
 +	}
 +
 +	s_backgroundInfo.samples = len / (s_backgroundInfo.width * s_backgroundInfo.channels);
 +
 +	s_backgroundSamples = s_backgroundInfo.samples;
 +
 +	//
 +	// start the background streaming
 +	//
 +	Sys_BeginStreamedFile( s_backgroundFile, 0x10000 );
 +}
 +
 +/*
 +======================
 +S_UpdateBackgroundTrack
 +======================
 +*/
 +void S_UpdateBackgroundTrack( void ) {
 +	int		bufferSamples;
 +	int		fileSamples;
 +	byte	raw[30000];		// just enough to fit in a mac stack frame
 +	int		fileBytes;
 +	int		r;
 +	static	float	musicVolume = 0.5f;
 +
 +	if ( !s_backgroundFile ) {
 +		return;
 +	}
 +
 +	// graeme see if this is OK
 +	musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f;
 +
 +	// don't bother playing anything if musicvolume is 0
 +	if ( musicVolume <= 0 ) {
 +		return;
 +	}
 +
 +	// see how many samples should be copied into the raw buffer
 +	if ( s_rawend < s_soundtime ) {
 +		s_rawend = s_soundtime;
 +	}
 +
 +	while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) {
 +		bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime);
 +
 +		// decide how much data needs to be read from the file
 +		fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed;
 +
 +		// don't try and read past the end of the file
 +		if ( fileSamples > s_backgroundSamples ) {
 +			fileSamples = s_backgroundSamples;
 +		}
 +
 +		// our max buffer size
 +		fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels);
 +		if ( fileBytes > sizeof(raw) ) {
 +			fileBytes = sizeof(raw);
 +			fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels);
 +		}
 +
 +		r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile );
 +		if ( r != fileBytes ) {
 +			Com_Printf("StreamedRead failure on music track\n");
 +			S_StopBackgroundTrack();
 +			return;
 +		}
 +
 +		// byte swap if needed
 +		S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw );
 +
 +		// add to raw buffer
 +		S_RawSamples( fileSamples, s_backgroundInfo.rate, 
 +			s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume );
 +
 +		s_backgroundSamples -= fileSamples;
 +		if ( !s_backgroundSamples ) {
 +			// loop
 +			if (s_backgroundLoop[0]) {
 +				Sys_EndStreamedFile( s_backgroundFile );
 +				FS_FCloseFile( s_backgroundFile );
 +				s_backgroundFile = 0;
 +				S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop );
 +				if ( !s_backgroundFile ) {
 +					return;		// loop failed to restart
 +				}
 +			} else {
 +				s_backgroundFile = 0;
 +				return;
 +			}
 +		}
 +	}
 +}
 +
 +
 +/*
 +======================
 +S_FreeOldestSound
 +======================
 +*/
 +
 +void S_FreeOldestSound() {
 +	int	i, oldest, used;
 +	sfx_t	*sfx;
 +	sndBuffer	*buffer, *nbuffer;
 +
 +	oldest = Com_Milliseconds();
 +	used = 0;
 +
 +	for (i=1 ; i < s_numSfx ; i++) {
 +		sfx = &s_knownSfx[i];
 +		if (sfx->inMemory && sfx->lastTimeUsed<oldest) {
 +			used = i;
 +			oldest = sfx->lastTimeUsed;
 +		}
 +	}
 +
 +	sfx = &s_knownSfx[used];
 +
 +	Com_DPrintf("S_FreeOldestSound: freeing sound %s\n", sfx->soundName);
 +
 +	buffer = sfx->soundData;
 +	while(buffer != NULL) {
 +		nbuffer = buffer->next;
 +		SND_free(buffer);
 +		buffer = nbuffer;
 +	}
 +	sfx->inMemory = qfalse;
 +	sfx->soundData = NULL;
 +}
 diff --git a/code/client/snd_local.h b/code/client/snd_local.h new file mode 100755 index 0000000..5b0c97a --- /dev/null +++ b/code/client/snd_local.h @@ -0,0 +1,204 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// snd_local.h -- private sound definations
 +
 +
 +#include "../game/q_shared.h"
 +#include "../qcommon/qcommon.h"
 +#include "snd_public.h"
 +
 +#define	PAINTBUFFER_SIZE		4096					// this is in samples
 +
 +#define SND_CHUNK_SIZE			1024					// samples
 +#define SND_CHUNK_SIZE_FLOAT	(SND_CHUNK_SIZE/2)		// floats
 +#define SND_CHUNK_SIZE_BYTE		(SND_CHUNK_SIZE*2)		// floats
 +
 +typedef struct {
 +	int			left;	// the final values will be clamped to +/- 0x00ffff00 and shifted down
 +	int			right;
 +} portable_samplepair_t;
 +
 +typedef struct adpcm_state {
 +    short	sample;		/* Previous output value */
 +    char	index;		/* Index into stepsize table */
 +} adpcm_state_t;
 +
 +typedef	struct sndBuffer_s {
 +	short					sndChunk[SND_CHUNK_SIZE];
 +	struct sndBuffer_s		*next;
 +    int						size;
 +	adpcm_state_t			adpcm;
 +} sndBuffer;
 +
 +typedef struct sfx_s {
 +	sndBuffer		*soundData;
 +	qboolean		defaultSound;			// couldn't be loaded, so use buzz
 +	qboolean		inMemory;				// not in Memory
 +	qboolean		soundCompressed;		// not in Memory
 +	int				soundCompressionMethod;	
 +	int 			soundLength;
 +	char 			soundName[MAX_QPATH];
 +	int				lastTimeUsed;
 +	struct sfx_s	*next;
 +} sfx_t;
 +
 +typedef struct {
 +	int			channels;
 +	int			samples;				// mono samples in buffer
 +	int			submission_chunk;		// don't mix less than this #
 +	int			samplebits;
 +	int			speed;
 +	byte		*buffer;
 +} dma_t;
 +
 +#define START_SAMPLE_IMMEDIATE	0x7fffffff
 +
 +typedef struct loopSound_s {
 +	vec3_t		origin;
 +	vec3_t		velocity;
 +	sfx_t		*sfx;
 +	int			mergeFrame;
 +	qboolean	active;
 +	qboolean	kill;
 +	qboolean	doppler;
 +	float		dopplerScale;
 +	float		oldDopplerScale;
 +	int			framenum;
 +} loopSound_t;
 +
 +typedef struct
 +{
 +	int			allocTime;
 +	int			startSample;	// START_SAMPLE_IMMEDIATE = set immediately on next mix
 +	int			entnum;			// to allow overriding a specific sound
 +	int			entchannel;		// to allow overriding a specific sound
 +	int			leftvol;		// 0-255 volume after spatialization
 +	int			rightvol;		// 0-255 volume after spatialization
 +	int			master_vol;		// 0-255 volume before spatialization
 +	float		dopplerScale;
 +	float		oldDopplerScale;
 +	vec3_t		origin;			// only use if fixed_origin is set
 +	qboolean	fixed_origin;	// use origin instead of fetching entnum's origin
 +	sfx_t		*thesfx;		// sfx structure
 +	qboolean	doppler;
 +} channel_t;
 +
 +
 +#define	WAV_FORMAT_PCM		1
 +
 +
 +typedef struct {
 +	int			format;
 +	int			rate;
 +	int			width;
 +	int			channels;
 +	int			samples;
 +	int			dataofs;		// chunk starts this many bytes from file start
 +} wavinfo_t;
 +
 +
 +/*
 +====================================================================
 +
 +  SYSTEM SPECIFIC FUNCTIONS
 +
 +====================================================================
 +*/
 +
 +// initializes cycling through a DMA buffer and returns information on it
 +qboolean SNDDMA_Init(void);
 +
 +// gets the current DMA position
 +int		SNDDMA_GetDMAPos(void);
 +
 +// shutdown the DMA xfer.
 +void	SNDDMA_Shutdown(void);
 +
 +void	SNDDMA_BeginPainting (void);
 +
 +void	SNDDMA_Submit(void);
 +
 +//====================================================================
 +
 +#define	MAX_CHANNELS			96
 +
 +extern	channel_t   s_channels[MAX_CHANNELS];
 +extern	channel_t   loop_channels[MAX_CHANNELS];
 +extern	int		numLoopChannels;
 +
 +extern	int		s_paintedtime;
 +extern	int		s_rawend;
 +extern	vec3_t	listener_forward;
 +extern	vec3_t	listener_right;
 +extern	vec3_t	listener_up;
 +extern	dma_t	dma;
 +
 +#define	MAX_RAW_SAMPLES	16384
 +extern	portable_samplepair_t	s_rawsamples[MAX_RAW_SAMPLES];
 +
 +extern cvar_t	*s_volume;
 +extern cvar_t	*s_nosound;
 +extern cvar_t	*s_khz;
 +extern cvar_t	*s_show;
 +extern cvar_t	*s_mixahead;
 +
 +extern cvar_t	*s_testsound;
 +extern cvar_t	*s_separation;
 +
 +qboolean S_LoadSound( sfx_t *sfx );
 +
 +void		SND_free(sndBuffer *v);
 +sndBuffer*	SND_malloc();
 +void		SND_setup();
 +
 +void S_PaintChannels(int endtime);
 +
 +void S_memoryLoad(sfx_t *sfx);
 +portable_samplepair_t *S_GetRawSamplePointer();
 +
 +// spatializes a channel
 +void S_Spatialize(channel_t *ch);
 +
 +// adpcm functions
 +int  S_AdpcmMemoryNeeded( const wavinfo_t *info );
 +void S_AdpcmEncodeSound( sfx_t *sfx, short *samples );
 +void S_AdpcmGetSamples(sndBuffer *chunk, short *to);
 +
 +// wavelet function
 +
 +#define SENTINEL_MULAW_ZERO_RUN 127
 +#define SENTINEL_MULAW_FOUR_BIT_RUN 126
 +
 +void S_FreeOldestSound();
 +
 +#define	NXStream byte
 +
 +void encodeWavelet(sfx_t *sfx, short *packets);
 +void decodeWavelet( sndBuffer *stream, short *packets);
 +
 +void encodeMuLaw( sfx_t *sfx, short *packets);
 +extern short mulawToShort[256];
 +
 +extern short *sfxScratchBuffer;
 +extern sfx_t *sfxScratchPointer;
 +extern int	   sfxScratchIndex;
 +
 diff --git a/code/client/snd_mem.c b/code/client/snd_mem.c new file mode 100755 index 0000000..0e400d5 --- /dev/null +++ b/code/client/snd_mem.c @@ -0,0 +1,404 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +/*****************************************************************************
 + * name:		snd_mem.c
 + *
 + * desc:		sound caching
 + *
 + * $Archive: /MissionPack/code/client/snd_mem.c $
 + *
 + *****************************************************************************/
 +
 +#include "snd_local.h"
 +
 +#define DEF_COMSOUNDMEGS "8"
 +
 +/*
 +===============================================================================
 +
 +memory management
 +
 +===============================================================================
 +*/
 +
 +static	sndBuffer	*buffer = NULL;
 +static	sndBuffer	*freelist = NULL;
 +static	int inUse = 0;
 +static	int totalInUse = 0;
 +
 +short *sfxScratchBuffer = NULL;
 +sfx_t *sfxScratchPointer = NULL;
 +int	   sfxScratchIndex = 0;
 +
 +void	SND_free(sndBuffer *v) {
 +	*(sndBuffer **)v = freelist;
 +	freelist = (sndBuffer*)v;
 +	inUse += sizeof(sndBuffer);
 +}
 +
 +sndBuffer*	SND_malloc() {
 +	sndBuffer *v;
 +redo:
 +	if (freelist == NULL) {
 +		S_FreeOldestSound();
 +		goto redo;
 +	}
 +
 +	inUse -= sizeof(sndBuffer);
 +	totalInUse += sizeof(sndBuffer);
 +
 +	v = freelist;
 +	freelist = *(sndBuffer **)freelist;
 +	v->next = NULL;
 +	return v;
 +}
 +
 +void SND_setup() {
 +	sndBuffer *p, *q;
 +	cvar_t	*cv;
 +	int scs;
 +
 +	cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
 +
 +	scs = (cv->integer*1536);
 +
 +	buffer = malloc(scs*sizeof(sndBuffer) );
 +	// allocate the stack based hunk allocator
 +	sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4);	//Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
 +	sfxScratchPointer = NULL;
 +
 +	inUse = scs*sizeof(sndBuffer);
 +	p = buffer;;
 +	q = p + scs;
 +	while (--q > p)
 +		*(sndBuffer **)q = q-1;
 +	
 +	*(sndBuffer **)q = NULL;
 +	freelist = p + scs - 1;
 +
 +	Com_Printf("Sound memory manager started\n");
 +}
 +
 +/*
 +===============================================================================
 +
 +WAV loading
 +
 +===============================================================================
 +*/
 +
 +static	byte	*data_p;
 +static	byte 	*iff_end;
 +static	byte 	*last_chunk;
 +static	byte 	*iff_data;
 +static	int 	iff_chunk_len;
 +
 +static short GetLittleShort(void)
 +{
 +	short val = 0;
 +	val = *data_p;
 +	val = val + (*(data_p+1)<<8);
 +	data_p += 2;
 +	return val;
 +}
 +
 +static int GetLittleLong(void)
 +{
 +	int val = 0;
 +	val = *data_p;
 +	val = val + (*(data_p+1)<<8);
 +	val = val + (*(data_p+2)<<16);
 +	val = val + (*(data_p+3)<<24);
 +	data_p += 4;
 +	return val;
 +}
 +
 +static void FindNextChunk(char *name)
 +{
 +	while (1)
 +	{
 +		data_p=last_chunk;
 +
 +		if (data_p >= iff_end)
 +		{	// didn't find the chunk
 +			data_p = NULL;
 +			return;
 +		}
 +		
 +		data_p += 4;
 +		iff_chunk_len = GetLittleLong();
 +		if (iff_chunk_len < 0)
 +		{
 +			data_p = NULL;
 +			return;
 +		}
 +		data_p -= 8;
 +		last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
 +		if (!strncmp((char *)data_p, name, 4))
 +			return;
 +	}
 +}
 +
 +static void FindChunk(char *name)
 +{
 +	last_chunk = iff_data;
 +	FindNextChunk (name);
 +}
 +
 +/*
 +============
 +GetWavinfo
 +============
 +*/
 +static wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
 +{
 +	wavinfo_t	info;
 +
 +	Com_Memset (&info, 0, sizeof(info));
 +
 +	if (!wav)
 +		return info;
 +		
 +	iff_data = wav;
 +	iff_end = wav + wavlength;
 +
 +// find "RIFF" chunk
 +	FindChunk("RIFF");
 +	if (!(data_p && !strncmp((char *)data_p+8, "WAVE", 4)))
 +	{
 +		Com_Printf("Missing RIFF/WAVE chunks\n");
 +		return info;
 +	}
 +
 +// get "fmt " chunk
 +	iff_data = data_p + 12;
 +// DumpChunks ();
 +
 +	FindChunk("fmt ");
 +	if (!data_p)
 +	{
 +		Com_Printf("Missing fmt chunk\n");
 +		return info;
 +	}
 +	data_p += 8;
 +	info.format = GetLittleShort();
 +	info.channels = GetLittleShort();
 +	info.rate = GetLittleLong();
 +	data_p += 4+2;
 +	info.width = GetLittleShort() / 8;
 +
 +	if (info.format != 1)
 +	{
 +		Com_Printf("Microsoft PCM format only\n");
 +		return info;
 +	}
 +
 +
 +// find data chunk
 +	FindChunk("data");
 +	if (!data_p)
 +	{
 +		Com_Printf("Missing data chunk\n");
 +		return info;
 +	}
 +
 +	data_p += 4;
 +	info.samples = GetLittleLong () / info.width;
 +	info.dataofs = data_p - wav;
 +
 +	return info;
 +}
 +
 +
 +/*
 +================
 +ResampleSfx
 +
 +resample / decimate to the current source rate
 +================
 +*/
 +static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
 +	int		outcount;
 +	int		srcsample;
 +	float	stepscale;
 +	int		i;
 +	int		sample, samplefrac, fracstep;
 +	int			part;
 +	sndBuffer	*chunk;
 +	
 +	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
 +
 +	outcount = sfx->soundLength / stepscale;
 +	sfx->soundLength = outcount;
 +
 +	samplefrac = 0;
 +	fracstep = stepscale * 256;
 +	chunk = sfx->soundData;
 +
 +	for (i=0 ; i<outcount ; i++)
 +	{
 +		srcsample = samplefrac >> 8;
 +		samplefrac += fracstep;
 +		if( inwidth == 2 ) {
 +			sample = LittleShort ( ((short *)data)[srcsample] );
 +		} else {
 +			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
 +		}
 +		part  = (i&(SND_CHUNK_SIZE-1));
 +		if (part == 0) {
 +			sndBuffer	*newchunk;
 +			newchunk = SND_malloc();
 +			if (chunk == NULL) {
 +				sfx->soundData = newchunk;
 +			} else {
 +				chunk->next = newchunk;
 +			}
 +			chunk = newchunk;
 +		}
 +
 +		chunk->sndChunk[part] = sample;
 +	}
 +}
 +
 +/*
 +================
 +ResampleSfx
 +
 +resample / decimate to the current source rate
 +================
 +*/
 +static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
 +	int			outcount;
 +	int			srcsample;
 +	float		stepscale;
 +	int			i;
 +	int			sample, samplefrac, fracstep;
 +	
 +	stepscale = (float)inrate / dma.speed;	// this is usually 0.5, 1, or 2
 +
 +	outcount = samples / stepscale;
 +
 +	samplefrac = 0;
 +	fracstep = stepscale * 256;
 +
 +	for (i=0 ; i<outcount ; i++)
 +	{
 +		srcsample = samplefrac >> 8;
 +		samplefrac += fracstep;
 +		if( inwidth == 2 ) {
 +			sample = LittleShort ( ((short *)data)[srcsample] );
 +		} else {
 +			sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
 +		}
 +		sfx[i] = sample;
 +	}
 +	return outcount;
 +}
 +
 +
 +//=============================================================================
 +
 +/*
 +==============
 +S_LoadSound
 +
 +The filename may be different than sfx->name in the case
 +of a forced fallback of a player specific sound
 +==============
 +*/
 +qboolean S_LoadSound( sfx_t *sfx )
 +{
 +	byte	*data;
 +	short	*samples;
 +	wavinfo_t	info;
 +	int		size;
 +
 +	// player specific sounds are never directly loaded
 +	if ( sfx->soundName[0] == '*') {
 +		return qfalse;
 +	}
 +
 +	// load it in
 +	size = FS_ReadFile( sfx->soundName, (void **)&data );
 +	if ( !data ) {
 +		return qfalse;
 +	}
 +
 +	info = GetWavinfo( sfx->soundName, data, size );
 +	if ( info.channels != 1 ) {
 +		Com_Printf ("%s is a stereo wav file\n", sfx->soundName);
 +		FS_FreeFile (data);
 +		return qfalse;
 +	}
 +
 +	if ( info.width == 1 ) {
 +		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
 +	}
 +
 +	if ( info.rate != 22050 ) {
 +		Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
 +	}
 +
 +	samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
 +
 +	sfx->lastTimeUsed = Com_Milliseconds()+1;
 +
 +	// each of these compression schemes works just fine
 +	// but the 16bit quality is much nicer and with a local
 +	// install assured we can rely upon the sound memory
 +	// manager to do the right thing for us and page
 +	// sound in as needed
 +
 +	if( sfx->soundCompressed == qtrue) {
 +		sfx->soundCompressionMethod = 1;
 +		sfx->soundData = NULL;
 +		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
 +		S_AdpcmEncodeSound(sfx, samples);
 +#if 0
 +	} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
 +		sfx->soundCompressionMethod = 3;
 +		sfx->soundData = NULL;
 +		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
 +		encodeMuLaw( sfx, samples);
 +	} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
 +		sfx->soundCompressionMethod = 2;
 +		sfx->soundData = NULL;
 +		sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
 +		encodeWavelet( sfx, samples);
 +#endif
 +	} else {
 +		sfx->soundCompressionMethod = 0;
 +		sfx->soundLength = info.samples;
 +		sfx->soundData = NULL;
 +		ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
 +	}
 +	
 +	Hunk_FreeTempMemory(samples);
 +	FS_FreeFile( data );
 +
 +	return qtrue;
 +}
 +
 +void S_DisplayFreeMemory() {
 +	Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
 +}
 diff --git a/code/client/snd_mix.c b/code/client/snd_mix.c new file mode 100755 index 0000000..ec983b9 --- /dev/null +++ b/code/client/snd_mix.c @@ -0,0 +1,681 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +// snd_mix.c -- portable code to mix sounds for snd_dma.c
 +
 +#include "snd_local.h"
 +
 +static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
 +static int snd_vol;
 +
 +// bk001119 - these not static, required by unix/snd_mixa.s
 +int*     snd_p;  
 +int      snd_linear_count;
 +short*   snd_out;
 +
 +#if !( (defined __linux__ || defined __FreeBSD__ ) && (defined __i386__) ) // rb010123
 +#if	!id386
 +
 +void S_WriteLinearBlastStereo16 (void)
 +{
 +	int		i;
 +	int		val;
 +
 +	for (i=0 ; i<snd_linear_count ; i+=2)
 +	{
 +		val = snd_p[i]>>8;
 +		if (val > 0x7fff)
 +			snd_out[i] = 0x7fff;
 +		else if (val < -32768)
 +			snd_out[i] = -32768;
 +		else
 +			snd_out[i] = val;
 +
 +		val = snd_p[i+1]>>8;
 +		if (val > 0x7fff)
 +			snd_out[i+1] = 0x7fff;
 +		else if (val < -32768)
 +			snd_out[i+1] = -32768;
 +		else
 +			snd_out[i+1] = val;
 +	}
 +}
 +#else
 +
 +__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
 +{
 +	__asm {
 +
 + push edi
 + push ebx
 + mov ecx,ds:dword ptr[snd_linear_count]
 + mov ebx,ds:dword ptr[snd_p]
 + mov edi,ds:dword ptr[snd_out]
 +LWLBLoopTop:
 + mov eax,ds:dword ptr[-8+ebx+ecx*4]
 + sar eax,8
 + cmp eax,07FFFh
 + jg LClampHigh
 + cmp eax,0FFFF8000h
 + jnl LClampDone
 + mov eax,0FFFF8000h
 + jmp LClampDone
 +LClampHigh:
 + mov eax,07FFFh
 +LClampDone:
 + mov edx,ds:dword ptr[-4+ebx+ecx*4]
 + sar edx,8
 + cmp edx,07FFFh
 + jg LClampHigh2
 + cmp edx,0FFFF8000h
 + jnl LClampDone2
 + mov edx,0FFFF8000h
 + jmp LClampDone2
 +LClampHigh2:
 + mov edx,07FFFh
 +LClampDone2:
 + shl edx,16
 + and eax,0FFFFh
 + or edx,eax
 + mov ds:dword ptr[-4+edi+ecx*2],edx
 + sub ecx,2
 + jnz LWLBLoopTop
 + pop ebx
 + pop edi
 + ret
 +	}
 +}
 +
 +#endif
 +#else
 +// forward declare, implementation somewhere else
 +void S_WriteLinearBlastStereo16 (void);
 +#endif
 +
 +void S_TransferStereo16 (unsigned long *pbuf, int endtime)
 +{
 +	int		lpos;
 +	int		ls_paintedtime;
 +	
 +	snd_p = (int *) paintbuffer;
 +	ls_paintedtime = s_paintedtime;
 +
 +	while (ls_paintedtime < endtime)
 +	{
 +	// handle recirculating buffer issues
 +		lpos = ls_paintedtime & ((dma.samples>>1)-1);
 +
 +		snd_out = (short *) pbuf + (lpos<<1);
 +
 +		snd_linear_count = (dma.samples>>1) - lpos;
 +		if (ls_paintedtime + snd_linear_count > endtime)
 +			snd_linear_count = endtime - ls_paintedtime;
 +
 +		snd_linear_count <<= 1;
 +
 +	// write a linear blast of samples
 +		S_WriteLinearBlastStereo16 ();
 +
 +		snd_p += snd_linear_count;
 +		ls_paintedtime += (snd_linear_count>>1);
 +	}
 +}
 +
 +/*
 +===================
 +S_TransferPaintBuffer
 +
 +===================
 +*/
 +void S_TransferPaintBuffer(int endtime)
 +{
 +	int 	out_idx;
 +	int 	count;
 +	int 	out_mask;
 +	int 	*p;
 +	int 	step;
 +	int		val;
 +	unsigned long *pbuf;
 +
 +	pbuf = (unsigned long *)dma.buffer;
 +
 +
 +	if ( s_testsound->integer ) {
 +		int		i;
 +		int		count;
 +
 +		// write a fixed sine wave
 +		count = (endtime - s_paintedtime);
 +		for (i=0 ; i<count ; i++)
 +			paintbuffer[i].left = paintbuffer[i].right = sin((s_paintedtime+i)*0.1)*20000*256;
 +	}
 +
 +
 +	if (dma.samplebits == 16 && dma.channels == 2)
 +	{	// optimized case
 +		S_TransferStereo16 (pbuf, endtime);
 +	}
 +	else
 +	{	// general case
 +		p = (int *) paintbuffer;
 +		count = (endtime - s_paintedtime) * dma.channels;
 +		out_mask = dma.samples - 1; 
 +		out_idx = s_paintedtime * dma.channels & out_mask;
 +		step = 3 - dma.channels;
 +
 +		if (dma.samplebits == 16)
 +		{
 +			short *out = (short *) pbuf;
 +			while (count--)
 +			{
 +				val = *p >> 8;
 +				p+= step;
 +				if (val > 0x7fff)
 +					val = 0x7fff;
 +				else if (val < -32768)
 +					val = -32768;
 +				out[out_idx] = val;
 +				out_idx = (out_idx + 1) & out_mask;
 +			}
 +		}
 +		else if (dma.samplebits == 8)
 +		{
 +			unsigned char *out = (unsigned char *) pbuf;
 +			while (count--)
 +			{
 +				val = *p >> 8;
 +				p+= step;
 +				if (val > 0x7fff)
 +					val = 0x7fff;
 +				else if (val < -32768)
 +					val = -32768;
 +				out[out_idx] = (val>>8) + 128;
 +				out_idx = (out_idx + 1) & out_mask;
 +			}
 +		}
 +	}
 +}
 +
 +
 +/*
 +===============================================================================
 +
 +CHANNEL MIXING
 +
 +===============================================================================
 +*/
 +
 +static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
 +	int						data, aoff, boff;
 +	int						leftvol, rightvol;
 +	int						i, j;
 +	portable_samplepair_t	*samp;
 +	sndBuffer				*chunk;
 +	short					*samples;
 +	float					ooff, fdata, fdiv, fleftvol, frightvol;
 +
 +	samp = &paintbuffer[ bufferOffset ];
 +
 +	if (ch->doppler) {
 +		sampleOffset = sampleOffset*ch->oldDopplerScale;
 +	}
 +
 +	chunk = sc->soundData;
 +	while (sampleOffset>=SND_CHUNK_SIZE) {
 +		chunk = chunk->next;
 +		sampleOffset -= SND_CHUNK_SIZE;
 +		if (!chunk) {
 +			chunk = sc->soundData;
 +		}
 +	}
 +
 +	if (!ch->doppler || ch->dopplerScale==1.0f) {
 +#if idppc_altivec
 +		vector signed short volume_vec;
 +		vector unsigned int volume_shift;
 +		int vectorCount, samplesLeft, chunkSamplesLeft;
 +#endif
 +		leftvol = ch->leftvol*snd_vol;
 +		rightvol = ch->rightvol*snd_vol;
 +		samples = chunk->sndChunk;
 +#if idppc_altivec
 +		((short *)&volume_vec)[0] = leftvol;
 +		((short *)&volume_vec)[1] = leftvol;
 +		((short *)&volume_vec)[4] = leftvol;
 +		((short *)&volume_vec)[5] = leftvol;
 +		((short *)&volume_vec)[2] = rightvol;
 +		((short *)&volume_vec)[3] = rightvol;
 +		((short *)&volume_vec)[6] = rightvol;
 +		((short *)&volume_vec)[7] = rightvol;
 +		volume_shift = vec_splat_u32(8);
 +		i = 0;
 +
 +		while(i < count) {
 +			/* Try to align destination to 16-byte boundary */
 +			while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
 +				data  = samples[sampleOffset++];
 +				samp[i].left += (data * leftvol)>>8;
 +				samp[i].right += (data * rightvol)>>8;
 +	
 +				if (sampleOffset == SND_CHUNK_SIZE) {
 +					chunk = chunk->next;
 +					samples = chunk->sndChunk;
 +					sampleOffset = 0;
 +				}
 +				i++;
 +			}
 +			/* Destination is now aligned.  Process as many 8-sample 
 +			   chunks as we can before we run out of room from the current
 +			   sound chunk.  We do 8 per loop to avoid extra source data reads. */
 +			samplesLeft = count - i;
 +			chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
 +			if(samplesLeft > chunkSamplesLeft)
 +				samplesLeft = chunkSamplesLeft;
 +			
 +			vectorCount = samplesLeft / 8;
 +			
 +			if(vectorCount)
 +			{
 +				vector unsigned char tmp;
 +				vector short s0, s1, sampleData0, sampleData1;
 +				vector short samples0, samples1;
 +				vector signed int left0, right0;
 +				vector signed int merge0, merge1;
 +				vector signed int d0, d1, d2, d3;				
 +				vector unsigned char samplePermute0 = 
 +					(vector unsigned char)(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
 +				vector unsigned char samplePermute1 = 
 +					(vector unsigned char)(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
 +				vector unsigned char loadPermute0, loadPermute1;
 +				
 +				// Rather than permute the vectors after we load them to do the sample
 +				// replication and rearrangement, we permute the alignment vector so
 +				// we do everything in one step below and avoid data shuffling.
 +				tmp = vec_lvsl(0,&samples[sampleOffset]);								
 +				loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
 +				loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
 +				
 +				s0 = *(vector short *)&samples[sampleOffset];
 +				while(vectorCount)
 +				{
 +					/* Load up source (16-bit) sample data */
 +					s1 = *(vector short *)&samples[sampleOffset+7];
 +					
 +					/* Load up destination sample data */
 +					d0 = *(vector signed int *)&samp[i];
 +					d1 = *(vector signed int *)&samp[i+2];
 +					d2 = *(vector signed int *)&samp[i+4];
 +					d3 = *(vector signed int *)&samp[i+6];
 +
 +					sampleData0 = vec_perm(s0,s1,loadPermute0);
 +					sampleData1 = vec_perm(s0,s1,loadPermute1);
 +					
 +					merge0 = vec_mule(sampleData0,volume_vec);
 +					merge0 = vec_sra(merge0,volume_shift);	/* Shift down to proper range */
 +					
 +					merge1 = vec_mulo(sampleData0,volume_vec);
 +					merge1 = vec_sra(merge1,volume_shift);
 +					
 +					d0 = vec_add(merge0,d0);
 +					d1 = vec_add(merge1,d1);
 +					
 +					merge0 = vec_mule(sampleData1,volume_vec);
 +					merge0 = vec_sra(merge0,volume_shift);	/* Shift down to proper range */
 +					
 +					merge1 = vec_mulo(sampleData1,volume_vec);
 +					merge1 = vec_sra(merge1,volume_shift);					
 +
 +					d2 = vec_add(merge0,d2);
 +					d3 = vec_add(merge1,d3);
 +
 +					/* Store destination sample data */
 +					*(vector signed int *)&samp[i] = d0;
 +					*(vector signed int *)&samp[i+2] = d1;
 +					*(vector signed int *)&samp[i+4] = d2;
 +					*(vector signed int *)&samp[i+6] = d3;
 +
 +					i += 8;
 +					vectorCount--;
 +					s0 = s1;
 +					sampleOffset += 8;
 +				}
 +				if (sampleOffset == SND_CHUNK_SIZE) {
 +					chunk = chunk->next;
 +					samples = chunk->sndChunk;
 +					sampleOffset = 0;
 +				}
 +			}
 +		}
 +#else			
 +		for ( i=0 ; i<count ; i++ ) {
 +			data  = samples[sampleOffset++];
 +			samp[i].left += (data * leftvol)>>8;
 +			samp[i].right += (data * rightvol)>>8;
 +
 +			if (sampleOffset == SND_CHUNK_SIZE) {
 +				chunk = chunk->next;
 +				samples = chunk->sndChunk;
 +				sampleOffset = 0;
 +			}
 +		}
 +#endif
 +	} else {
 +		fleftvol = ch->leftvol*snd_vol;
 +		frightvol = ch->rightvol*snd_vol;
 +
 +		ooff = sampleOffset;
 +		samples = chunk->sndChunk;
 +		
 +
 +
 +
 +		for ( i=0 ; i<count ; i++ ) {
 +
 +			aoff = ooff;
 +			ooff = ooff + ch->dopplerScale;
 +			boff = ooff;
 +			fdata = 0;
 +			for (j=aoff; j<boff; j++) {
 +				if (j == SND_CHUNK_SIZE) {
 +					chunk = chunk->next;
 +					if (!chunk) {
 +						chunk = sc->soundData;
 +					}
 +					samples = chunk->sndChunk;
 +					ooff -= SND_CHUNK_SIZE;
 +				}
 +				fdata  += samples[j&(SND_CHUNK_SIZE-1)];
 +			}
 +			fdiv = 256 * (boff-aoff);
 +			samp[i].left += (fdata * fleftvol)/fdiv;
 +			samp[i].right += (fdata * frightvol)/fdiv;
 +		}
 +	}
 +}
 +
 +void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
 +	int						data;
 +	int						leftvol, rightvol;
 +	int						i;
 +	portable_samplepair_t	*samp;
 +	sndBuffer				*chunk;
 +	short					*samples;
 +
 +	leftvol = ch->leftvol*snd_vol;
 +	rightvol = ch->rightvol*snd_vol;
 +
 +	i = 0;
 +	samp = &paintbuffer[ bufferOffset ];
 +	chunk = sc->soundData;
 +	while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) {
 +		chunk = chunk->next;
 +		sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4);
 +		i++;
 +	}
 +
 +	if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
 +		S_AdpcmGetSamples( chunk, sfxScratchBuffer );
 +		sfxScratchIndex = i;
 +		sfxScratchPointer = sc;
 +	}
 +
 +	samples = sfxScratchBuffer;
 +
 +	for ( i=0 ; i<count ; i++ ) {
 +		data  = samples[sampleOffset++];
 +		samp[i].left += (data * leftvol)>>8;
 +		samp[i].right += (data * rightvol)>>8;
 +
 +		if (sampleOffset == SND_CHUNK_SIZE*2) {
 +			chunk = chunk->next;
 +			decodeWavelet(chunk, sfxScratchBuffer);
 +			sfxScratchIndex++;
 +			sampleOffset = 0;
 +		}
 +	}
 +}
 +
 +void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
 +	int						data;
 +	int						leftvol, rightvol;
 +	int						i;
 +	portable_samplepair_t	*samp;
 +	sndBuffer				*chunk;
 +	short					*samples;
 +
 +	leftvol = ch->leftvol*snd_vol;
 +	rightvol = ch->rightvol*snd_vol;
 +
 +	i = 0;
 +	samp = &paintbuffer[ bufferOffset ];
 +	chunk = sc->soundData;
 +
 +	if (ch->doppler) {
 +		sampleOffset = sampleOffset*ch->oldDopplerScale;
 +	}
 +
 +	while (sampleOffset>=(SND_CHUNK_SIZE*4)) {
 +		chunk = chunk->next;
 +		sampleOffset -= (SND_CHUNK_SIZE*4);
 +		i++;
 +	}
 +
 +	if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
 +		S_AdpcmGetSamples( chunk, sfxScratchBuffer );
 +		sfxScratchIndex = i;
 +		sfxScratchPointer = sc;
 +	}
 +
 +	samples = sfxScratchBuffer;
 +
 +	for ( i=0 ; i<count ; i++ ) {
 +		data  = samples[sampleOffset++];
 +		samp[i].left += (data * leftvol)>>8;
 +		samp[i].right += (data * rightvol)>>8;
 +
 +		if (sampleOffset == SND_CHUNK_SIZE*4) {
 +			chunk = chunk->next;
 +			S_AdpcmGetSamples( chunk, sfxScratchBuffer);
 +			sampleOffset = 0;
 +			sfxScratchIndex++;
 +		}
 +	}
 +}
 +
 +void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
 +	int						data;
 +	int						leftvol, rightvol;
 +	int						i;
 +	portable_samplepair_t	*samp;
 +	sndBuffer				*chunk;
 +	byte					*samples;
 +	float					ooff;
 +
 +	leftvol = ch->leftvol*snd_vol;
 +	rightvol = ch->rightvol*snd_vol;
 +
 +	samp = &paintbuffer[ bufferOffset ];
 +	chunk = sc->soundData;
 +	while (sampleOffset>=(SND_CHUNK_SIZE*2)) {
 +		chunk = chunk->next;
 +		sampleOffset -= (SND_CHUNK_SIZE*2);
 +		if (!chunk) {
 +			chunk = sc->soundData;
 +		}
 +	}
 +
 +	if (!ch->doppler) {
 +		samples = (byte *)chunk->sndChunk + sampleOffset;
 +		for ( i=0 ; i<count ; i++ ) {
 +			data  = mulawToShort[*samples];
 +			samp[i].left += (data * leftvol)>>8;
 +			samp[i].right += (data * rightvol)>>8;
 +			samples++;
 +			if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
 +				chunk = chunk->next;
 +				samples = (byte *)chunk->sndChunk;
 +			}
 +		}
 +	} else {
 +		ooff = sampleOffset;
 +		samples = (byte *)chunk->sndChunk;
 +		for ( i=0 ; i<count ; i++ ) {
 +			data  = mulawToShort[samples[(int)(ooff)]];
 +			ooff = ooff + ch->dopplerScale;
 +			samp[i].left += (data * leftvol)>>8;
 +			samp[i].right += (data * rightvol)>>8;
 +			if (ooff >= SND_CHUNK_SIZE*2) {
 +				chunk = chunk->next;
 +				if (!chunk) {
 +					chunk = sc->soundData;
 +				}
 +				samples = (byte *)chunk->sndChunk;
 +				ooff = 0.0;
 +			}
 +		}
 +	}
 +}
 +
 +/*
 +===================
 +S_PaintChannels
 +===================
 +*/
 +void S_PaintChannels( int endtime ) {
 +	int 	i;
 +	int 	end;
 +	channel_t *ch;
 +	sfx_t	*sc;
 +	int		ltime, count;
 +	int		sampleOffset;
 +
 +
 +	snd_vol = s_volume->value*255;
 +
 +//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
 +	while ( s_paintedtime < endtime ) {
 +		// if paintbuffer is smaller than DMA buffer
 +		// we may need to fill it multiple times
 +		end = endtime;
 +		if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) {
 +			end = s_paintedtime + PAINTBUFFER_SIZE;
 +		}
 +
 +		// clear the paint buffer to either music or zeros
 +		if ( s_rawend < s_paintedtime ) {
 +			if ( s_rawend ) {
 +				//Com_DPrintf ("background sound underrun\n");
 +			}
 +			Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
 +		} else {
 +			// copy from the streaming sound source
 +			int		s;
 +			int		stop;
 +
 +			stop = (end < s_rawend) ? end : s_rawend;
 +
 +			for ( i = s_paintedtime ; i < stop ; i++ ) {
 +				s = i&(MAX_RAW_SAMPLES-1);
 +				paintbuffer[i-s_paintedtime] = s_rawsamples[s];
 +			}
 +//		if (i != end)
 +//			Com_Printf ("partial stream\n");
 +//		else
 +//			Com_Printf ("full stream\n");
 +			for ( ; i < end ; i++ ) {
 +				paintbuffer[i-s_paintedtime].left =
 +				paintbuffer[i-s_paintedtime].right = 0;
 +			}
 +		}
 +
 +		// paint in the channels.
 +		ch = s_channels;
 +		for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {		
 +			if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
 +				continue;
 +			}
 +
 +			ltime = s_paintedtime;
 +			sc = ch->thesfx;
 +
 +			sampleOffset = ltime - ch->startSample;
 +			count = end - ltime;
 +			if ( sampleOffset + count > sc->soundLength ) {
 +				count = sc->soundLength - sampleOffset;
 +			}
 +
 +			if ( count > 0 ) {	
 +				if( sc->soundCompressionMethod == 1) {
 +					S_PaintChannelFromADPCM		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +				} else if( sc->soundCompressionMethod == 2) {
 +					S_PaintChannelFromWavelet	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +				} else if( sc->soundCompressionMethod == 3) {
 +					S_PaintChannelFromMuLaw	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +				} else {
 +					S_PaintChannelFrom16		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +				}
 +			}
 +		}
 +
 +		// paint in the looped channels.
 +		ch = loop_channels;
 +		for ( i = 0; i < numLoopChannels ; i++, ch++ ) {		
 +			if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) {
 +				continue;
 +			}
 +
 +			ltime = s_paintedtime;
 +			sc = ch->thesfx;
 +
 +			if (sc->soundData==NULL || sc->soundLength==0) {
 +				continue;
 +			}
 +			// we might have to make two passes if it
 +			// is a looping sound effect and the end of
 +			// the sample is hit
 +			do {
 +				sampleOffset = (ltime % sc->soundLength);
 +
 +				count = end - ltime;
 +				if ( sampleOffset + count > sc->soundLength ) {
 +					count = sc->soundLength - sampleOffset;
 +				}
 +
 +				if ( count > 0 ) {	
 +					if( sc->soundCompressionMethod == 1) {
 +						S_PaintChannelFromADPCM		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +					} else if( sc->soundCompressionMethod == 2) {
 +						S_PaintChannelFromWavelet	(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +					} else if( sc->soundCompressionMethod == 3) {
 +						S_PaintChannelFromMuLaw		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +					} else {
 +						S_PaintChannelFrom16		(ch, sc, count, sampleOffset, ltime - s_paintedtime);
 +					}
 +					ltime += count;
 +				}
 +			} while ( ltime < end);
 +		}
 +
 +		// transfer out according to DMA format
 +		S_TransferPaintBuffer( end );
 +		s_paintedtime = end;
 +	}
 +}
 diff --git a/code/client/snd_public.h b/code/client/snd_public.h new file mode 100755 index 0000000..438fc53 --- /dev/null +++ b/code/client/snd_public.h @@ -0,0 +1,72 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +
 +void S_Init( void );
 +void S_Shutdown( void );
 +
 +// if origin is NULL, the sound will be dynamically sourced from the entity
 +void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
 +void S_StartLocalSound( sfxHandle_t sfx, int channelNum );
 +
 +void S_StartBackgroundTrack( const char *intro, const char *loop );
 +void S_StopBackgroundTrack( void );
 +
 +// cinematics and voice-over-network will send raw samples
 +// 1.0 volume will be direct output of source samples
 +void S_RawSamples (int samples, int rate, int width, int channels, 
 +				   const byte *data, float volume);
 +
 +// stop all sounds and the background track
 +void S_StopAllSounds( void );
 +
 +// all continuous looping sounds must be added before calling S_Update
 +void S_ClearLoopingSounds( qboolean killall );
 +void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
 +void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
 +void S_StopLoopingSound(int entityNum );
 +
 +// recompute the reletive volumes for all running sounds
 +// reletive to the given entityNum / orientation
 +void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
 +
 +// let the sound system know where an entity currently is
 +void S_UpdateEntityPosition( int entityNum, const vec3_t origin );
 +
 +void S_Update( void );
 +
 +void S_DisableSounds( void );
 +
 +void S_BeginRegistration( void );
 +
 +// RegisterSound will allways return a valid sample, even if it
 +// has to create a placeholder.  This prevents continuous filesystem
 +// checks for missing files
 +sfxHandle_t	S_RegisterSound( const char *sample, qboolean compressed );
 +
 +void S_DisplayFreeMemory(void);
 +
 +void S_ClearSoundBuffer( void );
 +
 +void SNDDMA_Activate( void );
 +
 +void S_UpdateBackgroundTrack( void );
 diff --git a/code/client/snd_wavelet.c b/code/client/snd_wavelet.c new file mode 100755 index 0000000..9b07ee1 --- /dev/null +++ b/code/client/snd_wavelet.c @@ -0,0 +1,253 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +
 +This file is part of Quake III Arena source code.
 +
 +Quake III Arena source code is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Quake III Arena source code is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Foobar; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#include "snd_local.h"
 +
 +long myftol( float f );
 +
 +#define C0 0.4829629131445341
 +#define C1 0.8365163037378079
 +#define C2 0.2241438680420134
 +#define C3 -0.1294095225512604
 +
 +void daub4(float b[], unsigned long n, int isign)
 +{
 +	float wksp[4097];
 +	float	*a=b-1;						// numerical recipies so a[1] = b[0]
 +
 +	unsigned long nh,nh1,i,j;
 +
 +	if (n < 4) return;
 +
 +	nh1=(nh=n >> 1)+1;
 +	if (isign >= 0) {
 +		for (i=1,j=1;j<=n-3;j+=2,i++) {
 +			wksp[i]	   = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3];
 +			wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3];
 +		}
 +		wksp[i   ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2];
 +		wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2];
 +	} else {
 +		wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1];
 +		wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1];
 +		for (i=1,j=3;i<nh;i++) {
 +			wksp[j++] = C2*a[i]+C1*a[i+nh]+C0*a[i+1]+C3*a[i+nh1];
 +			wksp[j++] = C3*a[i]-C0*a[i+nh]+C1*a[i+1]-C2*a[i+nh1];
 +		}
 +	}
 +	for (i=1;i<=n;i++) {
 +		a[i]=wksp[i];
 +	}
 +}
 +
 +void wt1(float a[], unsigned long n, int isign)
 +{
 +	unsigned long nn;
 +	int inverseStartLength = n/4;
 +	if (n < inverseStartLength) return;
 +	if (isign >= 0) {
 +		for (nn=n;nn>=inverseStartLength;nn>>=1) daub4(a,nn,isign);
 +	} else {
 +		for (nn=inverseStartLength;nn<=n;nn<<=1) daub4(a,nn,isign);
 +	}
 +}
 +
 +/* The number of bits required by each value */
 +static unsigned char numBits[] = {
 +   0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
 +   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
 +   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 +   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
 +   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 +   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 +   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 +   8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
 +};
 +
 +byte MuLawEncode(short s) {
 +	unsigned long adjusted;
 +	byte sign, exponent, mantissa;
 +
 +	sign = (s<0)?0:0x80;
 +
 +	if (s<0) s=-s;
 +	adjusted = (long)s << (16-sizeof(short)*8);
 +	adjusted += 128L + 4L;
 +	if (adjusted > 32767) adjusted = 32767;
 +	exponent = numBits[(adjusted>>7)&0xff] - 1;
 +	mantissa = (adjusted>>(exponent+3))&0xf;
 +	return ~(sign | (exponent<<4) | mantissa);
 +}
 +
 +short MuLawDecode(byte uLaw) {
 +	signed long adjusted;
 +	byte exponent, mantissa;
 +
 +	uLaw = ~uLaw;
 +	exponent = (uLaw>>4) & 0x7;
 +	mantissa = (uLaw&0xf) + 16;
 +	adjusted = (mantissa << (exponent +3)) - 128 - 4;
 +
 +	return (uLaw & 0x80)? adjusted : -adjusted;
 +}
 +
 +short mulawToShort[256];
 +static qboolean madeTable = qfalse;
 +
 +static	int	NXStreamCount;
 +
 +void NXPutc(NXStream *stream, char out) {
 +	stream[NXStreamCount++] = out;
 +}
 +
 +
 +void encodeWavelet( sfx_t *sfx, short *packets) {
 +	float	wksp[4097], temp;
 +	int		i, samples, size;
 +	sndBuffer		*newchunk, *chunk;
 +	byte			*out;
 +
 +	if (!madeTable) {
 +		for (i=0;i<256;i++) {
 +			mulawToShort[i] = (float)MuLawDecode((byte)i);
 +		}
 +		madeTable = qtrue;
 +	}
 +	chunk = NULL;
 +
 +	samples = sfx->soundLength;
 +	while(samples>0) {
 +		size = samples;
 +		if (size>(SND_CHUNK_SIZE*2)) {
 +			size = (SND_CHUNK_SIZE*2);
 +		}
 +
 +		if (size<4) {
 +			size = 4;
 +		}
 +
 +		newchunk = SND_malloc();
 +		if (sfx->soundData == NULL) {
 +			sfx->soundData = newchunk;
 +		} else {
 +			chunk->next = newchunk;
 +		}
 +		chunk = newchunk;
 +		for(i=0; i<size; i++) {
 +			wksp[i] = *packets;
 +			packets++;
 +		}
 +		wt1(wksp, size, 1);
 +		out = (byte *)chunk->sndChunk;
 +
 +		for(i=0;i<size;i++) {
 +			temp = wksp[i];
 +			if (temp > 32767) temp = 32767; else if (temp<-32768) temp = -32768;
 +			out[i] = MuLawEncode((short)temp);
 +		}
 +
 +		chunk->size = size;
 +		samples -= size;
 +	}
 +}
 +
 +void decodeWavelet(sndBuffer *chunk, short *to) {
 +	float			wksp[4097];
 +	int				i;
 +	byte			*out;
 +
 +	int size = chunk->size;
 +	
 +	out = (byte *)chunk->sndChunk;
 +	for(i=0;i<size;i++) {
 +		wksp[i] = mulawToShort[out[i]];
 +	}
 +
 +	wt1(wksp, size, -1);
 +	
 +	if (!to) return;
 +
 +	for(i=0; i<size; i++) {
 +		to[i] = wksp[i];
 +	}
 +}
 +
 +
 +void encodeMuLaw( sfx_t *sfx, short *packets) {
 +	int		i, samples, size, grade, poop;
 +	sndBuffer		*newchunk, *chunk;
 +	byte			*out;
 +
 +	if (!madeTable) {
 +		for (i=0;i<256;i++) {
 +			mulawToShort[i] = (float)MuLawDecode((byte)i);
 +		}
 +		madeTable = qtrue;
 +	}
 +
 +	chunk = NULL;
 +	samples = sfx->soundLength;
 +	grade = 0;
 +
 +	while(samples>0) {
 +		size = samples;
 +		if (size>(SND_CHUNK_SIZE*2)) {
 +			size = (SND_CHUNK_SIZE*2);
 +		}
 +
 +		newchunk = SND_malloc();
 +		if (sfx->soundData == NULL) {
 +			sfx->soundData = newchunk;
 +		} else {
 +			chunk->next = newchunk;
 +		}
 +		chunk = newchunk;
 +		out = (byte *)chunk->sndChunk;
 +		for(i=0; i<size; i++) {
 +			poop = packets[0]+grade;
 +			if (poop>32767) {
 +				poop = 32767;
 +			} else if (poop<-32768) {
 +				poop = -32768;
 +			}
 +			out[i] = MuLawEncode((short)poop);
 +			grade = poop - mulawToShort[out[i]];
 +			packets++;
 +		}
 +		chunk->size = size;
 +		samples -= size;
 +	}
 +}
 +
 +void decodeMuLaw(sndBuffer *chunk, short *to) {
 +	int				i;
 +	byte			*out;
 +
 +	int size = chunk->size;
 +	
 +	out = (byte *)chunk->sndChunk;
 +	for(i=0;i<size;i++) {
 +		to[i] = mulawToShort[out[i]];
 +	}
 +}
 +
 +
  | 
