26b79a0f1454a99e31e28cd35d98e4bbf872a5e8
[xonotic/netradiant.git] / tools / quake3 / q3data / video.c
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include <assert.h>
23 #include "q3data.h"
24
25 static int s_resample_width = 256;
26 static int s_resample_height = 256;
27
28 #define OUTPUT_TGAS         1
29
30 #define UNCOMPRESSED        0
31 #define BTC_COMPRESSION     1
32
33 static int s_compression_method = BTC_COMPRESSION;
34
35 static const char *CIN_EXTENSION = "cn2";
36 static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
37
38 static byte *s_soundtrack;
39 static char s_base[32];
40 static char s_output_base[32];
41
42 /*
43    ===============================================================================
44
45    WAV loading
46
47    ===============================================================================
48  */
49
50 typedef struct
51 {
52         int rate;
53         int width;
54         int channels;
55         int loopstart;
56         int samples;
57         int dataofs;                // chunk starts this many bytes from file start
58 } wavinfo_t;
59
60
61 byte    *data_p;
62 byte    *iff_end;
63 byte    *last_chunk;
64 byte    *iff_data;
65 int iff_chunk_len;
66
67
68 static int s_samplecounts[0x10000];
69 static wavinfo_t s_wavinfo;
70
71 short GetLittleShort( void ){
72         short val = 0;
73         val = *data_p;
74         val = val + ( *( data_p + 1 ) << 8 );
75         data_p += 2;
76         return val;
77 }
78
79 int GetLittleLong( void ){
80         int val = 0;
81         val = *data_p;
82         val = val + ( *( data_p + 1 ) << 8 );
83         val = val + ( *( data_p + 2 ) << 16 );
84         val = val + ( *( data_p + 3 ) << 24 );
85         data_p += 4;
86         return val;
87 }
88
89 void FindNextChunk( char *name ){
90         while ( 1 )
91         {
92                 data_p = last_chunk;
93
94                 if ( data_p >= iff_end ) { // didn't find the chunk
95                         data_p = NULL;
96                         return;
97                 }
98
99                 data_p += 4;
100                 iff_chunk_len = GetLittleLong();
101                 if ( iff_chunk_len < 0 ) {
102                         data_p = NULL;
103                         return;
104                 }
105 //              if (iff_chunk_len > 1024*1024)
106 //                      Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
107                 data_p -= 8;
108                 last_chunk = data_p + 8 + ( ( iff_chunk_len + 1 ) & ~1 );
109                 if ( !strncmp((const char *) data_p, name, 4 ) ) {
110                         return;
111                 }
112         }
113 }
114
115 void FindChunk( char *name ){
116         last_chunk = iff_data;
117         FindNextChunk( name );
118 }
119
120
121 void DumpChunks( void ){
122         char str[5];
123
124         str[4] = 0;
125         data_p = iff_data;
126         do
127         {
128                 memcpy( str, data_p, 4 );
129                 data_p += 4;
130                 iff_chunk_len = GetLittleLong();
131                 printf( "0x%x : %s (%d)\n", (int)( data_p - 4 ), str, iff_chunk_len );
132                 data_p += ( iff_chunk_len + 1 ) & ~1;
133         } while ( data_p < iff_end );
134 }
135
136 /*
137    ============
138    GetWavinfo
139    ============
140  */
141 wavinfo_t GetWavinfo( char *name, byte *wav, int wavlength ){
142         wavinfo_t info;
143         int i;
144         int format;
145         int samples;
146
147         memset( &info, 0, sizeof( info ) );
148
149         if ( !wav ) {
150                 return info;
151         }
152
153         iff_data = wav;
154         iff_end = wav + wavlength;
155
156 // find "RIFF" chunk
157         FindChunk( "RIFF" );
158         if ( !( data_p && !strncmp((const char *) (data_p + 8), "WAVE", 4 ) ) ) {
159                 printf( "Missing RIFF/WAVE chunks\n" );
160                 return info;
161         }
162
163 // get "fmt " chunk
164         iff_data = data_p + 12;
165 // DumpChunks ();
166
167         FindChunk( "fmt " );
168         if ( !data_p ) {
169                 printf( "Missing fmt chunk\n" );
170                 return info;
171         }
172         data_p += 8;
173         format = GetLittleShort();
174         if ( format != 1 ) {
175                 printf( "Microsoft PCM format only\n" );
176                 return info;
177         }
178
179         info.channels = GetLittleShort();
180         info.rate = GetLittleLong();
181         data_p += 4 + 2;
182         info.width = GetLittleShort() / 8;
183
184 // get cue chunk
185         FindChunk( "cue " );
186         if ( data_p ) {
187                 data_p += 32;
188                 info.loopstart = GetLittleLong();
189 //              Com_Printf("loopstart=%d\n", sfx->loopstart);
190
191                 // if the next chunk is a LIST chunk, look for a cue length marker
192                 FindNextChunk( "LIST" );
193                 if ( data_p ) {
194                         if ( !strncmp((const char *) (data_p + 28), "mark", 4 ) ) { // this is not a proper parse, but it works with cooledit...
195                                 data_p += 24;
196                                 i = GetLittleLong();    // samples in loop
197                                 info.samples = info.loopstart + i;
198                         }
199                 }
200         }
201         else{
202                 info.loopstart = -1;
203         }
204
205 // find data chunk
206         FindChunk( "data" );
207         if ( !data_p ) {
208                 printf( "Missing data chunk\n" );
209                 return info;
210         }
211
212         data_p += 4;
213         samples = GetLittleLong();
214
215         if ( info.samples ) {
216                 if ( samples < info.samples ) {
217                         Error( "Sound %s has a bad loop length", name );
218                 }
219         }
220         else{
221                 info.samples = samples;
222         }
223
224         info.dataofs = data_p - wav;
225
226         return info;
227 }
228
229 //=====================================================================
230
231 /*
232    ==============
233    LoadSoundtrack
234    ==============
235  */
236 void LoadSoundtrack( void ){
237         char name[1024];
238         FILE    *f;
239         int len;
240         int i, val, j;
241
242         s_soundtrack = NULL;
243         sprintf( name, "%svideo/%s/%s.wav", gamedir, s_base, s_base );
244         printf( "WAV: %s\n", name );
245         f = fopen( name, "rb" );
246         if ( !f ) {
247                 printf( "no soundtrack for %s\n", s_base );
248                 return;
249         }
250         len = Q_filelength( f );
251         s_soundtrack = malloc( len );
252         fread( s_soundtrack, 1, len, f );
253         fclose( f );
254
255         s_wavinfo = GetWavinfo( name, s_soundtrack, len );
256
257         // count samples for compression
258         memset( s_samplecounts, 0, sizeof( s_samplecounts ) );
259
260         j = s_wavinfo.samples / 2;
261         for ( i = 0 ; i < j ; i++ )
262         {
263                 val = ( (unsigned short *)( s_soundtrack + s_wavinfo.dataofs ) )[i];
264                 s_samplecounts[val]++;
265         }
266         val = 0;
267         for ( i = 0 ; i < 0x10000 ; i++ )
268                 if ( s_samplecounts[i] ) {
269                         val++;
270                 }
271
272         printf( "%i unique sample values\n", val );
273 }
274
275 /*
276    ==================
277    WriteSound
278    ==================
279  */
280 void WriteSound( FILE *output, int frame ){
281         int start, end;
282         int count;
283         int empty = 0;
284         int i;
285         int sample;
286         int width;
287
288         width = s_wavinfo.width * s_wavinfo.channels;
289
290         start = frame * s_wavinfo.rate / 14;
291         end = ( frame + 1 ) * s_wavinfo.rate / 14;
292         count = end - start;
293
294         for ( i = 0 ; i < count ; i++ )
295         {
296                 sample = start + i;
297                 if ( sample > s_wavinfo.samples || !s_soundtrack ) {
298                         fwrite( &empty, 1, width, output );
299                 }
300                 else{
301                         fwrite( s_soundtrack + s_wavinfo.dataofs + sample * width, 1, width,output );
302                 }
303         }
304 }
305
306 //==========================================================================
307
308 static float s_resampleXRatio;
309 static float s_resampleYRatio;
310
311 static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 ){
312         float w;
313         float rSum = 0, gSum = 0, bSum = 0;
314         float x = s0;
315         float sumWeight = 0;
316
317         for ( x = s0; x < s1; x++, src += 4 )
318         {
319                 if ( x == s0 ) {
320                         w = ( int ) ( s0 + 1 ) - x;
321                 }
322                 else if ( x + 1 >= s1 ) {
323                         w = s1 - ( int ) x;
324                 }
325                 else
326                 {
327                         w = 1.0f;
328                 }
329
330                 rSum += src[0] * w;
331                 gSum += src[1] * w;
332                 bSum += src[2] * w;
333                 sumWeight += w;
334         }
335
336         rSum /= sumWeight;
337         gSum /= sumWeight;
338         bSum /= sumWeight;
339
340         dst[0] = ( unsigned char ) ( rSum + 0.5 );
341         dst[1] = ( unsigned char ) ( gSum + 0.5 );
342         dst[2] = ( unsigned char ) ( bSum + 0.5 );
343 }
344
345 static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
346                                                                            unsigned char *src, // source pixels
347                                                                            int srcStep,        // stride of the source pixels
348                                                                            float s0, float s1 ){
349         float w;
350         float rSum = 0, gSum = 0, bSum = 0;
351         float y = s0;
352         float sumWeight = 0;
353
354         for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
355         {
356                 if ( y == s0 ) {
357                         w = ( int ) ( s0 + 1 ) - y;
358                 }
359                 else if ( y + 1 >= s1 ) {
360                         w = s1 - ( int ) y;
361                 }
362                 else
363                 {
364                         w = 1.0f;
365                 }
366
367                 rSum += src[0] * w;
368                 gSum += src[1] * w;
369                 bSum += src[2] * w;
370                 sumWeight += w;
371         }
372
373         rSum /= sumWeight;
374         gSum /= sumWeight;
375         bSum /= sumWeight;
376
377         dst[0] = ( unsigned char ) ( rSum + 0.5 );
378         dst[1] = ( unsigned char ) ( gSum + 0.5 );
379         dst[2] = ( unsigned char ) ( bSum + 0.5 );
380         dst[3] = 0xff;
381
382 }
383
384 static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth ){
385         int i;
386         unsigned char *indata = ( unsigned char * ) in->data;
387
388         indata += 4 * dstRow * in->width;
389
390         for ( i = 0; i < rowWidth; i++ )
391         {
392                 float c0 = i * s_resampleXRatio;
393                 float c1 = ( i + 1 ) * s_resampleXRatio;
394
395                 BoxFilterHorizontalElements( &dstStart[i * 4], &indata[( ( int ) c0 ) * 4], c0, c1 );
396         }
397 }
398
399 static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels ){
400         float c0, c1;
401         int i;
402
403         for ( i = 0; i < dstColHeight; i++ )
404         {
405                 c0 = i * s_resampleYRatio;
406                 c1 = ( i + 1 ) * s_resampleYRatio;
407
408                 BoxFilterVerticalElements( &dstStart[i * 4 * dstRowWidth], &srcStart[(int)c0 * srcRowWidthInPels * 4], srcRowWidthInPels * 4, c0, c1 );
409         }
410 }
411
412 #define DROP_SAMPLE     0
413 #define BOX_FILTER      1
414
415 static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight ){
416         int row, column;
417         unsigned char *indata = ( unsigned char * ) in->data;
418
419         s_resampleXRatio = in->width / ( float ) outWidth;
420         s_resampleYRatio = in->height / ( float ) outHeight;
421
422         if ( method == DROP_SAMPLE ) {
423                 for ( row = 0; row < outHeight; row++ )
424                 {
425                         int r = ( int ) ( row * s_resampleYRatio );
426
427                         for ( column = 0; column < outWidth; column++ )
428                         {
429                                 int c = ( int ) ( column * s_resampleXRatio );
430
431                                 out[( row * outWidth + column ) * 4 + 0] = indata[( r * in->width + c ) * 4 + 0];
432                                 out[( row * outWidth + column ) * 4 + 1] = indata[( r * in->width + c ) * 4 + 1];
433                                 out[( row * outWidth + column ) * 4 + 2] = indata[( r * in->width + c ) * 4 + 2];
434                                 out[( row * outWidth + column ) * 4 + 3] = 0xff;
435                         }
436                 }
437         }
438         else if ( method == BOX_FILTER ) {
439                 unsigned char intermediate[1024 * 1024 * 4];
440
441                 assert( in->height <= 1024 );
442                 assert( in->width <= 1024 );
443
444                 //
445                 // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
446                 //
447                 for ( row = 0; row < in->height; row++ )
448                 {
449                         BoxFilterRow( &intermediate[row * 4 * outWidth], in, row, outWidth );
450                 }
451
452                 //
453                 // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
454                 //
455                 for ( column = 0; column < outWidth; column++ )
456                 {
457                         BoxFilterColumn( &out[column * 4], &intermediate[column * 4], column, outWidth, outHeight, s_resample_width );
458                 }
459         }
460 }
461
462 static float BTCDistanceSquared( float a[3], float b[3] ){
463         return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
464                    ( b[1] - a[1] ) * ( b[1] - a[1] ) +
465                    ( b[2] - a[2] ) * ( b[2] - a[2] );
466 }
467
468 static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] ){
469         float longestDistance = -1;
470
471         int bX, bY;
472
473         //
474         // find the two points farthest from each other
475         //
476         for ( bY = 0; bY < 4; bY++ )
477         {
478                 for ( bX = 0; bX < 4; bX++ )
479                 {
480                         int cX, cY;
481                         float d;
482
483                         //
484                         // check the rest of the current row
485                         //
486                         for ( cX = bX + 1; cX < 4; cX++ )
487                         {
488                                 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance ) {
489                                         longestDistance = d;
490                                         endPoints[0][0] = bX;
491                                         endPoints[0][1] = bY;
492                                         endPoints[1][0] = cX;
493                                         endPoints[1][1] = bY;
494                                 }
495                         }
496
497                         //
498                         // check remaining rows and columns
499                         //
500                         for ( cY = bY + 1; cY < 4; cY++ )
501                         {
502                                 for ( cX = 0; cX < 4; cX++ )
503                                 {
504                                         if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance ) {
505                                                 longestDistance = d;
506                                                 endPoints[0][0] = bX;
507                                                 endPoints[0][1] = bY;
508                                                 endPoints[1][0] = cX;
509                                                 endPoints[1][1] = cY;
510                                         }
511                                 }
512                         }
513                 }
514         }
515 }
516
517 static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError ){
518         int i;
519         int blockY, blockX;
520         float dR, dG, dB;
521         float R, G, B;
522         float error = 0;
523         float colorLine[4][3];
524
525         //
526         // build the color line
527         //
528         dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
529                  inBlock[endPoints[0][1]][endPoints[0][0]][0];
530         dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
531                  inBlock[endPoints[0][1]][endPoints[0][0]][1];
532         dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
533                  inBlock[endPoints[0][1]][endPoints[0][0]][2];
534
535         dR *= 0.33f;
536         dG *= 0.33f;
537         dB *= 0.33f;
538
539         R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
540         G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
541         B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
542
543         for ( i = 0; i < 4; i++ )
544         {
545                 colorLine[i][0] = R;
546                 colorLine[i][1] = G;
547                 colorLine[i][2] = B;
548
549                 R += dR;
550                 G += dG;
551                 B += dB;
552         }
553
554         //
555         // quantize each pixel into the appropriate range
556         //
557         for ( blockY = 0; blockY < 4; blockY++ )
558         {
559                 for ( blockX = 0; blockX < 4; blockX++ )
560                 {
561                         float distance = 10000000000;
562                         int shortest = -1;
563
564                         for ( i = 0; i < 4; i++ )
565                         {
566                                 float d;
567
568                                 if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance ) {
569                                         distance = d;
570                                         shortest = i;
571                                 }
572                         }
573
574                         error += distance;
575
576                         //
577                         // if bestError is not -1 then that means this is a speculative quantization
578                         //
579                         if ( bestError != -1 ) {
580                                 if ( error > bestError ) {
581                                         return error;
582                                 }
583                         }
584
585                         btcQuantizedBlock[blockY][blockX] = shortest;
586                 }
587         }
588
589         return error;
590 }
591
592 /*
593 ** float BTCCompressBlock
594 */
595 static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] ){
596         int i;
597         int btcQuantizedBlock[4][4];    // values should be [0..3]
598         unsigned long encodedEndPoints, encodedBitmap;
599         unsigned int endPoints[2][2];       // endPoints[0] = color start, endPoints[1] = color end
600         int blockY, blockX;
601         float error = 0;
602         float bestError = 10000000000;
603         unsigned int bestEndPoints[2][2];
604
605 #if 0
606         //
607         // find the "ideal" end points for the color vector
608         //
609         BTCFindEndpoints( inBlock, endPoints );
610         error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
611         memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
612 #else
613         for ( blockY = 0; blockY < 4; blockY++ )
614         {
615                 for ( blockX = 0; blockX < 4; blockX++ )
616                 {
617                         int x2, y2;
618
619                         for ( y2 = 0; y2 < 4; y2++ )
620                         {
621                                 for ( x2 = 0; x2 < 4; x2++ )
622                                 {
623                                         if ( ( x2 == blockX ) && ( y2 == blockY ) ) {
624                                                 continue;
625                                         }
626
627                                         endPoints[0][0] = blockX;
628                                         endPoints[0][1] = blockY;
629                                         endPoints[1][0] = x2;
630                                         endPoints[1][1] = y2;
631
632                                         error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
633
634                                         if ( error < bestError ) {
635                                                 bestError = error;
636                                                 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
637                                         }
638                                 }
639                         }
640                 }
641         }
642
643         error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
644 #endif
645
646         //
647         // encode the results
648         //
649         encodedBitmap = 0;
650         for ( blockY = 0; blockY < 4; blockY++ )
651         {
652                 for ( blockX = 0; blockX < 4; blockX++ )
653                 {
654                         int shift = ( blockX + blockY * 4 ) * 2;
655                         encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
656                 }
657         }
658
659         //
660         // encode endpoints
661         //
662         encodedEndPoints = 0;
663         for ( i = 0; i < 2; i++ )
664         {
665                 int iR, iG, iB;
666
667                 iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
668                 if ( iR > 255 ) {
669                         iR = 255;
670                 }
671                 else if ( iR < 0 ) {
672                         iR = 0;
673                 }
674                 iR >>= 3;
675
676                 iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
677                 if ( iG > 255 ) {
678                         iG = 255;
679                 }
680                 else if ( iG < 0 ) {
681                         iG = 0;
682                 }
683                 iG >>= 2;
684
685                 iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
686                 if ( iB > 255 ) {
687                         iB = 255;
688                 }
689                 else if ( iB < 0 ) {
690                         iB = 0;
691                 }
692                 iB >>= 3;
693
694
695                 encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
696         }
697
698         //
699         // store
700         //
701         out[0] = encodedBitmap;
702         out[1] = encodedEndPoints;
703
704         return error;
705 }
706
707 /*
708 ** void BTCDecompressFrame
709 */
710 static void BTCDecompressFrame( unsigned long *src, unsigned char *dst ){
711         int x, y;
712         int iR, iG, iB;
713         int dstX, dstY;
714         float colorStart[3], colorEnd[3];
715         unsigned char colorRampABGR[4][4];
716         unsigned encoded;
717
718         memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
719
720         for ( y = 0; y < s_resample_height / 4; y++ )
721         {
722                 for ( x = 0; x < s_resample_width / 4; x++ )
723                 {
724                         unsigned colorStartPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] & 0xffff;
725                         unsigned colorEndPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] >> 16;
726
727                         //
728                         // grab the end points
729                         //   0 = color start
730                         //   1 = color end
731                         //
732                         iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
733                         iR = ( iR << 3 ) | ( iR >> 2 );
734                         iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 )  - 1 ) );
735                         iG = ( iG << 2 ) | ( iG >> 4 );
736                         iB = ( ( colorStartPacked ) & ( ( 1 << 5  ) - 1 ) );
737                         iB = ( iB << 3 ) | ( iB >> 2 );
738
739                         colorStart[0] = iR;
740                         colorStart[1] = iG;
741                         colorStart[2] = iB;
742                         colorRampABGR[0][0] = iR;
743                         colorRampABGR[0][1] = iG;
744                         colorRampABGR[0][2] = iB;
745
746                         iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
747                         iR = ( iR << 3 ) | ( iR >> 2 );
748                         iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 )  - 1 ) );
749                         iG = ( iG << 2 ) | ( iG >> 4 );
750                         iB = ( colorEndPacked & ( ( 1 << 5  ) - 1 ) );
751                         iB = ( iB << 3 ) | ( iB >> 2 );
752
753                         colorEnd[0] = iR;
754                         colorEnd[1] = iG;
755                         colorEnd[2] = iB;
756                         colorRampABGR[3][0] = iR;
757                         colorRampABGR[3][1] = iG;
758                         colorRampABGR[3][2] = iB;
759
760                         //
761                         // compute this block's color ramp
762                         // FIXME: This needs to be reversed on big-endian machines
763                         //
764
765                         colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
766                         colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
767                         colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
768
769                         colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
770                         colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
771                         colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
772
773                         //
774                         // decode the color data
775                         // information is encoded in 2-bit pixels, with low order bits corresponding
776                         // to upper left pixels.  These 2-bit values are indexed into the block's
777                         // computer color ramp.
778                         //
779                         encoded = src[( y * s_resample_width / 4 + x ) * 2 + 0];
780
781                         for ( dstY = 0; dstY < 4; dstY++ )
782                         {
783                                 for ( dstX = 0; dstX < 4; dstX++ )
784                                 {
785                                         memcpy( &dst[( y * 4 + dstY ) * s_resample_width * 4 + x * 4 * 4 + dstX * 4], colorRampABGR[encoded & 3], sizeof( colorRampABGR[0] ) );
786                                         encoded >>= 2;
787                                 }
788                         }
789                 }
790         }
791 }
792
793 /*
794 ** BTCCompressFrame
795 **
796 ** Perform a BTC compression using a 2-bit encoding at each pixel.  This
797 ** compression method is performed by decomposing the incoming image into
798 ** a sequence of 4x4 blocks.  At each block two color values are computed
799 ** that define the endpoints of a vector in color space that represent
800 ** the two colors "farthest apart".
801 */
802 static float BTCCompressFrame( unsigned char *src, unsigned long *dst ){
803         int x, y;
804         int bX, bY;
805         float btcBlock[4][4][3];
806
807         float error = 0;
808
809         for ( y = 0; y < s_resample_height / 4; y++ )
810         {
811                 for ( x = 0; x < s_resample_width / 4; x++ )
812                 {
813                         //
814                         // fill in the BTC block with raw values
815                         //
816                         for ( bY = 0; bY < 4; bY++ )
817                         {
818                                 for ( bX = 0; bX < 4; bX++ )
819                                 {
820                                         btcBlock[bY][bX][0] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 0];
821                                         btcBlock[bY][bX][1] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 1];
822                                         btcBlock[bY][bX][2] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 2];
823                                 }
824                         }
825
826                         error += BTCCompressBlock( btcBlock, &dst[( y * s_resample_width / 4 + x ) * 2] );
827                 }
828         }
829
830         return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
831 }
832
833 /*
834    ===================
835    LoadFrame
836    ===================
837  */
838 cblock_t LoadFrame( char *base, int frame, int digits, byte **palette ){
839         int ten3, ten2, ten1, ten0;
840         cblock_t in;
841         int width, height;
842         char name[1024];
843         FILE        *f;
844
845         in.data = NULL;
846         in.count = -1;
847
848         ten3 = frame / 1000;
849         ten2 = ( frame - ten3 * 1000 ) / 100;
850         ten1 = ( frame - ten3 * 1000 - ten2 * 100 ) / 10;
851         ten0 = frame % 10;
852
853         if ( digits == 4 ) {
854                 sprintf( name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0 );
855         }
856         else{
857                 sprintf( name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0 );
858         }
859
860         f = fopen( name, "rb" );
861         if ( !f ) {
862                 in.data = NULL;
863                 return in;
864         }
865         fclose( f );
866
867         printf( "%s", name );
868         LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
869         if ( palette ) {
870                 *palette = 0;
871         }
872 //      Load256Image (name, &in.data, palette, &width, &height);
873         in.count = width * height;
874         in.width = width;
875         in.height = height;
876 // FIXME: map 0 and 255!
877
878 #if 0
879         // rle compress
880         rle = RLE( in );
881         free( in.data );
882
883         return rle;
884 #endif
885
886         return in;
887 }
888
889 /*
890    ===============
891    Cmd_Video
892
893    video <directory> <framedigits>
894    ===============
895  */
896 void Cmd_Video( void ){
897         float sumError = 0, error = 0, maxError = 0;
898         char savename[1024];
899         char name[1024];
900         FILE    *output;
901         int startframe, frame;
902         int width, height;
903         int i;
904         int digits;
905         int minutes;
906         float fseconds;
907         int remSeconds;
908         cblock_t in;
909         unsigned char *resampled;
910         unsigned long *compressed;
911         clock_t start, stop;
912
913         GetToken( qfalse );
914         strcpy( s_base, token );
915         if ( g_release ) {
916 //              sprintf (savename, "video/%s.cin", token);
917 //              ReleaseFile (savename);
918                 return;
919         }
920
921         GetToken( qfalse );
922         strcpy( s_output_base, token );
923
924         GetToken( qfalse );
925         digits = atoi( token );
926
927         GetToken( qfalse );
928
929         if ( !strcmp( token, "btc" ) ) {
930                 s_compression_method = BTC_COMPRESSION;
931                 printf( "Compression: BTC\n" );
932         }
933         else if ( !strcmp( token, "uc" ) ) {
934                 s_compression_method = UNCOMPRESSED;
935                 printf( "Compression: none\n" );
936         }
937         else
938         {
939                 Error( "Uknown compression method '%s'\n", token );
940         }
941
942         GetToken( qfalse );
943         s_resample_width = atoi( token );
944
945         GetToken( qfalse );
946         s_resample_height = atoi( token );
947
948         resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
949         compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
950
951         printf( "Resample width: %d\n", s_resample_width );
952         printf( "Resample height: %d\n", s_resample_height );
953
954         // optionally skip frames
955         if ( TokenAvailable() ) {
956                 GetToken( qfalse );
957                 startframe = atoi( token );
958         }
959         else{
960                 startframe = 0;
961         }
962
963         sprintf( savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
964
965         // load the entire sound wav file if present
966         LoadSoundtrack();
967
968         if ( digits == 4 ) {
969                 sprintf( name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base );
970         }
971         else{
972                 sprintf( name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base );
973         }
974
975         printf( "%s\n", name );
976         LoadTGA( name, NULL, &width, &height );
977
978         output = fopen( savename, "wb" );
979         if ( !output ) {
980                 Error( "Can't open %s", savename );
981         }
982
983         // write header info
984         i = LittleLong( CIN_SIGNATURE );
985         fwrite( &i, 4, 1, output );
986         i = LittleLong( s_resample_width );
987         fwrite( &i, 4, 1, output );
988         i = LittleLong( s_resample_height );
989         fwrite( &i, 4, 1, output );
990         i = LittleLong( s_wavinfo.rate );
991         fwrite( &i, 4, 1, output );
992         i = LittleLong( s_wavinfo.width );
993         fwrite( &i, 4, 1, output );
994         i = LittleLong( s_wavinfo.channels );
995         fwrite( &i, 4, 1, output );
996         i = LittleLong( s_compression_method );
997         fwrite( &i, 4, 1, output );
998
999         start = clock();
1000
1001         // perform compression on a per frame basis
1002         for ( frame = startframe ;  ; frame++ )
1003         {
1004                 printf( "%02d: ", frame );
1005                 in = LoadFrame( s_base, frame, digits, 0 );
1006                 if ( !in.data ) {
1007                         break;
1008                 }
1009
1010                 ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
1011
1012                 if ( s_compression_method == UNCOMPRESSED ) {
1013                         printf( "\n" );
1014                         fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
1015
1016 #if OUTPUT_TGAS
1017                         {
1018                                 int x, y;
1019                                 char buffer[1000];
1020
1021                                 for ( y = 0; y < s_resample_height / 2; y++ )
1022                                 {
1023                                         for ( x = 0; x < s_resample_width; x++ )
1024                                         {
1025                                                 unsigned char tmp[4];
1026
1027                                                 tmp[0] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
1028                                                 tmp[1] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
1029                                                 tmp[2] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
1030                                                 tmp[3] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
1031
1032                                                 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = resampled[y * s_resample_width * 4 + x * 4 + 0];
1033                                                 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = resampled[y * s_resample_width * 4 + x * 4 + 1];
1034                                                 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = resampled[y * s_resample_width * 4 + x * 4 + 2];
1035                                                 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = resampled[y * s_resample_width * 4 + x * 4 + 3];
1036
1037                                                 resampled[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
1038                                                 resampled[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
1039                                                 resampled[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
1040                                                 resampled[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
1041                                         }
1042                                 }
1043
1044                                 sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
1045                                 WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
1046                         }
1047 #endif
1048                 }
1049                 else if ( s_compression_method == BTC_COMPRESSION ) {
1050                         error = BTCCompressFrame( resampled, compressed );
1051
1052                         sumError += error;
1053
1054                         if ( error > maxError ) {
1055                                 maxError = error;
1056                         }
1057
1058                         printf( " (error = %f)\n", error );
1059                         fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
1060
1061 #if OUTPUT_TGAS
1062                         {
1063                                 int x, y;
1064                                 unsigned char *uncompressed;
1065                                 char buffer[1000];
1066
1067                                 uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
1068                                 BTCDecompressFrame( compressed, uncompressed );
1069
1070                                 for ( y = 0; y < s_resample_height / 2; y++ )
1071                                 {
1072                                         for ( x = 0; x < s_resample_width; x++ )
1073                                         {
1074                                                 unsigned char tmp[4];
1075
1076                                                 tmp[0] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
1077                                                 tmp[1] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
1078                                                 tmp[2] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
1079                                                 tmp[3] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
1080
1081                                                 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = uncompressed[y * s_resample_width * 4 + x * 4 + 0];
1082                                                 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = uncompressed[y * s_resample_width * 4 + x * 4 + 1];
1083                                                 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = uncompressed[y * s_resample_width * 4 + x * 4 + 2];
1084                                                 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = uncompressed[y * s_resample_width * 4 + x * 4 + 3];
1085
1086                                                 uncompressed[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
1087                                                 uncompressed[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
1088                                                 uncompressed[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
1089                                                 uncompressed[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
1090                                         }
1091                                 }
1092
1093
1094                                 sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
1095                                 WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
1096
1097                                 free( uncompressed );
1098                         }
1099 #endif
1100                 }
1101
1102                 WriteSound( output, frame );
1103
1104                 free( in.data );
1105         }
1106         stop = clock();
1107
1108         printf( "\n" );
1109
1110         printf( "Total size: %ld\n", ftell( output ) );
1111         printf( "Average error: %f\n", sumError / ( frame - startframe ) );
1112         printf( "Max error: %f\n", maxError );
1113
1114         fseconds = ( stop - start ) / 1000.0f;
1115         minutes = fseconds / 60;
1116         remSeconds = fseconds - minutes * 60;
1117
1118         printf( "Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
1119         printf( "Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
1120
1121         fclose( output );
1122
1123         if ( s_soundtrack ) {
1124                 free( s_soundtrack );
1125                 s_soundtrack = 0;
1126         }
1127 }