/* 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 */ // To do // Sound error handling (when sound too short) // rle b4 huffing // adpcm encoding of sound #if 0 #include "qdata.h" #include "flex.h" #include "fc.h" #include "adpcm.h" #define MIN_REPT 15 #define MAX_REPT 0 #define HUF_TOKENS ( 256 + MAX_REPT ) #define BLOCKSIZE 8 #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h #define SQRT2 1.414213562 typedef struct hnode_s { int count; qboolean used; int children[2]; } hnode_t; typedef struct { int rate; int width; int channels; int loopstart; int samples; int dataofs; // chunk starts this many bytes from file start } wavinfo_t; // These weren`t picked out my ass.... // They were defined at http://www.rahul.net/jfm/dct.html // However, I think he plucked them out of his ass..... float Quantise[BLOCKSIZE * BLOCKSIZE]; float LUT_Quantise[BLOCKSIZE * BLOCKSIZE] = { 16.0F / 16.0F, 11.0F / 16.0F, 10.0F / 16.0F, 16.0F / 16.0F, 24.0F / 16.0F, 40.0F / 16.0F, 51.0F / 16.0F, 61.0F / 16.0F, 12.0F / 16.0F, 13.0F / 16.0F, 14.0F / 16.0F, 19.0F / 16.0F, 26.0F / 16.0F, 58.0F / 16.0F, 60.0F / 16.0F, 55.0F / 16.0F, 14.0F / 16.0F, 13.0F / 16.0F, 16.0F / 16.0F, 24.0F / 16.0F, 40.0F / 16.0F, 57.0F / 16.0F, 69.0F / 16.0F, 56.0F / 16.0F, 14.0F / 16.0F, 17.0F / 16.0F, 22.0F / 16.0F, 29.0F / 16.0F, 51.0F / 16.0F, 87.0F / 16.0F, 80.0F / 16.0F, 62.0F / 16.0F, 18.0F / 16.0F, 22.0F / 16.0F, 37.0F / 16.0F, 56.0F / 16.0F, 68.0F / 16.0F,109.0F / 16.0F,103.0F / 16.0F, 77.0F / 16.0F, 24.0F / 16.0F, 35.0F / 16.0F, 55.0F / 16.0F, 64.0F / 16.0F, 81.0F / 16.0F,104.0F / 16.0F,113.0F / 16.0F, 92.0F / 16.0F, 49.0F / 16.0F, 64.0F / 16.0F, 78.0F / 16.0F, 87.0F / 16.0F,103.0F / 16.0F,121.0F / 16.0F,120.0F / 16.0F,101.0F / 16.0F, 72.0F / 16.0F, 92.0F / 16.0F, 95.0F / 16.0F, 98.0F / 16.0F,112.0F / 16.0F,100.0F / 16.0F,103.0F / 16.0F, 99.0F / 16.0F }; int LUT_ZZ[BLOCKSIZE * BLOCKSIZE] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; char base[32]; byte *soundtrack; byte scaled[256][HUF_TOKENS]; unsigned int charbits1[256][HUF_TOKENS]; int charbitscount1[256][HUF_TOKENS]; hnode_t hnodes1[256][HUF_TOKENS * 2]; int numhnodes1[256]; int order0counts[256]; int numhnodes; hnode_t hnodes[512]; unsigned charbits[256]; int charbitscount[256]; CineHead_t cinehead; byte *data_p; byte *iff_end; byte *last_chunk; byte *iff_data; int iff_chunk_len; float dctbase[BLOCKSIZE][BLOCKSIZE]; float red[BLOCKSIZE * BLOCKSIZE]; float green[BLOCKSIZE * BLOCKSIZE]; float blue[BLOCKSIZE * BLOCKSIZE]; float temp[BLOCKSIZE * BLOCKSIZE]; wavinfo_t wavinfo; adpcm_t adpcm; /* =============================================================================== WAV loading =============================================================================== */ /* Intel ADPCM step variation table */ static int indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8, }; static int stepsizeTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; #if 0 static void adpcm_decoder( char *indata, short *outdata, int len, adpcm_state_t *state ){ signed char *inp; /* Input buffer pointer */ short *outp; /* output buffer pointer */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int step; /* Stepsize */ int valpred; /* Predicted value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int inputbuffer; /* place to keep next 4-bit value */ int bufferstep; /* toggle between inputbuffer/input */ outp = outdata; inp = (signed char *)indata; valpred = state->valprev; index = state->index; step = stepsizeTable[index]; bufferstep = 0; for (; len > 0; len-- ) { /* Step 1 - get the delta value */ if ( bufferstep ) { delta = inputbuffer & 0xf; } else { inputbuffer = *inp++; delta = ( inputbuffer >> 4 ) & 0xf; } bufferstep = !bufferstep; /* Step 2 - Find new index value (for later) */ index += indexTable[delta]; if ( index < 0 ) { index = 0; } if ( index > 88 ) { index = 88; } /* Step 3 - Separate sign and magnitude */ sign = delta & 8; delta = delta & 7; /* Step 4 - Compute difference and new predicted value */ /* ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment ** in adpcm_coder. */ vpdiff = step >> 3; if ( delta & 4 ) { vpdiff += step; } if ( delta & 2 ) { vpdiff += step >> 1; } if ( delta & 1 ) { vpdiff += step >> 2; } if ( sign ) { valpred -= vpdiff; } else{ valpred += vpdiff; } /* Step 5 - clamp output value */ if ( valpred > 32767 ) { valpred = 32767; } else if ( valpred < -32768 ) { valpred = -32768; } /* Step 6 - Update step value */ step = stepsizeTable[index]; /* Step 7 - Output value */ *outp++ = valpred; } state->valprev = valpred; state->index = index; } #endif void adpcm_coder( short *inp, adpcm_t *adpcm ){ int val; /* Current input sample value */ int sign; /* Current adpcm sign bit */ int delta; /* Current adpcm output value */ int diff; /* Difference between val and valprev */ int step; /* Stepsize */ int valpred; /* Predicted output value */ int vpdiff; /* Current change to valpred */ int index; /* Current step change index */ int outputbuffer; /* place to keep previous 4-bit value */ int bufferstep; /* toggle between outputbuffer/output */ adpcm_state_t *state; char *outp; int len; state = &adpcm->state; len = state->count; outp = adpcm->adpcm; valpred = state->in_valprev; index = state->in_index; step = stepsizeTable[index]; bufferstep = 1; while ( len-- ) { val = *inp++; /* Step 1 - compute difference with previous value */ diff = val - valpred; sign = ( diff < 0 ) ? 8 : 0; if ( sign ) { diff = -diff; } /* Step 2 - Divide and clamp */ /* Note: ** This code *approximately* computes: ** delta = diff*4/step; ** vpdiff = (delta+0.5)*step/4; ** but in shift step bits are dropped. The net result of this is ** that even if you have fast mul/div hardware you cannot put it to ** good use since the fixup would be too expensive. */ delta = 0; vpdiff = ( step >> 3 ); if ( diff >= step ) { delta = 4; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 2; diff -= step; vpdiff += step; } step >>= 1; if ( diff >= step ) { delta |= 1; vpdiff += step; } /* Step 3 - Update previous value */ if ( sign ) { valpred -= vpdiff; } else{ valpred += vpdiff; } /* Step 4 - Clamp previous value to 16 bits */ if ( valpred > 32767 ) { valpred = 32767; } else if ( valpred < -32768 ) { valpred = -32768; } /* Step 5 - Assemble value, update index and step values */ delta |= sign; index += indexTable[delta]; if ( index < 0 ) { index = 0; } if ( index > 88 ) { index = 88; } step = stepsizeTable[index]; /* Step 6 - Output value */ if ( bufferstep ) { outputbuffer = ( delta << 4 ) & 0xf0; } else{ *outp++ = ( delta & 0x0f ) | outputbuffer; } bufferstep = !bufferstep; } /* Output last step, if needed */ if ( !bufferstep ) { *outp++ = outputbuffer; } state->out_valprev = valpred; state->out_index = index; } 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 = *(long *)data_p; data_p += 4; if ( iff_chunk_len < 0 ) { data_p = NULL; return; } data_p -= 8; last_chunk = data_p + 8 + ( ( iff_chunk_len + 1 ) & ~1 ); if ( !strncmp( 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 = *(long *)data_p; data_p += 4; printf( "0x%x : %s (%d)\n", (int)( 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( data_p + 8, "WAVE", 4 ) ) ) { printf( "Missing RIFF/WAVE chunks\n" ); return( info ); } // get "fmt " chunk iff_data = data_p + 12; FindChunk( "fmt " ); if ( !data_p ) { printf( "Missing fmt chunk\n" ); return( info ); } data_p += 8; format = *(short *)data_p; data_p += 2; if ( format != 1 ) { printf( "Microsoft PCM format only\n" ); return( info ); } info.channels = *(short *)data_p; data_p += 2; info.rate = *(long *)data_p; data_p += 4; data_p += 6; info.width = *(short *)data_p / 8; data_p += 2; // get cue chunk FindChunk( "cue " ); if ( data_p ) { data_p += 32; info.loopstart = *(long *)data_p; data_p += 4; // if the next chunk is a LIST chunk, look for a cue length marker FindNextChunk( "LIST" ); if ( data_p ) { // this is not a proper parse, but it works with cooledit... if ( !strncmp( data_p + 28, "mark", 4 ) ) { data_p += 24; i = *(long *)data_p; // samples in loop data_p += 4; 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 = *(long *)data_p; data_p += 4; 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(){ char name[1024]; FILE *f; int len; soundtrack = NULL; sprintf( name, "%svideo/%s/%s.wav", gamedir, base, base ); printf( "\nLoading sound : %s\n", name ); f = fopen( name, "rb" ); if ( !f ) { printf( "\nNo soundtrack for %s\n", base ); return; } len = Q_filelength( f ); soundtrack = SafeMalloc( len, "LoadSoundtrack" ); fread( soundtrack, 1, len, f ); fclose( f ); wavinfo = GetWavinfo( name, soundtrack, len ); adpcm.state.out_valprev = 0; adpcm.state.out_index = 0; } // ================== // WriteSound // ================== int WriteSound( FILE *output, int frame, int numframes ){ int start, end; int count; int empty = 0; int width; char *work; width = wavinfo.width * wavinfo.channels; start = ( ( frame * wavinfo.rate / 14 ) + 31 ) & 0xffffffe0; // start sample end = ( ( ( frame + numframes ) * wavinfo.rate / 14 ) + 31 ) & 0xffffffe0; // end sample count = end - start; work = soundtrack + wavinfo.dataofs + ( start * width ); adpcm.state.count = count * wavinfo.channels; // Number of samples adpcm.state.in_valprev = adpcm.state.out_valprev; adpcm.state.in_index = adpcm.state.out_index; adpcm_coder( (short *)work, &adpcm ); WriteHeader( output, FC_SOUND_22KMADPCM, FC_ADPCM_VERSION, ( adpcm.state.count / 2 ) + sizeof( adpcm_state_t ), (char *)&adpcm ); return( count / 2 ); } // ============================== // Basic run length encoder // ============================== char *RLEZZ( char *in, char *out ){ int srun; char count; int idx = 0; while ( idx < 64 ) { srun = idx; // Start of run while ( idx < 63 ) { if ( in[LUT_ZZ[idx]] != in[LUT_ZZ[idx + 1]] ) { break; } idx++; } count = (char)( idx - srun ); // count of repeated bytes if ( !count ) { while ( idx < 63 ) { if ( in[LUT_ZZ[idx]] == in[LUT_ZZ[idx + 1]] ) { break; } idx++; } if ( idx == 63 ) { idx++; } count = (char)( idx - srun ); // count of unique bytes *out++ = count; while ( count-- ) *out++ = in[LUT_ZZ[srun++]]; } else { *out++ = -( count + 1 ); *out++ = in[LUT_ZZ[idx]]; idx++; } } return( out ); } // ============================== // Discrete Cosine Transformation // ============================== void init_base( float quant ){ int y, x; for ( y = 0; y < BLOCKSIZE; y++ ) for ( x = 0; x < BLOCKSIZE; x++ ) { if ( y == 0 ) { dctbase[y][x] = 1; } else{ dctbase[y][x] = SQRT2 * cos( ( ( x * 2 + 1 ) * y * M_PI ) / ( BLOCKSIZE * 2 ) ); } } for ( y = 0; y < BLOCKSIZE * BLOCKSIZE; y++ ) Quantise[y] = LUT_Quantise[y] / quant; } void SplitComponents( byte *src, int width, int height ){ int i, j; float *tr = red; float *tg = green; float *tb = blue; for ( i = 0; i < BLOCKSIZE; i++, src += ( width - BLOCKSIZE ) * 4 ) for ( j = 0; j < BLOCKSIZE; j++ ) { *tr++ = ( (float)*src++ ) - 128.0F; *tg++ = ( (float)*src++ ) - 128.0F; *tb++ = ( (float)*src++ ) - 128.0F; src++; } } void transferH( float *src, float *dst ){ int y, dx, dy; float sum; float *work; for ( y = 0; y < BLOCKSIZE; y++, src += BLOCKSIZE ) { for ( dy = 0; dy < BLOCKSIZE; dy++ ) { sum = 0; work = src; for ( dx = 0; dx < BLOCKSIZE; dx++, work++ ) sum += dctbase[dy][dx] * *work; *dst++ = sum / BLOCKSIZE; } } } void transferV( float *src, float *dst ){ int x, dy, fy; float sum; float *work; for ( x = 0; x < BLOCKSIZE; x++, src++, dst++ ) { for ( fy = 0; fy < BLOCKSIZE; fy++ ) { sum = 0; work = src; for ( dy = 0; dy < BLOCKSIZE; dy++, work += BLOCKSIZE ) sum += dctbase[fy][dy] * *work; dst[fy * BLOCKSIZE] = sum / BLOCKSIZE; } } } char *Combine( byte *dst, float *p, float *q ){ int i, j; byte rlesrc[BLOCKSIZE * BLOCKSIZE]; int c; byte *work; work = rlesrc; for ( j = 0; j < BLOCKSIZE; j++ ) for ( i = 0; i < BLOCKSIZE; i++ ) { c = (int)( ( *p++ / *q++ ) + 128.5F ); c -= 128; if ( c < -128 ) { c = -128; } if ( c > 127 ) { c = 127; } *work++ = (char)c; } dst = RLEZZ( rlesrc, dst ); return( dst ); } char *CombineComponents( char *dst, int width, int height ){ dst = Combine( dst, red, Quantise ); dst = Combine( dst, green, Quantise ); dst = Combine( dst, blue, Quantise ); return( dst ); } void DCT( cblock_t *out, cblock_t in, int width, int height ){ int x, y; char *cursrc; char *curdst; curdst = out->data; for ( y = 0; y < height; y += BLOCKSIZE ) for ( x = 0; x < width; x += BLOCKSIZE ) { cursrc = in.data + ( ( y * width ) + x ) * 4; SplitComponents( cursrc, width, height ); transferH( red, temp ); transferV( temp, red ); transferH( green, temp ); transferV( temp, green ); transferH( blue, temp ); transferV( temp, blue ); curdst = CombineComponents( curdst, width, height ); } out->count = curdst - out->data; } // ================== // BuildChars1 // ================== void BuildChars1( int prev, int nodenum, unsigned bits, int bitcount ){ hnode_t *node; if ( nodenum < HUF_TOKENS ) { if ( bitcount > 32 ) { Error( "bitcount > 32" ); } charbits1[prev][nodenum] = bits; charbitscount1[prev][nodenum] = bitcount; return; } node = &hnodes1[prev][nodenum]; bits <<= 1; BuildChars1( prev, node->children[0], bits, bitcount + 1 ); bits |= 1; BuildChars1( prev, node->children[1], bits, bitcount + 1 ); } // ================== // SmallestNode1 // ================== int SmallestNode1( hnode_t *hnodes, int numhnodes ){ int i; int best, bestnode; best = 99999999; bestnode = -1; for ( i = 0; i < numhnodes; i++ ) { if ( hnodes[i].used ) { continue; } if ( !hnodes[i].count ) { continue; } if ( hnodes[i].count < best ) { best = hnodes[i].count; bestnode = i; } } if ( bestnode == -1 ) { return( -1 ); } hnodes[bestnode].used = true; return( bestnode ); } // ================== // BuildTree1 // ================== void BuildTree1( int prev ){ hnode_t *node, *nodebase; int numhnodes; // build the nodes numhnodes = HUF_TOKENS; nodebase = hnodes1[prev]; while ( 1 ) { node = &nodebase[numhnodes]; // pick two lowest counts node->children[0] = SmallestNode1( nodebase, numhnodes ); if ( node->children[0] == -1 ) { break; // no more } node->children[1] = SmallestNode1( nodebase, numhnodes ); if ( node->children[1] == -1 ) { break; } node->count = nodebase[node->children[0]].count + nodebase[node->children[1]].count; numhnodes++; } numhnodes1[prev] = numhnodes - 1; BuildChars1( prev, numhnodes - 1, 0, 0 ); } // ================== // Huffman1_Count // ================== void Huffman1_Count( cblock_t in ){ int i; int prev; int v; int rept; prev = 0; for ( i = 0; i < in.count; i++ ) { v = in.data[i]; order0counts[v]++; hnodes1[prev][v].count++; prev = v; for ( rept = 1; ( i + rept < in.count ) && ( rept < MAX_REPT ); rept++ ) if ( in.data[i + rept] != v ) { break; } if ( rept > MIN_REPT ) { hnodes1[prev][255 + rept].count++; i += rept - 1; } } } // ================== // Huffman1_Build // ================== void Huffman1_Build(){ int i, j, v; int max; int total; for ( i = 0; i < 256; i++ ) { // normalize and save the counts max = 0; for ( j = 0; j < HUF_TOKENS; j++ ) { if ( hnodes1[i][j].count > max ) { max = hnodes1[i][j].count; } } if ( max == 0 ) { max = 1; } total = 0; // easy to overflow 32 bits here! for ( j = 0; j < HUF_TOKENS; j++ ) { v = ( hnodes1[i][j].count * (double) 255 + max - 1 ) / max; if ( v > 255 ) { Error( "v > 255" ); } scaled[i][j] = hnodes1[i][j].count = v; if ( v ) { total++; } } if ( total == 1 ) { // must have two tokens if ( !scaled[i][0] ) { scaled[i][0] = hnodes1[i][0].count = 1; } else{ scaled[i][1] = hnodes1[i][1].count = 1; } } BuildTree1( i ); } } // ================== // Huffman1 // Order 1 compression with pre-built table // ================== cblock_t Huffman1( cblock_t in ){ int i; int outbits, c; unsigned bits; byte *out_p; cblock_t out; int prev; int v; int rept; out_p = out.data = SafeMalloc( ( in.count * 2 ) + 1024 + 4, "Huffman" ); memset( out_p, 0, ( in.count * 2 ) + 1024 + 4 ); // leave space for compressed count out_p += 4; // write count *(long *)out_p = in.count; out_p += 4; // write bits outbits = 0; prev = 0; for ( i = 0; i < in.count; i++ ) { v = in.data[i]; c = charbitscount1[prev][v]; bits = charbits1[prev][v]; if ( !c ) { Error( "!bits" ); } while ( c ) { c--; if ( bits & ( 1 << c ) ) { out_p[outbits >> 3] |= 1 << ( outbits & 7 ); } outbits++; } prev = v; // check for repeat encodes for ( rept = 1; ( i + rept < in.count ) && ( rept < MAX_REPT ); rept++ ) if ( in.data[i + rept] != v ) { break; } if ( rept > MIN_REPT ) { c = charbitscount1[prev][255 + rept]; bits = charbits1[prev][255 + rept]; if ( !c ) { Error( "!bits" ); } while ( c ) { c--; if ( bits & ( 1 << c ) ) { out_p[outbits >> 3] |= 1 << ( outbits & 7 ); } outbits++; } i += rept - 1; } } out_p += ( outbits + 7 ) >> 3; out.count = out_p - out.data; out_p = out.data; *(long *)out_p = out.count; return( out ); } // =================== // LoadFrame // =================== void LoadFrame( cblock_t *out, char *base, int frame ){ cblock_t in; int width, height; char name[1024]; FILE *f; in.data = NULL; in.count = -1; sprintf( name, "%svideo/%s/%s%04i.tga", gamedir, base, base, frame ); f = fopen( name, "rb" ); if ( !f ) { out->data = NULL; return; } fclose( f ); LoadTGA( name, &in.data, &width, &height ); if ( ( width != cinehead.Width ) || ( height != cinehead.Height ) ) { free( in.data ); printf( "Invalid picture size\n" ); out->data = NULL; return; } out->data = SafeMalloc( width * height * 3, "LoadFrame" ); // rle could possibly expand file so this not 100% safe (however DCT should force a lot of compression) DCT( out, in, width, height ); free( in.data ); } // ================================== // Cmd_Video // // video // ================================== void Cmd_Video(){ char savename[256]; char name[256]; FILE *output; int frame; int width, height; cblock_t in, huffman; int size; float dctconst; int maxsize, ssize; int min_rle_size, warnings; int ave_image, ave_sound; GetScriptToken( false ); strcpy( base, token ); if ( g_release ) { return; } GetScriptToken( false ); dctconst = atof( token ); GetScriptToken( false ); maxsize = atoi( token ); sprintf( savename, "%svideo/%s.cin", gamedir, base ); // clear stuff memset( charbits1, 0, sizeof( charbits1 ) ); memset( charbitscount1, 0, sizeof( charbitscount1 ) ); memset( hnodes1, 0, sizeof( hnodes1 ) ); memset( numhnodes1, 0, sizeof( numhnodes1 ) ); memset( order0counts, 0, sizeof( order0counts ) ); // load the entire sound wav file if present LoadSoundtrack(); cinehead.SndRate = wavinfo.rate; cinehead.SndWidth = wavinfo.width; cinehead.SndChannels = wavinfo.channels; sprintf( name, "%svideo/%s/%s0000.tga", gamedir, base, base ); printf( "Loading sequence : %s\n", name ); printf( "DCT constant : %f\n", dctconst ); LoadTGA( name, NULL, &width, &height ); output = fopen( savename, "wb" ); if ( !output ) { Error( "Can't open %s", savename ); } if ( ( width % BLOCKSIZE ) || ( height % BLOCKSIZE ) ) { Error( "Width and height must be a multiple of %d", BLOCKSIZE ); } cinehead.Width = width; cinehead.Height = height; init_base( dctconst ); // build the dictionary printf( "Counting : " ); min_rle_size = 0; for ( frame = 0; ; frame++ ) { printf( "." ); LoadFrame( &in, base, frame ); if ( !in.data ) { break; } Huffman1_Count( in ); if ( in.count > min_rle_size ) { min_rle_size = in.count; } free( in.data ); } printf( "\n" ); cinehead.NumFrames = frame; printf( "Num Frames : %d\n", frame ); cinehead.MaxRleSize = ( min_rle_size + 0x1f ) & 0xfffffe0; cinehead.MaxSndSize = ( ( 4 * wavinfo.rate * wavinfo.channels / 14 ) + 0x1f ) & 0xffffffe0; WriteHeader( output, FC_HEADER_NAME, FC_HEADER_VERSION, sizeof( CineHead_t ), &cinehead ); // build nodes and write counts Huffman1_Build(); WriteHeader( output, FC_HUFFBITS_NAME, FC_HUFFBITS_VERSION, sizeof( scaled ), scaled ); WriteHeader( output, FC_QUANT_NAME, FC_QUANT_VERSION, sizeof( Quantise ), Quantise ); ave_image = 0; ave_sound = 0; warnings = 0; // compress it with the dictionary if ( soundtrack ) { ssize = WriteSound( output, frame, 4 ); ave_sound += ssize; } for ( frame = 0; frame < cinehead.NumFrames; frame++ ) { // save some sound samples printf( "Packing : ", frame ); LoadFrame( &in, base, frame ); // save the image huffman = Huffman1( in ); printf( "%d bytes rle, %d bytes huffman", in.count, huffman.count ); size = ( huffman.count + 3 ) & 0xfffffffc; // round up to longwords if ( size > maxsize ) { printf( " ** WARNING **" ); warnings++; } printf( "\n" ); ave_image += huffman.count; WriteHeader( output, FC_IMAGE_NAME, FC_IMAGE_VERSION, size, huffman.data ); if ( soundtrack ) { ssize = WriteSound( output, frame + 4, 1 ); ave_sound += ssize; } free( in.data ); free( huffman.data ); } printf( "\nTotal size: %d (headers + %d image + %d sound)\n", ftell( output ), ave_image, ave_sound ); printf( "Data rate : %d bytes per sec (image and sound)\n", ( ave_image + ave_sound ) / cinehead.NumFrames ); printf( "Cin created ok with %d warnings.\n", warnings ); fclose( output ); if ( soundtrack ) { free( soundtrack ); } } #endif void Cmd_Video(){ } // end