2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
23
/*****************************************************************************
26
* desc: video and cinematic playback
28
* $Archive: /MissionPack/code/client/cl_cin.c $
30
* cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256
32
*****************************************************************************/
35
#include "snd_local.h"
40
#define DEFAULT_CIN_WIDTH 512
41
#define DEFAULT_CIN_HEIGHT 512
43
#define ROQ_QUAD 0x1000
44
#define ROQ_QUAD_INFO 0x1001
45
#define ROQ_CODEBOOK 0x1002
46
#define ROQ_QUAD_VQ 0x1011
47
#define ROQ_QUAD_JPEG 0x1012
48
#define ROQ_QUAD_HANG 0x1013
49
#define ROQ_PACKET 0x1030
50
#define ZA_SOUND_MONO 0x1020
51
#define ZA_SOUND_STEREO 0x1021
53
#define MAX_VIDEO_HANDLES 16
55
extern glconfig_t glConfig;
56
extern int s_paintedtime;
60
static void RoQ_init( void );
62
/******************************************************************************
66
* Description: RoQ/RnR manipulation routines
67
* not entirely complete for first run
69
******************************************************************************/
71
static long ROQ_YY_tab[256];
72
static long ROQ_UB_tab[256];
73
static long ROQ_UG_tab[256];
74
static long ROQ_VG_tab[256];
75
static long ROQ_VR_tab[256];
76
static unsigned short vq2[256*16*4];
77
static unsigned short vq4[256*64*4];
78
static unsigned short vq8[256*256*4];
82
byte linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2];
87
byte *qStatus[2][32768];
89
long oldXOff, oldYOff, oldysize, oldxsize;
95
char fileName[MAX_OSPATH];
96
int CIN_WIDTH, CIN_HEIGHT;
97
int xpos, ypos, width, height;
98
qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader;
101
unsigned int startTime;
102
unsigned int lastTime;
106
unsigned int RoQFrameSize;
113
void ( *VQ0)(byte *status, void *qdata );
114
void ( *VQ1)(byte *status, void *qdata );
115
void ( *VQNormal)(byte *status, void *qdata );
116
void ( *VQBuffer)(byte *status, void *qdata );
118
long samplesPerPixel; // defaults to 2
120
unsigned int xsize, ysize, maxsize, minsize;
122
qboolean half, smootheddouble, inMemory;
134
static cinematics_t cin;
135
static cin_cache cinTable[MAX_VIDEO_HANDLES];
136
static int currentHandle = -1;
137
static int CL_handle = -1;
139
extern int s_soundtime; // sample PAIRS
140
extern int s_paintedtime; // sample PAIRS
143
void CIN_CloseAllVideos(void) {
146
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
147
if (cinTable[i].fileName[0] != 0 ) {
148
CIN_StopCinematic(i);
154
static int CIN_HandleForVideo(void) {
157
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
158
if ( cinTable[i].fileName[0] == 0 ) {
162
Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" );
167
extern int CL_ScaledMilliseconds(void);
169
//-----------------------------------------------------------------------------
172
// Allocates and initializes the square table.
177
//-----------------------------------------------------------------------------
178
static void RllSetupTable( void )
182
for (z=0;z<128;z++) {
183
cin.sqrTable[z] = (short)(z*z);
184
cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]);
190
//-----------------------------------------------------------------------------
191
// RllDecodeMonoToMono
193
// Decode mono source data into a mono buffer.
195
// Parameters: from -> buffer holding encoded data
196
// to -> buffer to hold decoded data
197
// size = number of bytes of input (= # of shorts of output)
198
// signedOutput = 0 for unsigned output, non-zero for signed output
199
// flag = flags from asset header
201
// Returns: Number of samples placed in output buffer
202
//-----------------------------------------------------------------------------
203
long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag)
209
prev = flag - 0x8000;
213
for (z=0;z<size;z++) {
214
prev = to[z] = (short)(prev + cin.sqrTable[from[z]]);
216
return size; //*sizeof(short));
220
//-----------------------------------------------------------------------------
221
// RllDecodeMonoToStereo
223
// Decode mono source data into a stereo buffer. Output is 4 times the number
224
// of bytes in the input.
226
// Parameters: from -> buffer holding encoded data
227
// to -> buffer to hold decoded data
228
// size = number of bytes of input (= 1/4 # of bytes of output)
229
// signedOutput = 0 for unsigned output, non-zero for signed output
230
// flag = flags from asset header
232
// Returns: Number of samples placed in output buffer
233
//-----------------------------------------------------------------------------
234
long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag)
240
prev = flag - 0x8000;
244
for (z = 0; z < size; z++) {
245
prev = (short)(prev + cin.sqrTable[from[z]]);
246
to[z*2+0] = to[z*2+1] = (short)(prev);
249
return size; // * 2 * sizeof(short));
253
//-----------------------------------------------------------------------------
254
// RllDecodeStereoToStereo
256
// Decode stereo source data into a stereo buffer.
258
// Parameters: from -> buffer holding encoded data
259
// to -> buffer to hold decoded data
260
// size = number of bytes of input (= 1/2 # of bytes of output)
261
// signedOutput = 0 for unsigned output, non-zero for signed output
262
// flag = flags from asset header
264
// Returns: Number of samples placed in output buffer
265
//-----------------------------------------------------------------------------
266
long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
269
unsigned char *zz = from;
273
prevL = (flag & 0xff00) - 0x8000;
274
prevR = ((flag & 0x00ff) << 8) - 0x8000;
276
prevL = flag & 0xff00;
277
prevR = (flag & 0x00ff) << 8;
280
for (z=0;z<size;z+=2) {
281
prevL = (short)(prevL + cin.sqrTable[*zz++]);
282
prevR = (short)(prevR + cin.sqrTable[*zz++]);
283
to[z+0] = (short)(prevL);
284
to[z+1] = (short)(prevR);
287
return (size>>1); //*sizeof(short));
291
//-----------------------------------------------------------------------------
292
// RllDecodeStereoToMono
294
// Decode stereo source data into a mono buffer.
296
// Parameters: from -> buffer holding encoded data
297
// to -> buffer to hold decoded data
298
// size = number of bytes of input (= # of bytes of output)
299
// signedOutput = 0 for unsigned output, non-zero for signed output
300
// flag = flags from asset header
302
// Returns: Number of samples placed in output buffer
303
//-----------------------------------------------------------------------------
304
long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
310
prevL = (flag & 0xff00) - 0x8000;
311
prevR = ((flag & 0x00ff) << 8) -0x8000;
313
prevL = flag & 0xff00;
314
prevR = (flag & 0x00ff) << 8;
317
for (z=0;z<size;z+=1) {
318
prevL= prevL + cin.sqrTable[from[z*2]];
319
prevR = prevR + cin.sqrTable[from[z*2+1]];
320
to[z] = (short)((prevL + prevR)/2);
326
/******************************************************************************
332
******************************************************************************/
334
static void move8_32( byte *src, byte *dst, int spl )
338
for(i = 0; i < 8; ++i)
340
memcpy(dst, src, 32);
346
/******************************************************************************
352
******************************************************************************/
354
static void move4_32( byte *src, byte *dst, int spl )
358
for(i = 0; i < 4; ++i)
360
memcpy(dst, src, 16);
366
/******************************************************************************
372
******************************************************************************/
374
static void blit8_32( byte *src, byte *dst, int spl )
378
for(i = 0; i < 8; ++i)
380
memcpy(dst, src, 32);
386
/******************************************************************************
392
******************************************************************************/
393
static void blit4_32( byte *src, byte *dst, int spl )
397
for(i = 0; i < 4; ++i)
399
memmove(dst, src, 16);
405
/******************************************************************************
411
******************************************************************************/
413
static void blit2_32( byte *src, byte *dst, int spl )
416
memcpy(dst+spl, src+8, 8);
419
/******************************************************************************
425
******************************************************************************/
427
static void blitVQQuad32fs( byte **status, unsigned char *data )
429
unsigned short newd, celdata, code;
430
unsigned int index, i;
437
spl = cinTable[currentHandle].samplesPerLine;
442
celdata = data[0] + data[1]*256;
448
code = (unsigned short)(celdata&0xc000);
452
case 0x8000: // vq code
453
blit8_32( (byte *)&vq8[(*data)*128], status[index], spl );
462
celdata = data[0] + data[1]*256;
468
code = (unsigned short)(celdata&0xc000); celdata <<= 2;
470
switch (code) { // code in top two bits of code
471
case 0x8000: // 4x4 vq code
472
blit4_32( (byte *)&vq4[(*data)*32], status[index], spl );
475
case 0xc000: // 2x2 vq code
476
blit2_32( (byte *)&vq2[(*data)*8], status[index], spl );
478
blit2_32( (byte *)&vq2[(*data)*8], status[index]+8, spl );
480
blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2, spl );
482
blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2+8, spl );
485
case 0x4000: // motion compensation
486
move4_32( status[index] + cin.mcomp[(*data)], status[index], spl );
493
case 0x4000: // motion compensation
494
move8_32( status[index] + cin.mcomp[(*data)], status[index], spl );
502
} while ( status[index] != NULL );
505
/******************************************************************************
511
******************************************************************************/
513
static void ROQ_GenYUVTables( void )
515
float t_ub,t_vr,t_ug,t_vg;
518
t_ub = (1.77200f/2.0f) * (float)(1<<6) + 0.5f;
519
t_vr = (1.40200f/2.0f) * (float)(1<<6) + 0.5f;
520
t_ug = (0.34414f/2.0f) * (float)(1<<6) + 0.5f;
521
t_vg = (0.71414f/2.0f) * (float)(1<<6) + 0.5f;
523
float x = (float)(2 * i - 255);
525
ROQ_UB_tab[i] = (long)( ( t_ub * x) + (1<<5));
526
ROQ_VR_tab[i] = (long)( ( t_vr * x) + (1<<5));
527
ROQ_UG_tab[i] = (long)( (-t_ug * x) );
528
ROQ_VG_tab[i] = (long)( (-t_vg * x) + (1<<5));
529
ROQ_YY_tab[i] = (long)( (i << 6) | (i >> 2) );
533
#define VQ2TO4(a,b,c,d) { \
556
#define VQ2TO2(a,b,c,d) { \
569
/******************************************************************************
575
******************************************************************************/
577
static unsigned short yuv_to_rgb( long y, long u, long v )
579
long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
581
r = (YY + ROQ_VR_tab[v]) >> 9;
582
g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8;
583
b = (YY + ROQ_UB_tab[u]) >> 9;
585
if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0;
586
if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31;
588
return (unsigned short)((r<<11)+(g<<5)+(b));
591
/******************************************************************************
597
******************************************************************************/
598
static unsigned int yuv_to_rgb24( long y, long u, long v )
600
long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
602
r = (YY + ROQ_VR_tab[v]) >> 6;
603
g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6;
604
b = (YY + ROQ_UB_tab[u]) >> 6;
606
if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0;
607
if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255;
609
return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24));
612
/******************************************************************************
618
******************************************************************************/
620
static void decodeCodeBook( byte *input, unsigned short roq_flags )
622
long i, j, two, four;
623
unsigned short *aptr, *bptr, *cptr, *dptr;
624
long y0,y1,y2,y3,cr,cb;
625
byte *bbptr, *baptr, *bcptr, *bdptr;
626
unsigned int *iaptr, *ibptr, *icptr, *idptr;
633
four = roq_flags&0xff;
638
bptr = (unsigned short *)vq2;
640
if (!cinTable[currentHandle].half) {
641
if (!cinTable[currentHandle].smootheddouble) {
645
if (cinTable[currentHandle].samplesPerPixel==2) {
653
*bptr++ = yuv_to_rgb( y0, cr, cb );
654
*bptr++ = yuv_to_rgb( y1, cr, cb );
655
*bptr++ = yuv_to_rgb( y2, cr, cb );
656
*bptr++ = yuv_to_rgb( y3, cr, cb );
659
cptr = (unsigned short *)vq4;
660
dptr = (unsigned short *)vq8;
662
for(i=0;i<four;i++) {
663
aptr = (unsigned short *)vq2 + (*input++)*4;
664
bptr = (unsigned short *)vq2 + (*input++)*4;
666
VQ2TO4(aptr,bptr,cptr,dptr);
668
} else if (cinTable[currentHandle].samplesPerPixel==4) {
669
ibptr = (unsigned int *)bptr;
677
*ibptr++ = yuv_to_rgb24( y0, cr, cb );
678
*ibptr++ = yuv_to_rgb24( y1, cr, cb );
679
*ibptr++ = yuv_to_rgb24( y2, cr, cb );
680
*ibptr++ = yuv_to_rgb24( y3, cr, cb );
683
icptr = (unsigned int *)vq4;
684
idptr = (unsigned int *)vq8;
686
for(i=0;i<four;i++) {
687
iaptr = (unsigned int *)vq2 + (*input++)*4;
688
ibptr = (unsigned int *)vq2 + (*input++)*4;
690
VQ2TO4(iaptr, ibptr, icptr, idptr);
692
} else if (cinTable[currentHandle].samplesPerPixel==1) {
693
bbptr = (byte *)bptr;
695
*bbptr++ = cinTable[currentHandle].gray[*input++];
696
*bbptr++ = cinTable[currentHandle].gray[*input++];
697
*bbptr++ = cinTable[currentHandle].gray[*input++];
698
*bbptr++ = cinTable[currentHandle].gray[*input]; input +=3;
704
for(i=0;i<four;i++) {
705
baptr = (byte *)vq2 + (*input++)*4;
706
bbptr = (byte *)vq2 + (*input++)*4;
708
VQ2TO4(baptr,bbptr,bcptr,bdptr);
713
// double height, smoothed
715
if (cinTable[currentHandle].samplesPerPixel==2) {
723
*bptr++ = yuv_to_rgb( y0, cr, cb );
724
*bptr++ = yuv_to_rgb( y1, cr, cb );
725
*bptr++ = yuv_to_rgb( ((y0*3)+y2)/4, cr, cb );
726
*bptr++ = yuv_to_rgb( ((y1*3)+y3)/4, cr, cb );
727
*bptr++ = yuv_to_rgb( (y0+(y2*3))/4, cr, cb );
728
*bptr++ = yuv_to_rgb( (y1+(y3*3))/4, cr, cb );
729
*bptr++ = yuv_to_rgb( y2, cr, cb );
730
*bptr++ = yuv_to_rgb( y3, cr, cb );
733
cptr = (unsigned short *)vq4;
734
dptr = (unsigned short *)vq8;
736
for(i=0;i<four;i++) {
737
aptr = (unsigned short *)vq2 + (*input++)*8;
738
bptr = (unsigned short *)vq2 + (*input++)*8;
740
VQ2TO4(aptr,bptr,cptr,dptr);
741
VQ2TO4(aptr,bptr,cptr,dptr);
744
} else if (cinTable[currentHandle].samplesPerPixel==4) {
745
ibptr = (unsigned int *)bptr;
753
*ibptr++ = yuv_to_rgb24( y0, cr, cb );
754
*ibptr++ = yuv_to_rgb24( y1, cr, cb );
755
*ibptr++ = yuv_to_rgb24( ((y0*3)+y2)/4, cr, cb );
756
*ibptr++ = yuv_to_rgb24( ((y1*3)+y3)/4, cr, cb );
757
*ibptr++ = yuv_to_rgb24( (y0+(y2*3))/4, cr, cb );
758
*ibptr++ = yuv_to_rgb24( (y1+(y3*3))/4, cr, cb );
759
*ibptr++ = yuv_to_rgb24( y2, cr, cb );
760
*ibptr++ = yuv_to_rgb24( y3, cr, cb );
763
icptr = (unsigned int *)vq4;
764
idptr = (unsigned int *)vq8;
766
for(i=0;i<four;i++) {
767
iaptr = (unsigned int *)vq2 + (*input++)*8;
768
ibptr = (unsigned int *)vq2 + (*input++)*8;
770
VQ2TO4(iaptr, ibptr, icptr, idptr);
771
VQ2TO4(iaptr, ibptr, icptr, idptr);
774
} else if (cinTable[currentHandle].samplesPerPixel==1) {
775
bbptr = (byte *)bptr;
780
y3 = (long)*input; input+= 3;
781
*bbptr++ = cinTable[currentHandle].gray[y0];
782
*bbptr++ = cinTable[currentHandle].gray[y1];
783
*bbptr++ = cinTable[currentHandle].gray[((y0*3)+y2)/4];
784
*bbptr++ = cinTable[currentHandle].gray[((y1*3)+y3)/4];
785
*bbptr++ = cinTable[currentHandle].gray[(y0+(y2*3))/4];
786
*bbptr++ = cinTable[currentHandle].gray[(y1+(y3*3))/4];
787
*bbptr++ = cinTable[currentHandle].gray[y2];
788
*bbptr++ = cinTable[currentHandle].gray[y3];
794
for(i=0;i<four;i++) {
795
baptr = (byte *)vq2 + (*input++)*8;
796
bbptr = (byte *)vq2 + (*input++)*8;
798
VQ2TO4(baptr,bbptr,bcptr,bdptr);
799
VQ2TO4(baptr,bbptr,bcptr,bdptr);
808
if (cinTable[currentHandle].samplesPerPixel==2) {
810
y0 = (long)*input; input+=2;
811
y2 = (long)*input; input+=2;
814
*bptr++ = yuv_to_rgb( y0, cr, cb );
815
*bptr++ = yuv_to_rgb( y2, cr, cb );
818
cptr = (unsigned short *)vq4;
819
dptr = (unsigned short *)vq8;
821
for(i=0;i<four;i++) {
822
aptr = (unsigned short *)vq2 + (*input++)*2;
823
bptr = (unsigned short *)vq2 + (*input++)*2;
825
VQ2TO2(aptr,bptr,cptr,dptr);
828
} else if (cinTable[currentHandle].samplesPerPixel == 1) {
829
bbptr = (byte *)bptr;
832
*bbptr++ = cinTable[currentHandle].gray[*input]; input+=2;
833
*bbptr++ = cinTable[currentHandle].gray[*input]; input+=4;
839
for(i=0;i<four;i++) {
840
baptr = (byte *)vq2 + (*input++)*2;
841
bbptr = (byte *)vq2 + (*input++)*2;
843
VQ2TO2(baptr,bbptr,bcptr,bdptr);
846
} else if (cinTable[currentHandle].samplesPerPixel == 4) {
847
ibptr = (unsigned int *) bptr;
849
y0 = (long)*input; input+=2;
850
y2 = (long)*input; input+=2;
853
*ibptr++ = yuv_to_rgb24( y0, cr, cb );
854
*ibptr++ = yuv_to_rgb24( y2, cr, cb );
857
icptr = (unsigned int *)vq4;
858
idptr = (unsigned int *)vq8;
860
for(i=0;i<four;i++) {
861
iaptr = (unsigned int *)vq2 + (*input++)*2;
862
ibptr = (unsigned int *)vq2 + (*input++)*2;
864
VQ2TO2(iaptr,ibptr,icptr,idptr);
871
/******************************************************************************
877
******************************************************************************/
879
static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff )
882
long bigx, bigy, lowx, lowy, useY;
885
offset = cinTable[currentHandle].screenDelta;
888
bigx = cinTable[currentHandle].xsize;
889
bigy = cinTable[currentHandle].ysize;
891
if (bigx > cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH;
892
if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT;
894
if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) {
896
scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel);
898
cin.qStatus[0][cinTable[currentHandle].onQuad ] = scroff;
899
cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset;
902
if ( quadSize != MINSIZE ) {
904
recurseQuad( startX, startY , quadSize, xOff, yOff );
905
recurseQuad( startX+quadSize, startY , quadSize, xOff, yOff );
906
recurseQuad( startX, startY+quadSize , quadSize, xOff, yOff );
907
recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff );
912
/******************************************************************************
918
******************************************************************************/
920
static void setupQuad( long xOff, long yOff )
922
long numQuadCels, i,x,y;
925
if (xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == cin.oldysize && cinTable[currentHandle].xsize == cin.oldxsize) {
931
cin.oldysize = cinTable[currentHandle].ysize;
932
cin.oldxsize = cinTable[currentHandle].xsize;
934
numQuadCels = (cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].CIN_HEIGHT) / (16);
935
numQuadCels += numQuadCels/4 + numQuadCels/16;
936
numQuadCels += 64; // for overflow
938
numQuadCels = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16);
939
numQuadCels += numQuadCels/4;
940
numQuadCels += 64; // for overflow
942
cinTable[currentHandle].onQuad = 0;
944
for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16)
945
for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16)
946
recurseQuad( x, y, 16, xOff, yOff );
950
for(i=(numQuadCels-64);i<numQuadCels;i++) {
951
cin.qStatus[0][i] = temp; // eoq
952
cin.qStatus[1][i] = temp; // eoq
956
/******************************************************************************
962
******************************************************************************/
964
static void readQuadInfo( byte *qData )
966
if (currentHandle < 0) return;
968
cinTable[currentHandle].xsize = qData[0]+qData[1]*256;
969
cinTable[currentHandle].ysize = qData[2]+qData[3]*256;
970
cinTable[currentHandle].maxsize = qData[4]+qData[5]*256;
971
cinTable[currentHandle].minsize = qData[6]+qData[7]*256;
973
cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize;
974
cinTable[currentHandle].CIN_WIDTH = cinTable[currentHandle].xsize;
976
cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].samplesPerPixel;
977
cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT*cinTable[currentHandle].samplesPerLine;
979
cinTable[currentHandle].half = qfalse;
980
cinTable[currentHandle].smootheddouble = qfalse;
982
cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal;
983
cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer;
985
cinTable[currentHandle].t[0] = cinTable[currentHandle].screenDelta;
986
cinTable[currentHandle].t[1] = -cinTable[currentHandle].screenDelta;
988
cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH;
989
cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT;
991
// rage pro is very slow at 512 wide textures, voodoo can't do it at all
992
if ( glConfig.hardwareType == GLHW_RAGEPRO || glConfig.maxTextureSize <= 256) {
993
if (cinTable[currentHandle].drawX>256) {
994
cinTable[currentHandle].drawX = 256;
996
if (cinTable[currentHandle].drawY>256) {
997
cinTable[currentHandle].drawY = 256;
999
if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) {
1000
Com_Printf("HACK: approxmimating cinematic for Rage Pro or Voodoo\n");
1005
/******************************************************************************
1011
******************************************************************************/
1013
static void RoQPrepMcomp( long xoff, long yoff )
1015
long i, j, x, y, temp, temp2;
1017
i=cinTable[currentHandle].samplesPerLine; j=cinTable[currentHandle].samplesPerPixel;
1018
if ( cinTable[currentHandle].xsize == (cinTable[currentHandle].ysize*4) && !cinTable[currentHandle].half ) { j = j+j; i = i+i; }
1021
temp2 = (y+yoff-8)*i;
1023
temp = (x+xoff-8)*j;
1024
cin.mcomp[(x*16)+y] = cinTable[currentHandle].normalBuffer0-(temp2+temp);
1029
/******************************************************************************
1035
******************************************************************************/
1037
static void initRoQ( void )
1039
if (currentHandle < 0) return;
1041
cinTable[currentHandle].VQNormal = (void (*)(byte *, void *))blitVQQuad32fs;
1042
cinTable[currentHandle].VQBuffer = (void (*)(byte *, void *))blitVQQuad32fs;
1043
cinTable[currentHandle].samplesPerPixel = 4;
1048
/******************************************************************************
1054
******************************************************************************/
1056
static byte* RoQFetchInterlaced( byte *source ) {
1059
if (currentHandle < 0) return NULL;
1061
src = (int *)source;
1062
dst = (int *)cinTable[currentHandle].buf2;
1064
for(x=0;x<256*256;x++) {
1068
return cinTable[currentHandle].buf2;
1071
static void RoQReset( void ) {
1073
if (currentHandle < 0) return;
1075
Sys_EndStreamedFile(cinTable[currentHandle].iFile);
1076
FS_FCloseFile( cinTable[currentHandle].iFile );
1077
FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
1078
// let the background thread start reading ahead
1079
Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 );
1080
Sys_StreamedRead (cin.file, 16, 1, cinTable[currentHandle].iFile);
1082
cinTable[currentHandle].status = FMV_LOOPED;
1085
/******************************************************************************
1091
******************************************************************************/
1093
static void RoQInterrupt(void)
1099
if (currentHandle < 0) return;
1101
Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile );
1102
if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
1103
if (cinTable[currentHandle].holdAtEnd==qfalse) {
1104
if (cinTable[currentHandle].looping) {
1107
cinTable[currentHandle].status = FMV_EOF;
1110
cinTable[currentHandle].status = FMV_IDLE;
1115
framedata = cin.file;
1117
// new frame is ready
1120
switch(cinTable[currentHandle].roq_id)
1123
if ((cinTable[currentHandle].numQuads&1)) {
1124
cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1];
1125
RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
1126
cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata);
1127
cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta;
1129
cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0];
1130
RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
1131
cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata );
1132
cinTable[currentHandle].buf = cin.linbuf;
1134
if (cinTable[currentHandle].numQuads == 0) { // first frame
1135
Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize);
1137
cinTable[currentHandle].numQuads++;
1138
cinTable[currentHandle].dirty = qtrue;
1141
decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags );
1144
if (!cinTable[currentHandle].silent) {
1145
ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
1146
S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f );
1149
case ZA_SOUND_STEREO:
1150
if (!cinTable[currentHandle].silent) {
1151
if (cinTable[currentHandle].numQuads == -1) {
1153
s_rawend = s_soundtime;
1155
ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
1156
S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f );
1160
if (cinTable[currentHandle].numQuads == -1) {
1161
readQuadInfo( framedata );
1163
// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
1164
cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value;
1166
if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0;
1169
cinTable[currentHandle].inMemory = cinTable[currentHandle].roq_flags;
1170
cinTable[currentHandle].RoQFrameSize = 0; // for header
1173
cinTable[currentHandle].RoQFrameSize = 0;
1178
cinTable[currentHandle].status = FMV_EOF;
1182
// read in next frame data
1184
if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
1185
if (cinTable[currentHandle].holdAtEnd==qfalse) {
1186
if (cinTable[currentHandle].looping) {
1189
cinTable[currentHandle].status = FMV_EOF;
1192
cinTable[currentHandle].status = FMV_IDLE;
1197
framedata += cinTable[currentHandle].RoQFrameSize;
1198
cinTable[currentHandle].roq_id = framedata[0] + framedata[1]*256;
1199
cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536;
1200
cinTable[currentHandle].roq_flags = framedata[6] + framedata[7]*256;
1201
cinTable[currentHandle].roqF0 = (char)framedata[7];
1202
cinTable[currentHandle].roqF1 = (char)framedata[6];
1204
if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) {
1205
Com_DPrintf("roq_size>65536||roq_id==0x1084\n");
1206
cinTable[currentHandle].status = FMV_EOF;
1207
if (cinTable[currentHandle].looping) {
1212
if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF)) { cinTable[currentHandle].inMemory--; framedata += 8; goto redump; }
1214
// one more frame hits the dust
1216
// assert(cinTable[currentHandle].RoQFrameSize <= 65536);
1217
// r = Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile );
1218
cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize+8;
1221
/******************************************************************************
1227
******************************************************************************/
1229
static void RoQ_init( void )
1231
// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
1232
cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds()*com_timescale->value;
1234
cinTable[currentHandle].RoQPlayed = 24;
1236
/* get frame rate */
1237
cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7]*256;
1239
if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30;
1241
cinTable[currentHandle].numQuads = -1;
1243
cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9]*256;
1244
cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11]*256 + cin.file[12]*65536;
1245
cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15]*256;
1247
if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) {
1253
/******************************************************************************
1259
******************************************************************************/
1261
static void RoQShutdown( void ) {
1264
if (!cinTable[currentHandle].buf) {
1268
if ( cinTable[currentHandle].status == FMV_IDLE ) {
1271
Com_DPrintf("finished cinematic\n");
1272
cinTable[currentHandle].status = FMV_IDLE;
1274
if (cinTable[currentHandle].iFile) {
1275
Sys_EndStreamedFile( cinTable[currentHandle].iFile );
1276
FS_FCloseFile( cinTable[currentHandle].iFile );
1277
cinTable[currentHandle].iFile = 0;
1280
if (cinTable[currentHandle].alterGameState) {
1281
cls.state = CA_DISCONNECTED;
1282
// we can't just do a vstr nextmap, because
1283
// if we are aborting the intro cinematic with
1284
// a devmap command, nextmap would be valid by
1285
// the time it was referenced
1286
s = Cvar_VariableString( "nextmap" );
1288
Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) );
1289
Cvar_Set( "nextmap", "" );
1293
cinTable[currentHandle].fileName[0] = 0;
1302
e_status CIN_StopCinematic(int handle) {
1304
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
1305
currentHandle = handle;
1307
Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName);
1309
if (!cinTable[currentHandle].buf) {
1313
if (cinTable[currentHandle].alterGameState) {
1314
if ( cls.state != CA_CINEMATIC ) {
1315
return cinTable[currentHandle].status;
1318
cinTable[currentHandle].status = FMV_EOF;
1328
Fetch and decompress the pending frame
1333
e_status CIN_RunCinematic (int handle)
1339
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
1341
if (cin.currentHandle != handle) {
1342
currentHandle = handle;
1343
cin.currentHandle = currentHandle;
1344
cinTable[currentHandle].status = FMV_EOF;
1348
if (cinTable[handle].playonwalls < -1)
1350
return cinTable[handle].status;
1353
currentHandle = handle;
1355
if (cinTable[currentHandle].alterGameState) {
1356
if ( cls.state != CA_CINEMATIC ) {
1357
return cinTable[currentHandle].status;
1361
if (cinTable[currentHandle].status == FMV_IDLE) {
1362
return cinTable[currentHandle].status;
1365
// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
1366
thisTime = CL_ScaledMilliseconds()*com_timescale->value;
1367
if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime))>100) {
1368
cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime;
1370
// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
1371
cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*3)/100);
1373
start = cinTable[currentHandle].startTime;
1374
while( (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads)
1375
&& (cinTable[currentHandle].status == FMV_PLAY) )
1378
if (start != cinTable[currentHandle].startTime) {
1379
// we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer
1380
cinTable[currentHandle].tfps = ((((CL_ScaledMilliseconds()*com_timescale->value)
1381
- cinTable[currentHandle].startTime)*3)/100);
1382
start = cinTable[currentHandle].startTime;
1386
cinTable[currentHandle].lastTime = thisTime;
1388
if (cinTable[currentHandle].status == FMV_LOOPED) {
1389
cinTable[currentHandle].status = FMV_PLAY;
1392
if (cinTable[currentHandle].status == FMV_EOF) {
1393
if (cinTable[currentHandle].looping) {
1400
return cinTable[currentHandle].status;
1409
int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) {
1410
unsigned short RoQID;
1411
char name[MAX_OSPATH];
1414
if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) {
1415
Com_sprintf (name, sizeof(name), "video/%s", arg);
1417
Com_sprintf (name, sizeof(name), "%s", arg);
1420
if (!(systemBits & CIN_system)) {
1421
for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
1422
if (!strcmp(cinTable[i].fileName, name) ) {
1428
Com_DPrintf("SCR_PlayCinematic( %s )\n", arg);
1430
Com_Memset(&cin, 0, sizeof(cinematics_t) );
1431
currentHandle = CIN_HandleForVideo();
1433
cin.currentHandle = currentHandle;
1435
strcpy(cinTable[currentHandle].fileName, name);
1437
cinTable[currentHandle].ROQSize = 0;
1438
cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
1440
if (cinTable[currentHandle].ROQSize<=0) {
1441
Com_DPrintf("play(%s), ROQSize<=0\n", arg);
1442
cinTable[currentHandle].fileName[0] = 0;
1446
CIN_SetExtents(currentHandle, x, y, w, h);
1447
CIN_SetLooping(currentHandle, (systemBits & CIN_loop)!=0);
1449
cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
1450
cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH;
1451
cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0;
1452
cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0;
1453
cinTable[currentHandle].playonwalls = 1;
1454
cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0;
1455
cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0;
1457
if (cinTable[currentHandle].alterGameState) {
1460
VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
1463
cinTable[currentHandle].playonwalls = cl_inGameVideo->integer;
1468
FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
1470
RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256;
1471
if (RoQID == 0x1084)
1474
// FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile);
1475
// let the background thread start reading ahead
1476
Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 );
1478
cinTable[currentHandle].status = FMV_PLAY;
1479
Com_DPrintf("trFMV::play(), playing %s\n", arg);
1481
if (cinTable[currentHandle].alterGameState) {
1482
cls.state = CA_CINEMATIC;
1487
s_rawend = s_soundtime;
1489
return currentHandle;
1491
Com_DPrintf("trFMV::play(), invalid RoQ ID\n");
1497
void CIN_SetExtents (int handle, int x, int y, int w, int h) {
1498
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1499
cinTable[handle].xpos = x;
1500
cinTable[handle].ypos = y;
1501
cinTable[handle].width = w;
1502
cinTable[handle].height = h;
1503
cinTable[handle].dirty = qtrue;
1506
void CIN_SetLooping(int handle, qboolean loop) {
1507
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1508
cinTable[handle].looping = loop;
1517
void CIN_DrawCinematic (int handle) {
1521
if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1523
if (!cinTable[handle].buf) {
1527
x = cinTable[handle].xpos;
1528
y = cinTable[handle].ypos;
1529
w = cinTable[handle].width;
1530
h = cinTable[handle].height;
1531
buf = cinTable[handle].buf;
1532
SCR_AdjustFrom640( &x, &y, &w, &h );
1534
if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) {
1535
int ix, iy, *buf2, *buf3, xm, ym, ll;
1537
xm = cinTable[handle].CIN_WIDTH/256;
1538
ym = cinTable[handle].CIN_HEIGHT/256;
1540
if (cinTable[handle].CIN_WIDTH==512) {
1545
buf2 = Hunk_AllocateTempMemory( 256*256*4 );
1546
if (xm==2 && ym==2) {
1552
for (iy = 0; iy<256; iy++) {
1554
for (ix = 0; ix<2048; ix+=8) {
1555
for(ic = ix;ic<(ix+4);ic++) {
1556
*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2;
1561
} else if (xm==2 && ym==1) {
1567
for (iy = 0; iy<256; iy++) {
1569
for (ix = 0; ix<2048; ix+=8) {
1570
for(ic = ix;ic<(ix+4);ic++) {
1571
*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1;
1577
for (iy = 0; iy<256; iy++) {
1578
for (ix = 0; ix<256; ix++) {
1579
buf2[(iy<<8)+ix] = buf3[((iy*ym)<<ll) + (ix*xm)];
1583
re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue);
1584
cinTable[handle].dirty = qfalse;
1585
Hunk_FreeTempMemory(buf2);
1589
re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty);
1590
cinTable[handle].dirty = qfalse;
1593
void CL_PlayCinematic_f(void) {
1596
int bits = CIN_system;
1598
Com_DPrintf("CL_PlayCinematic_f\n");
1599
if (cls.state == CA_CINEMATIC) {
1600
SCR_StopCinematic();
1603
arg = Cmd_Argv( 1 );
1607
if ((s && s[0] == '1') || Q_stricmp(arg,"demoend.roq")==0 || Q_stricmp(arg,"end.roq")==0) {
1610
if (s && s[0] == '2') {
1616
CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits );
1617
if (CL_handle >= 0) {
1620
} while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY); // wait for first frame (load codebook and sound)
1625
void SCR_DrawCinematic (void) {
1626
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
1627
CIN_DrawCinematic(CL_handle);
1631
void SCR_RunCinematic (void)
1633
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
1634
CIN_RunCinematic(CL_handle);
1638
void SCR_StopCinematic(void) {
1639
if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
1640
CIN_StopCinematic(CL_handle);
1646
void CIN_UploadCinematic(int handle) {
1647
if (handle >= 0 && handle < MAX_VIDEO_HANDLES) {
1648
if (!cinTable[handle].buf) {
1651
if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) {
1652
if (cinTable[handle].playonwalls == 0) {
1653
cinTable[handle].playonwalls = -1;
1655
if (cinTable[handle].playonwalls == -1) {
1656
cinTable[handle].playonwalls = -2;
1658
cinTable[handle].dirty = qfalse;
1662
re.UploadCinematic( 256, 256, 256, 256, cinTable[handle].buf, handle, cinTable[handle].dirty);
1663
if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) {
1664
cinTable[handle].playonwalls--;