diff options
author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
---|---|---|
committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/client | |
parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/client')
-rwxr-xr-x | code/client/cl_cgame.c | 2058 | ||||
-rwxr-xr-x | code/client/cl_cin.c | 3480 | ||||
-rwxr-xr-x | code/client/cl_console.c | 1572 | ||||
-rwxr-xr-x | code/client/cl_input.c | 1802 | ||||
-rwxr-xr-x | code/client/cl_keys.c | 2504 | ||||
-rwxr-xr-x | code/client/cl_main.c | 6648 | ||||
-rwxr-xr-x | code/client/cl_net_chan.c | 334 | ||||
-rwxr-xr-x | code/client/cl_parse.c | 1310 | ||||
-rwxr-xr-x | code/client/cl_scrn.c | 1094 | ||||
-rwxr-xr-x | code/client/cl_ui.c | 2400 | ||||
-rwxr-xr-x | code/client/client.h | 1038 | ||||
-rwxr-xr-x | code/client/keys.h | 114 | ||||
-rwxr-xr-x | code/client/snd_adpcm.c | 660 | ||||
-rwxr-xr-x | code/client/snd_dma.c | 3272 | ||||
-rwxr-xr-x | code/client/snd_local.h | 408 | ||||
-rwxr-xr-x | code/client/snd_mem.c | 808 | ||||
-rwxr-xr-x | code/client/snd_mix.c | 1362 | ||||
-rwxr-xr-x | code/client/snd_public.h | 144 | ||||
-rwxr-xr-x | code/client/snd_wavelet.c | 506 |
19 files changed, 15757 insertions, 15757 deletions
diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 0b259d3..56dc01c 100755 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -1,1029 +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
- }
- }
-
-}
-
-
-
+/* +=========================================================================== +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 index 7929624..f042d23 100755 --- a/code/client/cl_cin.c +++ b/code/client/cl_cin.c @@ -1,1740 +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--;
- }
- }
-}
-
+/* +=========================================================================== +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 index 50df0e2..a428e3f 100755 --- a/code/client/cl_console.c +++ b/code/client/cl_console.c @@ -1,786 +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;
-}
+/* +=========================================================================== +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 index f450ee7..06266eb 100755 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -1,901 +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);
-}
+/* +=========================================================================== +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 index 7dcd7d8..1ccfc52 100755 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -1,1252 +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;
- }
-}
-
+/* +=========================================================================== +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 index 89d3b48..61dbcc7 100755 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1,3324 +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;
-}
-
-
+/* +=========================================================================== +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 index d1e0b08..904636e 100755 --- a/code/client/cl_net_chan.c +++ b/code/client/cl_net_chan.c @@ -1,167 +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;
-}
+/* +=========================================================================== +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 index 9de3588..e3dedb3 100755 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -1,655 +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;
- }
- }
-}
-
-
+/* +=========================================================================== +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 index c4ccb40..a8da4ed 100755 --- a/code/client/cl_scrn.c +++ b/code/client/cl_scrn.c @@ -1,547 +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;
-}
-
+/* +=========================================================================== +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 index 80040a8..96cc07e 100755 --- a/code/client/cl_ui.c +++ b/code/client/cl_ui.c @@ -1,1200 +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 );
-}
+/* +=========================================================================== +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 index 09e02a8..f188f4b 100755 --- a/code/client/client.h +++ b/code/client/client.h @@ -1,519 +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 );
+/* +=========================================================================== +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 index bc598f4..c852f5a 100755 --- a/code/client/keys.h +++ b/code/client/keys.h @@ -1,57 +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);
+/* +=========================================================================== +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 index bc52ad4..3e335fb 100755 --- a/code/client/snd_adpcm.c +++ b/code/client/snd_adpcm.c @@ -1,330 +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;
- }
-}
+/*********************************************************** +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 index 6ecff18..aae87ec 100755 --- a/code/client/snd_dma.c +++ b/code/client/snd_dma.c @@ -1,1636 +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;
-}
+/* +=========================================================================== +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 index 5b0c97a..466c52b 100755 --- a/code/client/snd_local.h +++ b/code/client/snd_local.h @@ -1,204 +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;
-
+/* +=========================================================================== +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 index 0e400d5..23c413d 100755 --- a/code/client/snd_mem.c +++ b/code/client/snd_mem.c @@ -1,404 +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);
-}
+/* +=========================================================================== +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 index ec983b9..02be59c 100755 --- a/code/client/snd_mix.c +++ b/code/client/snd_mix.c @@ -1,681 +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;
- }
-}
+/* +=========================================================================== +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 index 438fc53..659d235 100755 --- a/code/client/snd_public.h +++ b/code/client/snd_public.h @@ -1,72 +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 );
+/* +=========================================================================== +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 index 9b07ee1..29b2608 100755 --- a/code/client/snd_wavelet.c +++ b/code/client/snd_wavelet.c @@ -1,253 +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]];
- }
-}
-
-
+/* +=========================================================================== +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]]; + } +} + + |