Index: code/client/snd_codec.c
===================================================================
--- code/client/snd_codec.c	(revision 917)
+++ code/client/snd_codec.c	(working copy)
@@ -105,6 +105,9 @@
 #if USE_CODEC_VORBIS
 	S_CodecRegister(&ogg_codec);
 #endif
+#if USE_CODEC_MP3
+	S_CodecRegister(&mp3_codec);
+#endif
 }
 
 /*
Index: code/client/snd_codec.h
===================================================================
--- code/client/snd_codec.h	(revision 917)
+++ code/client/snd_codec.h	(working copy)
@@ -95,4 +95,13 @@
 int S_OGG_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
 #endif // USE_CODEC_VORBIS
 
+// MP3 codec
+#ifdef USE_CODEC_MP3
+extern snd_codec_t mp3_codec;
+void *S_MP3_CodecLoad(const char *filename, snd_info_t *info);
+snd_stream_t *S_MP3_CodecOpenStream(const char *filename);
+void S_MP3_CodecCloseStream(snd_stream_t *stream);
+int S_MP3_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer);
+#endif // USE_CODEC_MP3
+
 #endif // !_SND_CODEC_H_
Index: code/client/snd_codec_mp3.c
===================================================================
--- code/client/snd_codec_mp3.c	(revision 0)
+++ code/client/snd_codec_mp3.c	(revision 0)
@@ -0,0 +1,716 @@
+/*
+===========================================================================
+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>
+Copyright (C) 2006 Thilo Schulz <arny@ats.s.bawue.de>
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+// MP3 support is enabled by this define
+#if USE_CODEC_MP3
+
+// includes for the Q3 sound system
+#include "client.h"
+#include "snd_codec.h"
+
+// includes for the MP3 codec
+#include <mad.h>
+
+#define MP3_SAMPLE_WIDTH		2
+#define MP3_PCMSAMPLES_PERSLICE		32
+
+// buffer size used when reading through the mp3
+#define MP3_DATA_BUFSIZ			128*1024
+
+// undefine this if you don't want any dithering.
+#define MP3_DITHERING
+
+// Q3 MP3 codec
+snd_codec_t mp3_codec =
+{
+	".mp3",
+	S_MP3_CodecLoad,
+	S_MP3_CodecOpenStream,
+	S_MP3_CodecReadStream,
+	S_MP3_CodecCloseStream,
+	NULL
+};
+
+// structure used for info purposes
+struct snd_codec_mp3_info
+{
+	byte encbuf[MP3_DATA_BUFSIZ];	// left over bytes not consumed
+					// by the decoder.
+	struct mad_stream madstream;	// uses encbuf as buffer.
+	struct mad_frame madframe;	// control structures for libmad.
+	struct mad_synth madsynth;
+
+	byte *pcmbuf;			// buffer for not-used samples.
+	int buflen;			// length of buffer data.
+	int pcmbufsize;			// amount of allocated memory for
+					// pcmbuf. This should have at least
+					// the size of a decoded mp3 frame.	
+
+	byte *dest;			// copy decoded data here.
+	int destlen;			// amount of already copied data.
+	int destsize;			// amount of bytes we must decode.
+};
+
+/*************** MP3 utility functions ***************/
+
+/*
+=================
+S_MP3_ReadData
+=================
+*/
+
+// feed libmad with data
+int S_MP3_ReadData(snd_stream_t *stream, struct mad_stream *madstream, byte *encbuf, int encbufsize)
+{
+	int retval;
+	int leftover;
+	
+	if(!stream)
+		return -1;
+	
+	leftover =  madstream->bufend - madstream->next_frame;
+	if(leftover > 0)
+		memmove(encbuf, madstream->this_frame, leftover);
+
+
+	// Fill the buffer right to the end
+	
+	retval = FS_Read(&encbuf[leftover], encbufsize - leftover, stream->file);
+
+	if(retval <= 0)
+	{
+		// EOF reached, that's ok.
+		return 0;
+	}
+	
+	mad_stream_buffer(madstream, encbuf, retval + leftover);
+	
+	return retval;
+}
+
+
+/*
+=================
+S_MP3_Scanfile
+
+to determine the samplecount, we apparently must get *all* headers :(
+I basically used the xmms-mad plugin source to see how this stuff works.
+
+returns a value < 0 on error.
+=================
+*/
+
+int S_MP3_Scanfile(snd_stream_t *stream)
+{
+	struct mad_stream madstream;
+	struct mad_header madheader;
+	int retval;
+	int samplecount;
+	byte encbuf[MP3_DATA_BUFSIZ];
+
+	// error out on invalid input.
+	if(!stream)
+		return -1;
+
+	mad_stream_init(&madstream);
+	mad_header_init(&madheader);
+	
+	while(1)
+	{
+		retval = S_MP3_ReadData(stream, &madstream, encbuf, sizeof(encbuf));
+		if(retval < 0)
+			return -1;
+		else if(retval == 0)
+			break;
+		
+		// Start decoding the headers.
+		while(1)
+		{
+			if((retval = mad_header_decode(&madheader, &madstream)) < 0)
+			{
+				if(madstream.error == MAD_ERROR_BUFLEN)
+				{
+					// We need to read more data
+					break;
+				}
+
+				if(!MAD_RECOVERABLE (madstream.error))
+				{
+					// unrecoverable error... we must bail out.
+					return retval;
+				}
+
+				mad_stream_skip(&madstream, madstream.skiplen);
+				continue;
+			}
+			
+			// we got a valid header.
+			
+			if(madheader.layer != MAD_LAYER_III)
+			{
+				// we don't support non-mp3s
+				return -1;
+			}
+
+			if(!stream->info.samples)
+			{
+				// This here is the very first frame. Set initial values now,
+				// that we expect to stay constant throughout the whole mp3.
+				
+				stream->info.rate = madheader.samplerate;
+				stream->info.width = MP3_SAMPLE_WIDTH;
+				stream->info.channels = MAD_NCHANNELS(&madheader);
+				stream->info.samples = 0;
+				stream->info.size = 0;				// same here.
+				stream->info.dataofs = 0;
+			}
+			else
+			{
+				// Check whether something changed that shouldn't.
+				
+				if(stream->info.rate != madheader.samplerate ||
+				   stream->info.channels != MAD_NCHANNELS(&madheader))
+					return -1;
+			}
+
+			// Update the counters
+			samplecount = MAD_NSBSAMPLES(&madheader) * MP3_PCMSAMPLES_PERSLICE;
+			stream->info.samples += samplecount;
+			stream->info.size += samplecount * stream->info.channels * stream->info.width;			
+		}
+	}
+	
+	// Reset the file pointer so we can do the real decoding.
+	FS_Seek(stream->file, 0, FS_SEEK_SET);
+	
+	return 0;
+}
+
+/************************ dithering functions ***************************/
+
+#ifdef MP3_DITHERING
+
+// All dithering done here is taken from the GPL'ed xmms-mad plugin.
+
+/* Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.       */
+/* Any feedback is very welcome. For any question, comments,       */
+/* see http://www.math.keio.ac.jp/matumoto/emt.html or email       */
+/* matumoto@math.keio.ac.jp                                        */
+
+/* Period parameters */
+#define MP3_DITH_N 624
+#define MP3_DITH_M 397
+#define MATRIX_A 0x9908b0df   /* constant vector a */
+#define UPPER_MASK 0x80000000 /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffff /* least significant r bits */
+
+/* Tempering parameters */
+#define TEMPERING_MASK_B 0x9d2c5680
+#define TEMPERING_MASK_C 0xefc60000
+#define TEMPERING_SHIFT_U(y)  (y >> 11)
+#define TEMPERING_SHIFT_S(y)  (y << 7)
+#define TEMPERING_SHIFT_T(y)  (y << 15)
+#define TEMPERING_SHIFT_L(y)  (y >> 18)
+
+static unsigned long mt[MP3_DITH_N]; /* the array for the state vector  */
+static int mti=MP3_DITH_N+1; /* mti==MP3_DITH_N+1 means mt[MP3_DITH_N] is not initialized */
+
+/* initializing the array with a NONZERO seed */
+void sgenrand(unsigned long seed)
+{
+    /* setting initial seeds to mt[MP3_DITH_N] using         */
+    /* the generator Line 25 of Table 1 in          */
+    /* [KNUTH 1981, The Art of Computer Programming */
+    /*    Vol. 2 (2nd Ed.), pp102]                  */
+    mt[0]= seed & 0xffffffff;
+    for (mti=1; mti<MP3_DITH_N; mti++)
+        mt[mti] = (69069 * mt[mti-1]) & 0xffffffff;
+}
+
+unsigned long genrand(void)
+{
+    unsigned long y;
+    static unsigned long mag01[2]={0x0, MATRIX_A};
+    /* mag01[x] = x * MATRIX_A  for x=0,1 */
+
+    if (mti >= MP3_DITH_N) { /* generate MP3_DITH_N words at one time */
+        int kk;
+
+        if (mti == MP3_DITH_N+1)   /* if sgenrand() has not been called, */
+            sgenrand(4357); /* a default initial seed is used   */
+
+        for (kk=0;kk<MP3_DITH_N-MP3_DITH_M;kk++) {
+            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+            mt[kk] = mt[kk+MP3_DITH_M] ^ (y >> 1) ^ mag01[y & 0x1];
+        }
+        for (;kk<MP3_DITH_N-1;kk++) {
+            y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+            mt[kk] = mt[kk+(MP3_DITH_M-MP3_DITH_N)] ^ (y >> 1) ^ mag01[y & 0x1];
+        }
+        y = (mt[MP3_DITH_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+        mt[MP3_DITH_N-1] = mt[MP3_DITH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];
+
+        mti = 0;
+    }
+
+    y = mt[mti++];
+    y ^= TEMPERING_SHIFT_U(y);
+    y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
+    y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
+    y ^= TEMPERING_SHIFT_L(y);
+
+    return y;
+}
+
+long triangular_dither_noise(int nbits) {
+    // parameter nbits : the peak-to-peak amplitude desired (in bits)
+    //  use with nbits set to    2 + nber of bits to be trimmed.
+    // (because triangular is made from two uniformly distributed processes,
+    // it starts at 2 bits peak-to-peak amplitude)
+    // see The Theory of Dithered Quantization by Robert Alexander Wannamaker
+    // for complete proof of why that's optimal
+
+    long v = (genrand()/2 - genrand()/2); // in ]-2^31, 2^31[
+    //int signe = (v>0) ? 1 : -1;
+    long P = 1 << (32 - nbits); // the power of 2
+    v /= P;
+    // now v in ]-2^(nbits-1), 2^(nbits-1) [
+
+    return v;
+}
+
+#endif // MP3_DITHERING
+
+/************************ decoder functions ***************************/
+
+/*
+=================
+S_MP3_Scale
+
+Converts the signal to 16 bit LE-PCM data and does dithering.
+
+- borrowed from xmms-mad plugin source.
+=================
+*/
+
+/*
+ * xmms-mad - mp3 plugin for xmms
+ * Copyright (C) 2001-2002 Sam Clegg
+ */
+
+signed int S_MP3_Scale(mad_fixed_t sample)
+{
+	int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16;
+#ifdef MP3_DITHERING
+	int dither;
+#endif
+	
+	// round
+	sample += (1L << (n_bits_to_loose - 1));
+
+#ifdef MP3_DITHERING
+	dither = triangular_dither_noise(n_bits_to_loose + 1);
+	sample += dither;
+#endif
+
+	/* clip */
+	if (sample >= MAD_F_ONE)
+		sample = MAD_F_ONE - 1;
+	else if (sample < -MAD_F_ONE)
+		sample = -MAD_F_ONE;
+
+	/* quantize */
+	return sample >> n_bits_to_loose;
+}
+
+
+
+/*
+=================
+S_MP3_PCMCopy
+
+Copy and convert pcm data until bytecount bytes have been written.
+return the position in pcm->samples.
+indicate the amount of actually written bytes in wrotecnt.
+=================
+*/
+
+int S_MP3_PCMCopy(byte *buf, struct mad_pcm *pcm, int bufofs,
+			 int sampleofs, int bytecount, int *wrotecnt)
+{
+	int written = 0;
+	signed int sample;
+	int framesize = pcm->channels * MP3_SAMPLE_WIDTH;
+
+	// add new pcm data.
+	while(written < bytecount && sampleofs < pcm->length)
+	{
+		sample = S_MP3_Scale(pcm->samples[0][sampleofs]);
+
+#ifdef Q3_BIG_ENDIAN
+		// output to 16 bit big endian PCM
+		buf[bufofs++] = (sample >> 8) & 0xff;
+		buf[bufofs++] = sample & 0xff;
+#else
+		// output to 16 bit little endian PCM
+		buf[bufofs++] = sample & 0xff;
+		buf[bufofs++] = (sample >> 8) & 0xff;
+#endif
+		
+		if(pcm->channels == 2)
+		{
+			sample = S_MP3_Scale(pcm->samples[1][sampleofs]);
+
+#ifdef Q3_BIG_ENDIAN
+			buf[bufofs++] = (sample >> 8) & 0xff;
+			buf[bufofs++] = sample & 0xff;
+#else
+			buf[bufofs++] = sample & 0xff;
+			buf[bufofs++] = (sample >> 8) & 0xff;
+#endif
+		}
+		
+		sampleofs++;
+		written += framesize;
+	}	
+
+	if(wrotecnt)
+		*wrotecnt = written;
+
+	return sampleofs;
+}
+
+
+/*
+=================
+S_MP3_Decode
+=================
+*/
+
+// gets executed for every decoded frame.
+int S_MP3_Decode(snd_stream_t *stream)
+{
+	struct snd_codec_mp3_info *mp3info;
+	struct mad_stream *madstream;
+	struct mad_frame *madframe;
+	struct mad_synth *madsynth;
+	struct mad_pcm *pcm;
+	int cursize;
+	int samplecount;
+	int needcount;
+	int wrote;
+	int retval;
+
+	if(!stream)
+		return -1;
+
+	mp3info = stream->ptr;
+	madstream = &mp3info->madstream;
+	madframe = &mp3info->madframe;
+
+	if(mad_frame_decode(madframe, madstream))
+	{
+		if(madstream->error == MAD_ERROR_BUFLEN)
+		{
+			// we need more data. Read another chunk.
+			retval = S_MP3_ReadData(stream, madstream, mp3info->encbuf, sizeof(mp3info->encbuf));
+
+			// call myself again now that buffer is full.
+			if(retval > 0)
+				retval = S_MP3_Decode(stream);
+		}
+		else if(MAD_RECOVERABLE(madstream->error))
+		{
+			mad_stream_skip(madstream, madstream->skiplen);
+			return S_MP3_Decode(stream);
+		}
+		else
+			retval = -1;
+
+		return retval;
+	}
+
+	// check whether this really is an mp3
+	if(madframe->header.layer != MAD_LAYER_III)
+		return -1;
+
+	// generate pcm data
+	madsynth = &mp3info->madsynth;
+	mad_synth_frame(madsynth, madframe);
+
+	pcm = &madsynth->pcm;
+
+	// perform a few checks to see whether something changed that shouldn't.
+		
+	if(stream->info.rate != pcm->samplerate ||
+	   stream->info.channels != pcm->channels)
+	{
+		return -1;
+	}
+	// see whether we have got enough data now.
+	cursize = pcm->length * pcm->channels * stream->info.width;
+	needcount = mp3info->destsize - mp3info->destlen;
+
+	// Copy exactly as many samples as required.
+	samplecount = S_MP3_PCMCopy(mp3info->dest, pcm,
+				    mp3info->destlen, 0, needcount, &wrote);
+	mp3info->destlen += wrote;
+	
+	if(samplecount < pcm->length)
+	{
+		// Not all samples got copied. Copy the rest into the pcm buffer.
+		samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
+					    mp3info->buflen,
+					    samplecount,
+					    mp3info->pcmbufsize - mp3info->buflen,
+					    &wrote);
+		mp3info->buflen += wrote;
+		
+
+		if(samplecount < pcm->length)
+		{
+			// The pcm buffer was not large enough. Make it bigger.
+			byte *newbuf = Z_Malloc(cursize);
+			
+			if(mp3info->pcmbuf)
+			{
+				memcpy(newbuf, mp3info->pcmbuf, mp3info->buflen);
+				Z_Free(mp3info->pcmbuf);
+			}
+			
+			mp3info->pcmbuf = newbuf;
+			mp3info->pcmbufsize = cursize;
+			
+			samplecount = S_MP3_PCMCopy(mp3info->pcmbuf, pcm,
+						    mp3info->buflen,
+						    samplecount,
+						    mp3info->pcmbufsize - mp3info->buflen,
+						    &wrote);
+			mp3info->buflen += wrote;		
+		}		
+		
+		// we're definitely done.
+		retval = 0;
+	}
+	else if(mp3info->destlen >= mp3info->destsize)
+		retval = 0;
+	else
+		retval = 1;
+
+	return retval;
+}
+
+/*************** Callback functions for quake3 ***************/
+
+/*
+=================
+S_MP3_CodecOpenStream
+=================
+*/
+
+snd_stream_t *S_MP3_CodecOpenStream(const char *filename)
+{
+	snd_stream_t *stream;
+	struct snd_codec_mp3_info *mp3info;
+
+	// Open the stream
+	stream = S_CodecUtilOpen(filename, &mp3_codec);
+	if(!stream || stream->length <= 0)
+		return NULL;
+
+	// We have to scan through the MP3 to determine the important mp3 info.
+	if(S_MP3_Scanfile(stream) < 0)
+	{
+		// scanning didn't work out...
+		S_CodecUtilClose(stream);
+		return NULL;
+	}
+
+	// Initialize the mp3 info structure we need for streaming
+	mp3info = Z_Malloc(sizeof(*mp3info));
+	if(!mp3info)
+	{
+		S_CodecUtilClose(stream);
+		return NULL;
+	}
+
+	stream->ptr = mp3info;
+
+	// initialize the libmad control structures.
+	mad_stream_init(&mp3info->madstream);
+	mad_frame_init(&mp3info->madframe);
+	mad_synth_init(&mp3info->madsynth);
+
+	if(S_MP3_ReadData(stream, &mp3info->madstream, mp3info->encbuf, sizeof(mp3info->encbuf)) <= 0)
+	{
+		// we didnt read anything, that's bad.
+		S_MP3_CodecCloseStream(stream);
+		return NULL;
+	}
+
+	return stream;
+}
+
+/*
+=================
+S_MP3_CodecCloseStream
+=================
+*/
+
+// free all memory we allocated.
+void S_MP3_CodecCloseStream(snd_stream_t *stream)
+{
+	struct snd_codec_mp3_info *mp3info;
+	
+	if(!stream)
+		return;
+		
+	// free all data in our mp3info tree
+
+	if(stream->ptr)
+	{
+		mp3info = stream->ptr;
+
+		if(mp3info->pcmbuf)
+			Z_Free(mp3info->pcmbuf);
+
+		mad_synth_finish(&mp3info->madsynth);
+		mad_frame_finish(&mp3info->madframe);
+		mad_stream_finish(&mp3info->madstream);
+	
+		Z_Free(stream->ptr);
+	}
+
+	S_CodecUtilClose(stream);
+}
+
+/*
+=================
+S_MP3_CodecReadStream
+=================
+*/
+int S_MP3_CodecReadStream(snd_stream_t *stream, int bytes, void *buffer)
+{
+	struct snd_codec_mp3_info *mp3info;
+	int retval;
+	
+	if(!stream)
+		return -1;
+		
+	mp3info = stream->ptr;
+
+	// Make sure we get complete frames all the way through.
+	bytes -= bytes % (stream->info.channels * stream->info.width);
+
+	if(mp3info->buflen)
+	{
+		if(bytes < mp3info->buflen)
+		{
+			// we still have enough bytes in our decoded pcm buffer
+			memcpy(buffer, mp3info->pcmbuf, bytes);
+		
+			// remove the portion from our buffer.
+			mp3info->buflen -= bytes;
+			memmove(mp3info->pcmbuf, &mp3info->pcmbuf[bytes], mp3info->buflen);
+			return bytes;
+		}
+		else
+		{
+			// copy over the samples we already have.
+			memcpy(buffer, mp3info->pcmbuf, mp3info->buflen);
+			mp3info->destlen = mp3info->buflen;
+			mp3info->buflen = 0;
+		}
+	}
+	else
+		mp3info->destlen = 0;
+	
+	mp3info->dest = buffer;
+	mp3info->destsize = bytes;
+
+	do
+	{
+		retval = S_MP3_Decode(stream);
+	} while(retval > 0);
+	
+	// if there was an error return nothing.
+	if(retval < 0)
+		return 0;
+	
+	return mp3info->destlen;
+}
+
+/*
+=====================================================================
+S_MP3_CodecLoad
+
+We handle S_MP3_CodecLoad as a special case of the streaming functions 
+where we read the whole stream at once.
+======================================================================
+*/
+void *S_MP3_CodecLoad(const char *filename, snd_info_t *info)
+{
+	snd_stream_t *stream;
+	byte *pcmbuffer;
+
+	// check if input is valid
+	if(!filename)
+		return NULL;
+
+	stream = S_MP3_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->dataofs = stream->info.dataofs;
+	
+	// allocate enough buffer for all pcm data
+	pcmbuffer = Z_Malloc(stream->info.size);
+	if(!pcmbuffer)
+	{
+		S_MP3_CodecCloseStream(stream);
+		return NULL;
+	}
+
+	info->size = S_MP3_CodecReadStream(stream, stream->info.size, pcmbuffer);
+
+	if(info->size <= 0)
+	{
+		// we didn't read anything at all. darn.
+		Z_Free(pcmbuffer);
+		pcmbuffer = NULL;
+	}
+
+	S_MP3_CodecCloseStream(stream);
+
+	return pcmbuffer;
+}
+
+#endif // USE_CODEC_MP3
