q3rally/engine/code/client/cl_cin.c

1979 lines
52 KiB
C
Raw Normal View History

2011-02-18 14:31:32 +00:00
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena 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 III Arena 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 III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/*****************************************************************************
* name: cl_cin.c
*
* desc: video and cinematic playback
*
* $Archive: /MissionPack/code/client/cl_cin.c $
*
* cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256
*
*****************************************************************************/
#include "client.h"
#include "snd_local.h"
#define MAXSIZE 8
#define MINSIZE 4
#define DEFAULT_CIN_WIDTH 512
#define DEFAULT_CIN_HEIGHT 512
#define ROQ_QUAD 0x1000
#define ROQ_QUAD_INFO 0x1001
#define ROQ_CODEBOOK 0x1002
#define ROQ_QUAD_VQ 0x1011
#define ROQ_QUAD_JPEG 0x1012
#define ROQ_QUAD_HANG 0x1013
#define ROQ_PACKET 0x1030
#define ZA_SOUND_MONO 0x1020
#define ZA_SOUND_STEREO 0x1021
#define MAX_VIDEO_HANDLES 16
static void RoQ_init( void );
/******************************************************************************
*
* Class: trFMV
*
* Description: RoQ/RnR manipulation routines
* not entirely complete for first run
*
******************************************************************************/
static long ROQ_YY_tab[256];
static long ROQ_UB_tab[256];
static long ROQ_UG_tab[256];
static long ROQ_VG_tab[256];
static long ROQ_VR_tab[256];
static unsigned short vq2[256*16*4];
static unsigned short vq4[256*64*4];
static unsigned short vq8[256*256*4];
2022-03-19 19:19:51 +00:00
typedef enum {
FT_ROQ = 0, // normal roq (vq3 stuff)
FT_OGM // ogm(ogg wrapper, vorbis audio, xvid/theora video) for WoP
} filetype_t;
2011-02-18 14:31:32 +00:00
typedef struct {
byte linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2];
byte file[65536];
short sqrTable[256];
int mcomp[256];
byte *qStatus[2][32768];
long oldXOff, oldYOff, oldysize, oldxsize;
int currentHandle;
} cinematics_t;
typedef struct {
char fileName[MAX_OSPATH];
int CIN_WIDTH, CIN_HEIGHT;
int xpos, ypos, width, height;
qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader;
fileHandle_t iFile;
e_status status;
int startTime;
int lastTime;
2011-02-18 14:31:32 +00:00
long tfps;
long RoQPlayed;
long ROQSize;
unsigned int RoQFrameSize;
long onQuad;
long numQuads;
long samplesPerLine;
unsigned int roq_id;
long screenDelta;
void ( *VQ0)(byte *status, void *qdata );
void ( *VQ1)(byte *status, void *qdata );
void ( *VQNormal)(byte *status, void *qdata );
void ( *VQBuffer)(byte *status, void *qdata );
long samplesPerPixel; // defaults to 2
byte* gray;
unsigned int xsize, ysize, maxsize, minsize;
qboolean half, smootheddouble, inMemory;
long normalBuffer0;
long roq_flags;
long roqF0;
long roqF1;
long t[2];
long roqFPS;
int playonwalls;
byte* buf;
long drawX, drawY;
2022-03-19 19:19:51 +00:00
filetype_t fileType;
2011-02-18 14:31:32 +00:00
} cin_cache;
static cinematics_t cin;
static cin_cache cinTable[MAX_VIDEO_HANDLES];
static int currentHandle = -1;
static int CL_handle = -1;
extern int s_soundtime; // sample PAIRS
void CIN_CloseAllVideos(void) {
int i;
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
if (cinTable[i].fileName[0] != 0 ) {
CIN_StopCinematic(i);
}
}
}
static int CIN_HandleForVideo(void) {
int i;
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
if ( cinTable[i].fileName[0] == 0 ) {
return i;
}
}
Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" );
return -1;
}
extern int CL_ScaledMilliseconds(void);
//-----------------------------------------------------------------------------
// RllSetupTable
//
// Allocates and initializes the square table.
//
// Parameters: None
//
// Returns: Nothing
//-----------------------------------------------------------------------------
static void RllSetupTable( void )
{
int z;
for (z=0;z<128;z++) {
cin.sqrTable[z] = (short)(z*z);
cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]);
}
}
//-----------------------------------------------------------------------------
// RllDecodeMonoToMono
//
// Decode mono source data into a mono buffer.
//
// Parameters: from -> buffer holding encoded data
// to -> buffer to hold decoded data
// size = number of bytes of input (= # of shorts of output)
// signedOutput = 0 for unsigned output, non-zero for signed output
// flag = flags from asset header
//
// Returns: Number of samples placed in output buffer
//-----------------------------------------------------------------------------
long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag)
{
unsigned int z;
int prev;
if (signedOutput)
prev = flag - 0x8000;
else
prev = flag;
for (z=0;z<size;z++) {
prev = to[z] = (short)(prev + cin.sqrTable[from[z]]);
}
return size; //*sizeof(short));
}
//-----------------------------------------------------------------------------
// RllDecodeMonoToStereo
//
// Decode mono source data into a stereo buffer. Output is 4 times the number
// of bytes in the input.
//
// Parameters: from -> buffer holding encoded data
// to -> buffer to hold decoded data
// size = number of bytes of input (= 1/4 # of bytes of output)
// signedOutput = 0 for unsigned output, non-zero for signed output
// flag = flags from asset header
//
// Returns: Number of samples placed in output buffer
//-----------------------------------------------------------------------------
long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag)
{
unsigned int z;
int prev;
if (signedOutput)
prev = flag - 0x8000;
else
prev = flag;
for (z = 0; z < size; z++) {
prev = (short)(prev + cin.sqrTable[from[z]]);
to[z*2+0] = to[z*2+1] = (short)(prev);
}
return size; // * 2 * sizeof(short));
}
//-----------------------------------------------------------------------------
// RllDecodeStereoToStereo
//
// Decode stereo source data into a stereo buffer.
//
// Parameters: from -> buffer holding encoded data
// to -> buffer to hold decoded data
// size = number of bytes of input (= 1/2 # of bytes of output)
// signedOutput = 0 for unsigned output, non-zero for signed output
// flag = flags from asset header
//
// Returns: Number of samples placed in output buffer
//-----------------------------------------------------------------------------
long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
{
unsigned int z;
unsigned char *zz = from;
int prevL, prevR;
if (signedOutput) {
prevL = (flag & 0xff00) - 0x8000;
prevR = ((flag & 0x00ff) << 8) - 0x8000;
} else {
prevL = flag & 0xff00;
prevR = (flag & 0x00ff) << 8;
}
for (z=0;z<size;z+=2) {
prevL = (short)(prevL + cin.sqrTable[*zz++]);
prevR = (short)(prevR + cin.sqrTable[*zz++]);
to[z+0] = (short)(prevL);
to[z+1] = (short)(prevR);
}
return (size>>1); //*sizeof(short));
}
//-----------------------------------------------------------------------------
// RllDecodeStereoToMono
//
// Decode stereo source data into a mono buffer.
//
// Parameters: from -> buffer holding encoded data
// to -> buffer to hold decoded data
// size = number of bytes of input (= # of bytes of output)
// signedOutput = 0 for unsigned output, non-zero for signed output
// flag = flags from asset header
//
// Returns: Number of samples placed in output buffer
//-----------------------------------------------------------------------------
long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
{
unsigned int z;
int prevL,prevR;
if (signedOutput) {
prevL = (flag & 0xff00) - 0x8000;
prevR = ((flag & 0x00ff) << 8) -0x8000;
} else {
prevL = flag & 0xff00;
prevR = (flag & 0x00ff) << 8;
}
for (z=0;z<size;z+=1) {
prevL= prevL + cin.sqrTable[from[z*2]];
prevR = prevR + cin.sqrTable[from[z*2+1]];
to[z] = (short)((prevL + prevR)/2);
}
return size;
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void move8_32( byte *src, byte *dst, int spl )
{
int i;
for(i = 0; i < 8; ++i)
{
memcpy(dst, src, 32);
src += spl;
dst += spl;
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void move4_32( byte *src, byte *dst, int spl )
{
int i;
for(i = 0; i < 4; ++i)
{
memcpy(dst, src, 16);
src += spl;
dst += spl;
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void blit8_32( byte *src, byte *dst, int spl )
{
int i;
for(i = 0; i < 8; ++i)
{
memcpy(dst, src, 32);
src += 32;
dst += spl;
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void blit4_32( byte *src, byte *dst, int spl )
{
2022-03-19 19:19:51 +00:00
int i;
2011-02-18 14:31:32 +00:00
for(i = 0; i < 4; ++i)
{
memmove(dst, src, 16);
src += 16;
dst += spl;
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void blit2_32( byte *src, byte *dst, int spl )
{
memcpy(dst, src, 8);
memcpy(dst+spl, src+8, 8);
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void blitVQQuad32fs( byte **status, unsigned char *data )
{
unsigned short newd, celdata, code;
unsigned int index, i;
int spl;
newd = 0;
celdata = 0;
index = 0;
spl = cinTable[currentHandle].samplesPerLine;
do {
if (!newd) {
newd = 7;
celdata = data[0] + data[1]*256;
data += 2;
} else {
newd--;
}
code = (unsigned short)(celdata&0xc000);
celdata <<= 2;
switch (code) {
case 0x8000: // vq code
blit8_32( (byte *)&vq8[(*data)*128], status[index], spl );
data++;
index += 5;
break;
case 0xc000: // drop
index++; // skip 8x8
for(i=0;i<4;i++) {
if (!newd) {
newd = 7;
celdata = data[0] + data[1]*256;
data += 2;
} else {
newd--;
}
code = (unsigned short)(celdata&0xc000); celdata <<= 2;
switch (code) { // code in top two bits of code
case 0x8000: // 4x4 vq code
blit4_32( (byte *)&vq4[(*data)*32], status[index], spl );
data++;
break;
case 0xc000: // 2x2 vq code
blit2_32( (byte *)&vq2[(*data)*8], status[index], spl );
data++;
blit2_32( (byte *)&vq2[(*data)*8], status[index]+8, spl );
data++;
blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2, spl );
data++;
blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2+8, spl );
data++;
break;
case 0x4000: // motion compensation
move4_32( status[index] + cin.mcomp[(*data)], status[index], spl );
data++;
break;
}
index++;
}
break;
case 0x4000: // motion compensation
move8_32( status[index] + cin.mcomp[(*data)], status[index], spl );
data++;
index += 5;
break;
case 0x0000:
index += 5;
break;
}
} while ( status[index] != NULL );
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
2022-03-19 19:19:51 +00:00
void ROQ_GenYUVTables( void )
2011-02-18 14:31:32 +00:00
{
float t_ub,t_vr,t_ug,t_vg;
long i;
t_ub = (1.77200f/2.0f) * (float)(1<<6) + 0.5f;
t_vr = (1.40200f/2.0f) * (float)(1<<6) + 0.5f;
t_ug = (0.34414f/2.0f) * (float)(1<<6) + 0.5f;
t_vg = (0.71414f/2.0f) * (float)(1<<6) + 0.5f;
for(i=0;i<256;i++) {
float x = (float)(2 * i - 255);
ROQ_UB_tab[i] = (long)( ( t_ub * x) + (1<<5));
ROQ_VR_tab[i] = (long)( ( t_vr * x) + (1<<5));
ROQ_UG_tab[i] = (long)( (-t_ug * x) );
ROQ_VG_tab[i] = (long)( (-t_vg * x) + (1<<5));
ROQ_YY_tab[i] = (long)( (i << 6) | (i >> 2) );
}
}
#define VQ2TO4(a,b,c,d) { \
*c++ = a[0]; \
*d++ = a[0]; \
*d++ = a[0]; \
*c++ = a[1]; \
*d++ = a[1]; \
*d++ = a[1]; \
*c++ = b[0]; \
*d++ = b[0]; \
*d++ = b[0]; \
*c++ = b[1]; \
*d++ = b[1]; \
*d++ = b[1]; \
*d++ = a[0]; \
*d++ = a[0]; \
*d++ = a[1]; \
*d++ = a[1]; \
*d++ = b[0]; \
*d++ = b[0]; \
*d++ = b[1]; \
*d++ = b[1]; \
a += 2; b += 2; }
#define VQ2TO2(a,b,c,d) { \
*c++ = *a; \
*d++ = *a; \
*d++ = *a; \
*c++ = *b; \
*d++ = *b; \
*d++ = *b; \
*d++ = *a; \
*d++ = *a; \
*d++ = *b; \
*d++ = *b; \
a++; b++; }
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static unsigned short yuv_to_rgb( long y, long u, long v )
{
long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
r = (YY + ROQ_VR_tab[v]) >> 9;
g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8;
b = (YY + ROQ_UB_tab[u]) >> 9;
if (r<0) r = 0;
if (g<0) g = 0;
if (b<0) b = 0;
if (r > 31) r = 31;
if (g > 63) g = 63;
if (b > 31) b = 31;
2011-02-18 14:31:32 +00:00
return (unsigned short)((r<<11)+(g<<5)+(b));
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static unsigned int yuv_to_rgb24( long y, long u, long v )
{
long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
r = (YY + ROQ_VR_tab[v]) >> 6;
g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6;
b = (YY + ROQ_UB_tab[u]) >> 6;
if (r<0) r = 0;
if (g<0) g = 0;
if (b<0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
2011-02-18 14:31:32 +00:00
ioquake3 resync to revision 3511 from 3444. This updates from SDL 2.0.4 to SDL 2.0.8. Fix nullptr dereference in front of nullptr check in FS_CheckPak0 Fix undefined behaviour due to shifting signed in snd_mem.c Fix shifting bits out of byte in tr_font.c Fix shift into sign in cl_cin.c Fix signed bit operations in MSG_ReadBits Add missing address operator in cm_polylib.c OpenGL1: Decay float[8] to float * in tr_marks.c Avoid srcList[-1] in snd_openal.c Fix the behaviour of CVAR_LATCH|CVAR_CHEAT cvars Maximize cURL buffer size Fix mouse grab after toggling fullscreen Fix q3history buffer not cleared between mods and OOB-access Revert "Removed "Color Depth" from q3_ui system settings, it didn't control anything." Fix displayed color/depth/stencil bits values Restore setting r_colorbits in q3_ui Make setting r_stencilbits more consistent in Team Arena UI Fix map list in Team Arena start server menu after entering SP menu Support SDL audio devices that require float32 samples. sdl_snd.c should just initialize SDL audio without checking SDL_WasInit(). There's no need to SDL_PauseAudio(1) before calling SDL_CloseAudio(). Added audio capture support to SDL backend. Use the SDL2 audio device interface instead of the legacy 1.2 API. Disable SDL audio capture until prebuilt SDL libraries are updated to 2.0.8. Update SDL2 to 2.0.8 Add SDL 2.0.1 headers for macOS PPC Make macOS Universal Bundle target 10.6 for x86 and x86_64 Fix possible bot goal state NULL pointer dereference Fix uninitialized bot_goal_t fields Remove unnecessary NULL pointer check in Cmd_RemoveCommand Make UI_DrawProportionalString handle NULL string Fix compiling against macOS system OpenAL and SDL2 frameworks Fix array index in CanDamage() function - discovered by MARTY Fix compiling Makefile (broke in macOS frameworks commit) Fix clearing keys for control in Team Arena UI Make s_useOpenAL be CVAR_LATCH Improvements for dedicated camera followers (team follow1/2) Fix not closing description.txt and fix path seperator Fix duplicate bots displayed in Team Arena ingame add bot menu OpenGL2: Fix parsing specularScale in shaders Don't allow SDL audio capture using pulseaudio Isolate the Altivec code so non-Altivec PPC targets can use the same binary. Limit -maltivec to specific source files on OpenBSD too (untested) Use SDL 2.0.1 headers for macOS ppc64 Fix console offset while Team Arena voiceMenu is open OpenGL2: Readd r_deluxeSpecular. Fix client kicked as unpure when missing the latest cgame/ui pk3s Don't create multiple windows when GL context creation fails Require OpenGL 1.2 for GL_CLAMP_TO_EDGE Fix Linux uninstaller requiring Bash Fix Linux uninstaller redirecting stderr to stdout in preuninstall.sh Reported by @illwieckz. Fix in_restart causing fatal error while video is shutdown Allow pkg-config binary to be overridden with PKG_CONFIG Make testgun command without argument disable test gun model Remove unused renderer_buffer variable Don't upload 8 bit grayscale images as 16 bit luminance OpenGL1: Use RE_UploadCinematic() instead of duplicate code Don't load non-core GL functions for OpenGL 3.2 core context Load OpenGL ES 2.0 function procs Don't check fixed function GL extensions when using shader pipeline OpenGL2: Fix world VAO cache drawing when glIndex_t is unsigned short OpenGL2: Misc fixes and cleanup Fix IQM root joint backlerp when joint number is more than 0 Improve IQM loading Improve IQM CPU vertex skinning performance OpenGL2: Add GPU vertex skinning for IQM models
2018-07-30 11:35:12 +00:00
return LittleLong ((unsigned long)((r)|(g<<8)|(b<<16))|(255UL<<24));
2011-02-18 14:31:32 +00:00
}
2022-03-19 19:19:51 +00:00
/******************************************************************************
*
* Function: Frame_yuv_to_rgb24
*
* Description: Used by the Theora(ogm) code
* moved the convertion into one function, to reduce the number of function-calls
*
******************************************************************************/
void Frame_yuv_to_rgb24(const unsigned char *y, const unsigned char *u, const unsigned char *v,
int width, int height, int y_stride, int uv_stride,
int yWShift, int uvWShift, int yHShift, int uvHShift, unsigned int *output)
{
int i, j, uvI;
long r, g, b, YY;
for(j = 0; j < height; ++j)
{
for(i = 0; i < width; ++i)
{
YY = (long)(ROQ_YY_tab[(y[(i >> yWShift) + (j >> yHShift) * y_stride])]);
uvI = (i >> uvWShift) + (j >> uvHShift) * uv_stride;
r = (YY + ROQ_VR_tab[v[uvI]]) >> 6;
g = (YY + ROQ_UG_tab[u[uvI]] + ROQ_VG_tab[v[uvI]]) >> 6;
b = (YY + ROQ_UB_tab[u[uvI]]) >> 6;
if(r < 0)
r = 0;
if(g < 0)
g = 0;
if(b < 0)
b = 0;
if(r > 255)
r = 255;
if(g > 255)
g = 255;
if(b > 255)
b = 255;
*output = LittleLong((r) | (g << 8) | (b << 16) | (255 << 24));
++output;
}
}
}
2011-02-18 14:31:32 +00:00
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void decodeCodeBook( byte *input, unsigned short roq_flags )
{
long i, j, two, four;
unsigned short *aptr, *bptr, *cptr, *dptr;
long y0,y1,y2,y3,cr,cb;
byte *bbptr, *baptr, *bcptr, *bdptr;
union {
unsigned int *i;
unsigned short *s;
} iaptr, ibptr, icptr, idptr;
if (!roq_flags) {
two = four = 256;
} else {
two = roq_flags>>8;
if (!two) two = 256;
four = roq_flags&0xff;
}
four *= 2;
bptr = (unsigned short *)vq2;
if (!cinTable[currentHandle].half) {
if (!cinTable[currentHandle].smootheddouble) {
//
// normal height
//
if (cinTable[currentHandle].samplesPerPixel==2) {
for(i=0;i<two;i++) {
y0 = (long)*input++;
y1 = (long)*input++;
y2 = (long)*input++;
y3 = (long)*input++;
cr = (long)*input++;
cb = (long)*input++;
*bptr++ = yuv_to_rgb( y0, cr, cb );
*bptr++ = yuv_to_rgb( y1, cr, cb );
*bptr++ = yuv_to_rgb( y2, cr, cb );
*bptr++ = yuv_to_rgb( y3, cr, cb );
}
cptr = (unsigned short *)vq4;
dptr = (unsigned short *)vq8;
for(i=0;i<four;i++) {
aptr = (unsigned short *)vq2 + (*input++)*4;
bptr = (unsigned short *)vq2 + (*input++)*4;
for(j=0;j<2;j++)
VQ2TO4(aptr,bptr,cptr,dptr);
}
} else if (cinTable[currentHandle].samplesPerPixel==4) {
ibptr.s = bptr;
for(i=0;i<two;i++) {
y0 = (long)*input++;
y1 = (long)*input++;
y2 = (long)*input++;
y3 = (long)*input++;
cr = (long)*input++;
cb = (long)*input++;
*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y1, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y3, cr, cb );
}
icptr.s = vq4;
idptr.s = vq8;
for(i=0;i<four;i++) {
iaptr.s = vq2;
iaptr.i += (*input++)*4;
ibptr.s = vq2;
ibptr.i += (*input++)*4;
for(j=0;j<2;j++)
VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
}
} else if (cinTable[currentHandle].samplesPerPixel==1) {
bbptr = (byte *)bptr;
for(i=0;i<two;i++) {
*bbptr++ = cinTable[currentHandle].gray[*input++];
*bbptr++ = cinTable[currentHandle].gray[*input++];
*bbptr++ = cinTable[currentHandle].gray[*input++];
*bbptr++ = cinTable[currentHandle].gray[*input]; input +=3;
}
bcptr = (byte *)vq4;
bdptr = (byte *)vq8;
for(i=0;i<four;i++) {
baptr = (byte *)vq2 + (*input++)*4;
bbptr = (byte *)vq2 + (*input++)*4;
for(j=0;j<2;j++)
VQ2TO4(baptr,bbptr,bcptr,bdptr);
}
}
} else {
//
// double height, smoothed
//
if (cinTable[currentHandle].samplesPerPixel==2) {
for(i=0;i<two;i++) {
y0 = (long)*input++;
y1 = (long)*input++;
y2 = (long)*input++;
y3 = (long)*input++;
cr = (long)*input++;
cb = (long)*input++;
*bptr++ = yuv_to_rgb( y0, cr, cb );
*bptr++ = yuv_to_rgb( y1, cr, cb );
*bptr++ = yuv_to_rgb( ((y0*3)+y2)/4, cr, cb );
*bptr++ = yuv_to_rgb( ((y1*3)+y3)/4, cr, cb );
*bptr++ = yuv_to_rgb( (y0+(y2*3))/4, cr, cb );
*bptr++ = yuv_to_rgb( (y1+(y3*3))/4, cr, cb );
*bptr++ = yuv_to_rgb( y2, cr, cb );
*bptr++ = yuv_to_rgb( y3, cr, cb );
}
cptr = (unsigned short *)vq4;
dptr = (unsigned short *)vq8;
for(i=0;i<four;i++) {
aptr = (unsigned short *)vq2 + (*input++)*8;
bptr = (unsigned short *)vq2 + (*input++)*8;
for(j=0;j<2;j++) {
VQ2TO4(aptr,bptr,cptr,dptr);
VQ2TO4(aptr,bptr,cptr,dptr);
}
}
} else if (cinTable[currentHandle].samplesPerPixel==4) {
ibptr.s = bptr;
for(i=0;i<two;i++) {
y0 = (long)*input++;
y1 = (long)*input++;
y2 = (long)*input++;
y3 = (long)*input++;
cr = (long)*input++;
cb = (long)*input++;
*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y1, cr, cb );
*ibptr.i++ = yuv_to_rgb24( ((y0*3)+y2)/4, cr, cb );
*ibptr.i++ = yuv_to_rgb24( ((y1*3)+y3)/4, cr, cb );
*ibptr.i++ = yuv_to_rgb24( (y0+(y2*3))/4, cr, cb );
*ibptr.i++ = yuv_to_rgb24( (y1+(y3*3))/4, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y3, cr, cb );
}
icptr.s = vq4;
idptr.s = vq8;
for(i=0;i<four;i++) {
iaptr.s = vq2;
iaptr.i += (*input++)*8;
ibptr.s = vq2;
ibptr.i += (*input++)*8;
for(j=0;j<2;j++) {
VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
}
}
} else if (cinTable[currentHandle].samplesPerPixel==1) {
bbptr = (byte *)bptr;
for(i=0;i<two;i++) {
y0 = (long)*input++;
y1 = (long)*input++;
y2 = (long)*input++;
y3 = (long)*input; input+= 3;
*bbptr++ = cinTable[currentHandle].gray[y0];
*bbptr++ = cinTable[currentHandle].gray[y1];
*bbptr++ = cinTable[currentHandle].gray[((y0*3)+y2)/4];
*bbptr++ = cinTable[currentHandle].gray[((y1*3)+y3)/4];
*bbptr++ = cinTable[currentHandle].gray[(y0+(y2*3))/4];
*bbptr++ = cinTable[currentHandle].gray[(y1+(y3*3))/4];
*bbptr++ = cinTable[currentHandle].gray[y2];
*bbptr++ = cinTable[currentHandle].gray[y3];
}
bcptr = (byte *)vq4;
bdptr = (byte *)vq8;
for(i=0;i<four;i++) {
baptr = (byte *)vq2 + (*input++)*8;
bbptr = (byte *)vq2 + (*input++)*8;
for(j=0;j<2;j++) {
VQ2TO4(baptr,bbptr,bcptr,bdptr);
VQ2TO4(baptr,bbptr,bcptr,bdptr);
}
}
}
}
} else {
//
// 1/4 screen
//
if (cinTable[currentHandle].samplesPerPixel==2) {
for(i=0;i<two;i++) {
y0 = (long)*input; input+=2;
y2 = (long)*input; input+=2;
cr = (long)*input++;
cb = (long)*input++;
*bptr++ = yuv_to_rgb( y0, cr, cb );
*bptr++ = yuv_to_rgb( y2, cr, cb );
}
cptr = (unsigned short *)vq4;
dptr = (unsigned short *)vq8;
for(i=0;i<four;i++) {
aptr = (unsigned short *)vq2 + (*input++)*2;
bptr = (unsigned short *)vq2 + (*input++)*2;
for(j=0;j<2;j++) {
VQ2TO2(aptr,bptr,cptr,dptr);
}
}
} else if (cinTable[currentHandle].samplesPerPixel == 1) {
bbptr = (byte *)bptr;
for(i=0;i<two;i++) {
*bbptr++ = cinTable[currentHandle].gray[*input]; input+=2;
*bbptr++ = cinTable[currentHandle].gray[*input]; input+=4;
}
bcptr = (byte *)vq4;
bdptr = (byte *)vq8;
for(i=0;i<four;i++) {
baptr = (byte *)vq2 + (*input++)*2;
bbptr = (byte *)vq2 + (*input++)*2;
for(j=0;j<2;j++) {
VQ2TO2(baptr,bbptr,bcptr,bdptr);
}
}
} else if (cinTable[currentHandle].samplesPerPixel == 4) {
ibptr.s = bptr;
for(i=0;i<two;i++) {
y0 = (long)*input; input+=2;
y2 = (long)*input; input+=2;
cr = (long)*input++;
cb = (long)*input++;
*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
}
icptr.s = vq4;
idptr.s = vq8;
for(i=0;i<four;i++) {
iaptr.s = vq2;
iaptr.i += (*input++)*2;
ibptr.s = vq2 + (*input++)*2;
ibptr.i += (*input++)*2;
for(j=0;j<2;j++) {
VQ2TO2(iaptr.i,ibptr.i,icptr.i,idptr.i);
}
}
}
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff )
{
byte *scroff;
long bigx, bigy, lowx, lowy, useY;
long offset;
offset = cinTable[currentHandle].screenDelta;
lowx = lowy = 0;
bigx = cinTable[currentHandle].xsize;
bigy = cinTable[currentHandle].ysize;
if (bigx > cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH;
if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT;
if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) {
useY = startY;
scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel);
cin.qStatus[0][cinTable[currentHandle].onQuad ] = scroff;
cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset;
}
if ( quadSize != MINSIZE ) {
quadSize >>= 1;
recurseQuad( startX, startY , quadSize, xOff, yOff );
recurseQuad( startX+quadSize, startY , quadSize, xOff, yOff );
recurseQuad( startX, startY+quadSize , quadSize, xOff, yOff );
recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff );
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void setupQuad( long xOff, long yOff )
{
long numQuadCels, i,x,y;
byte *temp;
if (xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == cin.oldysize && cinTable[currentHandle].xsize == cin.oldxsize) {
return;
}
cin.oldXOff = xOff;
cin.oldYOff = yOff;
cin.oldysize = cinTable[currentHandle].ysize;
cin.oldxsize = cinTable[currentHandle].xsize;
numQuadCels = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16);
numQuadCels += numQuadCels/4;
numQuadCels += 64; // for overflow
cinTable[currentHandle].onQuad = 0;
for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16)
for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16)
recurseQuad( x, y, 16, xOff, yOff );
temp = NULL;
for(i=(numQuadCels-64);i<numQuadCels;i++) {
cin.qStatus[0][i] = temp; // eoq
cin.qStatus[1][i] = temp; // eoq
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void readQuadInfo( byte *qData )
{
if (currentHandle < 0) return;
cinTable[currentHandle].xsize = qData[0]+qData[1]*256;
cinTable[currentHandle].ysize = qData[2]+qData[3]*256;
cinTable[currentHandle].maxsize = qData[4]+qData[5]*256;
cinTable[currentHandle].minsize = qData[6]+qData[7]*256;
cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize;
cinTable[currentHandle].CIN_WIDTH = cinTable[currentHandle].xsize;
cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].samplesPerPixel;
cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT*cinTable[currentHandle].samplesPerLine;
cinTable[currentHandle].half = qfalse;
cinTable[currentHandle].smootheddouble = qfalse;
cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal;
cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer;
cinTable[currentHandle].t[0] = cinTable[currentHandle].screenDelta;
cinTable[currentHandle].t[1] = -cinTable[currentHandle].screenDelta;
cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH;
cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT;
// rage pro is very slow at 512 wide textures, voodoo can't do it at all
if ( cls.glconfig.hardwareType == GLHW_RAGEPRO || cls.glconfig.maxTextureSize <= 256) {
2011-02-18 14:31:32 +00:00
if (cinTable[currentHandle].drawX>256) {
cinTable[currentHandle].drawX = 256;
}
if (cinTable[currentHandle].drawY>256) {
cinTable[currentHandle].drawY = 256;
}
if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) {
Com_Printf("HACK: approxmimating cinematic for Rage Pro or Voodoo\n");
}
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void RoQPrepMcomp( long xoff, long yoff )
{
long i, j, x, y, temp, temp2;
i=cinTable[currentHandle].samplesPerLine; j=cinTable[currentHandle].samplesPerPixel;
if ( cinTable[currentHandle].xsize == (cinTable[currentHandle].ysize*4) && !cinTable[currentHandle].half ) { j = j+j; i = i+i; }
for(y=0;y<16;y++) {
temp2 = (y+yoff-8)*i;
for(x=0;x<16;x++) {
temp = (x+xoff-8)*j;
cin.mcomp[(x*16)+y] = cinTable[currentHandle].normalBuffer0-(temp2+temp);
}
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void initRoQ( void )
{
if (currentHandle < 0) return;
cinTable[currentHandle].VQNormal = (void (*)(byte *, void *))blitVQQuad32fs;
cinTable[currentHandle].VQBuffer = (void (*)(byte *, void *))blitVQQuad32fs;
cinTable[currentHandle].samplesPerPixel = 4;
ROQ_GenYUVTables();
RllSetupTable();
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
/*
static byte* RoQFetchInterlaced( byte *source ) {
int x, *src, *dst;
if (currentHandle < 0) return NULL;
src = (int *)source;
dst = (int *)cinTable[currentHandle].buf2;
for(x=0;x<256*256;x++) {
*dst = *src;
dst++; src += 2;
}
return cinTable[currentHandle].buf2;
}
*/
static void RoQReset( void ) {
if (currentHandle < 0) return;
FS_FCloseFile( cinTable[currentHandle].iFile );
FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
// let the background thread start reading ahead
FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
RoQ_init();
cinTable[currentHandle].status = FMV_LOOPED;
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void RoQInterrupt(void)
{
byte *framedata;
short sbuf[32768];
int ssize;
if (currentHandle < 0) return;
FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile );
if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
if (cinTable[currentHandle].holdAtEnd==qfalse) {
if (cinTable[currentHandle].looping) {
RoQReset();
} else {
cinTable[currentHandle].status = FMV_EOF;
}
} else {
cinTable[currentHandle].status = FMV_IDLE;
}
return;
}
framedata = cin.file;
//
// new frame is ready
//
redump:
switch(cinTable[currentHandle].roq_id)
{
case ROQ_QUAD_VQ:
if ((cinTable[currentHandle].numQuads&1)) {
cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1];
RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata);
cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta;
} else {
cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0];
RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata );
cinTable[currentHandle].buf = cin.linbuf;
}
if (cinTable[currentHandle].numQuads == 0) { // first frame
Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize);
}
cinTable[currentHandle].numQuads++;
cinTable[currentHandle].dirty = qtrue;
break;
case ROQ_CODEBOOK:
decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags );
break;
case ZA_SOUND_MONO:
if (!cinTable[currentHandle].silent) {
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples(0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, -1);
2011-02-18 14:31:32 +00:00
}
break;
case ZA_SOUND_STEREO:
if (!cinTable[currentHandle].silent) {
if (cinTable[currentHandle].numQuads == -1) {
S_Update();
s_rawend[0] = s_soundtime;
}
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
S_RawSamples(0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1);
2011-02-18 14:31:32 +00:00
}
break;
case ROQ_QUAD_INFO:
if (cinTable[currentHandle].numQuads == -1) {
readQuadInfo( framedata );
setupQuad( 0, 0 );
cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds();
2011-02-18 14:31:32 +00:00
}
if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0;
break;
case ROQ_PACKET:
cinTable[currentHandle].inMemory = cinTable[currentHandle].roq_flags;
cinTable[currentHandle].RoQFrameSize = 0; // for header
break;
case ROQ_QUAD_HANG:
cinTable[currentHandle].RoQFrameSize = 0;
break;
case ROQ_QUAD_JPEG:
break;
default:
cinTable[currentHandle].status = FMV_EOF;
break;
}
//
// read in next frame data
//
if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
if (cinTable[currentHandle].holdAtEnd==qfalse) {
if (cinTable[currentHandle].looping) {
RoQReset();
} else {
cinTable[currentHandle].status = FMV_EOF;
}
} else {
cinTable[currentHandle].status = FMV_IDLE;
}
return;
}
framedata += cinTable[currentHandle].RoQFrameSize;
cinTable[currentHandle].roq_id = framedata[0] + framedata[1]*256;
cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536;
cinTable[currentHandle].roq_flags = framedata[6] + framedata[7]*256;
cinTable[currentHandle].roqF0 = (signed char)framedata[7];
cinTable[currentHandle].roqF1 = (signed char)framedata[6];
if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) {
Com_DPrintf("roq_size>65536||roq_id==0x1084\n");
cinTable[currentHandle].status = FMV_EOF;
if (cinTable[currentHandle].looping) {
RoQReset();
}
return;
}
if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF)) { cinTable[currentHandle].inMemory--; framedata += 8; goto redump; }
//
// one more frame hits the dust
//
// assert(cinTable[currentHandle].RoQFrameSize <= 65536);
// r = FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile );
cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize+8;
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
static void RoQ_init( void )
{
cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds();
2011-02-18 14:31:32 +00:00
cinTable[currentHandle].RoQPlayed = 24;
/* get frame rate */
cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7]*256;
if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30;
cinTable[currentHandle].numQuads = -1;
cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9]*256;
cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11]*256 + cin.file[12]*65536;
cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15]*256;
if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) {
return;
}
}
/******************************************************************************
*
* Function:
*
* Description:
*
******************************************************************************/
2022-03-19 19:19:51 +00:00
//FIXME: this isn't realy a "roq-shutdown" (it's more a CIN-shutdown, beside the file-closing)
2011-02-18 14:31:32 +00:00
static void RoQShutdown( void ) {
const char *s;
if (!cinTable[currentHandle].buf) {
2022-03-19 19:19:51 +00:00
//FIXME: there could be something that should be "shutdowned" even if we don't have a output frame (at least in the ogm code)
2011-02-18 14:31:32 +00:00
return;
}
if ( cinTable[currentHandle].status == FMV_IDLE ) {
return;
}
Com_DPrintf("finished cinematic\n");
cinTable[currentHandle].status = FMV_IDLE;
if (cinTable[currentHandle].iFile) {
FS_FCloseFile( cinTable[currentHandle].iFile );
cinTable[currentHandle].iFile = 0;
}
if (cinTable[currentHandle].alterGameState) {
clc.state = CA_DISCONNECTED;
2011-02-18 14:31:32 +00:00
// we can't just do a vstr nextmap, because
// if we are aborting the intro cinematic with
// a devmap command, nextmap would be valid by
// the time it was referenced
s = Cvar_VariableString( "nextmap" );
if ( s[0] ) {
Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) );
Cvar_Set( "nextmap", "" );
}
CL_handle = -1;
}
cinTable[currentHandle].fileName[0] = 0;
2022-03-19 19:19:51 +00:00
if (cinTable[currentHandle].fileType == FT_OGM)
{
Cin_OGM_Shutdown();
cinTable[currentHandle].buf = NULL;
}
2011-02-18 14:31:32 +00:00
currentHandle = -1;
}
/*
==================
CIN_StopCinematic
2011-02-18 14:31:32 +00:00
==================
*/
e_status CIN_StopCinematic(int handle) {
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
currentHandle = handle;
Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName);
if (!cinTable[currentHandle].buf) {
return FMV_EOF;
}
if (cinTable[currentHandle].alterGameState) {
if ( clc.state != CA_CINEMATIC ) {
2011-02-18 14:31:32 +00:00
return cinTable[currentHandle].status;
}
}
cinTable[currentHandle].status = FMV_EOF;
RoQShutdown();
return FMV_EOF;
}
/*
==================
CIN_RunCinematic
2011-02-18 14:31:32 +00:00
Fetch and decompress the pending frame
==================
*/
e_status CIN_RunCinematic (int handle)
{
int start = 0;
int thisTime = 0;
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
if (cin.currentHandle != handle) {
currentHandle = handle;
cin.currentHandle = currentHandle;
cinTable[currentHandle].status = FMV_EOF;
RoQReset();
}
if (cinTable[handle].playonwalls < -1)
{
return cinTable[handle].status;
}
currentHandle = handle;
if (cinTable[currentHandle].alterGameState) {
if ( clc.state != CA_CINEMATIC ) {
2011-02-18 14:31:32 +00:00
return cinTable[currentHandle].status;
}
}
if (cinTable[currentHandle].status == FMV_IDLE) {
return cinTable[currentHandle].status;
}
2022-03-19 19:19:51 +00:00
if (cinTable[currentHandle].fileType == FT_OGM)
{
if (Cin_OGM_Run(cinTable[currentHandle].startTime == 0 ? 0 : CL_ScaledMilliseconds() - cinTable[currentHandle].startTime))
cinTable[currentHandle].status = FMV_EOF;
else
{
int newW, newH;
qboolean resolutionChange = qfalse;
cinTable[currentHandle].buf = Cin_OGM_GetOutput(&newW, &newH);
if (newW != cinTable[currentHandle].CIN_WIDTH)
{
2022-03-19 19:19:51 +00:00
cinTable[currentHandle].CIN_WIDTH = newW;
resolutionChange = qtrue;
}
if (newH != cinTable[currentHandle].CIN_HEIGHT)
{
cinTable[currentHandle].CIN_HEIGHT = newH;
resolutionChange = qtrue;
}
if (resolutionChange)
{
cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH;
cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT;
// some old drivers can't do it at all
if (cls.glconfig.maxTextureSize <= 256) {
if (cinTable[currentHandle].drawX>256) {
cinTable[currentHandle].drawX = 256;
}
if (cinTable[currentHandle].drawY>256) {
cinTable[currentHandle].drawY = 256;
}
if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) {
Com_Printf("HACK: approxmimating cinematic to 256x256 from %dx%d\n", cinTable[currentHandle].CIN_WIDTH, cinTable[currentHandle].CIN_HEIGHT);
}
}
}
cinTable[currentHandle].status = FMV_PLAY;
cinTable[currentHandle].dirty = qtrue;
}
if (!cinTable[currentHandle].startTime)
cinTable[currentHandle].startTime = CL_ScaledMilliseconds();
if (cinTable[currentHandle].status == FMV_EOF)
{
if (cinTable[currentHandle].holdAtEnd)
{
cinTable[currentHandle].status = FMV_IDLE;
}
else if (cinTable[currentHandle].looping)
{
Cin_OGM_Shutdown();
Cin_OGM_Init(cinTable[currentHandle].fileName);
cinTable[currentHandle].buf = NULL;
cinTable[currentHandle].startTime = 0;
cinTable[currentHandle].status = FMV_PLAY;
}
else
{
RoQShutdown();
// Cin_OGM_Shutdown();
}
}
return cinTable[currentHandle].status;
}
thisTime = CL_ScaledMilliseconds();
2011-02-18 14:31:32 +00:00
if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime))>100) {
cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime;
}
cinTable[currentHandle].tfps = (((CL_ScaledMilliseconds() - cinTable[currentHandle].startTime)*3)/100);
2011-02-18 14:31:32 +00:00
start = cinTable[currentHandle].startTime;
while( (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads)
&& (cinTable[currentHandle].status == FMV_PLAY) )
{
RoQInterrupt();
if (start != cinTable[currentHandle].startTime) {
cinTable[currentHandle].tfps = (((CL_ScaledMilliseconds() - cinTable[currentHandle].startTime)*3)/100);
2011-02-18 14:31:32 +00:00
start = cinTable[currentHandle].startTime;
}
}
cinTable[currentHandle].lastTime = thisTime;
if (cinTable[currentHandle].status == FMV_LOOPED) {
cinTable[currentHandle].status = FMV_PLAY;
}
if (cinTable[currentHandle].status == FMV_EOF) {
if (cinTable[currentHandle].looping) {
RoQReset();
} else {
RoQShutdown();
return FMV_EOF;
2011-02-18 14:31:32 +00:00
}
}
return cinTable[currentHandle].status;
}
2022-03-19 19:19:51 +00:00
// Also see S_TheCheckExtension
qboolean CIN_TheCheckExtension(char *filename)
{
enum
{
CIN_RoQ,
CIN_roq,
#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA))
CIN_ogm,
CIN_ogv,
#endif
CIN_MAX
};
const char cin_ext[CIN_MAX][4] = { "RoQ\0", "roq\0"
#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA))
, "ogm\0", "ogv\0"
#endif
};
qboolean skipCin[CIN_MAX] = { qfalse, qfalse
#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA))
, qfalse, qfalse
#endif
};
fileHandle_t hnd;
char fn[MAX_QPATH];
int stringlen = strlen(filename);
char *extptr;
int i;
strncpy(fn, filename, stringlen+1);
extptr = strrchr(fn, '.');
if(!extptr)
{
extptr = &fn[stringlen];
extptr[0] = '.';
extptr[1] = 'R';
extptr[2] = 'o';
extptr[3] = 'Q';
extptr[4] = '\0';
stringlen += 4;
skipCin[CIN_RoQ] = qtrue;
}
FS_FOpenFileRead(fn, &hnd, qtrue);
if (!hnd)
{
extptr++;
for (i = 0; i < CIN_MAX; i++)
{
if (!strcmp(extptr, cin_ext[i]))
{
skipCin[i] = qtrue;
break;
}
}
for (i = 0; i < CIN_MAX; i++)
{
if (skipCin[i]) {
continue;
}
extptr[0] = cin_ext[i][0];
extptr[1] = cin_ext[i][1];
extptr[2] = cin_ext[i][2];
extptr[3] = '\0';
FS_FOpenFileRead(fn, &hnd, qtrue);
if (hnd) {
break;
}
}
if(!hnd) {
return qfalse;
}
}
FS_FCloseFile(hnd);
strcpy(filename, fn);
return qtrue;
}
2011-02-18 14:31:32 +00:00
/*
==================
CIN_PlayCinematic
2011-02-18 14:31:32 +00:00
==================
*/
int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) {
unsigned short RoQID;
char name[MAX_OSPATH];
2022-03-19 19:19:51 +00:00
#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA))
const char *ext;
#endif
2011-02-18 14:31:32 +00:00
int i;
if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) {
Com_sprintf (name, sizeof(name), "video/%s", arg);
} else {
Com_sprintf (name, sizeof(name), "%s", arg);
}
2022-03-19 19:19:51 +00:00
if (!CIN_TheCheckExtension(name))
{
// Can't find video
return -1;
}
2011-02-18 14:31:32 +00:00
if (!(systemBits & CIN_system)) {
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
if (!strcmp(cinTable[i].fileName, name) ) {
return i;
}
}
}
Com_DPrintf("CIN_PlayCinematic( %s )\n", arg);
2011-02-18 14:31:32 +00:00
Com_Memset(&cin, 0, sizeof(cinematics_t) );
currentHandle = CIN_HandleForVideo();
2022-03-19 19:19:51 +00:00
Com_Memset(&cinTable[currentHandle], 0, sizeof(cin_cache));
2011-02-18 14:31:32 +00:00
cin.currentHandle = currentHandle;
strcpy(cinTable[currentHandle].fileName, name);
2022-03-19 19:19:51 +00:00
#if defined(USE_CODEC_VORBIS) && (defined(USE_CIN_XVID) || defined(USE_CIN_THEORA))
ext = COM_GetExtension(name);
if (!Q_stricmp(ext, "ogm") || !Q_stricmp(ext, "ogv"))
{
if (Cin_OGM_Init(name))
{
Com_DPrintf("starting ogm-playback failed(%s)\n", arg);
cinTable[currentHandle].fileName[0] = 0;
Cin_OGM_Shutdown();
return -1;
}
cinTable[currentHandle].fileType = FT_OGM;
CIN_SetExtents(currentHandle, x, y, w, h);
CIN_SetLooping(currentHandle, (systemBits & CIN_loop) != 0);
cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0;
cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0;
cinTable[currentHandle].playonwalls = 1;
cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0;
cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0;
/* we will set this info after the first xvid-frame
cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH;
*/
if (cinTable[currentHandle].alterGameState) {
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
2022-03-19 19:19:51 +00:00
} else {
cinTable[currentHandle].playonwalls = cl_inGameVideo->integer;
}
if (cinTable[currentHandle].alterGameState) {
clc.state = CA_CINEMATIC;
}
cinTable[currentHandle].status = FMV_PLAY;
return currentHandle;
}
#endif
2011-02-18 14:31:32 +00:00
cinTable[currentHandle].ROQSize = 0;
cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
if (cinTable[currentHandle].ROQSize<=0) {
Com_DPrintf("play(%s), ROQSize<=0\n", arg);
cinTable[currentHandle].fileName[0] = 0;
return -1;
}
CIN_SetExtents(currentHandle, x, y, w, h);
CIN_SetLooping(currentHandle, (systemBits & CIN_loop)!=0);
cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH;
cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0;
cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0;
cinTable[currentHandle].playonwalls = 1;
cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0;
cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0;
if (cinTable[currentHandle].alterGameState) {
// close the menu
if ( uivm ) {
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
}
} else {
cinTable[currentHandle].playonwalls = cl_inGameVideo->integer;
}
initRoQ();
FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256;
if (RoQID == 0x1084)
{
RoQ_init();
// FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile);
cinTable[currentHandle].status = FMV_PLAY;
Com_DPrintf("trFMV::play(), playing %s\n", arg);
if (cinTable[currentHandle].alterGameState) {
clc.state = CA_CINEMATIC;
2011-02-18 14:31:32 +00:00
}
Con_Close();
if (!cinTable[currentHandle].silent) {
s_rawend[0] = s_soundtime;
}
2011-02-18 14:31:32 +00:00
return currentHandle;
}
Com_DPrintf("trFMV::play(), invalid RoQ ID\n");
RoQShutdown();
return -1;
}
void CIN_SetExtents (int handle, int x, int y, int w, int h) {
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
cinTable[handle].xpos = x;
cinTable[handle].ypos = y;
cinTable[handle].width = w;
cinTable[handle].height = h;
cinTable[handle].dirty = qtrue;
}
void CIN_SetLooping(int handle, qboolean loop) {
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
cinTable[handle].looping = loop;
}
/*
==================
CIN_ResampleCinematic
Resample cinematic to 256x256 and store in buf2
==================
*/
void CIN_ResampleCinematic(int handle, int *buf2) {
int ix, iy, *buf3, xm, ym, ll;
byte *buf;
buf = cinTable[handle].buf;
xm = cinTable[handle].CIN_WIDTH/256;
ym = cinTable[handle].CIN_HEIGHT/256;
ll = 8;
if (cinTable[handle].CIN_WIDTH==512) {
ll = 9;
}
buf3 = (int*)buf;
if (xm==2 && ym==2) {
byte *bc2, *bc3;
int ic, iiy;
bc2 = (byte *)buf2;
bc3 = (byte *)buf3;
for (iy = 0; iy<256; iy++) {
iiy = iy<<12;
for (ix = 0; ix<2048; ix+=8) {
for(ic = ix;ic<(ix+4);ic++) {
*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2;
bc2++;
}
}
}
} else if (xm==2 && ym==1) {
byte *bc2, *bc3;
int ic, iiy;
bc2 = (byte *)buf2;
bc3 = (byte *)buf3;
for (iy = 0; iy<256; iy++) {
iiy = iy<<11;
for (ix = 0; ix<2048; ix+=8) {
for(ic = ix;ic<(ix+4);ic++) {
*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1;
bc2++;
}
}
}
} else {
for (iy = 0; iy<256; iy++) {
for (ix = 0; ix<256; ix++) {
buf2[(iy<<8)+ix] = buf3[((iy*ym)<<ll) + (ix*xm)];
}
}
}
}
2011-02-18 14:31:32 +00:00
/*
==================
CIN_DrawCinematic
2011-02-18 14:31:32 +00:00
==================
*/
void CIN_DrawCinematic (int handle) {
float x, y, w, h;
byte *buf;
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
if (!cinTable[handle].buf) {
return;
}
x = cinTable[handle].xpos;
y = cinTable[handle].ypos;
w = cinTable[handle].width;
h = cinTable[handle].height;
buf = cinTable[handle].buf;
SCR_AdjustFrom640( &x, &y, &w, &h );
if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) {
int *buf2;
2011-02-18 14:31:32 +00:00
buf2 = Hunk_AllocateTempMemory( 256*256*4 );
CIN_ResampleCinematic(handle, buf2);
2011-02-18 14:31:32 +00:00
re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue);
cinTable[handle].dirty = qfalse;
Hunk_FreeTempMemory(buf2);
return;
}
re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty);
cinTable[handle].dirty = qfalse;
}
void CL_PlayCinematic_f(void) {
char *arg, *s;
int bits = CIN_system;
Com_DPrintf("CL_PlayCinematic_f\n");
if (clc.state == CA_CINEMATIC) {
2011-02-18 14:31:32 +00:00
SCR_StopCinematic();
}
arg = Cmd_Argv( 1 );
s = Cmd_Argv(2);
if ((s && s[0] == '1') || Q_stricmp(arg,"demoend.roq")==0 || Q_stricmp(arg,"end.roq")==0) {
bits |= CIN_hold;
}
if (s && s[0] == '2') {
bits |= CIN_loop;
}
S_StopAllSounds ();
CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits );
if (CL_handle >= 0) {
do {
SCR_RunCinematic();
} while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY); // wait for first frame (load codebook and sound)
}
}
void SCR_DrawCinematic (void) {
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
CIN_DrawCinematic(CL_handle);
}
}
void SCR_RunCinematic (void)
{
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
CIN_RunCinematic(CL_handle);
}
}
void SCR_StopCinematic(void) {
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
CIN_StopCinematic(CL_handle);
S_StopAllSounds ();
CL_handle = -1;
}
}
void CIN_UploadCinematic(int handle) {
if (handle >= 0 && handle < MAX_VIDEO_HANDLES) {
if (!cinTable[handle].buf) {
return;
}
if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) {
if (cinTable[handle].playonwalls == 0) {
cinTable[handle].playonwalls = -1;
} else {
if (cinTable[handle].playonwalls == -1) {
cinTable[handle].playonwalls = -2;
} else {
cinTable[handle].dirty = qfalse;
}
}
}
// Resample the video if needed
if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) {
int *buf2;
buf2 = Hunk_AllocateTempMemory( 256*256*4 );
CIN_ResampleCinematic(handle, buf2);
re.UploadCinematic( cinTable[handle].CIN_WIDTH, cinTable[handle].CIN_HEIGHT, 256, 256, (byte *)buf2, handle, qtrue);
cinTable[handle].dirty = qfalse;
Hunk_FreeTempMemory(buf2);
} else {
// Upload video at normal resolution
re.UploadCinematic( cinTable[handle].CIN_WIDTH, cinTable[handle].CIN_HEIGHT, cinTable[handle].drawX, cinTable[handle].drawY,
cinTable[handle].buf, handle, cinTable[handle].dirty);
cinTable[handle].dirty = qfalse;
}
2011-02-18 14:31:32 +00:00
if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) {
cinTable[handle].playonwalls--;
}
else if (cl_inGameVideo->integer != 0 && cinTable[handle].playonwalls != 1) {
cinTable[handle].playonwalls = 1;
}
}
}