/* Copyright (C) 1999-2007 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