diff options
author | Tim Angus <tim@ngus.net> | 2006-01-05 04:00:54 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2006-01-05 04:00:54 +0000 |
commit | 4e6ae21de9bc373801b68a28b66067480bda4288 (patch) | |
tree | 0ffa6b987d2ca06daacc7abffe453a4336b3b6da /src | |
parent | 436df98d2345c4caee3f415e75f009f96ef77697 (diff) |
* Merged ioq3-460
- avi exporter
- cl_autoRecordDemo
- .asm dependencies
Diffstat (limited to 'src')
-rw-r--r-- | src/client/cl_avi.c | 619 | ||||
-rw-r--r-- | src/client/cl_main.c | 143 | ||||
-rw-r--r-- | src/client/client.h | 12 | ||||
-rw-r--r-- | src/client/snd_dma.c | 6 | ||||
-rw-r--r-- | src/client/snd_main.c | 4 | ||||
-rw-r--r-- | src/client/snd_mix.c | 4 | ||||
-rw-r--r-- | src/qcommon/common.c | 310 | ||||
-rw-r--r-- | src/qcommon/files.c | 92 | ||||
-rw-r--r-- | src/qcommon/md4.c | 5 | ||||
-rw-r--r-- | src/qcommon/q_shared.h | 5 | ||||
-rw-r--r-- | src/qcommon/qcommon.h | 4 | ||||
-rw-r--r-- | src/renderer/tr_backend.c | 3 | ||||
-rw-r--r-- | src/renderer/tr_cmds.c | 27 | ||||
-rw-r--r-- | src/renderer/tr_image.c | 58 | ||||
-rw-r--r-- | src/renderer/tr_init.c | 47 | ||||
-rw-r--r-- | src/renderer/tr_local.h | 18 | ||||
-rw-r--r-- | src/renderer/tr_public.h | 3 | ||||
-rw-r--r-- | src/unix/Makefile | 360 | ||||
-rw-r--r-- | src/unix/ftola.s | 2 | ||||
-rw-r--r-- | src/unix/linux_common.c | 347 |
20 files changed, 1033 insertions, 1036 deletions
diff --git a/src/client/cl_avi.c b/src/client/cl_avi.c new file mode 100644 index 00000000..4a372d8b --- /dev/null +++ b/src/client/cl_avi.c @@ -0,0 +1,619 @@ +/* +=========================================================================== +Copyright (C) 2005-2006 Tim Angus + +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 Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "client.h" +#include "snd_local.h" + +#define MAX_RIFF_CHUNKS 16 + +typedef struct audioFormat_s +{ + int rate; + int format; + int channels; + int bits; + + int sampleSize; + int totalBytes; +} audioFormat_t; + +typedef struct aviFileData_s +{ + qboolean fileOpen; + fileHandle_t f; + char fileName[ MAX_QPATH ]; + int fileSize; + int moviOffset; + int moviSize; + + fileHandle_t idxF; + int numIndices; + + int frameRate; + int framePeriod; + int width, height; + int numVideoFrames; + int maxRecordSize; + qboolean motionJpeg; + + qboolean audio; + audioFormat_t a; + int numAudioFrames; + + int chunkStack[ MAX_RIFF_CHUNKS ]; + int chunkStackTop; + + byte *cBuffer, *eBuffer; +} aviFileData_t; + +static aviFileData_t afd; + +#define MAX_AVI_BUFFER 2048 + +static byte buffer[ MAX_AVI_BUFFER ]; +static int bufIndex; + +/* +=============== +SafeFS_Write +=============== +*/ +static ID_INLINE void SafeFS_Write( const void *buffer, int len, fileHandle_t f ) +{ + if( FS_Write( buffer, len, f ) < len ) + Com_Error( ERR_DROP, "Failed to write avi file\n" ); +} + +/* +=============== +WRITE_STRING +=============== +*/ +static ID_INLINE void WRITE_STRING( const char *s ) +{ + Com_Memcpy( &buffer[ bufIndex ], s, strlen( s ) ); + bufIndex += strlen( s ); +} + +/* +=============== +WRITE_4BYTES +=============== +*/ +static ID_INLINE void WRITE_4BYTES( int x ) +{ + buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF ); + buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF ); + buffer[ bufIndex + 2 ] = (byte)( ( x >> 16 ) & 0xFF ); + buffer[ bufIndex + 3 ] = (byte)( ( x >> 24 ) & 0xFF ); + bufIndex += 4; +} + +/* +=============== +WRITE_2BYTES +=============== +*/ +static ID_INLINE void WRITE_2BYTES( int x ) +{ + buffer[ bufIndex + 0 ] = (byte)( ( x >> 0 ) & 0xFF ); + buffer[ bufIndex + 1 ] = (byte)( ( x >> 8 ) & 0xFF ); + bufIndex += 2; +} + +/* +=============== +WRITE_1BYTES +=============== +*/ +static ID_INLINE void WRITE_1BYTES( int x ) +{ + buffer[ bufIndex ] = x; + bufIndex += 1; +} + +/* +=============== +START_CHUNK +=============== +*/ +static ID_INLINE void START_CHUNK( const char *s ) +{ + if( afd.chunkStackTop == MAX_RIFF_CHUNKS ) + { + Com_Error( ERR_DROP, "ERROR: Top of chunkstack breached\n" ); + } + + afd.chunkStack[ afd.chunkStackTop ] = bufIndex; + afd.chunkStackTop++; + WRITE_STRING( s ); + WRITE_4BYTES( 0 ); +} + +/* +=============== +END_CHUNK +=============== +*/ +static ID_INLINE void END_CHUNK( void ) +{ + int endIndex = bufIndex; + + if( afd.chunkStackTop <= 0 ) + { + Com_Error( ERR_DROP, "ERROR: Bottom of chunkstack breached\n" ); + } + + afd.chunkStackTop--; + bufIndex = afd.chunkStack[ afd.chunkStackTop ]; + bufIndex += 4; + WRITE_4BYTES( endIndex - bufIndex - 1 ); + bufIndex = endIndex; + bufIndex = PAD( bufIndex, 2 ); +} + +/* +=============== +CL_WriteAVIHeader +=============== +*/ +void CL_WriteAVIHeader( void ) +{ + bufIndex = 0; + afd.chunkStackTop = 0; + + START_CHUNK( "RIFF" ); + { + WRITE_STRING( "AVI " ); + { + START_CHUNK( "LIST" ); + { + WRITE_STRING( "hdrl" ); + WRITE_STRING( "avih" ); + WRITE_4BYTES( 56 ); //"avih" "chunk" size + WRITE_4BYTES( afd.framePeriod ); //dwMicroSecPerFrame + WRITE_4BYTES( afd.maxRecordSize * + afd.frameRate ); //dwMaxBytesPerSec + WRITE_4BYTES( 0 ); //dwReserved1 + WRITE_4BYTES( 0x110 ); //dwFlags bits HAS_INDEX and IS_INTERLEAVED + WRITE_4BYTES( afd.numVideoFrames ); //dwTotalFrames + WRITE_4BYTES( 0 ); //dwInitialFrame + + if( afd.audio ) //dwStreams + WRITE_4BYTES( 2 ); + else + WRITE_4BYTES( 1 ); + + WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize + WRITE_4BYTES( afd.width ); //dwWidth + WRITE_4BYTES( afd.height ); //dwHeight + WRITE_4BYTES( 0 ); //dwReserved[ 0 ] + WRITE_4BYTES( 0 ); //dwReserved[ 1 ] + WRITE_4BYTES( 0 ); //dwReserved[ 2 ] + WRITE_4BYTES( 0 ); //dwReserved[ 3 ] + + START_CHUNK( "LIST" ); + { + WRITE_STRING( "strl" ); + WRITE_STRING( "strh" ); + WRITE_4BYTES( 56 ); //"strh" "chunk" size + WRITE_STRING( "vids" ); + + if( afd.motionJpeg ) + WRITE_STRING( "MJPG" ); + else + WRITE_STRING( " BGR" ); + + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( 0 ); //dwPriority + WRITE_4BYTES( 0 ); //dwInitialFrame + + WRITE_4BYTES( 1 ); //dwTimescale + WRITE_4BYTES( afd.frameRate ); //dwDataRate + WRITE_4BYTES( 0 ); //dwStartTime + WRITE_4BYTES( afd.numVideoFrames ); //dwDataLength + + WRITE_4BYTES( afd.maxRecordSize ); //dwSuggestedBufferSize + WRITE_4BYTES( -1 ); //dwQuality + WRITE_4BYTES( 0 ); //dwSampleSize + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( afd.width ); //rcFrame + WRITE_2BYTES( afd.height ); //rcFrame + + WRITE_STRING( "strf" ); + WRITE_4BYTES( 40 ); //"strf" "chunk" size + WRITE_4BYTES( 40 ); //biSize + WRITE_4BYTES( afd.width ); //biWidth + WRITE_4BYTES( afd.height ); //biHeight + WRITE_2BYTES( 1 ); //biPlanes + WRITE_2BYTES( 24 ); //biBitCount + + if( afd.motionJpeg ) //biCompression + WRITE_STRING( "MJPG" ); + else + WRITE_STRING( " BGR" ); + + WRITE_4BYTES( afd.width * + afd.height ); //biSizeImage + WRITE_4BYTES( 0 ); //biXPelsPetMeter + WRITE_4BYTES( 0 ); //biYPelsPetMeter + WRITE_4BYTES( 0 ); //biClrUsed + WRITE_4BYTES( 0 ); //biClrImportant + } + END_CHUNK( ); + + if( afd.audio ) + { + START_CHUNK( "LIST" ); + { + WRITE_STRING( "strl" ); + WRITE_STRING( "strh" ); + WRITE_4BYTES( 56 ); //"strh" "chunk" size + WRITE_STRING( "auds" ); + WRITE_4BYTES( 0 ); //FCC + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( 0 ); //dwPriority + WRITE_4BYTES( 0 ); //dwInitialFrame + + WRITE_4BYTES( afd.a.sampleSize ); //dwTimescale + WRITE_4BYTES( afd.a.sampleSize * + afd.a.rate ); //dwDataRate + WRITE_4BYTES( 0 ); //dwStartTime + WRITE_4BYTES( afd.a.totalBytes / + afd.a.sampleSize ); //dwDataLength + + WRITE_4BYTES( 0 ); //dwSuggestedBufferSize + WRITE_4BYTES( -1 ); //dwQuality + WRITE_4BYTES( afd.a.sampleSize ); //dwSampleSize + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + WRITE_2BYTES( 0 ); //rcFrame + + WRITE_STRING( "strf" ); + WRITE_4BYTES( 18 ); //"strf" "chunk" size + WRITE_2BYTES( afd.a.format ); //wFormatTag + WRITE_2BYTES( afd.a.channels ); //nChannels + WRITE_4BYTES( afd.a.rate ); //nSamplesPerSec + WRITE_4BYTES( afd.a.sampleSize * + afd.a.rate ); //nAvgBytesPerSec + WRITE_2BYTES( afd.a.sampleSize ); //nBlockAlign + WRITE_2BYTES( afd.a.bits ); //wBitsPerSample + WRITE_2BYTES( 0 ); //cbSize + } + END_CHUNK( ); + } + } + END_CHUNK( ); + + afd.moviOffset = bufIndex; + + START_CHUNK( "LIST" ); + { + WRITE_STRING( "movi" ); + } + } + } +} + +/* +=============== +CL_OpenAVIForWriting + +Creates an AVI file and gets it into a state where +writing the actual data can begin +=============== +*/ +qboolean CL_OpenAVIForWriting( const char *fileName ) +{ + if( afd.fileOpen ) + return qfalse; + + Com_Memset( &afd, 0, sizeof( aviFileData_t ) ); + + // Don't start if a framerate has not been chosen + if( cl_avidemo->integer <= 0 ) + { + Com_Printf( S_COLOR_RED "cl_avidemo must be >= 1\n" ); + return qfalse; + } + + if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 ) + return qfalse; + + if( ( afd.idxF = FS_FOpenFileWrite( va( "%s.idx", fileName ) ) ) <= 0 ) + { + FS_FCloseFile( afd.f ); + return qfalse; + } + + Q_strncpyz( afd.fileName, fileName, MAX_QPATH ); + + afd.frameRate = cl_avidemo->integer; + afd.framePeriod = (int)( 1000000.0f / afd.frameRate ); + afd.width = cls.glconfig.vidWidth; + afd.height = cls.glconfig.vidHeight; + + if( cl_aviMotionJpeg->integer ) + afd.motionJpeg = qtrue; + else + afd.motionJpeg = qfalse; + + afd.cBuffer = Z_Malloc( afd.width * afd.height * 4 ); + afd.eBuffer = Z_Malloc( afd.width * afd.height * 4 ); + + afd.a.rate = dma.speed; + afd.a.format = WAV_FORMAT_PCM; + afd.a.channels = dma.channels; + afd.a.bits = dma.samplebits; + afd.a.sampleSize = ( afd.a.bits / 8 ) * afd.a.channels; + + if( afd.a.rate % afd.frameRate ) + { + int suggestRate = afd.frameRate; + + while( ( afd.a.rate % suggestRate ) && suggestRate >= 1 ) + suggestRate--; + + Com_Printf( S_COLOR_YELLOW "WARNING: cl_avidemo is not a divisor " + "of the audio rate, suggest %d\n", suggestRate ); + } + + if( !Cvar_VariableIntegerValue( "s_initsound" ) ) + { + afd.audio = qfalse; + } + else if( Q_stricmp( Cvar_VariableString( "s_backend" ), "OpenAL" ) ) + { + if( afd.a.bits == 16 && afd.a.channels == 2 ) + afd.audio = qtrue; + else + afd.audio = qfalse; //FIXME: audio not implemented for this case + } + else + { + afd.audio = qfalse; + Com_Printf( S_COLOR_YELLOW "WARNING: Audio capture is not supported " + "with OpenAL. Set s_useOpenAL to 0 for audio capture\n" ); + } + + // This doesn't write a real header, but allocates the + // correct amount of space at the beginning of the file + CL_WriteAVIHeader( ); + + SafeFS_Write( buffer, bufIndex, afd.f ); + afd.fileSize = bufIndex; + + bufIndex = 0; + START_CHUNK( "idx1" ); + SafeFS_Write( buffer, bufIndex, afd.idxF ); + + afd.moviSize = 4; // For the "movi" + afd.fileOpen = qtrue; + + return qtrue; +} + +/* +=============== +CL_WriteAVIVideoFrame +=============== +*/ +void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size ) +{ + int chunkOffset = afd.fileSize - afd.moviOffset - 8; + int chunkSize = 8 + size; + int paddingSize = PAD( size, 2 ) - size; + byte padding[ 4 ] = { 0 }; + + if( !afd.fileOpen ) + return; + + bufIndex = 0; + WRITE_STRING( "00dc" ); + WRITE_4BYTES( size ); + + SafeFS_Write( buffer, 8, afd.f ); + SafeFS_Write( imageBuffer, size, afd.f ); + SafeFS_Write( padding, paddingSize, afd.f ); + afd.fileSize += ( chunkSize + paddingSize ); + + afd.numVideoFrames++; + afd.moviSize += ( chunkSize + paddingSize ); + + if( size > afd.maxRecordSize ) + afd.maxRecordSize = size; + + // Index + bufIndex = 0; + WRITE_STRING( "00dc" ); //dwIdentifier + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( chunkOffset ); //dwOffset + WRITE_4BYTES( size ); //dwLength + SafeFS_Write( buffer, 16, afd.idxF ); + + afd.numIndices++; +} + +#define PCM_BUFFER_SIZE 44100 + +/* +=============== +CL_WriteAVIAudioFrame +=============== +*/ +void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size ) +{ + static byte pcmCaptureBuffer[ PCM_BUFFER_SIZE ] = { 0 }; + static int bytesInBuffer = 0; + + if( !afd.audio ) + return; + + if( !afd.fileOpen ) + return; + + if( bytesInBuffer + size > PCM_BUFFER_SIZE ) + { + Com_Printf( S_COLOR_YELLOW + "WARNING: Audio capture buffer overflow -- truncating\n" ); + size = PCM_BUFFER_SIZE - bytesInBuffer; + } + + Com_Memcpy( &pcmCaptureBuffer[ bytesInBuffer ], pcmBuffer, size ); + bytesInBuffer += size; + + // Only write if we have a frame's worth of audio + if( bytesInBuffer >= (int)ceil( afd.a.rate / cl_avidemo->value ) * + afd.a.sampleSize ) + { + int chunkOffset = afd.fileSize - afd.moviOffset - 8; + int chunkSize = 8 + bytesInBuffer; + int paddingSize = PAD( bytesInBuffer, 2 ) - bytesInBuffer; + byte padding[ 4 ] = { 0 }; + + bufIndex = 0; + WRITE_STRING( "01wb" ); + WRITE_4BYTES( bytesInBuffer ); + + SafeFS_Write( buffer, 8, afd.f ); + SafeFS_Write( pcmBuffer, bytesInBuffer, afd.f ); + SafeFS_Write( padding, paddingSize, afd.f ); + afd.fileSize += ( chunkSize + paddingSize ); + + afd.numAudioFrames++; + afd.moviSize += ( chunkSize + paddingSize ); + afd.a.totalBytes =+ bytesInBuffer; + + // Index + bufIndex = 0; + WRITE_STRING( "01wb" ); //dwIdentifier + WRITE_4BYTES( 0 ); //dwFlags + WRITE_4BYTES( chunkOffset ); //dwOffset + WRITE_4BYTES( bytesInBuffer ); //dwLength + SafeFS_Write( buffer, 16, afd.idxF ); + + afd.numIndices++; + + bytesInBuffer = 0; + } +} + +/* +=============== +CL_TakeVideoFrame +=============== +*/ +void CL_TakeVideoFrame( void ) +{ + // AVI file isn't open + if( !afd.fileOpen ) + return; + + re.TakeVideoFrame( afd.width, afd.height, + afd.cBuffer, afd.eBuffer, afd.motionJpeg ); +} + +/* +=============== +CL_CloseAVI + +Closes the AVI file and writes an index chunk +=============== +*/ +qboolean CL_CloseAVI( void ) +{ + int indexRemainder; + int indexSize = afd.numIndices * 16; + const char *idxFileName = va( "%s.idx", afd.fileName ); + + // AVI file isn't open + if( !afd.fileOpen ) + return qfalse; + + afd.fileOpen = qfalse; + + FS_Seek( afd.idxF, 4, FS_SEEK_SET ); + bufIndex = 0; + WRITE_4BYTES( indexSize ); + SafeFS_Write( buffer, bufIndex, afd.idxF ); + FS_FCloseFile( afd.idxF ); + + // Write index + + // Open the temp index file + if( ( indexSize = FS_FOpenFileRead( idxFileName, + &afd.idxF, qtrue ) ) <= 0 ) + { + FS_FCloseFile( afd.f ); + return qfalse; + } + + indexRemainder = indexSize; + + // Append index to end of avi file + while( indexRemainder > MAX_AVI_BUFFER ) + { + FS_Read( buffer, MAX_AVI_BUFFER, afd.idxF ); + SafeFS_Write( buffer, MAX_AVI_BUFFER, afd.f ); + afd.fileSize += MAX_AVI_BUFFER; + indexRemainder -= MAX_AVI_BUFFER; + } + FS_Read( buffer, indexRemainder, afd.idxF ); + SafeFS_Write( buffer, indexRemainder, afd.f ); + afd.fileSize += indexRemainder; + FS_FCloseFile( afd.idxF ); + + // Remove temp index file + FS_HomeRemove( idxFileName ); + + // Write the real header + FS_Seek( afd.f, 0, FS_SEEK_SET ); + CL_WriteAVIHeader( ); + + bufIndex = 4; + WRITE_4BYTES( afd.fileSize - 8 ); // "RIFF" size + + bufIndex = afd.moviOffset + 4; // Skip "LIST" + WRITE_4BYTES( afd.moviSize ); + + SafeFS_Write( buffer, bufIndex, afd.f ); + + Z_Free( afd.cBuffer ); + Z_Free( afd.eBuffer ); + FS_FCloseFile( afd.f ); + + Com_Printf( "Wrote %d:%d frames to %s\n", afd.numVideoFrames, afd.numAudioFrames, afd.fileName ); + + return qtrue; +} + +/* +=============== +CL_VideoRecording +=============== +*/ +qboolean CL_VideoRecording( void ) +{ + return afd.fileOpen; +} diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 0789ac9b..04d87cc1 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -44,7 +44,9 @@ cvar_t *cl_freezeDemo; cvar_t *cl_shownet; cvar_t *cl_showSend; cvar_t *cl_timedemo; +cvar_t *cl_autoRecordDemo; cvar_t *cl_avidemo; +cvar_t *cl_aviMotionJpeg; cvar_t *cl_forceavidemo; cvar_t *cl_freelook; @@ -283,7 +285,7 @@ void CL_Record_f( void ) { } // 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" ) ) { + if ( NET_IsLocalAddress( clc.serverAddress ) && !Cvar_VariableValue( "g_synchronousClients" ) ) { Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n"); } @@ -774,6 +776,11 @@ void CL_Disconnect( qboolean showMainMenu ) { // not connected to a pure server anymore cl_connectedToPureServer = qfalse; + + // Stop recording any video + if( CL_VideoRecording( ) ) { + CL_CloseAVI( ); + } } @@ -1105,6 +1112,11 @@ doesn't know what graphics to reload */ void CL_Vid_Restart_f( void ) { + // Settings may have changed so stop recording now + if( CL_VideoRecording( ) ) { + CL_CloseAVI( ); + } + // don't let them loop during the restart S_StopAllSounds(); // shutdown the UI @@ -1916,18 +1928,57 @@ void CL_Frame ( int msec ) { } // if recording an avi, lock to a fixed fps - if ( cl_avidemo->integer && msec) { + if ( CL_VideoRecording( ) && 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; + CL_TakeVideoFrame( ); + + // fixed time for next frame' + msec = (int)ceil( (1000.0f / cl_avidemo->value) * com_timescale->value ); + if (msec == 0) { + msec = 1; + } } } + if( cl_autoRecordDemo->integer ) { + if( cls.state == CA_ACTIVE && !clc.demorecording ) { + // If not recording a demo, and we should be, start one + qtime_t now; + char *nowString; + char *p; + char mapName[ MAX_QPATH ]; + char serverName[ MAX_OSPATH ]; + + Com_RealTime( &now ); + nowString = va( "%04d%02d%02d%02d%02d%02d", + 1900 + now.tm_year, + 1 + now.tm_mon, + now.tm_mday, + now.tm_hour, + now.tm_min, + now.tm_sec ); + + Q_strncpyz( serverName, cls.servername, MAX_OSPATH ); + // Replace the ":" in the address as it is not a valid + // file name character + p = strstr( serverName, ":" ); + if( p ) { + *p = '.'; + } + + Q_strncpyz( mapName, COM_SkipPath( cl.mapname ), sizeof( cl.mapname ) ); + COM_StripExtension( mapName, mapName ); + + Cbuf_ExecuteText( EXEC_NOW, + va( "record %s-%s-%s", nowString, serverName, mapName ) ); + } + else if( cls.state != CA_ACTIVE && clc.demorecording ) { + // Recording, but not CA_ACTIVE, so stop recording + CL_StopRecord_f( ); + } + } + // save the msec before checking pause cls.realFrametime = msec; @@ -2124,6 +2175,8 @@ void CL_InitRef( void ) { ri.CIN_UploadCinematic = CIN_UploadCinematic; ri.CIN_PlayCinematic = CIN_PlayCinematic; ri.CIN_RunCinematic = CIN_RunCinematic; + + ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame; ret = GetRefAPI( REF_API_VERSION, &ri ); @@ -2161,6 +2214,72 @@ void CL_SetModel_f( void ) { } } + +//=========================================================================================== + + +/* +=============== +CL_Video_f + +video +video [filename] +=============== +*/ +void CL_Video_f( void ) +{ + char filename[ MAX_OSPATH ]; + int i, last; + + if( Cmd_Argc( ) == 2 ) + { + // explicit filename + Com_sprintf( filename, MAX_OSPATH, "videos/%s.avi", Cmd_Argv( 1 ) ); + } + else + { + // scan for a free filename + for( i = 0; i <= 9999; i++ ) + { + int a, b, c, d; + + last = i; + + a = last / 1000; + last -= a * 1000; + b = last / 100; + last -= b * 100; + c = last / 10; + last -= c * 10; + d = last; + + Com_sprintf( filename, MAX_OSPATH, "videos/video%d%d%d%d.avi", + a, b, c, d ); + + if( !FS_FileExists( filename ) ) + break; // file doesn't exist + } + + if( i > 9999 ) + { + Com_Printf( S_COLOR_RED "ERROR: no free file names to create video\n" ); + return; + } + } + + CL_OpenAVIForWriting( filename ); +} + +/* +=============== +CL_StopVideo_f +=============== +*/ +void CL_StopVideo_f( void ) +{ + CL_CloseAVI( ); +} + /* ==================== CL_Init @@ -2196,7 +2315,9 @@ void CL_Init( void ) { cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); cl_timedemo = Cvar_Get ("timedemo", "0", 0); - cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0); + cl_autoRecordDemo = Cvar_Get ("cl_autoRecordDemo", "0", CVAR_ARCHIVE); + cl_avidemo = Cvar_Get ("cl_avidemo", "25", CVAR_ARCHIVE); + cl_aviMotionJpeg = Cvar_Get ("cl_aviMotionJpeg", "1", CVAR_ARCHIVE); cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0); rconAddress = Cvar_Get ("rconAddress", "", 0); @@ -2297,6 +2418,8 @@ void CL_Init( void ) { Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f ); Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f ); Cmd_AddCommand ("model", CL_SetModel_f ); + Cmd_AddCommand ("video", CL_Video_f ); + Cmd_AddCommand ("stopvideo", CL_StopVideo_f ); CL_InitRef(); SCR_Init (); @@ -2352,6 +2475,8 @@ void CL_Shutdown( void ) { Cmd_RemoveCommand ("serverstatus"); Cmd_RemoveCommand ("showip"); Cmd_RemoveCommand ("model"); + Cmd_RemoveCommand ("video"); + Cmd_RemoveCommand ("stopvideo"); Cvar_Set( "cl_running", "0" ); diff --git a/src/client/client.h b/src/client/client.h index e597047f..5f609dde 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -343,6 +343,8 @@ extern cvar_t *m_side; extern cvar_t *m_filter; extern cvar_t *cl_timedemo; +extern cvar_t *cl_avidemo; +extern cvar_t *cl_aviMotionJpeg; extern cvar_t *cl_activeAction; @@ -518,3 +520,13 @@ void LAN_SaveServersToCache( void ); 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 ); + +// +// cl_avi.c +// +qboolean CL_OpenAVIForWriting( const char *filename ); +void CL_TakeVideoFrame( void ); +void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size ); +void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size ); +qboolean CL_CloseAVI( void ); +qboolean CL_VideoRecording( void ); diff --git a/src/client/snd_dma.c b/src/client/snd_dma.c index 6860179f..d51a4b85 100644 --- a/src/client/snd_dma.c +++ b/src/client/snd_dma.c @@ -1140,6 +1140,12 @@ void S_GetSoundtime(void) fullsamples = dma.samples / dma.channels; + if( CL_VideoRecording( ) ) + { + s_soundtime += (int)ceil( dma.speed / cl_avidemo->value ); + return; + } + // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. samplepos = SNDDMA_GetDMAPos(); diff --git a/src/client/snd_main.c b/src/client/snd_main.c index d83996a8..1ecdc237 100644 --- a/src/client/snd_main.c +++ b/src/client/snd_main.c @@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA cvar_t *s_volume; cvar_t *s_musicVolume; cvar_t *s_doppler; +cvar_t *s_backend; static soundInterface_t si; @@ -371,6 +372,7 @@ void S_Init( void ) s_volume = Cvar_Get( "s_volume", "0.8", CVAR_ARCHIVE ); s_musicVolume = Cvar_Get( "s_musicvolume", "0.25", CVAR_ARCHIVE ); s_doppler = Cvar_Get( "s_doppler", "1", CVAR_ARCHIVE ); + s_backend = Cvar_Get( "s_backend", "", CVAR_ROM ); cv = Cvar_Get( "s_initsound", "1", 0 ); if( !cv->integer ) { @@ -389,10 +391,12 @@ void S_Init( void ) if( cv->integer ) { //OpenAL started = S_AL_Init( &si ); + Cvar_Set( "s_backend", "OpenAL" ); } if( !started ) { started = S_Base_Init( &si ); + Cvar_Set( "s_backend", "base" ); } if( started ) { diff --git a/src/client/snd_mix.c b/src/client/snd_mix.c index 85b10cba..4d9e716a 100644 --- a/src/client/snd_mix.c +++ b/src/client/snd_mix.c @@ -22,6 +22,7 @@ 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 "client.h" #include "snd_local.h" #if idppc_altivec && !defined(MACOS_X) #include <altivec.h> @@ -138,6 +139,9 @@ void S_TransferStereo16 (unsigned long *pbuf, int endtime) snd_p += snd_linear_count; ls_paintedtime += (snd_linear_count>>1); + + if( CL_VideoRecording( ) ) + CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 ); } } diff --git a/src/qcommon/common.c b/src/qcommon/common.c index 231f4aa8..b0cee0d3 100644 --- a/src/qcommon/common.c +++ b/src/qcommon/common.c @@ -2714,316 +2714,6 @@ void Com_Shutdown (void) { } -#if I_WANT_A_CUSTOM_MEMCPY && !defined(_WIN32) -void Com_Memcpy (void* dest, const void* src, const size_t count) -{ - memcpy(dest, src, count); -} - -void Com_Memset (void* dest, const int val, const size_t count) -{ - memset(dest, val, count); -} - -#elif I_WANT_A_CUSTOM_MEMCPY && defined(_WIN32) - -typedef enum -{ - PRE_READ, // prefetch assuming that buffer is used for reading only - PRE_WRITE, // prefetch assuming that buffer is used for writing only - PRE_READ_WRITE // prefetch assuming that buffer is used for both reading and writing -} e_prefetch; - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type); - -#define EMMS_INSTRUCTION __asm emms - -void _copyDWord (unsigned int* dest, const unsigned int constant, const unsigned int count) { - __asm - { - mov edx,dest - mov eax,constant - mov ecx,count - and ecx,~7 - jz padding - sub ecx,8 - jmp loopu - align 16 -loopu: - test [edx+ecx*4 + 28],ebx // fetch next block destination to L1 cache - mov [edx+ecx*4 + 0],eax - mov [edx+ecx*4 + 4],eax - mov [edx+ecx*4 + 8],eax - mov [edx+ecx*4 + 12],eax - mov [edx+ecx*4 + 16],eax - mov [edx+ecx*4 + 20],eax - mov [edx+ecx*4 + 24],eax - mov [edx+ecx*4 + 28],eax - sub ecx,8 - jge loopu -padding: mov ecx,count - mov ebx,ecx - and ecx,7 - jz outta - and ebx,~7 - lea edx,[edx+ebx*4] // advance dest pointer - test [edx+0],eax // fetch destination to L1 cache - cmp ecx,4 - jl skip4 - mov [edx+0],eax - mov [edx+4],eax - mov [edx+8],eax - mov [edx+12],eax - add edx,16 - sub ecx,4 -skip4: cmp ecx,2 - jl skip2 - mov [edx+0],eax - mov [edx+4],eax - add edx,8 - sub ecx,2 -skip2: cmp ecx,1 - jl outta - mov [edx+0],eax -outta: - } -} - -// optimized memory copy routine that handles all alignment -// cases and block sizes efficiently -void Com_Memcpy (void* dest, const void* src, const size_t count) { - Com_Prefetch (src, count, PRE_READ); - __asm - { - push edi - push esi - mov ecx,count - cmp ecx,0 // count = 0 check (just to be on the safe side) - je outta - mov edx,dest - mov ebx,src - cmp ecx,32 // padding only? - jl padding - - mov edi,ecx - and edi,~31 // edi = count&~31 - sub edi,32 - - align 16 -loopMisAligned: - mov eax,[ebx + edi + 0 + 0*8] - mov esi,[ebx + edi + 4 + 0*8] - mov [edx+edi+0 + 0*8],eax - mov [edx+edi+4 + 0*8],esi - mov eax,[ebx + edi + 0 + 1*8] - mov esi,[ebx + edi + 4 + 1*8] - mov [edx+edi+0 + 1*8],eax - mov [edx+edi+4 + 1*8],esi - mov eax,[ebx + edi + 0 + 2*8] - mov esi,[ebx + edi + 4 + 2*8] - mov [edx+edi+0 + 2*8],eax - mov [edx+edi+4 + 2*8],esi - mov eax,[ebx + edi + 0 + 3*8] - mov esi,[ebx + edi + 4 + 3*8] - mov [edx+edi+0 + 3*8],eax - mov [edx+edi+4 + 3*8],esi - sub edi,32 - jge loopMisAligned - - mov edi,ecx - and edi,~31 - add ebx,edi // increase src pointer - add edx,edi // increase dst pointer - and ecx,31 // new count - jz outta // if count = 0, get outta here - -padding: - cmp ecx,16 - jl skip16 - mov eax,dword ptr [ebx] - mov dword ptr [edx],eax - mov eax,dword ptr [ebx+4] - mov dword ptr [edx+4],eax - mov eax,dword ptr [ebx+8] - mov dword ptr [edx+8],eax - mov eax,dword ptr [ebx+12] - mov dword ptr [edx+12],eax - sub ecx,16 - add ebx,16 - add edx,16 -skip16: - cmp ecx,8 - jl skip8 - mov eax,dword ptr [ebx] - mov dword ptr [edx],eax - mov eax,dword ptr [ebx+4] - sub ecx,8 - mov dword ptr [edx+4],eax - add ebx,8 - add edx,8 -skip8: - cmp ecx,4 - jl skip4 - mov eax,dword ptr [ebx] // here 4-7 bytes - add ebx,4 - sub ecx,4 - mov dword ptr [edx],eax - add edx,4 -skip4: // 0-3 remaining bytes - cmp ecx,2 - jl skip2 - mov ax,word ptr [ebx] // two bytes - cmp ecx,3 // less than 3? - mov word ptr [edx],ax - jl outta - mov al,byte ptr [ebx+2] // last byte - mov byte ptr [edx+2],al - jmp outta -skip2: - cmp ecx,1 - jl outta - mov al,byte ptr [ebx] - mov byte ptr [edx],al -outta: - pop esi - pop edi - } -} - -void Com_Memset (void* dest, const int val, const size_t count) -{ - unsigned int fillval; - - if (count < 8) - { - __asm - { - mov edx,dest - mov eax, val - mov ah,al - mov ebx,eax - and ebx, 0xffff - shl eax,16 - add eax,ebx // eax now contains pattern - mov ecx,count - cmp ecx,4 - jl skip4 - mov [edx],eax // copy first dword - add edx,4 - sub ecx,4 - skip4: cmp ecx,2 - jl skip2 - mov word ptr [edx],ax // copy 2 bytes - add edx,2 - sub ecx,2 - skip2: cmp ecx,0 - je skip1 - mov byte ptr [edx],al // copy single byte - skip1: - } - return; - } - - fillval = val; - - fillval = fillval|(fillval<<8); - fillval = fillval|(fillval<<16); // fill dword with 8-bit pattern - - _copyDWord ((unsigned int*)(dest),fillval, count/4); - - __asm // padding of 0-3 bytes - { - mov ecx,count - mov eax,ecx - and ecx,3 - jz skipA - and eax,~3 - mov ebx,dest - add ebx,eax - mov eax,fillval - cmp ecx,2 - jl skipB - mov word ptr [ebx],ax - cmp ecx,2 - je skipA - mov byte ptr [ebx+2],al - jmp skipA -skipB: - cmp ecx,0 - je skipA - mov byte ptr [ebx],al -skipA: - } -} - -qboolean Com_Memcmp (const void *src0, const void *src1, const unsigned int count) -{ - unsigned int i; - // MMX version anyone? - - if (count >= 16) - { - unsigned int *dw = (unsigned int*)(src0); - unsigned int *sw = (unsigned int*)(src1); - - unsigned int nm2 = count/16; - for (i = 0; i < nm2; i+=4) - { - unsigned int tmp = (dw[i+0]-sw[i+0])|(dw[i+1]-sw[i+1])| - (dw[i+2]-sw[i+2])|(dw[i+3]-sw[i+3]); - if (tmp) - return qfalse; - } - } - if (count & 15) - { - byte *d = (byte*)src0; - byte *s = (byte*)src1; - for (i = count & 0xfffffff0; i < count; i++) - if (d[i]!=s[i]) - return qfalse; - } - - return qtrue; -} - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type) -{ - // write buffer prefetching is performed only if - // the processor benefits from it. Read and read/write - // prefetching is always performed. - - switch (type) - { - case PRE_WRITE : break; - case PRE_READ: - case PRE_READ_WRITE: - - __asm - { - mov ebx,s - mov ecx,bytes - cmp ecx,4096 // clamp to 4kB - jle skipClamp - mov ecx,4096 -skipClamp: - add ecx,0x1f - shr ecx,5 // number of cache lines - jz skip - jmp loopie - - align 16 - loopie: test byte ptr [ebx],al - add ebx,32 - dec ecx - jnz loopie - skip: - } - - break; - } -} -#endif //------------------------------------------------------------------------ diff --git a/src/qcommon/files.c b/src/qcommon/files.c index 1ea34cc6..4ebca39a 100644 --- a/src/qcommon/files.c +++ b/src/qcommon/files.c @@ -56,7 +56,7 @@ The "cd path" is the path to an alternate hierarchy that will be searched if a f is not located in the base path. A user can do a partial install that copies some data to a base path created on their hard drive and leave the rest on the cd. Files are never writen to the cd path. It defaults to a value set by the installer, like -"e:\quake3", but it can be overridden with "+set ds_cdpath g:\quake3". +"e:\quake3", but it can be overridden with "+set fs_cdpath g:\quake3". If a user runs the game directly from a CD, the base path would be on the CD. This should still function correctly, but all file writes will fail (harmlessly). @@ -201,7 +201,8 @@ or configs will never get loaded from disk! // every time a new demo pk3 file is built, this checksum must be updated. // the easiest way to get it is to just run the game and see what it spits out -#define DEMO_PAK_CHECKSUM 437558517u +#define DEMO_PAK0_CHECKSUM 2985612116u +#define PAK0_CHECKSUM 1566731103u // if this is defined, the executable positively won't work with any paks other // than the demo pak, even if productid is present. This is only used for our @@ -307,11 +308,6 @@ static char *fs_serverReferencedPakNames[MAX_SEARCH_PATHS]; // pk3 names char lastValidBase[MAX_OSPATH]; char lastValidGame[MAX_OSPATH]; -// productId: This file is copyright 1999 Id Software, and may not be duplicated except during a licensed installation of the full commercial version of Quake 3:Arena -static byte fs_scrambledProductId[152] = { -220, 129, 255, 108, 244, 163, 171, 55, 133, 65, 199, 36, 140, 222, 53, 99, 65, 171, 175, 232, 236, 193, 210, 250, 169, 104, 231, 231, 21, 201, 170, 208, 135, 175, 130, 136, 85, 215, 71, 23, 96, 32, 96, 83, 44, 240, 219, 138, 184, 215, 73, 27, 196, 247, 55, 139, 148, 68, 78, 203, 213, 238, 139, 23, 45, 205, 118, 186, 236, 230, 231, 107, 212, 1, 10, 98, 30, 20, 116, 180, 216, 248, 166, 35, 45, 22, 215, 229, 35, 116, 250, 167, 117, 3, 57, 55, 201, 229, 218, 222, 128, 12, 141, 149, 32, 110, 168, 215, 184, 53, 31, 147, 62, 12, 138, 67, 132, 54, 125, 6, 221, 148, 140, 4, 21, 44, 198, 3, 126, 12, 100, 236, 61, 42, 44, 251, 15, 135, 14, 134, 89, 92, 177, 246, 152, 106, 124, 78, 118, 80, 28, 42 -}; - #ifdef FS_MISSING FILE* missingFiles = NULL; #endif @@ -566,11 +562,22 @@ FS_Remove =========== */ -static void FS_Remove( const char *osPath ) { +void FS_Remove( const char *osPath ) { remove( osPath ); } /* +=========== +FS_HomeRemove + +=========== +*/ +void FS_HomeRemove( const char *homePath ) { + remove( FS_BuildOSPath( fs_homepath->string, + fs_gamedir, homePath ) ); +} + +/* ================ FS_FileExists @@ -2829,69 +2836,6 @@ static void FS_Startup( const char *gameName ) { Com_Printf( "%d files in pk3 files\n", fs_packFiles ); } - -/* -=================== -FS_SetRestrictions - -Looks for product keys and restricts media add on ability -if the full version is not found -=================== -*/ -static void FS_SetRestrictions( void ) { - searchpath_t *path; - char *productId; - - return; - -#ifndef PRE_RELEASE_DEMO - - // if fs_restrict is set, don't even look for the id file, - // which allows the demo release to be tested even if - // the full game is present - if ( !fs_restrict->integer ) { - // look for the full game id - FS_ReadFile( "productid.txt", (void **)&productId ); - if ( productId ) { - // check against the hardcoded string - int seed, i; - - seed = 5000; - for ( i = 0 ; i < sizeof( fs_scrambledProductId ) ; i++ ) { - if ( ( fs_scrambledProductId[i] ^ (seed&255) ) != productId[i] ) { - break; - } - seed = (69069 * seed + 1); - } - - FS_FreeFile( productId ); - - if ( i == sizeof( fs_scrambledProductId ) ) { - return; // no restrictions - } - Com_Error( ERR_FATAL, "Invalid product identification" ); - } - } -#endif - Cvar_Set( "fs_restrict", "1" ); - - Com_Printf( "\nRunning in restricted demo mode.\n\n" ); - - // restart the filesystem with just the demo directory - FS_Shutdown(qfalse); - FS_Startup( DEMOGAME ); - - // make sure that the pak file has the header checksum we expect - for ( path = fs_searchpaths ; path ; path = path->next ) { - if ( path->pack ) { - // a tiny attempt to keep the checksum from being scannable from the exe - if ( (path->pack->checksum ^ 0x02261994u) != (DEMO_PAK_CHECKSUM ^ 0x02261994u) ) { - Com_Error( ERR_FATAL, "Corrupted pak0.pk3: %u", path->pack->checksum ); - } - } - } -} - /* ===================== FS_GamePureChecksum @@ -3259,9 +3203,6 @@ void FS_InitFilesystem( void ) { // try to start up normally FS_Startup( BASEGAME ); - // see if we are going to allow add-ons - FS_SetRestrictions(); - // if we can't find default.cfg, assume that the paths are // busted and error out now, rather than getting an unreadable // graphics screen when the font fails to load @@ -3296,9 +3237,6 @@ void FS_Restart( int checksumFeed ) { // try to start up normally FS_Startup( BASEGAME ); - // see if we are going to allow add-ons - FS_SetRestrictions(); - // if we can't find default.cfg, assume that the paths are // busted and error out now, rather than getting an unreadable // graphics screen when the font fails to load diff --git a/src/qcommon/md4.c b/src/qcommon/md4.c index 24b79610..82c4b0d8 100644 --- a/src/qcommon/md4.c +++ b/src/qcommon/md4.c @@ -38,13 +38,8 @@ void MD4Init (MD4_CTX *); void MD4Update (MD4_CTX *, const unsigned char *, unsigned int); void MD4Final (unsigned char [16], MD4_CTX *); -#if I_WANT_A_CUSTOM_MEMCPY -void Com_Memset (void* dest, const int val, const size_t count); -void Com_Memcpy (void* dest, const void* src, const size_t count); -#else #define Com_Memset memset #define Com_Memcpy memcpy -#endif /* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm */ /* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved. diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h index 53848aa4..537dee36 100644 --- a/src/qcommon/q_shared.h +++ b/src/qcommon/q_shared.h @@ -243,13 +243,8 @@ void Snd_Memset (void* dest, const int val, const size_t count); #define Snd_Memset Com_Memset #endif -#if I_WANT_A_CUSTOM_MEMCPY -void Com_Memset (void* dest, const int val, const size_t count); -void Com_Memcpy (void* dest, const void* src, const size_t count); -#else #define Com_Memset memset #define Com_Memcpy memcpy -#endif #define CIN_system 1 #define CIN_loop 2 diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h index 843f04ef..9e9ca513 100644 --- a/src/qcommon/qcommon.h +++ b/src/qcommon/qcommon.h @@ -651,6 +651,9 @@ qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); void FS_Rename( const char *from, const char *to ); +void FS_Remove( const char *osPath ); +void FS_HomeRemove( const char *homePath ); + /* ============================================================== @@ -892,7 +895,6 @@ void S_ClearSoundBuffer( void ); void SCR_DebugGraph (float value, int color); // FIXME: move logging to common? - // // server interface // diff --git a/src/renderer/tr_backend.c b/src/renderer/tr_backend.c index 73449586..9c9b841a 100644 --- a/src/renderer/tr_backend.c +++ b/src/renderer/tr_backend.c @@ -1082,6 +1082,9 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_SCREENSHOT: data = RB_TakeScreenshotCmd( data ); break; + case RC_VIDEOFRAME: + data = RB_TakeVideoFrameCmd( data ); + break; case RC_END_OF_LIST: default: diff --git a/src/renderer/tr_cmds.c b/src/renderer/tr_cmds.c index bbcf6385..d637aec0 100644 --- a/src/renderer/tr_cmds.c +++ b/src/renderer/tr_cmds.c @@ -446,3 +446,30 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { backEnd.pc.msec = 0; } +/* +============= +RE_TakeVideoFrame +============= +*/ +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) +{ + videoFrameCommand_t *cmd; + + if( !tr.registered ) { + return; + } + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if( !cmd ) { + return; + } + + cmd->commandId = RC_VIDEOFRAME; + + cmd->width = width; + cmd->height = height; + cmd->captureBuffer = captureBuffer; + cmd->encodeBuffer = encodeBuffer; + cmd->motionJpeg = motionJpeg; +} diff --git a/src/renderer/tr_image.c b/src/renderer/tr_image.c index a3efa2f1..692f00c6 100644 --- a/src/renderer/tr_image.c +++ b/src/renderer/tr_image.c @@ -1853,6 +1853,64 @@ void SaveJPG(char * filename, int quality, int image_width, int image_height, un /* And we're done! */ } +/* +================= +SaveJPGToBuffer +================= +*/ +int SaveJPGToBuffer( byte *buffer, int quality, + int image_width, int image_height, + byte *image_buffer ) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + int row_stride; /* physical row width in image buffer */ + + /* Step 1: allocate and initialize JPEG compression object */ + cinfo.err = jpeg_std_error(&jerr); + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + jpegDest(&cinfo, buffer, image_width*image_height*4); + + /* Step 3: set parameters for compression */ + cinfo.image_width = image_width; /* image width and height, in pixels */ + cinfo.image_height = image_height; + cinfo.input_components = 4; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + /* Step 6: Finish compression */ + jpeg_finish_compress(&cinfo); + + /* Step 7: release JPEG compression object */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + return hackSize; +} + //=================================================================== /* diff --git a/src/renderer/tr_init.c b/src/renderer/tr_init.c index a3fd2ca6..79c910d1 100644 --- a/src/renderer/tr_init.c +++ b/src/renderer/tr_init.c @@ -701,6 +701,51 @@ void R_ScreenShotJPEG_f (void) { //============================================================================ /* +================== +RB_TakeVideoFrameCmd +================== +*/ +const void *RB_TakeVideoFrameCmd( const void *data ) +{ + const videoFrameCommand_t *cmd; + int frameSize; + int i; + + cmd = (const videoFrameCommand_t *)data; + + qglReadPixels( 0, 0, cmd->width, cmd->height, GL_RGBA, + GL_UNSIGNED_BYTE, cmd->captureBuffer ); + + // gamma correct + if( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) + R_GammaCorrect( cmd->captureBuffer, cmd->width * cmd->height * 4 ); + + if( cmd->motionJpeg ) + { + frameSize = SaveJPGToBuffer( cmd->encodeBuffer, 95, + cmd->width, cmd->height, cmd->captureBuffer ); + } + else + { + frameSize = cmd->width * cmd->height * 4; + + // Vertically flip the image + for( i = 0; i < cmd->height; i++ ) + { + Com_Memcpy( &cmd->encodeBuffer[ i * ( cmd->width * 4 ) ], + &cmd->captureBuffer[ ( cmd->height - i - 1 ) * ( cmd->width * 4 ) ], + cmd->width * 4 ); + } + } + + ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize ); + + return (const void *)(cmd + 1); +} + +//============================================================================ + +/* ** GL_SetDefaultState */ void GL_SetDefaultState( void ) @@ -1202,5 +1247,7 @@ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { re.GetEntityToken = R_GetEntityToken; re.inPVS = R_inPVS; + re.TakeVideoFrame = RE_TakeVideoFrame; + return &re; } diff --git a/src/renderer/tr_local.h b/src/renderer/tr_local.h index 4119efac..7efed35d 100644 --- a/src/renderer/tr_local.h +++ b/src/renderer/tr_local.h @@ -1216,6 +1216,7 @@ skin_t *R_GetSkinByHandle( qhandle_t hSkin ); int R_ComputeLOD( trRefEntity_t *ent ); +const void *RB_TakeVideoFrameCmd( const void *data ); // // tr_shader.c @@ -1580,6 +1581,15 @@ typedef struct { qboolean jpeg; } screenshotCommand_t; +typedef struct { + int commandId; + int width; + int height; + byte *captureBuffer; + byte *encodeBuffer; + qboolean motionJpeg; +} videoFrameCommand_t; + typedef enum { RC_END_OF_LIST, RC_SET_COLOR, @@ -1587,7 +1597,8 @@ typedef enum { RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, - RC_SCREENSHOT + RC_SCREENSHOT, + RC_VIDEOFRAME } renderCommand_t; @@ -1636,6 +1647,11 @@ void RE_StretchPic ( float x, float y, float w, float h, void RE_BeginFrame( stereoFrame_t stereoFrame ); void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer); +int SaveJPGToBuffer( byte *buffer, int quality, + int image_width, int image_height, + byte *image_buffer ); +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); // font stuff void R_InitFreeType( void ); diff --git a/src/renderer/tr_public.h b/src/renderer/tr_public.h index 92801ac4..e4e4d047 100644 --- a/src/renderer/tr_public.h +++ b/src/renderer/tr_public.h @@ -98,6 +98,8 @@ typedef struct { void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime); qboolean (*GetEntityToken)( char *buffer, int size ); qboolean (*inPVS)( const vec3_t p1, const vec3_t p2 ); + + void (*TakeVideoFrame)( int h, int w, byte* captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); } refexport_t; // @@ -157,6 +159,7 @@ typedef struct { int (*CIN_PlayCinematic)( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status (*CIN_RunCinematic) (int handle); + void (*CL_WriteAVIVideoFrame)( const byte *buffer, int size ); } refimport_t; diff --git a/src/unix/Makefile b/src/unix/Makefile index 793f3c7e..db7802dd 100644 --- a/src/unix/Makefile +++ b/src/unix/Makefile @@ -21,6 +21,12 @@ else COMPILE_ARCH=$(shell uname -m | sed -e s/i.86/i386/) endif +BUILD_CLIENT = +BUILD_CLIENT_SMP = +BUILD_SERVER = +BUILD_GAME_SO = +BUILD_GAME_QVM = + ############################################################################# # # If you require a different configuration from the defaults below, create a @@ -90,14 +96,6 @@ ifndef USE_LOCAL_HEADERS USE_LOCAL_HEADERS=1 endif -ifndef BUILD_CLIENT -BUILD_CLIENT=1 -endif - -ifndef BUILD_SERVER -BUILD_SERVER=1 -endif - ############################################################################# BD=debug-$(PLATFORM)-$(ARCH) @@ -135,7 +133,6 @@ MKDIR=mkdir ifeq ($(PLATFORM),linux) - GLIBC=-glibc CC=gcc ifeq ($(ARCH),alpha) @@ -227,23 +224,6 @@ ifeq ($(PLATFORM),linux) LDFLAGS+=-m32 endif - ifeq ($(ARCH),axp) - TARGETS=\ - $(B)/$(PLATFORM)tremded - else - TARGETS=\ - $(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm -# $(B)/$(PLATFORM)tremulous-smp \ - - endif - else # ifeq Linux ############################################################################# @@ -251,7 +231,6 @@ else # ifeq Linux ############################################################################# ifeq ($(PLATFORM),darwin) - GLIBC= CC=gcc # !!! FIXME: calling conventions are still broken! See Bugzilla #2519 @@ -326,17 +305,6 @@ ifeq ($(PLATFORM),darwin) endif endif - TARGETS=\ - $(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm - $(B)/$(PLATFORM)tremulous-smp \ - else # ifeq darwin @@ -382,14 +350,8 @@ ifeq ($(PLATFORM),mingw32) LDFLAGS+=-m32 endif - TARGETS=\ - $(B)/$(PLATFORM)tremulous$(BINEXT) \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm + BUILD_SERVER = 0 + BUILD_CLIENT_SMP = 0 else # ifeq mingw32 @@ -399,8 +361,6 @@ else # ifeq mingw32 ifeq ($(PLATFORM),freebsd) - GLIBC= #libc is irrelevant - ifneq (,$(findstring alpha,$(shell uname -m))) ARCH=axp else #default to i386 @@ -441,21 +401,6 @@ ifeq ($(PLATFORM),freebsd) CLIENT_LDFLAGS=-L/usr/X11R6/$(LIB) -lGL -lX11 -lXext -lXxf86dga -lXxf86vm - ifeq ($(ARCH),axp) - TARGETS=\ - $(B)/$(PLATFORM)tremded - else - TARGETS=\ - $(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm - endif - else # ifeq freebsd ############################################################################# @@ -482,17 +427,8 @@ ifeq ($(PLATFORM),netbsd) BASE_CFLAGS += -DNO_VM_COMPILED endif - ifeq ($(ARCH),i386) - TARGETS=\ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/$(PLATFORM)tremded - else - TARGETS=\ - $(B)/$(PLATFORM)tremded - - endif + BUILD_CLIENT = 0 + BUILD_GAME_QVM = 0 else # ifeq netbsd @@ -503,7 +439,6 @@ else # ifeq netbsd ifeq ($(PLATFORM),irix) ARCH=mips #default to MIPS - GLIBC= #libc is irrelevant CC=cc BASE_CFLAGS=-Dstricmp=strcasecmp -Xcpluscomm -woff 1185 -mips3 \ @@ -518,9 +453,6 @@ ifeq ($(PLATFORM),irix) LDFLAGS=-ldl -lm CLIENT_LDFLAGS=-L/usr/X11/$(LIB) -lGL -lX11 -lXext -lm - TARGETS=$(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded - else # ifeq IRIX ############################################################################# @@ -529,7 +461,6 @@ else # ifeq IRIX ifeq ($(PLATFORM),SunOS) - GLIBC= #libc is irrelevant CC=gcc INSTALL=ginstall MKDIR=gmkdir @@ -560,9 +491,9 @@ ifeq ($(PLATFORM),SunOS) OPTIMIZE = -O3 -ffast-math -funroll-loops ifeq ($(ARCH),sparc) - OPTIMIZE = -O0 -ffast-math -falign-loops=2 \ + OPTIMIZE = -O3 -ffast-math -falign-loops=2 \ -falign-jumps=2 -falign-functions=2 -fstrength-reduce \ - -mtune=ultrasparc -mv8plus -munaligned-doubles \ + -mtune=ultrasparc -mv8plus -mno-faster-structs \ -funroll-loops BASE_CFLAGS += -DNO_VM_COMPILED else @@ -584,6 +515,8 @@ ifeq ($(PLATFORM),SunOS) THREAD_LDFLAGS=-lpthread LDFLAGS=-lsocket -lnsl -ldl -lm + BOTCFLAGS=-O0 + ifeq ($(USE_SDL),1) CLIENT_LDFLAGS=$(shell sdl-config --libs) -L/usr/X11/lib -lGLU -lX11 -lXext else @@ -596,31 +529,6 @@ ifeq ($(PLATFORM),SunOS) LDFLAGS+=-m32 endif - ifeq ($(ARCH),sparc) - TARGETS=\ - $(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm \ - $(B)/$(PLATFORM)tremulous-smp - else - TARGETS=\ - $(B)/$(PLATFORM)tremulous \ - $(B)/$(PLATFORM)tremded \ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/base/vm/cgame.qvm \ - $(B)/base/vm/qagame.qvm \ - $(B)/base/vm/ui.qvm \ - $(B)/$(PLATFORM)tremulous-smp - - endif - else # ifeq SunOS ############################################################################# @@ -635,12 +543,6 @@ else # ifeq SunOS SHLIBCFLAGS=-fPIC SHLIBLDFLAGS=-shared - TARGETS=\ - $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ - $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ - $(B)/base/ui$(ARCH).$(SHLIBEXT) \ - $(B)/$(PLATFORM)tremded - endif #Linux endif #darwin endif #mingw32 @@ -649,30 +551,38 @@ endif #NetBSD endif #IRIX endif #SunOS -ifeq ($(USE_CCACHE),1) - CC := ccache $(CC) +TARGETS = + +ifneq ($(BUILD_SERVER),0) + TARGETS += $(B)/tremded.$(ARCH)$(BINEXT) endif -ifneq ($(BUILD_SERVER),1) - TARGETS := $(subst $(B)/$(PLATFORM)tremded,,$(TARGETS)) +ifneq ($(BUILD_CLIENT),0) + TARGETS += $(B)/tremulous.$(ARCH)$(BINEXT) + ifneq ($(BUILD_CLIENT_SMP),0) + TARGETS += $(B)/tremulous.$(ARCH)$(BINEXT) + endif endif -ifneq ($(BUILD_CLIENT),1) - TARGETS := \ - $(subst $(B)/base/cgame$(ARCH).$(SHLIBEXT),,\ - $(subst $(B)/base/ui$(ARCH).$(SHLIBEXT),,\ - $(subst $(B)/base/vm/cgame.qvm,,\ - $(subst $(B)/base/vm/ui.qvm,,\ - $(subst $(B)/$(PLATFORM)tremulous-smp$(BINEXT),,\ - $(subst $(B)/$(PLATFORM)tremulous$(BINEXT),,$(TARGETS) )))))) +ifneq ($(BUILD_GAME_SO),0) + TARGETS += \ + $(B)/base/cgame$(ARCH).$(SHLIBEXT) \ + $(B)/base/qagame$(ARCH).$(SHLIBEXT) \ + $(B)/base/ui$(ARCH).$(SHLIBEXT) endif -# Never build qvms when cross-compiling -ifeq ($(CROSS_COMPILING),1) - TARGETS := \ - $(subst $(B)/base/vm/qagame.qvm,,\ - $(subst $(B)/base/vm/cgame.qvm,,\ - $(subst $(B)/base/vm/ui.qvm,,$(TARGETS) ))) +ifneq ($(BUILD_GAME_QVM),0) + ifneq ($(CROSS_COMPILING),1) + TARGETS += \ + $(B)/base/vm/cgame.qvm \ + $(B)/base/vm/qagame.qvm \ + $(B)/base/vm/ui.qvm \ + qvmdeps + endif +endif + +ifeq ($(USE_CCACHE),1) + CC := ccache $(CC) endif ifdef DEFAULT_BASEDIR @@ -691,7 +601,7 @@ endif DO_CC=$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) -o $@ -c $< DO_SMP_CC=$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) -DSMP -o $@ -c $< -DO_BOT_CC=$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) -DBOTLIB -o $@ -c $< # $(SHLIBCFLAGS) # bk001212 +DO_BOT_CC=$(CC) $(NOTSHLIBCFLAGS) $(CFLAGS) $(BOTCFLAGS) -DBOTLIB -o $@ -c $< # $(SHLIBCFLAGS) # bk001212 DO_DEBUG_CC=$(CC) $(NOTSHLIBCFLAGS) $(DEBUG_CFLAGS) -o $@ -c $< DO_SHLIB_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< DO_SHLIB_DEBUG_CC=$(CC) $(DEBUG_CFLAGS) $(SHLIBCFLAGS) -o $@ -c $< @@ -767,6 +677,7 @@ Q3OBJ = \ $(B)/client/cl_parse.o \ $(B)/client/cl_scrn.o \ $(B)/client/cl_ui.o \ + $(B)/client/cl_avi.o \ \ $(B)/client/cm_load.o \ $(B)/client/cm_patch.o \ @@ -901,12 +812,21 @@ Q3OBJ = \ $(B)/client/tr_surface.o \ $(B)/client/tr_world.o \ - ifeq ($(ARCH),i386) Q3OBJ += $(B)/client/vm_x86.o + Q3OBJ += \ + $(B)/client/snd_mixa.o \ + $(B)/client/matha.o \ + $(B)/client/ftola.o \ + $(B)/client/snapvectora.o endif ifeq ($(ARCH),x86) Q3OBJ += $(B)/client/vm_x86.o + Q3OBJ += \ + $(B)/client/snd_mixa.o \ + $(B)/client/matha.o \ + $(B)/client/ftola.o \ + $(B)/client/snapvectora.o endif ifeq ($(ARCH),x86_64) Q3OBJ += $(B)/client/vm_x86_64.o @@ -918,48 +838,9 @@ ifeq ($(ARCH),ppc) endif endif -#platform specific objects -ifeq ($(PLATFORM),freebsd) -ifeq ($(ARCH),axp) - Q3POBJ= -else - Q3POBJ=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ - $(B)/client/sdl_glimp.o \ - $(B)/client/linux_glimp.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o \ - $(B)/client/snd_mixa.o \ - $(B)/client/matha.o - ifeq ($(ARCH),i386) - Q3POBJ += $(B)/client/ftola.o $(B)/client/snapvectora.o - Q3POBJ_SMP += $(B)/client/ftola.o $(B)/client/snapvectora.o - endif - -endif # FreeBSD-axp -else -ifeq ($(PLATFORM),irix) - Q3POBJ=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/irix_qgl.o \ - $(B)/client/irix_glimp.o \ - $(B)/client/irix_snd.o -else ifeq ($(PLATFORM),mingw32) - Q3POBJ=\ - $(B)/client/linux_common.o \ - $(B)/client/snd_mixa.o \ - $(B)/client/matha.o \ - $(B)/client/ftola.o \ - $(B)/client/snapvectora.o \ + Q3OBJ += \ $(B)/client/win_gamma.o \ $(B)/client/win_glimp.o \ $(B)/client/win_input.o \ @@ -972,123 +853,38 @@ ifeq ($(PLATFORM),mingw32) $(B)/client/win_wndproc.o \ $(B)/client/win_resource.o else -ifeq ($(PLATFORM),linux) -ifeq ($(ARCH),axp) - Q3POBJ= -else - Q3POBJ=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ - $(B)/client/linux_glimp.o \ - $(B)/client/sdl_glimp.o \ - $(B)/client/linux_joystick.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o \ - $(B)/client/snd_mixa.o \ - $(B)/client/matha.o \ - - Q3POBJ_SMP=\ + Q3OBJ += \ $(B)/client/unix_main.o \ $(B)/client/unix_net.o \ $(B)/client/unix_shared.o \ $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ $(B)/client/linux_qgl.o \ - $(B)/client/linux_glimp_smp.o \ - $(B)/client/linux_joystick.o \ $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o \ - $(B)/client/snd_mixa.o \ - $(B)/client/matha.o + $(B)/client/sdl_snd.o - ifeq ($(ARCH),i386) - Q3POBJ += $(B)/client/ftola.o $(B)/client/snapvectora.o - Q3POBJ_SMP += $(B)/client/ftola.o $(B)/client/snapvectora.o + ifeq ($(PLATFORM),linux) + Q3OBJ += $(B)/client/linux_joystick.o endif -endif #Linux-axp - -else -ifeq ($(PLATFORM),darwin) - Q3POBJ=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ - $(B)/client/linux_glimp.o \ - $(B)/client/sdl_glimp.o \ - $(B)/client/linux_joystick.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o \ - - Q3POBJ_SMP=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ - $(B)/client/sdl_glimp_smp.o \ - $(B)/client/linux_joystick.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o \ - ifeq ($(ARCH),i386) - I386OBJS := \ - $(B)/client/ftola.o \ - $(B)/client/snapvectora.o \ - $(B)/client/snd_mixa.o \ - $(B)/client/matha.o \ - - Q3POBJ += $(I386OBJS) - Q3POBJ_SMP += $(I386OBJS) + ifeq ($(USE_SDL),1) + ifneq ($(PLATFORM),darwin) + BUILD_CLIENT_SMP = 0 + endif endif -else -ifeq ($(PLATFORM),SunOS) - Q3POBJ=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ + Q3POBJ = \ $(B)/client/linux_glimp.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o + $(B)/client/sdl_glimp.o - Q3POBJ_SMP=\ - $(B)/client/unix_main.o \ - $(B)/client/unix_net.o \ - $(B)/client/unix_shared.o \ - $(B)/client/linux_signals.o \ - $(B)/client/linux_common.o \ - $(B)/client/linux_qgl.o \ + Q3POBJ_SMP = \ $(B)/client/linux_glimp_smp.o \ - $(B)/client/linux_snd.o \ - $(B)/client/sdl_snd.o - - ifeq ($(ARCH),i386) - Q3POBJ += $(B)/client/ftola.o $(B)/client/snapvectora.o $(B)/client/snd_mixa.o $(B)/client/matha.o - Q3POBJ_SMP += $(B)/client/ftola.o $(B)/client/snapvectora.o - endif - -endif #SunOS -endif #Linux -endif #darwin -endif #mingw32 -endif #IRIX -endif #FreeBSD + $(B)/client/sdl_glimp_smp.o +endif -$(B)/$(PLATFORM)tremulous$(BINEXT): $(Q3OBJ) $(Q3POBJ) $(LIBSDLMAIN) +$(B)/tremulous.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ) $(LIBSDLMAIN) $(CC) -o $@ $(Q3OBJ) $(Q3POBJ) $(CLIENT_LDFLAGS) $(LDFLAGS) $(LIBSDLMAIN) -$(B)/$(PLATFORM)tremulous-smp$(BINEXT): $(Q3OBJ) $(Q3POBJ_SMP) $(LIBSDLMAIN) +$(B)/tremulous-smp.$(ARCH)$(BINEXT): $(Q3OBJ) $(Q3POBJ_SMP) $(LIBSDLMAIN) $(CC) -o $@ $(Q3OBJ) $(Q3POBJ_SMP) $(CLIENT_LDFLAGS) \ $(THREAD_LDFLAGS) $(LDFLAGS) $(LIBSDLMAIN) @@ -1110,6 +906,7 @@ $(B)/client/cl_net_chan.o : $(CDIR)/cl_net_chan.c; $(DO_CC) $(B)/client/cl_parse.o : $(CDIR)/cl_parse.c; $(DO_CC) $(B)/client/cl_scrn.o : $(CDIR)/cl_scrn.c; $(DO_CC) $(B)/client/cl_ui.o : $(CDIR)/cl_ui.c; $(DO_CC) +$(B)/client/cl_avi.o : $(CDIR)/cl_avi.c; $(DO_CC) $(B)/client/snd_adpcm.o : $(CDIR)/snd_adpcm.c; $(DO_CC) $(B)/client/snd_dma.o : $(CDIR)/snd_dma.c; $(DO_CC) $(B)/client/snd_mem.o : $(CDIR)/snd_mem.c; $(DO_CC) @@ -1249,7 +1046,6 @@ $(B)/client/irix_glimp_smp.o : $(UDIR)/irix_glimp.c; $(DO_SMP_CC) $(B)/client/irix_snd.o : $(UDIR)/irix_snd.c; $(DO_CC) $(B)/client/irix_input.o : $(UDIR)/irix_input.c; $(DO_CC) $(B)/client/linux_signals.o : $(UDIR)/linux_signals.c; $(DO_CC) $(GL_CFLAGS) -$(B)/client/linux_common.o : $(UDIR)/linux_common.c; $(DO_CC) $(B)/client/linux_glimp.o : $(UDIR)/linux_glimp.c; $(DO_CC) $(GL_CFLAGS) $(B)/client/sdl_glimp.o : $(UDIR)/sdl_glimp.c; $(DO_CC) $(GL_CFLAGS) $(B)/client/linux_glimp_smp.o : $(UDIR)/linux_glimp.c; $(DO_SMP_CC) $(GL_CFLAGS) @@ -1352,7 +1148,6 @@ Q3DOBJ = \ $(B)/ded/l_struct.o \ \ $(B)/ded/linux_signals.o \ - $(B)/ded/linux_common.o \ $(B)/ded/unix_main.o \ $(B)/ded/unix_net.o \ $(B)/ded/unix_shared.o \ @@ -1376,7 +1171,7 @@ ifeq ($(ARCH),ppc) endif endif -$(B)/$(PLATFORM)tremded$(BINEXT): $(Q3DOBJ) +$(B)/tremded.$(ARCH)$(BINEXT): $(Q3DOBJ) $(CC) -o $@ $(Q3DOBJ) $(LDFLAGS) $(B)/ded/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_DED_CC) @@ -1434,7 +1229,6 @@ $(B)/ded/l_script.o : $(BLIBDIR)/l_script.c; $(DO_BOT_CC) $(B)/ded/l_struct.o : $(BLIBDIR)/l_struct.c; $(DO_BOT_CC) $(B)/ded/linux_signals.o : $(UDIR)/linux_signals.c; $(DO_DED_CC) -$(B)/ded/linux_common.o : $(UDIR)/linux_common.c; $(DO_DED_CC) $(B)/ded/unix_main.o : $(UDIR)/unix_main.c; $(DO_DED_CC) $(B)/ded/unix_net.o : $(UDIR)/unix_net.c; $(DO_DED_CC) $(B)/ded/unix_shared.o : $(UDIR)/unix_shared.c; $(DO_DED_CC) @@ -1611,11 +1405,11 @@ $(B)/base/qcommon/%.asm: $(CMDIR)/%.c ############################################################################# copyfiles: build_release - @if [ ! -d $(COPYDIR)/base ]; then echo "You need to set COPYDIR to where you installed Quake III!"; false; fi - $(INSTALL) -s -m 0755 $(BR)/$(PLATFORM)tremulous$(BINEXT) $(COPYDIR)/tremulous + @if [ ! -d $(COPYDIR)/baseq3 ]; then echo "You need to set COPYDIR to where you installed Trem!"; false; fi + $(INSTALL) -s -m 0755 $(BR)/tremulous.$(ARCH)$(BINEXT) $(COPYDIR)/tremulous.$(ARCH)$(BINEXT) - @if [ -f $(BR)/$(PLATFORM)tremded$(BINEXT) ]; then \ - $(INSTALL) -s -m 0755 $(BR)/$(PLATFORM)tremded$(BINEXT) $(COPYDIR)/tremded + @if [ -f $(BR)/tremded.$(ARCH)$(BINEXT) ]; then \ + $(INSTALL) -s -m 0755 $(BR)/tremded.$(ARCH)$(BINEXT) $(COPYDIR)/tremded.$(ARCH)$(BINEXT); \ fi -$(MKDIR) -p -m 0755 $(COPYDIR)/base $(INSTALL) -s -m 0755 $(BR)/base/cgame$(ARCH).$(SHLIBEXT) \ @@ -1655,6 +1449,12 @@ installer: build_release D_FILES=$(shell find . -name '*.d') +$(B)/base/vm/vm.d: $(GOBJ) $(CGOBJ) $(UIOBJ) + -rm -f $@ + find $(B)/base -iname '*.d' | xargs sed -e 's/\.o/\.asm/g' > $@ + +qvmdeps: $(B)/base/vm/vm.d + ifneq ($(strip $(D_FILES)),) include $(D_FILES) endif diff --git a/src/unix/ftola.s b/src/unix/ftola.s index a84f6aae..2459021b 100644 --- a/src/unix/ftola.s +++ b/src/unix/ftola.s @@ -30,7 +30,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "qasm.h" -#ifdef id386 +#if id386 .data diff --git a/src/unix/linux_common.c b/src/unix/linux_common.c deleted file mode 100644 index d7f31189..00000000 --- a/src/unix/linux_common.c +++ /dev/null @@ -1,347 +0,0 @@ -#if 0 // not used anymore -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2006 Tim Angus - -This file is part of Tremulous. - -Tremulous 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. - -Tremulous 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 Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/** - * GAS syntax equivalents of the MSVC asm memory calls in common.c - * - * The following changes have been made to the asm: - * 1. Registers are loaded by the inline asm arguments when possible - * 2. Labels have been changed to local label format (0,1,etc.) to allow inlining - * - * HISTORY: - * AH - Created on 08 Dec 2000 - */ - -#include <unistd.h> // AH - for size_t -#include <string.h> - -// bk001207 - we need something under Linux, too. Mac? -#if 1 // defined(C_ONLY) // bk010102 - dedicated? -void Com_Memcpy (void* dest, const void* src, const size_t count) { - memcpy(dest, src, count); -} - -void Com_Memset (void* dest, const int val, const size_t count) { - memset(dest, val, count); -} - -#else - -typedef enum { - PRE_READ, // prefetch assuming that buffer is used for reading only - PRE_WRITE, // prefetch assuming that buffer is used for writing only - PRE_READ_WRITE // prefetch assuming that buffer is used for both reading and writing -} e_prefetch; - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type); - -void _copyDWord (unsigned int* dest, const unsigned int constant, const unsigned int count) { - // MMX version not used on standard Pentium MMX - // because the dword version is faster (with - // proper destination prefetching) - __asm__ __volatile__ (" \ - //mov eax,constant // eax = val \ - //mov edx,dest // dest \ - //mov ecx,count \ - movd %%eax, %%mm0 \ - punpckldq %%mm0, %%mm0 \ -\ - // ensure that destination is qword aligned \ -\ - testl $7, %%edx // qword padding?\ - jz 0f \ - movl %%eax, (%%edx) \ - decl %%ecx \ - addl $4, %%edx \ -\ -0: movl %%ecx, %%ebx \ - andl $0xfffffff0, %%ecx \ - jz 2f \ - jmp 1f \ - .align 16 \ -\ - // funny ordering here to avoid commands \ - // that cross 32-byte boundaries (the \ - // [edx+0] version has a special 3-byte opcode... \ -1: movq %%mm0, 8(%%edx) \ - movq %%mm0, 16(%%edx) \ - movq %%mm0, 24(%%edx) \ - movq %%mm0, 32(%%edx) \ - movq %%mm0, 40(%%edx) \ - movq %%mm0, 48(%%edx) \ - movq %%mm0, 56(%%edx) \ - movq %%mm0, (%%edx)\ - addl $64, %%edx \ - subl $16, %%ecx \ - jnz 1b \ -2: \ - movl %%ebx, %%ecx // ebx = cnt \ - andl $0xfffffff0, %%ecx // ecx = cnt&~15 \ - subl %%ecx, %%ebx \ - jz 6f \ - cmpl $8, %%ebx \ - jl 3f \ -\ - movq %%mm0, (%%edx) \ - movq %%mm0, 8(%%edx) \ - movq %%mm0, 16(%%edx) \ - movq %%mm0, 24(%%edx) \ - addl $32, %%edx \ - subl $8, %%ebx \ - jz 6f \ -\ -3: cmpl $4, %%ebx \ - jl 4f \ - \ - movq %%mm0, (%%edx) \ - movq %%mm0, 8(%%edx) \ - addl $16, %%edx \ - subl $4, %%ebx \ -\ -4: cmpl $2, %%ebx \ - jl 5f \ - movq %%mm0, (%%edx) \ - addl $8, %%edx \ - subl $2, %%ebx \ -\ -5: cmpl $1, %%ebx \ - jl 6f \ - movl %%eax, (%%edx) \ -6: \ - emms \ - " - : : "a" (constant), "c" (count), "d" (dest) - : "%ebx", "%edi", "%esi", "cc", "memory"); -} - -// optimized memory copy routine that handles all alignment -// cases and block sizes efficiently -void Com_Memcpy (void* dest, const void* src, const size_t count) { - Com_Prefetch (src, count, PRE_READ); - __asm__ __volatile__ (" \ - pushl %%edi \ - pushl %%esi \ - //mov ecx,count \ - cmpl $0, %%ecx // count = 0 check (just to be on the safe side) \ - je 6f \ - //mov edx,dest \ - movl %0, %%ebx \ - cmpl $32, %%ecx // padding only? \ - jl 1f \ -\ - movl %%ecx, %%edi \ - andl $0xfffffe00, %%edi // edi = count&~31 \ - subl $32, %%edi \ -\ - .align 16 \ -0: \ - movl (%%ebx, %%edi, 1), %%eax \ - movl 4(%%ebx, %%edi, 1), %%esi \ - movl %%eax, (%%edx, %%edi, 1) \ - movl %%esi, 4(%%edx, %%edi, 1) \ - movl 8(%%ebx, %%edi, 1), %%eax \ - movl 12(%%ebx, %%edi, 1), %%esi \ - movl %%eax, 8(%%edx, %%edi, 1) \ - movl %%esi, 12(%%edx, %%edi, 1) \ - movl 16(%%ebx, %%edi, 1), %%eax \ - movl 20(%%ebx, %%edi, 1), %%esi \ - movl %%eax, 16(%%edx, %%edi, 1) \ - movl %%esi, 20(%%edx, %%edi, 1) \ - movl 24(%%ebx, %%edi, 1), %%eax \ - movl 28(%%ebx, %%edi, 1), %%esi \ - movl %%eax, 24(%%edx, %%edi, 1) \ - movl %%esi, 28(%%edx, %%edi, 1) \ - subl $32, %%edi \ - jge 0b \ - \ - movl %%ecx, %%edi \ - andl $0xfffffe00, %%edi \ - addl %%edi, %%ebx // increase src pointer \ - addl %%edi, %%edx // increase dst pointer \ - andl $31, %%ecx // new count \ - jz 6f // if count = 0, get outta here \ -\ -1: \ - cmpl $16, %%ecx \ - jl 2f \ - movl (%%ebx), %%eax \ - movl %%eax, (%%edx) \ - movl 4(%%ebx), %%eax \ - movl %%eax, 4(%%edx) \ - movl 8(%%ebx), %%eax \ - movl %%eax, 8(%%edx) \ - movl 12(%%ebx), %%eax \ - movl %%eax, 12(%%edx) \ - subl $16, %%ecx \ - addl $16, %%ebx \ - addl $16, %%edx \ -2: \ - cmpl $8, %%ecx \ - jl 3f \ - movl (%%ebx), %%eax \ - movl %%eax, (%%edx) \ - movl 4(%%ebx), %%eax \ - subl $8, %%ecx \ - movl %%eax, 4(%%edx) \ - addl $8, %%ebx \ - addl $8, %%edx \ -3: \ - cmpl $4, %%ecx \ - jl 4f \ - movl (%%ebx), %%eax // here 4-7 bytes \ - addl $4, %%ebx \ - subl $4, %%ecx \ - movl %%eax, (%%edx) \ - addl $4, %%edx \ -4: // 0-3 remaining bytes \ - cmpl $2, %%ecx \ - jl 5f \ - movw (%%ebx), %%ax // two bytes \ - cmpl $3, %%ecx // less than 3? \ - movw %%ax, (%%edx) \ - jl 6f \ - movb 2(%%ebx), %%al // last byte \ - movb %%al, 2(%%edx) \ - jmp 6f \ -5: \ - cmpl $1, %%ecx \ - jl 6f \ - movb (%%ebx), %%al \ - movb %%al, (%%edx) \ -6: \ - popl %%esi \ - popl %%edi \ - " - : : "m" (src), "d" (dest), "c" (count) - : "%eax", "%ebx", "%edi", "%esi", "cc", "memory"); -} - -void Com_Memset (void* dest, const int val, const size_t count) -{ - unsigned int fillval; - - if (count < 8) - { - __asm__ __volatile__ (" \ - //mov edx,dest \ - //mov eax, val \ - movb %%al, %%ah \ - movl %%eax, %%ebx \ - andl $0xffff, %%ebx \ - shll $16, %%eax \ - addl %%ebx, %%eax // eax now contains pattern \ - //mov ecx,count \ - cmpl $4, %%ecx \ - jl 0f \ - movl %%eax, (%%edx) // copy first dword \ - addl $4, %%edx \ - subl $4, %%ecx \ - 0: cmpl $2, %%ecx \ - jl 1f \ - movw %%ax, (%%edx) // copy 2 bytes \ - addl $2, %%edx \ - subl $2, %%ecx \ - 1: cmpl $0, %%ecx \ - je 2f \ - movb %%al, (%%edx) // copy single byte \ - 2: \ - " - : : "d" (dest), "a" (val), "c" (count) - : "%ebx", "%edi", "%esi", "cc", "memory"); - - return; - } - - fillval = val; - - fillval = fillval|(fillval<<8); - fillval = fillval|(fillval<<16); // fill dword with 8-bit pattern - - _copyDWord ((unsigned int*)(dest),fillval, count/4); - - __asm__ __volatile__ (" // padding of 0-3 bytes \ - //mov ecx,count \ - movl %%ecx, %%eax \ - andl $3, %%ecx \ - jz 1f \ - andl $0xffffff00, %%eax \ - //mov ebx,dest \ - addl %%eax, %%edx \ - movl %0, %%eax \ - cmpl $2, %%ecx \ - jl 0f \ - movw %%ax, (%%edx) \ - cmpl $2, %%ecx \ - je 1f \ - movb %%al, 2(%%edx) \ - jmp 1f \ -0: \ - cmpl $0, %%ecx\ - je 1f\ - movb %%al, (%%edx)\ -1: \ - " - : : "m" (fillval), "c" (count), "d" (dest) - : "%eax", "%ebx", "%edi", "%esi", "cc", "memory"); -} - -void Com_Prefetch (const void *s, const unsigned int bytes, e_prefetch type) -{ - // write buffer prefetching is performed only if - // the processor benefits from it. Read and read/write - // prefetching is always performed. - - switch (type) - { - case PRE_WRITE : break; - case PRE_READ: - case PRE_READ_WRITE: - - __asm__ __volatile__ ("\ - //mov ebx,s\ - //mov ecx,bytes\ - cmpl $4096, %%ecx // clamp to 4kB\ - jle 0f\ - movl $4096, %%ecx\ - 0:\ - addl $0x1f, %%ecx\ - shrl $5, %%ecx // number of cache lines\ - jz 2f\ - jmp 1f\ -\ - .align 16\ - 1: testb %%al, (%%edx)\ - addl $32, %%edx\ - decl %%ecx\ - jnz 1b\ - 2:\ - " - : : "d" (s), "c" (bytes) - : "%eax", "%ebx", "%edi", "%esi", "memory", "cc"); - - break; - } -} - -#endif -#endif |