--- /dev/null
+/*
+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 <directory> <framedigits>
+// ==================================
+
+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
+