-#include <assert.h>\r
-#include "q3data.h"\r
-\r
-static int s_resample_width = 256;\r
-static int s_resample_height = 256;\r
-\r
-#define OUTPUT_TGAS 1\r
-\r
-#define UNCOMPRESSED 0\r
-#define BTC_COMPRESSION 1\r
-\r
-static int s_compression_method = BTC_COMPRESSION;\r
-\r
-static const char *CIN_EXTENSION = "cn2";\r
-static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );\r
-\r
-static byte *s_soundtrack;\r
-static char s_base[32];\r
-static char s_output_base[32];\r
-\r
-/*\r
-===============================================================================\r
-\r
-WAV loading\r
-\r
-===============================================================================\r
-*/\r
-\r
-typedef struct\r
-{\r
- int rate;\r
- int width;\r
- int channels;\r
- int loopstart;\r
- int samples;\r
- int dataofs; // chunk starts this many bytes from file start\r
-} wavinfo_t;\r
-\r
-\r
-byte *data_p;\r
-byte *iff_end;\r
-byte *last_chunk;\r
-byte *iff_data;\r
-int iff_chunk_len;\r
-\r
-\r
-static int s_samplecounts[0x10000];\r
-static wavinfo_t s_wavinfo;\r
-\r
-short GetLittleShort(void)\r
-{\r
- short val = 0;\r
- val = *data_p;\r
- val = val + (*(data_p+1)<<8);\r
- data_p += 2;\r
- return val;\r
-}\r
-\r
-int GetLittleLong(void)\r
-{\r
- int val = 0;\r
- val = *data_p;\r
- val = val + (*(data_p+1)<<8);\r
- val = val + (*(data_p+2)<<16);\r
- val = val + (*(data_p+3)<<24);\r
- data_p += 4;\r
- return val;\r
-}\r
-\r
-void FindNextChunk(char *name)\r
-{\r
- while (1)\r
- {\r
- data_p=last_chunk;\r
-\r
- if (data_p >= iff_end)\r
- { // didn't find the chunk\r
- data_p = NULL;\r
- return;\r
- }\r
- \r
- data_p += 4;\r
- iff_chunk_len = GetLittleLong();\r
- if (iff_chunk_len < 0)\r
- {\r
- data_p = NULL;\r
- return;\r
- }\r
-// if (iff_chunk_len > 1024*1024)\r
-// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);\r
- data_p -= 8;\r
- last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );\r
- if (!strncmp(data_p, name, 4))\r
- return;\r
- }\r
-}\r
-\r
-void FindChunk(char *name)\r
-{\r
- last_chunk = iff_data;\r
- FindNextChunk (name);\r
-}\r
-\r
-\r
-void DumpChunks(void)\r
-{\r
- char str[5];\r
- \r
- str[4] = 0;\r
- data_p=iff_data;\r
- do\r
- {\r
- memcpy (str, data_p, 4);\r
- data_p += 4;\r
- iff_chunk_len = GetLittleLong();\r
- printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);\r
- data_p += (iff_chunk_len + 1) & ~1;\r
- } while (data_p < iff_end);\r
-}\r
-\r
-/*\r
-============\r
-GetWavinfo\r
-============\r
-*/\r
-wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)\r
-{\r
- wavinfo_t info;\r
- int i;\r
- int format;\r
- int samples;\r
-\r
- memset (&info, 0, sizeof(info));\r
-\r
- if (!wav)\r
- return info;\r
- \r
- iff_data = wav;\r
- iff_end = wav + wavlength;\r
-\r
-// find "RIFF" chunk\r
- FindChunk("RIFF");\r
- if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))\r
- {\r
- printf("Missing RIFF/WAVE chunks\n");\r
- return info;\r
- }\r
-\r
-// get "fmt " chunk\r
- iff_data = data_p + 12;\r
-// DumpChunks ();\r
-\r
- FindChunk("fmt ");\r
- if (!data_p)\r
- {\r
- printf("Missing fmt chunk\n");\r
- return info;\r
- }\r
- data_p += 8;\r
- format = GetLittleShort();\r
- if (format != 1)\r
- {\r
- printf("Microsoft PCM format only\n");\r
- return info;\r
- }\r
-\r
- info.channels = GetLittleShort();\r
- info.rate = GetLittleLong();\r
- data_p += 4+2;\r
- info.width = GetLittleShort() / 8;\r
-\r
-// get cue chunk\r
- FindChunk("cue ");\r
- if (data_p)\r
- {\r
- data_p += 32;\r
- info.loopstart = GetLittleLong();\r
-// Com_Printf("loopstart=%d\n", sfx->loopstart);\r
-\r
- // if the next chunk is a LIST chunk, look for a cue length marker\r
- FindNextChunk ("LIST");\r
- if (data_p)\r
- {\r
- if (!strncmp (data_p + 28, "mark", 4))\r
- { // this is not a proper parse, but it works with cooledit...\r
- data_p += 24;\r
- i = GetLittleLong (); // samples in loop\r
- info.samples = info.loopstart + i;\r
- }\r
- }\r
- }\r
- else\r
- info.loopstart = -1;\r
-\r
-// find data chunk\r
- FindChunk("data");\r
- if (!data_p)\r
- {\r
- printf("Missing data chunk\n");\r
- return info;\r
- }\r
-\r
- data_p += 4;\r
- samples = GetLittleLong ();\r
-\r
- if (info.samples)\r
- {\r
- if (samples < info.samples)\r
- Error ("Sound %s has a bad loop length", name);\r
- }\r
- else\r
- info.samples = samples;\r
-\r
- info.dataofs = data_p - wav;\r
-\r
- return info;\r
-}\r
-\r
-//=====================================================================\r
-\r
-/*\r
-==============\r
-LoadSoundtrack\r
-==============\r
-*/\r
-void LoadSoundtrack (void)\r
-{\r
- char name[1024];\r
- FILE *f;\r
- int len;\r
- int i, val, j;\r
-\r
- s_soundtrack = NULL;\r
- sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base);\r
- printf ("WAV: %s\n", name);\r
- f = fopen (name, "rb");\r
- if (!f)\r
- {\r
- printf ("no soundtrack for %s\n", s_base);\r
- return;\r
- }\r
- len = Q_filelength(f);\r
- s_soundtrack = malloc(len);\r
- fread (s_soundtrack, 1, len, f);\r
- fclose (f);\r
-\r
- s_wavinfo = GetWavinfo (name, s_soundtrack, len);\r
-\r
- // count samples for compression\r
- memset (s_samplecounts, 0, sizeof(s_samplecounts));\r
-\r
- j = s_wavinfo.samples/2;\r
- for (i=0 ; i<j ; i++)\r
- {\r
- val = ((unsigned short *)( s_soundtrack + s_wavinfo.dataofs))[i];\r
- s_samplecounts[val]++;\r
- }\r
- val = 0;\r
- for (i=0 ; i<0x10000 ; i++)\r
- if (s_samplecounts[i])\r
- val++;\r
-\r
- printf ("%i unique sample values\n", val);\r
-}\r
-\r
-/*\r
-==================\r
-WriteSound\r
-==================\r
-*/\r
-void WriteSound (FILE *output, int frame)\r
-{\r
- int start, end;\r
- int count;\r
- int empty = 0;\r
- int i;\r
- int sample;\r
- int width;\r
-\r
- width = s_wavinfo.width * s_wavinfo.channels;\r
-\r
- start = frame*s_wavinfo.rate/14;\r
- end = (frame+1)*s_wavinfo.rate/14;\r
- count = end - start;\r
-\r
- for (i=0 ; i<count ; i++)\r
- {\r
- sample = start+i;\r
- if (sample > s_wavinfo.samples || !s_soundtrack)\r
- fwrite (&empty, 1, width, output);\r
- else\r
- fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output);\r
- }\r
-}\r
-\r
-//==========================================================================\r
-\r
-static float s_resampleXRatio;\r
-static float s_resampleYRatio;\r
-\r
-static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 )\r
-{\r
- float w;\r
- float rSum = 0, gSum = 0, bSum = 0;\r
- float x = s0;\r
- float sumWeight = 0;\r
-\r
- for ( x = s0; x < s1; x++, src += 4 )\r
- {\r
- if ( x == s0 )\r
- {\r
- w = ( int ) ( s0 + 1 ) - x;\r
- }\r
- else if ( x + 1 >= s1 )\r
- {\r
- w = s1 - ( int ) x;\r
- }\r
- else\r
- {\r
- w = 1.0f;\r
- }\r
-\r
- rSum += src[0] * w;\r
- gSum += src[1] * w;\r
- bSum += src[2] * w;\r
- sumWeight += w;\r
- }\r
-\r
- rSum /= sumWeight;\r
- gSum /= sumWeight;\r
- bSum /= sumWeight;\r
-\r
- dst[0] = ( unsigned char ) ( rSum + 0.5 );\r
- dst[1] = ( unsigned char ) ( gSum + 0.5 );\r
- dst[2] = ( unsigned char ) ( bSum + 0.5 );\r
-}\r
-\r
-static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process\r
- unsigned char *src, // source pixels\r
- int srcStep, // stride of the source pixels\r
- float s0, float s1 )\r
-{\r
- float w;\r
- float rSum = 0, gSum = 0, bSum = 0;\r
- float y = s0;\r
- float sumWeight = 0;\r
-\r
- for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )\r
- {\r
- if ( y == s0 )\r
- {\r
- w = ( int ) ( s0 + 1 ) - y;\r
- }\r
- else if ( y + 1 >= s1 )\r
- {\r
- w = s1 - ( int ) y;\r
- }\r
- else\r
- {\r
- w = 1.0f;\r
- }\r
-\r
- rSum += src[0] * w;\r
- gSum += src[1] * w;\r
- bSum += src[2] * w;\r
- sumWeight += w;\r
- }\r
-\r
- rSum /= sumWeight;\r
- gSum /= sumWeight;\r
- bSum /= sumWeight;\r
-\r
- dst[0] = ( unsigned char ) ( rSum + 0.5 );\r
- dst[1] = ( unsigned char ) ( gSum + 0.5 );\r
- dst[2] = ( unsigned char ) ( bSum + 0.5 );\r
- dst[3] = 0xff;\r
-\r
-}\r
-\r
-static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth )\r
-{\r
- int i;\r
- unsigned char *indata = ( unsigned char * ) in->data;\r
-\r
- indata += 4 * dstRow * in->width;\r
-\r
- for ( i = 0; i < rowWidth; i++ )\r
- {\r
- float c0 = i * s_resampleXRatio;\r
- float c1 = ( i + 1 ) * s_resampleXRatio;\r
-\r
- BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 );\r
- }\r
-}\r
-\r
-static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels )\r
-{\r
- float c0, c1;\r
- int i;\r
-\r
- for ( i = 0; i < dstColHeight; i++ )\r
- {\r
- c0 = i * s_resampleYRatio;\r
- c1 = ( i + 1 ) * s_resampleYRatio;\r
-\r
- BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 );\r
- }\r
-}\r
-\r
-#define DROP_SAMPLE 0\r
-#define BOX_FILTER 1\r
-\r
-static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight )\r
-{\r
- int row, column;\r
- unsigned char *indata = ( unsigned char * ) in->data;\r
-\r
- s_resampleXRatio = in->width / ( float ) outWidth;\r
- s_resampleYRatio = in->height / ( float ) outHeight;\r
-\r
- if ( method == DROP_SAMPLE )\r
- {\r
- for ( row = 0; row < outHeight; row++ )\r
- {\r
- int r = ( int ) ( row * s_resampleYRatio );\r
-\r
- for ( column = 0; column < outWidth; column++ )\r
- {\r
- int c = ( int ) ( column * s_resampleXRatio );\r
-\r
- out[(row*outWidth+column)*4+0] = indata[(r*in->width+c)*4+0];\r
- out[(row*outWidth+column)*4+1] = indata[(r*in->width+c)*4+1];\r
- out[(row*outWidth+column)*4+2] = indata[(r*in->width+c)*4+2];\r
- out[(row*outWidth+column)*4+3] = 0xff;\r
- }\r
- }\r
- }\r
- else if ( method == BOX_FILTER )\r
- {\r
- unsigned char intermediate[1024*1024*4];\r
-\r
- assert( in->height <= 1024 );\r
- assert( in->width <= 1024 );\r
-\r
- //\r
- // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image\r
- //\r
- for ( row = 0; row < in->height; row++ )\r
- {\r
- BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth );\r
- }\r
-\r
- //\r
- // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image\r
- //\r
- for ( column = 0; column < outWidth; column++ )\r
- {\r
- BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width );\r
- }\r
- }\r
-}\r
-\r
-static float BTCDistanceSquared( float a[3], float b[3] )\r
-{\r
- return ( b[0] - a[0] ) * ( b[0] - a[0] ) + \r
- ( b[1] - a[1] ) * ( b[1] - a[1] ) +\r
- ( b[2] - a[2] ) * ( b[2] - a[2] );\r
-}\r
-\r
-static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] )\r
-{\r
- float longestDistance = -1;\r
-\r
- int bX, bY;\r
-\r
- //\r
- // find the two points farthest from each other\r
- //\r
- for ( bY = 0; bY < 4; bY++ )\r
- {\r
- for ( bX = 0; bX < 4; bX++ )\r
- {\r
- int cX, cY;\r
- float d;\r
-\r
- //\r
- // check the rest of the current row\r
- //\r
- for ( cX = bX + 1; cX < 4; cX++ )\r
- {\r
- if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance )\r
- {\r
- longestDistance = d;\r
- endPoints[0][0] = bX;\r
- endPoints[0][1] = bY;\r
- endPoints[1][0] = cX;\r
- endPoints[1][1] = bY;\r
- }\r
- }\r
-\r
- //\r
- // check remaining rows and columns\r
- //\r
- for ( cY = bY+1; cY < 4; cY++ )\r
- {\r
- for ( cX = 0; cX < 4; cX++ )\r
- {\r
- if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance )\r
- {\r
- longestDistance = d;\r
- endPoints[0][0] = bX;\r
- endPoints[0][1] = bY;\r
- endPoints[1][0] = cX;\r
- endPoints[1][1] = cY;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-}\r
-\r
-static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError )\r
-{\r
- int i;\r
- int blockY, blockX;\r
- float dR, dG, dB;\r
- float R, G, B;\r
- float error = 0;\r
- float colorLine[4][3];\r
-\r
- //\r
- // build the color line\r
- //\r
- dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -\r
- inBlock[endPoints[0][1]][endPoints[0][0]][0];\r
- dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -\r
- inBlock[endPoints[0][1]][endPoints[0][0]][1];\r
- dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -\r
- inBlock[endPoints[0][1]][endPoints[0][0]][2];\r
-\r
- dR *= 0.33f;\r
- dG *= 0.33f;\r
- dB *= 0.33f;\r
-\r
- R = inBlock[endPoints[0][1]][endPoints[0][0]][0];\r
- G = inBlock[endPoints[0][1]][endPoints[0][0]][1];\r
- B = inBlock[endPoints[0][1]][endPoints[0][0]][2];\r
-\r
- for ( i = 0; i < 4; i++ )\r
- {\r
- colorLine[i][0] = R;\r
- colorLine[i][1] = G;\r
- colorLine[i][2] = B;\r
-\r
- R += dR;\r
- G += dG;\r
- B += dB;\r
- }\r
-\r
- //\r
- // quantize each pixel into the appropriate range\r
- //\r
- for ( blockY = 0; blockY < 4; blockY++ )\r
- {\r
- for ( blockX = 0; blockX < 4; blockX++ )\r
- {\r
- float distance = 10000000000;\r
- int shortest = -1;\r
-\r
- for ( i = 0; i < 4; i++ )\r
- {\r
- float d;\r
-\r
- if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance )\r
- {\r
- distance = d;\r
- shortest = i;\r
- }\r
- }\r
-\r
- error += distance;\r
-\r
- //\r
- // if bestError is not -1 then that means this is a speculative quantization\r
- //\r
- if ( bestError != -1 )\r
- {\r
- if ( error > bestError )\r
- return error;\r
- }\r
-\r
- btcQuantizedBlock[blockY][blockX] = shortest;\r
- }\r
- }\r
-\r
- return error;\r
-}\r
-\r
-/*\r
-** float BTCCompressBlock\r
-*/\r
-static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] )\r
-{\r
- int i;\r
- int btcQuantizedBlock[4][4]; // values should be [0..3]\r
- unsigned long encodedEndPoints, encodedBitmap;\r
- unsigned int endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end\r
- int blockY, blockX;\r
- float error = 0;\r
- float bestError = 10000000000;\r
- unsigned int bestEndPoints[2][2];\r
-\r
-#if 0\r
- //\r
- // find the "ideal" end points for the color vector \r
- //\r
- BTCFindEndpoints( inBlock, endPoints );\r
- error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );\r
- memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );\r
-#else\r
- for ( blockY = 0; blockY < 4; blockY++ )\r
- {\r
- for ( blockX = 0; blockX < 4; blockX++ )\r
- {\r
- int x2, y2;\r
-\r
- for ( y2 = 0; y2 < 4; y2++ )\r
- {\r
- for ( x2 = 0; x2 < 4; x2++ )\r
- {\r
- if ( ( x2 == blockX ) && ( y2 == blockY ) )\r
- continue;\r
-\r
- endPoints[0][0] = blockX;\r
- endPoints[0][1] = blockY;\r
- endPoints[1][0] = x2;\r
- endPoints[1][1] = y2;\r
-\r
- error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );\r
-\r
- if ( error < bestError )\r
- {\r
- bestError = error;\r
- memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );\r
-#endif\r
-\r
- //\r
- // encode the results\r
- //\r
- encodedBitmap = 0;\r
- for ( blockY = 0; blockY < 4; blockY++ )\r
- {\r
- for ( blockX = 0; blockX < 4; blockX++ )\r
- {\r
- int shift = ( blockX + blockY * 4 ) * 2;\r
- encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;\r
- }\r
- }\r
-\r
- //\r
- // encode endpoints\r
- //\r
- encodedEndPoints = 0;\r
- for ( i = 0; i < 2; i++ )\r
- {\r
- int iR, iG, iB;\r
-\r
- iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );\r
- if ( iR > 255 ) \r
- iR = 255;\r
- else if ( iR < 0 ) \r
- iR = 0;\r
- iR >>= 3;\r
-\r
- iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );\r
- if ( iG > 255 )\r
- iG = 255;\r
- else if ( iG < 0 )\r
- iG = 0;\r
- iG >>= 2;\r
-\r
- iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );\r
- if ( iB > 255 )\r
- iB = 255;\r
- else if ( iB < 0 )\r
- iB = 0;\r
- iB >>= 3;\r
-\r
-\r
- encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );\r
- }\r
-\r
- //\r
- // store \r
- //\r
- out[0] = encodedBitmap;\r
- out[1] = encodedEndPoints;\r
-\r
- return error;\r
-}\r
-\r
-/*\r
-** void BTCDecompressFrame\r
-*/\r
-static void BTCDecompressFrame( unsigned long *src, unsigned char *dst )\r
-{\r
- int x, y;\r
- int iR, iG, iB;\r
- int dstX, dstY;\r
- float colorStart[3], colorEnd[3];\r
- unsigned char colorRampABGR[4][4];\r
- unsigned encoded;\r
-\r
- memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );\r
-\r
- for ( y = 0; y < s_resample_height / 4; y++ )\r
- {\r
- for ( x = 0; x < s_resample_width / 4; x++ )\r
- {\r
- unsigned colorStartPacked = src[(y*s_resample_width/4 + x)*2 + 1] & 0xffff;\r
- unsigned colorEndPacked = src[(y*s_resample_width/4 + x)*2 + 1] >> 16;\r
-\r
- //\r
- // grab the end points\r
- // 0 = color start\r
- // 1 = color end\r
- //\r
- iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );\r
- iR = ( iR << 3 ) | ( iR >> 2 );\r
- iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );\r
- iG = ( iG << 2 ) | ( iG >> 4 );\r
- iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) );\r
- iB = ( iB << 3 ) | ( iB >> 2 );\r
-\r
- colorStart[0] = iR;\r
- colorStart[1] = iG;\r
- colorStart[2] = iB;\r
- colorRampABGR[0][0] = iR;\r
- colorRampABGR[0][1] = iG;\r
- colorRampABGR[0][2] = iB;\r
-\r
- iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );\r
- iR = ( iR << 3 ) | ( iR >> 2 );\r
- iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );\r
- iG = ( iG << 2 ) | ( iG >> 4 );\r
- iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) );\r
- iB = ( iB << 3 ) | ( iB >> 2 );\r
-\r
- colorEnd[0] = iR;\r
- colorEnd[1] = iG;\r
- colorEnd[2] = iB;\r
- colorRampABGR[3][0] = iR;\r
- colorRampABGR[3][1] = iG;\r
- colorRampABGR[3][2] = iB;\r
- \r
- //\r
- // compute this block's color ramp\r
- // FIXME: This needs to be reversed on big-endian machines\r
- //\r
- \r
- colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;\r
- colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;\r
- colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;\r
-\r
- colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;\r
- colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;\r
- colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;\r
-\r
- //\r
- // decode the color data\r
- // information is encoded in 2-bit pixels, with low order bits corresponding\r
- // to upper left pixels. These 2-bit values are indexed into the block's\r
- // computer color ramp.\r
- //\r
- encoded = src[(y*s_resample_width/4 + x)*2 + 0];\r
-\r
- for ( dstY = 0; dstY < 4; dstY++ )\r
- {\r
- for ( dstX = 0; dstX < 4; dstX++ )\r
- {\r
- memcpy( &dst[(y*4+dstY)*s_resample_width*4+x*4*4+dstX*4], colorRampABGR[encoded&3], sizeof( colorRampABGR[0] ) );\r
- encoded >>= 2;\r
- }\r
- }\r
- }\r
- }\r
-}\r
-\r
-/*\r
-** BTCCompressFrame\r
-**\r
-** Perform a BTC compression using a 2-bit encoding at each pixel. This\r
-** compression method is performed by decomposing the incoming image into\r
-** a sequence of 4x4 blocks. At each block two color values are computed\r
-** that define the endpoints of a vector in color space that represent\r
-** the two colors "farthest apart".\r
-*/\r
-static float BTCCompressFrame( unsigned char *src, unsigned long *dst )\r
-{\r
- int x, y;\r
- int bX, bY;\r
- float btcBlock[4][4][3];\r
-\r
- float error = 0;\r
- \r
- for ( y = 0; y < s_resample_height / 4; y++ )\r
- {\r
- for ( x = 0; x < s_resample_width / 4; x++ )\r
- {\r
- //\r
- // fill in the BTC block with raw values\r
- //\r
- for ( bY = 0; bY < 4; bY++ )\r
- {\r
- for ( bX = 0; bX < 4; bX++ )\r
- {\r
- btcBlock[bY][bX][0] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 0];\r
- btcBlock[bY][bX][1] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 1];\r
- btcBlock[bY][bX][2] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 2];\r
- }\r
- }\r
-\r
- error += BTCCompressBlock( btcBlock, &dst[(y*s_resample_width/4+x)*2] );\r
- }\r
- }\r
-\r
- return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );\r
-}\r
-\r
-/*\r
-===================\r
-LoadFrame\r
-===================\r
-*/\r
-cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)\r
-{\r
- int ten3, ten2, ten1, ten0;\r
- cblock_t in;\r
- int width, height;\r
- char name[1024];\r
- FILE *f;\r
-\r
- in.data = NULL;\r
- in.count = -1;\r
-\r
- ten3 = frame/1000;\r
- ten2 = (frame-ten3*1000)/100;\r
- ten1 = (frame-ten3*1000-ten2*100)/10;\r
- ten0 = frame%10;\r
-\r
- if (digits == 4)\r
- sprintf (name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0);\r
- else\r
- sprintf (name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0);\r
-\r
- f = fopen(name, "rb");\r
- if (!f)\r
- {\r
- in.data = NULL;\r
- return in;\r
- }\r
- fclose (f);\r
-\r
- printf ("%s", name);\r
- LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );\r
- if ( palette )\r
- *palette = 0;\r
-// Load256Image (name, &in.data, palette, &width, &height);\r
- in.count = width*height;\r
- in.width = width;\r
- in.height = height;\r
-// FIXME: map 0 and 255!\r
-\r
-#if 0\r
- // rle compress\r
- rle = RLE(in);\r
- free (in.data);\r
-\r
- return rle;\r
-#endif\r
-\r
- return in;\r
-}\r
-\r
-/*\r
-===============\r
-Cmd_Video\r
-\r
-video <directory> <framedigits>\r
-===============\r
-*/\r
-void Cmd_Video (void)\r
-{\r
- float sumError = 0, error = 0, maxError = 0;\r
- char savename[1024];\r
- char name[1024];\r
- FILE *output;\r
- int startframe, frame;\r
- int width, height;\r
- int i;\r
- int digits;\r
- int minutes;\r
- float fseconds;\r
- int remSeconds;\r
- cblock_t in;\r
- unsigned char *resampled;\r
- unsigned long *compressed;\r
- clock_t start, stop;\r
-\r
- GetToken (qfalse);\r
- strcpy (s_base, token);\r
- if (g_release)\r
- {\r
-// sprintf (savename, "video/%s.cin", token);\r
-// ReleaseFile (savename);\r
- return;\r
- }\r
-\r
- GetToken( qfalse );\r
- strcpy( s_output_base, token );\r
-\r
- GetToken (qfalse);\r
- digits = atoi(token);\r
-\r
- GetToken( qfalse );\r
-\r
- if ( !strcmp( token, "btc" ) )\r
- {\r
- s_compression_method = BTC_COMPRESSION;\r
- printf( "Compression: BTC\n" );\r
- }\r
- else if ( !strcmp( token, "uc" ) )\r
- {\r
- s_compression_method = UNCOMPRESSED;\r
- printf( "Compression: none\n" );\r
- }\r
- else\r
- {\r
- Error( "Uknown compression method '%s'\n", token );\r
- }\r
-\r
- GetToken( qfalse );\r
- s_resample_width = atoi( token );\r
-\r
- GetToken( qfalse );\r
- s_resample_height = atoi( token );\r
-\r
- resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );\r
- compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );\r
-\r
- printf( "Resample width: %d\n", s_resample_width );\r
- printf( "Resample height: %d\n", s_resample_height );\r
-\r
- // optionally skip frames\r
- if (TokenAvailable ())\r
- {\r
- GetToken (qfalse);\r
- startframe = atoi(token);\r
- }\r
- else\r
- startframe=0;\r
-\r
- sprintf (savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );\r
-\r
- // load the entire sound wav file if present\r
- LoadSoundtrack ();\r
-\r
- if (digits == 4)\r
- sprintf (name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base);\r
- else\r
- sprintf (name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base);\r
-\r
- printf ("%s\n", name);\r
- LoadTGA( name, NULL, &width, &height);\r
-\r
- output = fopen (savename, "wb");\r
- if (!output)\r
- Error ("Can't open %s", savename);\r
-\r
- // write header info\r
- i = LittleLong( CIN_SIGNATURE );\r
- fwrite (&i, 4, 1, output );\r
- i = LittleLong (s_resample_width);\r
- fwrite (&i, 4, 1, output);\r
- i = LittleLong (s_resample_height);\r
- fwrite (&i, 4, 1, output);\r
- i = LittleLong (s_wavinfo.rate);\r
- fwrite (&i, 4, 1, output);\r
- i = LittleLong (s_wavinfo.width);\r
- fwrite (&i, 4, 1, output);\r
- i = LittleLong (s_wavinfo.channels);\r
- fwrite (&i, 4, 1, output);\r
- i = LittleLong ( s_compression_method );\r
- fwrite (&i, 4, 1, output );\r
-\r
- start = clock();\r
-\r
- // perform compression on a per frame basis\r
- for ( frame=startframe ; ; frame++)\r
- {\r
- printf ("%02d: ", frame);\r
- in = LoadFrame (s_base, frame, digits, 0 );\r
- if (!in.data)\r
- break;\r
-\r
- ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );\r
-\r
- if ( s_compression_method == UNCOMPRESSED )\r
- {\r
- printf( "\n" );\r
- fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );\r
-\r
-#if OUTPUT_TGAS\r
- {\r
- int x, y;\r
- char buffer[1000];\r
-\r
- for ( y = 0; y < s_resample_height/2; y++ )\r
- {\r
- for ( x = 0; x < s_resample_width; x++ )\r
- {\r
- unsigned char tmp[4];\r
-\r
- tmp[0] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];\r
- tmp[1] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];\r
- tmp[2] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];\r
- tmp[3] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];\r
-\r
- resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = resampled[y*s_resample_width*4 + x*4 + 0];\r
- resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = resampled[y*s_resample_width*4 + x*4 + 1];\r
- resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = resampled[y*s_resample_width*4 + x*4 + 2];\r
- resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = resampled[y*s_resample_width*4 + x*4 + 3];\r
-\r
- resampled[y*s_resample_width*4 + x*4 + 0] = tmp[0];\r
- resampled[y*s_resample_width*4 + x*4 + 1] = tmp[1];\r
- resampled[y*s_resample_width*4 + x*4 + 2] = tmp[2];\r
- resampled[y*s_resample_width*4 + x*4 + 3] = tmp[3];\r
- }\r
- }\r
-\r
- sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );\r
- WriteTGA( buffer, resampled, s_resample_width, s_resample_height );\r
- }\r
-#endif\r
- }\r
- else if ( s_compression_method == BTC_COMPRESSION )\r
- {\r
- error = BTCCompressFrame( resampled, compressed );\r
-\r
- sumError += error;\r
-\r
- if ( error > maxError ) \r
- maxError = error;\r
-\r
- printf( " (error = %f)\n", error );\r
- fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );\r
-\r
-#if OUTPUT_TGAS\r
- {\r
- int x, y;\r
- unsigned char *uncompressed;\r
- char buffer[1000];\r
-\r
- uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );\r
- BTCDecompressFrame( compressed, uncompressed );\r
-\r
- for ( y = 0; y < s_resample_height/2; y++ )\r
- {\r
- for ( x = 0; x < s_resample_width; x++ )\r
- {\r
- unsigned char tmp[4];\r
-\r
- tmp[0] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];\r
- tmp[1] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];\r
- tmp[2] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];\r
- tmp[3] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];\r
-\r
- uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = uncompressed[y*s_resample_width*4 + x*4 + 0];\r
- uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = uncompressed[y*s_resample_width*4 + x*4 + 1];\r
- uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = uncompressed[y*s_resample_width*4 + x*4 + 2];\r
- uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = uncompressed[y*s_resample_width*4 + x*4 + 3];\r
-\r
- uncompressed[y*s_resample_width*4 + x*4 + 0] = tmp[0];\r
- uncompressed[y*s_resample_width*4 + x*4 + 1] = tmp[1];\r
- uncompressed[y*s_resample_width*4 + x*4 + 2] = tmp[2];\r
- uncompressed[y*s_resample_width*4 + x*4 + 3] = tmp[3];\r
- }\r
- }\r
-\r
-\r
- sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );\r
- WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );\r
-\r
- free( uncompressed );\r
- }\r
-#endif\r
- }\r
-\r
- WriteSound( output, frame );\r
-\r
- free (in.data);\r
- }\r
- stop = clock();\r
-\r
- printf ("\n");\r
-\r
- printf ("Total size: %i\n", ftell( output ) );\r
- printf ("Average error: %f\n", sumError / ( frame - startframe ) );\r
- printf ("Max error: %f\n", maxError );\r
-\r
- fseconds = ( stop - start ) / 1000.0f;\r
- minutes = fseconds / 60;\r
- remSeconds = fseconds - minutes * 60;\r
-\r
- printf ("Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );\r
- printf ("Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );\r
-\r
- fclose (output);\r
-\r
- if ( s_soundtrack )\r
- {\r
- free( s_soundtrack );\r
- s_soundtrack = 0;\r
- }\r
-}\r
+/*
+ Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+ For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+ This file is part of GtkRadiant.
+
+ GtkRadiant 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.
+
+ GtkRadiant 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 GtkRadiant; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <assert.h>
+#include "q3data.h"
+
+static int s_resample_width = 256;
+static int s_resample_height = 256;
+
+#define OUTPUT_TGAS 1
+
+#define UNCOMPRESSED 0
+#define BTC_COMPRESSION 1
+
+static int s_compression_method = BTC_COMPRESSION;
+
+static const char *CIN_EXTENSION = "cn2";
+static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
+
+static byte *s_soundtrack;
+static char s_base[32];
+static char s_output_base[32];
+
+/*
+ ===============================================================================
+
+ WAV loading
+
+ ===============================================================================
+ */
+
+typedef struct
+{
+ int rate;
+ int width;
+ int channels;
+ int loopstart;
+ int samples;
+ int dataofs; // chunk starts this many bytes from file start
+} wavinfo_t;
+
+
+byte *data_p;
+byte *iff_end;
+byte *last_chunk;
+byte *iff_data;
+int iff_chunk_len;
+
+
+static int s_samplecounts[0x10000];
+static wavinfo_t s_wavinfo;
+
+short GetLittleShort( void ){
+ short val = 0;
+ val = *data_p;
+ val = val + ( *( data_p + 1 ) << 8 );
+ data_p += 2;
+ return val;
+}
+
+int GetLittleLong( void ){
+ int val = 0;
+ val = *data_p;
+ val = val + ( *( data_p + 1 ) << 8 );
+ val = val + ( *( data_p + 2 ) << 16 );
+ val = val + ( *( data_p + 3 ) << 24 );
+ data_p += 4;
+ return val;
+}
+
+void FindNextChunk( char *name ){
+ while ( 1 )
+ {
+ data_p = last_chunk;
+
+ if ( data_p >= iff_end ) { // didn't find the chunk
+ data_p = NULL;
+ return;
+ }
+
+ data_p += 4;
+ iff_chunk_len = GetLittleLong();
+ if ( iff_chunk_len < 0 ) {
+ data_p = NULL;
+ return;
+ }
+// if (iff_chunk_len > 1024*1024)
+// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+ data_p -= 8;
+ last_chunk = data_p + 8 + ( ( iff_chunk_len + 1 ) & ~1 );
+ if ( !strncmp((const char *) data_p, name, 4 ) ) {
+ return;
+ }
+ }
+}
+
+void FindChunk( char *name ){
+ last_chunk = iff_data;
+ FindNextChunk( name );
+}
+
+
+void DumpChunks( void ){
+ char str[5];
+
+ str[4] = 0;
+ data_p = iff_data;
+ do
+ {
+ memcpy( str, data_p, 4 );
+ data_p += 4;
+ iff_chunk_len = GetLittleLong();
+ printf( "%p : %s (%d)\n", (void *) ( data_p - 4 ), str, iff_chunk_len );
+ data_p += ( iff_chunk_len + 1 ) & ~1;
+ } while ( data_p < iff_end );
+}
+
+/*
+ ============
+ GetWavinfo
+ ============
+ */
+wavinfo_t GetWavinfo( char *name, byte *wav, int wavlength ){
+ wavinfo_t info;
+ int i;
+ int format;
+ int samples;
+
+ memset( &info, 0, sizeof( info ) );
+
+ if ( !wav ) {
+ return info;
+ }
+
+ iff_data = wav;
+ iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+ FindChunk( "RIFF" );
+ if ( !( data_p && !strncmp((const char *) (data_p + 8), "WAVE", 4 ) ) ) {
+ printf( "Missing RIFF/WAVE chunks\n" );
+ return info;
+ }
+
+// get "fmt " chunk
+ iff_data = data_p + 12;
+// DumpChunks ();
+
+ FindChunk( "fmt " );
+ if ( !data_p ) {
+ printf( "Missing fmt chunk\n" );
+ return info;
+ }
+ data_p += 8;
+ format = GetLittleShort();
+ if ( format != 1 ) {
+ printf( "Microsoft PCM format only\n" );
+ return info;
+ }
+
+ info.channels = GetLittleShort();
+ info.rate = GetLittleLong();
+ data_p += 4 + 2;
+ info.width = GetLittleShort() / 8;
+
+// get cue chunk
+ FindChunk( "cue " );
+ if ( data_p ) {
+ data_p += 32;
+ info.loopstart = GetLittleLong();
+// Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+ // if the next chunk is a LIST chunk, look for a cue length marker
+ FindNextChunk( "LIST" );
+ if ( data_p ) {
+ if ( !strncmp((const char *) (data_p + 28), "mark", 4 ) ) { // this is not a proper parse, but it works with cooledit...
+ data_p += 24;
+ i = GetLittleLong(); // samples in loop
+ info.samples = info.loopstart + i;
+ }
+ }
+ }
+ else{
+ info.loopstart = -1;
+ }
+
+// find data chunk
+ FindChunk( "data" );
+ if ( !data_p ) {
+ printf( "Missing data chunk\n" );
+ return info;
+ }
+
+ data_p += 4;
+ samples = GetLittleLong();
+
+ if ( info.samples ) {
+ if ( samples < info.samples ) {
+ Error( "Sound %s has a bad loop length", name );
+ }
+ }
+ else{
+ info.samples = samples;
+ }
+
+ info.dataofs = data_p - wav;
+
+ return info;
+}
+
+//=====================================================================
+
+/*
+ ==============
+ LoadSoundtrack
+ ==============
+ */
+void LoadSoundtrack( void ){
+ char name[1024];
+ FILE *f;
+ int len;
+ int i, val, j;
+
+ s_soundtrack = NULL;
+ sprintf( name, "%svideo/%s/%s.wav", gamedir, s_base, s_base );
+ printf( "WAV: %s\n", name );
+ f = fopen( name, "rb" );
+ if ( !f ) {
+ printf( "no soundtrack for %s\n", s_base );
+ return;
+ }
+ len = Q_filelength( f );
+ s_soundtrack = malloc( len );
+ fread( s_soundtrack, 1, len, f );
+ fclose( f );
+
+ s_wavinfo = GetWavinfo( name, s_soundtrack, len );
+
+ // count samples for compression
+ memset( s_samplecounts, 0, sizeof( s_samplecounts ) );
+
+ j = s_wavinfo.samples / 2;
+ for ( i = 0 ; i < j ; i++ )
+ {
+ val = ( (unsigned short *)( s_soundtrack + s_wavinfo.dataofs ) )[i];
+ s_samplecounts[val]++;
+ }
+ val = 0;
+ for ( i = 0 ; i < 0x10000 ; i++ )
+ if ( s_samplecounts[i] ) {
+ val++;
+ }
+
+ printf( "%i unique sample values\n", val );
+}
+
+/*
+ ==================
+ WriteSound
+ ==================
+ */
+void WriteSound( FILE *output, int frame ){
+ int start, end;
+ int count;
+ int empty = 0;
+ int i;
+ int sample;
+ int width;
+
+ width = s_wavinfo.width * s_wavinfo.channels;
+
+ start = frame * s_wavinfo.rate / 14;
+ end = ( frame + 1 ) * s_wavinfo.rate / 14;
+ count = end - start;
+
+ for ( i = 0 ; i < count ; i++ )
+ {
+ sample = start + i;
+ if ( sample > s_wavinfo.samples || !s_soundtrack ) {
+ fwrite( &empty, 1, width, output );
+ }
+ else{
+ fwrite( s_soundtrack + s_wavinfo.dataofs + sample * width, 1, width,output );
+ }
+ }
+}
+
+//==========================================================================
+
+static float s_resampleXRatio;
+static float s_resampleYRatio;
+
+static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 ){
+ float w;
+ float rSum = 0, gSum = 0, bSum = 0;
+ float x = s0;
+ float sumWeight = 0;
+
+ for ( x = s0; x < s1; x++, src += 4 )
+ {
+ if ( x == s0 ) {
+ w = ( int ) ( s0 + 1 ) - x;
+ }
+ else if ( x + 1 >= s1 ) {
+ w = s1 - ( int ) x;
+ }
+ else
+ {
+ w = 1.0f;
+ }
+
+ rSum += src[0] * w;
+ gSum += src[1] * w;
+ bSum += src[2] * w;
+ sumWeight += w;
+ }
+
+ rSum /= sumWeight;
+ gSum /= sumWeight;
+ bSum /= sumWeight;
+
+ dst[0] = ( unsigned char ) ( rSum + 0.5 );
+ dst[1] = ( unsigned char ) ( gSum + 0.5 );
+ dst[2] = ( unsigned char ) ( bSum + 0.5 );
+}
+
+static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
+ unsigned char *src, // source pixels
+ int srcStep, // stride of the source pixels
+ float s0, float s1 ){
+ float w;
+ float rSum = 0, gSum = 0, bSum = 0;
+ float y = s0;
+ float sumWeight = 0;
+
+ for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
+ {
+ if ( y == s0 ) {
+ w = ( int ) ( s0 + 1 ) - y;
+ }
+ else if ( y + 1 >= s1 ) {
+ w = s1 - ( int ) y;
+ }
+ else
+ {
+ w = 1.0f;
+ }
+
+ rSum += src[0] * w;
+ gSum += src[1] * w;
+ bSum += src[2] * w;
+ sumWeight += w;
+ }
+
+ rSum /= sumWeight;
+ gSum /= sumWeight;
+ bSum /= sumWeight;
+
+ dst[0] = ( unsigned char ) ( rSum + 0.5 );
+ dst[1] = ( unsigned char ) ( gSum + 0.5 );
+ dst[2] = ( unsigned char ) ( bSum + 0.5 );
+ dst[3] = 0xff;
+
+}
+
+static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth ){
+ int i;
+ unsigned char *indata = ( unsigned char * ) in->data;
+
+ indata += 4 * dstRow * in->width;
+
+ for ( i = 0; i < rowWidth; i++ )
+ {
+ float c0 = i * s_resampleXRatio;
+ float c1 = ( i + 1 ) * s_resampleXRatio;
+
+ BoxFilterHorizontalElements( &dstStart[i * 4], &indata[( ( int ) c0 ) * 4], c0, c1 );
+ }
+}
+
+static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels ){
+ float c0, c1;
+ int i;
+
+ for ( i = 0; i < dstColHeight; i++ )
+ {
+ c0 = i * s_resampleYRatio;
+ c1 = ( i + 1 ) * s_resampleYRatio;
+
+ BoxFilterVerticalElements( &dstStart[i * 4 * dstRowWidth], &srcStart[(int)c0 * srcRowWidthInPels * 4], srcRowWidthInPels * 4, c0, c1 );
+ }
+}
+
+#define DROP_SAMPLE 0
+#define BOX_FILTER 1
+
+static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight ){
+ int row, column;
+ unsigned char *indata = ( unsigned char * ) in->data;
+
+ s_resampleXRatio = in->width / ( float ) outWidth;
+ s_resampleYRatio = in->height / ( float ) outHeight;
+
+ if ( method == DROP_SAMPLE ) {
+ for ( row = 0; row < outHeight; row++ )
+ {
+ int r = ( int ) ( row * s_resampleYRatio );
+
+ for ( column = 0; column < outWidth; column++ )
+ {
+ int c = ( int ) ( column * s_resampleXRatio );
+
+ out[( row * outWidth + column ) * 4 + 0] = indata[( r * in->width + c ) * 4 + 0];
+ out[( row * outWidth + column ) * 4 + 1] = indata[( r * in->width + c ) * 4 + 1];
+ out[( row * outWidth + column ) * 4 + 2] = indata[( r * in->width + c ) * 4 + 2];
+ out[( row * outWidth + column ) * 4 + 3] = 0xff;
+ }
+ }
+ }
+ else if ( method == BOX_FILTER ) {
+ unsigned char intermediate[1024 * 1024 * 4];
+
+ assert( in->height <= 1024 );
+ assert( in->width <= 1024 );
+
+ //
+ // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
+ //
+ for ( row = 0; row < in->height; row++ )
+ {
+ BoxFilterRow( &intermediate[row * 4 * outWidth], in, row, outWidth );
+ }
+
+ //
+ // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
+ //
+ for ( column = 0; column < outWidth; column++ )
+ {
+ BoxFilterColumn( &out[column * 4], &intermediate[column * 4], column, outWidth, outHeight, s_resample_width );
+ }
+ }
+}
+
+static float BTCDistanceSquared( float a[3], float b[3] ){
+ return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
+ ( b[1] - a[1] ) * ( b[1] - a[1] ) +
+ ( b[2] - a[2] ) * ( b[2] - a[2] );
+}
+
+static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] ){
+ float longestDistance = -1;
+
+ int bX, bY;
+
+ //
+ // find the two points farthest from each other
+ //
+ for ( bY = 0; bY < 4; bY++ )
+ {
+ for ( bX = 0; bX < 4; bX++ )
+ {
+ int cX, cY;
+ float d;
+
+ //
+ // check the rest of the current row
+ //
+ for ( cX = bX + 1; cX < 4; cX++ )
+ {
+ if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance ) {
+ longestDistance = d;
+ endPoints[0][0] = bX;
+ endPoints[0][1] = bY;
+ endPoints[1][0] = cX;
+ endPoints[1][1] = bY;
+ }
+ }
+
+ //
+ // check remaining rows and columns
+ //
+ for ( cY = bY + 1; cY < 4; cY++ )
+ {
+ for ( cX = 0; cX < 4; cX++ )
+ {
+ if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance ) {
+ longestDistance = d;
+ endPoints[0][0] = bX;
+ endPoints[0][1] = bY;
+ endPoints[1][0] = cX;
+ endPoints[1][1] = cY;
+ }
+ }
+ }
+ }
+ }
+}
+
+static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError ){
+ int i;
+ int blockY, blockX;
+ float dR, dG, dB;
+ float R, G, B;
+ float error = 0;
+ float colorLine[4][3];
+
+ //
+ // build the color line
+ //
+ dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
+ inBlock[endPoints[0][1]][endPoints[0][0]][0];
+ dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
+ inBlock[endPoints[0][1]][endPoints[0][0]][1];
+ dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
+ inBlock[endPoints[0][1]][endPoints[0][0]][2];
+
+ dR *= 0.33f;
+ dG *= 0.33f;
+ dB *= 0.33f;
+
+ R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
+ G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
+ B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
+
+ for ( i = 0; i < 4; i++ )
+ {
+ colorLine[i][0] = R;
+ colorLine[i][1] = G;
+ colorLine[i][2] = B;
+
+ R += dR;
+ G += dG;
+ B += dB;
+ }
+
+ //
+ // quantize each pixel into the appropriate range
+ //
+ for ( blockY = 0; blockY < 4; blockY++ )
+ {
+ for ( blockX = 0; blockX < 4; blockX++ )
+ {
+ float distance = 10000000000;
+ int shortest = -1;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ float d;
+
+ if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance ) {
+ distance = d;
+ shortest = i;
+ }
+ }
+
+ error += distance;
+
+ //
+ // if bestError is not -1 then that means this is a speculative quantization
+ //
+ if ( bestError != -1 ) {
+ if ( error > bestError ) {
+ return error;
+ }
+ }
+
+ btcQuantizedBlock[blockY][blockX] = shortest;
+ }
+ }
+
+ return error;
+}
+
+/*
+** float BTCCompressBlock
+*/
+static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] ){
+ int i;
+ int btcQuantizedBlock[4][4]; // values should be [0..3]
+ unsigned long encodedEndPoints, encodedBitmap;
+ unsigned int endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end
+ int blockY, blockX;
+ float error = 0;
+ float bestError = 10000000000;
+ unsigned int bestEndPoints[2][2];
+
+#if 0
+ //
+ // find the "ideal" end points for the color vector
+ //
+ BTCFindEndpoints( inBlock, endPoints );
+ error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
+ memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
+#else
+ for ( blockY = 0; blockY < 4; blockY++ )
+ {
+ for ( blockX = 0; blockX < 4; blockX++ )
+ {
+ int x2, y2;
+
+ for ( y2 = 0; y2 < 4; y2++ )
+ {
+ for ( x2 = 0; x2 < 4; x2++ )
+ {
+ if ( ( x2 == blockX ) && ( y2 == blockY ) ) {
+ continue;
+ }
+
+ endPoints[0][0] = blockX;
+ endPoints[0][1] = blockY;
+ endPoints[1][0] = x2;
+ endPoints[1][1] = y2;
+
+ error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
+
+ if ( error < bestError ) {
+ bestError = error;
+ memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
+ }
+ }
+ }
+ }
+ }
+
+ error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
+#endif
+
+ //
+ // encode the results
+ //
+ encodedBitmap = 0;
+ for ( blockY = 0; blockY < 4; blockY++ )
+ {
+ for ( blockX = 0; blockX < 4; blockX++ )
+ {
+ int shift = ( blockX + blockY * 4 ) * 2;
+ encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
+ }
+ }
+
+ //
+ // encode endpoints
+ //
+ encodedEndPoints = 0;
+ for ( i = 0; i < 2; i++ )
+ {
+ int iR, iG, iB;
+
+ iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
+ if ( iR > 255 ) {
+ iR = 255;
+ }
+ else if ( iR < 0 ) {
+ iR = 0;
+ }
+ iR >>= 3;
+
+ iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
+ if ( iG > 255 ) {
+ iG = 255;
+ }
+ else if ( iG < 0 ) {
+ iG = 0;
+ }
+ iG >>= 2;
+
+ iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
+ if ( iB > 255 ) {
+ iB = 255;
+ }
+ else if ( iB < 0 ) {
+ iB = 0;
+ }
+ iB >>= 3;
+
+
+ encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
+ }
+
+ //
+ // store
+ //
+ out[0] = encodedBitmap;
+ out[1] = encodedEndPoints;
+
+ return error;
+}
+
+/*
+** void BTCDecompressFrame
+*/
+static void BTCDecompressFrame( unsigned long *src, unsigned char *dst ){
+ int x, y;
+ int iR, iG, iB;
+ int dstX, dstY;
+ float colorStart[3], colorEnd[3];
+ unsigned char colorRampABGR[4][4];
+ unsigned encoded;
+
+ memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
+
+ for ( y = 0; y < s_resample_height / 4; y++ )
+ {
+ for ( x = 0; x < s_resample_width / 4; x++ )
+ {
+ unsigned colorStartPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] & 0xffff;
+ unsigned colorEndPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] >> 16;
+
+ //
+ // grab the end points
+ // 0 = color start
+ // 1 = color end
+ //
+ iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
+ iR = ( iR << 3 ) | ( iR >> 2 );
+ iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
+ iG = ( iG << 2 ) | ( iG >> 4 );
+ iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) );
+ iB = ( iB << 3 ) | ( iB >> 2 );
+
+ colorStart[0] = iR;
+ colorStart[1] = iG;
+ colorStart[2] = iB;
+ colorRampABGR[0][0] = iR;
+ colorRampABGR[0][1] = iG;
+ colorRampABGR[0][2] = iB;
+
+ iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
+ iR = ( iR << 3 ) | ( iR >> 2 );
+ iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
+ iG = ( iG << 2 ) | ( iG >> 4 );
+ iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) );
+ iB = ( iB << 3 ) | ( iB >> 2 );
+
+ colorEnd[0] = iR;
+ colorEnd[1] = iG;
+ colorEnd[2] = iB;
+ colorRampABGR[3][0] = iR;
+ colorRampABGR[3][1] = iG;
+ colorRampABGR[3][2] = iB;
+
+ //
+ // compute this block's color ramp
+ // FIXME: This needs to be reversed on big-endian machines
+ //
+
+ colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
+ colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
+ colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
+
+ colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
+ colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
+ colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
+
+ //
+ // decode the color data
+ // information is encoded in 2-bit pixels, with low order bits corresponding
+ // to upper left pixels. These 2-bit values are indexed into the block's
+ // computer color ramp.
+ //
+ encoded = src[( y * s_resample_width / 4 + x ) * 2 + 0];
+
+ for ( dstY = 0; dstY < 4; dstY++ )
+ {
+ for ( dstX = 0; dstX < 4; dstX++ )
+ {
+ memcpy( &dst[( y * 4 + dstY ) * s_resample_width * 4 + x * 4 * 4 + dstX * 4], colorRampABGR[encoded & 3], sizeof( colorRampABGR[0] ) );
+ encoded >>= 2;
+ }
+ }
+ }
+ }
+}
+
+/*
+** BTCCompressFrame
+**
+** Perform a BTC compression using a 2-bit encoding at each pixel. This
+** compression method is performed by decomposing the incoming image into
+** a sequence of 4x4 blocks. At each block two color values are computed
+** that define the endpoints of a vector in color space that represent
+** the two colors "farthest apart".
+*/
+static float BTCCompressFrame( unsigned char *src, unsigned long *dst ){
+ int x, y;
+ int bX, bY;
+ float btcBlock[4][4][3];
+
+ float error = 0;
+
+ for ( y = 0; y < s_resample_height / 4; y++ )
+ {
+ for ( x = 0; x < s_resample_width / 4; x++ )
+ {
+ //
+ // fill in the BTC block with raw values
+ //
+ for ( bY = 0; bY < 4; bY++ )
+ {
+ for ( bX = 0; bX < 4; bX++ )
+ {
+ btcBlock[bY][bX][0] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 0];
+ btcBlock[bY][bX][1] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 1];
+ btcBlock[bY][bX][2] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 2];
+ }
+ }
+
+ error += BTCCompressBlock( btcBlock, &dst[( y * s_resample_width / 4 + x ) * 2] );
+ }
+ }
+
+ return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
+}
+
+/*
+ ===================
+ LoadFrame
+ ===================
+ */
+cblock_t LoadFrame( char *base, int frame, int digits, byte **palette ){
+ int ten3, ten2, ten1, ten0;
+ cblock_t in;
+ int width, height;
+ char name[1024];
+ FILE *f;
+
+ in.data = NULL;
+ in.count = -1;
+
+ ten3 = frame / 1000;
+ ten2 = ( frame - ten3 * 1000 ) / 100;
+ ten1 = ( frame - ten3 * 1000 - ten2 * 100 ) / 10;
+ ten0 = frame % 10;
+
+ if ( digits == 4 ) {
+ sprintf( name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0 );
+ }
+ else{
+ sprintf( name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0 );
+ }
+
+ f = fopen( name, "rb" );
+ if ( !f ) {
+ in.data = NULL;
+ return in;
+ }
+ fclose( f );
+
+ printf( "%s", name );
+ LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
+ if ( palette ) {
+ *palette = 0;
+ }
+// Load256Image (name, &in.data, palette, &width, &height);
+ in.count = width * height;
+ in.width = width;
+ in.height = height;
+// FIXME: map 0 and 255!
+
+#if 0
+ // rle compress
+ rle = RLE( in );
+ free( in.data );
+
+ return rle;
+#endif
+
+ return in;
+}
+
+/*
+ ===============
+ Cmd_Video
+
+ video <directory> <framedigits>
+ ===============
+ */
+void Cmd_Video( void ){
+ float sumError = 0, error = 0, maxError = 0;
+ char savename[1024];
+ char name[1024];
+ FILE *output;
+ int startframe, frame;
+ int width, height;
+ int i;
+ int digits;
+ int minutes;
+ float fseconds;
+ int remSeconds;
+ cblock_t in;
+ unsigned char *resampled;
+ unsigned long *compressed;
+ clock_t start, stop;
+
+ GetToken( qfalse );
+ strcpy( s_base, token );
+ if ( g_release ) {
+// sprintf (savename, "video/%s.cin", token);
+// ReleaseFile (savename);
+ return;
+ }
+
+ GetToken( qfalse );
+ strcpy( s_output_base, token );
+
+ GetToken( qfalse );
+ digits = atoi( token );
+
+ GetToken( qfalse );
+
+ if ( !strcmp( token, "btc" ) ) {
+ s_compression_method = BTC_COMPRESSION;
+ printf( "Compression: BTC\n" );
+ }
+ else if ( !strcmp( token, "uc" ) ) {
+ s_compression_method = UNCOMPRESSED;
+ printf( "Compression: none\n" );
+ }
+ else
+ {
+ Error( "Uknown compression method '%s'\n", token );
+ }
+
+ GetToken( qfalse );
+ s_resample_width = atoi( token );
+
+ GetToken( qfalse );
+ s_resample_height = atoi( token );
+
+ resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
+ compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
+
+ printf( "Resample width: %d\n", s_resample_width );
+ printf( "Resample height: %d\n", s_resample_height );
+
+ // optionally skip frames
+ if ( TokenAvailable() ) {
+ GetToken( qfalse );
+ startframe = atoi( token );
+ }
+ else{
+ startframe = 0;
+ }
+
+ sprintf( savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
+
+ // load the entire sound wav file if present
+ LoadSoundtrack();
+
+ if ( digits == 4 ) {
+ sprintf( name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base );
+ }
+ else{
+ sprintf( name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base );
+ }
+
+ printf( "%s\n", name );
+ LoadTGA( name, NULL, &width, &height );
+
+ output = fopen( savename, "wb" );
+ if ( !output ) {
+ Error( "Can't open %s", savename );
+ }
+
+ // write header info
+ i = LittleLong( CIN_SIGNATURE );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_resample_width );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_resample_height );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_wavinfo.rate );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_wavinfo.width );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_wavinfo.channels );
+ fwrite( &i, 4, 1, output );
+ i = LittleLong( s_compression_method );
+ fwrite( &i, 4, 1, output );
+
+ start = clock();
+
+ // perform compression on a per frame basis
+ for ( frame = startframe ; ; frame++ )
+ {
+ printf( "%02d: ", frame );
+ in = LoadFrame( s_base, frame, digits, 0 );
+ if ( !in.data ) {
+ break;
+ }
+
+ ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
+
+ if ( s_compression_method == UNCOMPRESSED ) {
+ printf( "\n" );
+ fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
+
+#if OUTPUT_TGAS
+ {
+ int x, y;
+ char buffer[1000];
+
+ for ( y = 0; y < s_resample_height / 2; y++ )
+ {
+ for ( x = 0; x < s_resample_width; x++ )
+ {
+ unsigned char tmp[4];
+
+ tmp[0] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
+ tmp[1] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
+ tmp[2] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
+ tmp[3] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
+
+ resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = resampled[y * s_resample_width * 4 + x * 4 + 0];
+ resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = resampled[y * s_resample_width * 4 + x * 4 + 1];
+ resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = resampled[y * s_resample_width * 4 + x * 4 + 2];
+ resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = resampled[y * s_resample_width * 4 + x * 4 + 3];
+
+ resampled[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
+ resampled[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
+ resampled[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
+ resampled[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
+ }
+ }
+
+ sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
+ WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
+ }
+#endif
+ }
+ else if ( s_compression_method == BTC_COMPRESSION ) {
+ error = BTCCompressFrame( resampled, compressed );
+
+ sumError += error;
+
+ if ( error > maxError ) {
+ maxError = error;
+ }
+
+ printf( " (error = %f)\n", error );
+ fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
+
+#if OUTPUT_TGAS
+ {
+ int x, y;
+ unsigned char *uncompressed;
+ char buffer[1000];
+
+ uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
+ BTCDecompressFrame( compressed, uncompressed );
+
+ for ( y = 0; y < s_resample_height / 2; y++ )
+ {
+ for ( x = 0; x < s_resample_width; x++ )
+ {
+ unsigned char tmp[4];
+
+ tmp[0] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
+ tmp[1] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
+ tmp[2] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
+ tmp[3] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
+
+ uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = uncompressed[y * s_resample_width * 4 + x * 4 + 0];
+ uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = uncompressed[y * s_resample_width * 4 + x * 4 + 1];
+ uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = uncompressed[y * s_resample_width * 4 + x * 4 + 2];
+ uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = uncompressed[y * s_resample_width * 4 + x * 4 + 3];
+
+ uncompressed[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
+ uncompressed[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
+ uncompressed[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
+ uncompressed[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
+ }
+ }
+
+
+ sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
+ WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
+
+ free( uncompressed );
+ }
+#endif
+ }
+
+ WriteSound( output, frame );
+
+ free( in.data );
+ }
+ stop = clock();
+
+ printf( "\n" );
+
+ printf( "Total size: %ld\n", ftell( output ) );
+ printf( "Average error: %f\n", sumError / ( frame - startframe ) );
+ printf( "Max error: %f\n", maxError );
+
+ fseconds = ( stop - start ) / 1000.0f;
+ minutes = fseconds / 60;
+ remSeconds = fseconds - minutes * 60;
+
+ printf( "Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
+ printf( "Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
+
+ fclose( output );
+
+ if ( s_soundtrack ) {
+ free( s_soundtrack );
+ s_soundtrack = 0;
+ }
+}