aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--games/ioquake3/Makefile14
-rw-r--r--games/ioquake3/files/extra-patch-cellshading933
-rw-r--r--games/ioquake3/files/extra-patch-mp3753
-rw-r--r--games/ioquake3/files/patch-Makefile85
-rw-r--r--games/ioquake3/pkg-message2
-rw-r--r--games/iourbanterror/Makefile14
-rw-r--r--games/iourbanterror/files/extra-patch-cellshading933
-rw-r--r--games/iourbanterror/files/extra-patch-mp3753
-rw-r--r--games/iourbanterror/files/patch-Makefile85
-rw-r--r--games/iourbanterror/pkg-message2
10 files changed, 3538 insertions, 36 deletions
diff --git a/games/ioquake3/Makefile b/games/ioquake3/Makefile
index c2f42bfbbec3..cb37008aa1c1 100644
--- a/games/ioquake3/Makefile
+++ b/games/ioquake3/Makefile
@@ -19,8 +19,10 @@ USE_BZIP2= yes
USE_GMAKE= yes
OPTIONS= CLIENT "Build client" on \
+ CELLSHADING "Enable Cell Shading effect" off \
GAMELIBS "Build game libraries (when not mandatory)" off \
DEDICATED "Build dedicated server" on \
+ MP3 "Enable MP3 support" off \
OPENAL "Enable OpenAL (3D sound) support" off \
OPENAL_DLOPEN "Enable dynamic loading of OpenAL" off \
OPTIMIZED_CFLAGS "Enable compilation optimizations" on \
@@ -29,7 +31,7 @@ OPTIONS= CLIENT "Build client" on \
SMP "Build SMP (threaded) client" on \
VORBIS "Enable Ogg Vorbis codec support" off
-MAKE_ENV+= DEFAULT_BASEDIR="${Q3DIR}" LIBDIR="${LIBDIR}" \
+MAKE_ENV= DEFAULT_BASEDIR="${Q3DIR}" LIBDIR="${LIBDIR}" \
PTHREAD_LIBS="${PTHREAD_LIBS}"
PLIST_SUB= LIBDIR="${LIBDIR:S/${PREFIX}\///}"
@@ -53,6 +55,10 @@ HAVE_VM_COMPILED= yes
MAKE_ENV+= HAVE_VM_COMPILED=true
.endif
+.if defined(WITH_CELLSHADING)
+EXTRA_PATCHES+= ${FILESDIR}/extra-patch-cellshading
+.endif
+
.if !defined(WITHOUT_CLIENT) || !defined(WITHOUT_SMP)
# OpenAL
. if defined(WITH_OPENAL)
@@ -104,6 +110,12 @@ PLIST_SUB+= GAMELIBS=""
PLIST_SUB+= GAMELIBS="@comment "
.endif
+.if defined(WITH_MP3)
+EXTRA_PATCHES+= ${FILESDIR}/extra-patch-mp3
+LIB_DEPENDS+= mad.2:${PORTSDIR}/audio/libmad
+MAKE_ENV+= USE_CODEC_MP3=1
+.endif
+
.if !defined(WITHOUT_OPTIMIZED_CFLAGS)
MAKE_ENV+= USE_OPTIMIZED_CFLAGS=1
.endif
diff --git a/games/ioquake3/files/extra-patch-cellshading b/games/ioquake3/files/extra-patch-cellshading
new file mode 100644
index 000000000000..ce459a17bd45
--- /dev/null
+++ b/games/ioquake3/files/extra-patch-cellshading
@@ -0,0 +1,933 @@
+Index: code/renderer/tr_image.c
+===================================================================
+--- code/renderer/tr_image.c (revision 933)
++++ code/renderer/tr_image.c (working copy)
+@@ -34,7 +34,24 @@
+ #define JPEG_INTERNALS
+ #include "../jpeg-6/jpeglib.h"
+
++/**
++ * Headers for cell shading
++ * @author Jordi Prats Catala
++ * @author Guillermo Miranda Alamo
++ */
++/*
++byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows);
++void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++*/
++//void kuwahara(int columns, int rows, byte *targa_rgba);
+
++
+ static void LoadBMP( const char *name, byte **pic, int *width, int *height );
+ static void LoadTGA( const char *name, byte **pic, int *width, int *height );
+ static void LoadJPG( const char *name, byte **pic, int *width, int *height );
+@@ -799,7 +816,643 @@
+ return image;
+ }
+
++/****************************
++RGB GET/SET
++****************************/
+
++//RED
++static byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ return *pixbuf;
++}
++
++static void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ *pixbuf=value;
++}
++//GREEN
++static byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ pixbuf++;
++ return *pixbuf;
++}
++
++static void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf++;
++ *pixbuf=value;
++}
++//BLUE
++static byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=2;
++ return *pixbuf;
++}
++
++static void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=2;
++ *pixbuf=value;
++}
++//ALPHA
++static byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=3;
++ return *pixbuf;
++}
++
++static void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=3;
++ *pixbuf=value;
++}
++
++//RGB
++static void getImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ //if(rows<=y)
++ y=y%rows;
++ //if(columns<=x)
++ x=x%columns;
++ //x*=((x<0)?-1:1);
++ //y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4 + x*4;
++
++ rgb[0]=*pixbuf;
++ rgb[1]=*(pixbuf+1);
++ rgb[2]=*(pixbuf+2);
++}
++
++static void setImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb)
++{
++ byte *pixbuf;
++
++ //x*=((x<0)?-1:1);
++ //y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4 + (x*4);
++
++ *pixbuf=(byte)(rgb[0]);
++ *(pixbuf+1)=(byte)(rgb[1]);
++ *(pixbuf+2)=(byte)(rgb[2]);
++}
++
++/****************************
++NO BRAINER'S BLUR
++****************************/
++static void blur(int columns, int rows, byte *targa_rgba)
++{
++ int row, column;
++ float sum;
++
++
++ for(row=0; row<rows; row++)
++ {
++ //pixbuf = targa_rgba + row*columns*4;
++ for(column=0; column<columns; column++)
++ {
++ sum=0;
++ sum+=getImageR(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageR(targa_rgba,column,row,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageR(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageR(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageR(targa_rgba, column, row, columns, rows, (byte)sum);
++ ////////////////////
++ sum=0;
++ sum+=getImageG(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageG(targa_rgba,column,row,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageG(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageG(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageG(targa_rgba, column, row, columns, rows, (byte)sum);
++ ////////////////////////
++ sum=0;
++ sum+=getImageB(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageB(targa_rgba,column,row,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageB(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageB(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageB(targa_rgba, column, row, columns, rows, (byte)sum);
++
++ // "halftoning"
++ /*if((row%5==0)&&(column%5==1))
++ {
++ gris=0;
++ gris+=red;
++ gris+=green;
++ gris+=blue;
++ gris/=3;
++
++ gris=255-gris;
++ if(gris<0)
++ gris=0;
++
++ setImageR(targa_rgba, column, row, columns, rows, (byte)gris);
++ setImageG(targa_rgba, column, row, columns, rows, (byte)gris);
++ setImageB(targa_rgba, column, row, columns, rows, (byte)gris);
++
++ }*/
++
++ }
++ }
++
++}
++
++
++/****************************
++COLORED LIGHTMAP
++****************************/
++void whiteTextureOne(int columns, int rows, byte *targa_rgba){
++ //byte *pixbyf;
++ int row, column;
++ long rMean=0, gMean=0, bMean=0;
++ int pixels=0;
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ // Don't count fully transparent pixels
++ if(getImageA(targa_rgba,column,row,columns,rows)==0)
++ continue;
++ // Sum pixels values
++ rMean+=getImageR(targa_rgba,column,row,columns,rows);
++ gMean+=getImageG(targa_rgba,column,row,columns,rows);
++ bMean+=getImageB(targa_rgba,column,row,columns,rows);
++ pixels++;
++ }
++ }
++
++ // Calculate average
++ if(pixels>0){
++ rMean=((float)rMean/(float)pixels);
++ gMean=((float)gMean/(float)pixels);
++ bMean=((float)bMean/(float)pixels);
++ }
++ else{
++ return;
++ }
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ setImageR(targa_rgba,column,row,columns,rows,rMean);
++ setImageG(targa_rgba,column,row,columns,rows,gMean);
++ setImageB(targa_rgba,column,row,columns,rows,bMean);
++ }
++ }
++}
++
++int diffSquare(int mean, int val){
++ float variance = (val-mean)/255.0f;
++ float radius = mean<128?mean:255-mean;
++ return mean+(radius*variance);
++}
++
++/****************************
++DECONTRAST
++****************************/
++void whiteTextureTwo(int columns, int rows, byte *targa_rgba){
++ int row, column;
++ long rMean=0, gMean=0, bMean=0;
++ int r=0, g=0, b=0;
++ int pixels=0;
++
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ // Don't count fully transparent pixels
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ // Sum pixels values
++ rMean+=getImageR(targa_rgba,column,row,columns,rows);
++ gMean+=getImageG(targa_rgba,column,row,columns,rows);
++ bMean+=getImageB(targa_rgba,column,row,columns,rows);
++ pixels++;
++ }
++ }
++
++ // Calculate average
++ if(pixels>0){
++ rMean=rMean/pixels;
++ gMean=gMean/pixels;
++ bMean=bMean/pixels;
++ }
++ else{
++ return;
++ }
++
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ r=getImageR(targa_rgba,column,row,columns,rows);
++ g=getImageG(targa_rgba,column,row,columns,rows);
++ b=getImageB(targa_rgba,column,row,columns,rows);
++
++ setImageR(targa_rgba,column,row,columns,rows,diffSquare(rMean,r));
++ setImageG(targa_rgba,column,row,columns,rows,diffSquare(gMean,g));
++ setImageB(targa_rgba,column,row,columns,rows,diffSquare(bMean,b));
++
++ }
++ }
++}
++
++/****************************
++KUWAHARA ,FAILS SOMEWHERE
++****************************/
++#define KWH_RADIUS 2
++static void mean_variance(int x0, int y0, int x1, int y1, int columns, int rows, byte *targa_rgba, vec4_t mv )
++{
++ short min=255*3, max=0;
++ unsigned short count= 0;
++ short row, column;
++ unsigned short value;
++ vec3_t rgb;
++
++ mv[0]=mv[1]=mv[2]=mv[3]=0;
++
++ for(row=y0;row<=y1;row++)
++ {
++ for(column=x0;column<=x1;column++)
++ {
++ getImageRGB(targa_rgba,column,row,columns,rows,rgb);
++
++ VectorAdd(mv,rgb,mv);
++
++ count++;
++ value=rgb[0]+rgb[1]+rgb[2];
++ if(value<min) min=value;
++ if(value>max) max=value;
++ }
++ }
++
++ mv[0]/=count;
++ mv[1]/=count;
++ mv[2]/=count;
++ mv[3]= (max-min)/3.0f;
++}
++
++
++static void rgb_kuwahara(int x, int y, int columns, int rows, byte *targa_rgba, vec4_t bmv)
++{
++ vec4_t mv;
++ bmv[0]=bmv[1]=bmv[2]=bmv[3]=255;
++
++ mean_variance(x-KWH_RADIUS, y-KWH_RADIUS, x, y, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x, y-KWH_RADIUS, x+KWH_RADIUS, y, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x, y, x+KWH_RADIUS, y+KWH_RADIUS, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x-KWH_RADIUS, y, x, y+KWH_RADIUS, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++}
++
++static void kuwahara(int columns, int rows, byte *targa_rgba){
++ int row, column;
++ vec4_t rgbv;
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ rgb_kuwahara(column, row, columns, rows, targa_rgba, rgbv);
++ setImageRGB(targa_rgba,column,row,columns,rows,rgbv);
++ }
++ }
++}
++
++
++#define FLT_MAX 3.40282346638528860000e+38
++static void kuwahara3(int columns, int rows, byte *targa_rgba)
++{
++ byte channel;
++ int size = 10;
++ int index1,index2;
++ int width = columns-4;
++ int height = rows-4;
++ int size2 = (size+1)/2;
++ int offset = (size-1)/2;
++ const int width2 = columns + offset;
++ const int height2 = rows + offset;
++ int x1start = 4;
++ int y1start = 4;
++ int x2, y2;
++ int sum, sum2, n, v=0, xbase, ybase;
++ int y1,x1;
++ int xbase2=0, ybase2=0;
++ float var, min;
++ float** mean, **variance;
++
++ //blur(columns, rows, targa_rgba);
++
++ // I hate malloc I hate malloc I hate malloc I hate malloc I hate malloc I hate malloc
++ mean = (float**)malloc(sizeof(float*)*width2);
++ for(index1=0;index1<width2;index1++)
++ mean[index1] = (float*)malloc(sizeof(float)*height2);
++
++ variance = (float**)malloc(sizeof(float*)*width2);
++ for(index2=0;index2<width2;index2++)
++ variance[index2] = (float*)malloc(sizeof(float)*height2);
++
++ // For each channel (R,G,B)
++ // for(channel=0;channel<2;channel++)
++ // FTL
++ for(channel=0;channel<3;channel++){
++ for (y1=y1start-offset; y1<y1start+height; y1++) {
++
++ for (x1=x1start-offset; x1<x1start+width; x1++) {
++ sum=0; sum2=0; n=0;
++ for (x2=x1; x2<x1+size2; x2++) {
++ for (y2=y1; y2<y1+size2; y2++) {
++ //v = i(x2, y2);
++ switch(channel){
++ case 0:
++ v = getImageR(targa_rgba,x2,y2,columns,rows);
++ break;
++ case 1:
++ v = getImageG(targa_rgba,x2,y2,columns,rows);
++ break;
++ case 2:
++ v = getImageB(targa_rgba,x2,y2,columns,rows);
++ break;
++ }
++ //v = *targa_rgba + y2*columns*4+x2*4;
++ v/=10;
++ v*=10;
++ sum += v;
++ sum2 += v*v;
++ n++;
++ }
++ }
++ //cerr << "Accedo" << endl;
++ mean[x1+offset][y1+offset] = (float)(sum/n);
++ variance[x1+offset][y1+offset] = (float)((n*sum2-sum*sum)/n);
++ }
++ }
++
++ for (y1=y1start; y1<y1start+height; y1++) {
++ /*if ((y1%20)==0)
++ cout << (0.7+0.3*(y1-y1start)/height);*/
++ for (x1=x1start; x1<x1start+width; x1++) {
++ min = FLT_MAX;
++ xbase = x1; ybase=y1;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ xbase = x1+offset;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ ybase = y1+offset;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ xbase = x1;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ //i(x1, y1)=(int)(mean[xbase2][ybase2]+0.5);
++ switch(channel){
++ case 0:
++ setImageR(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ case 1:
++ setImageG(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ case 2:
++ setImageB(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ }
++ }
++ }
++ }
++ // Fuck mean & variance, this is hell (!+) Bad Religion
++ for(index1=0;index1<width2;index1++)
++ free(mean[index1]);
++ free(mean);
++
++ for(index2=0;index2<width2;index2++)
++ free(variance[index2]);
++ free(variance);
++
++ //blur(columns, rows, targa_rgba);
++}
++
++/****************************
++Symmetric Nearest Neighbour
++****************************/
++
++#define SNN_RADIUS 3
++
++static int deltaE(int l1,int a1,int b1,int l2,int a2,int b2)
++{
++ return (l1-l2)*(l1-l2) + (a1-a2)*(a1-a2) + (b1-b2)*(b1-b2);
++}
++
++static void snn(int columns, int rows, byte *targa_rgba)
++{
++
++ int row, column;
++ unsigned short sumR, sumG, sumB;
++ unsigned short count;
++ short u, v;
++ byte r, g, b;
++ byte r1, g1, b1;
++ byte r2, g2, b2;
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ sumR=0;
++ sumG=0;
++ sumB=0;
++ count=0;
++
++ r=getImageR(targa_rgba,column,row,columns,rows);
++ g=getImageG(targa_rgba,column,row,columns,rows);
++ b=getImageB(targa_rgba,column,row,columns,rows);
++
++ for(v=-SNN_RADIUS;v<=0;v++)
++ {
++ for(u=-SNN_RADIUS;u<=SNN_RADIUS;u++)
++ {
++ if(v==0&&u>=0) break;
++ // Sum pixels values
++ r1=getImageR(targa_rgba,column+u,row+v,columns,rows);
++ g1=getImageG(targa_rgba,column+u,row+v,columns,rows);
++ b1=getImageB(targa_rgba,column+u,row+v,columns,rows);
++
++ r2=getImageR(targa_rgba,column-u,row-v,columns,rows);
++ g2=getImageG(targa_rgba,column-u,row-v,columns,rows);
++ b2=getImageB(targa_rgba,column-u,row-v,columns,rows);
++
++ if ( deltaE(r,g,b,r1,g1,b1) < deltaE(r,g,b,r2,g2,b2))
++ {
++ sumR += r1;
++ sumG += g1;
++ sumB += b1;
++ }
++ else
++ {
++ sumR += r2;
++ sumG += g2;
++ sumB += b2;
++ }
++ count++;
++ }
++ }
++
++ r=(byte)((int)(2*sumR+r)/(int)(2*count+1));
++ g=(byte)((int)(2*sumG+g)/(int)(2*count+1));
++ b=(byte)((int)(2*sumB+b)/(int)(2*count+1));
++
++ setImageR(targa_rgba,column,row,columns,rows,r);
++ setImageG(targa_rgba,column,row,columns,rows,g);
++ setImageB(targa_rgba,column,row,columns,rows,b);
++ }
++ }
++}
++
++
++
+ /*
+ =========================================================
+
+@@ -1968,6 +2621,50 @@
+ } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) {
+ LoadJPG( name, pic, width, height );
+ }
++
++ switch(r_celshadalgo->integer)
++ {
++ case 1:
++ whiteTextureOne(*width,*height,*pic);
++ break;
++ case 2:
++ whiteTextureTwo(*width,*height,*pic);
++ break;
++ case 10:
++ kuwahara(*width,*height,*pic);
++ break;
++ case 11:
++ blur(*width,*height,*pic);
++ kuwahara(*width,*height,*pic);
++ break;
++ case 12:
++ kuwahara(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 13:
++ blur(*width,*height,*pic);
++ kuwahara(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 20:
++ snn(*width,*height,*pic);
++ break;
++ case 21:
++ blur(*width,*height,*pic);
++ snn(*width,*height,*pic);
++ break;
++ case 22:
++ snn(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 23:
++ blur(*width,*height,*pic);
++ snn(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ default:
++ break;
++ }
+ }
+
+
+Index: code/renderer/tr_init.c
+===================================================================
+--- code/renderer/tr_init.c (revision 933)
++++ code/renderer/tr_init.c (working copy)
+@@ -111,6 +111,10 @@
+ cvar_t *r_roundImagesDown;
+ cvar_t *r_colorMipLevels;
+ cvar_t *r_picmip;
++// Next one added for cell shading algorithm selection
++cvar_t *r_celshadalgo;
++//. next one for enable/disable cel bordering all together.
++cvar_t *r_celoutline;
+ cvar_t *r_showtris;
+ cvar_t *r_showsky;
+ cvar_t *r_shownormals;
+@@ -1110,6 +1114,10 @@
+ r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT);
+ r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT);
+ r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT);
++ // for cell shading algorithm selection
++ r_celshadalgo = ri.Cvar_Get ("r_celshadalgo", "1", CVAR_LATCH);
++ // cel outline option
++ r_celoutline = ri.Cvar_Get("r_celoutline","1", CVAR_ARCHIVE);
+ r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT);
+ r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT);
+ r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT);
+Index: code/renderer/tr_local.h
+===================================================================
+--- code/renderer/tr_local.h (revision 933)
++++ code/renderer/tr_local.h (working copy)
+@@ -1063,6 +1063,8 @@
+ extern cvar_t *r_uiFullScreen; // ui is running fullscreen
+
+ extern cvar_t *r_logFile; // number of frames to emit GL logs
++extern cvar_t *r_celshadalgo; // Cell shading, chooses method: 0 = disabled, 1 = kuwahara, 2 = whiteTexture
++extern cvar_t *r_celoutline; //. cel outline. 1 on, 0 off. (maybe other options later)
+ extern cvar_t *r_showtris; // enables wireframe rendering of the world
+ extern cvar_t *r_showsky; // forces sky in front of all surfaces
+ extern cvar_t *r_shownormals; // draws wireframe normals
+Index: code/renderer/tr_shade.c
+===================================================================
+--- code/renderer/tr_shade.c (revision 933)
++++ code/renderer/tr_shade.c (working copy)
+@@ -201,6 +201,86 @@
+ }
+
+
++//R_DRAWCEL
++static void R_DrawCel( int numIndexes, const glIndex_t *indexes ) {
++ int primitives;
++
++ if(
++ //. ignore the 2d projection. do i smell the HUD?
++ (backEnd.projection2D == qtrue) ||
++ //. ignore general entitites that are sprites. SEE NOTE #3.
++ (backEnd.currentEntity->e.reType == RT_SPRITE) ||
++ //. ignore these liquids. why? ever see liquid with tris on the surface? exactly. SEE NOTE #4.
++ (tess.shader->contentFlags & (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_FOG)) ||
++ //. ignore things that are two sided, meaning mostly things that have transparency. SEE NOTE #1.
++ (tess.shader->cullType == CT_TWO_SIDED)
++
++ ) {
++ return;
++ }
++
++ primitives = r_primitives->integer;
++
++ // default is to use triangles if compiled vertex arrays are present
++ if ( primitives == 0 ) {
++ if ( qglLockArraysEXT ) {
++ primitives = 2;
++ } else {
++ primitives = 1;
++ }
++ }
++
++ //. correction for mirrors. SEE NOTE #2.
++ if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_FRONT); }
++ else { qglCullFace (GL_BACK); }
++
++ qglEnable (GL_BLEND);
++ qglBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);
++ qglColor3f (0.0f,0.0f,0.0f);
++ qglLineWidth( (float) r_celoutline->integer );
++
++ if(primitives == 2) {
++ qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes );
++ } else if(primitives == 1) {
++ R_DrawStripElements( numIndexes, indexes, qglArrayElement );
++ } else if(primitives == 3) {
++ R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete );
++ }
++
++ //. correction for mirrors. SEE NOTE #2.
++ if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_BACK); }
++ else { qglCullFace (GL_FRONT); }
++
++ qglDisable (GL_BLEND);
++
++ return;
++
++/* Notes
++
++1. this is going to be a pain in the arse. it fixes things like light `beams` from being cel'd but it
++also will ignore any other shader set with no culling. this usually is everything that is translucent.
++but this is a good hack to clean up the screen untill something more selective comes along. or who knows
++group desision might actually be that this is liked. if so i take back calling it a `hack`, lol.
++ = bob.
++
++2. mirrors display correctly because the normals of the displayed are inverted of normal space. so to
++continue to have them display correctly, we must invert them inversely from a normal inversion.
++ = bob.
++
++3. this turns off a lot of space hogging sprite cel outlines. picture if you will five people in a small
++room all shooting rockets. each smoke puff gets a big black square around it, each explosion gets a big
++black square around it, and now nobody can see eachother because everyones screen is solid black.
++ = bob.
++
++4. ignoring liquids means you will not get black tris lines all over the top of your liquid. i put this in
++after seeing the lava on q3dm7 and water on q3ctf2 that had black lines all over the top, making the
++liquids look solid instead of... liquid.
++ = bob.
++
++*/
++}
++
++
+ /*
+ =============================================================
+
+@@ -245,6 +325,33 @@
+ GL_Bind( bundle->image[ index ] );
+ }
+
++//DRAWCEL
++static void DrawCel (shaderCommands_t *input) {
++
++ GL_Bind( tr.whiteImage );
++ qglColor3f (1,1,1);
++
++ GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
++
++ qglDisableClientState (GL_COLOR_ARRAY);
++ qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
++
++ qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
++
++ if (qglLockArraysEXT) {
++ qglLockArraysEXT(0, input->numVertexes);
++ GLimp_LogComment( "glLockArraysEXT\n" );
++ }
++
++ R_DrawCel( input->numIndexes, input->indexes );
++
++ if (qglUnlockArraysEXT) {
++ qglUnlockArraysEXT();
++ GLimp_LogComment( "glUnlockArraysEXT\n" );
++ }
++
++}
++
+ /*
+ ================
+ DrawTris
+@@ -1140,6 +1247,12 @@
+ qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value );
+ }
+
++ //. show me cel outlines.
++ //. there has to be a better place to put this.
++ if(r_celoutline->integer > 0) {
++ DrawCel(&tess);
++ }
++
+ //
+ // if there is only a single pass then we can enable color
+ // and texture arrays before we compile, otherwise we need
+Index: code/renderer/tr_shader.c
+===================================================================
+--- code/renderer/tr_shader.c (revision 933)
++++ code/renderer/tr_shader.c (working copy)
+@@ -2744,7 +2744,17 @@
+ */
+ qhandle_t RE_RegisterShaderNoMip( const char *name ) {
+ shader_t *sh;
++ // Remember previous value
++ int old_r_celshadalgo;
+
++ /*
++ * This will prevent sprites, like buttons, go through
++ * cel shading filters, like kuwahara.
++ * @author gmiranda
++ */
++ old_r_celshadalgo = r_celshadalgo->integer;
++ r_celshadalgo->integer=0;
++
+ if ( strlen( name ) >= MAX_QPATH ) {
+ Com_Printf( "Shader name exceeds MAX_QPATH\n" );
+ return 0;
+@@ -2752,6 +2762,9 @@
+
+ sh = R_FindShader( name, LIGHTMAP_2D, qfalse );
+
++ // Restore value
++ r_celshadalgo->integer=old_r_celshadalgo;
++
+ // we want to return 0 if the shader failed to
+ // load for some reason, but R_FindShader should
+ // still keep a name allocated for it, so if
diff --git a/games/ioquake3/files/extra-patch-mp3 b/games/ioquake3/files/extra-patch-mp3
new file mode 100644
index 000000000000..b33f2a660571
--- /dev/null
+++ b/games/ioquake3/files/extra-patch-mp3
@@ -0,0 +1,753 @@
+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
diff --git a/games/ioquake3/files/patch-Makefile b/games/ioquake3/files/patch-Makefile
index c2732bfca0d6..70d1aa3fe694 100644
--- a/games/ioquake3/files/patch-Makefile
+++ b/games/ioquake3/files/patch-Makefile
@@ -1,6 +1,6 @@
--- Makefile.orig Tue Nov 28 19:05:39 2006
-+++ Makefile Tue Dec 26 00:19:11 2006
-@@ -26,11 +26,19 @@
++++ Makefile Fri Sep 14 14:44:03 2007
+@@ -26,11 +26,20 @@
endif
endif
@@ -15,6 +15,7 @@
+BUILD_GAME_SO?=0
+BUILD_SERVER?=0
+HAVE_VM_COMPILED?=false
++USE_CODEC_MP3?=0
+USE_CODEC_VORBIS?=0
+USE_LOCAL_HEADERS?=0
+USE_OPENAL?=0
@@ -25,7 +26,7 @@
#############################################################################
#
-@@ -88,30 +96,10 @@
+@@ -88,30 +97,10 @@
endif
export USE_CCACHE
@@ -57,7 +58,41 @@
CDIR=$(MOUNT_DIR)/client
SDIR=$(MOUNT_DIR)/server
RDIR=$(MOUNT_DIR)/renderer
-@@ -444,18 +432,12 @@
+@@ -185,6 +174,10 @@
+ BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ BASE_CFLAGS += -DUSE_CODEC_MP3=1
++ endif
++
+ ifeq ($(USE_SDL),1)
+ BASE_CFLAGS += -DUSE_SDL_VIDEO=1 -DUSE_SDL_SOUND=1 $(shell sdl-config --cflags)
+ GL_CFLAGS =
+@@ -243,6 +236,10 @@
+ endif
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ CLIENT_LDFLAGS += -lmad
++ endif
++
+ ifeq ($(USE_CODEC_VORBIS),1)
+ CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
+ endif
+@@ -342,6 +339,11 @@
+ endif
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ BASE_CFLAGS += -DUSE_CODEC_MP3=1
++ CLIENT_LDFLAGS += -lmad
++ endif
++
+ ifeq ($(USE_CODEC_VORBIS),1)
+ BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
+ CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
+@@ -444,18 +446,12 @@
ifeq ($(PLATFORM),freebsd)
@@ -79,7 +114,7 @@
ifeq ($(USE_OPENAL),1)
BASE_CFLAGS += -DUSE_OPENAL=1
-@@ -468,47 +450,61 @@
+@@ -468,47 +464,61 @@
BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
endif
@@ -164,7 +199,7 @@
endif
endif
-@@ -516,7 +512,6 @@
+@@ -516,7 +526,6 @@
CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
endif
@@ -172,7 +207,7 @@
else # ifeq freebsd
#############################################################################
-@@ -670,24 +665,25 @@
+@@ -670,24 +679,25 @@
TARGETS =
ifneq ($(BUILD_SERVER),0)
@@ -209,7 +244,7 @@
endif
ifneq ($(BUILD_GAME_QVM),0)
-@@ -749,11 +745,11 @@
+@@ -749,11 +759,11 @@
$(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(DEBUG_CFLAGS) $(DEPEND_CFLAGS)"
build_release: B=$(BR)
@@ -223,7 +258,15 @@
targets: $(TARGETS)
-@@ -964,13 +960,10 @@
+@@ -836,6 +846,7 @@
+ $(B)/client/snd_codec.o \
+ $(B)/client/snd_codec_wav.o \
+ $(B)/client/snd_codec_ogg.o \
++ $(B)/client/snd_codec_mp3.o \
+ \
+ $(B)/client/qal.o \
+ $(B)/client/snd_openal.o \
+@@ -964,13 +975,10 @@
ifeq ($(ARCH),i386)
Q3OBJ += $(B)/client/vm_x86.o
endif
@@ -239,7 +282,7 @@
Q3OBJ += $(B)/client/$(VM_PPC).o
endif
endif
-@@ -1017,10 +1010,10 @@
+@@ -1017,10 +1025,10 @@
$(B)/client/sdl_glimp_smp.o
endif
@@ -252,7 +295,15 @@
$(CC) -o $@ $(Q3OBJ) $(Q3POBJ_SMP) $(CLIENT_LDFLAGS) \
$(THREAD_LDFLAGS) $(LDFLAGS) $(LIBSDLMAIN)
-@@ -1317,18 +1310,15 @@
+@@ -1056,6 +1064,7 @@
+ $(B)/client/snd_codec.o : $(CDIR)/snd_codec.c; $(DO_CC)
+ $(B)/client/snd_codec_wav.o : $(CDIR)/snd_codec_wav.c; $(DO_CC)
+ $(B)/client/snd_codec_ogg.o : $(CDIR)/snd_codec_ogg.c; $(DO_CC)
++$(B)/client/snd_codec_mp3.o : $(CDIR)/snd_codec_mp3.c; $(DO_CC)
+
+ $(B)/client/qal.o : $(CDIR)/qal.c; $(DO_CC)
+ $(B)/client/snd_openal.o : $(CDIR)/snd_openal.c; $(DO_CC)
+@@ -1317,18 +1326,15 @@
ifeq ($(ARCH),i386)
Q3DOBJ += $(B)/ded/vm_x86.o
endif
@@ -274,7 +325,7 @@
$(CC) -o $@ $(Q3DOBJ) $(LDFLAGS)
$(B)/ded/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_DED_CC)
-@@ -1445,7 +1435,7 @@
+@@ -1445,7 +1451,7 @@
Q3CGOBJ = $(Q3CGOBJ_) $(B)/baseq3/cgame/cg_syscalls.o
Q3CGVMOBJ = $(Q3CGOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -283,7 +334,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ)
$(B)/baseq3/vm/cgame.qvm: $(Q3CGVMOBJ) $(CGDIR)/cg_syscalls.asm
-@@ -1486,7 +1476,7 @@
+@@ -1486,7 +1492,7 @@
MPCGOBJ = $(MPCGOBJ_) $(B)/missionpack/cgame/cg_syscalls.o
MPCGVMOBJ = $(MPCGOBJ_:%.o=%.asm) $(B)/missionpack/game/bg_lib.asm
@@ -292,7 +343,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ)
$(B)/missionpack/vm/cgame.qvm: $(MPCGVMOBJ) $(CGDIR)/cg_syscalls.asm
-@@ -1536,7 +1526,7 @@
+@@ -1536,7 +1542,7 @@
Q3GOBJ = $(Q3GOBJ_) $(B)/baseq3/game/g_syscalls.o
Q3GVMOBJ = $(Q3GOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -301,7 +352,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ)
$(B)/baseq3/vm/qagame.qvm: $(Q3GVMOBJ) $(GDIR)/g_syscalls.asm
-@@ -1584,7 +1574,7 @@
+@@ -1584,7 +1590,7 @@
MPGOBJ = $(MPGOBJ_) $(B)/missionpack/game/g_syscalls.o
MPGVMOBJ = $(MPGOBJ_:%.o=%.asm) $(B)/missionpack/game/bg_lib.asm
@@ -310,7 +361,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ)
$(B)/missionpack/vm/qagame.qvm: $(MPGVMOBJ) $(GDIR)/g_syscalls.asm
-@@ -1644,7 +1634,7 @@
+@@ -1644,7 +1650,7 @@
Q3UIOBJ = $(Q3UIOBJ_) $(B)/missionpack/ui/ui_syscalls.o
Q3UIVMOBJ = $(Q3UIOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -319,7 +370,7 @@
$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ)
$(B)/baseq3/vm/ui.qvm: $(Q3UIVMOBJ) $(UIDIR)/ui_syscalls.asm
-@@ -1669,7 +1659,7 @@
+@@ -1669,7 +1675,7 @@
MPUIOBJ = $(MPUIOBJ_) $(B)/missionpack/ui/ui_syscalls.o
MPUIVMOBJ = $(MPUIOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
diff --git a/games/ioquake3/pkg-message b/games/ioquake3/pkg-message
index a812c9d679ba..1f128f1231e9 100644
--- a/games/ioquake3/pkg-message
+++ b/games/ioquake3/pkg-message
@@ -12,4 +12,6 @@ they have additional variables which would be removed if other engines
overwrite them. But you can safely copy the original directory to the new one
for the first time.
+If you enabled CELLSHADING, check files/extra-patch-cellshading for variables.
+
==============================================================================
diff --git a/games/iourbanterror/Makefile b/games/iourbanterror/Makefile
index c2f42bfbbec3..cb37008aa1c1 100644
--- a/games/iourbanterror/Makefile
+++ b/games/iourbanterror/Makefile
@@ -19,8 +19,10 @@ USE_BZIP2= yes
USE_GMAKE= yes
OPTIONS= CLIENT "Build client" on \
+ CELLSHADING "Enable Cell Shading effect" off \
GAMELIBS "Build game libraries (when not mandatory)" off \
DEDICATED "Build dedicated server" on \
+ MP3 "Enable MP3 support" off \
OPENAL "Enable OpenAL (3D sound) support" off \
OPENAL_DLOPEN "Enable dynamic loading of OpenAL" off \
OPTIMIZED_CFLAGS "Enable compilation optimizations" on \
@@ -29,7 +31,7 @@ OPTIONS= CLIENT "Build client" on \
SMP "Build SMP (threaded) client" on \
VORBIS "Enable Ogg Vorbis codec support" off
-MAKE_ENV+= DEFAULT_BASEDIR="${Q3DIR}" LIBDIR="${LIBDIR}" \
+MAKE_ENV= DEFAULT_BASEDIR="${Q3DIR}" LIBDIR="${LIBDIR}" \
PTHREAD_LIBS="${PTHREAD_LIBS}"
PLIST_SUB= LIBDIR="${LIBDIR:S/${PREFIX}\///}"
@@ -53,6 +55,10 @@ HAVE_VM_COMPILED= yes
MAKE_ENV+= HAVE_VM_COMPILED=true
.endif
+.if defined(WITH_CELLSHADING)
+EXTRA_PATCHES+= ${FILESDIR}/extra-patch-cellshading
+.endif
+
.if !defined(WITHOUT_CLIENT) || !defined(WITHOUT_SMP)
# OpenAL
. if defined(WITH_OPENAL)
@@ -104,6 +110,12 @@ PLIST_SUB+= GAMELIBS=""
PLIST_SUB+= GAMELIBS="@comment "
.endif
+.if defined(WITH_MP3)
+EXTRA_PATCHES+= ${FILESDIR}/extra-patch-mp3
+LIB_DEPENDS+= mad.2:${PORTSDIR}/audio/libmad
+MAKE_ENV+= USE_CODEC_MP3=1
+.endif
+
.if !defined(WITHOUT_OPTIMIZED_CFLAGS)
MAKE_ENV+= USE_OPTIMIZED_CFLAGS=1
.endif
diff --git a/games/iourbanterror/files/extra-patch-cellshading b/games/iourbanterror/files/extra-patch-cellshading
new file mode 100644
index 000000000000..ce459a17bd45
--- /dev/null
+++ b/games/iourbanterror/files/extra-patch-cellshading
@@ -0,0 +1,933 @@
+Index: code/renderer/tr_image.c
+===================================================================
+--- code/renderer/tr_image.c (revision 933)
++++ code/renderer/tr_image.c (working copy)
+@@ -34,7 +34,24 @@
+ #define JPEG_INTERNALS
+ #include "../jpeg-6/jpeglib.h"
+
++/**
++ * Headers for cell shading
++ * @author Jordi Prats Catala
++ * @author Guillermo Miranda Alamo
++ */
++/*
++byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows);
++byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows);
++void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value);
++*/
++//void kuwahara(int columns, int rows, byte *targa_rgba);
+
++
+ static void LoadBMP( const char *name, byte **pic, int *width, int *height );
+ static void LoadTGA( const char *name, byte **pic, int *width, int *height );
+ static void LoadJPG( const char *name, byte **pic, int *width, int *height );
+@@ -799,7 +816,643 @@
+ return image;
+ }
+
++/****************************
++RGB GET/SET
++****************************/
+
++//RED
++static byte getImageR(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ return *pixbuf;
++}
++
++static void setImageR(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ *pixbuf=value;
++}
++//GREEN
++static byte getImageG(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++
++ pixbuf++;
++ return *pixbuf;
++}
++
++static void setImageG(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf++;
++ *pixbuf=value;
++}
++//BLUE
++static byte getImageB(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ if(rows<=y)
++ y=y%rows;
++ if(columns<=x)
++ x=x%columns;
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=2;
++ return *pixbuf;
++}
++
++static void setImageB(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=2;
++ *pixbuf=value;
++}
++//ALPHA
++static byte getImageA(byte *targa_rgba, int x, int y, int columns, int rows)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=3;
++ return *pixbuf;
++}
++
++static void setImageA(byte *targa_rgba, int x, int y, int columns, int rows, byte value)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4;
++
++ pixbuf+=(x*4);
++ pixbuf+=3;
++ *pixbuf=value;
++}
++
++//RGB
++static void getImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb)
++{
++ byte *pixbuf;
++
++ x*=((x<0)?-1:1);
++ y*=((y<0)?-1:1);
++ //if(rows<=y)
++ y=y%rows;
++ //if(columns<=x)
++ x=x%columns;
++ //x*=((x<0)?-1:1);
++ //y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4 + x*4;
++
++ rgb[0]=*pixbuf;
++ rgb[1]=*(pixbuf+1);
++ rgb[2]=*(pixbuf+2);
++}
++
++static void setImageRGB(byte *targa_rgba, int x, int y, int columns, int rows, vec3_t rgb)
++{
++ byte *pixbuf;
++
++ //x*=((x<0)?-1:1);
++ //y*=((y<0)?-1:1);
++
++ pixbuf = targa_rgba + y*columns*4 + (x*4);
++
++ *pixbuf=(byte)(rgb[0]);
++ *(pixbuf+1)=(byte)(rgb[1]);
++ *(pixbuf+2)=(byte)(rgb[2]);
++}
++
++/****************************
++NO BRAINER'S BLUR
++****************************/
++static void blur(int columns, int rows, byte *targa_rgba)
++{
++ int row, column;
++ float sum;
++
++
++ for(row=0; row<rows; row++)
++ {
++ //pixbuf = targa_rgba + row*columns*4;
++ for(column=0; column<columns; column++)
++ {
++ sum=0;
++ sum+=getImageR(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageR(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageR(targa_rgba,column,row,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageR(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageR(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageR(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageR(targa_rgba, column, row, columns, rows, (byte)sum);
++ ////////////////////
++ sum=0;
++ sum+=getImageG(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageG(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageG(targa_rgba,column,row,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageG(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageG(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageG(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageG(targa_rgba, column, row, columns, rows, (byte)sum);
++ ////////////////////////
++ sum=0;
++ sum+=getImageB(targa_rgba,column-1,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row-1,columns,rows);
++ sum+=getImageB(targa_rgba,column-1,row,columns,rows);
++ sum+=getImageB(targa_rgba,column,row,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row,columns,rows);
++ sum+=getImageB(targa_rgba,column-1,row+1,columns,rows);
++ sum+=getImageB(targa_rgba,column,row+1,columns,rows);
++ sum+=getImageB(targa_rgba,column+1,row+1,columns,rows);
++
++ sum/=9.0f;
++
++ setImageB(targa_rgba, column, row, columns, rows, (byte)sum);
++
++ // "halftoning"
++ /*if((row%5==0)&&(column%5==1))
++ {
++ gris=0;
++ gris+=red;
++ gris+=green;
++ gris+=blue;
++ gris/=3;
++
++ gris=255-gris;
++ if(gris<0)
++ gris=0;
++
++ setImageR(targa_rgba, column, row, columns, rows, (byte)gris);
++ setImageG(targa_rgba, column, row, columns, rows, (byte)gris);
++ setImageB(targa_rgba, column, row, columns, rows, (byte)gris);
++
++ }*/
++
++ }
++ }
++
++}
++
++
++/****************************
++COLORED LIGHTMAP
++****************************/
++void whiteTextureOne(int columns, int rows, byte *targa_rgba){
++ //byte *pixbyf;
++ int row, column;
++ long rMean=0, gMean=0, bMean=0;
++ int pixels=0;
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ // Don't count fully transparent pixels
++ if(getImageA(targa_rgba,column,row,columns,rows)==0)
++ continue;
++ // Sum pixels values
++ rMean+=getImageR(targa_rgba,column,row,columns,rows);
++ gMean+=getImageG(targa_rgba,column,row,columns,rows);
++ bMean+=getImageB(targa_rgba,column,row,columns,rows);
++ pixels++;
++ }
++ }
++
++ // Calculate average
++ if(pixels>0){
++ rMean=((float)rMean/(float)pixels);
++ gMean=((float)gMean/(float)pixels);
++ bMean=((float)bMean/(float)pixels);
++ }
++ else{
++ return;
++ }
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ setImageR(targa_rgba,column,row,columns,rows,rMean);
++ setImageG(targa_rgba,column,row,columns,rows,gMean);
++ setImageB(targa_rgba,column,row,columns,rows,bMean);
++ }
++ }
++}
++
++int diffSquare(int mean, int val){
++ float variance = (val-mean)/255.0f;
++ float radius = mean<128?mean:255-mean;
++ return mean+(radius*variance);
++}
++
++/****************************
++DECONTRAST
++****************************/
++void whiteTextureTwo(int columns, int rows, byte *targa_rgba){
++ int row, column;
++ long rMean=0, gMean=0, bMean=0;
++ int r=0, g=0, b=0;
++ int pixels=0;
++
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ // Don't count fully transparent pixels
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ // Sum pixels values
++ rMean+=getImageR(targa_rgba,column,row,columns,rows);
++ gMean+=getImageG(targa_rgba,column,row,columns,rows);
++ bMean+=getImageB(targa_rgba,column,row,columns,rows);
++ pixels++;
++ }
++ }
++
++ // Calculate average
++ if(pixels>0){
++ rMean=rMean/pixels;
++ gMean=gMean/pixels;
++ bMean=bMean/pixels;
++ }
++ else{
++ return;
++ }
++
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ if(getImageA(targa_rgba,column,row,columns,rows)<32)
++ continue;
++ r=getImageR(targa_rgba,column,row,columns,rows);
++ g=getImageG(targa_rgba,column,row,columns,rows);
++ b=getImageB(targa_rgba,column,row,columns,rows);
++
++ setImageR(targa_rgba,column,row,columns,rows,diffSquare(rMean,r));
++ setImageG(targa_rgba,column,row,columns,rows,diffSquare(gMean,g));
++ setImageB(targa_rgba,column,row,columns,rows,diffSquare(bMean,b));
++
++ }
++ }
++}
++
++/****************************
++KUWAHARA ,FAILS SOMEWHERE
++****************************/
++#define KWH_RADIUS 2
++static void mean_variance(int x0, int y0, int x1, int y1, int columns, int rows, byte *targa_rgba, vec4_t mv )
++{
++ short min=255*3, max=0;
++ unsigned short count= 0;
++ short row, column;
++ unsigned short value;
++ vec3_t rgb;
++
++ mv[0]=mv[1]=mv[2]=mv[3]=0;
++
++ for(row=y0;row<=y1;row++)
++ {
++ for(column=x0;column<=x1;column++)
++ {
++ getImageRGB(targa_rgba,column,row,columns,rows,rgb);
++
++ VectorAdd(mv,rgb,mv);
++
++ count++;
++ value=rgb[0]+rgb[1]+rgb[2];
++ if(value<min) min=value;
++ if(value>max) max=value;
++ }
++ }
++
++ mv[0]/=count;
++ mv[1]/=count;
++ mv[2]/=count;
++ mv[3]= (max-min)/3.0f;
++}
++
++
++static void rgb_kuwahara(int x, int y, int columns, int rows, byte *targa_rgba, vec4_t bmv)
++{
++ vec4_t mv;
++ bmv[0]=bmv[1]=bmv[2]=bmv[3]=255;
++
++ mean_variance(x-KWH_RADIUS, y-KWH_RADIUS, x, y, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x, y-KWH_RADIUS, x+KWH_RADIUS, y, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x, y, x+KWH_RADIUS, y+KWH_RADIUS, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++
++ mean_variance(x-KWH_RADIUS, y, x, y+KWH_RADIUS, columns, rows, targa_rgba, mv);
++ if( mv[3] < bmv[3] )
++ {
++ Vector4Copy(mv,bmv);
++ }
++}
++
++static void kuwahara(int columns, int rows, byte *targa_rgba){
++ int row, column;
++ vec4_t rgbv;
++
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ rgb_kuwahara(column, row, columns, rows, targa_rgba, rgbv);
++ setImageRGB(targa_rgba,column,row,columns,rows,rgbv);
++ }
++ }
++}
++
++
++#define FLT_MAX 3.40282346638528860000e+38
++static void kuwahara3(int columns, int rows, byte *targa_rgba)
++{
++ byte channel;
++ int size = 10;
++ int index1,index2;
++ int width = columns-4;
++ int height = rows-4;
++ int size2 = (size+1)/2;
++ int offset = (size-1)/2;
++ const int width2 = columns + offset;
++ const int height2 = rows + offset;
++ int x1start = 4;
++ int y1start = 4;
++ int x2, y2;
++ int sum, sum2, n, v=0, xbase, ybase;
++ int y1,x1;
++ int xbase2=0, ybase2=0;
++ float var, min;
++ float** mean, **variance;
++
++ //blur(columns, rows, targa_rgba);
++
++ // I hate malloc I hate malloc I hate malloc I hate malloc I hate malloc I hate malloc
++ mean = (float**)malloc(sizeof(float*)*width2);
++ for(index1=0;index1<width2;index1++)
++ mean[index1] = (float*)malloc(sizeof(float)*height2);
++
++ variance = (float**)malloc(sizeof(float*)*width2);
++ for(index2=0;index2<width2;index2++)
++ variance[index2] = (float*)malloc(sizeof(float)*height2);
++
++ // For each channel (R,G,B)
++ // for(channel=0;channel<2;channel++)
++ // FTL
++ for(channel=0;channel<3;channel++){
++ for (y1=y1start-offset; y1<y1start+height; y1++) {
++
++ for (x1=x1start-offset; x1<x1start+width; x1++) {
++ sum=0; sum2=0; n=0;
++ for (x2=x1; x2<x1+size2; x2++) {
++ for (y2=y1; y2<y1+size2; y2++) {
++ //v = i(x2, y2);
++ switch(channel){
++ case 0:
++ v = getImageR(targa_rgba,x2,y2,columns,rows);
++ break;
++ case 1:
++ v = getImageG(targa_rgba,x2,y2,columns,rows);
++ break;
++ case 2:
++ v = getImageB(targa_rgba,x2,y2,columns,rows);
++ break;
++ }
++ //v = *targa_rgba + y2*columns*4+x2*4;
++ v/=10;
++ v*=10;
++ sum += v;
++ sum2 += v*v;
++ n++;
++ }
++ }
++ //cerr << "Accedo" << endl;
++ mean[x1+offset][y1+offset] = (float)(sum/n);
++ variance[x1+offset][y1+offset] = (float)((n*sum2-sum*sum)/n);
++ }
++ }
++
++ for (y1=y1start; y1<y1start+height; y1++) {
++ /*if ((y1%20)==0)
++ cout << (0.7+0.3*(y1-y1start)/height);*/
++ for (x1=x1start; x1<x1start+width; x1++) {
++ min = FLT_MAX;
++ xbase = x1; ybase=y1;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ xbase = x1+offset;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ ybase = y1+offset;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ xbase = x1;
++ var = variance[xbase][ybase];
++ if (var<min){
++ min= var;
++ xbase2=xbase;
++ ybase2=ybase;
++ }
++ //i(x1, y1)=(int)(mean[xbase2][ybase2]+0.5);
++ switch(channel){
++ case 0:
++ setImageR(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ case 1:
++ setImageG(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ case 2:
++ setImageB(targa_rgba,x1,y1,columns,rows,(byte)(mean[xbase2][ybase2]+0.5));
++ break;
++ }
++ }
++ }
++ }
++ // Fuck mean & variance, this is hell (!+) Bad Religion
++ for(index1=0;index1<width2;index1++)
++ free(mean[index1]);
++ free(mean);
++
++ for(index2=0;index2<width2;index2++)
++ free(variance[index2]);
++ free(variance);
++
++ //blur(columns, rows, targa_rgba);
++}
++
++/****************************
++Symmetric Nearest Neighbour
++****************************/
++
++#define SNN_RADIUS 3
++
++static int deltaE(int l1,int a1,int b1,int l2,int a2,int b2)
++{
++ return (l1-l2)*(l1-l2) + (a1-a2)*(a1-a2) + (b1-b2)*(b1-b2);
++}
++
++static void snn(int columns, int rows, byte *targa_rgba)
++{
++
++ int row, column;
++ unsigned short sumR, sumG, sumB;
++ unsigned short count;
++ short u, v;
++ byte r, g, b;
++ byte r1, g1, b1;
++ byte r2, g2, b2;
++ for(row=0;row<rows;row++){
++ for(column=0;column<columns;column++){
++ sumR=0;
++ sumG=0;
++ sumB=0;
++ count=0;
++
++ r=getImageR(targa_rgba,column,row,columns,rows);
++ g=getImageG(targa_rgba,column,row,columns,rows);
++ b=getImageB(targa_rgba,column,row,columns,rows);
++
++ for(v=-SNN_RADIUS;v<=0;v++)
++ {
++ for(u=-SNN_RADIUS;u<=SNN_RADIUS;u++)
++ {
++ if(v==0&&u>=0) break;
++ // Sum pixels values
++ r1=getImageR(targa_rgba,column+u,row+v,columns,rows);
++ g1=getImageG(targa_rgba,column+u,row+v,columns,rows);
++ b1=getImageB(targa_rgba,column+u,row+v,columns,rows);
++
++ r2=getImageR(targa_rgba,column-u,row-v,columns,rows);
++ g2=getImageG(targa_rgba,column-u,row-v,columns,rows);
++ b2=getImageB(targa_rgba,column-u,row-v,columns,rows);
++
++ if ( deltaE(r,g,b,r1,g1,b1) < deltaE(r,g,b,r2,g2,b2))
++ {
++ sumR += r1;
++ sumG += g1;
++ sumB += b1;
++ }
++ else
++ {
++ sumR += r2;
++ sumG += g2;
++ sumB += b2;
++ }
++ count++;
++ }
++ }
++
++ r=(byte)((int)(2*sumR+r)/(int)(2*count+1));
++ g=(byte)((int)(2*sumG+g)/(int)(2*count+1));
++ b=(byte)((int)(2*sumB+b)/(int)(2*count+1));
++
++ setImageR(targa_rgba,column,row,columns,rows,r);
++ setImageG(targa_rgba,column,row,columns,rows,g);
++ setImageB(targa_rgba,column,row,columns,rows,b);
++ }
++ }
++}
++
++
++
+ /*
+ =========================================================
+
+@@ -1968,6 +2621,50 @@
+ } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) {
+ LoadJPG( name, pic, width, height );
+ }
++
++ switch(r_celshadalgo->integer)
++ {
++ case 1:
++ whiteTextureOne(*width,*height,*pic);
++ break;
++ case 2:
++ whiteTextureTwo(*width,*height,*pic);
++ break;
++ case 10:
++ kuwahara(*width,*height,*pic);
++ break;
++ case 11:
++ blur(*width,*height,*pic);
++ kuwahara(*width,*height,*pic);
++ break;
++ case 12:
++ kuwahara(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 13:
++ blur(*width,*height,*pic);
++ kuwahara(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 20:
++ snn(*width,*height,*pic);
++ break;
++ case 21:
++ blur(*width,*height,*pic);
++ snn(*width,*height,*pic);
++ break;
++ case 22:
++ snn(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ case 23:
++ blur(*width,*height,*pic);
++ snn(*width,*height,*pic);
++ blur(*width,*height,*pic);
++ break;
++ default:
++ break;
++ }
+ }
+
+
+Index: code/renderer/tr_init.c
+===================================================================
+--- code/renderer/tr_init.c (revision 933)
++++ code/renderer/tr_init.c (working copy)
+@@ -111,6 +111,10 @@
+ cvar_t *r_roundImagesDown;
+ cvar_t *r_colorMipLevels;
+ cvar_t *r_picmip;
++// Next one added for cell shading algorithm selection
++cvar_t *r_celshadalgo;
++//. next one for enable/disable cel bordering all together.
++cvar_t *r_celoutline;
+ cvar_t *r_showtris;
+ cvar_t *r_showsky;
+ cvar_t *r_shownormals;
+@@ -1110,6 +1114,10 @@
+ r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT);
+ r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT);
+ r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT);
++ // for cell shading algorithm selection
++ r_celshadalgo = ri.Cvar_Get ("r_celshadalgo", "1", CVAR_LATCH);
++ // cel outline option
++ r_celoutline = ri.Cvar_Get("r_celoutline","1", CVAR_ARCHIVE);
+ r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT);
+ r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT);
+ r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT);
+Index: code/renderer/tr_local.h
+===================================================================
+--- code/renderer/tr_local.h (revision 933)
++++ code/renderer/tr_local.h (working copy)
+@@ -1063,6 +1063,8 @@
+ extern cvar_t *r_uiFullScreen; // ui is running fullscreen
+
+ extern cvar_t *r_logFile; // number of frames to emit GL logs
++extern cvar_t *r_celshadalgo; // Cell shading, chooses method: 0 = disabled, 1 = kuwahara, 2 = whiteTexture
++extern cvar_t *r_celoutline; //. cel outline. 1 on, 0 off. (maybe other options later)
+ extern cvar_t *r_showtris; // enables wireframe rendering of the world
+ extern cvar_t *r_showsky; // forces sky in front of all surfaces
+ extern cvar_t *r_shownormals; // draws wireframe normals
+Index: code/renderer/tr_shade.c
+===================================================================
+--- code/renderer/tr_shade.c (revision 933)
++++ code/renderer/tr_shade.c (working copy)
+@@ -201,6 +201,86 @@
+ }
+
+
++//R_DRAWCEL
++static void R_DrawCel( int numIndexes, const glIndex_t *indexes ) {
++ int primitives;
++
++ if(
++ //. ignore the 2d projection. do i smell the HUD?
++ (backEnd.projection2D == qtrue) ||
++ //. ignore general entitites that are sprites. SEE NOTE #3.
++ (backEnd.currentEntity->e.reType == RT_SPRITE) ||
++ //. ignore these liquids. why? ever see liquid with tris on the surface? exactly. SEE NOTE #4.
++ (tess.shader->contentFlags & (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_FOG)) ||
++ //. ignore things that are two sided, meaning mostly things that have transparency. SEE NOTE #1.
++ (tess.shader->cullType == CT_TWO_SIDED)
++
++ ) {
++ return;
++ }
++
++ primitives = r_primitives->integer;
++
++ // default is to use triangles if compiled vertex arrays are present
++ if ( primitives == 0 ) {
++ if ( qglLockArraysEXT ) {
++ primitives = 2;
++ } else {
++ primitives = 1;
++ }
++ }
++
++ //. correction for mirrors. SEE NOTE #2.
++ if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_FRONT); }
++ else { qglCullFace (GL_BACK); }
++
++ qglEnable (GL_BLEND);
++ qglBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);
++ qglColor3f (0.0f,0.0f,0.0f);
++ qglLineWidth( (float) r_celoutline->integer );
++
++ if(primitives == 2) {
++ qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes );
++ } else if(primitives == 1) {
++ R_DrawStripElements( numIndexes, indexes, qglArrayElement );
++ } else if(primitives == 3) {
++ R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete );
++ }
++
++ //. correction for mirrors. SEE NOTE #2.
++ if(backEnd.viewParms.isMirror == qtrue) { qglCullFace (GL_BACK); }
++ else { qglCullFace (GL_FRONT); }
++
++ qglDisable (GL_BLEND);
++
++ return;
++
++/* Notes
++
++1. this is going to be a pain in the arse. it fixes things like light `beams` from being cel'd but it
++also will ignore any other shader set with no culling. this usually is everything that is translucent.
++but this is a good hack to clean up the screen untill something more selective comes along. or who knows
++group desision might actually be that this is liked. if so i take back calling it a `hack`, lol.
++ = bob.
++
++2. mirrors display correctly because the normals of the displayed are inverted of normal space. so to
++continue to have them display correctly, we must invert them inversely from a normal inversion.
++ = bob.
++
++3. this turns off a lot of space hogging sprite cel outlines. picture if you will five people in a small
++room all shooting rockets. each smoke puff gets a big black square around it, each explosion gets a big
++black square around it, and now nobody can see eachother because everyones screen is solid black.
++ = bob.
++
++4. ignoring liquids means you will not get black tris lines all over the top of your liquid. i put this in
++after seeing the lava on q3dm7 and water on q3ctf2 that had black lines all over the top, making the
++liquids look solid instead of... liquid.
++ = bob.
++
++*/
++}
++
++
+ /*
+ =============================================================
+
+@@ -245,6 +325,33 @@
+ GL_Bind( bundle->image[ index ] );
+ }
+
++//DRAWCEL
++static void DrawCel (shaderCommands_t *input) {
++
++ GL_Bind( tr.whiteImage );
++ qglColor3f (1,1,1);
++
++ GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
++
++ qglDisableClientState (GL_COLOR_ARRAY);
++ qglDisableClientState (GL_TEXTURE_COORD_ARRAY);
++
++ qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD
++
++ if (qglLockArraysEXT) {
++ qglLockArraysEXT(0, input->numVertexes);
++ GLimp_LogComment( "glLockArraysEXT\n" );
++ }
++
++ R_DrawCel( input->numIndexes, input->indexes );
++
++ if (qglUnlockArraysEXT) {
++ qglUnlockArraysEXT();
++ GLimp_LogComment( "glUnlockArraysEXT\n" );
++ }
++
++}
++
+ /*
+ ================
+ DrawTris
+@@ -1140,6 +1247,12 @@
+ qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value );
+ }
+
++ //. show me cel outlines.
++ //. there has to be a better place to put this.
++ if(r_celoutline->integer > 0) {
++ DrawCel(&tess);
++ }
++
+ //
+ // if there is only a single pass then we can enable color
+ // and texture arrays before we compile, otherwise we need
+Index: code/renderer/tr_shader.c
+===================================================================
+--- code/renderer/tr_shader.c (revision 933)
++++ code/renderer/tr_shader.c (working copy)
+@@ -2744,7 +2744,17 @@
+ */
+ qhandle_t RE_RegisterShaderNoMip( const char *name ) {
+ shader_t *sh;
++ // Remember previous value
++ int old_r_celshadalgo;
+
++ /*
++ * This will prevent sprites, like buttons, go through
++ * cel shading filters, like kuwahara.
++ * @author gmiranda
++ */
++ old_r_celshadalgo = r_celshadalgo->integer;
++ r_celshadalgo->integer=0;
++
+ if ( strlen( name ) >= MAX_QPATH ) {
+ Com_Printf( "Shader name exceeds MAX_QPATH\n" );
+ return 0;
+@@ -2752,6 +2762,9 @@
+
+ sh = R_FindShader( name, LIGHTMAP_2D, qfalse );
+
++ // Restore value
++ r_celshadalgo->integer=old_r_celshadalgo;
++
+ // we want to return 0 if the shader failed to
+ // load for some reason, but R_FindShader should
+ // still keep a name allocated for it, so if
diff --git a/games/iourbanterror/files/extra-patch-mp3 b/games/iourbanterror/files/extra-patch-mp3
new file mode 100644
index 000000000000..b33f2a660571
--- /dev/null
+++ b/games/iourbanterror/files/extra-patch-mp3
@@ -0,0 +1,753 @@
+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
diff --git a/games/iourbanterror/files/patch-Makefile b/games/iourbanterror/files/patch-Makefile
index c2732bfca0d6..70d1aa3fe694 100644
--- a/games/iourbanterror/files/patch-Makefile
+++ b/games/iourbanterror/files/patch-Makefile
@@ -1,6 +1,6 @@
--- Makefile.orig Tue Nov 28 19:05:39 2006
-+++ Makefile Tue Dec 26 00:19:11 2006
-@@ -26,11 +26,19 @@
++++ Makefile Fri Sep 14 14:44:03 2007
+@@ -26,11 +26,20 @@
endif
endif
@@ -15,6 +15,7 @@
+BUILD_GAME_SO?=0
+BUILD_SERVER?=0
+HAVE_VM_COMPILED?=false
++USE_CODEC_MP3?=0
+USE_CODEC_VORBIS?=0
+USE_LOCAL_HEADERS?=0
+USE_OPENAL?=0
@@ -25,7 +26,7 @@
#############################################################################
#
-@@ -88,30 +96,10 @@
+@@ -88,30 +97,10 @@
endif
export USE_CCACHE
@@ -57,7 +58,41 @@
CDIR=$(MOUNT_DIR)/client
SDIR=$(MOUNT_DIR)/server
RDIR=$(MOUNT_DIR)/renderer
-@@ -444,18 +432,12 @@
+@@ -185,6 +174,10 @@
+ BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ BASE_CFLAGS += -DUSE_CODEC_MP3=1
++ endif
++
+ ifeq ($(USE_SDL),1)
+ BASE_CFLAGS += -DUSE_SDL_VIDEO=1 -DUSE_SDL_SOUND=1 $(shell sdl-config --cflags)
+ GL_CFLAGS =
+@@ -243,6 +236,10 @@
+ endif
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ CLIENT_LDFLAGS += -lmad
++ endif
++
+ ifeq ($(USE_CODEC_VORBIS),1)
+ CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
+ endif
+@@ -342,6 +339,11 @@
+ endif
+ endif
+
++ ifeq ($(USE_CODEC_MP3),1)
++ BASE_CFLAGS += -DUSE_CODEC_MP3=1
++ CLIENT_LDFLAGS += -lmad
++ endif
++
+ ifeq ($(USE_CODEC_VORBIS),1)
+ BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
+ CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
+@@ -444,18 +446,12 @@
ifeq ($(PLATFORM),freebsd)
@@ -79,7 +114,7 @@
ifeq ($(USE_OPENAL),1)
BASE_CFLAGS += -DUSE_OPENAL=1
-@@ -468,47 +450,61 @@
+@@ -468,47 +464,61 @@
BASE_CFLAGS += -DUSE_CODEC_VORBIS=1
endif
@@ -164,7 +199,7 @@
endif
endif
-@@ -516,7 +512,6 @@
+@@ -516,7 +526,6 @@
CLIENT_LDFLAGS += -lvorbisfile -lvorbis -logg
endif
@@ -172,7 +207,7 @@
else # ifeq freebsd
#############################################################################
-@@ -670,24 +665,25 @@
+@@ -670,24 +679,25 @@
TARGETS =
ifneq ($(BUILD_SERVER),0)
@@ -209,7 +244,7 @@
endif
ifneq ($(BUILD_GAME_QVM),0)
-@@ -749,11 +745,11 @@
+@@ -749,11 +759,11 @@
$(MAKE) targets B=$(BD) CFLAGS="$(CFLAGS) $(DEBUG_CFLAGS) $(DEPEND_CFLAGS)"
build_release: B=$(BR)
@@ -223,7 +258,15 @@
targets: $(TARGETS)
-@@ -964,13 +960,10 @@
+@@ -836,6 +846,7 @@
+ $(B)/client/snd_codec.o \
+ $(B)/client/snd_codec_wav.o \
+ $(B)/client/snd_codec_ogg.o \
++ $(B)/client/snd_codec_mp3.o \
+ \
+ $(B)/client/qal.o \
+ $(B)/client/snd_openal.o \
+@@ -964,13 +975,10 @@
ifeq ($(ARCH),i386)
Q3OBJ += $(B)/client/vm_x86.o
endif
@@ -239,7 +282,7 @@
Q3OBJ += $(B)/client/$(VM_PPC).o
endif
endif
-@@ -1017,10 +1010,10 @@
+@@ -1017,10 +1025,10 @@
$(B)/client/sdl_glimp_smp.o
endif
@@ -252,7 +295,15 @@
$(CC) -o $@ $(Q3OBJ) $(Q3POBJ_SMP) $(CLIENT_LDFLAGS) \
$(THREAD_LDFLAGS) $(LDFLAGS) $(LIBSDLMAIN)
-@@ -1317,18 +1310,15 @@
+@@ -1056,6 +1064,7 @@
+ $(B)/client/snd_codec.o : $(CDIR)/snd_codec.c; $(DO_CC)
+ $(B)/client/snd_codec_wav.o : $(CDIR)/snd_codec_wav.c; $(DO_CC)
+ $(B)/client/snd_codec_ogg.o : $(CDIR)/snd_codec_ogg.c; $(DO_CC)
++$(B)/client/snd_codec_mp3.o : $(CDIR)/snd_codec_mp3.c; $(DO_CC)
+
+ $(B)/client/qal.o : $(CDIR)/qal.c; $(DO_CC)
+ $(B)/client/snd_openal.o : $(CDIR)/snd_openal.c; $(DO_CC)
+@@ -1317,18 +1326,15 @@
ifeq ($(ARCH),i386)
Q3DOBJ += $(B)/ded/vm_x86.o
endif
@@ -274,7 +325,7 @@
$(CC) -o $@ $(Q3DOBJ) $(LDFLAGS)
$(B)/ded/sv_bot.o : $(SDIR)/sv_bot.c; $(DO_DED_CC)
-@@ -1445,7 +1435,7 @@
+@@ -1445,7 +1451,7 @@
Q3CGOBJ = $(Q3CGOBJ_) $(B)/baseq3/cgame/cg_syscalls.o
Q3CGVMOBJ = $(Q3CGOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -283,7 +334,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(Q3CGOBJ)
$(B)/baseq3/vm/cgame.qvm: $(Q3CGVMOBJ) $(CGDIR)/cg_syscalls.asm
-@@ -1486,7 +1476,7 @@
+@@ -1486,7 +1492,7 @@
MPCGOBJ = $(MPCGOBJ_) $(B)/missionpack/cgame/cg_syscalls.o
MPCGVMOBJ = $(MPCGOBJ_:%.o=%.asm) $(B)/missionpack/game/bg_lib.asm
@@ -292,7 +343,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(MPCGOBJ)
$(B)/missionpack/vm/cgame.qvm: $(MPCGVMOBJ) $(CGDIR)/cg_syscalls.asm
-@@ -1536,7 +1526,7 @@
+@@ -1536,7 +1542,7 @@
Q3GOBJ = $(Q3GOBJ_) $(B)/baseq3/game/g_syscalls.o
Q3GVMOBJ = $(Q3GOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -301,7 +352,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(Q3GOBJ)
$(B)/baseq3/vm/qagame.qvm: $(Q3GVMOBJ) $(GDIR)/g_syscalls.asm
-@@ -1584,7 +1574,7 @@
+@@ -1584,7 +1590,7 @@
MPGOBJ = $(MPGOBJ_) $(B)/missionpack/game/g_syscalls.o
MPGVMOBJ = $(MPGOBJ_:%.o=%.asm) $(B)/missionpack/game/bg_lib.asm
@@ -310,7 +361,7 @@
$(CC) $(SHLIBLDFLAGS) -o $@ $(MPGOBJ)
$(B)/missionpack/vm/qagame.qvm: $(MPGVMOBJ) $(GDIR)/g_syscalls.asm
-@@ -1644,7 +1634,7 @@
+@@ -1644,7 +1650,7 @@
Q3UIOBJ = $(Q3UIOBJ_) $(B)/missionpack/ui/ui_syscalls.o
Q3UIVMOBJ = $(Q3UIOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
@@ -319,7 +370,7 @@
$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3UIOBJ)
$(B)/baseq3/vm/ui.qvm: $(Q3UIVMOBJ) $(UIDIR)/ui_syscalls.asm
-@@ -1669,7 +1659,7 @@
+@@ -1669,7 +1675,7 @@
MPUIOBJ = $(MPUIOBJ_) $(B)/missionpack/ui/ui_syscalls.o
MPUIVMOBJ = $(MPUIOBJ_:%.o=%.asm) $(B)/baseq3/game/bg_lib.asm
diff --git a/games/iourbanterror/pkg-message b/games/iourbanterror/pkg-message
index a812c9d679ba..1f128f1231e9 100644
--- a/games/iourbanterror/pkg-message
+++ b/games/iourbanterror/pkg-message
@@ -12,4 +12,6 @@ they have additional variables which would be removed if other engines
overwrite them. But you can safely copy the original directory to the new one
for the first time.
+If you enabled CELLSHADING, check files/extra-patch-cellshading for variables.
+
==============================================================================