mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
1700 lines
35 KiB
C
1700 lines
35 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This file is part of Quake 2 source code.
|
|
|
|
Quake 2 source code 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.
|
|
|
|
Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
|
|
#include "client.h"
|
|
|
|
#define RoQ_INFO 0x1001
|
|
#define RoQ_QUAD_CODEBOOK 0x1002
|
|
#define RoQ_QUAD_VQ 0x1011
|
|
#define RoQ_SOUND_MONO 0x1020
|
|
#define RoQ_SOUND_STEREO 0x1021
|
|
|
|
#define RoQ_ID_MOT 0x0000
|
|
#define RoQ_ID_FCC 0x0001
|
|
#define RoQ_ID_SLD 0x0002
|
|
#define RoQ_ID_CCC 0x0003
|
|
|
|
#ifdef ROQ_SUPPORT
|
|
|
|
typedef struct {
|
|
unsigned short id;
|
|
unsigned int size;
|
|
unsigned short argument;
|
|
} roqChunk_t;
|
|
|
|
typedef struct {
|
|
byte y[4];
|
|
byte u;
|
|
byte v;
|
|
} roqCell_t;
|
|
|
|
typedef struct {
|
|
byte idx[4];
|
|
} roqQCell_t;
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
qboolean playing;
|
|
fileHandle_t file;
|
|
int size;
|
|
int start;
|
|
int remaining;
|
|
qboolean isRoQ;
|
|
int rate;
|
|
|
|
int x;
|
|
int y;
|
|
int w;
|
|
int h;
|
|
int flags;
|
|
|
|
int sndRate;
|
|
int sndWidth;
|
|
int sndChannels;
|
|
|
|
int vidWidth;
|
|
int vidHeight;
|
|
byte *vidBuffer;
|
|
|
|
int rawWidth;
|
|
int rawHeight;
|
|
byte *rawBuffer;
|
|
|
|
int frameCount;
|
|
int frameTime;
|
|
|
|
unsigned palette[256];
|
|
|
|
// PCX stuff
|
|
byte *pcxBuffer;
|
|
|
|
// Order 1 Huffman stuff
|
|
int *hNodes1;
|
|
int hNumNodes1[256];
|
|
|
|
int hUsed[512];
|
|
int hCount[512];
|
|
|
|
byte *hBuffer;
|
|
|
|
// RoQ stuff
|
|
roqChunk_t roqChunk;
|
|
roqCell_t roqCells[256];
|
|
roqQCell_t roqQCells[256];
|
|
|
|
short roqSndSqrTable[256];
|
|
|
|
byte *roqBuffer;
|
|
byte *roqBufferPtr[2];
|
|
} cinematic_t;
|
|
|
|
static cinematic_t cinematics[MAX_CINEMATICS];
|
|
|
|
|
|
typedef struct
|
|
{
|
|
int rendType;
|
|
const char *renderer_string;
|
|
const char *vendor_string;
|
|
const char *version_string;
|
|
const char *extensions_string;
|
|
|
|
// for parsing newer OpenGL versions
|
|
int version_major;
|
|
int version_minor;
|
|
int version_release;
|
|
|
|
qboolean allowCDS;
|
|
// max texture size
|
|
int max_texsize;
|
|
int max_texunits;
|
|
|
|
// non-power of two texture support
|
|
qboolean arbTextureNonPowerOfTwo;
|
|
|
|
qboolean vertexBufferObject;
|
|
qboolean multitexture;
|
|
qboolean mtexcombine; // added Vic's RGB brightening
|
|
|
|
qboolean have_stencil;
|
|
qboolean extStencilWrap;
|
|
qboolean atiSeparateStencil;
|
|
qboolean extStencilTwoSide;
|
|
|
|
qboolean extCompiledVertArray;
|
|
qboolean drawRangeElements;
|
|
|
|
// texture shader support
|
|
qboolean arb_fragment_program;
|
|
qboolean arb_vertex_program;
|
|
qboolean NV_texshaders;
|
|
|
|
// anisotropic filtering
|
|
qboolean anisotropic;
|
|
float max_anisotropy;
|
|
|
|
qboolean newTexFormat; // whether to use GL_RGBA textures / GL_BGRA lightmaps
|
|
} glconfig_t;
|
|
extern glconfig_t glConfig;
|
|
|
|
#if 0
|
|
/*
|
|
=================
|
|
Q_strncat
|
|
|
|
Never goes past bounds or leaves without a terminating 0
|
|
=================
|
|
*/
|
|
void Q_strncat (char *dst, const char *src, int dstSize)
|
|
{
|
|
int len;
|
|
|
|
len = strlen(dst);
|
|
if (len >= dstSize)
|
|
Com_Error(ERR_FATAL, "Q_strncat: already overflowed");
|
|
|
|
Q_strncpyz(dst + len, src, dstSize - len);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
Com_FileExtension
|
|
|
|
Returns the extension, if any (does not include the .)
|
|
=================
|
|
*/
|
|
void Com_FileExtension (const char *path, char *dst, int dstSize)
|
|
{
|
|
const char *s, *last;
|
|
|
|
s = last = path + strlen(path);
|
|
while (*s != '/' && *s != '\\' && s != path){
|
|
if (*s == '.'){
|
|
last = s+1;
|
|
break;
|
|
}
|
|
|
|
s--;
|
|
}
|
|
|
|
Q_strncpyz(dst, last, dstSize);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Com_DefaultExtension
|
|
|
|
If path doesn't have a .EXT, append newExtension (newExtension should
|
|
include the .)
|
|
=================
|
|
*/
|
|
void Com_DefaultExtension (char *path, size_t maxSize, const char *newExtension)
|
|
{
|
|
char *s;
|
|
|
|
s = path + strlen(path);
|
|
while (*s != '/' && *s != '\\' && s != path){
|
|
if (*s == '.')
|
|
return; // It has an extension
|
|
|
|
s--;
|
|
}
|
|
|
|
Q_strncatz(path, newExtension, maxSize);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Com_DefaultPath
|
|
|
|
If path doesn't have a / or \\, append newPath (newPath should not
|
|
include the /)
|
|
=================
|
|
*/
|
|
void Com_DefaultPath (char *path, int maxSize, const char *newPath)
|
|
{
|
|
char oldPath[MAX_OSPATH], *s;
|
|
|
|
s = path;
|
|
while (*s){
|
|
if (*s == '/' || *s == '\\')
|
|
return; // It has a path
|
|
|
|
s++;
|
|
}
|
|
|
|
Q_strncpyz(oldPath, path, sizeof(oldPath));
|
|
Com_sprintf(path, maxSize, "%s/%s", newPath, oldPath);
|
|
}
|
|
|
|
//=============================================================
|
|
|
|
/*
|
|
=================
|
|
CIN_Skip
|
|
=================
|
|
*/
|
|
static void CIN_Skip (cinematic_t *cin, int count)
|
|
{
|
|
FS_Seek(cin->file, count, FS_SEEK_CUR);
|
|
cin->remaining -= count;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CIN_SoundSqrTableInit
|
|
=================
|
|
*/
|
|
static void CIN_SoundSqrTableInit (cinematic_t *cin)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 128; i++){
|
|
cin->roqSndSqrTable[i] = i * i;
|
|
cin->roqSndSqrTable[i+128] = -(i * i);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadChunk
|
|
=================
|
|
*/
|
|
static void CIN_ReadChunk (cinematic_t *cin)
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
|
|
FS_Read(&chunk->id, sizeof(chunk->id), cin->file);
|
|
FS_Read(&chunk->size, sizeof(chunk->size), cin->file);
|
|
FS_Read(&chunk->argument, sizeof(chunk->argument), cin->file);
|
|
|
|
chunk->id = LittleShort(chunk->id);
|
|
chunk->size = LittleLong(chunk->size);
|
|
chunk->argument = LittleShort(chunk->argument);
|
|
|
|
cin->remaining -= sizeof(roqChunk_t);
|
|
|
|
//if (cin->frameCount < 20)
|
|
// Com_Printf("read ROQ chunk of size %u, id %u, argument %u, %u remaining\n", chunk->size, chunk->id, chunk->argument, cin->remaining);
|
|
}
|
|
|
|
#define Clamp(a,b,c) (((a) < (b)) ? (b) : ((a) > (c)) ? (c) : (a))
|
|
/*
|
|
=================
|
|
CIN_ReadInfo
|
|
=================
|
|
*/
|
|
static void CIN_ReadInfo (cinematic_t *cin)
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
short data[4];
|
|
|
|
FS_Read(data, sizeof(data), cin->file);
|
|
cin->remaining -= sizeof(data);
|
|
|
|
cin->vidWidth = LittleShort(data[0]);
|
|
cin->vidHeight = LittleShort(data[1]);
|
|
|
|
if (cin->roqBuffer)
|
|
Z_Free(cin->roqBuffer);
|
|
|
|
cin->roqBuffer = Z_Malloc(cin->vidWidth * cin->vidHeight * 4 * 2);
|
|
|
|
cin->roqBufferPtr[0] = cin->roqBuffer;
|
|
cin->roqBufferPtr[1] = cin->roqBuffer + cin->vidWidth * cin->vidHeight * 4;
|
|
|
|
cin->rawWidth = Clamp(cin->vidWidth, 1, glConfig.max_texsize); // was 512
|
|
cin->rawHeight = Clamp(cin->vidHeight, 1, glConfig.max_texsize); // was 512
|
|
|
|
if (cin->rawWidth != cin->vidWidth || cin->rawHeight != cin->vidHeight)
|
|
{
|
|
if (cin->rawBuffer)
|
|
Z_Free(cin->rawBuffer);
|
|
|
|
cin->rawBuffer = Z_Malloc(cin->rawWidth * cin->rawHeight * 4);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadCodebook
|
|
=================
|
|
*/
|
|
static void CIN_ReadCodebook (cinematic_t *cin)
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
int nv1, nv2;
|
|
|
|
nv1 = (chunk->argument >> 8) & 0xff;
|
|
if (!nv1)
|
|
nv1 = 256;
|
|
|
|
nv2 = chunk->argument & 0xff;
|
|
if (!nv2 && (nv1 * 6 < chunk->size))
|
|
nv2 = 256;
|
|
|
|
FS_Read(cin->roqCells, sizeof(roqCell_t) * nv1, cin->file);
|
|
FS_Read(cin->roqQCells, sizeof(roqQCell_t) * nv2, cin->file);
|
|
cin->remaining -= chunk->size;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_DecodeBlock
|
|
=================
|
|
*/
|
|
static void CIN_DecodeBlock (byte *dst0, byte *dst1, const byte *src0, const byte *src1, float u, float v)
|
|
{
|
|
int rgb[3];
|
|
|
|
// Convert YCbCr to RGB
|
|
rgb[0] = 1.402 * v;
|
|
rgb[1] = -0.34414 * u - 0.71414 * v;
|
|
rgb[2] = 1.772 * u;
|
|
|
|
// 1st pixel
|
|
dst0[0] = Clamp(rgb[0] + src0[0], 0, 255);
|
|
dst0[1] = Clamp(rgb[1] + src0[0], 0, 255);
|
|
dst0[2] = Clamp(rgb[2] + src0[0], 0, 255);
|
|
dst0[3] = 255;
|
|
|
|
// 2nd pixel
|
|
dst0[4] = Clamp(rgb[0] + src0[1], 0, 255);
|
|
dst0[5] = Clamp(rgb[1] + src0[1], 0, 255);
|
|
dst0[6] = Clamp(rgb[2] + src0[1], 0, 255);
|
|
dst0[7] = 255;
|
|
|
|
// 3rd pixel
|
|
dst1[0] = Clamp(rgb[0] + src1[0], 0, 255);
|
|
dst1[1] = Clamp(rgb[1] + src1[0], 0, 255);
|
|
dst1[2] = Clamp(rgb[2] + src1[0], 0, 255);
|
|
dst1[3] = 255;
|
|
|
|
// 4th pixel
|
|
dst1[4] = Clamp(rgb[0] + src1[1], 0, 255);
|
|
dst1[5] = Clamp(rgb[1] + src1[1], 0, 255);
|
|
dst1[6] = Clamp(rgb[2] + src1[1], 0, 255);
|
|
dst1[7] = 255;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ApplyVector2x2
|
|
=================
|
|
*/
|
|
static void CIN_ApplyVector2x2 (cinematic_t *cin, int x, int y, const roqCell_t *cell)
|
|
{
|
|
byte *dst0, *dst1;
|
|
|
|
dst0 = cin->roqBufferPtr[0] + (y * cin->vidWidth + x) * 4;
|
|
dst1 = dst0 + cin->vidWidth * 4;
|
|
|
|
CIN_DecodeBlock(dst0, dst1, cell->y, cell->y+2, (float)((int)cell->u - 128), (float)((int)cell->v - 128));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ApplyVector4x4
|
|
=================
|
|
*/
|
|
static void CIN_ApplyVector4x4 (cinematic_t *cin, int x, int y, const roqCell_t *cell)
|
|
{
|
|
byte *dst0, *dst1;
|
|
byte yp[4];
|
|
float u, v;
|
|
|
|
u = (float)((int)cell->u - 128);
|
|
v = (float)((int)cell->v - 128);
|
|
|
|
yp[0] = yp[1] = cell->y[0];
|
|
yp[2] = yp[3] = cell->y[1];
|
|
|
|
dst0 = cin->roqBufferPtr[0] + (y * cin->vidWidth + x) * 4;
|
|
dst1 = dst0 + cin->vidWidth * 4;
|
|
|
|
CIN_DecodeBlock(dst0, dst0+8, yp, yp+2, u, v);
|
|
CIN_DecodeBlock(dst1, dst1+8, yp, yp+2, u, v);
|
|
|
|
yp[0] = yp[1] = cell->y[2];
|
|
yp[2] = yp[3] = cell->y[3];
|
|
|
|
dst0 += cin->vidWidth * 4 * 2;
|
|
dst1 += cin->vidWidth * 4 * 2;
|
|
|
|
CIN_DecodeBlock(dst0, dst0+8, yp, yp+2, u, v);
|
|
CIN_DecodeBlock(dst1, dst1+8, yp, yp+2, u, v);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ApplyMotion4x4
|
|
=================
|
|
*/
|
|
static void CIN_ApplyMotion4x4 (cinematic_t *cin, int x, int y, byte mv, char meanX, char meanY)
|
|
{
|
|
byte *src, *dst;
|
|
int x1, y1;
|
|
int i;
|
|
|
|
x1 = x + 8 - (mv >> 4) - meanX;
|
|
y1 = y + 8 - (mv & 15) - meanY;
|
|
|
|
src = cin->roqBufferPtr[1] + (y1 * cin->vidWidth + x1) * 4;
|
|
dst = cin->roqBufferPtr[0] + (y * cin->vidWidth + x) * 4;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
memcpy(dst, src, 4 * 4);
|
|
|
|
src += cin->vidWidth * 4;
|
|
dst += cin->vidWidth * 4;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ApplyMotion8x8
|
|
=================
|
|
*/
|
|
static void CIN_ApplyMotion8x8 (cinematic_t *cin, int x, int y, byte mv, char meanX, char meanY)
|
|
{
|
|
byte *src, *dst;
|
|
int x1, y1;
|
|
int i;
|
|
|
|
x1 = x + 8 - (mv >> 4) - meanX;
|
|
y1 = y + 8 - (mv & 15) - meanY;
|
|
|
|
src = cin->roqBufferPtr[1] + (y1 * cin->vidWidth + x1) * 4;
|
|
dst = cin->roqBufferPtr[0] + (y * cin->vidWidth + x) * 4;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
memcpy(dst, src, 8 * 4);
|
|
|
|
src += cin->vidWidth * 4;
|
|
dst += cin->vidWidth * 4;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_SmallestNode1
|
|
=================
|
|
*/
|
|
static int CIN_SmallestNode1 (cinematic_t *cin, int numNodes)
|
|
{
|
|
int i;
|
|
int best, bestNode;
|
|
|
|
best = 99999999;
|
|
bestNode = -1;
|
|
|
|
for (i = 0; i < numNodes; i++){
|
|
if (cin->hUsed[i])
|
|
continue;
|
|
if (!cin->hCount[i])
|
|
continue;
|
|
if (cin->hCount[i] < best){
|
|
best = cin->hCount[i];
|
|
bestNode = i;
|
|
}
|
|
}
|
|
|
|
if (bestNode == -1)
|
|
return -1;
|
|
|
|
cin->hUsed[bestNode] = true;
|
|
return bestNode;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_Huff1TableInit
|
|
|
|
Reads the 64k counts table and initializes the node trees
|
|
=================
|
|
*/
|
|
static void CIN_Huff1TableInit (cinematic_t *cin)
|
|
{
|
|
int prev;
|
|
int j;
|
|
int *node, *nodeBase;
|
|
byte counts[256];
|
|
int numNodes;
|
|
|
|
if (!cin->hNodes1)
|
|
cin->hNodes1 = Z_Malloc(256 * 256 * 4 * 2);
|
|
|
|
for (prev = 0; prev < 256; prev++)
|
|
{
|
|
memset(cin->hCount, 0, sizeof(cin->hCount));
|
|
memset(cin->hUsed, 0, sizeof(cin->hUsed));
|
|
|
|
// Read a row of counts
|
|
FS_Read(counts, sizeof(counts), cin->file);
|
|
cin->remaining -= sizeof(counts);
|
|
|
|
for (j = 0; j < 256; j++)
|
|
cin->hCount[j] = counts[j];
|
|
|
|
// Build the nodes
|
|
numNodes = 256;
|
|
nodeBase = cin->hNodes1 + prev*256*2;
|
|
|
|
while (numNodes != 511)
|
|
{
|
|
node = nodeBase + (numNodes-256)*2;
|
|
|
|
// Pick two lowest counts
|
|
node[0] = CIN_SmallestNode1(cin, numNodes);
|
|
if (node[0] == -1)
|
|
break;
|
|
|
|
node[1] = CIN_SmallestNode1(cin, numNodes);
|
|
if (node[1] == -1)
|
|
break;
|
|
|
|
cin->hCount[numNodes] = cin->hCount[node[0]] + cin->hCount[node[1]];
|
|
numNodes++;
|
|
}
|
|
|
|
cin->hNumNodes1[prev] = numNodes-1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_Huff1Decompress
|
|
=================
|
|
*/
|
|
static void CIN_Huff1Decompress (cinematic_t *cin, const byte *data, int size)
|
|
{
|
|
const byte *input;
|
|
unsigned *out;
|
|
int count;
|
|
int in;
|
|
int nodeNum;
|
|
int *nodes, *nodesBase;
|
|
|
|
if (!cin->hBuffer)
|
|
cin->hBuffer = Z_Malloc(cin->vidWidth * cin->vidHeight * 4);
|
|
|
|
// Get decompressed count
|
|
count = data[0] + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
|
|
input = data + 4;
|
|
out = (unsigned *)cin->hBuffer;
|
|
|
|
// Read bits
|
|
nodesBase = cin->hNodes1 - 256*2; // Nodes 0-255 aren't stored
|
|
|
|
nodes = nodesBase;
|
|
nodeNum = cin->hNumNodes1[0];
|
|
while (count)
|
|
{
|
|
in = *input++;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
|
|
if (nodeNum < 256)
|
|
{
|
|
nodes = nodesBase + (nodeNum<<9);
|
|
*out++ = cin->palette[nodeNum];
|
|
if (!--count)
|
|
break;
|
|
nodeNum = cin->hNumNodes1[nodeNum];
|
|
}
|
|
nodeNum = nodes[nodeNum*2 + (in&1)];
|
|
in >>= 1;
|
|
}
|
|
|
|
if (input - data != size && input - data != size+1)
|
|
Com_Error(ERR_DROP, "CIN_Huff1Decompress: decompression overread by %i", (input - data) - size);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadPalette
|
|
=================
|
|
*/
|
|
static void CIN_ReadPalette (cinematic_t *cin)
|
|
{
|
|
int i;
|
|
byte palette[768], *pal;
|
|
|
|
FS_Read(palette, sizeof(palette), cin->file);
|
|
cin->remaining -= sizeof(palette);
|
|
|
|
pal = (byte *)cin->palette;
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
pal[i*4+0] = palette[i*3+0];
|
|
pal[i*4+1] = palette[i*3+1];
|
|
pal[i*4+2] = palette[i*3+2];
|
|
pal[i*4+3] = 255;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ResampleFrame
|
|
=================
|
|
*/
|
|
static void CIN_ResampleFrame (cinematic_t *cin)
|
|
{
|
|
int i, j;
|
|
unsigned *src, *dst;
|
|
int frac, fracStep;
|
|
|
|
if (cin->rawWidth == cin->vidWidth && cin->rawHeight == cin->vidHeight)
|
|
return;
|
|
|
|
dst = (unsigned *)cin->rawBuffer;
|
|
fracStep = cin->vidWidth * 0x10000 / cin->rawWidth;
|
|
|
|
for (i = 0; i < cin->rawHeight; i++, dst += cin->rawWidth)
|
|
{
|
|
src = (unsigned *)cin->vidBuffer + cin->vidWidth * (i * cin->vidHeight / cin->rawHeight);
|
|
frac = fracStep >> 1;
|
|
|
|
for (j = 0; j < cin->rawWidth; j++)
|
|
{
|
|
dst[j] = src[frac>>16];
|
|
frac += fracStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadVideoFrame
|
|
=================
|
|
*/
|
|
static void CIN_ReadVideoFrame (cinematic_t *cin)
|
|
{
|
|
if (!cin->isRoQ)
|
|
{
|
|
byte compressed[0x20000];
|
|
int size;
|
|
|
|
FS_Read(&size, sizeof(size), cin->file);
|
|
cin->remaining -= sizeof(size);
|
|
|
|
size = LittleLong(size);
|
|
if (size < 1 || size > sizeof(compressed))
|
|
Com_Error(ERR_DROP, "CIN_ReadVideoFrame: bad compressed frame size (%i)", size);
|
|
|
|
FS_Read(compressed, size, cin->file);
|
|
cin->remaining -= size;
|
|
|
|
CIN_Huff1Decompress(cin, compressed, size);
|
|
|
|
cin->vidBuffer = cin->hBuffer;
|
|
}
|
|
else
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
roqQCell_t *qcell;
|
|
int i, vqFlgPos, vqId, pos, xPos, yPos, x, y, xp, yp;
|
|
short vqFlg;
|
|
byte c[4], *tmp;
|
|
|
|
vqFlg = 0;
|
|
vqFlgPos = -1;
|
|
|
|
xPos = yPos = 0;
|
|
pos = chunk->size;
|
|
|
|
while (pos > 0)
|
|
{
|
|
for (yp = yPos; yp < yPos + 16; yp += 8)
|
|
{
|
|
for (xp = xPos; xp < xPos + 16; xp += 8)
|
|
{
|
|
if (vqFlgPos < 0)
|
|
{
|
|
FS_Read(&vqFlg, sizeof(vqFlg), cin->file);
|
|
pos -= sizeof(vqFlg);
|
|
|
|
vqFlg = LittleShort(vqFlg);
|
|
vqFlgPos = 7;
|
|
}
|
|
|
|
vqId = (vqFlg >> (vqFlgPos * 2)) & 0x3;
|
|
vqFlgPos--;
|
|
|
|
switch (vqId)
|
|
{
|
|
case RoQ_ID_MOT:
|
|
|
|
break;
|
|
case RoQ_ID_FCC:
|
|
FS_Read(c, 1, cin->file);
|
|
pos--;
|
|
|
|
CIN_ApplyMotion8x8(cin, xp, yp, c[0], (char)((chunk->argument >> 8) & 0xff), (char)(chunk->argument & 0xff));
|
|
|
|
break;
|
|
case RoQ_ID_SLD:
|
|
FS_Read(c, 1, cin->file);
|
|
pos--;
|
|
|
|
qcell = cin->roqQCells + c[0];
|
|
CIN_ApplyVector4x4(cin, xp, yp, cin->roqCells + qcell->idx[0]);
|
|
CIN_ApplyVector4x4(cin, xp+4, yp, cin->roqCells + qcell->idx[1]);
|
|
CIN_ApplyVector4x4(cin, xp, yp+4, cin->roqCells + qcell->idx[2]);
|
|
CIN_ApplyVector4x4(cin, xp+4, yp+4, cin->roqCells + qcell->idx[3]);
|
|
|
|
break;
|
|
case RoQ_ID_CCC:
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
x = xp;
|
|
y = yp;
|
|
|
|
if (i & 0x01)
|
|
x += 4;
|
|
if (i & 0x02)
|
|
y += 4;
|
|
|
|
if (vqFlgPos < 0)
|
|
{
|
|
FS_Read(&vqFlg, sizeof(vqFlg), cin->file);
|
|
pos -= sizeof(vqFlg);
|
|
|
|
vqFlg = LittleShort(vqFlg);
|
|
vqFlgPos = 7;
|
|
}
|
|
|
|
vqId = (vqFlg >> (vqFlgPos * 2)) & 0x3;
|
|
vqFlgPos--;
|
|
|
|
switch (vqId)
|
|
{
|
|
case RoQ_ID_MOT:
|
|
|
|
break;
|
|
case RoQ_ID_FCC:
|
|
FS_Read(c, 1, cin->file);
|
|
pos--;
|
|
|
|
CIN_ApplyMotion4x4(cin, x, y, c[0], (char)((chunk->argument >> 8) & 0xff), (char)(chunk->argument & 0xff));
|
|
|
|
break;
|
|
case RoQ_ID_SLD:
|
|
FS_Read(c, 1, cin->file);
|
|
pos--;
|
|
|
|
qcell = cin->roqQCells + c[0];
|
|
CIN_ApplyVector2x2(cin, x, y, cin->roqCells + qcell->idx[0]);
|
|
CIN_ApplyVector2x2(cin, x+2, y, cin->roqCells + qcell->idx[1]);
|
|
CIN_ApplyVector2x2(cin, x, y+2, cin->roqCells + qcell->idx[2]);
|
|
CIN_ApplyVector2x2(cin, x+2, y+2, cin->roqCells + qcell->idx[3]);
|
|
|
|
break;
|
|
case RoQ_ID_CCC:
|
|
FS_Read(&c, 4, cin->file);
|
|
pos -= 4;
|
|
|
|
CIN_ApplyVector2x2(cin, x, y, cin->roqCells + c[0]);
|
|
CIN_ApplyVector2x2(cin, x+2, y, cin->roqCells + c[1]);
|
|
CIN_ApplyVector2x2(cin, x, y+2, cin->roqCells + c[2]);
|
|
CIN_ApplyVector2x2(cin, x+2, y+2, cin->roqCells + c[3]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
Com_Error(ERR_DROP, "CIN_ReadVideoFrame: unknown VQ code (%i)", vqId);
|
|
}
|
|
}
|
|
}
|
|
|
|
xPos += 16;
|
|
if (xPos >= cin->vidWidth)
|
|
{
|
|
xPos -= cin->vidWidth;
|
|
yPos += 16;
|
|
}
|
|
if (yPos >= cin->vidHeight && pos)
|
|
{
|
|
CIN_Skip(cin, pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
cin->remaining -= (chunk->size - pos);
|
|
|
|
if (cin->frameCount == 0)
|
|
memcpy(cin->roqBufferPtr[1], cin->roqBufferPtr[0], cin->vidWidth * cin->vidHeight * 4);
|
|
else
|
|
{
|
|
tmp = cin->roqBufferPtr[0];
|
|
cin->roqBufferPtr[0] = cin->roqBufferPtr[1];
|
|
cin->roqBufferPtr[1] = tmp;
|
|
}
|
|
|
|
cin->vidBuffer = cin->roqBufferPtr[1];
|
|
}
|
|
|
|
// Resample
|
|
CIN_ResampleFrame(cin);
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadAudioFrame
|
|
=================
|
|
*/
|
|
static void CIN_ReadAudioFrame (cinematic_t *cin)
|
|
{
|
|
byte data[0x40000];
|
|
int samples;
|
|
|
|
if (!cin->isRoQ)
|
|
{
|
|
byte *p;
|
|
int start, end, len;
|
|
|
|
start = cin->frameCount*cin->sndRate/14;
|
|
end = (cin->frameCount+1)*cin->sndRate/14;
|
|
samples = end - start;
|
|
len = samples * cin->sndWidth * cin->sndChannels;
|
|
|
|
if (cin->flags & CIN_SILENT)
|
|
{
|
|
CIN_Skip(cin, len);
|
|
return;
|
|
}
|
|
|
|
// HACK: gross hack to keep cinematic audio sync'ed using OpenAL
|
|
if (cin->frameCount == 0)
|
|
{
|
|
samples += 4096;
|
|
|
|
if (cin->sndWidth == 2)
|
|
memset(data, 0x00, 4096 * cin->sndWidth * cin->sndChannels);
|
|
else
|
|
memset(data, 0x80, 4096 * cin->sndWidth * cin->sndChannels);
|
|
|
|
p = data + (4096 * cin->sndWidth * cin->sndChannels);
|
|
}
|
|
else
|
|
p = data;
|
|
|
|
FS_Read(p, len, cin->file);
|
|
cin->remaining -= len;
|
|
}
|
|
else
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
byte compressed[0x20000];
|
|
short l, r;
|
|
int i;
|
|
|
|
if (cin->flags & CIN_SILENT)
|
|
{
|
|
CIN_Skip(cin, cin->roqChunk.size);
|
|
return;
|
|
}
|
|
|
|
FS_Read(compressed, chunk->size, cin->file);
|
|
cin->remaining -= chunk->size;
|
|
|
|
if (chunk->id == RoQ_SOUND_MONO)
|
|
{
|
|
cin->sndChannels = 1;
|
|
|
|
l = chunk->argument;
|
|
|
|
for (i = 0; i < chunk->size; i++)
|
|
{
|
|
l += cin->roqSndSqrTable[compressed[i]];
|
|
|
|
((short *)&data)[i] = l;
|
|
}
|
|
|
|
samples = chunk->size;
|
|
}
|
|
else if (chunk->id == RoQ_SOUND_STEREO)
|
|
{
|
|
cin->sndChannels = 2;
|
|
|
|
l = chunk->argument & 0xff00;
|
|
r = (chunk->argument & 0xff) << 8;
|
|
|
|
for (i = 0; i < chunk->size; i += 2)
|
|
{
|
|
l += cin->roqSndSqrTable[compressed[i+0]];
|
|
r += cin->roqSndSqrTable[compressed[i+1]];
|
|
|
|
((short *)&data)[i+0] = l;
|
|
((short *)&data)[i+1] = r;
|
|
}
|
|
|
|
samples = chunk->size / 2;
|
|
}
|
|
}
|
|
|
|
// Send sound to mixer
|
|
//S_StreamRawSamples(data, samples, cin->sndRate, cin->sndWidth, cin->sndChannels);
|
|
S_RawSamples(samples, cin->sndRate, cin->sndWidth, cin->sndChannels, data, false);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_ReadNextFrame
|
|
=================
|
|
*/
|
|
static qboolean CIN_ReadNextFrame (cinematic_t *cin)
|
|
{
|
|
if (!cin->isRoQ)
|
|
{
|
|
int command;
|
|
|
|
while (cin->remaining > 0)
|
|
{
|
|
FS_Read(&command, sizeof(command), cin->file);
|
|
cin->remaining -= sizeof(command);
|
|
|
|
command = LittleLong(command);
|
|
|
|
if (cin->remaining <= 0 || command == 2)
|
|
return false; // Done
|
|
|
|
if (command == 1)
|
|
CIN_ReadPalette(cin);
|
|
|
|
CIN_ReadVideoFrame(cin);
|
|
CIN_ReadAudioFrame(cin);
|
|
|
|
cin->frameCount++;
|
|
cl.cinematicframe = cin->frameCount;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
roqChunk_t *chunk = &cin->roqChunk;
|
|
|
|
while (cin->remaining > 0)
|
|
{
|
|
CIN_ReadChunk(cin);
|
|
|
|
if (cin->remaining <= 0 || chunk->size > cin->remaining)
|
|
return false; // Done
|
|
|
|
if (chunk->size <= 0)
|
|
continue;
|
|
|
|
if (chunk->id == RoQ_INFO)
|
|
CIN_ReadInfo(cin);
|
|
else if (chunk->id == RoQ_QUAD_CODEBOOK)
|
|
CIN_ReadCodebook(cin);
|
|
else if (chunk->id == RoQ_QUAD_VQ)
|
|
{
|
|
CIN_ReadVideoFrame(cin);
|
|
|
|
cin->frameCount++;
|
|
cl.cinematicframe = cin->frameCount;
|
|
return true;
|
|
}
|
|
else if (chunk->id == RoQ_SOUND_MONO || chunk->id == RoQ_SOUND_STEREO)
|
|
CIN_ReadAudioFrame(cin);
|
|
else
|
|
CIN_Skip(cin, cin->roqChunk.size);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_StaticCinematic
|
|
=================
|
|
*/
|
|
static qboolean CIN_StaticCinematic (cinematic_t *cin, const char *name)
|
|
{
|
|
byte *buffer;
|
|
byte *in, *out;
|
|
pcx_t *pcx;
|
|
int x, y, len;
|
|
int dataByte, runLength;
|
|
byte palette[768], *pal;
|
|
int i;
|
|
|
|
// Load the file
|
|
len = FS_LoadFile((char *)name, (void **)&buffer);
|
|
if (!buffer)
|
|
return false;
|
|
|
|
// Parse the PCX file
|
|
pcx = (pcx_t *)buffer;
|
|
|
|
pcx->xmin = LittleShort(pcx->xmin);
|
|
pcx->ymin = LittleShort(pcx->ymin);
|
|
pcx->xmax = LittleShort(pcx->xmax);
|
|
pcx->ymax = LittleShort(pcx->ymax);
|
|
pcx->hres = LittleShort(pcx->hres);
|
|
pcx->vres = LittleShort(pcx->vres);
|
|
pcx->bytes_per_line = LittleShort(pcx->bytes_per_line);
|
|
pcx->palette_type = LittleShort(pcx->palette_type);
|
|
|
|
in = &pcx->data;
|
|
|
|
if (pcx->manufacturer != 0x0A || pcx->version != 5 || pcx->encoding != 1)
|
|
{
|
|
FS_FreeFile(buffer);
|
|
Com_Error(ERR_DROP, "CIN_StaticCinematic: invalid PCX header");
|
|
}
|
|
|
|
if (pcx->bits_per_pixel != 8 || pcx->color_planes != 1){
|
|
FS_FreeFile(buffer);
|
|
Com_Error(ERR_DROP, "CIN_StaticCinematic: only 8 bit PCX images supported");
|
|
}
|
|
|
|
if (pcx->xmax >= 640 || pcx->ymax >= 480 || pcx->xmax <= 0 || pcx->ymax <= 0)
|
|
{
|
|
FS_FreeFile(buffer);
|
|
Com_Error(ERR_DROP, "CIN_StaticCinematic: bad PCX file (%i x %i)", pcx->xmax, pcx->ymax);
|
|
}
|
|
|
|
memcpy(palette, (byte *)buffer + len - 768, 768);
|
|
|
|
pal = (byte *)cin->palette;
|
|
for (i = 0; i < 256; i++){
|
|
pal[i*4+0] = palette[i*3+0];
|
|
pal[i*4+1] = palette[i*3+1];
|
|
pal[i*4+2] = palette[i*3+2];
|
|
pal[i*4+3] = 255;
|
|
}
|
|
|
|
cin->vidWidth = pcx->xmax+1;
|
|
cin->vidHeight = pcx->ymax+1;
|
|
|
|
cin->pcxBuffer = out = Z_Malloc(cin->vidWidth * cin->vidHeight * 4);
|
|
|
|
for (y = 0; y <= pcx->ymax; y++)
|
|
{
|
|
for (x = 0; x <= pcx->xmax; )
|
|
{
|
|
dataByte = *in++;
|
|
|
|
if((dataByte & 0xC0) == 0xC0)
|
|
{
|
|
runLength = dataByte & 0x3F;
|
|
dataByte = *in++;
|
|
}
|
|
else
|
|
runLength = 1;
|
|
|
|
while (runLength-- > 0)
|
|
{
|
|
*(unsigned *)out = cin->palette[dataByte];
|
|
|
|
out += 4;
|
|
x++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in - buffer > len)
|
|
{
|
|
FS_FreeFile(buffer);
|
|
Z_Free(cin->pcxBuffer);
|
|
cin->pcxBuffer = NULL;
|
|
Com_Error(ERR_DROP, "CIN_StaticCinematic: PCX file was malformed");
|
|
}
|
|
|
|
FS_FreeFile(buffer);
|
|
|
|
cin->vidBuffer = cin->pcxBuffer;
|
|
|
|
if (glConfig.arbTextureNonPowerOfTwo) {
|
|
cin->rawWidth = cin->vidWidth;
|
|
cin->rawHeight = cin->vidHeight;
|
|
}
|
|
else {
|
|
cin->rawWidth = 256;
|
|
cin->rawHeight = 256;
|
|
}
|
|
|
|
if (cin->rawWidth != cin->vidWidth || cin->rawHeight != cin->vidHeight)
|
|
cin->rawBuffer = Z_Malloc(cin->rawWidth * cin->rawHeight * 4);
|
|
|
|
// Resample
|
|
CIN_ResampleFrame(cin);
|
|
|
|
cin->frameCount = -1;
|
|
cin->frameTime = cls.realtime;
|
|
cl.cinematicframe = cin->frameCount;
|
|
cl.cinematictime = cin->frameTime;
|
|
|
|
cin->playing = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_HandleForVideo
|
|
=================
|
|
*/
|
|
static cinematic_t *CIN_HandleForVideo (cinHandle_t *handle)
|
|
{
|
|
cinematic_t *cin;
|
|
int i;
|
|
|
|
for (i = 0, cin = cinematics; i < MAX_CINEMATICS; i++, cin++)
|
|
{
|
|
if (cin->playing)
|
|
continue;
|
|
|
|
*handle = i+1;
|
|
return cin;
|
|
}
|
|
|
|
Com_Error(ERR_DROP, "CIN_HandleForVideo: none free\n");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_GetVideoByHandle
|
|
=================
|
|
*/
|
|
static cinematic_t *CIN_GetVideoByHandle (cinHandle_t handle)
|
|
{
|
|
if (handle <= 0 || handle > MAX_CINEMATICS)
|
|
Com_Error(ERR_DROP, "CIN_GetVideoByHandle: out of range");
|
|
|
|
return &cinematics[handle-1];
|
|
}
|
|
|
|
/*
|
|
==================
|
|
SCR_RunCinematic
|
|
==================
|
|
*/
|
|
void SCR_RunCinematic (void)
|
|
{
|
|
// Do nothing
|
|
//CIN_RunCinematic(cls.cinematicHandle);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_RunCinematic
|
|
=================
|
|
*/
|
|
qboolean CIN_RunCinematic (cinHandle_t handle)
|
|
{
|
|
cinematic_t *cin;
|
|
int frame;
|
|
|
|
cin = CIN_GetVideoByHandle(handle);
|
|
|
|
if (!cin->playing)
|
|
return false; // Not running
|
|
|
|
if (cin->frameCount == -1)
|
|
return true; // Static image
|
|
|
|
/*if (cls.key_dest != key_game)
|
|
{ // pause if menu or console is up
|
|
//cl.cinematictime = cls.realtime - cl.cinematicframe*1000/14;
|
|
cin->frameTime = cls.realtime - cin->frameTime*1000/14;
|
|
cl.cinematictime = cin->frameTime;
|
|
return true;
|
|
}*/
|
|
|
|
frame = (cls.realtime - cin->frameTime) * cin->rate/1000;
|
|
if (frame <= cin->frameCount)
|
|
return true;
|
|
|
|
if (frame > cin->frameCount+1)
|
|
cin->frameTime = cls.realtime - cin->frameCount * 1000/cin->rate;
|
|
|
|
if (!CIN_ReadNextFrame(cin))
|
|
{
|
|
if (cin->flags & CIN_LOOPED)
|
|
{
|
|
// Restart the cinematic
|
|
FS_Seek(cin->file, 0, FS_SEEK_SET);
|
|
cin->remaining = cin->size;
|
|
|
|
// Skip over the header
|
|
CIN_Skip(cin, cin->start);
|
|
|
|
cin->frameCount = 0;
|
|
cin->frameTime = cls.realtime;
|
|
cl.cinematicframe = cin->frameCount;
|
|
cl.cinematictime = cin->frameTime;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false; // Finished
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_SetExtents
|
|
=================
|
|
*/
|
|
void CIN_SetExtents (cinHandle_t handle, int x, int y, int w, int h)
|
|
{
|
|
cinematic_t *cin;
|
|
float realx, realy, realw, realh;
|
|
|
|
cin = CIN_GetVideoByHandle(handle);
|
|
|
|
if (!cin->playing)
|
|
return; // Not running
|
|
|
|
realx = x; realy = y; realw = w; realh = h;
|
|
SCR_AdjustFrom640 (&realx, &realy, &realw, &realh, ALIGN_CENTER);
|
|
cin->x = realx;
|
|
cin->y = realy;
|
|
cin->w = realw;
|
|
cin->h = realh;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SCR_PlayCinematic
|
|
=================
|
|
*/
|
|
void SCR_PlayCinematic (char *name)
|
|
{
|
|
char filename[MAX_QPATH];
|
|
|
|
// If currently playing another, stop it
|
|
SCR_StopCinematic();
|
|
|
|
Com_DPrintf("SCR_PlayCinematic( %s )\n", name);
|
|
|
|
cl.cinematicframe = 0;
|
|
if (!Q_stricmp(name+strlen(name)-4, ".pcx"))
|
|
{
|
|
Q_strncpyz(filename, name, sizeof(filename));
|
|
Com_DefaultPath(filename, sizeof(filename), "pics");
|
|
cl.cinematicframe = -1;
|
|
cl.cinematictime = 1;
|
|
SCR_EndLoadingPlaque ();
|
|
cls.state = ca_active;
|
|
}
|
|
else
|
|
{
|
|
Q_strncpyz(filename, name, sizeof(filename));
|
|
Com_DefaultPath(filename, sizeof(filename), "video");
|
|
Com_DefaultExtension(filename, sizeof(filename), ".cin");
|
|
}
|
|
|
|
// cls.cinematicHandle = CIN_PlayCinematic(filename, 0, 0, viddef.width, viddef.height, CIN_SYSTEM);
|
|
cls.cinematicHandle = CIN_PlayCinematic(filename, 0, 0, 640, 480, CIN_SYSTEM);
|
|
if (!cls.cinematicHandle)
|
|
{
|
|
Com_Printf("Cinematic %s not found\n", filename);
|
|
cl.cinematictime = 0; // done
|
|
SCR_FinishCinematic();
|
|
}
|
|
else
|
|
{
|
|
SCR_EndLoadingPlaque ();
|
|
cls.state = ca_active;
|
|
cl.cinematicframe = 0;
|
|
cl.cinematictime = Sys_Milliseconds ();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SCR_StopCinematic
|
|
=================
|
|
*/
|
|
void SCR_StopCinematic (void)
|
|
{
|
|
if (!cls.cinematicHandle)
|
|
return;
|
|
|
|
Com_DPrintf("SCR_StopCinematic()\n");
|
|
|
|
CIN_StopCinematic(cls.cinematicHandle);
|
|
cls.cinematicHandle = 0;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
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));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SCR_DrawCinematic
|
|
=================
|
|
*/
|
|
qboolean SCR_DrawCinematic (void)
|
|
{
|
|
|
|
if (cl.cinematictime <= 0)
|
|
return false;
|
|
|
|
if (!cls.cinematicHandle)
|
|
return false;
|
|
|
|
if (!CIN_RunCinematic(cls.cinematicHandle))
|
|
{
|
|
SCR_StopCinematic();
|
|
SCR_FinishCinematic();
|
|
return false;
|
|
}
|
|
|
|
// CIN_SetExtents(cls.cinematicHandle, 0, 0, viddef.width, viddef.height);
|
|
CIN_SetExtents(cls.cinematicHandle, 0, 0, 640, 480);
|
|
CIN_DrawCinematic(cls.cinematicHandle);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_DrawCinematic
|
|
=================
|
|
*/
|
|
void CIN_DrawCinematic (cinHandle_t handle){
|
|
|
|
cinematic_t *cin;
|
|
|
|
cin = CIN_GetVideoByHandle(handle);
|
|
|
|
if (!cin->playing)
|
|
return; // Not running
|
|
|
|
if (cin->frameCount == -1) // Knightmare- HACK to show JPG endscreens
|
|
{
|
|
char picname[MAX_QPATH] = "/";
|
|
float x=0, y=0, w=640, h=480;
|
|
// strncat(picname, cin->name);
|
|
Q_strncatz(picname, cin->name, sizeof(picname));
|
|
SCR_AdjustFrom640 (&x, &y, &w, &h, ALIGN_CENTER);
|
|
if (w < viddef.width || h < viddef.height)
|
|
R_DrawFill (0, 0, viddef.width, viddef.height, 0, 0, 0, 255);
|
|
// R_DrawStretchPic (x, y, viddef.width, viddef.height, picname, 1.0);
|
|
R_DrawStretchPic (x, y, w, h, picname, 1.0);
|
|
return;
|
|
} // end JPG hack
|
|
|
|
if (cin->w < viddef.width || cin->h < viddef.height)
|
|
R_DrawFill (0, 0, viddef.width, viddef.height, 0, 0, 0, 255);
|
|
if (cin->rawWidth == cin->vidWidth && cin->rawHeight == cin->vidHeight)
|
|
R_DrawStretchRaw(cin->x, cin->y, cin->w, cin->h, cin->vidBuffer, cin->vidWidth, cin->vidHeight); //(cin->flags & CIN_SHADER));
|
|
else
|
|
R_DrawStretchRaw(cin->x, cin->y, cin->w, cin->h, cin->rawBuffer, cin->rawWidth, cin->rawHeight); //(cin->flags & CIN_SHADER));
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_PlayCinematic
|
|
=================
|
|
*/
|
|
cinHandle_t CIN_PlayCinematic (const char *name, int x, int y, int w, int h, int flags)
|
|
{
|
|
cinematic_t *cin;
|
|
cinHandle_t handle;
|
|
int i;
|
|
char extension[8];
|
|
float realx, realy, realw, realh;
|
|
|
|
// See if already playing this cinematic
|
|
for (i = 0, cin = cinematics; i < MAX_CINEMATICS; i++, cin++)
|
|
{
|
|
if (!cin->playing)
|
|
continue;
|
|
|
|
if (!Q_stricmp(cin->name, (char *)name))
|
|
return i+1;
|
|
}
|
|
|
|
Com_FileExtension(name, extension, sizeof(extension));
|
|
|
|
if (!Q_stricmp(extension, "cin")) // RoQ autoreplace hack
|
|
{
|
|
char s[MAX_QPATH];
|
|
int len;
|
|
len = strlen(name);
|
|
// strncpy (s, name);
|
|
Q_strncpyz (s, name, sizeof(s));
|
|
s[len-3]='r'; s[len-2]='o'; s[len-1]='q';
|
|
handle = CIN_PlayCinematic (s, x, y ,w, h, flags);
|
|
if (handle)
|
|
return handle;
|
|
}
|
|
|
|
// Find a free handle
|
|
cin = CIN_HandleForVideo(&handle);
|
|
|
|
// Fill it in
|
|
Q_strncpyz(cin->name, name, sizeof(cin->name));
|
|
realx = x; realy = y; realw = w; realh = h;
|
|
SCR_AdjustFrom640 (&realx, &realy, &realw, &realh, ALIGN_CENTER);
|
|
cin->x = realx;
|
|
cin->y = realy;
|
|
cin->w = realw;
|
|
cin->h = realh;
|
|
cin->flags = flags;
|
|
|
|
if (cin->flags & CIN_SYSTEM)
|
|
{
|
|
CDAudio_Stop(); // Make sure CD audio isn't playing
|
|
S_StopAllSounds(); // Make sure sound isn't playing
|
|
//UI_SetActiveMenu(UI_CLOSEMENU);
|
|
UI_ForceMenuOff(); // Close the menu
|
|
}
|
|
|
|
//Com_FileExtension(name, extension, sizeof(extension));
|
|
|
|
if (!Q_stricmp(extension, "pcx"))
|
|
{
|
|
// Static PCX image
|
|
if (!CIN_StaticCinematic(cin, name))
|
|
return 0;
|
|
|
|
return handle;
|
|
}
|
|
|
|
if (!Q_strcasecmp(extension, "roq"))
|
|
{
|
|
cin->isRoQ = true;
|
|
cin->rate = 30;
|
|
}
|
|
else if (!Q_stricmp(extension, "cin"))
|
|
{
|
|
cin->isRoQ = false;
|
|
cin->rate = 14;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
// Open the cinematic file
|
|
cin->size = FS_FOpenFile(name, &cin->file, FS_READ);
|
|
if (!cin->file)
|
|
return 0;
|
|
|
|
cin->remaining = cin->size;
|
|
|
|
// Read the header
|
|
if (!cin->isRoQ)
|
|
{
|
|
FS_Read(&cin->vidWidth, sizeof(cin->vidWidth), cin->file);
|
|
FS_Read(&cin->vidHeight, sizeof(cin->vidHeight), cin->file);
|
|
cin->vidWidth = LittleLong(cin->vidWidth);
|
|
cin->vidHeight = LittleLong(cin->vidHeight);
|
|
|
|
FS_Read(&cin->sndRate, sizeof(cin->sndRate), cin->file);
|
|
FS_Read(&cin->sndWidth, sizeof(cin->sndWidth), cin->file);
|
|
FS_Read(&cin->sndChannels, sizeof(cin->sndChannels), cin->file);
|
|
cin->sndRate = LittleLong(cin->sndRate);
|
|
cin->sndWidth = LittleLong(cin->sndWidth);
|
|
cin->sndChannels = LittleLong(cin->sndChannels);
|
|
|
|
cin->remaining -= 20;
|
|
|
|
if (glConfig.arbTextureNonPowerOfTwo) {
|
|
cin->rawWidth = cin->vidWidth;
|
|
cin->rawHeight = cin->vidHeight;
|
|
}
|
|
else {
|
|
cin->rawWidth = 256;
|
|
cin->rawHeight = 256;
|
|
}
|
|
|
|
if (cin->rawWidth != cin->vidWidth || cin->rawHeight != cin->vidHeight)
|
|
cin->rawBuffer = Z_Malloc(cin->rawWidth * cin->rawHeight * 4);
|
|
|
|
CIN_Huff1TableInit(cin);
|
|
|
|
cin->start = FS_FTell(cin->file);
|
|
}
|
|
else
|
|
{
|
|
cin->sndRate = 22050;
|
|
cin->sndWidth = 2;
|
|
|
|
CIN_ReadChunk(cin);
|
|
|
|
CIN_SoundSqrTableInit(cin);
|
|
|
|
cin->start = FS_FTell(cin->file);
|
|
}
|
|
|
|
//if (!(cin->flags & CIN_SILENT))
|
|
// S_StartStreaming();
|
|
|
|
cin->frameCount = 0;
|
|
cin->frameTime = cls.realtime;
|
|
cl.cinematicframe = cin->frameCount;
|
|
cl.cinematictime = cin->frameTime;
|
|
|
|
cin->playing = true;
|
|
|
|
// Read the first frame
|
|
CIN_ReadNextFrame(cin);
|
|
|
|
return handle;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CIN_StopCinematic
|
|
=================
|
|
*/
|
|
void CIN_StopCinematic (cinHandle_t handle)
|
|
{
|
|
cinematic_t *cin;
|
|
|
|
if (!handle)
|
|
return;
|
|
|
|
cin = CIN_GetVideoByHandle(handle);
|
|
|
|
if (!cin->playing)
|
|
return; // Not running
|
|
|
|
//if (!(cin->flags & CIN_SILENT))
|
|
// S_StopStreaming();
|
|
|
|
if (cin->rawBuffer)
|
|
Z_Free(cin->rawBuffer);
|
|
|
|
if (cin->pcxBuffer)
|
|
Z_Free(cin->pcxBuffer);
|
|
|
|
if (cin->hNodes1)
|
|
Z_Free(cin->hNodes1);
|
|
if (cin->hBuffer)
|
|
Z_Free(cin->hBuffer);
|
|
|
|
if (cin->roqBuffer)
|
|
Z_Free(cin->roqBuffer);
|
|
|
|
if (cin->file)
|
|
FS_FCloseFile(cin->file);
|
|
|
|
memset(cin, 0, sizeof(*cin));
|
|
}
|
|
#endif // ROQ_SUPPORT
|