diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/cl_cin.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/cl_cin.c')
-rw-r--r-- | source/cl_cin.c | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/source/cl_cin.c b/source/cl_cin.c new file mode 100644 index 0000000..441f3c5 --- /dev/null +++ b/source/cl_cin.c @@ -0,0 +1,653 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program 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. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +#include "cl_local.h" + +typedef struct +{ + byte *data; + int count; +} cblock_t; + +typedef struct +{ + qboolean restart_sound; + int s_rate; + int s_width; + int s_channels; + + int width; + int height; + byte *pic; + byte *pic_pending; + + // order 1 huffman stuff + int *hnodes1; // [256][256][2]; + int numhnodes1[256]; + + int h_used[512]; + int h_count[512]; +} cinematics_t; + +cinematics_t cin; + +/* +================================================================= + +PCX LOADING + +================================================================= +*/ + + +/* +============== +SCR_LoadPCX +============== +*/ +void SCR_LoadPCX (char *filename, byte **pic, byte **palette, int *width, int *height) +{ + byte *raw; + pcx_t *pcx; + int x, y; + int len; + int dataByte, runLength; + byte *out, *pix; + + *pic = NULL; + + // + // load the file + // + len = FS_LoadFile (filename, (void **)&raw); + if (!raw) + return; // Com_Printf ("Bad pcx file %s\n", filename); + + // + // parse the PCX file + // + pcx = (pcx_t *)raw; + raw = &pcx->data; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 640 + || pcx->ymax >= 480) + { + Com_Printf ("Bad pcx file %s\n", filename); + return; + } + + out = Z_Malloc ( (pcx->ymax+1) * (pcx->xmax+1) ); + + *pic = out; + + pix = out; + + if (palette) + { + *palette = Z_Malloc(768); + memcpy (*palette, (byte *)pcx + len - 768, 768); + } + + if (width) + *width = pcx->xmax+1; + if (height) + *height = pcx->ymax+1; + + for (y=0 ; y<=pcx->ymax ; y++, pix += pcx->xmax+1) + { + for (x=0 ; x<=pcx->xmax ; ) + { + dataByte = *raw++; + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = *raw++; + } + else + runLength = 1; + + while(runLength-- > 0) + pix[x++] = dataByte; + } + + } + + if ( raw - (byte *)pcx > len) + { + Com_Printf ("PCX file %s was malformed", filename); + Z_Free (*pic); + *pic = NULL; + } + + FS_FreeFile (pcx); +} + +//============================================================= + +/* +================== +SCR_StopCinematic +================== +*/ +void SCR_StopCinematic (void) +{ + cl.cinematictime = 0; // done + if (cin.pic) + { + Z_Free (cin.pic); + cin.pic = NULL; + } + if (cin.pic_pending) + { + Z_Free (cin.pic_pending); + cin.pic_pending = NULL; + } + if (cl.cinematicpalette_active) + { + ref.CinematicSetPalette(NULL); + cl.cinematicpalette_active = qfalse; + } + if (cl.cinematic_file) + { + FS_FCloseFile( cl.cinematic_file ); + cl.cinematic_file = 0; + } + if (cin.hnodes1) + { + Z_Free (cin.hnodes1); + cin.hnodes1 = NULL; + } + + // switch back down to 11 khz sound if necessary + if (cin.restart_sound) + { + cin.restart_sound = qfalse; + CL_Snd_Restart_f (); + } + +} + +/* +==================== +SCR_FinishCinematic + +Called when either the cinematic completes, or it is aborted +==================== +*/ +void SCR_FinishCinematic (void) +{ + if( cls.state < ca_connected || cls.demoplayback ) { + return; + } + // tell the server to advance to the next map / cinematic + CL_ClientCommand( va( "nextserver %i\n", cl.servercount ) ); +} + +//========================================================================== + +/* +================== +SmallestNode1 +================== +*/ +int SmallestNode1 (int numhnodes) +{ + int i; + int best, bestnode; + + best = 99999999; + bestnode = -1; + for (i=0 ; i<numhnodes ; i++) + { + if (cin.h_used[i]) + continue; + if (!cin.h_count[i]) + continue; + if (cin.h_count[i] < best) + { + best = cin.h_count[i]; + bestnode = i; + } + } + + if (bestnode == -1) + return -1; + + cin.h_used[bestnode] = qtrue; + return bestnode; +} + + +/* +================== +Huff1TableInit + +Reads the 64k counts table and initializes the node trees +================== +*/ +void Huff1TableInit (void) +{ + int prev; + int j; + int *node, *nodebase; + byte counts[256]; + int numhnodes; + + cin.hnodes1 = Z_Malloc (256*256*2*4); + memset (cin.hnodes1, 0, 256*256*2*4); + + for (prev=0 ; prev<256 ; prev++) + { + memset (cin.h_count,0,sizeof(cin.h_count)); + memset (cin.h_used,0,sizeof(cin.h_used)); + + // read a row of counts + FS_Read (counts, sizeof(counts), cl.cinematic_file); + for (j=0 ; j<256 ; j++) + cin.h_count[j] = counts[j]; + + // build the nodes + numhnodes = 256; + nodebase = cin.hnodes1 + prev*256*2; + + while (numhnodes != 511) + { + node = nodebase + (numhnodes-256)*2; + + // pick two lowest counts + node[0] = SmallestNode1 (numhnodes); + if (node[0] == -1) + break; // no more + + node[1] = SmallestNode1 (numhnodes); + if (node[1] == -1) + break; + + cin.h_count[numhnodes] = cin.h_count[node[0]] + cin.h_count[node[1]]; + numhnodes++; + } + + cin.numhnodes1[prev] = numhnodes-1; + } +} + +/* +================== +Huff1Decompress +================== +*/ +cblock_t Huff1Decompress (cblock_t in) +{ + byte *input; + byte *out_p; + int nodenum; + int count; + cblock_t out; + int inbyte; + int *hnodes, *hnodesbase; +//int i; + + // get decompressed count + count = in.data[0] + (in.data[1]<<8) + (in.data[2]<<16) + (in.data[3]<<24); + input = in.data + 4; + out_p = out.data = Z_Malloc (count); + + // read bits + + hnodesbase = cin.hnodes1 - 256*2; // nodes 0-255 aren't stored + + hnodes = hnodesbase; + nodenum = cin.numhnodes1[0]; + while (count) + { + inbyte = *input++; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + //----------- + if (nodenum < 256) + { + hnodes = hnodesbase + (nodenum<<9); + *out_p++ = nodenum; + if (!--count) + break; + nodenum = cin.numhnodes1[nodenum]; + } + nodenum = hnodes[nodenum*2 + (inbyte&1)]; + inbyte >>=1; + } + + if (input - in.data != in.count && input - in.data != in.count+1) + { + Com_Printf ("Decompression overread by %i", (input - in.data) - in.count); + } + out.count = out_p - out.data; + + return out; +} + +/* +================== +SCR_ReadNextFrame +================== +*/ +byte *SCR_ReadNextFrame (void) +{ + int r; + int command; + byte samples[22050/14*4]; + byte compressed[0x20000]; + int size; + byte *pic; + cblock_t in, huf1; + int start, end, count; + + // read the next frame + r = FS_Read( &command, 4, cl.cinematic_file ); + + if (r != 4) + return NULL; + command = LittleLong(command); + if (command == 2) + return NULL; // last frame marker + + if (command == 1) + { // read palette + FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); + cl.cinematicpalette_active=0; // dubious.... exposes an edge case + } + + // decompress the next frame + FS_Read (&size, 4, cl.cinematic_file); + size = LittleLong(size); + if (size > sizeof(compressed) || size < 1) + Com_Error (ERR_DROP, "Bad compressed frame size"); + FS_Read (compressed, size, cl.cinematic_file); + + // read sound + start = cl.cinematicframe*cin.s_rate/14; + end = (cl.cinematicframe+1)*cin.s_rate/14; + count = end - start; + + FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file); + + S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples); + + in.data = compressed; + in.count = size; + + huf1 = Huff1Decompress (in); + + pic = huf1.data; + + cl.cinematicframe++; + + return pic; +} + + +/* +================== +SCR_RunCinematic + +================== +*/ +void SCR_RunCinematic (void) +{ + int frame; + + if (cl.cinematictime <= 0) + { + SCR_StopCinematic (); + return; + } + + if (cl.cinematicframe == -1) + return; // static image + + if (cls.key_dest & (KEY_MENU|KEY_CONSOLE)) + { // pause if menu or console is up + cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + return; + } + + frame = (cls.realtime - cl.cinematictime)*14.0/1000; + if (frame <= cl.cinematicframe) + return; + if (frame > cl.cinematicframe+1) + { + Com_Printf ("Dropped frame: %i > %i\n", frame, cl.cinematicframe+1); + cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14; + } + if (cin.pic) + Z_Free (cin.pic); + cin.pic = cin.pic_pending; + cin.pic_pending = NULL; + cin.pic_pending = SCR_ReadNextFrame (); + if (!cin.pic_pending) + { + SCR_StopCinematic (); + SCR_FinishCinematic (); + cl.cinematictime = 1; // hack to get the black screen behind loading + SCR_BeginLoadingPlaque (); + cl.cinematictime = 0; + return; + } +} + +/* +================== +SCR_DrawCinematic + +Returns qtrue if a cinematic is active, meaning the view rendering +should be skipped +================== +*/ +qboolean SCR_DrawCinematic (void) +{ + if (cl.cinematictime <= 0) + { + return qfalse; + } + + if (cls.key_dest & KEY_MENU) + { // blank screen and pause if menu is up + ref.CinematicSetPalette(NULL); + cl.cinematicpalette_active = qfalse; + return qtrue; + } + + if (!cl.cinematicpalette_active) + { + ref.CinematicSetPalette(cl.cinematicpalette); + cl.cinematicpalette_active = qtrue; + } + + if (!cin.pic) + return qtrue; + + ref.DrawStretchRaw (0, 0, scr_glconfig.vidWidth, scr_glconfig.vidHeight, + cin.width, cin.height, cin.pic); + + return qtrue; +} + +/* +================== +SCR_PlayCinematic + +================== +*/ +void SCR_PlayCinematic (const char *arg) +{ + int width, height; + byte *palette; + char name[MAX_QPATH], *dot; + int old_khz; + + Com_DPrintf( "SCR_PlayCinematic( %s )\n", arg ); + + cl.cinematicframe = 0; + dot = strstr (arg, "."); + if (dot && !strcmp (dot, ".pcx")) + { // static pcx image + Com_sprintf (name, sizeof(name), "pics/%s", arg); + SCR_LoadPCX (name, &cin.pic, &palette, &cin.width, &cin.height); + cl.cinematicframe = -1; + cl.cinematictime = 1; + SCR_EndLoadingPlaque (); + cls.state = ca_cinematic; + if (!cin.pic) + { + Com_Printf ("%s not found.\n", name); + cl.cinematictime = 0; + } + else + { + memcpy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette)); + Z_Free (palette); + } + + Con_Close(); + return; + } + + Com_sprintf (name, sizeof(name), "video/%s", arg); + FS_FOpenFile (name, &cl.cinematic_file, FS_MODE_READ); + if (!cl.cinematic_file) + { +// Com_Error (ERR_DROP, "Cinematic %s not found.\n", name); + SCR_FinishCinematic (); + cl.cinematictime = 0; // done + return; + } + + SCR_EndLoadingPlaque (); + + cls.state = ca_cinematic; + + FS_Read (&width, 4, cl.cinematic_file); + FS_Read (&height, 4, cl.cinematic_file); + cin.width = LittleLong(width); + cin.height = LittleLong(height); + + FS_Read (&cin.s_rate, 4, cl.cinematic_file); + cin.s_rate = LittleLong(cin.s_rate); + FS_Read (&cin.s_width, 4, cl.cinematic_file); + cin.s_width = LittleLong(cin.s_width); + FS_Read (&cin.s_channels, 4, cl.cinematic_file); + cin.s_channels = LittleLong(cin.s_channels); + + Huff1TableInit (); + + // switch up to 22 khz sound if necessary + old_khz = Cvar_VariableValue ("s_khz"); + if (old_khz != cin.s_rate/1000) + { + cin.restart_sound = qtrue; + Cvar_SetValue ("s_khz", cin.s_rate/1000); + CL_Snd_Restart_f (); + Cvar_SetValue ("s_khz", old_khz); + } + + cl.cinematicframe = 0; + cin.pic = SCR_ReadNextFrame (); + cl.cinematictime = Sys_Milliseconds (); + + Con_Close(); +} |