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