551 lines
11 KiB
C
551 lines
11 KiB
C
/*
|
|
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 "client.h"
|
|
|
|
typedef struct
|
|
{
|
|
byte *data;
|
|
int count;
|
|
} cblock_t;
|
|
|
|
typedef struct
|
|
{
|
|
bool 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;
|
|
/*
|
|
==================
|
|
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)
|
|
{
|
|
re->CinematicSetPalette(NULL);
|
|
cl.cinematicpalette_active = false;
|
|
}
|
|
if (cl.cinematic_file)
|
|
{
|
|
FS_Close (cl.cinematic_file);
|
|
cl.cinematic_file = NULL;
|
|
}
|
|
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 = false;
|
|
CL_Snd_Restart_f ();
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
====================
|
|
SCR_FinishCinematic
|
|
|
|
Called when either the cinematic completes, or it is aborted
|
|
====================
|
|
*/
|
|
void SCR_FinishCinematic (void)
|
|
{
|
|
// tell the server to advance to the next map / cinematic
|
|
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
|
SZ_Print (&cls.netchan.message, 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] = true;
|
|
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 (cl.cinematic_file, counts, sizeof(counts));
|
|
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)
|
|
{
|
|
Msg ("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 (cl.cinematic_file, &command, 4 );
|
|
if (r == 0) // we'll give it one more chance
|
|
r = FS_Read (cl.cinematic_file, &command, 4);
|
|
|
|
if (!r) return NULL;
|
|
command = LittleLong(command);
|
|
if (command == 2)
|
|
return NULL; // last frame marker
|
|
|
|
if (command == 1)
|
|
{ // read palette
|
|
FS_Read (cl.cinematic_file, cl.cinematicpalette, sizeof(cl.cinematicpalette));
|
|
cl.cinematicpalette_active=0; // dubious.... exposes an edge case
|
|
}
|
|
|
|
// decompress the next frame
|
|
FS_Read (cl.cinematic_file, &size, 4);
|
|
size = LittleLong(size);
|
|
if (size > sizeof(compressed) || size < 1)
|
|
Com_Error (ERR_DROP, "Bad compressed frame size");
|
|
FS_Read (cl.cinematic_file, compressed, size);
|
|
|
|
// read sound
|
|
start = cl.cinematicframe*cin.s_rate/14;
|
|
end = (cl.cinematicframe+1)*cin.s_rate/14;
|
|
count = end - start;
|
|
|
|
FS_Read (cl.cinematic_file, samples, count*cin.s_width*cin.s_channels);
|
|
|
|
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_game)
|
|
{ // 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)
|
|
{
|
|
Msg ("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 true if a cinematic is active, meaning the view rendering
|
|
should be skipped
|
|
==================
|
|
*/
|
|
bool SCR_DrawCinematic (void)
|
|
{
|
|
if (cl.cinematictime <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (cls.key_dest == key_menu)
|
|
{
|
|
// blank screen and pause if menu is up
|
|
re->CinematicSetPalette( NULL );
|
|
cl.cinematicpalette_active = false;
|
|
return true;
|
|
}
|
|
|
|
if (!cl.cinematicpalette_active)
|
|
{
|
|
re->CinematicSetPalette(cl.cinematicpalette);
|
|
cl.cinematicpalette_active = true;
|
|
}
|
|
|
|
if (!cin.pic)
|
|
return true;
|
|
|
|
re->DrawStretchRaw (0, 0, viddef.width, viddef.height, cin.width, cin.height, cin.pic);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SCR_PlayCinematic
|
|
|
|
==================
|
|
*/
|
|
void SCR_PlayCinematic (char *arg)
|
|
{
|
|
int width, height;
|
|
byte *palette;
|
|
char name[MAX_OSPATH];
|
|
int old_khz;
|
|
rgbdata_t *pic = NULL;
|
|
const char *ext = FS_FileExtension( arg );
|
|
|
|
cl.cinematicframe = 0;
|
|
|
|
if (!strcmp (ext, "pcx"))
|
|
{
|
|
pic = FS_LoadImage( va("textures/base_menu/%s", arg), NULL, 0 );
|
|
|
|
if(pic)
|
|
{
|
|
cin.pic = pic->buffer;
|
|
palette = pic->palette;
|
|
cin.width = pic->width;
|
|
cin.height = pic->height;
|
|
|
|
cl.cinematicframe = -1;
|
|
cl.cinematictime = 1;
|
|
|
|
SCR_EndLoadingPlaque ();
|
|
|
|
cls.state = ca_active;
|
|
Mem_Copy (cl.cinematicpalette, palette, sizeof(cl.cinematicpalette));
|
|
}
|
|
else Com_Error (ERR_DROP, "%s not found.\n", arg );
|
|
return;
|
|
}
|
|
|
|
sprintf (name, "video/%s", arg);
|
|
cl.cinematic_file = FS_Open(name, "rb" );
|
|
if (!cl.cinematic_file)
|
|
{
|
|
SCR_FinishCinematic ();
|
|
cl.cinematictime = 0; // done
|
|
return;
|
|
}
|
|
|
|
SCR_EndLoadingPlaque ();
|
|
|
|
cls.state = ca_active;
|
|
|
|
FS_Read (cl.cinematic_file, &width, 4);
|
|
FS_Read (cl.cinematic_file, &height, 4);
|
|
cin.width = LittleLong(width);
|
|
cin.height = LittleLong(height);
|
|
|
|
FS_Read (cl.cinematic_file, &cin.s_rate, 4);
|
|
cin.s_rate = LittleLong(cin.s_rate);
|
|
FS_Read (cl.cinematic_file, &cin.s_width, 4);
|
|
cin.s_width = LittleLong(cin.s_width);
|
|
FS_Read (cl.cinematic_file, &cin.s_channels, 4);
|
|
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 = true;
|
|
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 ();
|
|
}
|