mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-29 15:11:54 +00:00
9998050654
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant@183 8a3a26a2-13c4-0310-b231-cf6edde360e5
1149 lines
24 KiB
C
1149 lines
24 KiB
C
/*
|
|
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 <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
|
|
|