First version.

This commit is contained in:
Paradox460 committed 2008-01-05 04:20:33 +00:00
commit ef1c2cc347
473 files changed
+305366

No files matched your search

+664
View File
@@ -0,0 +1,664 @@
/*
===========================================================================
Copyright (C) 2005-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
===========================================================================
*/
#include "client.h"
#include "snd_local.h"
#define INDEX_FILE_EXTENSION ".index.dat"
#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 - 4 );
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_4BYTES( 0 ); // BI_RGB
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" );
WRITE_4BYTES( afd.width *
afd.height ); //biSizeImage
} else {
WRITE_4BYTES( 0 ); // BI_RGB
WRITE_4BYTES( afd.width *
afd.height*3 ); //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_aviFrameRate->integer <= 0 )
{
Com_Printf( S_COLOR_RED "cl_aviFrameRate must be >= 1\n" );
return qfalse;
}
if( ( afd.f = FS_FOpenFileWrite( fileName ) ) <= 0 )
return qfalse;
if( ( afd.idxF = FS_FOpenFileWrite(
va( "%s" INDEX_FILE_EXTENSION, fileName ) ) ) <= 0 )
{
FS_FCloseFile( afd.f );
return qfalse;
}
Q_strncpyz( afd.fileName, fileName, MAX_QPATH );
afd.frameRate = cl_aviFrameRate->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_aviFrameRate 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_CheckFileSize
===============
*/
static qboolean CL_CheckFileSize( int bytesToAdd )
{
unsigned int newFileSize;
newFileSize =
afd.fileSize + // Current file size
bytesToAdd + // What we want to add
( afd.numIndices * 16 ) + // The index
4; // The index size
// I assume all the operating systems
// we target can handle a 2Gb file
if( newFileSize > INT_MAX )
{
// Close the current file...
CL_CloseAVI( );
// ...And open a new one
CL_OpenAVIForWriting( va( "%s_", afd.fileName ) );
return qtrue;
}
return qfalse;
}
/*
===============
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;
// Chunk header + contents + padding
if( CL_CheckFileSize( 8 + size + 2 ) )
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( 0x00000010 ); //dwFlags (all frames are KeyFrames)
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;
// Chunk header + contents + padding
if( CL_CheckFileSize( 8 + bytesInBuffer + size + 2 ) )
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( (float)afd.a.rate / (float)afd.frameRate ) *
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" INDEX_FILE_EXTENSION, 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;
}
File diff suppressed because it is too large. Load diff
+1658
View File
File diff suppressed because it is too large. Load diff
+701
View File
@@ -0,0 +1,701 @@
/*
===========================================================================
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
===========================================================================
*/
// console.c
#include "client.h"
int g_console_field_width = 78;
#define NUM_CON_TIMES 4
#define CON_TEXTSIZE 32768
typedef struct {
qboolean initialized;
short text[CON_TEXTSIZE];
int current; // line where next message will be printed
int x; // offset in current line for next print
int display; // bottom of console displays this line
int linewidth; // characters across screen
int totallines; // total lines in console scrollback
float xadjust; // for wide aspect screens
float displayFrac; // aproaches finalFrac at scr_conspeed
float finalFrac; // 0.0 to 1.0 lines of console to display
int vislines; // in scanlines
vec4_t color;
} console_t;
extern console_t con;
console_t con;
cvar_t *con_conspeed;
#define DEFAULT_CONSOLE_WIDTH 78
vec4_t console_color = {1.0, 1.0, 1.0, 1.0};
/*
================
Con_ToggleConsole_f
================
*/
void Con_ToggleConsole_f (void) {
// closing a full screen console restarts the demo loop
if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) {
CL_StartDemoLoop();
return;
}
Field_Clear( &g_consoleField );
g_consoleField.widthInChars = g_console_field_width;
cls.keyCatchers ^= KEYCATCH_CONSOLE;
}
/*
================
Con_MessageMode_f
================
*/
void Con_MessageMode_f (void) {
chat_playerNum = -1;
chat_team = qfalse;
Field_Clear( &chatField );
chatField.widthInChars = 30;
cls.keyCatchers ^= KEYCATCH_MESSAGE;
}
/*
================
Con_MessageMode2_f
================
*/
void Con_MessageMode2_f (void) {
chat_playerNum = -1;
chat_team = qtrue;
Field_Clear( &chatField );
chatField.widthInChars = 25;
cls.keyCatchers ^= KEYCATCH_MESSAGE;
}
/*
================
Con_MessageMode3_f
================
*/
void Con_MessageMode3_f (void) {
chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
chat_playerNum = -1;
return;
}
chat_team = qfalse;
Field_Clear( &chatField );
chatField.widthInChars = 30;
cls.keyCatchers ^= KEYCATCH_MESSAGE;
}
/*
================
Con_MessageMode4_f
================
*/
void Con_MessageMode4_f (void) {
chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER );
if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
chat_playerNum = -1;
return;
}
chat_team = qfalse;
Field_Clear( &chatField );
chatField.widthInChars = 30;
cls.keyCatchers ^= KEYCATCH_MESSAGE;
}
/*
================
Con_Clear_f
================
*/
void Con_Clear_f (void) {
int i;
for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
}
Con_Bottom(); // go to end
}
/*
================
Con_Dump_f
Save the console contents out to a file
================
*/
void Con_Dump_f (void)
{
int l, x, i;
short *line;
fileHandle_t f;
char buffer[1024];
if (Cmd_Argc() != 2)
{
Com_Printf ("usage: condump <filename>\n");
return;
}
Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) );
f = FS_FOpenFileWrite( Cmd_Argv( 1 ) );
if (!f)
{
Com_Printf ("ERROR: couldn't open.\n");
return;
}
// skip empty lines
for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
{
line = con.text + (l%con.totallines)*con.linewidth;
for (x=0 ; x<con.linewidth ; x++)
if ((line[x] & 0xff) != ' ')
break;
if (x != con.linewidth)
break;
}
// write the remaining lines
buffer[con.linewidth] = 0;
for ( ; l <= con.current ; l++)
{
line = con.text + (l%con.totallines)*con.linewidth;
for(i=0; i<con.linewidth; i++)
buffer[i] = line[i] & 0xff;
for (x=con.linewidth-1 ; x>=0 ; x--)
{
if (buffer[x] == ' ')
buffer[x] = 0;
else
break;
}
strcat( buffer, "\n" );
FS_Write(buffer, strlen(buffer), f);
}
FS_FCloseFile( f );
}
/*
================
Con_ClearNotify
================
*/
void Con_ClearNotify( void ) {
Cmd_TokenizeString( NULL );
CL_GameConsoleText( );
}
/*
================
Con_CheckResize
If the line width has changed, reformat the buffer.
================
*/
void Con_CheckResize (void)
{
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
short tbuf[CON_TEXTSIZE];
width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2;
if (width == con.linewidth)
return;
if (width < 1) // video hasn't been initialized yet
{
width = DEFAULT_CONSOLE_WIDTH;
con.linewidth = width;
con.totallines = CON_TEXTSIZE / con.linewidth;
for(i=0; i<CON_TEXTSIZE; i++)
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
}
else
{
oldwidth = con.linewidth;
con.linewidth = width;
oldtotallines = con.totallines;
con.totallines = CON_TEXTSIZE / con.linewidth;
numlines = oldtotallines;
if (con.totallines < numlines)
numlines = con.totallines;
numchars = oldwidth;
if (con.linewidth < numchars)
numchars = con.linewidth;
Com_Memcpy (tbuf, con.text, CON_TEXTSIZE * sizeof(short));
for(i=0; i<CON_TEXTSIZE; i++)
con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
for (i=0 ; i<numlines ; i++)
{
for (j=0 ; j<numchars ; j++)
{
con.text[(con.totallines - 1 - i) * con.linewidth + j] =
tbuf[((con.current - i + oldtotallines) %
oldtotallines) * oldwidth + j];
}
}
}
con.current = con.totallines - 1;
con.display = con.current;
}
/*
================
Con_Init
================
*/
void Con_Init (void) {
int i;
con_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
Field_Clear( &g_consoleField );
g_consoleField.widthInChars = g_console_field_width;
for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
Field_Clear( &historyEditLines[i] );
historyEditLines[i].widthInChars = g_console_field_width;
}
CL_LoadConsoleHistory( );
Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
Cmd_AddCommand ("messagemode", Con_MessageMode_f);
Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
Cmd_AddCommand ("messagemode3", Con_MessageMode3_f);
Cmd_AddCommand ("messagemode4", Con_MessageMode4_f);
Cmd_AddCommand ("clear", Con_Clear_f);
Cmd_AddCommand ("condump", Con_Dump_f);
}
/*
===============
Con_Linefeed
===============
*/
void Con_Linefeed (qboolean skipnotify)
{
int i;
con.x = 0;
if (con.display == con.current)
con.display++;
con.current++;
for(i=0; i<con.linewidth; i++)
con.text[(con.current%con.totallines)*con.linewidth+i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
}
/*
================
CL_ConsolePrint
Handles cursor positioning, line wrapping, etc
All console printing must go through this in order to be logged to disk
If no console is visible, the text will appear at the top of the game window
================
*/
void CL_ConsolePrint( char *txt ) {
int y;
int c, l;
int color;
qboolean skipnotify = qfalse; // NERVE - SMF
// TTimo - prefix for text that shows up in console but not in notify
// backported from RTCW
if ( !Q_strncmp( txt, "[skipnotify]", 12 ) ) {
skipnotify = qtrue;
txt += 12;
}
// for some demos we don't want to ever show anything on the console
if ( cl_noprint && cl_noprint->integer ) {
return;
}
if (!con.initialized) {
con.color[0] =
con.color[1] =
con.color[2] =
con.color[3] = 1.0f;
con.linewidth = -1;
Con_CheckResize ();
con.initialized = qtrue;
}
if( !skipnotify && !( cls.keyCatchers & KEYCATCH_CONSOLE ) ) {
Cmd_SaveCmdContext( );
// feed the text to cgame
Cmd_TokenizeString( txt );
CL_GameConsoleText( );
Cmd_RestoreCmdContext( );
}
color = ColorIndex(COLOR_WHITE);
while ( (c = *txt) != 0 ) {
if ( Q_IsColorString( txt ) ) {
color = ColorIndex( *(txt+1) );
txt += 2;
continue;
}
// count word length
for (l=0 ; l< con.linewidth ; l++) {
if ( txt[l] <= ' ') {
break;
}
}
// word wrap
if (l != con.linewidth && (con.x + l >= con.linewidth) ) {
Con_Linefeed(skipnotify);
}
txt++;
switch (c)
{
case '\n':
Con_Linefeed (skipnotify);
break;
case '\r':
con.x = 0;
break;
default: // display character and advance
y = con.current % con.totallines;
con.text[y*con.linewidth+con.x] = (color << 8) | c;
con.x++;
if (con.x >= con.linewidth) {
Con_Linefeed(skipnotify);
con.x = 0;
}
break;
}
}
}
/*
==============================================================================
DRAWING
==============================================================================
*/
/*
================
Con_DrawInput
Draw the editline after a ] prompt
================
*/
void Con_DrawInput (void) {
int y;
if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) {
return;
}
y = con.vislines - ( SMALLCHAR_HEIGHT * 2 );
re.SetColor( con.color );
SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' );
Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y,
SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue );
}
/*
================
Con_DrawSolidConsole
Draws the console with the solid background
================
*/
void Con_DrawSolidConsole( float frac ) {
int i, x, y;
int rows;
short *text;
int row;
int lines;
// qhandle_t conShader;
int currentColor;
vec4_t color;
lines = cls.glconfig.vidHeight * frac;
if (lines <= 0)
return;
if (lines > cls.glconfig.vidHeight )
lines = cls.glconfig.vidHeight;
// on wide screens, we will center the text
con.xadjust = 0;
SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL );
// draw the background
y = frac * SCREEN_HEIGHT - 2;
if ( y < 1 ) {
y = 0;
}
else {
SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader );
}
color[0] = 1;
color[1] = 0;
color[2] = 0;
color[3] = 1;
SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color );
// draw the version number
re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
i = strlen( SVN_VERSION );
for (x=0 ; x<i ; x++) {
SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH,
(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), SVN_VERSION[x] );
}
// draw the text
con.vislines = lines;
rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH; // rows of text to draw
y = lines - (SMALLCHAR_HEIGHT*3);
// draw from the bottom up
if (con.display != con.current)
{
// draw arrows to show the buffer is backscrolled
re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
for (x=0 ; x<con.linewidth ; x+=4)
SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, '^' );
y -= SMALLCHAR_HEIGHT;
rows--;
}
row = con.display;
if ( con.x == 0 ) {
row--;
}
currentColor = 7;
re.SetColor( g_color_table[currentColor] );
for (i=0 ; i<rows ; i++, y -= SMALLCHAR_HEIGHT, row--)
{
if (row < 0)
break;
if (con.current - row >= con.totallines) {
// past scrollback wrap point
continue;
}
text = con.text + (row % con.totallines)*con.linewidth;
for (x=0 ; x<con.linewidth ; x++) {
if ( ( text[x] & 0xff ) == ' ' ) {
continue;
}
if ( ( (text[x]>>8)&7 ) != currentColor ) {
currentColor = (text[x]>>8)&7;
re.SetColor( g_color_table[currentColor] );
}
SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff );
}
}
// draw the input prompt, user text, and cursor if desired
Con_DrawInput ();
re.SetColor( NULL );
}
/*
==================
Con_DrawConsole
==================
*/
void Con_DrawConsole( void ) {
// check for console width changes from a vid mode change
Con_CheckResize ();
// if disconnected, render console full screen
if ( cls.state == CA_DISCONNECTED ) {
if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) {
Con_DrawSolidConsole( 1.0 );
return;
}
}
if ( con.displayFrac ) {
Con_DrawSolidConsole( con.displayFrac );
}
if( cls.keyCatchers & ( KEYCATCH_UI | KEYCATCH_CGAME ) )
return;
// draw the chat line
if( cls.keyCatchers & KEYCATCH_MESSAGE )
{
int skip;
if( chat_team )
{
SCR_DrawBigString( 8, 232, "Team Say:", 1.0f );
skip = 11;
}
else
{
SCR_DrawBigString( 8, 232, "Say:", 1.0f );
skip = 5;
}
Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, 232,
SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue );
}
}
//================================================================
/*
==================
Con_RunConsole
Scroll it up or down
==================
*/
void Con_RunConsole (void) {
// decide on the destination height of the console
if ( cls.keyCatchers & KEYCATCH_CONSOLE )
con.finalFrac = 0.5; // half screen
else
con.finalFrac = 0; // none visible
// scroll towards the destination height
if (con.finalFrac < con.displayFrac)
{
con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001;
if (con.finalFrac > con.displayFrac)
con.displayFrac = con.finalFrac;
}
else if (con.finalFrac > con.displayFrac)
{
con.displayFrac += con_conspeed->value*cls.realFrametime*0.001;
if (con.finalFrac < con.displayFrac)
con.displayFrac = con.finalFrac;
}
}
void Con_PageUp( void ) {
con.display -= 2;
if ( con.current - con.display >= con.totallines ) {
con.display = con.current - con.totallines + 1;
}
}
void Con_PageDown( void ) {
con.display += 2;
if (con.display > con.current) {
con.display = con.current;
}
}
void Con_Top( void ) {
con.display = con.totallines;
if ( con.current - con.display >= con.totallines ) {
con.display = con.current - con.totallines + 1;
}
}
void Con_Bottom( void ) {
con.display = con.current;
}
void Con_Close( void ) {
if ( !com_cl_running->integer ) {
return;
}
Field_Clear( &g_consoleField );
cls.keyCatchers &= ~KEYCATCH_CONSOLE;
con.finalFrac = 0; // none visible
con.displayFrac = 0;
}
+360
View File
@@ -0,0 +1,360 @@
/*
===========================================================================
Copyright (C) 2006 Tony J. White (tjw@tjw.org)
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
===========================================================================
*/
#if USE_CURL
#include "client.h"
cvar_t *cl_cURLLib;
#if USE_CURL_DLOPEN
#if USE_SDL_VIDEO
#include "SDL.h"
#include "SDL_loadso.h"
#define OBJTYPE void *
#define OBJLOAD(x) SDL_LoadObject(x)
#define SYMLOAD(x,y) SDL_LoadFunction(x,y)
#define OBJFREE(x) SDL_UnloadObject(x)
#elif defined _WIN32
#include <windows.h>
#define OBJTYPE HMODULE
#define OBJLOAD(x) LoadLibrary(x)
#define SYMLOAD(x,y) GetProcAddress(x,y)
#define OBJFREE(x) FreeLibrary(x)
#elif defined __linux__ || defined __FreeBSD__ || defined MACOS_X || defined __sun
#include <dlfcn.h>
#define OBJTYPE void *
#define OBJLOAD(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL)
#define SYMLOAD(x,y) dlsym(x,y)
#define OBJFREE(x) dlclose(x)
#else
#error "Your platform has no lib loading code or it is disabled"
#endif
#if defined __linux__ || defined __FreeBSD__ || defined MACOS_X
#include <unistd.h>
#include <sys/types.h>
#endif
char* (*qcurl_version)(void);
CURL* (*qcurl_easy_init)(void);
CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...);
CURLcode (*qcurl_easy_perform)(CURL *curl);
void (*qcurl_easy_cleanup)(CURL *curl);
CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...);
CURL* (*qcurl_easy_duphandle)(CURL *curl);
void (*qcurl_easy_reset)(CURL *curl);
const char *(*qcurl_easy_strerror)(CURLcode);
CURLM* (*qcurl_multi_init)(void);
CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle,
CURL *curl_handle);
CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle,
CURL *curl_handle);
CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle,
int *running_handles);
CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle);
CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle,
int *msgs_in_queue);
const char *(*qcurl_multi_strerror)(CURLMcode);
static OBJTYPE cURLLib = NULL;
/*
=================
GPA
=================
*/
static void *GPA(char *str)
{
void *rv;
rv = SYMLOAD(cURLLib, str);
if(!rv)
{
Com_Printf("Can't load symbol %s\n", str);
clc.cURLEnabled = qfalse;
return NULL;
}
else
{
Com_DPrintf("Loaded symbol %s (0x%p)\n", str, rv);
return rv;
}
}
#endif /* USE_CURL_DLOPEN */
/*
=================
CL_cURL_Init
=================
*/
qboolean CL_cURL_Init()
{
#if USE_CURL_DLOPEN
if(cURLLib)
return qtrue;
Com_Printf("Loading \"%s\"...", cl_cURLLib->string);
if( (cURLLib = OBJLOAD(cl_cURLLib->string)) == 0 )
{
#ifdef _WIN32
return qfalse;
#else
char fn[1024];
getcwd(fn, sizeof(fn));
strncat(fn, "/", sizeof(fn)-strlen(fn)-1);
strncat(fn, cl_cURLLib->string, sizeof(fn)-strlen(fn)-1);
if( (cURLLib = OBJLOAD(fn)) == 0 )
{
return qfalse;
}
#endif /* _WIN32 */
}
clc.cURLEnabled = qtrue;
qcurl_version = GPA("curl_version");
qcurl_easy_init = GPA("curl_easy_init");
qcurl_easy_setopt = GPA("curl_easy_setopt");
qcurl_easy_perform = GPA("curl_easy_perform");
qcurl_easy_cleanup = GPA("curl_easy_cleanup");
qcurl_easy_getinfo = GPA("curl_easy_getinfo");
qcurl_easy_duphandle = GPA("curl_easy_duphandle");
qcurl_easy_reset = GPA("curl_easy_reset");
qcurl_easy_strerror = GPA("curl_easy_strerror");
qcurl_multi_init = GPA("curl_multi_init");
qcurl_multi_add_handle = GPA("curl_multi_add_handle");
qcurl_multi_remove_handle = GPA("curl_multi_remove_handle");
qcurl_multi_fdset = GPA("curl_multi_fdset");
qcurl_multi_perform = GPA("curl_multi_perform");
qcurl_multi_cleanup = GPA("curl_multi_cleanup");
qcurl_multi_info_read = GPA("curl_multi_info_read");
qcurl_multi_strerror = GPA("curl_multi_strerror");
if(!clc.cURLEnabled)
{
CL_cURL_Shutdown();
Com_Printf("FAIL One or more symbols not found\n");
return qfalse;
}
Com_Printf("OK\n");
return qtrue;
#else
clc.cURLEnabled = qtrue;
return qtrue;
#endif /* USE_CURL_DLOPEN */
}
/*
=================
CL_cURL_Shutdown
=================
*/
void CL_cURL_Shutdown( void )
{
CL_cURL_Cleanup();
#if USE_CURL_DLOPEN
if(cURLLib)
{
OBJFREE(cURLLib);
cURLLib = NULL;
}
qcurl_easy_init = NULL;
qcurl_easy_setopt = NULL;
qcurl_easy_perform = NULL;
qcurl_easy_cleanup = NULL;
qcurl_easy_getinfo = NULL;
qcurl_easy_duphandle = NULL;
qcurl_easy_reset = NULL;
qcurl_multi_init = NULL;
qcurl_multi_add_handle = NULL;
qcurl_multi_remove_handle = NULL;
qcurl_multi_fdset = NULL;
qcurl_multi_perform = NULL;
qcurl_multi_cleanup = NULL;
qcurl_multi_info_read = NULL;
qcurl_multi_strerror = NULL;
#endif /* USE_CURL_DLOPEN */
}
void CL_cURL_Cleanup(void)
{
if(clc.downloadCURLM) {
if(clc.downloadCURL) {
qcurl_multi_remove_handle(clc.downloadCURLM,
clc.downloadCURL);
qcurl_easy_cleanup(clc.downloadCURL);
}
qcurl_multi_cleanup(clc.downloadCURLM);
clc.downloadCURLM = NULL;
clc.downloadCURL = NULL;
}
else if(clc.downloadCURL) {
qcurl_easy_cleanup(clc.downloadCURL);
clc.downloadCURL = NULL;
}
}
static int CL_cURL_CallbackProgress( void *dummy, double dltotal, double dlnow,
double ultotal, double ulnow )
{
clc.downloadSize = (int)dltotal;
Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
clc.downloadCount = (int)dlnow;
Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
return 0;
}
static int CL_cURL_CallbackWrite(void *buffer, size_t size, size_t nmemb,
void *stream)
{
FS_Write( buffer, size*nmemb, ((fileHandle_t*)stream)[0] );
return size*nmemb;
}
void CL_cURL_BeginDownload( const char *localName, const char *remoteURL )
{
clc.cURLUsed = qtrue;
Com_Printf("URL: %s\n", remoteURL);
Com_DPrintf("***** CL_cURL_BeginDownload *****\n"
"Localname: %s\n"
"RemoteURL: %s\n"
"****************************\n", localName, remoteURL);
CL_cURL_Cleanup();
Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL));
Q_strncpyz(clc.downloadName, localName, sizeof(clc.downloadName));
Com_sprintf(clc.downloadTempName, sizeof(clc.downloadTempName),
"%s.tmp", localName);
// Set so UI gets access to it
Cvar_Set("cl_downloadName", localName);
Cvar_Set("cl_downloadSize", "0");
Cvar_Set("cl_downloadCount", "0");
Cvar_SetValue("cl_downloadTime", cls.realtime);
clc.downloadBlock = 0; // Starting new file
clc.downloadCount = 0;
clc.downloadCURL = qcurl_easy_init();
if(!clc.downloadCURL) {
Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_easy_init() "
"failed\n");
return;
}
clc.download = FS_SV_FOpenFileWrite(clc.downloadTempName);
if(!clc.download) {
Com_Error(ERR_DROP, "CL_cURL_BeginDownload: failed to open "
"%s for writing\n", clc.downloadTempName);
return;
}
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, clc.download);
if(com_developer->integer)
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER, va("ioQ3://%s",
NET_AdrToString(clc.serverAddress)));
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s",
Q3_VERSION, qcurl_version()));
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION,
CL_cURL_CallbackWrite);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION,
CL_cURL_CallbackProgress);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1);
qcurl_easy_setopt(clc.downloadCURL, CURLOPT_MAXREDIRS, 5);
clc.downloadCURLM = qcurl_multi_init();
if(!clc.downloadCURLM) {
qcurl_easy_cleanup(clc.downloadCURL);
clc.downloadCURL = NULL;
Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_multi_init() "
"failed\n");
return;
}
qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL);
if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) &&
!clc.cURLDisconnected) {
CL_AddReliableCommand("disconnect");
CL_WritePacket();
CL_WritePacket();
CL_WritePacket();
clc.cURLDisconnected = qtrue;
}
}
void CL_cURL_PerformDownload(void)
{
CURLMcode res;
CURLMsg *msg;
int c;
int i = 0;
res = qcurl_multi_perform(clc.downloadCURLM, &c);
while(res == CURLM_CALL_MULTI_PERFORM && i < 100) {
res = qcurl_multi_perform(clc.downloadCURLM, &c);
i++;
}
if(res == CURLM_CALL_MULTI_PERFORM)
return;
msg = qcurl_multi_info_read(clc.downloadCURLM, &c);
if(msg == NULL) {
return;
}
FS_FCloseFile(clc.download);
if(msg->msg == CURLMSG_DONE && msg->data.result == CURLE_OK) {
FS_SV_Rename(clc.downloadTempName, clc.downloadName);
clc.downloadRestart = qtrue;
}
else {
long code;
qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE,
&code);
Com_Error(ERR_DROP, "Download Error: %s Code: %ld URL: %s",
qcurl_easy_strerror(msg->data.result),
code, clc.downloadURL);
}
*clc.downloadTempName = *clc.downloadName = 0;
Cvar_Set( "cl_downloadName", "" );
CL_NextDownload();
}
#endif /* USE_CURL */
+101
View File
@@ -0,0 +1,101 @@
/*
===========================================================================
Copyright (C) 2006 Tony J. White (tjw@tjw.org)
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
===========================================================================
*/
#ifndef __QCURL_H__
#define __QCURL_H__
extern cvar_t *cl_cURLLib;
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#ifdef WIN32
#define DEFAULT_CURL_LIB "libcurl-3.dll"
#elif defined(MACOS_X)
#define DEFAULT_CURL_LIB "libcurl.dylib"
#else
#define DEFAULT_CURL_LIB "libcurl.so.3"
#endif
#if USE_LOCAL_HEADERS
#include "../libcurl/curl/curl.h"
#else
#include <curl/curl.h>
#endif
#if USE_CURL_DLOPEN
extern char* (*qcurl_version)(void);
extern CURL* (*qcurl_easy_init)(void);
extern CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...);
extern CURLcode (*qcurl_easy_perform)(CURL *curl);
extern void (*qcurl_easy_cleanup)(CURL *curl);
extern CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...);
extern void (*qcurl_easy_reset)(CURL *curl);
extern const char *(*qcurl_easy_strerror)(CURLcode);
extern CURLM* (*qcurl_multi_init)(void);
extern CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle,
CURL *curl_handle);
extern CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle,
CURL *curl_handle);
extern CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
extern CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle,
int *running_handles);
extern CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle);
extern CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle,
int *msgs_in_queue);
extern const char *(*qcurl_multi_strerror)(CURLMcode);
#else
#define qcurl_version curl_version
#define qcurl_easy_init curl_easy_init
#define qcurl_easy_setopt curl_easy_setopt
#define qcurl_easy_perform curl_easy_perform
#define qcurl_easy_cleanup curl_easy_cleanup
#define qcurl_easy_getinfo curl_easy_getinfo
#define qcurl_easy_duphandle curl_easy_duphandle
#define qcurl_easy_reset curl_easy_reset
#define qcurl_easy_strerror curl_easy_strerror
#define qcurl_multi_init curl_multi_init
#define qcurl_multi_add_handle curl_multi_add_handle
#define qcurl_multi_remove_handle curl_multi_remove_handle
#define qcurl_multi_fdset curl_multi_fdset
#define qcurl_multi_perform curl_multi_perform
#define qcurl_multi_cleanup curl_multi_cleanup
#define qcurl_multi_info_read curl_multi_info_read
#define qcurl_multi_strerror curl_multi_strerror
#endif
qboolean CL_cURL_Init( void );
void CL_cURL_Shutdown( void );
void CL_cURL_BeginDownload( const char *localName, const char *remoteURL );
void CL_cURL_PerformDownload( void );
void CL_cURL_Cleanup( void );
#endif // __QCURL_H__
+903
View File
@@ -0,0 +1,903 @@
/*
===========================================================================
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
===========================================================================
*/
// cl.input.c -- builds an intended movement command to send to the server
#include "client.h"
unsigned frame_msec;
int old_com_frameTime;
/*
===============================================================================
KEY BUTTONS
Continuous button event tracking is complicated by the fact that two different
input sources (say, mouse button 1 and the control key) can both press the
same button, but the button should only be released when both of the
pressing key have been released.
When a key event issues a button command (+forward, +attack, etc), it appends
its key number as argv(1) so it can be matched up with the release.
argv(2) will be set to the time the event happened, which allows exact
control even at low framerates when the down and up events may both get qued
at the same time.
===============================================================================
*/
kbutton_t in_left, in_right, in_forward, in_back;
kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
kbutton_t in_strafe, in_speed;
kbutton_t in_up, in_down;
kbutton_t in_buttons[16];
qboolean in_mlooking;
void IN_MLookDown( void ) {
in_mlooking = qtrue;
}
void IN_MLookUp( void ) {
in_mlooking = qfalse;
if ( !cl_freelook->integer ) {
IN_CenterView ();
}
}
void IN_KeyDown( kbutton_t *b ) {
int k;
char *c;
c = Cmd_Argv(1);
if ( c[0] ) {
k = atoi(c);
} else {
k = -1; // typed manually at the console for continuous down
}
if ( k == b->down[0] || k == b->down[1] ) {
return; // repeating key
}
if ( !b->down[0] ) {
b->down[0] = k;
} else if ( !b->down[1] ) {
b->down[1] = k;
} else {
Com_Printf ("Three keys down for a button!\n");
return;
}
if ( b->active ) {
return; // still down
}
// save timestamp for partial frame summing
c = Cmd_Argv(2);
b->downtime = atoi(c);
b->active = qtrue;
b->wasPressed = qtrue;
}
void IN_KeyUp( kbutton_t *b ) {
int k;
char *c;
unsigned uptime;
c = Cmd_Argv(1);
if ( c[0] ) {
k = atoi(c);
} else {
// typed manually at the console, assume for unsticking, so clear all
b->down[0] = b->down[1] = 0;
b->active = qfalse;
return;
}
if ( b->down[0] == k ) {
b->down[0] = 0;
} else if ( b->down[1] == k ) {
b->down[1] = 0;
} else {
return; // key up without coresponding down (menu pass through)
}
if ( b->down[0] || b->down[1] ) {
return; // some other key is still holding it down
}
b->active = qfalse;
// save timestamp for partial frame summing
c = Cmd_Argv(2);
uptime = atoi(c);
if ( uptime ) {
b->msec += uptime - b->downtime;
} else {
b->msec += frame_msec / 2;
}
b->active = qfalse;
}
/*
===============
CL_KeyState
Returns the fraction of the frame that the key was down
===============
*/
float CL_KeyState( kbutton_t *key ) {
float val;
int msec;
msec = key->msec;
key->msec = 0;
if ( key->active ) {
// still down
if ( !key->downtime ) {
msec = com_frameTime;
} else {
msec += com_frameTime - key->downtime;
}
key->downtime = com_frameTime;
}
#if 0
if (msec) {
Com_Printf ("%i ", msec);
}
#endif
val = (float)msec / frame_msec;
if ( val < 0 ) {
val = 0;
}
if ( val > 1 ) {
val = 1;
}
return val;
}
void IN_UpDown(void) {IN_KeyDown(&in_up);}
void IN_UpUp(void) {IN_KeyUp(&in_up);}
void IN_DownDown(void) {IN_KeyDown(&in_down);}
void IN_DownUp(void) {IN_KeyUp(&in_down);}
void IN_LeftDown(void) {IN_KeyDown(&in_left);}
void IN_LeftUp(void) {IN_KeyUp(&in_left);}
void IN_RightDown(void) {IN_KeyDown(&in_right);}
void IN_RightUp(void) {IN_KeyUp(&in_right);}
void IN_ForwardDown(void) {IN_KeyDown(&in_forward);}
void IN_ForwardUp(void) {IN_KeyUp(&in_forward);}
void IN_BackDown(void) {IN_KeyDown(&in_back);}
void IN_BackUp(void) {IN_KeyUp(&in_back);}
void IN_LookupDown(void) {IN_KeyDown(&in_lookup);}
void IN_LookupUp(void) {IN_KeyUp(&in_lookup);}
void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);}
void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);}
void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);}
void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);}
void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);}
void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);}
void IN_SpeedDown(void) {IN_KeyDown(&in_speed);}
void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);}
void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);}
void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);}
void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);}
void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);}
void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);}
void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);}
void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);}
void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);}
void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);}
void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);}
void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);}
void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);}
void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);}
void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);}
void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);}
void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);}
void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);}
void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);}
void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);}
void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);}
void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);}
void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);}
void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);}
void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);}
void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);}
void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);}
void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);}
void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);}
void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);}
void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);}
void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);}
void IN_ButtonDown (void) {
IN_KeyDown(&in_buttons[1]);}
void IN_ButtonUp (void) {
IN_KeyUp(&in_buttons[1]);}
void IN_CenterView (void) {
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]);
}
//==========================================================================
cvar_t *cl_upspeed;
cvar_t *cl_forwardspeed;
cvar_t *cl_sidespeed;
cvar_t *cl_yawspeed;
cvar_t *cl_pitchspeed;
cvar_t *cl_run;
cvar_t *cl_anglespeedkey;
/*
================
CL_AdjustAngles
Moves the local angle positions
================
*/
void CL_AdjustAngles( void ) {
float speed;
if ( in_speed.active ) {
speed = 0.001 * cls.frametime * cl_anglespeedkey->value;
} else {
speed = 0.001 * cls.frametime;
}
if ( !in_strafe.active ) {
cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
}
cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup);
cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown);
}
/*
================
CL_KeyMove
Sets the usercmd_t based on key states
================
*/
void CL_KeyMove( usercmd_t *cmd ) {
int movespeed;
int forward, side, up;
//
// adjust for speed key / running
// the walking flag is to keep animations consistant
// even during acceleration and develeration
//
if ( in_speed.active ^ cl_run->integer ) {
movespeed = 127;
cmd->buttons &= ~BUTTON_WALKING;
} else {
cmd->buttons |= BUTTON_WALKING;
movespeed = 64;
}
forward = 0;
side = 0;
up = 0;
if ( in_strafe.active ) {
side += movespeed * CL_KeyState (&in_right);
side -= movespeed * CL_KeyState (&in_left);
}
side += movespeed * CL_KeyState (&in_moveright);
side -= movespeed * CL_KeyState (&in_moveleft);
up += movespeed * CL_KeyState (&in_up);
up -= movespeed * CL_KeyState (&in_down);
forward += movespeed * CL_KeyState (&in_forward);
forward -= movespeed * CL_KeyState (&in_back);
cmd->forwardmove = ClampChar( forward );
cmd->rightmove = ClampChar( side );
cmd->upmove = ClampChar( up );
}
/*
=================
CL_MouseEvent
=================
*/
void CL_MouseEvent( int dx, int dy, int time ) {
if ( cls.keyCatchers & KEYCATCH_UI ) {
VM_Call( uivm, UI_MOUSE_EVENT, dx, dy );
} else if (cls.keyCatchers & KEYCATCH_CGAME) {
VM_Call (cgvm, CG_MOUSE_EVENT, dx, dy);
} else {
cl.mouseDx[cl.mouseIndex] += dx;
cl.mouseDy[cl.mouseIndex] += dy;
}
}
/*
=================
CL_JoystickEvent
Joystick values stay set until changed
=================
*/
void CL_JoystickEvent( int axis, int value, int time ) {
if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) {
Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis );
}
cl.joystickAxis[axis] = value;
}
/*
=================
CL_JoystickMove
=================
*/
void CL_JoystickMove( usercmd_t *cmd ) {
int movespeed;
float anglespeed;
if ( in_speed.active ^ cl_run->integer ) {
movespeed = 2;
} else {
movespeed = 1;
cmd->buttons |= BUTTON_WALKING;
}
if ( in_speed.active ) {
anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value;
} else {
anglespeed = 0.001 * cls.frametime;
}
if ( !in_strafe.active ) {
cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE];
} else {
cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] );
}
if ( in_mlooking ) {
cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD];
} else {
cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] );
}
cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] );
}
/*
=================
CL_MouseMove
=================
*/
void CL_MouseMove( usercmd_t *cmd ) {
float mx, my;
float accelSensitivity;
float rate;
// allow mouse smoothing
if ( m_filter->integer ) {
mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5;
my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5;
} else {
mx = cl.mouseDx[cl.mouseIndex];
my = cl.mouseDy[cl.mouseIndex];
}
cl.mouseIndex ^= 1;
cl.mouseDx[cl.mouseIndex] = 0;
cl.mouseDy[cl.mouseIndex] = 0;
rate = sqrt( mx * mx + my * my ) / (float)frame_msec;
accelSensitivity = ( cl_sensitivity->value *
cl_platformSensitivity->value ) + rate * cl_mouseAccel->value;
// scale by FOV
accelSensitivity *= cl.cgameSensitivity;
if ( rate && cl_showMouseRate->integer ) {
Com_Printf( "%f : %f\n", rate, accelSensitivity );
}
mx *= accelSensitivity;
my *= accelSensitivity;
if (!mx && !my) {
return;
}
// add mouse X/Y movement to cmd
if ( in_strafe.active ) {
cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx );
} else {
cl.viewangles[YAW] -= m_yaw->value * mx;
}
if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) {
cl.viewangles[PITCH] += m_pitch->value * my;
} else {
cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my );
}
}
/*
==============
CL_CmdButtons
==============
*/
void CL_CmdButtons( usercmd_t *cmd ) {
int i;
//
// figure button bits
// send a button bit even if the key was pressed and released in
// less than a frame
//
for (i = 0 ; i < 15 ; i++) {
if ( in_buttons[i].active || in_buttons[i].wasPressed ) {
cmd->buttons |= 1 << i;
}
in_buttons[i].wasPressed = qfalse;
}
if ( cls.keyCatchers ) {
cmd->buttons |= BUTTON_TALK;
}
// allow the game to know if any key at all is
// currently pressed, even if it isn't bound to anything
if ( anykeydown && !cls.keyCatchers ) {
cmd->buttons |= BUTTON_ANY;
}
}
/*
==============
CL_FinishMove
==============
*/
void CL_FinishMove( usercmd_t *cmd ) {
int i;
// copy the state that the cgame is currently sending
cmd->weapon = cl.cgameUserCmdValue;
// send the current server time so the amount of movement
// can be determined without allowing cheating
cmd->serverTime = cl.serverTime;
for (i=0 ; i<3 ; i++) {
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
}
}
/*
=================
CL_CreateCmd
=================
*/
usercmd_t CL_CreateCmd( void ) {
usercmd_t cmd;
vec3_t oldAngles;
VectorCopy( cl.viewangles, oldAngles );
// keyboard angle adjustment
CL_AdjustAngles ();
Com_Memset( &cmd, 0, sizeof( cmd ) );
CL_CmdButtons( &cmd );
// get basic movement from keyboard
CL_KeyMove( &cmd );
// get basic movement from mouse
CL_MouseMove( &cmd );
// get basic movement from joystick
CL_JoystickMove( &cmd );
// check to make sure the angles haven't wrapped
if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
cl.viewangles[PITCH] = oldAngles[PITCH] + 90;
} else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) {
cl.viewangles[PITCH] = oldAngles[PITCH] - 90;
}
// store out the final values
CL_FinishMove( &cmd );
// draw debug graphs of turning for mouse testing
if ( cl_debugMove->integer ) {
if ( cl_debugMove->integer == 1 ) {
SCR_DebugGraph( abs(cl.viewangles[YAW] - oldAngles[YAW]), 0 );
}
if ( cl_debugMove->integer == 2 ) {
SCR_DebugGraph( abs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 );
}
}
return cmd;
}
/*
=================
CL_CreateNewCommands
Create a new usercmd_t structure for this frame
=================
*/
void CL_CreateNewCommands( void ) {
usercmd_t *cmd;
int cmdNum;
// no need to create usercmds until we have a gamestate
if ( cls.state < CA_PRIMED ) {
return;
}
frame_msec = com_frameTime - old_com_frameTime;
// if running less than 5fps, truncate the extra time to prevent
// unexpected moves after a hitch
if ( frame_msec > 200 ) {
frame_msec = 200;
}
old_com_frameTime = com_frameTime;
// generate a command for this frame
cl.cmdNumber++;
cmdNum = cl.cmdNumber & CMD_MASK;
cl.cmds[cmdNum] = CL_CreateCmd ();
cmd = &cl.cmds[cmdNum];
}
/*
=================
CL_ReadyToSendPacket
Returns qfalse if we are over the maxpackets limit
and should choke back the bandwidth a bit by not sending
a packet this frame. All the commands will still get
delivered in the next packet, but saving a header and
getting more delta compression will reduce total bandwidth.
=================
*/
qboolean CL_ReadyToSendPacket( void ) {
int oldPacketNum;
int delta;
// don't send anything if playing back a demo
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
return qfalse;
}
// If we are downloading, we send no less than 50ms between packets
if ( *clc.downloadTempName &&
cls.realtime - clc.lastPacketSentTime < 50 ) {
return qfalse;
}
// if we don't have a valid gamestate yet, only send
// one packet a second
if ( cls.state != CA_ACTIVE &&
cls.state != CA_PRIMED &&
!*clc.downloadTempName &&
cls.realtime - clc.lastPacketSentTime < 1000 ) {
return qfalse;
}
// send every frame for loopbacks
if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) {
return qtrue;
}
// send every frame for LAN
if ( cl_lanForcePackets->integer && Sys_IsLANAddress( clc.netchan.remoteAddress ) ) {
return qtrue;
}
// check for exceeding cl_maxpackets
if ( cl_maxpackets->integer < 15 ) {
Cvar_Set( "cl_maxpackets", "15" );
} else if ( cl_maxpackets->integer > 125 ) {
Cvar_Set( "cl_maxpackets", "125" );
}
oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK;
delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime;
if ( delta < 1000 / cl_maxpackets->integer ) {
// the accumulated commands will go out in the next packet
return qfalse;
}
return qtrue;
}
/*
===================
CL_WritePacket
Create and send the command packet to the server
Including both the reliable commands and the usercmds
During normal gameplay, a client packet will contain something like:
4 sequence number
2 qport
4 serverid
4 acknowledged sequence number
4 clc.serverCommandSequence
<optional reliable commands>
1 clc_move or clc_moveNoDelta
1 command count
<count * usercmds>
===================
*/
void CL_WritePacket( void ) {
msg_t buf;
byte data[MAX_MSGLEN];
int i, j;
usercmd_t *cmd, *oldcmd;
usercmd_t nullcmd;
int packetNum;
int oldPacketNum;
int count, key;
// don't send anything if playing back a demo
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
return;
}
Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
oldcmd = &nullcmd;
MSG_Init( &buf, data, sizeof(data) );
MSG_Bitstream( &buf );
// write the current serverId so the server
// can tell if this is from the current gameState
MSG_WriteLong( &buf, cl.serverId );
// write the last message we received, which can
// be used for delta compression, and is also used
// to tell if we dropped a gamestate
MSG_WriteLong( &buf, clc.serverMessageSequence );
// write the last reliable message we received
MSG_WriteLong( &buf, clc.serverCommandSequence );
// write any unacknowledged clientCommands
for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) {
MSG_WriteByte( &buf, clc_clientCommand );
MSG_WriteLong( &buf, i );
MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
}
// we want to send all the usercmds that were generated in the last
// few packet, so even if a couple packets are dropped in a row,
// all the cmds will make it to the server
if ( cl_packetdup->integer < 0 ) {
Cvar_Set( "cl_packetdup", "0" );
} else if ( cl_packetdup->integer > 5 ) {
Cvar_Set( "cl_packetdup", "5" );
}
oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK;
count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber;
if ( count > MAX_PACKET_USERCMDS ) {
count = MAX_PACKET_USERCMDS;
Com_Printf("MAX_PACKET_USERCMDS\n");
}
if ( count >= 1 ) {
if ( cl_showSend->integer ) {
Com_Printf( "(%i)", count );
}
// begin a client move command
if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting
|| clc.serverMessageSequence != cl.snap.messageNum ) {
MSG_WriteByte (&buf, clc_moveNoDelta);
} else {
MSG_WriteByte (&buf, clc_move);
}
// write the command count
MSG_WriteByte( &buf, count );
// use the checksum feed in the key
key = clc.checksumFeed;
// also use the message acknowledge
key ^= clc.serverMessageSequence;
// also use the last acknowledged server command in the key
key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32);
// write all the commands, including the predicted command
for ( i = 0 ; i < count ; i++ ) {
j = (cl.cmdNumber - count + i + 1) & CMD_MASK;
cmd = &cl.cmds[j];
MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd);
oldcmd = cmd;
}
}
//
// deliver the message
//
packetNum = clc.netchan.outgoingSequence & PACKET_MASK;
cl.outPackets[ packetNum ].p_realtime = cls.realtime;
cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime;
cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber;
clc.lastPacketSentTime = cls.realtime;
if ( cl_showSend->integer ) {
Com_Printf( "%i ", buf.cursize );
}
CL_Netchan_Transmit (&clc.netchan, &buf);
// clients never really should have messages large enough
// to fragment, but in case they do, fire them all off
// at once
// TTimo: this causes a packet burst, which is bad karma for winsock
// added a WARNING message, we'll see if there are legit situations where this happens
while ( clc.netchan.unsentFragments ) {
Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" );
CL_Netchan_TransmitNextFragment( &clc.netchan );
}
}
/*
=================
CL_SendCmd
Called every frame to builds and sends a command packet to the server.
=================
*/
void CL_SendCmd( void ) {
// don't send any message if not connected
if ( cls.state < CA_CONNECTED ) {
return;
}
// don't send commands if paused
if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) {
return;
}
// we create commands even if a demo is playing,
CL_CreateNewCommands();
// don't send a packet if the last packet was sent too recently
if ( !CL_ReadyToSendPacket() ) {
if ( cl_showSend->integer ) {
Com_Printf( ". " );
}
return;
}
CL_WritePacket();
}
/*
============
CL_InitInput
============
*/
void CL_InitInput( void ) {
Cmd_AddCommand ("centerview",IN_CenterView);
Cmd_AddCommand ("+moveup",IN_UpDown);
Cmd_AddCommand ("-moveup",IN_UpUp);
Cmd_AddCommand ("+movedown",IN_DownDown);
Cmd_AddCommand ("-movedown",IN_DownUp);
Cmd_AddCommand ("+left",IN_LeftDown);
Cmd_AddCommand ("-left",IN_LeftUp);
Cmd_AddCommand ("+right",IN_RightDown);
Cmd_AddCommand ("-right",IN_RightUp);
Cmd_AddCommand ("+forward",IN_ForwardDown);
Cmd_AddCommand ("-forward",IN_ForwardUp);
Cmd_AddCommand ("+back",IN_BackDown);
Cmd_AddCommand ("-back",IN_BackUp);
Cmd_AddCommand ("+lookup", IN_LookupDown);
Cmd_AddCommand ("-lookup", IN_LookupUp);
Cmd_AddCommand ("+lookdown", IN_LookdownDown);
Cmd_AddCommand ("-lookdown", IN_LookdownUp);
Cmd_AddCommand ("+strafe", IN_StrafeDown);
Cmd_AddCommand ("-strafe", IN_StrafeUp);
Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
Cmd_AddCommand ("+moveright", IN_MoverightDown);
Cmd_AddCommand ("-moveright", IN_MoverightUp);
Cmd_AddCommand ("+speed", IN_SpeedDown);
Cmd_AddCommand ("-speed", IN_SpeedUp);
Cmd_AddCommand ("+attack", IN_Button0Down);
Cmd_AddCommand ("-attack", IN_Button0Up);
Cmd_AddCommand ("+button0", IN_Button0Down);
Cmd_AddCommand ("-button0", IN_Button0Up);
Cmd_AddCommand ("+button1", IN_Button1Down);
Cmd_AddCommand ("-button1", IN_Button1Up);
Cmd_AddCommand ("+button2", IN_Button2Down);
Cmd_AddCommand ("-button2", IN_Button2Up);
Cmd_AddCommand ("+button3", IN_Button3Down);
Cmd_AddCommand ("-button3", IN_Button3Up);
Cmd_AddCommand ("+button4", IN_Button4Down);
Cmd_AddCommand ("-button4", IN_Button4Up);
Cmd_AddCommand ("+button5", IN_Button5Down);
Cmd_AddCommand ("-button5", IN_Button5Up);
Cmd_AddCommand ("+button6", IN_Button6Down);
Cmd_AddCommand ("-button6", IN_Button6Up);
Cmd_AddCommand ("+button7", IN_Button7Down);
Cmd_AddCommand ("-button7", IN_Button7Up);
Cmd_AddCommand ("+button8", IN_Button8Down);
Cmd_AddCommand ("-button8", IN_Button8Up);
Cmd_AddCommand ("+button9", IN_Button9Down);
Cmd_AddCommand ("-button9", IN_Button9Up);
Cmd_AddCommand ("+button10", IN_Button10Down);
Cmd_AddCommand ("-button10", IN_Button10Up);
Cmd_AddCommand ("+button11", IN_Button11Down);
Cmd_AddCommand ("-button11", IN_Button11Up);
Cmd_AddCommand ("+button12", IN_Button12Down);
Cmd_AddCommand ("-button12", IN_Button12Up);
Cmd_AddCommand ("+button13", IN_Button13Down);
Cmd_AddCommand ("-button13", IN_Button13Up);
Cmd_AddCommand ("+button14", IN_Button14Down);
Cmd_AddCommand ("-button14", IN_Button14Up);
Cmd_AddCommand ("+mlook", IN_MLookDown);
Cmd_AddCommand ("-mlook", IN_MLookUp);
cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
cl_debugMove = Cvar_Get ("cl_debugMove", "0", 0);
}
+1549
View File
File diff suppressed because it is too large. Load diff
+3534
View File
File diff suppressed because it is too large. Load diff
+168
View File
@@ -0,0 +1,168 @@
/*
===========================================================================
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
===========================================================================
*/
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "client.h"
/*
==============
CL_Netchan_Encode
// first 12 bytes of the data are always:
long serverId;
long messageAcknowledge;
long reliableAcknowledge;
==============
*/
static void CL_Netchan_Encode( msg_t *msg ) {
int serverId, messageAcknowledge, reliableAcknowledge;
int i, index, srdc, sbit, soob;
byte key, *string;
if ( msg->cursize <= CL_ENCODE_START ) {
return;
}
srdc = msg->readcount;
sbit = msg->bit;
soob = msg->oob;
msg->bit = 0;
msg->readcount = 0;
msg->oob = 0;
serverId = MSG_ReadLong(msg);
messageAcknowledge = MSG_ReadLong(msg);
reliableAcknowledge = MSG_ReadLong(msg);
msg->oob = soob;
msg->bit = sbit;
msg->readcount = srdc;
string = (byte *)clc.serverCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
index = 0;
//
key = clc.challenge ^ serverId ^ messageAcknowledge;
for (i = CL_ENCODE_START; i < msg->cursize; i++) {
// modify the key with the last received now acknowledged server command
if (!string[index])
index = 0;
if (string[index] > 127 || string[index] == '%') {
key ^= '.' << (i & 1);
}
else {
key ^= string[index] << (i & 1);
}
index++;
// encode the data with this key
*(msg->data + i) = (*(msg->data + i)) ^ key;
}
}
/*
==============
CL_Netchan_Decode
// first four bytes of the data are always:
long reliableAcknowledge;
==============
*/
static void CL_Netchan_Decode( msg_t *msg ) {
long reliableAcknowledge, i, index;
byte key, *string;
int srdc, sbit, soob;
srdc = msg->readcount;
sbit = msg->bit;
soob = msg->oob;
msg->oob = 0;
reliableAcknowledge = MSG_ReadLong(msg);
msg->oob = soob;
msg->bit = sbit;
msg->readcount = srdc;
string = (byte *) clc.reliableCommands[ reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ];
index = 0;
// xor the client challenge with the netchan sequence number (need something that changes every message)
key = clc.challenge ^ LittleLong( *(unsigned *)msg->data );
for (i = msg->readcount + CL_DECODE_START; i < msg->cursize; i++) {
// modify the key with the last sent and with this message acknowledged client command
if (!string[index])
index = 0;
if (string[index] > 127 || string[index] == '%') {
key ^= '.' << (i & 1);
}
else {
key ^= string[index] << (i & 1);
}
index++;
// decode the data with this key
*(msg->data + i) = *(msg->data + i) ^ key;
}
}
/*
=================
CL_Netchan_TransmitNextFragment
=================
*/
void CL_Netchan_TransmitNextFragment( netchan_t *chan ) {
Netchan_TransmitNextFragment( chan );
}
/*
===============
CL_Netchan_Transmit
================
*/
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) {
MSG_WriteByte( msg, clc_EOF );
CL_Netchan_Encode( msg );
Netchan_Transmit( chan, msg->cursize, msg->data );
}
extern int oldsize;
int newsize = 0;
/*
=================
CL_Netchan_Process
=================
*/
qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) {
int ret;
ret = Netchan_Process( chan, msg );
if (!ret)
return qfalse;
CL_Netchan_Decode( msg );
newsize += msg->cursize;
return qtrue;
}
+722
View File
@@ -0,0 +1,722 @@
/*
===========================================================================
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
===========================================================================
*/
// cl_parse.c -- parse a message received from the server
#include "client.h"
char *svc_strings[256] = {
"svc_bad",
"svc_nop",
"svc_gamestate",
"svc_configstring",
"svc_baseline",
"svc_serverCommand",
"svc_download",
"svc_snapshot"
};
void SHOWNET( msg_t *msg, char *s) {
if ( cl_shownet->integer >= 2) {
Com_Printf ("%3i:%s\n", msg->readcount-1, s);
}
}
/*
=========================================================================
MESSAGE PARSING
=========================================================================
*/
/*
==================
CL_DeltaEntity
Parses deltas from the given base and adds the resulting entity
to the current frame
==================
*/
void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
qboolean unchanged) {
entityState_t *state;
// save the parsed entity state into the big circular buffer so
// it can be used as the source for a later delta
state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
if ( unchanged ) {
*state = *old;
} else {
MSG_ReadDeltaEntity( msg, old, state, newnum );
}
if ( state->number == (MAX_GENTITIES-1) ) {
return; // entity was delta removed
}
cl.parseEntitiesNum++;
frame->numEntities++;
}
/*
==================
CL_ParsePacketEntities
==================
*/
void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
int newnum;
entityState_t *oldstate;
int oldindex, oldnum;
newframe->parseEntitiesNum = cl.parseEntitiesNum;
newframe->numEntities = 0;
// delta from the entities present in oldframe
oldindex = 0;
oldstate = NULL;
if (!oldframe) {
oldnum = 99999;
} else {
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
while ( 1 ) {
// read the entity index number
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
if ( newnum == (MAX_GENTITIES-1) ) {
break;
}
if ( msg->readcount > msg->cursize ) {
Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
}
while ( oldnum < newnum ) {
// one or more entities from the old packet are unchanged
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
}
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
if (oldnum == newnum) {
// delta from previous state
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum);
}
CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
continue;
}
if ( oldnum > newnum ) {
// delta from baseline
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum);
}
CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
continue;
}
}
// any remaining entities in the old frame are copied over
while ( oldnum != 99999 ) {
// one or more entities from the old packet are unchanged
if ( cl_shownet->integer == 3 ) {
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
}
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
oldindex++;
if ( oldindex >= oldframe->numEntities ) {
oldnum = 99999;
} else {
oldstate = &cl.parseEntities[
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
oldnum = oldstate->number;
}
}
}
/*
================
CL_ParseSnapshot
If the snapshot is parsed properly, it will be copied to
cl.snap and saved in cl.snapshots[]. If the snapshot is invalid
for any reason, no changes to the state will be made at all.
================
*/
void CL_ParseSnapshot( msg_t *msg ) {
int len;
clSnapshot_t *old;
clSnapshot_t newSnap;
int deltaNum;
int oldMessageNum;
int i, packetNum;
// get the reliable sequence acknowledge number
// NOTE: now sent with all server to client messages
//clc.reliableAcknowledge = MSG_ReadLong( msg );
// read in the new snapshot to a temporary buffer
// we will only copy to cl.snap if it is valid
Com_Memset (&newSnap, 0, sizeof(newSnap));
// we will have read any new server commands in this
// message before we got to svc_snapshot
newSnap.serverCommandNum = clc.serverCommandSequence;
newSnap.serverTime = MSG_ReadLong( msg );
// if we were just unpaused, we can only *now* really let the
// change come into effect or the client hangs.
cl_paused->modified = 0;
newSnap.messageNum = clc.serverMessageSequence;
deltaNum = MSG_ReadByte( msg );
if ( !deltaNum ) {
newSnap.deltaNum = -1;
} else {
newSnap.deltaNum = newSnap.messageNum - deltaNum;
}
newSnap.snapFlags = MSG_ReadByte( msg );
// If the frame is delta compressed from data that we
// no longer have available, we must suck up the rest of
// the frame, but not use it, then ask for a non-compressed
// message
if ( newSnap.deltaNum <= 0 ) {
newSnap.valid = qtrue; // uncompressed frame
old = NULL;
clc.demowaiting = qfalse; // we can start recording now
} else {
old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
if ( !old->valid ) {
// should never happen
Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
} else if ( old->messageNum != newSnap.deltaNum ) {
// The frame that the server did the delta from
// is too old, so we can't reconstruct it properly.
Com_Printf ("Delta frame too old.\n");
} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
Com_Printf ("Delta parseEntitiesNum too old.\n");
} else {
newSnap.valid = qtrue; // valid delta parse
}
}
// read areamask
len = MSG_ReadByte( msg );
if(len > sizeof(newSnap.areamask))
{
Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask.", len);
return;
}
MSG_ReadData( msg, &newSnap.areamask, len);
// read playerinfo
SHOWNET( msg, "playerstate" );
if ( old ) {
MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
} else {
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
}
// read packet entities
SHOWNET( msg, "packet entities" );
CL_ParsePacketEntities( msg, old, &newSnap );
// if not valid, dump the entire thing now that it has
// been properly read
if ( !newSnap.valid ) {
return;
}
// clear the valid flags of any snapshots between the last
// received and this one, so if there was a dropped packet
// it won't look like something valid to delta from next
// time we wrap around in the buffer
oldMessageNum = cl.snap.messageNum + 1;
if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
}
for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
}
// copy to the current good spot
cl.snap = newSnap;
cl.snap.ping = 999;
// calculate ping time
for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
break;
}
}
// save the frame off in the backup array for later delta comparisons
cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
if (cl_shownet->integer == 3) {
Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum,
cl.snap.deltaNum, cl.snap.ping );
}
cl.newSnapshots = qtrue;
}
//=====================================================================
int cl_connectedToPureServer;
int cl_connectedToCheatServer;
/*
==================
CL_SystemInfoChanged
The systeminfo configstring has been changed, so parse
new information out of it. This will happen at every
gamestate, and possibly during gameplay.
==================
*/
void CL_SystemInfoChanged( void ) {
char *systemInfo;
const char *s, *t;
char key[BIG_INFO_KEY];
char value[BIG_INFO_VALUE];
qboolean gameSet;
systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
// NOTE TTimo:
// when the serverId changes, any further messages we send to the server will use this new serverId
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
// in some cases, outdated cp commands might get sent with this news serverId
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
// don't set any vars when playing a demo
if ( clc.demoplaying ) {
return;
}
s = Info_ValueForKey( systemInfo, "sv_cheats" );
cl_connectedToCheatServer = atoi( s );
if ( !cl_connectedToCheatServer ) {
Cvar_SetCheatState();
}
// check pure server string
s = Info_ValueForKey( systemInfo, "sv_paks" );
t = Info_ValueForKey( systemInfo, "sv_pakNames" );
FS_PureServerSetLoadedPaks( s, t );
s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
FS_PureServerSetReferencedPaks( s, t );
gameSet = qfalse;
// scan through all the variables in the systeminfo and locally set cvars to match
s = systemInfo;
while ( s ) {
int cvar_flags;
Info_NextPair( &s, key, value );
if ( !key[0] ) {
break;
}
// ehw!
if (!Q_stricmp(key, "fs_game"))
{
if(FS_CheckDirTraversal(value))
{
Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value);
continue;
}
gameSet = qtrue;
}
if((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT)
Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM);
else
{
// If this cvar may not be modified by a server discard the value.
if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED)))
{
Com_Printf(S_COLOR_YELLOW "WARNING: server is not allowed to set %s=%s\n", key, value);
continue;
}
Cvar_Set(key, value);
}
}
// if game folder should not be set and it is set at the client side
if ( !gameSet && *Cvar_VariableString("fs_game") ) {
Cvar_Set( "fs_game", "" );
}
cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
}
/*
==================
CL_ParseServerInfo
==================
*/
static void CL_ParseServerInfo(void)
{
const char *serverInfo;
serverInfo = cl.gameState.stringData
+ cl.gameState.stringOffsets[ CS_SERVERINFO ];
clc.sv_allowDownload = atoi(Info_ValueForKey(serverInfo,
"sv_allowDownload"));
Q_strncpyz(clc.sv_dlURL,
Info_ValueForKey(serverInfo, "sv_dlURL"),
sizeof(clc.sv_dlURL));
}
/*
==================
CL_ParseGamestate
==================
*/
void CL_ParseGamestate( msg_t *msg ) {
int i;
entityState_t *es;
int newnum;
entityState_t nullstate;
int cmd;
char *s;
Con_Close();
clc.connectPacketCount = 0;
// wipe local client state
CL_ClearState();
// a gamestate always marks a server command sequence
clc.serverCommandSequence = MSG_ReadLong( msg );
// parse all the configstrings and baselines
cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
while ( 1 ) {
cmd = MSG_ReadByte( msg );
if ( cmd == svc_EOF ) {
break;
}
if ( cmd == svc_configstring ) {
int len;
i = MSG_ReadShort( msg );
if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
}
s = MSG_ReadBigString( msg );
len = strlen( s );
if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
}
// append it to the gameState string buffer
cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
cl.gameState.dataCount += len + 1;
} else if ( cmd == svc_baseline ) {
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
}
Com_Memset (&nullstate, 0, sizeof(nullstate));
es = &cl.entityBaselines[ newnum ];
MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
} else {
Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
}
}
clc.clientNum = MSG_ReadLong(msg);
// read the checksum feed
clc.checksumFeed = MSG_ReadLong( msg );
// parse useful values out of CS_SERVERINFO
CL_ParseServerInfo();
// parse serverId and other cvars
CL_SystemInfoChanged();
// stop recording now so the demo won't have an unnecessary level load at the end.
if(cl_autoRecordDemo->integer && clc.demorecording)
CL_StopRecord_f();
// reinitialize the filesystem if the game directory has changed
FS_ConditionalRestart( clc.checksumFeed );
// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
// cgame
CL_InitDownloads();
// make sure the game starts
Cvar_Set( "cl_paused", "0" );
}
//=====================================================================
/*
=====================
CL_ParseDownload
A download message has been received from the server
=====================
*/
void CL_ParseDownload ( msg_t *msg ) {
int size;
unsigned char data[MAX_MSGLEN];
int block;
if (!*clc.downloadTempName) {
Com_Printf("Server sending download, but no download was requested\n");
CL_AddReliableCommand( "stopdl" );
return;
}
// read the data
block = MSG_ReadShort ( msg );
if ( !block )
{
// block zero is special, contains file size
clc.downloadSize = MSG_ReadLong ( msg );
Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
if (clc.downloadSize < 0)
{
Com_Error(ERR_DROP, MSG_ReadString( msg ) );
return;
}
}
size = MSG_ReadShort ( msg );
if (size < 0 || size > sizeof(data))
{
Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk.", size);
return;
}
MSG_ReadData(msg, data, size);
if (clc.downloadBlock != block) {
Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
return;
}
// open the file if not opened yet
if (!clc.download)
{
clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
if (!clc.download) {
Com_Printf( "Could not create %s\n", clc.downloadTempName );
CL_AddReliableCommand( "stopdl" );
CL_NextDownload();
return;
}
}
if (size)
FS_Write( data, size, clc.download );
CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
clc.downloadBlock++;
clc.downloadCount += size;
// So UI gets access to it
Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
if (!size) { // A zero length block means EOF
if (clc.download) {
FS_FCloseFile( clc.download );
clc.download = 0;
// rename the file
FS_SV_Rename ( clc.downloadTempName, clc.downloadName );
}
*clc.downloadTempName = *clc.downloadName = 0;
Cvar_Set( "cl_downloadName", "" );
// send intentions now
// We need this because without it, we would hold the last nextdl and then start
// loading right away. If we take a while to load, the server is happily trying
// to send us that last block over and over.
// Write it twice to help make sure we acknowledge the download
CL_WritePacket();
CL_WritePacket();
// get another file if needed
CL_NextDownload ();
}
}
/*
=====================
CL_ParseCommandString
Command strings are just saved off until cgame asks for them
when it transitions a snapshot
=====================
*/
void CL_ParseCommandString( msg_t *msg ) {
char *s;
int seq;
int index;
seq = MSG_ReadLong( msg );
s = MSG_ReadString( msg );
// see if we have already executed stored it off
if ( clc.serverCommandSequence >= seq ) {
return;
}
clc.serverCommandSequence = seq;
index = seq & (MAX_RELIABLE_COMMANDS-1);
Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
}
/*
=====================
CL_ParseServerMessage
=====================
*/
void CL_ParseServerMessage( msg_t *msg ) {
int cmd;
if ( cl_shownet->integer == 1 ) {
Com_Printf ("%i ",msg->cursize);
} else if ( cl_shownet->integer >= 2 ) {
Com_Printf ("------------------\n");
}
MSG_Bitstream(msg);
// get the reliable sequence acknowledge number
clc.reliableAcknowledge = MSG_ReadLong( msg );
//
if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
clc.reliableAcknowledge = clc.reliableSequence;
}
//
// parse the message
//
while ( 1 ) {
if ( msg->readcount > msg->cursize ) {
Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
break;
}
cmd = MSG_ReadByte( msg );
if ( cmd == svc_EOF) {
SHOWNET( msg, "END OF MESSAGE" );
break;
}
if ( cl_shownet->integer >= 2 ) {
if ( !svc_strings[cmd] ) {
Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
} else {
SHOWNET( msg, svc_strings[cmd] );
}
}
// other commands
switch ( cmd ) {
default:
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
break;
case svc_nop:
break;
case svc_serverCommand:
CL_ParseCommandString( msg );
break;
case svc_gamestate:
CL_ParseGamestate( msg );
break;
case svc_snapshot:
CL_ParseSnapshot( msg );
break;
case svc_download:
CL_ParseDownload( msg );
break;
}
}
}
+524
View File
@@ -0,0 +1,524 @@
/*
===========================================================================
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
===========================================================================
*/
// cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
#include "client.h"
qboolean scr_initialized; // ready to draw
cvar_t *cl_timegraph;
cvar_t *cl_debuggraph;
cvar_t *cl_graphheight;
cvar_t *cl_graphscale;
cvar_t *cl_graphshift;
/*
================
SCR_DrawNamedPic
Coordinates are 640*480 virtual values
=================
*/
void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
qhandle_t hShader;
assert( width != 0 );
hShader = re.RegisterShader( picname );
SCR_AdjustFrom640( &x, &y, &width, &height );
re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
/*
================
SCR_AdjustFrom640
Adjusted for resolution and screen aspect ratio
================
*/
void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) {
float xscale;
float yscale;
#if 0
// adjust for wide screens
if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
*x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) );
}
#endif
// scale for screen sizes
xscale = cls.glconfig.vidWidth / 640.0;
yscale = cls.glconfig.vidHeight / 480.0;
if ( x ) {
*x *= xscale;
}
if ( y ) {
*y *= yscale;
}
if ( w ) {
*w *= xscale;
}
if ( h ) {
*h *= yscale;
}
}
/*
================
SCR_FillRect
Coordinates are 640*480 virtual values
=================
*/
void SCR_FillRect( float x, float y, float width, float height, const float *color ) {
re.SetColor( color );
SCR_AdjustFrom640( &x, &y, &width, &height );
re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader );
re.SetColor( NULL );
}
/*
================
SCR_DrawPic
Coordinates are 640*480 virtual values
=================
*/
void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
SCR_AdjustFrom640( &x, &y, &width, &height );
re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
/*
** SCR_DrawChar
** chars are drawn at 640*480 virtual screen size
*/
static void SCR_DrawChar( int x, int y, float size, int ch ) {
int row, col;
float frow, fcol;
float ax, ay, aw, ah;
ch &= 255;
if ( ch == ' ' ) {
return;
}
if ( y < -size ) {
return;
}
ax = x;
ay = y;
aw = size;
ah = size;
SCR_AdjustFrom640( &ax, &ay, &aw, &ah );
row = ch>>4;
col = ch&15;
frow = row*0.0625;
fcol = col*0.0625;
size = 0.0625;
re.DrawStretchPic( ax, ay, aw, ah,
fcol, frow,
fcol + size, frow + size,
cls.charSetShader );
}
/*
** SCR_DrawSmallChar
** small chars are drawn at native screen resolution
*/
void SCR_DrawSmallChar( int x, int y, int ch ) {
int row, col;
float frow, fcol;
float size;
ch &= 255;
if ( ch == ' ' ) {
return;
}
if ( y < -SMALLCHAR_HEIGHT ) {
return;
}
row = ch>>4;
col = ch&15;
frow = row*0.0625;
fcol = col*0.0625;
size = 0.0625;
re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
fcol, frow,
fcol + size, frow + size,
cls.charSetShader );
}
/*
==================
SCR_DrawBigString[Color]
Draws a multi-colored string with a drop shadow, optionally forcing
to a fixed color.
Coordinates are at 640 by 480 virtual resolution
==================
*/
void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor ) {
vec4_t color;
const char *s;
int xx;
// draw the drop shadow
color[0] = color[1] = color[2] = 0;
color[3] = setColor[3];
re.SetColor( color );
s = string;
xx = x;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
continue;
}
SCR_DrawChar( xx+2, y+2, size, *s );
xx += size;
s++;
}
// draw the colored text
s = string;
xx = x;
re.SetColor( setColor );
while ( *s ) {
if ( Q_IsColorString( s ) ) {
if ( !forceColor ) {
Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
color[3] = setColor[3];
re.SetColor( color );
}
s += 2;
continue;
}
SCR_DrawChar( xx, y, size, *s );
xx += size;
s++;
}
re.SetColor( NULL );
}
void SCR_DrawBigString( int x, int y, const char *s, float alpha ) {
float color[4];
color[0] = color[1] = color[2] = 1.0;
color[3] = alpha;
SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse );
}
void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ) {
SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue );
}
/*
==================
SCR_DrawSmallString[Color]
Draws a multi-colored string with a drop shadow, optionally forcing
to a fixed color.
Coordinates are at 640 by 480 virtual resolution
==================
*/
void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor ) {
vec4_t color;
const char *s;
int xx;
// draw the colored text
s = string;
xx = x;
re.SetColor( setColor );
while ( *s ) {
if ( Q_IsColorString( s ) ) {
if ( !forceColor ) {
Com_Memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
color[3] = setColor[3];
re.SetColor( color );
}
s += 2;
continue;
}
SCR_DrawSmallChar( xx, y, *s );
xx += SMALLCHAR_WIDTH;
s++;
}
re.SetColor( NULL );
}
/*
** SCR_Strlen -- skips color escape codes
*/
static int SCR_Strlen( const char *str ) {
const char *s = str;
int count = 0;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
} else {
count++;
s++;
}
}
return count;
}
/*
** SCR_GetBigStringWidth
*/
int SCR_GetBigStringWidth( const char *str ) {
return SCR_Strlen( str ) * 16;
}
//===============================================================================
/*
===============================================================================
DEBUG GRAPH
===============================================================================
*/
typedef struct
{
float value;
int color;
} graphsamp_t;
static int current;
static graphsamp_t values[1024];
/*
==============
SCR_DebugGraph
==============
*/
void SCR_DebugGraph (float value, int color)
{
values[current&1023].value = value;
values[current&1023].color = color;
current++;
}
/*
==============
SCR_DrawDebugGraph
==============
*/
void SCR_DrawDebugGraph (void)
{
int a, x, y, w, i, h;
float v;
int color;
//
// draw the graph
//
w = cls.glconfig.vidWidth;
x = 0;
y = cls.glconfig.vidHeight;
re.SetColor( g_color_table[0] );
re.DrawStretchPic(x, y - cl_graphheight->integer,
w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader );
re.SetColor( NULL );
for (a=0 ; a<w ; a++)
{
i = (current-1-a+1024) & 1023;
v = values[i].value;
color = values[i].color;
v = v * cl_graphscale->integer + cl_graphshift->integer;
if (v < 0)
v += cl_graphheight->integer * (1+(int)(-v / cl_graphheight->integer));
h = (int)v % cl_graphheight->integer;
re.DrawStretchPic( x+w-1-a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader );
}
}
//=============================================================================
/*
==================
SCR_Init
==================
*/
void SCR_Init( void ) {
cl_timegraph = Cvar_Get ("timegraph", "0", CVAR_CHEAT);
cl_debuggraph = Cvar_Get ("debuggraph", "0", CVAR_CHEAT);
cl_graphheight = Cvar_Get ("graphheight", "32", CVAR_CHEAT);
cl_graphscale = Cvar_Get ("graphscale", "1", CVAR_CHEAT);
cl_graphshift = Cvar_Get ("graphshift", "0", CVAR_CHEAT);
scr_initialized = qtrue;
}
//=======================================================
/*
==================
SCR_DrawScreenField
This will be called twice if rendering in stereo mode
==================
*/
void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
re.BeginFrame( stereoFrame );
// wide aspect ratio screens need to have the sides cleared
// unless they are displaying game renderings
if ( cls.state != CA_ACTIVE && cls.state != CA_CINEMATIC ) {
if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
re.SetColor( g_color_table[0] );
re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader );
re.SetColor( NULL );
}
}
if ( !uivm ) {
Com_DPrintf("draw screen without UI loaded\n");
return;
}
// if the menu is going to cover the entire screen, we
// don't need to render anything under it
if ( !VM_Call( uivm, UI_IS_FULLSCREEN )) {
switch( cls.state ) {
default:
Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad cls.state" );
break;
case CA_CINEMATIC:
SCR_DrawCinematic();
break;
case CA_DISCONNECTED:
// force menu up
S_StopAllSounds();
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
break;
case CA_CONNECTING:
case CA_CHALLENGING:
case CA_CONNECTED:
// connecting clients will only show the connection dialog
// refresh to update the time
VM_Call( uivm, UI_REFRESH, cls.realtime );
VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
break;
case CA_LOADING:
case CA_PRIMED:
// draw the game information screen and loading progress
CL_CGameRendering( stereoFrame );
// also draw the connection information, so it doesn't
// flash away too briefly on local or lan games
// refresh to update the time
VM_Call( uivm, UI_REFRESH, cls.realtime );
VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
break;
case CA_ACTIVE:
CL_CGameRendering( stereoFrame );
break;
}
}
// the menu draws next
if ( cls.keyCatchers & KEYCATCH_UI && uivm ) {
VM_Call( uivm, UI_REFRESH, cls.realtime );
}
// console draws next
Con_DrawConsole ();
// debug graph can be drawn on top of anything
if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) {
SCR_DrawDebugGraph ();
}
}
/*
==================
SCR_UpdateScreen
This is called every frame, and can also be called explicitly to flush
text to the screen.
==================
*/
void SCR_UpdateScreen( void ) {
static int recursive;
if ( !scr_initialized ) {
return; // not initialized yet
}
if ( ++recursive > 2 ) {
Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
}
recursive = 1;
// if running in stereo, we need to draw the frame twice
if ( cls.glconfig.stereoEnabled ) {
SCR_DrawScreenField( STEREO_LEFT );
SCR_DrawScreenField( STEREO_RIGHT );
} else {
SCR_DrawScreenField( STEREO_CENTER );
}
if ( com_speeds->integer ) {
re.EndFrame( &time_frontend, &time_backend );
} else {
re.EndFrame( NULL, NULL );
}
recursive = 0;
}
+1102
View File
File diff suppressed because it is too large. Load diff
+561
View File
@@ -0,0 +1,561 @@
/*
===========================================================================
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
===========================================================================
*/
// client.h -- primary header for client
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "../renderer/tr_public.h"
#include "../ui/ui_public.h"
#include "keys.h"
#include "snd_public.h"
#include "../cgame/cg_public.h"
#include "../game/bg_public.h"
#if USE_CURL
#include "cl_curl.h"
#endif /* USE_CURL */
// file full of random crap that gets used to create cl_guid
#define QKEY_FILE "qkey"
#define QKEY_SIZE 2048
#define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits
// snapshots are a view of the server at a given time
typedef struct {
qboolean valid; // cleared if delta parsing was invalid
int snapFlags; // rate delayed and dropped commands
int serverTime; // server time the message is valid for (in msec)
int messageNum; // copied from netchan->incoming_sequence
int deltaNum; // messageNum the delta is from
int ping; // time from when cmdNum-1 was sent to time packet was reeceived
byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
int cmdNum; // the next cmdNum the server is expecting
playerState_t ps; // complete information about the current player at this time
int numEntities; // all of the entities that need to be presented
int parseEntitiesNum; // at the time of this snapshot
int serverCommandNum; // execute all commands up to this before
// making the snapshot current
} clSnapshot_t;
/*
=============================================================================
the clientActive_t structure is wiped completely at every
new gamestate_t, potentially several times during an established connection
=============================================================================
*/
typedef struct {
int p_cmdNumber; // cl.cmdNumber when packet was sent
int p_serverTime; // usercmd->serverTime when packet was sent
int p_realtime; // cls.realtime when packet was sent
} outPacket_t;
// the parseEntities array must be large enough to hold PACKET_BACKUP frames of
// entities, so that when a delta compressed message arives from the server
// it can be un-deltad from the original
#define MAX_PARSE_ENTITIES 2048
extern int g_console_field_width;
typedef struct {
int timeoutcount; // it requres several frames in a timeout condition
// to disconnect, preventing debugging breaks from
// causing immediate disconnects on continue
clSnapshot_t snap; // latest received from server
int serverTime; // may be paused during play
int oldServerTime; // to prevent time from flowing bakcwards
int oldFrameServerTime; // to check tournament restarts
int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta
// this value changes as net lag varies
qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate
// cleared when CL_AdjustTimeDelta looks at it
qboolean newSnapshots; // set on parse of any valid packet
gameState_t gameState; // configstrings
char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO
int parseEntitiesNum; // index (not anded off) into cl_parse_entities[]
int mouseDx[2], mouseDy[2]; // added to by mouse events
int mouseIndex;
int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events
// cgame communicates a few values to the client system
int cgameUserCmdValue; // current weapon to add to usercmd_t
float cgameSensitivity;
// cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last
// properly generated command
usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds
int cmdNumber; // incremented each frame, because multiple
// frames may need to be packed into a single packet
outPacket_t outPackets[PACKET_BACKUP]; // information about each packet we have sent out
// the client maintains its own idea of view angles, which are
// sent to the server each frame. It is cleared to 0 upon entering each level.
// the server sends a delta each frame which is added to the locally
// tracked view angles to account for standing on rotating objects,
// and teleport direction changes
vec3_t viewangles;
int serverId; // included in each client message so the server
// can tell if it is for a prior map_restart
// big stuff at end of structure so most offsets are 15 bits or less
clSnapshot_t snapshots[PACKET_BACKUP];
entityState_t entityBaselines[MAX_GENTITIES]; // for delta compression when not in previous frame
entityState_t parseEntities[MAX_PARSE_ENTITIES];
} clientActive_t;
extern clientActive_t cl;
/*
=============================================================================
the clientConnection_t structure is wiped when disconnecting from a server,
either to go to a full screen console, play a demo, or connect to a different server
A connection can be to either a server through the network layer or a
demo through a file.
=============================================================================
*/
typedef struct {
int clientNum;
int lastPacketSentTime; // for retransmits during connection
int lastPacketTime; // for timeouts
netadr_t serverAddress;
int connectTime; // for connection retransmits
int connectPacketCount; // for display on connection dialog
char serverMessage[MAX_STRING_TOKENS]; // for display on connection dialog
int challenge; // from the server to use for connecting
int checksumFeed; // from the server for checksum calculations
// these are our reliable messages that go to the server
int reliableSequence;
int reliableAcknowledge; // the last one the server has executed
char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
// server message (unreliable) and command (reliable) sequence
// numbers are NOT cleared at level changes, but continue to
// increase as long as the connection is valid
// message sequence is used by both the network layer and the
// delta compression layer
int serverMessageSequence;
// reliable messages received from server
int serverCommandSequence;
int lastExecutedServerCommand; // last server command grabbed or executed with CL_GetServerCommand
char serverCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
// file transfer from server
fileHandle_t download;
char downloadTempName[MAX_OSPATH];
char downloadName[MAX_OSPATH];
#ifdef USE_CURL
qboolean cURLEnabled;
qboolean cURLUsed;
qboolean cURLDisconnected;
char downloadURL[MAX_OSPATH];
CURL *downloadCURL;
CURLM *downloadCURLM;
#endif /* USE_CURL */
int sv_allowDownload;
char sv_dlURL[MAX_CVAR_VALUE_STRING];
int downloadNumber;
int downloadBlock; // block we are waiting for
int downloadCount; // how many bytes we got
int downloadSize; // how many bytes we got
char downloadList[MAX_INFO_STRING]; // list of paks we need to download
qboolean downloadRestart; // if true, we need to do another FS_Restart because we downloaded a pak
// demo information
char demoName[MAX_QPATH];
qboolean spDemoRecording;
qboolean demorecording;
qboolean demoplaying;
qboolean demowaiting; // don't record until a non-delta message is received
qboolean firstDemoFrameSkipped;
fileHandle_t demofile;
int timeDemoFrames; // counter of rendered frames
int timeDemoStart; // cls.realtime before first frame
int timeDemoBaseTime; // each frame will be at this time + frameNum * 50
// big stuff at end of structure so most offsets are 15 bits or less
netchan_t netchan;
} clientConnection_t;
extern clientConnection_t clc;
/*
==================================================================
the clientStatic_t structure is never wiped, and is used even when
no client connection is active at all
==================================================================
*/
typedef struct {
netadr_t adr;
int start;
int time;
char info[MAX_INFO_STRING];
} ping_t;
typedef struct {
netadr_t adr;
char hostName[MAX_HOSTNAME_LENGTH];
char mapName[MAX_NAME_LENGTH];
char game[MAX_NAME_LENGTH];
int netType;
int gameType;
int clients;
int maxClients;
int minPing;
int maxPing;
int ping;
qboolean visible;
} serverInfo_t;
typedef struct {
byte ip[4];
unsigned short port;
} serverAddress_t;
typedef struct {
connstate_t state; // connection status
int keyCatchers; // bit flags
qboolean cddialog; // bring up the cd needed dialog next frame
char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect)
// when the server clears the hunk, all of these must be restarted
qboolean rendererStarted;
qboolean soundStarted;
qboolean soundRegistered;
qboolean uiStarted;
qboolean cgameStarted;
int framecount;
int frametime; // msec since last frame
int realtime; // ignores pause
int realFrametime; // ignoring pause, so console always works
int numlocalservers;
serverInfo_t localServers[MAX_OTHER_SERVERS];
int numglobalservers;
serverInfo_t globalServers[MAX_GLOBAL_SERVERS];
// additional global servers
int numGlobalServerAddresses;
serverAddress_t globalServerAddresses[MAX_GLOBAL_SERVERS];
int numfavoriteservers;
serverInfo_t favoriteServers[MAX_OTHER_SERVERS];
int nummplayerservers;
serverInfo_t mplayerServers[MAX_OTHER_SERVERS];
int pingUpdateSource; // source currently pinging or updating
int masterNum;
// update server info
netadr_t updateServer;
char updateChallenge[MAX_TOKEN_CHARS];
char updateInfoString[MAX_INFO_STRING];
netadr_t authorizeServer;
// rendering info
glconfig_t glconfig;
qhandle_t charSetShader;
qhandle_t whiteShader;
qhandle_t consoleShader;
} clientStatic_t;
extern clientStatic_t cls;
//=============================================================================
extern vm_t *cgvm; // interface to cgame dll or vm
extern vm_t *uivm; // interface to ui dll or vm
extern refexport_t re; // interface to refresh .dll
//
// cvars
//
extern cvar_t *cl_nodelta;
extern cvar_t *cl_debugMove;
extern cvar_t *cl_noprint;
extern cvar_t *cl_timegraph;
extern cvar_t *cl_maxpackets;
extern cvar_t *cl_packetdup;
extern cvar_t *cl_shownet;
extern cvar_t *cl_showSend;
extern cvar_t *cl_timeNudge;
extern cvar_t *cl_showTimeDelta;
extern cvar_t *cl_freezeDemo;
extern cvar_t *cl_yawspeed;
extern cvar_t *cl_pitchspeed;
extern cvar_t *cl_run;
extern cvar_t *cl_anglespeedkey;
extern cvar_t *cl_sensitivity;
extern cvar_t *cl_platformSensitivity;
extern cvar_t *cl_freelook;
extern cvar_t *cl_mouseAccel;
extern cvar_t *cl_showMouseRate;
extern cvar_t *m_pitch;
extern cvar_t *m_yaw;
extern cvar_t *m_forward;
extern cvar_t *m_side;
extern cvar_t *m_filter;
extern cvar_t *cl_timedemo;
extern cvar_t *cl_aviFrameRate;
extern cvar_t *cl_aviMotionJpeg;
extern cvar_t *cl_activeAction;
extern cvar_t *cl_allowDownload;
extern cvar_t *cl_downloadMethod;
extern cvar_t *cl_conXOffset;
extern cvar_t *cl_inGameVideo;
extern cvar_t *cl_lanForcePackets;
extern cvar_t *cl_autoRecordDemo;
//=================================================
//
// cl_main
//
void CL_Init (void);
void CL_FlushMemory(void);
void CL_ShutdownAll(void);
void CL_AddReliableCommand( const char *cmd );
void CL_StartHunkUsers( void );
void CL_Disconnect_f (void);
void CL_GetChallengePacket (void);
void CL_Vid_Restart_f( void );
void CL_Snd_Restart_f (void);
void CL_StartDemoLoop( void );
void CL_NextDemo( void );
void CL_ReadDemoMessage( void );
demoState_t CL_DemoState( void );
int CL_DemoPos( void );
void CL_DemoName( char *buffer, int size );
void CL_StopRecord_f( void );
void CL_InitDownloads(void);
void CL_NextDownload(void);
void CL_GetPing( int n, char *buf, int buflen, int *pingtime );
void CL_GetPingInfo( int n, char *buf, int buflen );
void CL_ClearPing( int n );
int CL_GetPingQueueCount( void );
void CL_ShutdownRef( void );
void CL_InitRef( void );
int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen );
qboolean CL_CheckPaused(void);
//
// cl_input
//
typedef struct {
int down[2]; // key nums holding it down
unsigned downtime; // msec timestamp
unsigned msec; // msec down this frame if both a down and up happened
qboolean active; // current state
qboolean wasPressed; // set when down, not cleared when up
} kbutton_t;
extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;
void CL_InitInput (void);
void CL_SendCmd (void);
void CL_ClearState (void);
void CL_ReadPackets (void);
void CL_WritePacket( void );
void IN_CenterView (void);
void CL_VerifyCode( void );
float CL_KeyState (kbutton_t *key);
char *Key_KeynumToString (int keynum);
//
// cl_parse.c
//
extern int cl_connectedToPureServer;
extern int cl_connectedToCheatServer;
void CL_SystemInfoChanged( void );
void CL_ParseServerMessage( msg_t *msg );
//====================================================================
void CL_ServerInfoPacket( netadr_t from, msg_t *msg );
void CL_LocalServers_f( void );
void CL_GlobalServers_f( void );
void CL_FavoriteServers_f( void );
void CL_Ping_f( void );
qboolean CL_UpdateVisiblePings_f( int source );
//
// console
//
void Con_DrawCharacter (int cx, int line, int num);
void Con_CheckResize (void);
void Con_Init (void);
void Con_Clear_f (void);
void Con_ToggleConsole_f (void);
void Con_DrawNotify (void);
void Con_ClearNotify (void);
void Con_RunConsole (void);
void Con_DrawConsole (void);
void Con_PageUp( void );
void Con_PageDown( void );
void Con_Top( void );
void Con_Bottom( void );
void Con_Close( void );
void CL_LoadConsoleHistory( void );
void CL_SaveConsoleHistory( void );
//
// cl_scrn.c
//
void SCR_Init (void);
void SCR_UpdateScreen (void);
void SCR_DebugGraph (float value, int color);
int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates
void SCR_AdjustFrom640( float *x, float *y, float *w, float *h );
void SCR_FillRect( float x, float y, float width, float height,
const float *color );
void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader );
void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname );
void SCR_DrawBigString( int x, int y, const char *s, float alpha ); // draws a string with embedded color control characters with fade
void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color ); // ignores embedded color control characters
void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor );
void SCR_DrawSmallChar( int x, int y, int ch );
//
// cl_cin.c
//
void CL_PlayCinematic_f( void );
void SCR_DrawCinematic (void);
void SCR_RunCinematic (void);
void SCR_StopCinematic (void);
int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits);
e_status CIN_StopCinematic(int handle);
e_status CIN_RunCinematic (int handle);
void CIN_DrawCinematic (int handle);
void CIN_SetExtents (int handle, int x, int y, int w, int h);
void CIN_SetLooping (int handle, qboolean loop);
void CIN_UploadCinematic(int handle);
void CIN_CloseAllVideos(void);
//
// cl_cgame.c
//
void CL_InitCGame( void );
void CL_ShutdownCGame( void );
qboolean CL_GameCommand( void );
void CL_GameConsoleText( void );
void CL_CGameRendering( stereoFrame_t stereo );
void CL_SetCGameTime( void );
void CL_FirstSnapshot( void );
void CL_ShaderStateChanged(void);
//
// cl_ui.c
//
void CL_InitUI( void );
void CL_ShutdownUI( void );
int Key_GetCatcher( void );
void Key_SetCatcher( int catcher );
void LAN_LoadCachedServers( void );
void LAN_SaveServersToCache( void );
//
// cl_net_chan.c
//
void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg); //int length, const byte *data );
void CL_Netchan_TransmitNextFragment( netchan_t *chan );
qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg );
//
// 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 );
+278
View File
@@ -0,0 +1,278 @@
/*
===========================================================================
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
===========================================================================
*/
//
#ifndef __KEYCODES_H__
#define __KEYCODES_H__
//
// these are the key numbers that should be passed to KeyEvent
//
// normal keys should be passed as lowercased ascii
typedef enum {
K_NONE = -1,
K_TAB = 9,
K_ENTER = 13,
K_ESCAPE = 27,
K_SPACE = 32,
K_BACKSPACE = 127,
K_COMMAND = 128,
K_CAPSLOCK,
K_POWER,
K_PAUSE,
K_UPARROW,
K_DOWNARROW,
K_LEFTARROW,
K_RIGHTARROW,
K_ALT,
K_CTRL,
K_SHIFT,
K_INS,
K_DEL,
K_PGDN,
K_PGUP,
K_HOME,
K_END,
K_F1,
K_F2,
K_F3,
K_F4,
K_F5,
K_F6,
K_F7,
K_F8,
K_F9,
K_F10,
K_F11,
K_F12,
K_F13,
K_F14,
K_F15,
K_KP_HOME,
K_KP_UPARROW,
K_KP_PGUP,
K_KP_LEFTARROW,
K_KP_5,
K_KP_RIGHTARROW,
K_KP_END,
K_KP_DOWNARROW,
K_KP_PGDN,
K_KP_ENTER,
K_KP_INS,
K_KP_DEL,
K_KP_SLASH,
K_KP_MINUS,
K_KP_PLUS,
K_KP_NUMLOCK,
K_KP_STAR,
K_KP_EQUALS,
K_MOUSE1,
K_MOUSE2,
K_MOUSE3,
K_MOUSE4,
K_MOUSE5,
K_MWHEELDOWN,
K_MWHEELUP,
K_JOY1,
K_JOY2,
K_JOY3,
K_JOY4,
K_JOY5,
K_JOY6,
K_JOY7,
K_JOY8,
K_JOY9,
K_JOY10,
K_JOY11,
K_JOY12,
K_JOY13,
K_JOY14,
K_JOY15,
K_JOY16,
K_JOY17,
K_JOY18,
K_JOY19,
K_JOY20,
K_JOY21,
K_JOY22,
K_JOY23,
K_JOY24,
K_JOY25,
K_JOY26,
K_JOY27,
K_JOY28,
K_JOY29,
K_JOY30,
K_JOY31,
K_JOY32,
K_AUX1,
K_AUX2,
K_AUX3,
K_AUX4,
K_AUX5,
K_AUX6,
K_AUX7,
K_AUX8,
K_AUX9,
K_AUX10,
K_AUX11,
K_AUX12,
K_AUX13,
K_AUX14,
K_AUX15,
K_AUX16,
K_WORLD_0,
K_WORLD_1,
K_WORLD_2,
K_WORLD_3,
K_WORLD_4,
K_WORLD_5,
K_WORLD_6,
K_WORLD_7,
K_WORLD_8,
K_WORLD_9,
K_WORLD_10,
K_WORLD_11,
K_WORLD_12,
K_WORLD_13,
K_WORLD_14,
K_WORLD_15,
K_WORLD_16,
K_WORLD_17,
K_WORLD_18,
K_WORLD_19,
K_WORLD_20,
K_WORLD_21,
K_WORLD_22,
K_WORLD_23,
K_WORLD_24,
K_WORLD_25,
K_WORLD_26,
K_WORLD_27,
K_WORLD_28,
K_WORLD_29,
K_WORLD_30,
K_WORLD_31,
K_WORLD_32,
K_WORLD_33,
K_WORLD_34,
K_WORLD_35,
K_WORLD_36,
K_WORLD_37,
K_WORLD_38,
K_WORLD_39,
K_WORLD_40,
K_WORLD_41,
K_WORLD_42,
K_WORLD_43,
K_WORLD_44,
K_WORLD_45,
K_WORLD_46,
K_WORLD_47,
K_WORLD_48,
K_WORLD_49,
K_WORLD_50,
K_WORLD_51,
K_WORLD_52,
K_WORLD_53,
K_WORLD_54,
K_WORLD_55,
K_WORLD_56,
K_WORLD_57,
K_WORLD_58,
K_WORLD_59,
K_WORLD_60,
K_WORLD_61,
K_WORLD_62,
K_WORLD_63,
K_WORLD_64,
K_WORLD_65,
K_WORLD_66,
K_WORLD_67,
K_WORLD_68,
K_WORLD_69,
K_WORLD_70,
K_WORLD_71,
K_WORLD_72,
K_WORLD_73,
K_WORLD_74,
K_WORLD_75,
K_WORLD_76,
K_WORLD_77,
K_WORLD_78,
K_WORLD_79,
K_WORLD_80,
K_WORLD_81,
K_WORLD_82,
K_WORLD_83,
K_WORLD_84,
K_WORLD_85,
K_WORLD_86,
K_WORLD_87,
K_WORLD_88,
K_WORLD_89,
K_WORLD_90,
K_WORLD_91,
K_WORLD_92,
K_WORLD_93,
K_WORLD_94,
K_WORLD_95,
K_SUPER,
K_COMPOSE,
K_MODE,
K_HELP,
K_PRINT,
K_SYSREQ,
K_SCROLLOCK,
K_BREAK,
K_MENU,
K_EURO,
K_UNDO,
MAX_KEYS
} keyNum_t;
// MAX_KEYS replaces K_LAST_KEY, however some mods may have used K_LAST_KEY
// in detecting binds, so we leave it defined to the old hardcoded value
// of maxiumum keys to prevent mods from crashing older versions of the engine
#define K_LAST_KEY 256
// The menu code needs to get both key and char events, but
// to avoid duplicating the paths, the char events are just
// distinguished by or'ing in K_CHAR_FLAG (ugly)
#define K_CHAR_FLAG 1024
#endif
+58
View File
@@ -0,0 +1,58 @@
/*
===========================================================================
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
===========================================================================
*/
#include "keycodes.h"
typedef struct {
qboolean down;
int repeats; // if > 1, it is autorepeating
char *binding;
} qkey_t;
extern qboolean key_overstrikeMode;
extern qkey_t keys[MAX_KEYS];
// NOTE TTimo the declaration of field_t and Field_Clear is now in qcommon/qcommon.h
void Field_KeyDownEvent( field_t *edit, int key );
void Field_CharEvent( field_t *edit, int ch );
void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor );
void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor );
#define COMMAND_HISTORY 32
extern field_t historyEditLines[COMMAND_HISTORY];
extern field_t g_consoleField;
extern field_t chatField;
extern int anykeydown;
extern qboolean chat_team;
extern int chat_playerNum;
void Key_WriteBindings( fileHandle_t f );
void Key_SetBinding( int keynum, const char *binding );
char *Key_GetBinding( int keynum );
qboolean Key_IsDown( int keynum );
qboolean Key_GetOverstrikeMode( void );
void Key_SetOverstrikeMode( qboolean state );
void Key_ClearStates( void );
int Key_GetKey(const char *binding);
void Key_KeynumToStringBuf( int keynum, char *buf, int buflen );
void Key_GetBindingBuf( int keynum, char *buf, int buflen );
+366
View File
@@ -0,0 +1,366 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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
===========================================================================
*/
// Dynamically loads OpenAL
#if USE_OPENAL
#include "qal.h"
#if USE_OPENAL_DLOPEN
#if USE_SDL_VIDEO
#include "SDL.h"
#include "SDL_loadso.h"
#define OBJTYPE void *
#define OBJLOAD(x) SDL_LoadObject(x)
#define SYMLOAD(x,y) SDL_LoadFunction(x,y)
#define OBJFREE(x) SDL_UnloadObject(x)
#elif defined _WIN32
#include <windows.h>
#define OBJTYPE HMODULE
#define OBJLOAD(x) LoadLibrary(x)
#define SYMLOAD(x,y) GetProcAddress(x,y)
#define OBJFREE(x) FreeLibrary(x)
#elif defined __linux__ || defined __FreeBSD__ || defined MACOS_X || defined __sun
#include <dlfcn.h>
#define OBJTYPE void *
#define OBJLOAD(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL)
#define SYMLOAD(x,y) dlsym(x,y)
#define OBJFREE(x) dlclose(x)
#else
#error "Your platform has no lib loading code or it is disabled"
#endif
#if defined __linux__ || defined __FreeBSD__ || defined MACOS_X
#include <unistd.h>
#include <sys/types.h>
#endif
LPALENABLE qalEnable;
LPALDISABLE qalDisable;
LPALISENABLED qalIsEnabled;
LPALGETSTRING qalGetString;
LPALGETBOOLEANV qalGetBooleanv;
LPALGETINTEGERV qalGetIntegerv;
LPALGETFLOATV qalGetFloatv;
LPALGETDOUBLEV qalGetDoublev;
LPALGETBOOLEAN qalGetBoolean;
LPALGETINTEGER qalGetInteger;
LPALGETFLOAT qalGetFloat;
LPALGETDOUBLE qalGetDouble;
LPALGETERROR qalGetError;
LPALISEXTENSIONPRESENT qalIsExtensionPresent;
LPALGETPROCADDRESS qalGetProcAddress;
LPALGETENUMVALUE qalGetEnumValue;
LPALLISTENERF qalListenerf;
LPALLISTENER3F qalListener3f;
LPALLISTENERFV qalListenerfv;
LPALLISTENERI qalListeneri;
LPALGETLISTENERF qalGetListenerf;
LPALGETLISTENER3F qalGetListener3f;
LPALGETLISTENERFV qalGetListenerfv;
LPALGETLISTENERI qalGetListeneri;
LPALGENSOURCES qalGenSources;
LPALDELETESOURCES qalDeleteSources;
LPALISSOURCE qalIsSource;
LPALSOURCEF qalSourcef;
LPALSOURCE3F qalSource3f;
LPALSOURCEFV qalSourcefv;
LPALSOURCEI qalSourcei;
LPALGETSOURCEF qalGetSourcef;
LPALGETSOURCE3F qalGetSource3f;
LPALGETSOURCEFV qalGetSourcefv;
LPALGETSOURCEI qalGetSourcei;
LPALSOURCEPLAYV qalSourcePlayv;
LPALSOURCESTOPV qalSourceStopv;
LPALSOURCEREWINDV qalSourceRewindv;
LPALSOURCEPAUSEV qalSourcePausev;
LPALSOURCEPLAY qalSourcePlay;
LPALSOURCESTOP qalSourceStop;
LPALSOURCEREWIND qalSourceRewind;
LPALSOURCEPAUSE qalSourcePause;
LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
LPALGENBUFFERS qalGenBuffers;
LPALDELETEBUFFERS qalDeleteBuffers;
LPALISBUFFER qalIsBuffer;
LPALBUFFERDATA qalBufferData;
LPALGETBUFFERF qalGetBufferf;
LPALGETBUFFERI qalGetBufferi;
LPALDOPPLERFACTOR qalDopplerFactor;
LPALDOPPLERVELOCITY qalDopplerVelocity;
LPALDISTANCEMODEL qalDistanceModel;
LPALCCREATECONTEXT qalcCreateContext;
LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
LPALCPROCESSCONTEXT qalcProcessContext;
LPALCSUSPENDCONTEXT qalcSuspendContext;
LPALCDESTROYCONTEXT qalcDestroyContext;
LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
LPALCOPENDEVICE qalcOpenDevice;
LPALCCLOSEDEVICE qalcCloseDevice;
LPALCGETERROR qalcGetError;
LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
LPALCGETPROCADDRESS qalcGetProcAddress;
LPALCGETENUMVALUE qalcGetEnumValue;
LPALCGETSTRING qalcGetString;
LPALCGETINTEGERV qalcGetIntegerv;
static OBJTYPE OpenALLib = NULL;
static qboolean alinit_fail = qfalse;
/*
=================
GPA
=================
*/
static void *GPA(char *str)
{
void *rv;
rv = SYMLOAD(OpenALLib, str);
if(!rv)
{
Com_Printf( " Can't load symbol %s\n", str);
alinit_fail = qtrue;
return NULL;
}
else
{
Com_DPrintf( " Loaded symbol %s (%p)\n", str, rv);
return rv;
}
}
/*
=================
QAL_Init
=================
*/
qboolean QAL_Init(const char *libname)
{
if(OpenALLib)
return qtrue;
Com_Printf( "Loading \"%s\"...\n", libname);
if( (OpenALLib = OBJLOAD(libname)) == 0 )
{
#ifdef _WIN32
return qfalse;
#else
char fn[1024];
getcwd(fn, sizeof(fn));
strncat(fn, "/", sizeof(fn) - strlen(fn) - 1);
strncat(fn, libname, sizeof(fn) - strlen(fn) - 1);
if( (OpenALLib = OBJLOAD(fn)) == 0 )
{
return qfalse;
}
#endif
}
alinit_fail = qfalse;
qalEnable = GPA("alEnable");
qalDisable = GPA("alDisable");
qalIsEnabled = GPA("alIsEnabled");
qalGetString = GPA("alGetString");
qalGetBooleanv = GPA("alGetBooleanv");
qalGetIntegerv = GPA("alGetIntegerv");
qalGetFloatv = GPA("alGetFloatv");
qalGetDoublev = GPA("alGetDoublev");
qalGetBoolean = GPA("alGetBoolean");
qalGetInteger = GPA("alGetInteger");
qalGetFloat = GPA("alGetFloat");
qalGetDouble = GPA("alGetDouble");
qalGetError = GPA("alGetError");
qalIsExtensionPresent = GPA("alIsExtensionPresent");
qalGetProcAddress = GPA("alGetProcAddress");
qalGetEnumValue = GPA("alGetEnumValue");
qalListenerf = GPA("alListenerf");
qalListener3f = GPA("alListener3f");
qalListenerfv = GPA("alListenerfv");
qalListeneri = GPA("alListeneri");
qalGetListenerf = GPA("alGetListenerf");
qalGetListener3f = GPA("alGetListener3f");
qalGetListenerfv = GPA("alGetListenerfv");
qalGetListeneri = GPA("alGetListeneri");
qalGenSources = GPA("alGenSources");
qalDeleteSources = GPA("alDeleteSources");
qalIsSource = GPA("alIsSource");
qalSourcef = GPA("alSourcef");
qalSource3f = GPA("alSource3f");
qalSourcefv = GPA("alSourcefv");
qalSourcei = GPA("alSourcei");
qalGetSourcef = GPA("alGetSourcef");
qalGetSource3f = GPA("alGetSource3f");
qalGetSourcefv = GPA("alGetSourcefv");
qalGetSourcei = GPA("alGetSourcei");
qalSourcePlayv = GPA("alSourcePlayv");
qalSourceStopv = GPA("alSourceStopv");
qalSourceRewindv = GPA("alSourceRewindv");
qalSourcePausev = GPA("alSourcePausev");
qalSourcePlay = GPA("alSourcePlay");
qalSourceStop = GPA("alSourceStop");
qalSourceRewind = GPA("alSourceRewind");
qalSourcePause = GPA("alSourcePause");
qalSourceQueueBuffers = GPA("alSourceQueueBuffers");
qalSourceUnqueueBuffers = GPA("alSourceUnqueueBuffers");
qalGenBuffers = GPA("alGenBuffers");
qalDeleteBuffers = GPA("alDeleteBuffers");
qalIsBuffer = GPA("alIsBuffer");
qalBufferData = GPA("alBufferData");
qalGetBufferf = GPA("alGetBufferf");
qalGetBufferi = GPA("alGetBufferi");
qalDopplerFactor = GPA("alDopplerFactor");
qalDopplerVelocity = GPA("alDopplerVelocity");
qalDistanceModel = GPA("alDistanceModel");
qalcCreateContext = GPA("alcCreateContext");
qalcMakeContextCurrent = GPA("alcMakeContextCurrent");
qalcProcessContext = GPA("alcProcessContext");
qalcSuspendContext = GPA("alcSuspendContext");
qalcDestroyContext = GPA("alcDestroyContext");
qalcGetCurrentContext = GPA("alcGetCurrentContext");
qalcGetContextsDevice = GPA("alcGetContextsDevice");
qalcOpenDevice = GPA("alcOpenDevice");
qalcCloseDevice = GPA("alcCloseDevice");
qalcGetError = GPA("alcGetError");
qalcIsExtensionPresent = GPA("alcIsExtensionPresent");
qalcGetProcAddress = GPA("alcGetProcAddress");
qalcGetEnumValue = GPA("alcGetEnumValue");
qalcGetString = GPA("alcGetString");
qalcGetIntegerv = GPA("alcGetIntegerv");
if(alinit_fail)
{
QAL_Shutdown();
Com_Printf( " One or more symbols not found\n");
return qfalse;
}
return qtrue;
}
/*
=================
QAL_Shutdown
=================
*/
void QAL_Shutdown( void )
{
if(OpenALLib)
{
OBJFREE(OpenALLib);
OpenALLib = NULL;
}
qalEnable = NULL;
qalDisable = NULL;
qalIsEnabled = NULL;
qalGetString = NULL;
qalGetBooleanv = NULL;
qalGetIntegerv = NULL;
qalGetFloatv = NULL;
qalGetDoublev = NULL;
qalGetBoolean = NULL;
qalGetInteger = NULL;
qalGetFloat = NULL;
qalGetDouble = NULL;
qalGetError = NULL;
qalIsExtensionPresent = NULL;
qalGetProcAddress = NULL;
qalGetEnumValue = NULL;
qalListenerf = NULL;
qalListener3f = NULL;
qalListenerfv = NULL;
qalListeneri = NULL;
qalGetListenerf = NULL;
qalGetListener3f = NULL;
qalGetListenerfv = NULL;
qalGetListeneri = NULL;
qalGenSources = NULL;
qalDeleteSources = NULL;
qalIsSource = NULL;
qalSourcef = NULL;
qalSource3f = NULL;
qalSourcefv = NULL;
qalSourcei = NULL;
qalGetSourcef = NULL;
qalGetSource3f = NULL;
qalGetSourcefv = NULL;
qalGetSourcei = NULL;
qalSourcePlayv = NULL;
qalSourceStopv = NULL;
qalSourceRewindv = NULL;
qalSourcePausev = NULL;
qalSourcePlay = NULL;
qalSourceStop = NULL;
qalSourceRewind = NULL;
qalSourcePause = NULL;
qalSourceQueueBuffers = NULL;
qalSourceUnqueueBuffers = NULL;
qalGenBuffers = NULL;
qalDeleteBuffers = NULL;
qalIsBuffer = NULL;
qalBufferData = NULL;
qalGetBufferf = NULL;
qalGetBufferi = NULL;
qalDopplerFactor = NULL;
qalDopplerVelocity = NULL;
qalDistanceModel = NULL;
qalcCreateContext = NULL;
qalcMakeContextCurrent = NULL;
qalcProcessContext = NULL;
qalcSuspendContext = NULL;
qalcDestroyContext = NULL;
qalcGetCurrentContext = NULL;
qalcGetContextsDevice = NULL;
qalcOpenDevice = NULL;
qalcCloseDevice = NULL;
qalcGetError = NULL;
qalcIsExtensionPresent = NULL;
qalcGetProcAddress = NULL;
qalcGetEnumValue = NULL;
qalcGetString = NULL;
qalcGetIntegerv = NULL;
}
#else
qboolean QAL_Init(const char *libname)
{
return qtrue;
}
void QAL_Shutdown( void )
{
}
#endif
#endif
+246
View File
@@ -0,0 +1,246 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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
===========================================================================
*/
#ifndef __QAL_H__
#define __QAL_H__
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#if USE_OPENAL_DLOPEN
#define AL_NO_PROTOTYPES
#define ALC_NO_PROTOTYPES
#endif
#if USE_LOCAL_HEADERS
#include "../AL/al.h"
#include "../AL/alc.h"
#else
#ifdef _MSC_VER
// MSVC users must install the OpenAL SDK which doesn't use the AL/*.h scheme.
#include <al.h>
#include <alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#endif
#if USE_OPENAL_DLOPEN
extern LPALENABLE qalEnable;
extern LPALDISABLE qalDisable;
extern LPALISENABLED qalIsEnabled;
extern LPALGETSTRING qalGetString;
extern LPALGETBOOLEANV qalGetBooleanv;
extern LPALGETINTEGERV qalGetIntegerv;
extern LPALGETFLOATV qalGetFloatv;
extern LPALGETDOUBLEV qalGetDoublev;
extern LPALGETBOOLEAN qalGetBoolean;
extern LPALGETINTEGER qalGetInteger;
extern LPALGETFLOAT qalGetFloat;
extern LPALGETDOUBLE qalGetDouble;
extern LPALGETERROR qalGetError;
extern LPALISEXTENSIONPRESENT qalIsExtensionPresent;
extern LPALGETPROCADDRESS qalGetProcAddress;
extern LPALGETENUMVALUE qalGetEnumValue;
extern LPALLISTENERF qalListenerf;
extern LPALLISTENER3F qalListener3f;
extern LPALLISTENERFV qalListenerfv;
extern LPALLISTENERI qalListeneri;
extern LPALLISTENER3I qalListener3i;
extern LPALLISTENERIV qalListeneriv;
extern LPALGETLISTENERF qalGetListenerf;
extern LPALGETLISTENER3F qalGetListener3f;
extern LPALGETLISTENERFV qalGetListenerfv;
extern LPALGETLISTENERI qalGetListeneri;
extern LPALGETLISTENER3I qalGetListener3i;
extern LPALGETLISTENERIV qalGetListeneriv;
extern LPALGENSOURCES qalGenSources;
extern LPALDELETESOURCES qalDeleteSources;
extern LPALISSOURCE qalIsSource;
extern LPALSOURCEF qalSourcef;
extern LPALSOURCE3F qalSource3f;
extern LPALSOURCEFV qalSourcefv;
extern LPALSOURCEI qalSourcei;
extern LPALSOURCE3I qalSource3i;
extern LPALSOURCEIV qalSourceiv;
extern LPALGETSOURCEF qalGetSourcef;
extern LPALGETSOURCE3F qalGetSource3f;
extern LPALGETSOURCEFV qalGetSourcefv;
extern LPALGETSOURCEI qalGetSourcei;
extern LPALGETSOURCE3I qalGetSource3i;
extern LPALGETSOURCEIV qalGetSourceiv;
extern LPALSOURCEPLAYV qalSourcePlayv;
extern LPALSOURCESTOPV qalSourceStopv;
extern LPALSOURCEREWINDV qalSourceRewindv;
extern LPALSOURCEPAUSEV qalSourcePausev;
extern LPALSOURCEPLAY qalSourcePlay;
extern LPALSOURCESTOP qalSourceStop;
extern LPALSOURCEREWIND qalSourceRewind;
extern LPALSOURCEPAUSE qalSourcePause;
extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
extern LPALGENBUFFERS qalGenBuffers;
extern LPALDELETEBUFFERS qalDeleteBuffers;
extern LPALISBUFFER qalIsBuffer;
extern LPALBUFFERDATA qalBufferData;
extern LPALBUFFERF qalBufferf;
extern LPALBUFFER3F qalBuffer3f;
extern LPALBUFFERFV qalBufferfv;
extern LPALBUFFERF qalBufferi;
extern LPALBUFFER3F qalBuffer3i;
extern LPALBUFFERFV qalBufferiv;
extern LPALGETBUFFERF qalGetBufferf;
extern LPALGETBUFFER3F qalGetBuffer3f;
extern LPALGETBUFFERFV qalGetBufferfv;
extern LPALGETBUFFERI qalGetBufferi;
extern LPALGETBUFFER3I qalGetBuffer3i;
extern LPALGETBUFFERIV qalGetBufferiv;
extern LPALDOPPLERFACTOR qalDopplerFactor;
extern LPALDOPPLERVELOCITY qalDopplerVelocity;
extern LPALSPEEDOFSOUND qalSpeedOfSound;
extern LPALDISTANCEMODEL qalDistanceModel;
extern LPALCCREATECONTEXT qalcCreateContext;
extern LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
extern LPALCPROCESSCONTEXT qalcProcessContext;
extern LPALCSUSPENDCONTEXT qalcSuspendContext;
extern LPALCDESTROYCONTEXT qalcDestroyContext;
extern LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
extern LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
extern LPALCOPENDEVICE qalcOpenDevice;
extern LPALCCLOSEDEVICE qalcCloseDevice;
extern LPALCGETERROR qalcGetError;
extern LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
extern LPALCGETPROCADDRESS qalcGetProcAddress;
extern LPALCGETENUMVALUE qalcGetEnumValue;
extern LPALCGETSTRING qalcGetString;
extern LPALCGETINTEGERV qalcGetIntegerv;
extern LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice;
extern LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice;
extern LPALCCAPTURESTART qalcCaptureStart;
extern LPALCCAPTURESTOP qalcCaptureStop;
extern LPALCCAPTURESAMPLES qalcCaptureSamples;
#else
#define qalEnable alEnable
#define qalDisable alDisable
#define qalIsEnabled alIsEnabled
#define qalGetString alGetString
#define qalGetBooleanv alGetBooleanv
#define qalGetIntegerv alGetIntegerv
#define qalGetFloatv alGetFloatv
#define qalGetDoublev alGetDoublev
#define qalGetBoolean alGetBoolean
#define qalGetInteger alGetInteger
#define qalGetFloat alGetFloat
#define qalGetDouble alGetDouble
#define qalGetError alGetError
#define qalIsExtensionPresent alIsExtensionPresent
#define qalGetProcAddress alGetProcAddress
#define qalGetEnumValue alGetEnumValue
#define qalListenerf alListenerf
#define qalListener3f alListener3f
#define qalListenerfv alListenerfv
#define qalListeneri alListeneri
#define qalListener3i alListener3i
#define qalListeneriv alListeneriv
#define qalGetListenerf alGetListenerf
#define qalGetListener3f alGetListener3f
#define qalGetListenerfv alGetListenerfv
#define qalGetListeneri alGetListeneri
#define qalGetListener3i alGetListener3i
#define qalGetListeneriv alGetListeneriv
#define qalGenSources alGenSources
#define qalDeleteSources alDeleteSources
#define qalIsSource alIsSource
#define qalSourcef alSourcef
#define qalSource3f alSource3f
#define qalSourcefv alSourcefv
#define qalSourcei alSourcei
#define qalSource3i alSource3i
#define qalSourceiv alSourceiv
#define qalGetSourcef alGetSourcef
#define qalGetSource3f alGetSource3f
#define qalGetSourcefv alGetSourcefv
#define qalGetSourcei alGetSourcei
#define qalGetSource3i alGetSource3i
#define qalGetSourceiv alGetSourceiv
#define qalSourcePlayv alSourcePlayv
#define qalSourceStopv alSourceStopv
#define qalSourceRewindv alSourceRewindv
#define qalSourcePausev alSourcePausev
#define qalSourcePlay alSourcePlay
#define qalSourceStop alSourceStop
#define qalSourceRewind alSourceRewind
#define qalSourcePause alSourcePause
#define qalSourceQueueBuffers alSourceQueueBuffers
#define qalSourceUnqueueBuffers alSourceUnqueueBuffers
#define qalGenBuffers alGenBuffers
#define qalDeleteBuffers alDeleteBuffers
#define qalIsBuffer alIsBuffer
#define qalBufferData alBufferData
#define qalBufferf alBufferf
#define qalBuffer3f alBuffer3f
#define qalBufferfv alBufferfv
#define qalBufferi alBufferi
#define qalBuffer3i alBuffer3i
#define qalBufferiv alBufferiv
#define qalGetBufferf alGetBufferf
#define qalGetBuffer3f alGetBuffer3f
#define qalGetBufferfv alGetBufferfv
#define qalGetBufferi alGetBufferi
#define qalGetBuffer3i alGetBuffer3i
#define qalGetBufferiv alGetBufferiv
#define qalDopplerFactor alDopplerFactor
#define qalDopplerVelocity alDopplerVelocity
#define qalSpeedOfSound alSpeedOfSound
#define qalDistanceModel alDistanceModel
#define qalcCreateContext alcCreateContext
#define qalcMakeContextCurrent alcMakeContextCurrent
#define qalcProcessContext alcProcessContext
#define qalcSuspendContext alcSuspendContext
#define qalcDestroyContext alcDestroyContext
#define qalcGetCurrentContext alcGetCurrentContext
#define qalcGetContextsDevice alcGetContextsDevice
#define qalcOpenDevice alcOpenDevice
#define qalcCloseDevice alcCloseDevice
#define qalcGetError alcGetError
#define qalcIsExtensionPresent alcIsExtensionPresent
#define qalcGetProcAddress alcGetProcAddress
#define qalcGetEnumValue alcGetEnumValue
#define qalcGetString alcGetString
#define qalcGetIntegerv alcGetIntegerv
#define qalcCaptureOpenDevice alcCaptureOpenDevice
#define qalcCaptureCloseDevice alcCaptureCloseDevice
#define qalcCaptureStart alcCaptureStart
#define qalcCaptureStop alcCaptureStop
#define qalcCaptureSamples alcCaptureSamples
#endif
qboolean QAL_Init(const char *libname);
void QAL_Shutdown( void );
#endif // __QAL_H__
+330
View File
@@ -0,0 +1,330 @@
/***********************************************************
Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
Netherlands.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the names of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
******************************************************************/
/*
** Intel/DVI ADPCM coder/decoder.
**
** The algorithm for this coder was taken from the IMA Compatability Project
** proceedings, Vol 2, Number 2; May 1992.
**
** Version 1.2, 18-Dec-92.
*/
#include "snd_local.h"
/* Intel ADPCM step variation table */
static int indexTable[16] = {
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8,
};
static int stepsizeTable[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
void S_AdpcmEncode( short indata[], char outdata[], int len, struct adpcm_state *state ) {
short *inp; /* Input buffer pointer */
signed char *outp; /* output buffer pointer */
int val; /* Current input sample value */
int sign; /* Current adpcm sign bit */
int delta; /* Current adpcm output value */
int diff; /* Difference between val and sample */
int step; /* Stepsize */
int valpred; /* Predicted output value */
int vpdiff; /* Current change to valpred */
int index; /* Current step change index */
int outputbuffer; /* place to keep previous 4-bit value */
int bufferstep; /* toggle between outputbuffer/output */
outp = (signed char *)outdata;
inp = indata;
valpred = state->sample;
index = state->index;
step = stepsizeTable[index];
outputbuffer = 0; // quiet a compiler warning
bufferstep = 1;
for ( ; len > 0 ; len-- ) {
val = *inp++;
/* Step 1 - compute difference with previous value */
diff = val - valpred;
sign = (diff < 0) ? 8 : 0;
if ( sign ) diff = (-diff);
/* Step 2 - Divide and clamp */
/* Note:
** This code *approximately* computes:
** delta = diff*4/step;
** vpdiff = (delta+0.5)*step/4;
** but in shift step bits are dropped. The net result of this is
** that even if you have fast mul/div hardware you cannot put it to
** good use since the fixup would be too expensive.
*/
delta = 0;
vpdiff = (step >> 3);
if ( diff >= step ) {
delta = 4;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 2;
diff -= step;
vpdiff += step;
}
step >>= 1;
if ( diff >= step ) {
delta |= 1;
vpdiff += step;
}
/* Step 3 - Update previous value */
if ( sign )
valpred -= vpdiff;
else
valpred += vpdiff;
/* Step 4 - Clamp previous value to 16 bits */
if ( valpred > 32767 )
valpred = 32767;
else if ( valpred < -32768 )
valpred = -32768;
/* Step 5 - Assemble value, update index and step values */
delta |= sign;
index += indexTable[delta];
if ( index < 0 ) index = 0;
if ( index > 88 ) index = 88;
step = stepsizeTable[index];
/* Step 6 - Output value */
if ( bufferstep ) {
outputbuffer = (delta << 4) & 0xf0;
} else {
*outp++ = (delta & 0x0f) | outputbuffer;
}
bufferstep = !bufferstep;
}
/* Output last step, if needed */
if ( !bufferstep )
*outp++ = outputbuffer;
state->sample = valpred;
state->index = index;
}
/* static */ void S_AdpcmDecode( const char indata[], short *outdata, int len, struct adpcm_state *state ) {
signed char *inp; /* Input buffer pointer */
int outp; /* output buffer pointer */
int sign; /* Current adpcm sign bit */
int delta; /* Current adpcm output value */
int step; /* Stepsize */
int valpred; /* Predicted value */
int vpdiff; /* Current change to valpred */
int index; /* Current step change index */
int inputbuffer; /* place to keep next 4-bit value */
int bufferstep; /* toggle between inputbuffer/input */
outp = 0;
inp = (signed char *)indata;
valpred = state->sample;
index = state->index;
step = stepsizeTable[index];
bufferstep = 0;
inputbuffer = 0; // quiet a compiler warning
for ( ; len > 0 ; len-- ) {
/* Step 1 - get the delta value */
if ( bufferstep ) {
delta = inputbuffer & 0xf;
} else {
inputbuffer = *inp++;
delta = (inputbuffer >> 4) & 0xf;
}
bufferstep = !bufferstep;
/* Step 2 - Find new index value (for later) */
index += indexTable[delta];
if ( index < 0 ) index = 0;
if ( index > 88 ) index = 88;
/* Step 3 - Separate sign and magnitude */
sign = delta & 8;
delta = delta & 7;
/* Step 4 - Compute difference and new predicted value */
/*
** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
** in adpcm_coder.
*/
vpdiff = step >> 3;
if ( delta & 4 ) vpdiff += step;
if ( delta & 2 ) vpdiff += step>>1;
if ( delta & 1 ) vpdiff += step>>2;
if ( sign )
valpred -= vpdiff;
else
valpred += vpdiff;
/* Step 5 - clamp output value */
if ( valpred > 32767 )
valpred = 32767;
else if ( valpred < -32768 )
valpred = -32768;
/* Step 6 - Update step value */
step = stepsizeTable[index];
/* Step 7 - Output value */
outdata[outp] = valpred;
outp++;
}
state->sample = valpred;
state->index = index;
}
/*
====================
S_AdpcmMemoryNeeded
Returns the amount of memory (in bytes) needed to store the samples in out internal adpcm format
====================
*/
int S_AdpcmMemoryNeeded( const wavinfo_t *info ) {
float scale;
int scaledSampleCount;
int sampleMemory;
int blockCount;
int headerMemory;
// determine scale to convert from input sampling rate to desired sampling rate
scale = (float)info->rate / dma.speed;
// calc number of samples at playback sampling rate
scaledSampleCount = info->samples / scale;
// calc memory need to store those samples using ADPCM at 4 bits per sample
sampleMemory = scaledSampleCount / 2;
// calc number of sample blocks needed of PAINTBUFFER_SIZE
blockCount = scaledSampleCount / PAINTBUFFER_SIZE;
if( scaledSampleCount % PAINTBUFFER_SIZE ) {
blockCount++;
}
// calc memory needed to store the block headers
headerMemory = blockCount * sizeof(adpcm_state_t);
return sampleMemory + headerMemory;
}
/*
====================
S_AdpcmGetSamples
====================
*/
void S_AdpcmGetSamples(sndBuffer *chunk, short *to) {
adpcm_state_t state;
byte *out;
// get the starting state from the block header
state.index = chunk->adpcm.index;
state.sample = chunk->adpcm.sample;
out = (byte *)chunk->sndChunk;
// get samples
S_AdpcmDecode((char *) out, to, SND_CHUNK_SIZE_BYTE*2, &state );
}
/*
====================
S_AdpcmEncodeSound
====================
*/
void S_AdpcmEncodeSound( sfx_t *sfx, short *samples ) {
adpcm_state_t state;
int inOffset;
int count;
int n;
sndBuffer *newchunk, *chunk;
byte *out;
inOffset = 0;
count = sfx->soundLength;
state.index = 0;
state.sample = samples[0];
chunk = NULL;
while( count ) {
n = count;
if( n > SND_CHUNK_SIZE_BYTE*2 ) {
n = SND_CHUNK_SIZE_BYTE*2;
}
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
// output the header
chunk->adpcm.index = state.index;
chunk->adpcm.sample = state.sample;
out = (byte *)chunk->sndChunk;
// encode the samples
S_AdpcmEncode( samples + inOffset, (char *) out, n, &state );
inOffset += n;
count -= n;
}
}
+234
View File
@@ -0,0 +1,234 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
static snd_codec_t *codecs;
/*
=================
S_FileExtension
=================
*/
static char *S_FileExtension(const char *fni)
{
// we should search from the ending to the last '/'
char *fn = (char *) fni + strlen(fni) - 1;
char *eptr = NULL;
while(*fn != '/' && fn != fni)
{
if(*fn == '.')
eptr = fn;
fn--;
}
return eptr;
}
/*
=================
S_FindCodecForFile
Select an appropriate codec for a file based on its extension
=================
*/
static snd_codec_t *S_FindCodecForFile(const char *filename)
{
char *ext = S_FileExtension(filename);
snd_codec_t *codec = codecs;
if(!ext)
{
// No extension - auto-detect
while(codec)
{
char fn[MAX_QPATH];
// there is no extension so we do not need to subtract 4 chars
Q_strncpyz(fn, filename, MAX_QPATH);
COM_DefaultExtension(fn, MAX_QPATH, codec->ext);
// Check it exists
if(FS_ReadFile(fn, NULL) != -1)
return codec;
// Nope. Next!
codec = codec->next;
}
// Nothin'
return NULL;
}
while(codec)
{
if(!Q_stricmp(ext, codec->ext))
return codec;
codec = codec->next;
}
return NULL;
}
/*
=================
S_CodecInit
=================
*/
void S_CodecInit()
{
codecs = NULL;
S_CodecRegister(&wav_codec);
#if USE_CODEC_VORBIS
S_CodecRegister(&ogg_codec);
#endif
}
/*
=================
S_CodecShutdown
=================
*/
void S_CodecShutdown()
{
codecs = NULL;
}
/*
=================
S_CodecRegister
=================
*/
void S_CodecRegister(snd_codec_t *codec)
{
codec->next = codecs;
codecs = codec;
}
/*
=================
S_CodecLoad
=================
*/
void *S_CodecLoad(const char *filename, snd_info_t *info)
{
snd_codec_t *codec;
char fn[MAX_QPATH];
codec = S_FindCodecForFile(filename);
if(!codec)
{
Com_Printf("Unknown extension for %s\n", filename);
return NULL;
}
strncpy(fn, filename, sizeof(fn));
COM_DefaultExtension(fn, sizeof(fn), codec->ext);
return codec->load(fn, info);
}
/*
=================
S_CodecOpenStream
=================
*/
snd_stream_t *S_CodecOpenStream(const char *filename)
{
snd_codec_t *codec;
char fn[MAX_QPATH];
codec = S_FindCodecForFile(filename);
if(!codec)
{
Com_Printf("Unknown extension for %s\n", filename);
return NULL;
}
strncpy(fn, filename, sizeof(fn));
COM_DefaultExtension(fn, sizeof(fn), codec->ext);
return codec->open(fn);
}
void S_CodecCloseStream(snd_stream_t *stream)
{
stream->codec->close(stream);
}
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
return stream->codec->read(stream, bytes, buffer);
}
//=======================================================================
// Util functions (used by codecs)
/*
=================
S_CodecUtilOpen
=================
*/
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec)
{
snd_stream_t *stream;
fileHandle_t hnd;
int length;
// Try to open the file
length = FS_FOpenFileRead(filename, &hnd, qtrue);
if(!hnd)
{
Com_Printf("Can't read sound file %s\n", filename);
return NULL;
}
// Allocate a stream
stream = Z_Malloc(sizeof(snd_stream_t));
if(!stream)
{
FS_FCloseFile(hnd);
return NULL;
}
// Copy over, return
stream->codec = codec;
stream->file = hnd;
stream->length = length;
return stream;
}
/*
=================
S_CodecUtilClose
=================
*/
void S_CodecUtilClose(snd_stream_t *stream)
{
FS_FCloseFile(stream->file);
Z_Free(stream);
}
+99
View File
@@ -0,0 +1,99 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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
===========================================================================
*/
#ifndef _SND_CODEC_H_
#define _SND_CODEC_H_
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
typedef struct snd_info_s
{
int rate;
int width;
int channels;
int samples;
int size;
int dataofs;
} snd_info_t;
typedef struct snd_codec_s snd_codec_t;
typedef struct snd_stream_s
{
snd_codec_t *codec;
fileHandle_t file;
snd_info_t info;
int length;
int pos;
void *ptr;
} snd_stream_t;
// Codec functions
typedef void *(*CODEC_LOAD)(const char *filename, snd_info_t *info);
typedef snd_stream_t *(*CODEC_OPEN)(const char *filename);
typedef int (*CODEC_READ)(snd_stream_t *stream, int bytes, void *buffer);
typedef void (*CODEC_CLOSE)(snd_stream_t *stream);
// Codec data structure
struct snd_codec_s
{
char *ext;
CODEC_LOAD load;
CODEC_OPEN open;
CODEC_READ read;
CODEC_CLOSE close;
snd_codec_t *next;
};
// Codec management
void S_CodecInit( void );
void S_CodecShutdown( void );
void S_CodecRegister(snd_codec_t *codec);
void *S_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_CodecOpenStream(const char *filename);
void S_CodecCloseStream(snd_stream_t *stream);
int S_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Util functions (used by codecs)
snd_stream_t *S_CodecUtilOpen(const char *filename, snd_codec_t *codec);
void S_CodecUtilClose(snd_stream_t *stream);
// WAV Codec
extern snd_codec_t wav_codec;
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_WAV_CodecOpenStream(const char *filename);
void S_WAV_CodecCloseStream(snd_stream_t *stream);
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
// Ogg Vorbis codec
#ifdef USE_CODEC_VORBIS
extern snd_codec_t ogg_codec;
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info);
snd_stream_t *S_OGG_CodecOpenStream(const char *filename);
void S_OGG_CodecCloseStream(snd_stream_t *stream);
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
#endif // USE_CODEC_VORBIS
#endif // !_SND_CODEC_H_
+473
View File
@@ -0,0 +1,473 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de>
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
===========================================================================
*/
// OGG support is enabled by this define
#if USE_CODEC_VORBIS
// includes for the Q3 sound system
#include "client.h"
#include "snd_codec.h"
// includes for the OGG codec
#include <errno.h>
#include <vorbis/vorbisfile.h>
// The OGG codec can return the samples in a number of different formats,
// we use the standard signed short format.
#define OGG_SAMPLEWIDTH 2
// Q3 OGG codec
snd_codec_t ogg_codec =
{
".ogg",
S_OGG_CodecLoad,
S_OGG_CodecOpenStream,
S_OGG_CodecReadStream,
S_OGG_CodecCloseStream,
NULL
};
// callbacks for vobisfile
// fread() replacement
size_t S_OGG_Callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
{
snd_stream_t *stream;
int byteSize = 0;
int bytesRead = 0;
size_t nMembRead = 0;
// check if input is valid
if(!ptr)
{
errno = EFAULT;
return 0;
}
if(!(size && nmemb))
{
// It's not an error, caller just wants zero bytes!
errno = 0;
return 0;
}
if(!datasource)
{
errno = EBADF;
return 0;
}
// we use a snd_stream_t in the generic pointer to pass around
stream = (snd_stream_t *) datasource;
// FS_Read does not support multi-byte elements
byteSize = nmemb * size;
// read it with the Q3 function FS_Read()
bytesRead = FS_Read(ptr, byteSize, stream->file);
// update the file position
stream->pos += bytesRead;
// this function returns the number of elements read not the number of bytes
nMembRead = bytesRead / size;
// even if the last member is only read partially
// it is counted as a whole in the return value
if(bytesRead % size)
{
nMembRead++;
}
return nMembRead;
}
// fseek() replacement
int S_OGG_Callback_seek(void *datasource, ogg_int64_t offset, int whence)
{
snd_stream_t *stream;
int retVal = 0;
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// snd_stream_t in the generic pointer
stream = (snd_stream_t *) datasource;
// we must map the whence to its Q3 counterpart
switch(whence)
{
case SEEK_SET :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(!(retVal == 0))
{
return retVal;
}
// keep track of file position
stream->pos = (int) offset;
break;
}
case SEEK_CUR :
{
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) offset, FS_SEEK_CUR);
// something has gone wrong, so we return here
if(!(retVal == 0))
{
return retVal;
}
// keep track of file position
stream->pos += (int) offset;
break;
}
case SEEK_END :
{
// Quake 3 seems to have trouble with FS_SEEK_END
// so we use the file length and FS_SEEK_SET
// set the file position in the actual file with the Q3 function
retVal = FS_Seek(stream->file, (long) stream->length + (long) offset, FS_SEEK_SET);
// something has gone wrong, so we return here
if(!(retVal == 0))
{
return retVal;
}
// keep track of file position
stream->pos = stream->length + (int) offset;
break;
}
default :
{
// unknown whence, so we return an error
errno = EINVAL;
return -1;
}
}
// stream->pos shouldn't be smaller than zero or bigger than the filesize
stream->pos = (stream->pos < 0) ? 0 : stream->pos;
stream->pos = (stream->pos > stream->length) ? stream->length : stream->pos;
return 0;
}
// fclose() replacement
int S_OGG_Callback_close(void *datasource)
{
// we do nothing here and close all things manually in S_OGG_CodecCloseStream()
return 0;
}
// ftell() replacement
long S_OGG_Callback_tell(void *datasource)
{
// check if input is valid
if(!datasource)
{
errno = EBADF;
return -1;
}
// we keep track of the file position in stream->pos
return (long) (((snd_stream_t *) datasource) -> pos);
}
// the callback structure
const ov_callbacks S_OGG_Callbacks =
{
&S_OGG_Callback_read,
&S_OGG_Callback_seek,
&S_OGG_Callback_close,
&S_OGG_Callback_tell
};
/*
=================
S_OGG_CodecOpenStream
=================
*/
snd_stream_t *S_OGG_CodecOpenStream(const char *filename)
{
snd_stream_t *stream;
// OGG codec control structure
OggVorbis_File *vf;
// some variables used to get informations about the OGG
vorbis_info *OGGInfo;
ogg_int64_t numSamples;
// check if input is valid
if(!filename)
{
return NULL;
}
// Open the stream
stream = S_CodecUtilOpen(filename, &ogg_codec);
if(!stream)
{
return NULL;
}
// alloctate the OggVorbis_File
vf = Z_Malloc(sizeof(OggVorbis_File));
if(!vf)
{
S_CodecUtilClose(stream);
return NULL;
}
// open the codec with our callbacks and stream as the generic pointer
if(ov_open_callbacks(stream, vf, NULL, 0, S_OGG_Callbacks) != 0)
{
Z_Free(vf);
S_CodecUtilClose(stream);
return NULL;
}
// the stream must be seekable
if(!ov_seekable(vf))
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(stream);
return NULL;
}
// we only support OGGs with one substream
if(ov_streams(vf) != 1)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(stream);
return NULL;
}
// get the info about channels and rate
OGGInfo = ov_info(vf, 0);
if(!OGGInfo)
{
ov_clear(vf);
Z_Free(vf);
S_CodecUtilClose(stream);
return NULL;
}
// get the number of sample-frames in the OGG
numSamples = ov_pcm_total(vf, 0);
// fill in the info-structure in the stream
stream->info.rate = OGGInfo->rate;
stream->info.width = OGG_SAMPLEWIDTH;
stream->info.channels = OGGInfo->channels;
stream->info.samples = numSamples;
stream->info.size = stream->info.samples * stream->info.channels * stream->info.width;
stream->info.dataofs = 0;
// We use stream->pos for the file pointer in the compressed ogg file
stream->pos = 0;
// We use the generic pointer in stream for the OGG codec control structure
stream->ptr = vf;
return stream;
}
/*
=================
S_OGG_CodecCloseStream
=================
*/
void S_OGG_CodecCloseStream(snd_stream_t *stream)
{
// check if input is valid
if(!stream)
{
return;
}
// let the OGG codec cleanup its stuff
ov_clear((OggVorbis_File *) stream->ptr);
// free the OGG codec control struct
Z_Free(stream->ptr);
// close the stream
S_CodecUtilClose(stream);
}
/*
=================
S_OGG_CodecReadStream
=================
*/
int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
// buffer handling
int bytesRead, bytesLeft, c;
char *bufPtr;
// Bitstream for the decoder
int BS = 0;
// big endian machines want their samples in big endian order
int IsBigEndian = 0;
# ifdef Q3_BIG_ENDIAN
IsBigEndian = 1;
# endif // Q3_BIG_ENDIAN
// check if input is valid
if(!(stream && buffer))
{
return 0;
}
if(bytes <= 0)
{
return 0;
}
bytesRead = 0;
bytesLeft = bytes;
bufPtr = buffer;
// cycle until we have the requested or all available bytes read
while(-1)
{
// read some bytes from the OGG codec
c = ov_read((OggVorbis_File *) stream->ptr, bufPtr, bytesLeft, IsBigEndian, OGG_SAMPLEWIDTH, 1, &BS);
// no more bytes are left
if(c <= 0)
{
break;
}
bytesRead += c;
bytesLeft -= c;
bufPtr += c;
// we have enough bytes
if(bytesLeft <= 0)
{
break;
}
}
return bytesRead;
}
/*
=====================================================================
S_OGG_CodecLoad
We handle S_OGG_CodecLoad as a special case of the streaming functions
where we read the whole stream at once.
======================================================================
*/
void *S_OGG_CodecLoad(const char *filename, snd_info_t *info)
{
snd_stream_t *stream;
byte *buffer;
int bytesRead;
// check if input is valid
if(!(filename && info))
{
return NULL;
}
// open the file as a stream
stream = S_OGG_CodecOpenStream(filename);
if(!stream)
{
return NULL;
}
// copy over the info
info->rate = stream->info.rate;
info->width = stream->info.width;
info->channels = stream->info.channels;
info->samples = stream->info.samples;
info->size = stream->info.size;
info->dataofs = stream->info.dataofs;
// allocate a buffer
// this buffer must be free-ed by the caller of this function
buffer = Z_Malloc(info->size);
if(!buffer)
{
S_OGG_CodecCloseStream(stream);
return NULL;
}
// fill the buffer
bytesRead = S_OGG_CodecReadStream(stream, info->size, buffer);
// we don't even have read a single byte
if(bytesRead <= 0)
{
Z_Free(buffer);
S_OGG_CodecCloseStream(stream);
return NULL;
}
S_OGG_CodecCloseStream(stream);
return buffer;
}
#endif // USE_CODEC_VORBIS
+295
View File
@@ -0,0 +1,295 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
/*
=================
FGetLittleLong
=================
*/
static int FGetLittleLong( fileHandle_t f ) {
int v;
FS_Read( &v, sizeof(v), f );
return LittleLong( v);
}
/*
=================
FGetLittleShort
=================
*/
static short FGetLittleShort( fileHandle_t f ) {
short v;
FS_Read( &v, sizeof(v), f );
return LittleShort( v);
}
/*
=================
S_ReadChunkInfo
=================
*/
static int S_ReadChunkInfo(fileHandle_t f, char *name)
{
int len, r;
name[4] = 0;
r = FS_Read(name, 4, f);
if(r != 4)
return -1;
len = FGetLittleLong(f);
if( len < 0 ) {
Com_Printf( S_COLOR_YELLOW "WARNING: Negative chunk length\n" );
return -1;
}
return len;
}
/*
=================
S_FindRIFFChunk
Returns the length of the data in the chunk, or -1 if not found
=================
*/
static int S_FindRIFFChunk( fileHandle_t f, char *chunk ) {
char name[5];
int len;
while( ( len = S_ReadChunkInfo(f, name) ) >= 0 )
{
// If this is the right chunk, return
if( !Q_strncmp( name, chunk, 4 ) )
return len;
len = PAD( len, 2 );
// Not the right chunk - skip it
FS_Seek( f, len, FS_SEEK_CUR );
}
return -1;
}
/*
=================
S_ByteSwapRawSamples
=================
*/
static void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
int i;
if ( width != 2 ) {
return;
}
if ( LittleShort( 256 ) == 256 ) {
return;
}
if ( s_channels == 2 ) {
samples <<= 1;
}
for ( i = 0 ; i < samples ; i++ ) {
((short *)data)[i] = LittleShort( ((short *)data)[i] );
}
}
/*
=================
S_ReadRIFFHeader
=================
*/
static qboolean S_ReadRIFFHeader(fileHandle_t file, snd_info_t *info)
{
char dump[16];
int wav_format;
int bits;
int fmtlen = 0;
// skip the riff wav header
FS_Read(dump, 12, file);
// Scan for the format chunk
if((fmtlen = S_FindRIFFChunk(file, "fmt ")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"fmt\" chunk\n");
return qfalse;
}
// Save the parameters
wav_format = FGetLittleShort(file);
info->channels = FGetLittleShort(file);
info->rate = FGetLittleLong(file);
FGetLittleLong(file);
FGetLittleShort(file);
bits = FGetLittleShort(file);
if( bits < 8 )
{
Com_Printf( S_COLOR_RED "ERROR: Less than 8 bit sound is not supported\n");
return qfalse;
}
info->width = bits / 8;
info->dataofs = 0;
// Skip the rest of the format chunk if required
if(fmtlen > 16)
{
fmtlen -= 16;
FS_Seek( file, fmtlen, FS_SEEK_CUR );
}
// Scan for the data chunk
if( (info->size = S_FindRIFFChunk(file, "data")) < 0)
{
Com_Printf( S_COLOR_RED "ERROR: Couldn't find \"data\" chunk\n");
return qfalse;
}
info->samples = (info->size / info->width) / info->channels;
return qtrue;
}
// WAV codec
snd_codec_t wav_codec =
{
".wav",
S_WAV_CodecLoad,
S_WAV_CodecOpenStream,
S_WAV_CodecReadStream,
S_WAV_CodecCloseStream,
NULL
};
/*
=================
S_WAV_CodecLoad
=================
*/
void *S_WAV_CodecLoad(const char *filename, snd_info_t *info)
{
fileHandle_t file;
void *buffer;
// Try to open the file
FS_FOpenFileRead(filename, &file, qtrue);
if(!file)
{
Com_Printf( S_COLOR_RED "ERROR: Could not open \"%s\"\n",
filename);
return NULL;
}
// Read the RIFF header
if(!S_ReadRIFFHeader(file, info))
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Incorrect/unsupported format in \"%s\"\n",
filename);
return NULL;
}
// Allocate some memory
buffer = Z_Malloc(info->size);
if(!buffer)
{
FS_FCloseFile(file);
Com_Printf( S_COLOR_RED "ERROR: Out of memory reading \"%s\"\n",
filename);
return NULL;
}
// Read, byteswap
FS_Read(buffer, info->size, file);
S_ByteSwapRawSamples(info->samples, info->width, info->channels, (byte *)buffer);
// Close and return
FS_FCloseFile(file);
return buffer;
}
/*
=================
S_WAV_CodecOpenStream
=================
*/
snd_stream_t *S_WAV_CodecOpenStream(const char *filename)
{
snd_stream_t *rv;
// Open
rv = S_CodecUtilOpen(filename, &wav_codec);
if(!rv)
return NULL;
// Read the RIFF header
if(!S_ReadRIFFHeader(rv->file, &rv->info))
{
S_CodecUtilClose(rv);
return NULL;
}
return rv;
}
/*
=================
S_WAV_CodecCloseStream
=================
*/
void S_WAV_CodecCloseStream(snd_stream_t *stream)
{
S_CodecUtilClose(stream);
}
/*
=================
S_WAV_CodecReadStream
=================
*/
int S_WAV_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
{
int remaining = stream->info.size - stream->pos;
int samples;
if(remaining <= 0)
return 0;
if(bytes > remaining)
bytes = remaining;
stream->pos += bytes;
samples = (bytes / stream->info.width) / stream->info.channels;
FS_Read(buffer, bytes, stream->file);
S_ByteSwapRawSamples(samples, stream->info.width, stream->info.channels, buffer);
return bytes;
}
+1500
View File
File diff suppressed because it is too large. Load diff
+244
View File
@@ -0,0 +1,244 @@
/*
===========================================================================
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
===========================================================================
*/
// snd_local.h -- private sound definations
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "snd_public.h"
#define PAINTBUFFER_SIZE 4096 // this is in samples
#define SND_CHUNK_SIZE 1024 // samples
#define SND_CHUNK_SIZE_FLOAT (SND_CHUNK_SIZE/2) // floats
#define SND_CHUNK_SIZE_BYTE (SND_CHUNK_SIZE*2) // floats
typedef struct {
int left; // the final values will be clamped to +/- 0x00ffff00 and shifted down
int right;
} portable_samplepair_t;
typedef struct adpcm_state {
short sample; /* Previous output value */
char index; /* Index into stepsize table */
} adpcm_state_t;
typedef struct sndBuffer_s {
short sndChunk[SND_CHUNK_SIZE];
struct sndBuffer_s *next;
int size;
adpcm_state_t adpcm;
} sndBuffer;
typedef struct sfx_s {
sndBuffer *soundData;
qboolean defaultSound; // couldn't be loaded, so use buzz
qboolean inMemory; // not in Memory
qboolean soundCompressed; // not in Memory
int soundCompressionMethod;
int soundLength;
char soundName[MAX_QPATH];
int lastTimeUsed;
struct sfx_s *next;
} sfx_t;
typedef struct {
int channels;
int samples; // mono samples in buffer
int submission_chunk; // don't mix less than this #
int samplebits;
int speed;
byte *buffer;
} dma_t;
#define START_SAMPLE_IMMEDIATE 0x7fffffff
#define MAX_DOPPLER_SCALE 50.0f //arbitrary
typedef struct loopSound_s {
vec3_t origin;
vec3_t velocity;
sfx_t *sfx;
int mergeFrame;
qboolean active;
qboolean kill;
qboolean doppler;
float dopplerScale;
float oldDopplerScale;
int framenum;
} loopSound_t;
typedef struct
{
int allocTime;
int startSample; // START_SAMPLE_IMMEDIATE = set immediately on next mix
int entnum; // to allow overriding a specific sound
int entchannel; // to allow overriding a specific sound
int leftvol; // 0-255 volume after spatialization
int rightvol; // 0-255 volume after spatialization
int master_vol; // 0-255 volume before spatialization
float dopplerScale;
float oldDopplerScale;
vec3_t origin; // only use if fixed_origin is set
qboolean fixed_origin; // use origin instead of fetching entnum's origin
sfx_t *thesfx; // sfx structure
qboolean doppler;
} channel_t;
#define WAV_FORMAT_PCM 1
typedef struct {
int format;
int rate;
int width;
int channels;
int samples;
int dataofs; // chunk starts this many bytes from file start
} wavinfo_t;
// Interface between Q3 sound "api" and the sound backend
typedef struct
{
void (*Shutdown)(void);
void (*StartSound)( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
void (*StartLocalSound)( sfxHandle_t sfx, int channelNum );
void (*StartBackgroundTrack)( const char *intro, const char *loop );
void (*StopBackgroundTrack)( void );
void (*RawSamples)(int samples, int rate, int width, int channels, const byte *data, float volume);
void (*StopAllSounds)( void );
void (*ClearLoopingSounds)( qboolean killall );
void (*AddLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void (*AddRealLoopingSound)( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void (*StopLoopingSound)(int entityNum );
void (*Respatialize)( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
void (*UpdateEntityPosition)( int entityNum, const vec3_t origin );
void (*Update)( void );
void (*DisableSounds)( void );
void (*BeginRegistration)( void );
sfxHandle_t (*RegisterSound)( const char *sample, qboolean compressed );
void (*ClearSoundBuffer)( void );
void (*SoundInfo)( void );
void (*SoundList)( void );
} soundInterface_t;
/*
====================================================================
SYSTEM SPECIFIC FUNCTIONS
====================================================================
*/
// initializes cycling through a DMA buffer and returns information on it
qboolean SNDDMA_Init(void);
// gets the current DMA position
int SNDDMA_GetDMAPos(void);
// shutdown the DMA xfer.
void SNDDMA_Shutdown(void);
void SNDDMA_BeginPainting (void);
void SNDDMA_Submit(void);
//====================================================================
#define MAX_CHANNELS 96
extern channel_t s_channels[MAX_CHANNELS];
extern channel_t loop_channels[MAX_CHANNELS];
extern int numLoopChannels;
extern int s_paintedtime;
extern int s_rawend;
extern vec3_t listener_forward;
extern vec3_t listener_right;
extern vec3_t listener_up;
extern dma_t dma;
#define MAX_RAW_SAMPLES 16384
extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
extern cvar_t *s_volume;
extern cvar_t *s_musicVolume;
extern cvar_t *s_doppler;
extern cvar_t *s_testsound;
qboolean S_LoadSound( sfx_t *sfx );
void SND_free(sndBuffer *v);
sndBuffer* SND_malloc( void );
void SND_setup( void );
void S_PaintChannels(int endtime);
void S_memoryLoad(sfx_t *sfx);
portable_samplepair_t *S_GetRawSamplePointer( void );
// spatializes a channel
void S_Spatialize(channel_t *ch);
// adpcm functions
int S_AdpcmMemoryNeeded( const wavinfo_t *info );
void S_AdpcmEncodeSound( sfx_t *sfx, short *samples );
void S_AdpcmGetSamples(sndBuffer *chunk, short *to);
// wavelet function
#define SENTINEL_MULAW_ZERO_RUN 127
#define SENTINEL_MULAW_FOUR_BIT_RUN 126
void S_FreeOldestSound( void );
#define NXStream byte
void encodeWavelet(sfx_t *sfx, short *packets);
void decodeWavelet( sndBuffer *stream, short *packets);
void encodeMuLaw( sfx_t *sfx, short *packets);
extern short mulawToShort[256];
extern short *sfxScratchBuffer;
extern sfx_t *sfxScratchPointer;
extern int sfxScratchIndex;
qboolean S_Base_Init( soundInterface_t *si );
// OpenAL stuff
typedef enum
{
SRCPRI_AMBIENT = 0, // Ambient sound effects
SRCPRI_ENTITY, // Entity sound effects
SRCPRI_ONESHOT, // One-shot sounds
SRCPRI_LOCAL, // Local sounds
SRCPRI_STREAM // Streams (music, cutscenes)
} alSrcPriority_t;
typedef int srcHandle_t;
qboolean S_AL_Init( soundInterface_t *si );
+438
View File
@@ -0,0 +1,438 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
#include "snd_codec.h"
#include "snd_local.h"
#include "snd_public.h"
cvar_t *s_volume;
cvar_t *s_musicVolume;
cvar_t *s_doppler;
cvar_t *s_backend;
static soundInterface_t si;
/*
=================
S_ValidateInterface
=================
*/
static qboolean S_ValidSoundInterface( soundInterface_t *si )
{
if( !si->Shutdown ) return qfalse;
if( !si->StartSound ) return qfalse;
if( !si->StartLocalSound ) return qfalse;
if( !si->StartBackgroundTrack ) return qfalse;
if( !si->StopBackgroundTrack ) return qfalse;
if( !si->RawSamples ) return qfalse;
if( !si->StopAllSounds ) return qfalse;
if( !si->ClearLoopingSounds ) return qfalse;
if( !si->AddLoopingSound ) return qfalse;
if( !si->AddRealLoopingSound ) return qfalse;
if( !si->StopLoopingSound ) return qfalse;
if( !si->Respatialize ) return qfalse;
if( !si->UpdateEntityPosition ) return qfalse;
if( !si->Update ) return qfalse;
if( !si->DisableSounds ) return qfalse;
if( !si->BeginRegistration ) return qfalse;
if( !si->RegisterSound ) return qfalse;
if( !si->ClearSoundBuffer ) return qfalse;
if( !si->SoundInfo ) return qfalse;
if( !si->SoundList ) return qfalse;
return qtrue;
}
/*
=================
S_StartSound
=================
*/
void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
{
if( si.StartSound ) {
si.StartSound( origin, entnum, entchannel, sfx );
}
}
/*
=================
S_StartLocalSound
=================
*/
void S_StartLocalSound( sfxHandle_t sfx, int channelNum )
{
if( si.StartLocalSound ) {
si.StartLocalSound( sfx, channelNum );
}
}
/*
=================
S_StartBackgroundTrack
=================
*/
void S_StartBackgroundTrack( const char *intro, const char *loop )
{
if( si.StartBackgroundTrack ) {
si.StartBackgroundTrack( intro, loop );
}
}
/*
=================
S_StopBackgroundTrack
=================
*/
void S_StopBackgroundTrack( void )
{
if( si.StopBackgroundTrack ) {
si.StopBackgroundTrack( );
}
}
/*
=================
S_RawSamples
=================
*/
void S_RawSamples (int samples, int rate, int width, int channels,
const byte *data, float volume)
{
if( si.RawSamples ) {
si.RawSamples( samples, rate, width, channels, data, volume );
}
}
/*
=================
S_StopAllSounds
=================
*/
void S_StopAllSounds( void )
{
if( si.StopAllSounds ) {
si.StopAllSounds( );
}
}
/*
=================
S_ClearLoopingSounds
=================
*/
void S_ClearLoopingSounds( qboolean killall )
{
if( si.ClearLoopingSounds ) {
si.ClearLoopingSounds( killall );
}
}
/*
=================
S_AddLoopingSound
=================
*/
void S_AddLoopingSound( int entityNum, const vec3_t origin,
const vec3_t velocity, sfxHandle_t sfx )
{
if( si.AddLoopingSound ) {
si.AddLoopingSound( entityNum, origin, velocity, sfx );
}
}
/*
=================
S_AddRealLoopingSound
=================
*/
void S_AddRealLoopingSound( int entityNum, const vec3_t origin,
const vec3_t velocity, sfxHandle_t sfx )
{
if( si.AddRealLoopingSound ) {
si.AddRealLoopingSound( entityNum, origin, velocity, sfx );
}
}
/*
=================
S_StopLoopingSound
=================
*/
void S_StopLoopingSound( int entityNum )
{
if( si.StopLoopingSound ) {
si.StopLoopingSound( entityNum );
}
}
/*
=================
S_Respatialize
=================
*/
void S_Respatialize( int entityNum, const vec3_t origin,
vec3_t axis[3], int inwater )
{
if( si.Respatialize ) {
si.Respatialize( entityNum, origin, axis, inwater );
}
}
/*
=================
S_UpdateEntityPosition
=================
*/
void S_UpdateEntityPosition( int entityNum, const vec3_t origin )
{
if( si.UpdateEntityPosition ) {
si.UpdateEntityPosition( entityNum, origin );
}
}
/*
=================
S_Update
=================
*/
void S_Update( void )
{
if( si.Update ) {
si.Update( );
}
}
/*
=================
S_DisableSounds
=================
*/
void S_DisableSounds( void )
{
if( si.DisableSounds ) {
si.DisableSounds( );
}
}
/*
=================
S_BeginRegistration
=================
*/
void S_BeginRegistration( void )
{
if( si.BeginRegistration ) {
si.BeginRegistration( );
}
}
/*
=================
S_RegisterSound
=================
*/
sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed )
{
if( si.RegisterSound ) {
return si.RegisterSound( sample, compressed );
} else {
return 0;
}
}
/*
=================
S_ClearSoundBuffer
=================
*/
void S_ClearSoundBuffer( void )
{
if( si.ClearSoundBuffer ) {
si.ClearSoundBuffer( );
}
}
/*
=================
S_SoundInfo
=================
*/
void S_SoundInfo( void )
{
if( si.SoundInfo ) {
si.SoundInfo( );
}
}
/*
=================
S_SoundList
=================
*/
void S_SoundList( void )
{
if( si.SoundList ) {
si.SoundList( );
}
}
//=============================================================================
/*
=================
S_Play_f
=================
*/
void S_Play_f( void ) {
int i;
sfxHandle_t h;
char name[256];
if( !si.RegisterSound || !si.StartLocalSound ) {
return;
}
i = 1;
while ( i<Cmd_Argc() ) {
if ( !Q_strrchr(Cmd_Argv(i), '.') ) {
Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
} else {
Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
}
h = si.RegisterSound( name, qfalse );
if( h ) {
si.StartLocalSound( h, CHAN_LOCAL_SOUND );
}
i++;
}
}
/*
=================
S_Music_f
=================
*/
void S_Music_f( void ) {
int c;
if( !si.StartBackgroundTrack ) {
return;
}
c = Cmd_Argc();
if ( c == 2 ) {
si.StartBackgroundTrack( Cmd_Argv(1), NULL );
} else if ( c == 3 ) {
si.StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2) );
} else {
Com_Printf ("music <musicfile> [loopfile]\n");
return;
}
}
//=============================================================================
/*
=================
S_Init
=================
*/
void S_Init( void )
{
cvar_t *cv;
qboolean started = qfalse;
Com_Printf( "------ Initializing Sound ------\n" );
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 ) {
Com_Printf( "Sound disabled.\n" );
} else {
S_CodecInit( );
Cmd_AddCommand( "play", S_Play_f );
Cmd_AddCommand( "music", S_Music_f );
Cmd_AddCommand( "s_list", S_SoundList );
Cmd_AddCommand( "s_stop", S_StopAllSounds );
Cmd_AddCommand( "s_info", S_SoundInfo );
cv = Cvar_Get( "s_useOpenAL", "0", CVAR_ARCHIVE );
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 ) {
if( !S_ValidSoundInterface( &si ) ) {
Com_Error( ERR_FATAL, "Sound interface invalid." );
}
S_SoundInfo( );
Com_Printf( "Sound initialization successful.\n" );
} else {
Com_Printf( "Sound initialization failed.\n" );
}
}
Com_Printf( "--------------------------------\n");
}
/*
=================
S_Shutdown
=================
*/
void S_Shutdown( void )
{
if( si.Shutdown ) {
si.Shutdown( );
}
Com_Memset( &si, 0, sizeof( soundInterface_t ) );
Cmd_RemoveCommand( "play" );
Cmd_RemoveCommand( "music");
Cmd_RemoveCommand( "s_list" );
Cmd_RemoveCommand( "s_stop" );
Cmd_RemoveCommand( "s_info" );
S_CodecShutdown( );
}
+266
View File
@@ -0,0 +1,266 @@
/*
===========================================================================
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
===========================================================================
*/
/*****************************************************************************
* name: snd_mem.c
*
* desc: sound caching
*
* $Archive: /MissionPack/code/client/snd_mem.c $
*
*****************************************************************************/
#include "snd_local.h"
#include "snd_codec.h"
#define DEF_COMSOUNDMEGS "8"
/*
===============================================================================
memory management
===============================================================================
*/
static sndBuffer *buffer = NULL;
static sndBuffer *freelist = NULL;
static int inUse = 0;
static int totalInUse = 0;
short *sfxScratchBuffer = NULL;
sfx_t *sfxScratchPointer = NULL;
int sfxScratchIndex = 0;
void SND_free(sndBuffer *v) {
*(sndBuffer **)v = freelist;
freelist = (sndBuffer*)v;
inUse += sizeof(sndBuffer);
}
sndBuffer* SND_malloc(void) {
sndBuffer *v;
redo:
if (freelist == NULL) {
S_FreeOldestSound();
goto redo;
}
inUse -= sizeof(sndBuffer);
totalInUse += sizeof(sndBuffer);
v = freelist;
freelist = *(sndBuffer **)freelist;
v->next = NULL;
return v;
}
void SND_setup(void) {
sndBuffer *p, *q;
cvar_t *cv;
int scs;
cv = Cvar_Get( "com_soundMegs", DEF_COMSOUNDMEGS, CVAR_LATCH | CVAR_ARCHIVE );
scs = (cv->integer*1536);
buffer = malloc(scs*sizeof(sndBuffer) );
// allocate the stack based hunk allocator
sfxScratchBuffer = malloc(SND_CHUNK_SIZE * sizeof(short) * 4); //Hunk_Alloc(SND_CHUNK_SIZE * sizeof(short) * 4);
sfxScratchPointer = NULL;
inUse = scs*sizeof(sndBuffer);
p = buffer;;
q = p + scs;
while (--q > p)
*(sndBuffer **)q = q-1;
*(sndBuffer **)q = NULL;
freelist = p + scs - 1;
Com_Printf("Sound memory manager started\n");
}
/*
================
ResampleSfx
resample / decimate to the current source rate
================
*/
static void ResampleSfx( sfx_t *sfx, int inrate, int inwidth, byte *data, qboolean compressed ) {
int outcount;
int srcsample;
float stepscale;
int i;
int sample, samplefrac, fracstep;
int part;
sndBuffer *chunk;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = sfx->soundLength / stepscale;
sfx->soundLength = outcount;
samplefrac = 0;
fracstep = stepscale * 256;
chunk = sfx->soundData;
for (i=0 ; i<outcount ; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if( inwidth == 2 ) {
sample = ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
part = (i&(SND_CHUNK_SIZE-1));
if (part == 0) {
sndBuffer *newchunk;
newchunk = SND_malloc();
if (chunk == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
}
chunk->sndChunk[part] = sample;
}
}
/*
================
ResampleSfx
resample / decimate to the current source rate
================
*/
static int ResampleSfxRaw( short *sfx, int inrate, int inwidth, int samples, byte *data ) {
int outcount;
int srcsample;
float stepscale;
int i;
int sample, samplefrac, fracstep;
stepscale = (float)inrate / dma.speed; // this is usually 0.5, 1, or 2
outcount = samples / stepscale;
samplefrac = 0;
fracstep = stepscale * 256;
for (i=0 ; i<outcount ; i++)
{
srcsample = samplefrac >> 8;
samplefrac += fracstep;
if( inwidth == 2 ) {
sample = LittleShort ( ((short *)data)[srcsample] );
} else {
sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8;
}
sfx[i] = sample;
}
return outcount;
}
//=============================================================================
/*
==============
S_LoadSound
The filename may be different than sfx->name in the case
of a forced fallback of a player specific sound
==============
*/
qboolean S_LoadSound( sfx_t *sfx )
{
byte *data;
short *samples;
snd_info_t info;
// int size;
// player specific sounds are never directly loaded
if ( sfx->soundName[0] == '*') {
return qfalse;
}
// load it in
data = S_CodecLoad(sfx->soundName, &info);
if(!data)
return qfalse;
if ( info.width == 1 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is a 8 bit wav file\n", sfx->soundName);
}
if ( info.rate != 22050 ) {
Com_DPrintf(S_COLOR_YELLOW "WARNING: %s is not a 22kHz wav file\n", sfx->soundName);
}
samples = Hunk_AllocateTempMemory(info.samples * sizeof(short) * 2);
sfx->lastTimeUsed = Com_Milliseconds()+1;
// each of these compression schemes works just fine
// but the 16bit quality is much nicer and with a local
// install assured we can rely upon the sound memory
// manager to do the right thing for us and page
// sound in as needed
if( sfx->soundCompressed == qtrue) {
sfx->soundCompressionMethod = 1;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, data + info.dataofs );
S_AdpcmEncodeSound(sfx, samples);
#if 0
} else if (info.samples>(SND_CHUNK_SIZE*16) && info.width >1) {
sfx->soundCompressionMethod = 3;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeMuLaw( sfx, samples);
} else if (info.samples>(SND_CHUNK_SIZE*6400) && info.width >1) {
sfx->soundCompressionMethod = 2;
sfx->soundData = NULL;
sfx->soundLength = ResampleSfxRaw( samples, info.rate, info.width, info.samples, (data + info.dataofs) );
encodeWavelet( sfx, samples);
#endif
} else {
sfx->soundCompressionMethod = 0;
sfx->soundLength = info.samples;
sfx->soundData = NULL;
ResampleSfx( sfx, info.rate, info.width, data + info.dataofs, qfalse );
}
Hunk_FreeTempMemory(samples);
Z_Free(data);
return qtrue;
}
void S_DisplayFreeMemory(void) {
Com_Printf("%d bytes free sound buffer memory, %d total used\n", inUse, totalInUse);
}
+752
View File
@@ -0,0 +1,752 @@
/*
===========================================================================
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
===========================================================================
*/
// 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>
#endif
static portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE];
static int snd_vol;
// bk001119 - these not static, required by unix/snd_mixa.s
int* snd_p;
int snd_linear_count;
short* snd_out;
#if !id386 // if configured not to use asm
void S_WriteLinearBlastStereo16 (void)
{
int i;
int val;
for (i=0 ; i<snd_linear_count ; i+=2)
{
val = snd_p[i]>>8;
if (val > 0x7fff)
snd_out[i] = 0x7fff;
else if (val < -32768)
snd_out[i] = -32768;
else
snd_out[i] = val;
val = snd_p[i+1]>>8;
if (val > 0x7fff)
snd_out[i+1] = 0x7fff;
else if (val < -32768)
snd_out[i+1] = -32768;
else
snd_out[i+1] = val;
}
}
#elif defined(__GNUC__)
// uses snd_mixa.s
void S_WriteLinearBlastStereo16 (void);
#else
__declspec( naked ) void S_WriteLinearBlastStereo16 (void)
{
__asm {
push edi
push ebx
mov ecx,ds:dword ptr[snd_linear_count]
mov ebx,ds:dword ptr[snd_p]
mov edi,ds:dword ptr[snd_out]
LWLBLoopTop:
mov eax,ds:dword ptr[-8+ebx+ecx*4]
sar eax,8
cmp eax,07FFFh
jg LClampHigh
cmp eax,0FFFF8000h
jnl LClampDone
mov eax,0FFFF8000h
jmp LClampDone
LClampHigh:
mov eax,07FFFh
LClampDone:
mov edx,ds:dword ptr[-4+ebx+ecx*4]
sar edx,8
cmp edx,07FFFh
jg LClampHigh2
cmp edx,0FFFF8000h
jnl LClampDone2
mov edx,0FFFF8000h
jmp LClampDone2
LClampHigh2:
mov edx,07FFFh
LClampDone2:
shl edx,16
and eax,0FFFFh
or edx,eax
mov ds:dword ptr[-4+edi+ecx*2],edx
sub ecx,2
jnz LWLBLoopTop
pop ebx
pop edi
ret
}
}
#endif
void S_TransferStereo16 (unsigned long *pbuf, int endtime)
{
int lpos;
int ls_paintedtime;
snd_p = (int *) paintbuffer;
ls_paintedtime = s_paintedtime;
while (ls_paintedtime < endtime)
{
// handle recirculating buffer issues
lpos = ls_paintedtime & ((dma.samples>>1)-1);
snd_out = (short *) pbuf + (lpos<<1);
snd_linear_count = (dma.samples>>1) - lpos;
if (ls_paintedtime + snd_linear_count > endtime)
snd_linear_count = endtime - ls_paintedtime;
snd_linear_count <<= 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16 ();
snd_p += snd_linear_count;
ls_paintedtime += (snd_linear_count>>1);
if( CL_VideoRecording( ) )
CL_WriteAVIAudioFrame( (byte *)snd_out, snd_linear_count << 1 );
}
}
/*
===================
S_TransferPaintBuffer
===================
*/
void S_TransferPaintBuffer(int endtime)
{
int out_idx;
int count;
int out_mask;
int *p;
int step;
int val;
unsigned long *pbuf;
pbuf = (unsigned long *)dma.buffer;
if ( s_testsound->integer ) {
int i;
int count;
// write a fixed sine wave
count = (endtime - s_paintedtime);
for (i=0 ; i<count ; i++)
paintbuffer[i].left = paintbuffer[i].right = sin((s_paintedtime+i)*0.1)*20000*256;
}
if (dma.samplebits == 16 && dma.channels == 2)
{ // optimized case
S_TransferStereo16 (pbuf, endtime);
}
else
{ // general case
p = (int *) paintbuffer;
count = (endtime - s_paintedtime) * dma.channels;
out_mask = dma.samples - 1;
out_idx = s_paintedtime * dma.channels & out_mask;
step = 3 - dma.channels;
if (dma.samplebits == 16)
{
short *out = (short *) pbuf;
while (count--)
{
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < -32768)
val = -32768;
out[out_idx] = val;
out_idx = (out_idx + 1) & out_mask;
}
}
else if (dma.samplebits == 8)
{
unsigned char *out = (unsigned char *) pbuf;
while (count--)
{
val = *p >> 8;
p+= step;
if (val > 0x7fff)
val = 0x7fff;
else if (val < -32768)
val = -32768;
out[out_idx] = (val>>8) + 128;
out_idx = (out_idx + 1) & out_mask;
}
}
}
}
/*
===============================================================================
CHANNEL MIXING
===============================================================================
*/
#if idppc_altivec
static void S_PaintChannelFrom16_altivec( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata, fdiv, fleftvol, frightvol;
samp = &paintbuffer[ bufferOffset ];
if (ch->doppler) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
chunk = sc->soundData;
while (sampleOffset>=SND_CHUNK_SIZE) {
chunk = chunk->next;
sampleOffset -= SND_CHUNK_SIZE;
if (!chunk) {
chunk = sc->soundData;
}
}
if (!ch->doppler || ch->dopplerScale==1.0f) {
vector signed short volume_vec;
vector unsigned int volume_shift;
int vectorCount, samplesLeft, chunkSamplesLeft;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
((short *)&volume_vec)[0] = leftvol;
((short *)&volume_vec)[1] = leftvol;
((short *)&volume_vec)[4] = leftvol;
((short *)&volume_vec)[5] = leftvol;
((short *)&volume_vec)[2] = rightvol;
((short *)&volume_vec)[3] = rightvol;
((short *)&volume_vec)[6] = rightvol;
((short *)&volume_vec)[7] = rightvol;
volume_shift = vec_splat_u32(8);
i = 0;
while(i < count) {
/* Try to align destination to 16-byte boundary */
while(i < count && (((unsigned long)&samp[i] & 0x1f) || ((count-i) < 8) || ((SND_CHUNK_SIZE - sampleOffset) < 8))) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
i++;
}
/* Destination is now aligned. Process as many 8-sample
chunks as we can before we run out of room from the current
sound chunk. We do 8 per loop to avoid extra source data reads. */
samplesLeft = count - i;
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
if(samplesLeft > chunkSamplesLeft)
samplesLeft = chunkSamplesLeft;
vectorCount = samplesLeft / 8;
if(vectorCount)
{
vector unsigned char tmp;
vector short s0, s1, sampleData0, sampleData1;
vector signed int merge0, merge1;
vector signed int d0, d1, d2, d3;
vector unsigned char samplePermute0 =
VECCONST_UINT8(0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7);
vector unsigned char samplePermute1 =
VECCONST_UINT8(8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15);
vector unsigned char loadPermute0, loadPermute1;
// Rather than permute the vectors after we load them to do the sample
// replication and rearrangement, we permute the alignment vector so
// we do everything in one step below and avoid data shuffling.
tmp = vec_lvsl(0,&samples[sampleOffset]);
loadPermute0 = vec_perm(tmp,tmp,samplePermute0);
loadPermute1 = vec_perm(tmp,tmp,samplePermute1);
s0 = *(vector short *)&samples[sampleOffset];
while(vectorCount)
{
/* Load up source (16-bit) sample data */
s1 = *(vector short *)&samples[sampleOffset+7];
/* Load up destination sample data */
d0 = *(vector signed int *)&samp[i];
d1 = *(vector signed int *)&samp[i+2];
d2 = *(vector signed int *)&samp[i+4];
d3 = *(vector signed int *)&samp[i+6];
sampleData0 = vec_perm(s0,s1,loadPermute0);
sampleData1 = vec_perm(s0,s1,loadPermute1);
merge0 = vec_mule(sampleData0,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData0,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d0 = vec_add(merge0,d0);
d1 = vec_add(merge1,d1);
merge0 = vec_mule(sampleData1,volume_vec);
merge0 = vec_sra(merge0,volume_shift); /* Shift down to proper range */
merge1 = vec_mulo(sampleData1,volume_vec);
merge1 = vec_sra(merge1,volume_shift);
d2 = vec_add(merge0,d2);
d3 = vec_add(merge1,d3);
/* Store destination sample data */
*(vector signed int *)&samp[i] = d0;
*(vector signed int *)&samp[i+2] = d1;
*(vector signed int *)&samp[i+4] = d2;
*(vector signed int *)&samp[i+6] = d3;
i += 8;
vectorCount--;
s0 = s1;
sampleOffset += 8;
}
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
}
} else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
ooff = sampleOffset;
samples = chunk->sndChunk;
for ( i=0 ; i<count ; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale;
boff = ooff;
fdata = 0;
for (j=aoff; j<boff; j++) {
if (j == SND_CHUNK_SIZE) {
chunk = chunk->next;
if (!chunk) {
chunk = sc->soundData;
}
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
fdata += samples[j&(SND_CHUNK_SIZE-1)];
}
fdiv = 256 * (boff-aoff);
samp[i].left += (fdata * fleftvol)/fdiv;
samp[i].right += (fdata * frightvol)/fdiv;
}
}
}
#endif
static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata, fdiv, fleftvol, frightvol;
samp = &paintbuffer[ bufferOffset ];
if (ch->doppler) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
chunk = sc->soundData;
while (sampleOffset>=SND_CHUNK_SIZE) {
chunk = chunk->next;
sampleOffset -= SND_CHUNK_SIZE;
if (!chunk) {
chunk = sc->soundData;
}
}
if (!ch->doppler || ch->dopplerScale==1.0f) {
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
for ( i=0 ; i<count ; i++ ) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
} else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
ooff = sampleOffset;
samples = chunk->sndChunk;
for ( i=0 ; i<count ; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale;
boff = ooff;
fdata = 0;
for (j=aoff; j<boff; j++) {
if (j == SND_CHUNK_SIZE) {
chunk = chunk->next;
if (!chunk) {
chunk = sc->soundData;
}
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
fdata += samples[j&(SND_CHUNK_SIZE-1)];
}
fdiv = 256 * (boff-aoff);
samp[i].left += (fdata * fleftvol)/fdiv;
samp[i].right += (fdata * frightvol)/fdiv;
}
}
}
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
#if idppc_altivec
if (com_altivec->integer) {
// must be in a seperate function or G3 systems will crash.
S_PaintChannelFrom16_altivec( ch, sc, count, sampleOffset, bufferOffset );
return;
}
#endif
S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset );
}
void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
i = 0;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
while (sampleOffset>=(SND_CHUNK_SIZE_FLOAT*4)) {
chunk = chunk->next;
sampleOffset -= (SND_CHUNK_SIZE_FLOAT*4);
i++;
}
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sfxScratchIndex = i;
sfxScratchPointer = sc;
}
samples = sfxScratchBuffer;
for ( i=0 ; i<count ; i++ ) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE*2) {
chunk = chunk->next;
decodeWavelet(chunk, sfxScratchBuffer);
sfxScratchIndex++;
sampleOffset = 0;
}
}
}
void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
i = 0;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
if (ch->doppler) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
while (sampleOffset>=(SND_CHUNK_SIZE*4)) {
chunk = chunk->next;
sampleOffset -= (SND_CHUNK_SIZE*4);
i++;
}
if (i!=sfxScratchIndex || sfxScratchPointer != sc) {
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sfxScratchIndex = i;
sfxScratchPointer = sc;
}
samples = sfxScratchBuffer;
for ( i=0 ; i<count ; i++ ) {
data = samples[sampleOffset++];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (sampleOffset == SND_CHUNK_SIZE*4) {
chunk = chunk->next;
S_AdpcmGetSamples( chunk, sfxScratchBuffer);
sampleOffset = 0;
sfxScratchIndex++;
}
}
}
void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
byte *samples;
float ooff;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
while (sampleOffset>=(SND_CHUNK_SIZE*2)) {
chunk = chunk->next;
sampleOffset -= (SND_CHUNK_SIZE*2);
if (!chunk) {
chunk = sc->soundData;
}
}
if (!ch->doppler) {
samples = (byte *)chunk->sndChunk + sampleOffset;
for ( i=0 ; i<count ; i++ ) {
data = mulawToShort[*samples];
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
samples++;
if (samples == (byte *)chunk->sndChunk+(SND_CHUNK_SIZE*2)) {
chunk = chunk->next;
samples = (byte *)chunk->sndChunk;
}
}
} else {
ooff = sampleOffset;
samples = (byte *)chunk->sndChunk;
for ( i=0 ; i<count ; i++ ) {
data = mulawToShort[samples[(int)(ooff)]];
ooff = ooff + ch->dopplerScale;
samp[i].left += (data * leftvol)>>8;
samp[i].right += (data * rightvol)>>8;
if (ooff >= SND_CHUNK_SIZE*2) {
chunk = chunk->next;
if (!chunk) {
chunk = sc->soundData;
}
samples = (byte *)chunk->sndChunk;
ooff = 0.0;
}
}
}
}
/*
===================
S_PaintChannels
===================
*/
void S_PaintChannels( int endtime ) {
int i;
int end;
channel_t *ch;
sfx_t *sc;
int ltime, count;
int sampleOffset;
snd_vol = s_volume->value*255;
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
while ( s_paintedtime < endtime ) {
// if paintbuffer is smaller than DMA buffer
// we may need to fill it multiple times
end = endtime;
if ( endtime - s_paintedtime > PAINTBUFFER_SIZE ) {
end = s_paintedtime + PAINTBUFFER_SIZE;
}
// clear the paint buffer to either music or zeros
if ( s_rawend < s_paintedtime ) {
if ( s_rawend ) {
//Com_DPrintf ("background sound underrun\n");
}
Com_Memset(paintbuffer, 0, (end - s_paintedtime) * sizeof(portable_samplepair_t));
} else {
// copy from the streaming sound source
int s;
int stop;
stop = (end < s_rawend) ? end : s_rawend;
for ( i = s_paintedtime ; i < stop ; i++ ) {
s = i&(MAX_RAW_SAMPLES-1);
paintbuffer[i-s_paintedtime] = s_rawsamples[s];
}
// if (i != end)
// Com_Printf ("partial stream\n");
// else
// Com_Printf ("full stream\n");
for ( ; i < end ; i++ ) {
paintbuffer[i-s_paintedtime].left =
paintbuffer[i-s_paintedtime].right = 0;
}
}
// paint in the channels.
ch = s_channels;
for ( i = 0; i < MAX_CHANNELS ; i++, ch++ ) {
if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
continue;
}
ltime = s_paintedtime;
sc = ch->thesfx;
sampleOffset = ltime - ch->startSample;
count = end - ltime;
if ( sampleOffset + count > sc->soundLength ) {
count = sc->soundLength - sampleOffset;
}
if ( count > 0 ) {
if( sc->soundCompressionMethod == 1) {
S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else if( sc->soundCompressionMethod == 2) {
S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else if( sc->soundCompressionMethod == 3) {
S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else {
S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime);
}
}
}
// paint in the looped channels.
ch = loop_channels;
for ( i = 0; i < numLoopChannels ; i++, ch++ ) {
if ( !ch->thesfx || (!ch->leftvol && !ch->rightvol )) {
continue;
}
ltime = s_paintedtime;
sc = ch->thesfx;
if (sc->soundData==NULL || sc->soundLength==0) {
continue;
}
// we might have to make two passes if it
// is a looping sound effect and the end of
// the sample is hit
do {
sampleOffset = (ltime % sc->soundLength);
count = end - ltime;
if ( sampleOffset + count > sc->soundLength ) {
count = sc->soundLength - sampleOffset;
}
if ( count > 0 ) {
if( sc->soundCompressionMethod == 1) {
S_PaintChannelFromADPCM (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else if( sc->soundCompressionMethod == 2) {
S_PaintChannelFromWavelet (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else if( sc->soundCompressionMethod == 3) {
S_PaintChannelFromMuLaw (ch, sc, count, sampleOffset, ltime - s_paintedtime);
} else {
S_PaintChannelFrom16 (ch, sc, count, sampleOffset, ltime - s_paintedtime);
}
ltime += count;
}
} while ( ltime < end);
}
// transfer out according to DMA format
S_TransferPaintBuffer( end );
s_paintedtime = end;
}
}
File diff suppressed because it is too large. Load diff
+73
View File
@@ -0,0 +1,73 @@
/*
===========================================================================
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
===========================================================================
*/
void S_Init( void );
void S_Shutdown( void );
// if origin is NULL, the sound will be dynamically sourced from the entity
void S_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx );
void S_StartLocalSound( sfxHandle_t sfx, int channelNum );
void S_StartBackgroundTrack( const char *intro, const char *loop );
void S_StopBackgroundTrack( void );
// cinematics and voice-over-network will send raw samples
// 1.0 volume will be direct output of source samples
void S_RawSamples (int samples, int rate, int width, int channels,
const byte *data, float volume);
// stop all sounds and the background track
void S_StopAllSounds( void );
// all continuous looping sounds must be added before calling S_Update
void S_ClearLoopingSounds( qboolean killall );
void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void S_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx );
void S_StopLoopingSound(int entityNum );
// recompute the reletive volumes for all running sounds
// reletive to the given entityNum / orientation
void S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater );
// let the sound system know where an entity currently is
void S_UpdateEntityPosition( int entityNum, const vec3_t origin );
void S_Update( void );
void S_DisableSounds( void );
void S_BeginRegistration( void );
// RegisterSound will allways return a valid sample, even if it
// has to create a placeholder. This prevents continuous filesystem
// checks for missing files
sfxHandle_t S_RegisterSound( const char *sample, qboolean compressed );
void S_DisplayFreeMemory(void);
void S_ClearSoundBuffer( void );
void SNDDMA_Activate( void );
void S_UpdateBackgroundTrack( void );
+254
View File
@@ -0,0 +1,254 @@
/*
===========================================================================
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
===========================================================================
*/
#include "snd_local.h"
long myftol( float f );
#define C0 0.4829629131445341
#define C1 0.8365163037378079
#define C2 0.2241438680420134
#define C3 -0.1294095225512604
void daub4(float b[], unsigned long n, int isign)
{
float wksp[4097];
float *a=b-1; // numerical recipies so a[1] = b[0]
unsigned long nh,nh1,i,j;
if (n < 4) return;
nh1=(nh=n >> 1)+1;
if (isign >= 0) {
for (i=1,j=1;j<=n-3;j+=2,i++) {
wksp[i] = C0*a[j]+C1*a[j+1]+C2*a[j+2]+C3*a[j+3];
wksp[i+nh] = C3*a[j]-C2*a[j+1]+C1*a[j+2]-C0*a[j+3];
}
wksp[i ] = C0*a[n-1]+C1*a[n]+C2*a[1]+C3*a[2];
wksp[i+nh] = C3*a[n-1]-C2*a[n]+C1*a[1]-C0*a[2];
} else {
wksp[1] = C2*a[nh]+C1*a[n]+C0*a[1]+C3*a[nh1];
wksp[2] = C3*a[nh]-C0*a[n]+C1*a[1]-C2*a[nh1];
for (i=1,j=3;i<nh;i++) {
wksp[j++] = C2*a[i]+C1*a[i+nh]+C0*a[i+1]+C3*a[i+nh1];
wksp[j++] = C3*a[i]-C0*a[i+nh]+C1*a[i+1]-C2*a[i+nh1];
}
}
for (i=1;i<=n;i++) {
a[i]=wksp[i];
}
}
void wt1(float a[], unsigned long n, int isign)
{
unsigned long nn;
int inverseStartLength = n/4;
if (n < inverseStartLength) return;
if (isign >= 0) {
for (nn=n;nn>=inverseStartLength;nn>>=1) daub4(a,nn,isign);
} else {
for (nn=inverseStartLength;nn<=n;nn<<=1) daub4(a,nn,isign);
}
}
/* The number of bits required by each value */
static unsigned char numBits[] = {
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
};
byte MuLawEncode(short s) {
unsigned long adjusted;
byte sign, exponent, mantissa;
sign = (s<0)?0:0x80;
if (s<0) s=-s;
adjusted = (long)s << (16-sizeof(short)*8);
adjusted += 128L + 4L;
if (adjusted > 32767) adjusted = 32767;
exponent = numBits[(adjusted>>7)&0xff] - 1;
mantissa = (adjusted>>(exponent+3))&0xf;
return ~(sign | (exponent<<4) | mantissa);
}
short MuLawDecode(byte uLaw) {
signed long adjusted;
byte exponent, mantissa;
uLaw = ~uLaw;
exponent = (uLaw>>4) & 0x7;
mantissa = (uLaw&0xf) + 16;
adjusted = (mantissa << (exponent +3)) - 128 - 4;
return (uLaw & 0x80)? adjusted : -adjusted;
}
short mulawToShort[256];
static qboolean madeTable = qfalse;
static int NXStreamCount;
void NXPutc(NXStream *stream, char out) {
stream[NXStreamCount++] = out;
}
void encodeWavelet( sfx_t *sfx, short *packets) {
float wksp[4097], temp;
int i, samples, size;
sndBuffer *newchunk, *chunk;
byte *out;
if (!madeTable) {
for (i=0;i<256;i++) {
mulawToShort[i] = (float)MuLawDecode((byte)i);
}
madeTable = qtrue;
}
chunk = NULL;
samples = sfx->soundLength;
while(samples>0) {
size = samples;
if (size>(SND_CHUNK_SIZE*2)) {
size = (SND_CHUNK_SIZE*2);
}
if (size<4) {
size = 4;
}
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
for(i=0; i<size; i++) {
wksp[i] = *packets;
packets++;
}
wt1(wksp, size, 1);
out = (byte *)chunk->sndChunk;
for(i=0;i<size;i++) {
temp = wksp[i];
if (temp > 32767) temp = 32767; else if (temp<-32768) temp = -32768;
out[i] = MuLawEncode((short)temp);
}
chunk->size = size;
samples -= size;
}
}
void decodeWavelet(sndBuffer *chunk, short *to) {
float wksp[4097];
int i;
byte *out;
int size = chunk->size;
out = (byte *)chunk->sndChunk;
for(i=0;i<size;i++) {
wksp[i] = mulawToShort[out[i]];
}
wt1(wksp, size, -1);
if (!to) return;
for(i=0; i<size; i++) {
to[i] = wksp[i];
}
}
void encodeMuLaw( sfx_t *sfx, short *packets) {
int i, samples, size, grade, poop;
sndBuffer *newchunk, *chunk;
byte *out;
if (!madeTable) {
for (i=0;i<256;i++) {
mulawToShort[i] = (float)MuLawDecode((byte)i);
}
madeTable = qtrue;
}
chunk = NULL;
samples = sfx->soundLength;
grade = 0;
while(samples>0) {
size = samples;
if (size>(SND_CHUNK_SIZE*2)) {
size = (SND_CHUNK_SIZE*2);
}
newchunk = SND_malloc();
if (sfx->soundData == NULL) {
sfx->soundData = newchunk;
} else {
chunk->next = newchunk;
}
chunk = newchunk;
out = (byte *)chunk->sndChunk;
for(i=0; i<size; i++) {
poop = packets[0]+grade;
if (poop>32767) {
poop = 32767;
} else if (poop<-32768) {
poop = -32768;
}
out[i] = MuLawEncode((short)poop);
grade = poop - mulawToShort[out[i]];
packets++;
}
chunk->size = size;
samples -= size;
}
}
void decodeMuLaw(sndBuffer *chunk, short *to) {
int i;
byte *out;
int size = chunk->size;
out = (byte *)chunk->sndChunk;
for(i=0;i<size;i++) {
to[i] = mulawToShort[out[i]];
}
}