void Sys_FS_FindClose( void ) { assert( findbase ); if( fdir ) { closedir( fdir ); fdir = NULL; } fdfd = -1; fdots = 0; Mem_TempFree( findbase ); findbase = NULL; findbase_size = 0; findpattern = NULL; if( findpath ) { Mem_TempFree( findpath ); findpath = NULL; findpath_size = 0; } }
// free the result from mm_parseResponse2 void MM_FreeResponse( char **argv ) { if( argv ) { if( argv[0] ) Mem_TempFree( argv[0] ); Mem_TempFree( argv ); } }
/* * R_Bloom_InitTextures */ static void R_Bloom_InitTextures( void ) { qbyte *data; int size; if( glConfig.ext.texture_non_power_of_two ) { screen_texture_width = glState.width; screen_texture_height = glState.height; } else { // find closer power of 2 to screen size for (screen_texture_width = 1;screen_texture_width < glState.width;screen_texture_width <<= 1); for (screen_texture_height = 1;screen_texture_height < glState.height;screen_texture_height <<= 1); } // disable blooms if we can't handle a texture of that size if( screen_texture_width > glConfig.maxTextureSize || screen_texture_height > glConfig.maxTextureSize ) { screen_texture_width = screen_texture_height = 0; Cvar_ForceSet( "r_bloom", "0" ); Com_Printf( S_COLOR_YELLOW "WARNING: 'R_InitBloomScreenTexture' too high resolution for light bloom, effect disabled\n" ); return; } // init the screen texture size = screen_texture_width * screen_texture_height * 4; data = Mem_TempMalloc( size ); memset( data, 255, size ); r_bloomscreentexture = R_LoadPic( "***r_bloomscreentexture***", &data, screen_texture_width, screen_texture_height, IT_CLAMP|IT_NOMIPMAP|IT_NOCOMPRESS|IT_NOPICMIP, 3 ); Mem_TempFree( data ); // validate bloom size and init the bloom effect texture R_Bloom_InitEffectTexture(); // if screensize is more than 2x the bloom effect texture, set up for stepped downsampling r_bloomdownsamplingtexture = NULL; r_screendownsamplingtexture_size = 0; if( (glState.width > (BLOOM_SIZE * 2) || glState.height > (BLOOM_SIZE * 2)) && !r_bloom_fast_sample->integer ) { r_screendownsamplingtexture_size = (int)( BLOOM_SIZE * 2 ); data = Mem_TempMalloc( r_screendownsamplingtexture_size * r_screendownsamplingtexture_size * 4 ); memset( data, 0, r_screendownsamplingtexture_size * r_screendownsamplingtexture_size * 4 ); r_bloomdownsamplingtexture = R_LoadPic( "***r_bloomdownsamplingtexture***", &data, r_screendownsamplingtexture_size, r_screendownsamplingtexture_size, IT_CLAMP|IT_NOMIPMAP|IT_NOCOMPRESS|IT_NOPICMIP, 3 ); Mem_TempFree( data ); } // init the screen backup texture if( r_screendownsamplingtexture_size ) R_Bloom_InitBackUpTexture( r_screendownsamplingtexture_size, r_screendownsamplingtexture_size ); else R_Bloom_InitBackUpTexture( BLOOM_SIZE, BLOOM_SIZE ); }
/* * Con_Dump_f * * Save the console contents out to a file */ static void Con_Dump_f( void ) { int file; size_t buffer_size; char *buffer; size_t name_size; char *name; const char *newline = "\r\n"; if( !con_initialized ) return; if( Cmd_Argc() != 2 ) { Com_Printf( "usage: condump <filename>\n" ); return; } name_size = sizeof( char ) * ( strlen( Cmd_Argv( 1 ) ) + strlen( ".txt" ) + 1 ); name = Mem_TempMalloc( name_size ); Q_strncpyz( name, Cmd_Argv( 1 ), name_size ); COM_DefaultExtension( name, ".txt", name_size ); COM_SanitizeFilePath( name ); if( !COM_ValidateRelativeFilename( name ) ) { Com_Printf( "Invalid filename.\n" ); Mem_TempFree( name ); return; } if( FS_FOpenFile( name, &file, FS_WRITE ) == -1 ) { Com_Printf( "Couldn't open: %s\n", name ); Mem_TempFree( name ); return; } buffer_size = Con_BufferText( NULL, newline ) + 1; buffer = Mem_TempMalloc( buffer_size ); Con_BufferText( buffer, newline ); FS_Write( buffer, buffer_size - 1, file ); FS_FCloseFile( file ); Mem_TempFree( buffer ); Com_Printf( "Dumped console text: %s\n", name ); Mem_TempFree( name ); }
/* * R_Bloom_InitEffectTexture */ static void R_Bloom_InitEffectTexture( void ) { qbyte *data; int limit; if( r_bloom_sample_size->integer < 32 ) Cvar_ForceSet( "r_bloom_sample_size", "32" ); // make sure bloom size doesn't have stupid values limit = min( r_bloom_sample_size->integer, min( screen_texture_width, screen_texture_height ) ); if( glConfig.ext.texture_non_power_of_two ) BLOOM_SIZE = limit; else // make sure bloom size is a power of 2 for( BLOOM_SIZE = 32; (BLOOM_SIZE<<1) <= limit; BLOOM_SIZE <<= 1 ); if( BLOOM_SIZE != r_bloom_sample_size->integer ) Cvar_ForceSet( "r_bloom_sample_size", va( "%i", BLOOM_SIZE ) ); data = Mem_TempMalloc( BLOOM_SIZE * BLOOM_SIZE * 4 ); memset( data, 0, BLOOM_SIZE * BLOOM_SIZE * 4 ); r_bloomeffecttexture = R_LoadPic( "***r_bloomeffecttexture***", &data, BLOOM_SIZE, BLOOM_SIZE, IT_CLAMP|IT_NOMIPMAP|IT_NOCOMPRESS|IT_NOPICMIP, 3 ); Mem_TempFree( data ); }
/* * Com_LoadScriptLibrary */ static void *Com_LoadScriptLibrary( const char *basename, void *parms ) { size_t file_size; char *file; void *( *GetAngelwrapAPI )(void *); dllfunc_t funcs[2]; if( script_library ) Com_Error( ERR_FATAL, "Com_LoadScriptLibrary without Com_UnloadScriptLibrary" ); file_size = strlen( LIB_DIRECTORY "/" ) + strlen( basename ) + 1 + strlen( ARCH ) + strlen( LIB_SUFFIX ) + 1; file = Mem_TempMalloc( file_size ); Q_snprintfz( file, file_size, LIB_DIRECTORY "/%s_" ARCH LIB_SUFFIX, basename ); funcs[0].name = "GetAngelwrapAPI"; funcs[0].funcPointer = ( void ** )&GetAngelwrapAPI; funcs[1].name = NULL; script_library = Com_LoadLibrary( file, funcs ); Mem_TempFree( file ); if( script_library ) return GetAngelwrapAPI( parms ); return NULL; }
/* * ML_Update */ bool ML_Update( void ) { int i, len, total, newpaks; size_t size; char *map, *maps, *filename; newpaks = FS_Rescan(); if( !newpaks ) return false; total = FS_GetFileListExt( "maps", ".bsp", NULL, &size, 0, 0 ); if( size ) { maps = ( char* )Mem_TempMalloc( size ); FS_GetFileList( "maps", ".bsp", maps, size, 0, 0 ); for( i = 0, len = 0; i < total; i++ ) { map = maps + len; len += strlen( map ) + 1; filename = ( char * )COM_FileBase( map ); COM_StripExtension( filename ); // don't check for existance of each file itself, as we've just got the fresh list if( !ML_FilenameExistsExt( filename, true ) ) ML_AddMap( filename, MLIST_UNKNOWN_MAPNAME ); } Mem_TempFree( maps ); } return true; }
// callback for scandir int evdev_filter( const struct dirent *de ) { char *filename; int fd; // check that we have character file if( de->d_type != DT_CHR ) return 0; filename = Mem_TempMalloc( strlen( EVDEV_DIR ) + strlen( de->d_name ) + 1 ); strcpy( filename, EVDEV_DIR ); strcat( filename, de->d_name ); // open the file and see if we have a mouse fd = open( filename, O_RDONLY ); Mem_TempFree( filename ); if( fd != -1 ) { if( evdev_ismouse( fd ) ) { close( fd ); return 1; } close( fd ); } return 0; }
/* * CL_ReadServerCache */ void CL_ReadServerCache( void ) { int filelen, filehandle; qbyte *buf = NULL; char *ptr, *token; netadr_t adr; char adrString[64]; qboolean favorite = qfalse; filelen = FS_FOpenFile( SERVERSFILE, &filehandle, FS_READ ); if( !filehandle || filelen < 1 ) { FS_FCloseFile( filehandle ); } else { buf = Mem_TempMalloc( filelen + 1 ); filelen = FS_Read( buf, filelen, filehandle ); FS_FCloseFile( filehandle ); } if( !buf ) return; ptr = ( char * )buf; while( ptr ) { token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) break; if( !Q_stricmp( token, "master" ) ) { favorite = qfalse; continue; } if( !Q_stricmp( token, "favorites" ) ) { favorite = qtrue; continue; } if( NET_StringToAddress( token, &adr ) ) { Q_strncpyz( adrString, token, sizeof( adrString ) ); token = COM_ParseExt( &ptr, qfalse ); if( !token[0] ) continue; if( favorite ) CL_AddServerToList( &favoritesList, adrString, (unsigned int)atoi( token ) ); else CL_AddServerToList( &masterList, adrString, (unsigned int)atoi( token ) ); } } Mem_TempFree( buf ); }
/* * TV_GenericConnect_f */ static void TV_GenericConnect_f( socket_type_t socket ) { netadr_t serveraddress; char *servername, *password, *name; upstream_t *upstream; unsigned int delay; if( Cmd_Argc() < 2 ) { Com_Printf( "Usage: %s <server> [password] [name] [delay]\n", Cmd_Argv( 0 ) ); return; } if( !NET_StringToAddress( Cmd_Argv( 1 ), &serveraddress ) ) { Com_Printf( "Bad server address: %s\n", Cmd_Argv( 1 ) ); return; } servername = TempCopyString( Cmd_Argv( 1 ) ); password = ( Cmd_Argc() >= 3 ? TempCopyString( Cmd_Argv( 2 ) ) : NULL ); name = ( Cmd_Argc() >= 4 ? Cmd_Argv( 3 ) : "" ); delay = ( Cmd_Argc() >= 5 ? (unsigned)atoi( Cmd_Argv( 4 ) )*1000 : RELAY_GLOBAL_DELAY ); if( TV_UpstreamForText( servername, &upstream ) ) { if( upstream->state >= CA_CONNECTED && upstream->relay.delay == delay && upstream->socket && upstream->socket->type == socket ) { Com_Printf( "Already connected to %s\n", servername ); goto exit; } TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" ); upstream = NULL; } upstream = TV_Upstream_New( servername, name, delay ); assert( upstream ); TV_Upstream_Connect( upstream, servername, password, socket, &serveraddress ); exit: Mem_TempFree( servername ); if( password ) Mem_TempFree( password ); }
/* * TV_Upstream_StartDemo */ void TV_Upstream_StartDemo( upstream_t *upstream, const char *demoname, qboolean randomize ) { char *name, *filepath; int tempdemofilehandle, tempdemofilelen; name = filepath = NULL; tempdemofilehandle = 0; tempdemofilelen = -1; TV_Upstream_NextDemo( demoname, upstream->demo.filename, randomize, &name, &filepath ); if( filepath ) { if( COM_ValidateRelativeFilename( filepath ) ) tempdemofilelen = FS_FOpenFile( filepath, &tempdemofilehandle, FS_READ|SNAP_DEMO_GZ ); } TV_Upstream_StopDemo( upstream ); if( name ) Com_Printf( "Starting demo from %s\n", filepath ); upstream->demo.playing = qtrue; upstream->demo.filename = name ? TV_Upstream_CopyString( upstream, name ) : NULL; upstream->demo.filehandle = tempdemofilehandle; upstream->demo.filelen = tempdemofilelen; upstream->demo.random = randomize; upstream->state = CA_HANDSHAKE; upstream->reliable = qfalse; upstream->servername = TV_Upstream_CopyString( upstream, demoname ); // can be demo filename/pattern or demolist filename upstream->rejected = qfalse; upstream->lastPacketReceivedTime = tvs.realtime; // reset the timeout limit upstream->multiview = qfalse; upstream->precacheDone = qfalse; if( name ) Mem_TempFree( name ); if( filepath ) Mem_TempFree( filepath ); }
/* * CL_SoundModule_StartLocalSound */ void CL_SoundModule_StartLocalSound( const char *name ) { assert( name ); if( se ) { char *finalname; finalname = CL_SetSoundExtension( name ); se->StartLocalSound( finalname ); Mem_TempFree( finalname ); } }
/* * CL_SoundModule_Load * * Helper function to try loading sound module with certain name */ static bool CL_SoundModule_Load( const char *name, sound_import_t *import, bool verbose ) { int apiversion; size_t file_size; char *file; void *( *GetSoundAPI )(void *); dllfunc_t funcs[2]; if( verbose ) Com_Printf( "Loading sound module: %s\n", name ); file_size = strlen( LIB_DIRECTORY "/" LIB_PREFIX "snd_" ) + strlen( name ) + 1 + strlen( ARCH ) + strlen( LIB_SUFFIX ) + 1; file = Mem_TempMalloc( file_size ); Q_snprintfz( file, file_size, LIB_DIRECTORY "/" LIB_PREFIX "snd_%s_" ARCH LIB_SUFFIX, name ); funcs[0].name = "GetSoundAPI"; funcs[0].funcPointer = ( void ** )&GetSoundAPI; funcs[1].name = NULL; sound_library = Com_LoadLibrary( file, funcs ); Mem_TempFree( file ); if( !sound_library ) { Com_Printf( "Loading %s failed\n", name ); return false; } s_loaded = true; se = ( sound_export_t * )GetSoundAPI( import ); apiversion = se->API(); if( apiversion != SOUND_API_VERSION ) { CL_SoundModule_Shutdown( verbose ); Com_Printf( "Wrong module version for %s: %i, not %i\n", name, apiversion, SOUND_API_VERSION ); return false; } if( !se->Init( VID_GetWindowHandle(), MAX_EDICTS, verbose ) ) { CL_SoundModule_Shutdown( verbose ); Com_Printf( "Initialization of %s failed\n", name ); return false; } if( verbose ) Com_Printf( "Initialization of %s succesful\n", name ); return true; }
/* * SV_ReloadPureList */ static void SV_ReloadPureList( void ) { char **paks; int i, numpaks; Com_FreePureList( &svs.purelist ); // game modules if( sv_pure_forcemodulepk3->string[0] ) { if( Q_strnicmp( COM_FileBase( sv_pure_forcemodulepk3->string ), "modules", strlen( "modules" ) ) || !FS_IsPakValid( sv_pure_forcemodulepk3->string, NULL ) ) { Com_Printf( "Warning: Invalid value for sv_pure_forcemodulepk3, disabling\n" ); Cvar_ForceSet( "sv_pure_forcemodulepk3", "" ); } else { SV_AddPurePak( sv_pure_forcemodulepk3->string ); } } if( !sv_pure_forcemodulepk3->string[0] ) { char *libname; int libname_size; libname_size = strlen( LIB_PREFIX ) + 5 + strlen( ARCH ) + strlen( LIB_SUFFIX ) + 1; libname = Mem_TempMalloc( libname_size ); Q_snprintfz( libname, libname_size, LIB_PREFIX "game_" ARCH LIB_SUFFIX ); if( !FS_PakNameForFile( libname ) ) { if( sv_pure->integer ) { Com_Printf( "Warning: Game module not in pk3, disabling pure mode\n" ); Com_Printf( "sv_pure_forcemodulepk3 can be used to force the pure system to use a different module\n" ); Cvar_ForceSet( "sv_pure", "0" ); } } else { SV_AddPureFile( libname ); } Mem_TempFree( libname ); libname = NULL; } // *pure.(pk3|pak) paks = NULL; numpaks = FS_GetExplicitPurePakList( &paks ); if( numpaks ) { for( i = 0; i < numpaks; i++ ) { SV_AddPurePak( paks[i] ); Mem_ZoneFree( paks[i] ); } Mem_ZoneFree( paks ); } }
/* * CL_SoundModule_StartBackgroundTrack */ void CL_SoundModule_StartBackgroundTrack( const char *intro, const char *loop ) { assert( intro ); if( se ) { char *finalintro, *finalloop; finalintro = CL_SetSoundExtension( intro ); if( loop ) { finalloop = CL_SetSoundExtension( loop ); } else { finalloop = NULL; } se->StartBackgroundTrack( finalintro, finalloop ); Mem_TempFree( finalintro ); if( finalloop ) Mem_TempFree( finalloop ); } }
/* * R_Bloom_InitBackUpTexture */ static void R_Bloom_InitBackUpTexture( int width, int height ) { qbyte *data; data = Mem_TempMalloc( width * height * 4 ); memset( data, 0, width * height * 4 ); r_screenbackuptexture_width = width; r_screenbackuptexture_height = height; r_bloombackuptexture = R_LoadPic( "***r_bloombackuptexture***", &data, width, height, IT_CLAMP|IT_NOMIPMAP|IT_NOCOMPRESS|IT_NOPICMIP, 3 ); Mem_TempFree( data ); }
/* * Sys_FS_FindNext */ const char *Sys_FS_FindNext( unsigned musthave, unsigned canhave ) { struct dirent64 *d; assert( fdir ); assert( findbase && findpattern ); if( !fdir ) return NULL; while( ( d = readdir64( fdir ) ) != NULL ) { if( !CompareAttributes( d, findbase, musthave, canhave ) ) continue; if( fdots > 0 ) { // . and .. never match const char *base = COM_FileBase( d->d_name ); if( !strcmp( base, "." ) || !strcmp( base, ".." ) ) { fdots--; continue; } } if( !*findpattern || Com_GlobMatch( findpattern, d->d_name, 0 ) ) { const char *dname = d->d_name; size_t dname_len = strlen( dname ); size_t size = sizeof( char ) * ( findbase_size + dname_len + 1 + 1 ); if( findpath_size < size ) { if( findpath ) Mem_TempFree( findpath ); findpath_size = size * 2; // extra size to reduce reallocs findpath = Mem_TempMalloc( findpath_size ); } Q_snprintfz( findpath, findpath_size, "%s/%s%s", findbase, dname, dname[dname_len-1] != '/' && FS_DirentIsDir( d, findbase ) ? "/" : "" ); if( CompareAttributesForPath( d, findpath, musthave, canhave ) ) return findpath; } } return NULL; }
static int wswcurl_debug_callback( CURL *curl, curl_infotype infotype, char *buf, size_t buf_size, void *userp ) { char *temp; if( infotype != CURLINFO_TEXT ) { return 0; } temp = Mem_TempMalloc( buf_size + 1 ); memcpy( temp, buf, buf_size ); Com_Printf( "%s\n", temp ); Mem_TempFree( temp ); return 0; }
int evdev_scandevices( void ) { struct dirent **de; // list of pointers char *filename; char deviceName[256]; int n; if( m_evdev_fds ) { free( m_evdev_fds ); m_evdev_fds = 0; } m_evdev_num = 0; n = scandir( EVDEV_DIR, &de, evdev_filter, NULL ); if( n > 0 ) { m_evdev_fds = calloc( n, sizeof( *m_evdev_fds ) ); m_evdev_num = n; while( n-- ) { filename = Mem_TempMalloc( strlen( EVDEV_DIR ) + strlen( de[n]->d_name ) + 1 ); strcpy( filename, EVDEV_DIR ); strcat( filename, de[n]->d_name ); m_evdev_fds[n] = open( filename, O_RDONLY | O_NONBLOCK ); // some nice information about the device if( ioctl( m_evdev_fds[n], EVIOCGNAME(sizeof(deviceName)-1), deviceName) < 0 ) deviceName[0] = '\0'; Com_Printf( "Evdev: Found %s (%s)\n", deviceName, filename ); Mem_TempFree( filename ); free( de[n] ); } free( de ); } return m_evdev_num; }
/* * CL_SoundModule_RegisterSound */ struct sfx_s *CL_SoundModule_RegisterSound( const char *name ) { assert( name ); if( se ) { struct sfx_s *retval; char *finalname; finalname = CL_SetSoundExtension( name ); retval = se->RegisterSound( finalname ); Mem_TempFree( finalname ); return retval; } else { return NULL; } }
/* * TV_Demo_f */ static void TV_Demo_f( void ) { char *servername, *name, *mode; upstream_t *upstream; unsigned int delay; qboolean randomize = qtrue; if( Cmd_Argc() < 2 ) { Com_Printf( "Usage: %s <pattern|playlist> [name] [ordered|random] [delay]\n", Cmd_Argv( 0 ) ); return; } servername = TempCopyString( Cmd_Argv( 1 ) ); name = ( Cmd_Argc() >= 3 ? Cmd_Argv( 2 ) : "" ); mode = ( Cmd_Argc() >= 4 ? Cmd_Argv( 3 ) : "" ); delay = ( Cmd_Argc() >= 5 ? (unsigned)atoi( Cmd_Argv( 4 ) )*1000 : RELAY_MIN_DELAY ); if( !Q_stricmp( mode, "ordered" ) ) randomize = qfalse; if( TV_UpstreamForText( servername, &upstream ) ) { if( upstream->state >= CA_CONNECTED && upstream->relay.delay == delay ) { Com_Printf( "Already connected to %s\n", servername ); goto exit; } TV_Upstream_Shutdown( upstream, "Disconnected by adminstrator" ); upstream = NULL; } upstream = TV_Upstream_New( servername, name, delay ); assert( upstream ); TV_Upstream_StartDemo( upstream, servername, randomize ); exit: Mem_TempFree( servername ); }
/* * TV_Upstream_IsAutoRecordable */ qboolean TV_Upstream_IsAutoRecordable( upstream_t *upstream ) { char *s, *t; static const char *seps = ";"; qboolean match = qfalse; upstream_t *tupstream; assert( upstream ); if( upstream->demo.playing ) return qfalse; if( !Q_stricmp( tv_autorecord->string, "*" ) ) return qtrue; // search for the given upstream in record list (semicolon separated) s = TempCopyString( tv_autorecord->string ); t = strtok( s, seps ); while( t != NULL ) { qboolean res = TV_UpstreamForText( t, &tupstream ); if( res && (tupstream == upstream) ) { match = qtrue; break; // found a match } t = strtok( NULL, seps ); } Mem_TempFree( s ); if( match ) return qtrue; return qfalse; }
/* * CM_LoadMap * Loads in the map and all submodels * * for spawning a server with no map at all, call like this: * CM_LoadMap( "", qfalse, &checksum ); // no real map */ cmodel_t *CM_LoadMap( cmodel_state_t *cms, const char *name, qboolean clientload, unsigned *checksum ) { int length; unsigned *buf; char *header; const modelFormatDescr_t *descr; bspFormatDesc_t *bspFormat = NULL; assert( cms ); assert( name && strlen( name ) < MAX_CONFIGSTRING_CHARS ); assert( checksum ); cms->map_clientload = clientload; if( !strcmp( cms->map_name, name ) && ( clientload || !Cvar_Value( "flushmap" ) ) ) { *checksum = cms->checksum; if( !clientload ) { memset( cms->map_areaportals, 0, cms->numareas * cms->numareas * sizeof( *cms->map_areaportals ) ); CM_FloodAreaConnections( cms ); } return cms->map_cmodels; // still have the right version } CM_Clear( cms ); if( !name || !name[0] ) { cms->numleafs = 1; cms->numcmodels = 2; *checksum = 0; return cms->map_cmodels; // cinematic servers won't have anything at all } // // load the file // length = FS_LoadFile( name, ( void ** )&buf, NULL, 0 ); if( !buf ) Com_Error( ERR_DROP, "Couldn't load %s", name ); cms->checksum = Com_MD5Digest32( ( const qbyte * )buf, length ); *checksum = cms->checksum; // call the apropriate loader descr = Q_FindFormatDescriptor( cm_supportedformats, ( const qbyte * )buf, (const bspFormatDesc_t **)&bspFormat ); if( !descr ) Com_Error( ERR_DROP, "CM_LoadMap: unknown fileid for %s", name ); if( !bspFormat ) Com_Error( ERR_DROP, "CM_LoadMap: %s: unknown bsp format" ); // copy header into temp variable to be saveed in a cvar header = Mem_TempMalloc( descr->headerLen + 1 ); memcpy( header, buf, descr->headerLen ); header[descr->headerLen] = '\0'; // store map format description in cvars Cvar_ForceSet( "cm_mapHeader", header ); Cvar_ForceSet( "cm_mapVersion", va( "%i", LittleLong( *((int *)((qbyte *)buf + descr->headerLen)) ) ) ); Mem_TempFree( header ); descr->loader( cms, NULL, buf, bspFormat ); CM_InitBoxHull( cms ); CM_InitOctagonHull( cms ); if( cms->numareas ) { cms->map_areas = Mem_Alloc( cms->mempool, cms->numareas * sizeof( *cms->map_areas ) ); cms->map_areaportals = Mem_Alloc( cms->mempool, cms->numareas * cms->numareas * sizeof( *cms->map_areaportals ) ); memset( cms->map_areaportals, 0, cms->numareas * cms->numareas * sizeof( *cms->map_areaportals ) ); CM_FloodAreaConnections( cms ); } memset( cms->nullrow, 255, MAX_CM_LEAFS / 8 ); Q_strncpyz( cms->map_name, name, sizeof( cms->map_name ) ); return cms->map_cmodels; }
/* * ML_InitFromCache * Fills map list array from cache, much faster */ static void ML_InitFromCache( void ) { int count, i, total, len; size_t size = 0; char *buffer, *chr, *current, *curend; char *temp, *maps, *map; mapdir_t *dir, *curmap, *prev; if( ml_initialized ) return; total = FS_GetFileListExt( "maps", ".bsp", NULL, &size, 0, 0 ); if( !total ) return; // load maps from directory reading into a list maps = temp = ( char* )Mem_TempMalloc( size + sizeof( mapdir_t ) * total ); temp += size; FS_GetFileList( "maps", ".bsp", maps, size, 0, 0 ); len = 0; prev = NULL; dir = NULL; for( i = 0; i < total; i++ ) { map = maps + len; len += strlen( map ) + 1; curmap = ( mapdir_t * )temp; temp += sizeof( mapdir_t ); COM_StripExtension( map ); if( !i ) dir = curmap; else { prev->next = curmap; curmap->prev = prev; } curmap->filename = map; prev = curmap; } FS_LoadCacheFile( MLIST_CACHE, (void **)&buffer, NULL, 0 ); if( !buffer ) { Mem_TempFree( maps ); return; } current = curend = buffer; count = 0; for( chr = buffer; *chr; chr++ ) { // current character is a delimiter if( *chr == '\n' ) { if( *(chr-1) == '\r' ) *(chr-1) = '\0'; // clear the CR too *chr = '\0'; // clear the LF // if we have got both params if( !( ++count & 1 ) ) { // check if its in the maps directory for( curmap = dir; curmap; curmap = curmap->next ) { if( !Q_stricmp( curmap->filename, current ) ) { if( curmap->prev ) curmap->prev->next = curmap->next; else dir = curmap->next; if( curmap->next ) curmap->next->prev = curmap->prev; break; } } // if we found it in the maps directory if( curmap ) { COM_SanitizeFilePath( current ); // well, if we've got a map with an unknown fullname, load it from map if( !strcmp( curend + 1, MLIST_UNKNOWN_MAPNAME ) ) ML_AddMap( current, NULL ); else ML_AddMap( current, curend + 1 ); } current = chr + 1; } else curend = chr; } } // we've now loaded the mapcache, but there may be files which // have been added to maps directory, and the mapcache isnt aware // these will be left over in our directory list for( curmap = dir; curmap; curmap = curmap->next ) ML_AddMap( curmap->filename, NULL ); Mem_TempFree( maps ); FS_FreeFile( buffer ); }
/* * TV_Upstream_NextDemo */ void TV_Upstream_NextDemo( const char *demoname, const char *curdemo, qboolean randomize, char **name, char **filepath ) { int i, j, total; size_t bufsize, len, dir_size; char *file, *buf, **match, *dir; const char *extension, *pattern, *p; *name = *filepath = NULL; assert( demoname ); assert( *demoname ); buf = NULL; bufsize = 0; total = 0; // check if user specified a demo pattern (e.g. "tutorials/*.wd10") or a demolist filename extension = COM_FileExtension( demoname ); if( extension && !Q_stricmp( extension, APP_DEMO_EXTENSION_STR ) ) pattern = demoname; else pattern = ""; dir_size = strlen( "demos" ) + strlen( pattern ) + 1; dir = Mem_TempMalloc( dir_size ); strcpy( dir, "demos" ); if( *pattern ) { // find first character that looks like a wildcard const char *last_slash = NULL; p = pattern; do { if( *p == '/' ) last_slash = p; else if( *p == '?' || *p == '*' || *p == '[' ) break; } while( *++p ); // append the path part of wildcard to dir and shift the pattern if( last_slash ) { Q_strncatz( dir, "/", dir_size ); Q_strncatz( dir, pattern, strlen( dir ) + (last_slash - pattern) + 1 ); pattern = last_slash + 1; } bufsize = 0; total = FS_GetFileListExt( dir, APP_DEMO_EXTENSION_STR, NULL, &bufsize, 0, 0 ); if( !total ) bufsize = 0; if( bufsize ) { buf = Mem_TempMalloc( bufsize ); FS_GetFileList( dir, APP_DEMO_EXTENSION_STR, buf, bufsize, 0, 0 ); } } else { // load demolist file and pick next available demo int filehandle = 0, filelen = -1; // load list from file filelen = FS_FOpenFile( demoname, &filehandle, FS_READ ); if( filehandle && filelen > 0 ) { bufsize = (size_t)(filelen + 1); buf = Mem_TempMalloc( bufsize ); FS_Read( buf, filelen, filehandle ); FS_FCloseFile( filehandle ); } // parse the list stripping CRLF characters if( buf ) { p = strtok( buf, "\r\n" ); if( p ) { char *newbuf; size_t newbufsize; newbufsize = 0; newbuf = Mem_TempMalloc( bufsize ); while( p != NULL ) { total++; Q_strncpyz( newbuf + newbufsize, p, bufsize - newbufsize ); newbufsize += strlen( p ) + 1; p = strtok( NULL, "\r\n" ); } Mem_TempFree( buf ); buf = newbuf; } } } if( buf ) { int next; // get the list of demo files match = Mem_TempMalloc( total * sizeof( char * ) ); total = 0; next = (randomize ? -1 : 0); for( len = 0; buf[len]; ) { file = buf + len; len += strlen( file ) + 1; if( *pattern ) { if( !Com_GlobMatch( pattern, file, qfalse ) ) continue; } // avoid replays if( curdemo && !Q_stricmp( file, curdemo ) ) { // if ordered, schedule the next map if( !randomize ) next = total; continue; } match[total++] = file; } // pick a new random demo if possible or otherwise try the old one if( total ) { if( next < 0 ) next = rand() % total; // walk the list until we find an existing file // in case of pattern match, the check is not necessary though for( i = 0; i < total; i++ ) { j = (i + next) % total; if( !*pattern ) { extension = COM_FileExtension( match[j] ); if( FS_FOpenFile( va( "demos/%s%s", match[j], (extension ? "" : APP_DEMO_EXTENSION_STR) ), NULL, FS_READ ) == -1 ) continue; } *name = TempCopyString( match[j] ); break; } } // fallback to current demo if( !*name && curdemo ) *name = TempCopyString( curdemo ); Mem_TempFree( match ); // append the dir to filename, sigh.. if( *name ) { size_t filepath_size; filepath_size = strlen( dir ) + strlen( "/" ) + strlen( *name ) + strlen( APP_DEMO_EXTENSION_STR ) + 1; *filepath = Mem_TempMalloc( filepath_size ); strcpy( *filepath, dir ); strcat( *filepath, "/" ); strcat( *filepath, *name ); COM_DefaultExtension( *filepath, APP_DEMO_EXTENSION_STR, filepath_size ); } Mem_TempFree( buf ); } Mem_TempFree( dir ); }
/* * Com_Printf * * Both client and server can use this, and it will output * to the apropriate place. */ void Com_Printf( const char *format, ... ) { va_list argptr; char msg[MAX_PRINTMSG]; time_t timestamp; char timestamp_str[MAX_PRINTMSG]; struct tm *timestampptr; timestamp = time( NULL ); timestampptr = gmtime( ×tamp ); strftime( timestamp_str, MAX_PRINTMSG, "%Y-%m-%dT%H:%M:%SZ ", timestampptr ); va_start( argptr, format ); Q_vsnprintfz( msg, sizeof( msg ), format, argptr ); va_end( argptr ); if( rd_target ) { if( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) { rd_flush( rd_target, rd_buffer, rd_extra ); *rd_buffer = 0; } strcat( rd_buffer, msg ); return; } QMutex_Lock( com_print_mutex ); Con_Print( msg ); // also echo to debugging console Sys_ConsoleOutput( msg ); // logconsole if( logconsole && logconsole->modified ) { logconsole->modified = qfalse; if( log_file ) { FS_FCloseFile( log_file ); log_file = 0; } if( logconsole->string && logconsole->string[0] ) { size_t name_size; char *name; name_size = strlen( logconsole->string ) + strlen( ".log" ) + 1; name = ( char* )Mem_TempMalloc( name_size ); Q_strncpyz( name, logconsole->string, name_size ); COM_DefaultExtension( name, ".log", name_size ); if( FS_FOpenFile( name, &log_file, ( logconsole_append && logconsole_append->integer ? FS_APPEND : FS_WRITE ) ) == -1 ) { log_file = 0; Com_Printf( "Couldn't open: %s\n", name ); } Mem_TempFree( name ); } } if( log_file ) { if( logconsole_timestamp && logconsole_timestamp->integer ) FS_Printf( log_file, "%s", timestamp_str ); FS_Printf( log_file, "%s", msg ); if( logconsole_flush && logconsole_flush->integer ) FS_Flush( log_file ); // force it to save every time } QMutex_Unlock( com_print_mutex ); }
/* * SV_BeginDownload_f * Responds to reliable download packet with reliable initdownload packet */ static void SV_BeginDownload_f( client_t *client ) { const char *requestname; const char *uploadname; size_t alloc_size; unsigned checksum; char *url; const char *errormsg = NULL; qboolean allow, requestpak; qboolean local_http = SV_Web_Running() && sv_uploads_http->integer != 0; requestpak = ( atoi( Cmd_Argv( 1 ) ) == 1 ); requestname = Cmd_Argv( 2 ); if( !requestname[0] || !COM_ValidateRelativeFilename( requestname ) ) { SV_DenyDownload( client, "Invalid filename" ); return; } if( !SV_FilenameForDownloadRequest( requestname, requestpak, &uploadname, &errormsg ) ) { assert( errormsg != NULL ); SV_DenyDownload( client, errormsg ); return; } if( FS_CheckPakExtension( uploadname ) ) { allow = qfalse; // allow downloading paks from the pure list, if not spawned if( client->state < CS_SPAWNED ) { purelist_t *purefile; purefile = svs.purelist; while( purefile ) { if( !strcmp( uploadname, purefile->filename ) ) { allow = qtrue; break; } purefile = purefile->next; } } // game module has a change to allow extra downloads if( !allow && !SV_GameAllowDownload( client, requestname, uploadname ) ) { SV_DenyDownload( client, "Downloading of this file is not allowed" ); return; } } else { if( !SV_GameAllowDownload( client, requestname, uploadname ) ) { SV_DenyDownload( client, "Downloading of this file is not allowed" ); return; } } // we will just overwrite old download, if any if( client->download.name ) { if( client->download.data ) { FS_FreeBaseFile( client->download.data ); client->download.data = NULL; } Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; } client->download.size = FS_LoadBaseFile( uploadname, NULL, NULL, 0 ); if( client->download.size == -1 ) { Com_Printf( "Error getting size of %s for uploading\n", uploadname ); client->download.size = 0; SV_DenyDownload( client, "Error getting file size" ); return; } checksum = FS_ChecksumBaseFile( uploadname ); client->download.timeout = svs.realtime + 1000 * 60 * 60; // this is web download timeout alloc_size = sizeof( char ) * ( strlen( uploadname ) + 1 ); client->download.name = Mem_ZoneMalloc( alloc_size ); Q_strncpyz( client->download.name, uploadname, alloc_size ); Com_Printf( "Offering %s to %s\n", client->download.name, client->name ); if( FS_CheckPakExtension( uploadname ) && ( local_http || sv_uploads_baseurl->string[0] != 0 ) ) { // .pk3 and .pak download from the web if( local_http ) { url = TempCopyString( va( "files/%s", uploadname ) ); } else { alloc_size = sizeof( char ) * ( strlen( sv_uploads_baseurl->string ) + 1 ); url = Mem_TempMalloc( alloc_size ); Q_snprintfz( url, alloc_size, "%s/", sv_uploads_baseurl->string ); } } else if( SV_IsDemoDownloadRequest( requestname ) && ( local_http || sv_uploads_demos_baseurl->string[0] != 0 ) ) { // demo file download from the web if( local_http ) { url = TempCopyString( va( "files/%s", uploadname ) ); } else { alloc_size = sizeof( char ) * ( strlen( sv_uploads_demos_baseurl->string ) + 1 ); url = Mem_TempMalloc( alloc_size ); Q_snprintfz( url, alloc_size, "%s/", sv_uploads_demos_baseurl->string ); } } else { url = NULL; } // start the download SV_InitClientMessage( client, &tmpMessage, NULL, 0 ); SV_SendServerCommand( client, "initdownload \"%s\" %i %u %i \"%s\"", client->download.name, client->download.size, checksum, local_http ? 1 : 0, ( url ? url : "" ) ); SV_AddReliableCommandsToMessage( client, &tmpMessage ); SV_SendMessageToClient( client, &tmpMessage ); if( url ) { Mem_TempFree( url ); url = NULL; } }
/* * SV_WebDownload */ static qboolean SV_WebDownload( const char *baseUrl, const char *filepath, qboolean overwrite, qboolean silent ) { qboolean success; int alloc_size; char *temppath, *writepath, *url; if( developer->integer ) silent = qfalse; if( !baseUrl || !baseUrl[0] || !filepath ) return qfalse; if( !strrchr( baseUrl, '/' ) ) { if( !silent ) Com_Printf( "SV_WebDownload: Invalid URL\n" ); return qfalse; } if( filepath[0] == '/' ) // filepath should never begin with a slash filepath++; if( !COM_ValidateRelativeFilename( filepath ) ) { if( !silent ) Com_Printf( "SV_WebDownload: Invalid filename\n" ); return qfalse; } if( !COM_FileExtension( filepath ) ) { if( !silent ) Com_Printf( "SV_WebDownload: no file extension\n" ); return qfalse; } // full url (baseurl + path) alloc_size = strlen( baseUrl ) + 1 + strlen( filepath ) + 1; url = Mem_TempMalloc( alloc_size ); if( baseUrl[ strlen( baseUrl ) - 1 ] == '/' ) // url includes last slash Q_snprintfz( url, alloc_size, "%s%s", baseUrl, filepath ); else Q_snprintfz( url, alloc_size, "%s/%s", baseUrl, filepath ); // add .tmp (relative + .tmp) alloc_size = strlen( filepath ) + strlen( ".tmp" ) + 1; temppath = Mem_TempMalloc( alloc_size ); Q_snprintfz( temppath, alloc_size, "%s.tmp", filepath ); // full write path for curl alloc_size = strlen( FS_WriteDirectory() ) + 1 + strlen( temppath ) + 1; writepath = Mem_TempMalloc( alloc_size ); Q_snprintfz( writepath, alloc_size, "%s/%s", FS_WriteDirectory(), temppath ); webDownloadPercentPrint = 0; webDownloadPercentStarted = qfalse; success = Web_Get( url, NULL, writepath, qtrue, 60 * 30, 60, SV_WebDownloadProgress, qfalse ); if( webDownloadPercentStarted ) Com_Printf( "\n" ); if( !success ) { if( !silent ) Com_Printf( "Failed to download remote file.\n" ); goto failed; } // rename the downloaded file if( !FS_MoveBaseFile( temppath, filepath ) ) { if( !overwrite ) { if( !silent ) Com_Printf( "Failed to rename temporary file.\n" ); goto failed; } // check if it failed because there already exists a file with the same name // and in this case remove this file if( FS_FOpenBaseFile( filepath, NULL, FS_READ ) != -1 ) { char *backfile; alloc_size = strlen( filepath ) + strlen( ".bak" ) + 1; backfile = Mem_TempMalloc( alloc_size ); Q_snprintfz( backfile, alloc_size, "%s.bak", filepath ); // if there is already a .bak file, destroy it if( FS_FOpenBaseFile( backfile, NULL, FS_READ ) != -1 ) FS_RemoveBaseFile( backfile ); // move the current file into .bak file if( !FS_MoveBaseFile( filepath, backfile ) ) { Mem_TempFree( backfile ); if( !silent ) Com_Printf( "Failed to backup destination file.\n" ); goto failed; } // now try renaming the downloaded file again if( !FS_MoveBaseFile( temppath, filepath ) ) { // didn't work, so restore the backup file if( FS_MoveBaseFile( backfile, filepath ) ) { if( !silent ) Com_Printf( "Failed to rename temporary file, restoring from backup.\n" ); } else { if( !silent ) Com_Printf( "Failed to rename temporary file and restore from backup.\n" ); } Mem_TempFree( backfile ); goto failed; } Mem_TempFree( backfile ); } } Mem_TempFree( temppath ); Mem_TempFree( writepath ); Mem_TempFree( url ); return qtrue; failed: if( !silent ) Com_Printf( "Removing temporary file: %s\n", writepath ); FS_RemoveAbsoluteFile( writepath ); Mem_TempFree( temppath ); Mem_TempFree( writepath ); Mem_TempFree( url ); return qfalse; }
/* * SV_AutoUpdateFromWeb */ void SV_AutoUpdateFromWeb( qboolean checkOnly ) { static const char *autoUpdateBaseUrl = APP_UPDATE_URL APP_SERVER_UPDATE_DIRECTORY; char checksumString1[32], checksumString2[32]; unsigned int checksum; qboolean success; int length, filenum; qbyte *data; const char *token, *ptr; char path[MAX_QPATH]; int downloadCount = 0, downloadFailed = 0; char newVersionTag[MAX_QPATH]; qboolean newVersion = qfalse; if( !dedicated->integer ) return; assert( svs.mapcmd[0] ); if( !checkOnly ) SV_UpdateActivity(); Com_Printf( "\n" ); Com_Printf( "========== Starting Auto Update ===========\n" ); Com_Printf( "Checking for updates\n" ); // download the update file list success = SV_WebDownload( autoUpdateBaseUrl, APP_SERVER_UPDATE_FILE, qtrue, qtrue ); // set as last updated today if( !checkOnly ) Cvar_ForceSet( "sv_lastAutoUpdate", va( "%i", (int)Com_DaysSince1900() ) ); if( !success ) // no update to do goto done; // read the file list if( ( length = FS_FOpenBaseFile( APP_SERVER_UPDATE_FILE, &filenum, FS_READ ) ) == -1 ) { Com_Printf( "WARNING: Couldn't find %s\n", path ); goto done; } if( !length ) { FS_FCloseFile( filenum ); goto done; } data = Mem_TempMalloc( length + 1 ); FS_Read( data, length, filenum ); FS_FCloseFile( filenum ); FS_RemoveBaseFile( APP_SERVER_UPDATE_FILE ); ptr = (const char *)data; // first token is always the current release version token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // compare versions Q_strncpyz( newVersionTag, token, sizeof( newVersionTag ) ); if( atof( newVersionTag ) > atof( va( "%4.3f", APP_VERSION ) ) ) newVersion = qtrue; while( ptr ) { // we got what should be a checksum token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // copy checksum reported by server Q_strncpyz( checksumString1, token, sizeof( checksumString1 ) ); // get filename token = COM_ParseExt( &ptr, qtrue ); if( !token[0] ) goto cancel; // filename should never begin with a slash if( token[0] == '/' ) token++; Q_strncpyz( path, token, sizeof( path ) ); // we got what should be a file path if( !COM_ValidateRelativeFilename( path ) ) { Com_Printf( "WARNING: Invalid filename %s\n", path ); continue; } checksum = FS_ChecksumBaseFile( path ); Q_snprintfz( checksumString2, sizeof( checksumString2 ), "%u", checksum ); // if same checksum no need to update if( !strcmp( checksumString1, checksumString2 ) ) continue; // if it's a pack file and the file exists it can't be replaced, so skip if( FS_CheckPakExtension( path ) && checksum ) { Com_Printf( "WARNING: Purity check failed for: %s\n", path ); Com_Printf( "WARNING: This file has been locally modified. It is highly \n" ); Com_Printf( "WARNING: recommended to restore the original file.\n" ); Com_Printf( "WARNING: Reinstalling \""APPLICATION"\" might be convenient.\n" ); continue; } if( checkOnly ) { Com_Printf( "File update available : %s\n", path ); continue; } if( developer->integer ) Com_Printf( "Downloading update of %s (checksum %s local checksum %s)\n", path, checksumString1, checksumString2 ); else Com_Printf( "Updating %s\n", path ); if( !SV_WebDownload( autoUpdateBaseUrl, path, qtrue, qtrue ) ) { Com_Printf( "Failed to update %s\n", path ); downloadFailed++; } downloadCount++; } cancel: Mem_TempFree( data ); done: if( newVersion ) { if( downloadCount ) { if( downloadFailed ) Com_Printf( "This version of "APPLICATION" was updated incompletely\n" ); else Com_Printf( "This version of "APPLICATION" was updated successfully\n\n" ); } Com_Printf( "****** Version %s of "APPLICATION" is available. ******\n", newVersionTag ); Com_Printf( "****** Please download the new version at "APP_URL" ******\n" ); } else if( downloadCount ) { if( downloadFailed ) Com_Printf( APPLICATION" was updated incompletely\n" ); else Com_Printf( APPLICATION" was updated successfully\n" ); } else if( !checkOnly ) { if( downloadFailed ) Com_Printf( "At least one file failed to update\n" ); else Com_Printf( APPLICATION" is up to date\n" ); } Com_Printf( "========== Auto Update Finished ===========\n" ); Com_Printf( "\n" ); // update the map list, which also does a filesystem rescan ML_Update(); // if there are any new filesystem entries, restart if( FS_GetNotifications() & FS_NOTIFT_NEWPAKS ) { if( sv.state != ss_dead ) { // restart the current map, SV_Map also rescans the filesystem Com_Printf( "The server will now restart...\n\n" ); // start the default map if current map isn't available Cbuf_ExecuteText( EXEC_APPEND, va( "map %s\n", svs.mapcmd[0] ? svs.mapcmd : sv_defaultmap->string ) ); } } }
/* * TV_Upstream_StartDemoRecord */ void TV_Upstream_StartDemoRecord( upstream_t *upstream, const char *demoname, qboolean silent ) { char *servername, *temp; size_t name_size; assert( upstream ); assert( demoname ); if( upstream->demo.playing ) { if( !silent ) Com_Printf( "You can't record from another demo.\n" ); return; } if( upstream->demo.recording ) { if( !silent ) Com_Printf( "Already recording.\n" ); return; } // strip the port number from servername servername = TempCopyString( upstream->servername ); temp = strstr( servername, ":" ); if( temp ) *temp = '\0'; // store the name name_size = sizeof( char ) * ( strlen( "demos/tvserver" ) + 1 + strlen( servername ) + 1 + strlen( demoname ) + strlen( APP_DEMO_EXTENSION_STR ) + 1 ); upstream->demo.filename = Mem_ZoneMalloc( name_size ); Q_snprintfz( upstream->demo.filename, name_size, "demos/tvserver/%s/%s", servername, demoname ); COM_SanitizeFilePath( upstream->demo.filename ); COM_DefaultExtension( upstream->demo.filename, APP_DEMO_EXTENSION_STR, name_size ); Mem_TempFree( servername ); if( !COM_ValidateRelativeFilename( upstream->demo.filename ) ) { if( !silent ) Com_Printf( "Invalid filename.\n" ); Mem_ZoneFree( upstream->demo.filename ); return; } // temp name name_size = sizeof( char ) * ( strlen( upstream->demo.filename ) + strlen( ".rec" ) + 1 ); upstream->demo.tempname = Mem_ZoneMalloc( name_size ); Q_snprintfz( upstream->demo.tempname, name_size, "%s.rec", upstream->demo.filename ); // open the demo file if( FS_FOpenFile( upstream->demo.tempname, &upstream->demo.filehandle, FS_WRITE|SNAP_DEMO_GZ ) == -1 ) { Com_Printf( "Error: Couldn't create the demo file.\n" ); Mem_ZoneFree( upstream->demo.tempname ); upstream->demo.tempname = NULL; Mem_ZoneFree( upstream->demo.filename ); upstream->demo.filename = NULL; return; } if( !silent ) Com_Printf( "Recording demo: %s\n", upstream->demo.filename ); upstream->demo.recording = qtrue; upstream->demo.localtime = 0; upstream->demo.basetime = upstream->demo.duration = 0; // don't start saving messages until a non-delta compressed message is received TV_Upstream_AddReliableCommand( upstream, "nodelta" ); // request non delta compressed frame from server upstream->demo.waiting = qtrue; // the rest of the demo file will be individual frames }