1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 #include "../server/exe_headers.h"
26 
27 /*****************************************************************************
28  * name:		cl_cin.c
29  *
30  * desc:		video and cinematic playback
31  *
32  * $Archive: /MissionPack/code/client/cl_cin.c $
33  * $Author: Ttimo $
34  * $Revision: 82 $
35  * $Modtime: 4/13/01 4:48p $
36  * $Date: 4/13/01 4:48p $
37  *
38  * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256
39  *
40  *****************************************************************************/
41 
42 #include "client.h"
43 #include "client_ui.h"	// CHC
44 #include "snd_local.h"
45 #include "qcommon/stringed_ingame.h"
46 
47 #define MAXSIZE				8
48 #define MINSIZE				4
49 
50 #define DEFAULT_CIN_WIDTH	512
51 #define DEFAULT_CIN_HEIGHT	512
52 
53 #define ROQ_QUAD			0x1000
54 #define ROQ_QUAD_INFO		0x1001
55 #define ROQ_CODEBOOK		0x1002
56 #define ROQ_QUAD_VQ			0x1011
57 #define ROQ_QUAD_JPEG		0x1012
58 #define ROQ_QUAD_HANG		0x1013
59 #define ROQ_PACKET			0x1030
60 #define ZA_SOUND_MONO		0x1020
61 #define ZA_SOUND_STEREO		0x1021
62 
63 #define MAX_VIDEO_HANDLES	16
64 
65 extern void S_CIN_StopSound(sfxHandle_t sfxHandle);
66 static void RoQ_init( void );
67 
68 /******************************************************************************
69 *
70 * Class:		trFMV
71 *
72 * Description:	RoQ/RnR manipulation routines
73 *				not entirely complete for first run
74 *
75 ******************************************************************************/
76 
77 static	long				ROQ_YY_tab[256];
78 static	long				ROQ_UB_tab[256];
79 static	long				ROQ_UG_tab[256];
80 static	long				ROQ_VG_tab[256];
81 static	long				ROQ_VR_tab[256];
82 static	unsigned short		vq2[256*16*4];
83 static	unsigned short		vq4[256*64*4];
84 static	unsigned short		vq8[256*256*4];
85 
86 typedef struct {
87 	byte				linbuf[DEFAULT_CIN_WIDTH*DEFAULT_CIN_HEIGHT*4*2];
88 	byte				file[65536];
89 	short				sqrTable[256];
90 
91 	int					mcomp[256];
92 	byte				*qStatus[2][32768];
93 
94 	long				oldXOff, oldYOff, oldysize, oldxsize;
95 
96 	int					currentHandle;
97 } cinematics_t;
98 
99 typedef struct {
100 	char				fileName[MAX_OSPATH];
101 	int					CIN_WIDTH, CIN_HEIGHT;
102 	int					xpos, ypos, width, height;
103 	qboolean			looping, holdAtEnd, dirty, alterGameState, silent, shader;
104 	fileHandle_t		iFile;	// 0 = none
105 	e_status			status;
106 	unsigned int		startTime;
107 	unsigned int		lastTime;
108 	long				tfps;
109 	long				RoQPlayed;
110 	long				ROQSize;
111 	unsigned int		RoQFrameSize;
112 	long				onQuad;
113 	long				numQuads;
114 	long				samplesPerLine;
115 	unsigned int		roq_id;
116 	long				screenDelta;
117 
118 	void ( *VQ0)(byte *status, void *qdata );
119 	void ( *VQ1)(byte *status, void *qdata );
120 	void ( *VQNormal)(byte *status, void *qdata );
121 	void ( *VQBuffer)(byte *status, void *qdata );
122 
123 	long        samplesPerPixel;        // defaults to 2
124 	byte*				gray;
125 	unsigned int		xsize, ysize, maxsize, minsize;
126 
127 	qboolean		half, smootheddouble, inMemory;
128 	long				normalBuffer0;
129 	long				roq_flags;
130 	long				roqF0;
131 	long				roqF1;
132 	long				t[2];
133 	long				roqFPS;
134 	int					playonwalls;
135 	byte*				buf;
136 	long				drawX, drawY;
137 	sfxHandle_t	hSFX;	// 0 = none
138 	qhandle_t		hCRAWLTEXT;	// 0 = none
139 } cin_cache;
140 
141 static cinematics_t		cin;
142 static cin_cache		cinTable[MAX_VIDEO_HANDLES];
143 static int				currentHandle = -1;
144 static int				CL_handle = -1;
145 static int				CL_iPlaybackStartTime;	// so I can stop users quitting playback <1 second after it starts
146 
147 extern int				s_soundtime;		// sample PAIRS
148 extern int   			s_paintedtime; 		// sample PAIRS
149 
150 
CIN_CloseAllVideos(void)151 void CIN_CloseAllVideos(void) {
152 	int		i;
153 
154 	for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
155 		if (cinTable[i].fileName[0] != 0 ) {
156 			CIN_StopCinematic(i);
157 		}
158 	}
159 }
160 
161 
CIN_HandleForVideo(void)162 static int CIN_HandleForVideo(void) {
163 	int		i;
164 
165 	for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
166 		if ( cinTable[i].fileName[0] == 0 ) {
167 			return i;
168 		}
169 	}
170 	Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" );
171 	return -1;
172 }
173 
174 
175 
176 
177 //-----------------------------------------------------------------------------
178 // RllSetupTable
179 //
180 // Allocates and initializes the square table.
181 //
182 // Parameters:	None
183 //
184 // Returns:		Nothing
185 //-----------------------------------------------------------------------------
RllSetupTable(void)186 static void RllSetupTable( void )
187 {
188 	int z;
189 
190 	for (z=0;z<128;z++) {
191 		cin.sqrTable[z] = (short)(z*z);
192 		cin.sqrTable[z+128] = (short)(-cin.sqrTable[z]);
193 	}
194 }
195 
196 
197 
198 //-----------------------------------------------------------------------------
199 // RllDecodeMonoToMono
200 //
201 // Decode mono source data into a mono buffer.
202 //
203 // Parameters:	from -> buffer holding encoded data
204 //				to ->	buffer to hold decoded data
205 //				size =	number of bytes of input (= # of shorts of output)
206 //				signedOutput = 0 for unsigned output, non-zero for signed output
207 //				flag = flags from asset header
208 //
209 // Returns:		Number of samples placed in output buffer
210 //-----------------------------------------------------------------------------
211 /*
212 static long RllDecodeMonoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput ,unsigned short flag)
213 {
214 	unsigned int z;
215 	int prev;
216 
217 	if (signedOutput)
218 		prev =  flag - 0x8000;
219 	else
220 		prev = flag;
221 
222 	for (z=0;z<size;z++) {
223 		prev = to[z] = (short)(prev + cin.sqrTable[from[z]]);
224 	}
225 	return size;	//*sizeof(short));
226 }
227 */
228 
229 //-----------------------------------------------------------------------------
230 // RllDecodeMonoToStereo
231 //
232 // Decode mono source data into a stereo buffer. Output is 4 times the number
233 // of bytes in the input.
234 //
235 // Parameters:	from -> buffer holding encoded data
236 //				to ->	buffer to hold decoded data
237 //				size =	number of bytes of input (= 1/4 # of bytes of output)
238 //				signedOutput = 0 for unsigned output, non-zero for signed output
239 //				flag = flags from asset header
240 //
241 // Returns:		Number of samples placed in output buffer
242 //-----------------------------------------------------------------------------
RllDecodeMonoToStereo(unsigned char * from,short * to,unsigned int size,char signedOutput,unsigned short flag)243 static long RllDecodeMonoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag)
244 {
245 	unsigned int z;
246 	int prev;
247 
248 	if (signedOutput)
249 		prev =  flag - 0x8000;
250 	else
251 		prev = flag;
252 
253 	for (z = 0; z < size; z++) {
254 		prev = (short)(prev + cin.sqrTable[from[z]]);
255 		to[z*2+0] = to[z*2+1] = (short)(prev);
256 	}
257 
258 	return size;	// * 2 * sizeof(short));
259 }
260 
261 
262 //-----------------------------------------------------------------------------
263 // RllDecodeStereoToStereo
264 //
265 // Decode stereo source data into a stereo buffer.
266 //
267 // Parameters:	from -> buffer holding encoded data
268 //				to ->	buffer to hold decoded data
269 //				size =	number of bytes of input (= 1/2 # of bytes of output)
270 //				signedOutput = 0 for unsigned output, non-zero for signed output
271 //				flag = flags from asset header
272 //
273 // Returns:		Number of samples placed in output buffer
274 //-----------------------------------------------------------------------------
RllDecodeStereoToStereo(unsigned char * from,short * to,unsigned int size,char signedOutput,unsigned short flag)275 static long RllDecodeStereoToStereo(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
276 {
277 	unsigned int z;
278 	unsigned char *zz = from;
279 	int	prevL, prevR;
280 
281 	if (signedOutput) {
282 		prevL = (flag & 0xff00) - 0x8000;
283 		prevR = ((flag & 0x00ff) << 8) - 0x8000;
284 	} else {
285 		prevL = flag & 0xff00;
286 		prevR = (flag & 0x00ff) << 8;
287 	}
288 
289 	for (z=0;z<size;z+=2) {
290                 prevL = (short)(prevL + cin.sqrTable[*zz++]);
291                 prevR = (short)(prevR + cin.sqrTable[*zz++]);
292                 to[z+0] = (short)(prevL);
293                 to[z+1] = (short)(prevR);
294 	}
295 
296 	return (size>>1);	//*sizeof(short));
297 }
298 
299 
300 //-----------------------------------------------------------------------------
301 // RllDecodeStereoToMono
302 //
303 // Decode stereo source data into a mono buffer.
304 //
305 // Parameters:	from -> buffer holding encoded data
306 //				to ->	buffer to hold decoded data
307 //				size =	number of bytes of input (= # of bytes of output)
308 //				signedOutput = 0 for unsigned output, non-zero for signed output
309 //				flag = flags from asset header
310 //
311 // Returns:		Number of samples placed in output buffer
312 //-----------------------------------------------------------------------------
313 /*
314 static long RllDecodeStereoToMono(unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag)
315 {
316 	unsigned int z;
317 	int prevL,prevR;
318 
319 	if (signedOutput) {
320 		prevL = (flag & 0xff00) - 0x8000;
321 		prevR = ((flag & 0x00ff) << 8) -0x8000;
322 	} else {
323 		prevL = flag & 0xff00;
324 		prevR = (flag & 0x00ff) << 8;
325 	}
326 
327 	for (z=0;z<size;z+=1) {
328 		prevL= prevL + cin.sqrTable[from[z*2]];
329 		prevR = prevR + cin.sqrTable[from[z*2+1]];
330 		to[z] = (short)((prevL + prevR)/2);
331 	}
332 
333 	return size;
334 }
335 */
336 /******************************************************************************
337 *
338 * Function:
339 *
340 * Description:
341 *
342 ******************************************************************************/
343 
move8_32(byte * src,byte * dst,int spl)344 static void move8_32( byte *src, byte *dst, int spl )
345 {
346 	int i;
347 
348 	for(i = 0; i < 8; ++i)
349 	{
350 		memcpy(dst, src, 32);
351 		src += spl;
352 		dst += spl;
353 	}
354 }
355 
356 /******************************************************************************
357 *
358 * Function:
359 *
360 * Description:
361 *
362 ******************************************************************************/
363 
move4_32(byte * src,byte * dst,int spl)364 static void move4_32( byte *src, byte *dst, int spl  )
365 {
366 	int i;
367 
368 	for(i = 0; i < 4; ++i)
369 	{
370 		memcpy(dst, src, 16);
371 		src += spl;
372 		dst += spl;
373 	}
374 }
375 
376 /******************************************************************************
377 *
378 * Function:
379 *
380 * Description:
381 *
382 ******************************************************************************/
383 
blit8_32(byte * src,byte * dst,int spl)384 static void blit8_32( byte *src, byte *dst, int spl  )
385 {
386 	int i;
387 
388 	for(i = 0; i < 8; ++i)
389 	{
390 		memcpy(dst, src, 32);
391 		src += 32;
392 		dst += spl;
393 	}
394 }
395 
396 /******************************************************************************
397 *
398 * Function:
399 *
400 * Description:
401 *
402 ******************************************************************************/
blit4_32(byte * src,byte * dst,int spl)403 static void blit4_32( byte *src, byte *dst, int spl  )
404 {
405 	int i;
406 
407 	for(i = 0; i < 4; ++i)
408 	{
409 		memmove(dst, src, 16);
410 		src += 16;
411 		dst += spl;
412 	}
413 }
414 
415 /******************************************************************************
416 *
417 * Function:
418 *
419 * Description:
420 *
421 ******************************************************************************/
422 
blit2_32(byte * src,byte * dst,int spl)423 static void blit2_32( byte *src, byte *dst, int spl  )
424 {
425 	memcpy(dst, src, 8);
426 	memcpy(dst+spl, src+8, 8);
427 }
428 
429 /******************************************************************************
430 *
431 * Function:
432 *
433 * Description:
434 *
435 ******************************************************************************/
436 
blitVQQuad32fs(byte ** status,unsigned char * data)437 static void blitVQQuad32fs( byte **status, unsigned char *data )
438 {
439 unsigned short	newd, celdata, code;
440 unsigned int	index, i;
441 int		spl;
442 
443 	newd	= 0;
444 	celdata = 0;
445 	index	= 0;
446 
447         spl = cinTable[currentHandle].samplesPerLine;
448 
449 	do {
450 		if (!newd) {
451 			newd = 7;
452 			celdata = data[0] + data[1]*256;
453 			data += 2;
454 		} else {
455 			newd--;
456 		}
457 
458 		code = (unsigned short)(celdata&0xc000);
459 		celdata <<= 2;
460 
461 		switch (code) {
462 			case	0x8000:													// vq code
463 				blit8_32( (byte *)&vq8[(*data)*128], status[index], spl );
464 				data++;
465 				index += 5;
466 				break;
467 			case	0xc000:													// drop
468 				index++;													// skip 8x8
469 				for(i=0;i<4;i++) {
470 					if (!newd) {
471 						newd = 7;
472 						celdata = data[0] + data[1]*256;
473 						data += 2;
474 					} else {
475 						newd--;
476 					}
477 
478 					code = (unsigned short)(celdata&0xc000); celdata <<= 2;
479 
480 					switch (code) {											// code in top two bits of code
481 						case	0x8000:										// 4x4 vq code
482 							blit4_32( (byte *)&vq4[(*data)*32], status[index], spl );
483 							data++;
484 							break;
485 						case	0xc000:										// 2x2 vq code
486 							blit2_32( (byte *)&vq2[(*data)*8], status[index], spl );
487 							data++;
488 							blit2_32( (byte *)&vq2[(*data)*8], status[index]+8, spl );
489 							data++;
490 							blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2, spl );
491 							data++;
492 							blit2_32( (byte *)&vq2[(*data)*8], status[index]+spl*2+8, spl );
493 							data++;
494 							break;
495 						case	0x4000:										// motion compensation
496 							move4_32( status[index] + cin.mcomp[(*data)], status[index], spl );
497 							data++;
498 							break;
499 					}
500 					index++;
501 				}
502 				break;
503 			case	0x4000:													// motion compensation
504 				move8_32( status[index] + cin.mcomp[(*data)], status[index], spl );
505 				data++;
506 				index += 5;
507 				break;
508 			case	0x0000:
509 				index += 5;
510 				break;
511 		}
512 	} while ( status[index] != NULL );
513 }
514 
515 /******************************************************************************
516 *
517 * Function:
518 *
519 * Description:
520 *
521 ******************************************************************************/
522 
ROQ_GenYUVTables(void)523 static void ROQ_GenYUVTables( void )
524 {
525 	float t_ub,t_vr,t_ug,t_vg;
526 	long i;
527 
528 	t_ub = (1.77200f/2.0f) * (float)(1<<6) + 0.5f;
529 	t_vr = (1.40200f/2.0f) * (float)(1<<6) + 0.5f;
530 	t_ug = (0.34414f/2.0f) * (float)(1<<6) + 0.5f;
531 	t_vg = (0.71414f/2.0f) * (float)(1<<6) + 0.5f;
532 	for(i=0;i<256;i++) {
533 		float x = (float)(2 * i - 255);
534 
535 		ROQ_UB_tab[i] = (long)( ( t_ub * x) + (1<<5));
536 		ROQ_VR_tab[i] = (long)( ( t_vr * x) + (1<<5));
537 		ROQ_UG_tab[i] = (long)( (-t_ug * x)		 );
538 		ROQ_VG_tab[i] = (long)( (-t_vg * x) + (1<<5));
539 		ROQ_YY_tab[i] = (long)( (i << 6) | (i >> 2) );
540 	}
541 }
542 
543 #define VQ2TO4(a,b,c,d) { \
544     	*c++ = a[0];	\
545 	*d++ = a[0];	\
546 	*d++ = a[0];	\
547 	*c++ = a[1];	\
548 	*d++ = a[1];	\
549 	*d++ = a[1];	\
550 	*c++ = b[0];	\
551 	*d++ = b[0];	\
552 	*d++ = b[0];	\
553 	*c++ = b[1];	\
554 	*d++ = b[1];	\
555 	*d++ = b[1];	\
556 	*d++ = a[0];	\
557 	*d++ = a[0];	\
558 	*d++ = a[1];	\
559 	*d++ = a[1];	\
560 	*d++ = b[0];	\
561 	*d++ = b[0];	\
562 	*d++ = b[1];	\
563 	*d++ = b[1];	\
564 	a += 2; b += 2; }
565 
566 #define VQ2TO2(a,b,c,d) { \
567 	*c++ = *a;	\
568 	*d++ = *a;	\
569 	*d++ = *a;	\
570 	*c++ = *b;	\
571 	*d++ = *b;	\
572 	*d++ = *b;	\
573 	*d++ = *a;	\
574 	*d++ = *a;	\
575 	*d++ = *b;	\
576 	*d++ = *b;	\
577 	a++; b++; }
578 
579 /******************************************************************************
580 *
581 * Function:
582 *
583 * Description:
584 *
585 ******************************************************************************/
586 
yuv_to_rgb(long y,long u,long v)587 static unsigned short yuv_to_rgb( long y, long u, long v )
588 {
589 	long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
590 
591 	r = (YY + ROQ_VR_tab[v]) >> 9;
592 	g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8;
593 	b = (YY + ROQ_UB_tab[u]) >> 9;
594 
595 	if (r<0)
596 		r = 0;
597 	if (g<0)
598 		g = 0;
599 	if (b<0)
600 		b = 0;
601 	if (r > 31)
602 		r = 31;
603 	if (g > 63)
604 		g = 63;
605 	if (b > 31)
606 		b = 31;
607 
608 	return (unsigned short)((r<<11)+(g<<5)+(b));
609 }
610 
611 /******************************************************************************
612 *
613 * Function:
614 *
615 * Description:
616 *
617 ******************************************************************************/
618 
yuv_to_rgb24(long y,long u,long v)619 static unsigned int yuv_to_rgb24( long y, long u, long v )
620 {
621 	long r,g,b,YY = (long)(ROQ_YY_tab[(y)]);
622 
623 	r = (YY + ROQ_VR_tab[v]) >> 6;
624 	g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6;
625 	b = (YY + ROQ_UB_tab[u]) >> 6;
626 
627 	if (r<0)
628 		r = 0;
629 	if (g<0)
630 		g = 0;
631 	if (b<0)
632 		b = 0;
633 	if (r > 255)
634 		r = 255;
635 	if (g > 255)
636 		g = 255;
637 	if (b > 255)
638 		b = 255;
639 
640 	return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24));
641 }
642 
643 /******************************************************************************
644 *
645 * Function:
646 *
647 * Description:
648 *
649 ******************************************************************************/
650 
decodeCodeBook(byte * input,unsigned short roq_flags)651 static void decodeCodeBook( byte *input, unsigned short roq_flags )
652 {
653 	long	i, j, two, four;
654 	unsigned short	*aptr, *bptr, *cptr, *dptr;
655 	long	y0,y1,y2,y3,cr,cb;
656 	byte	*bbptr, *baptr, *bcptr, *bdptr;
657 	union {
658 		unsigned int *i;
659 		unsigned short *s;
660 	} iaptr, ibptr, icptr, idptr;
661 
662 	if (!roq_flags) {
663 		two = four = 256;
664 	} else {
665 		two  = roq_flags>>8;
666 		if (!two) two = 256;
667 		four = roq_flags&0xff;
668 	}
669 
670 	four *= 2;
671 
672 	bptr = (unsigned short *)vq2;
673 
674 	if (!cinTable[currentHandle].half) {
675 		if (!cinTable[currentHandle].smootheddouble) {
676 //
677 // normal height
678 //
679 			if (cinTable[currentHandle].samplesPerPixel==2) {
680 				for(i=0;i<two;i++) {
681 					y0 = (long)*input++;
682 					y1 = (long)*input++;
683 					y2 = (long)*input++;
684 					y3 = (long)*input++;
685 					cr = (long)*input++;
686 					cb = (long)*input++;
687 					*bptr++ = yuv_to_rgb( y0, cr, cb );
688 					*bptr++ = yuv_to_rgb( y1, cr, cb );
689 					*bptr++ = yuv_to_rgb( y2, cr, cb );
690 					*bptr++ = yuv_to_rgb( y3, cr, cb );
691 				}
692 
693 				cptr = (unsigned short *)vq4;
694 				dptr = (unsigned short *)vq8;
695 
696 				for(i=0;i<four;i++) {
697 					aptr = (unsigned short *)vq2 + (*input++)*4;
698 					bptr = (unsigned short *)vq2 + (*input++)*4;
699 					for(j=0;j<2;j++)
700 						VQ2TO4(aptr,bptr,cptr,dptr);
701 				}
702 			} else if (cinTable[currentHandle].samplesPerPixel==4) {
703 				ibptr.s = bptr;
704 				for(i=0;i<two;i++) {
705 					y0 = (long)*input++;
706 					y1 = (long)*input++;
707 					y2 = (long)*input++;
708 					y3 = (long)*input++;
709 					cr = (long)*input++;
710 					cb = (long)*input++;
711 					*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
712 					*ibptr.i++ = yuv_to_rgb24( y1, cr, cb );
713 					*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
714 					*ibptr.i++ = yuv_to_rgb24( y3, cr, cb );
715 				}
716 
717 				icptr.s = vq4;
718 				idptr.s = vq8;
719 
720 				for(i=0;i<four;i++) {
721 					iaptr.s = vq2;
722 					iaptr.i += (*input++)*4;
723 					ibptr.s = vq2;
724 					ibptr.i += (*input++)*4;
725 					for(j=0;j<2;j++)
726 						VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
727 				}
728 			} else if (cinTable[currentHandle].samplesPerPixel==1) {
729 				bbptr = (byte *)bptr;
730 				for(i=0;i<two;i++) {
731 					*bbptr++ = cinTable[currentHandle].gray[*input++];
732 					*bbptr++ = cinTable[currentHandle].gray[*input++];
733 					*bbptr++ = cinTable[currentHandle].gray[*input++];
734 					*bbptr++ = cinTable[currentHandle].gray[*input]; input +=3;
735 				}
736 
737 				bcptr = (byte *)vq4;
738 				bdptr = (byte *)vq8;
739 
740 				for(i=0;i<four;i++) {
741 					baptr = (byte *)vq2 + (*input++)*4;
742 					bbptr = (byte *)vq2 + (*input++)*4;
743 					for(j=0;j<2;j++)
744 						VQ2TO4(baptr,bbptr,bcptr,bdptr);
745 				}
746 			}
747 		} else {
748 //
749 // double height, smoothed
750 //
751 			if (cinTable[currentHandle].samplesPerPixel==2) {
752 				for(i=0;i<two;i++) {
753 					y0 = (long)*input++;
754 					y1 = (long)*input++;
755 					y2 = (long)*input++;
756 					y3 = (long)*input++;
757 					cr = (long)*input++;
758 					cb = (long)*input++;
759 					*bptr++ = yuv_to_rgb( y0, cr, cb );
760 					*bptr++ = yuv_to_rgb( y1, cr, cb );
761 					*bptr++ = yuv_to_rgb( ((y0*3)+y2)/4, cr, cb );
762 					*bptr++ = yuv_to_rgb( ((y1*3)+y3)/4, cr, cb );
763 					*bptr++ = yuv_to_rgb( (y0+(y2*3))/4, cr, cb );
764 					*bptr++ = yuv_to_rgb( (y1+(y3*3))/4, cr, cb );
765 					*bptr++ = yuv_to_rgb( y2, cr, cb );
766 					*bptr++ = yuv_to_rgb( y3, cr, cb );
767 				}
768 
769 				cptr = (unsigned short *)vq4;
770 				dptr = (unsigned short *)vq8;
771 
772 				for(i=0;i<four;i++) {
773 					aptr = (unsigned short *)vq2 + (*input++)*8;
774 					bptr = (unsigned short *)vq2 + (*input++)*8;
775 					for(j=0;j<2;j++) {
776 						VQ2TO4(aptr,bptr,cptr,dptr);
777 						VQ2TO4(aptr,bptr,cptr,dptr);
778 					}
779 				}
780 			} else if (cinTable[currentHandle].samplesPerPixel==4) {
781 				ibptr.s = bptr;
782 				for(i=0;i<two;i++) {
783 					y0 = (long)*input++;
784 					y1 = (long)*input++;
785 					y2 = (long)*input++;
786 					y3 = (long)*input++;
787 					cr = (long)*input++;
788 					cb = (long)*input++;
789 					*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
790 					*ibptr.i++ = yuv_to_rgb24( y1, cr, cb );
791 					*ibptr.i++ = yuv_to_rgb24( ((y0*3)+y2)/4, cr, cb );
792 					*ibptr.i++ = yuv_to_rgb24( ((y1*3)+y3)/4, cr, cb );
793 					*ibptr.i++ = yuv_to_rgb24( (y0+(y2*3))/4, cr, cb );
794 					*ibptr.i++ = yuv_to_rgb24( (y1+(y3*3))/4, cr, cb );
795 					*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
796 					*ibptr.i++ = yuv_to_rgb24( y3, cr, cb );
797 				}
798 
799 				icptr.s = vq4;
800 				idptr.s = vq8;
801 
802 				for(i=0;i<four;i++) {
803 					iaptr.s = vq2;
804 					iaptr.i += (*input++)*8;
805 					ibptr.s = vq2;
806 					ibptr.i += (*input++)*8;
807 					for(j=0;j<2;j++) {
808 						VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
809 						VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i);
810 					}
811 				}
812 			} else if (cinTable[currentHandle].samplesPerPixel==1) {
813 				bbptr = (byte *)bptr;
814 				for(i=0;i<two;i++) {
815 					y0 = (long)*input++;
816 					y1 = (long)*input++;
817 					y2 = (long)*input++;
818 					y3 = (long)*input; input+= 3;
819 					*bbptr++ = cinTable[currentHandle].gray[y0];
820 					*bbptr++ = cinTable[currentHandle].gray[y1];
821 					*bbptr++ = cinTable[currentHandle].gray[((y0*3)+y2)/4];
822 					*bbptr++ = cinTable[currentHandle].gray[((y1*3)+y3)/4];
823 					*bbptr++ = cinTable[currentHandle].gray[(y0+(y2*3))/4];
824 					*bbptr++ = cinTable[currentHandle].gray[(y1+(y3*3))/4];
825 					*bbptr++ = cinTable[currentHandle].gray[y2];
826 					*bbptr++ = cinTable[currentHandle].gray[y3];
827 				}
828 
829 				bcptr = (byte *)vq4;
830 				bdptr = (byte *)vq8;
831 
832 				for(i=0;i<four;i++) {
833 					baptr = (byte *)vq2 + (*input++)*8;
834 					bbptr = (byte *)vq2 + (*input++)*8;
835 					for(j=0;j<2;j++) {
836 						VQ2TO4(baptr,bbptr,bcptr,bdptr);
837 						VQ2TO4(baptr,bbptr,bcptr,bdptr);
838 					}
839 				}
840 			}
841 		}
842 	} else {
843 //
844 // 1/4 screen
845 //
846 		if (cinTable[currentHandle].samplesPerPixel==2) {
847 			for(i=0;i<two;i++) {
848 				y0 = (long)*input; input+=2;
849 				y2 = (long)*input; input+=2;
850 				cr = (long)*input++;
851 				cb = (long)*input++;
852 				*bptr++ = yuv_to_rgb( y0, cr, cb );
853 				*bptr++ = yuv_to_rgb( y2, cr, cb );
854 			}
855 
856 			cptr = (unsigned short *)vq4;
857 			dptr = (unsigned short *)vq8;
858 
859 			for(i=0;i<four;i++) {
860 				aptr = (unsigned short *)vq2 + (*input++)*2;
861 				bptr = (unsigned short *)vq2 + (*input++)*2;
862 				for(j=0;j<2;j++) {
863 					VQ2TO2(aptr,bptr,cptr,dptr);
864 				}
865 			}
866 		} else if (cinTable[currentHandle].samplesPerPixel == 1) {
867 			bbptr = (byte *)bptr;
868 
869 			for(i=0;i<two;i++) {
870 				*bbptr++ = cinTable[currentHandle].gray[*input]; input+=2;
871 				*bbptr++ = cinTable[currentHandle].gray[*input]; input+=4;
872 			}
873 
874 			bcptr = (byte *)vq4;
875 			bdptr = (byte *)vq8;
876 
877 			for(i=0;i<four;i++) {
878 				baptr = (byte *)vq2 + (*input++)*2;
879 				bbptr = (byte *)vq2 + (*input++)*2;
880 				for(j=0;j<2;j++) {
881 					VQ2TO2(baptr,bbptr,bcptr,bdptr);
882 				}
883 			}
884 		} else if (cinTable[currentHandle].samplesPerPixel == 4) {
885 			ibptr.s = bptr;
886 			for(i=0;i<two;i++) {
887 				y0 = (long)*input; input+=2;
888 				y2 = (long)*input; input+=2;
889 				cr = (long)*input++;
890 				cb = (long)*input++;
891 				*ibptr.i++ = yuv_to_rgb24( y0, cr, cb );
892 				*ibptr.i++ = yuv_to_rgb24( y2, cr, cb );
893 			}
894 
895 			icptr.s = vq4;
896 			idptr.s = vq8;
897 
898 			for(i=0;i<four;i++) {
899 				iaptr.s = vq2;
900 				iaptr.i += (*input++)*2;
901 				ibptr.s = vq2 + (*input++)*2;
902 				ibptr.i += (*input++)*2;
903 				for(j=0;j<2;j++) {
904 					VQ2TO2(iaptr.i,ibptr.i,icptr.i,idptr.i);
905 				}
906 			}
907 		}
908 	}
909 }
910 
911 /******************************************************************************
912 *
913 * Function:
914 *
915 * Description:
916 *
917 ******************************************************************************/
918 
recurseQuad(long startX,long startY,long quadSize,long xOff,long yOff)919 static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff )
920 {
921 	byte *scroff;
922 	long bigx, bigy, lowx, lowy, useY;
923 	long offset;
924 
925 	offset = cinTable[currentHandle].screenDelta;
926 
927 	lowx = lowy = 0;
928 	bigx = cinTable[currentHandle].xsize;
929 	bigy = cinTable[currentHandle].ysize;
930 
931 	if (bigx > cinTable[currentHandle].CIN_WIDTH) bigx = cinTable[currentHandle].CIN_WIDTH;
932 	if (bigy > cinTable[currentHandle].CIN_HEIGHT) bigy = cinTable[currentHandle].CIN_HEIGHT;
933 
934 	if ( (startX >= lowx) && (startX+quadSize) <= (bigx) && (startY+quadSize) <= (bigy) && (startY >= lowy) && quadSize <= MAXSIZE) {
935 		useY = startY;
936 		scroff = cin.linbuf + (useY+((cinTable[currentHandle].CIN_HEIGHT-bigy)>>1)+yOff)*(cinTable[currentHandle].samplesPerLine) + (((startX+xOff))*cinTable[currentHandle].samplesPerPixel);
937 
938 		cin.qStatus[0][cinTable[currentHandle].onQuad  ] = scroff;
939 		cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff+offset;
940 	}
941 
942 	if ( quadSize != MINSIZE ) {
943 		quadSize >>= 1;
944 		recurseQuad( startX,		  startY		  , quadSize, xOff, yOff );
945 		recurseQuad( startX+quadSize, startY		  , quadSize, xOff, yOff );
946 		recurseQuad( startX,		  startY+quadSize , quadSize, xOff, yOff );
947 		recurseQuad( startX+quadSize, startY+quadSize , quadSize, xOff, yOff );
948 	}
949 }
950 
951 
952 /******************************************************************************
953 *
954 * Function:
955 *
956 * Description:
957 *
958 ******************************************************************************/
959 
setupQuad(long xOff,long yOff)960 static void setupQuad( long xOff, long yOff )
961 {
962 	long numQuadCels, i,x,y;
963 	byte *temp;
964 
965 	if (xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == (unsigned)cin.oldysize && cinTable[currentHandle].xsize == (unsigned)cin.oldxsize) {
966 		return;
967 	}
968 
969 	cin.oldXOff = xOff;
970 	cin.oldYOff = yOff;
971 	cin.oldysize = cinTable[currentHandle].ysize;
972 	cin.oldxsize = cinTable[currentHandle].xsize;
973 /*	Enisform: Not in q3 source
974 	numQuadCels  = (cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].CIN_HEIGHT) / (16);
975 	numQuadCels += numQuadCels/4 + numQuadCels/16;
976 	numQuadCels += 64;							  // for overflow
977 */
978 
979 	numQuadCels  = (cinTable[currentHandle].xsize*cinTable[currentHandle].ysize) / (16);
980 	numQuadCels += numQuadCels/4;
981 	numQuadCels += 64;							  // for overflow
982 
983 	cinTable[currentHandle].onQuad = 0;
984 
985 	for(y=0;y<(long)cinTable[currentHandle].ysize;y+=16)
986 		for(x=0;x<(long)cinTable[currentHandle].xsize;x+=16)
987 			recurseQuad( x, y, 16, xOff, yOff );
988 
989 	temp = NULL;
990 
991 	for(i=(numQuadCels-64);i<numQuadCels;i++) {
992 		cin.qStatus[0][i] = temp;			  // eoq
993 		cin.qStatus[1][i] = temp;			  // eoq
994 	}
995 }
996 
997 
998 /******************************************************************************
999 *
1000 * Function:
1001 *
1002 * Description:
1003 *
1004 ******************************************************************************/
1005 
readQuadInfo(byte * qData)1006 static void readQuadInfo( byte *qData )
1007 {
1008 	if (currentHandle < 0) return;
1009 
1010 	cinTable[currentHandle].xsize    = qData[0]+qData[1]*256;
1011 	cinTable[currentHandle].ysize    = qData[2]+qData[3]*256;
1012 	cinTable[currentHandle].maxsize  = qData[4]+qData[5]*256;
1013 	cinTable[currentHandle].minsize  = qData[6]+qData[7]*256;
1014 
1015 	cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize;
1016 	cinTable[currentHandle].CIN_WIDTH  = cinTable[currentHandle].xsize;
1017 
1018 	cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH*cinTable[currentHandle].samplesPerPixel;
1019 	cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT*cinTable[currentHandle].samplesPerLine;
1020 
1021 	cinTable[currentHandle].half = qfalse;
1022 	cinTable[currentHandle].smootheddouble = qfalse;
1023 
1024 	cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal;
1025 	cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer;
1026 
1027 	cinTable[currentHandle].t[0] = cinTable[currentHandle].screenDelta;
1028 	cinTable[currentHandle].t[1] = -cinTable[currentHandle].screenDelta;
1029 
1030 	cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH;
1031 	cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT;
1032 	// jic the card sucks
1033 	if ( cls.glconfig.maxTextureSize <= 256) {
1034         if (cinTable[currentHandle].drawX>256) {
1035             cinTable[currentHandle].drawX = 256;
1036         }
1037         if (cinTable[currentHandle].drawY>256) {
1038             cinTable[currentHandle].drawY = 256;
1039         }
1040 		if (cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256) {
1041 			Com_Printf("HACK: approxmimating cinematic for Rage Pro or Voodoo\n");
1042 		}
1043 	}
1044 }
1045 
1046 /******************************************************************************
1047 *
1048 * Function:
1049 *
1050 * Description:
1051 *
1052 ******************************************************************************/
1053 
RoQPrepMcomp(long xoff,long yoff)1054 static void RoQPrepMcomp( long xoff, long yoff )
1055 {
1056 	long i, j, x, y, temp, temp2;
1057 
1058 	i=cinTable[currentHandle].samplesPerLine; j=cinTable[currentHandle].samplesPerPixel;
1059 	if ( cinTable[currentHandle].xsize == (cinTable[currentHandle].ysize*4) && !cinTable[currentHandle].half ) { j = j+j; i = i+i; }
1060 
1061 	for(y=0;y<16;y++) {
1062 		temp2 = (y+yoff-8)*i;
1063 		for(x=0;x<16;x++) {
1064 			temp = (x+xoff-8)*j;
1065 			cin.mcomp[(x*16)+y] = cinTable[currentHandle].normalBuffer0-(temp2+temp);
1066 		}
1067 	}
1068 }
1069 
1070 /******************************************************************************
1071 *
1072 * Function:
1073 *
1074 * Description:
1075 *
1076 ******************************************************************************/
1077 
initRoQ(void)1078 static void initRoQ( void )
1079 {
1080 	if (currentHandle < 0) return;
1081 
1082 	cinTable[currentHandle].VQNormal = (void (*)(byte *, void *))blitVQQuad32fs;
1083 	cinTable[currentHandle].VQBuffer = (void (*)(byte *, void *))blitVQQuad32fs;
1084 	cinTable[currentHandle].samplesPerPixel = 4;
1085 	ROQ_GenYUVTables();
1086 	RllSetupTable();
1087 }
1088 
1089 /******************************************************************************
1090 *
1091 * Function:
1092 *
1093 * Description:
1094 *
1095 ******************************************************************************/
1096 /*
1097 static byte* RoQFetchInterlaced( byte *source ) {
1098 	int x, *src, *dst;
1099 
1100 	if (currentHandle < 0) return NULL;
1101 
1102 	src = (int *)source;
1103 	dst = (int *)cinTable[currentHandle].buf2;
1104 
1105 	for(x=0;x<256*256;x++) {
1106 		*dst = *src;
1107 		dst++; src += 2;
1108 	}
1109 	return cinTable[currentHandle].buf2;
1110 }
1111 */
RoQReset(void)1112 static void RoQReset( void ) {
1113 
1114 	if (currentHandle < 0) return;
1115 
1116 	FS_FCloseFile( cinTable[currentHandle].iFile );
1117 	FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
1118 	// let the background thread start reading ahead
1119 	FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
1120 	RoQ_init();
1121 	cinTable[currentHandle].status = FMV_LOOPED;
1122 }
1123 
1124 /******************************************************************************
1125 *
1126 * Function:
1127 *
1128 * Description:
1129 *
1130 ******************************************************************************/
1131 
RoQInterrupt(void)1132 static void RoQInterrupt(void)
1133 {
1134 	byte				*framedata;
1135         short		sbuf[32768];
1136         int		ssize;
1137 
1138 	if (currentHandle < 0) return;
1139 
1140 	FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile );
1141 	if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
1142 		if (cinTable[currentHandle].holdAtEnd==qfalse) {
1143 			if (cinTable[currentHandle].looping) {
1144 				RoQReset();
1145 			} else {
1146 				cinTable[currentHandle].status = FMV_EOF;
1147 			}
1148 		} else {
1149 			cinTable[currentHandle].status = FMV_IDLE;
1150 		}
1151 		return;
1152 	}
1153 
1154 	framedata = cin.file;
1155 //
1156 // new frame is ready
1157 //
1158 redump:
1159 	switch(cinTable[currentHandle].roq_id)
1160 	{
1161 		case	ROQ_QUAD_VQ:
1162 			if ((cinTable[currentHandle].numQuads&1)) {
1163 				cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1];
1164 				RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
1165 				cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata);
1166 				cinTable[currentHandle].buf = 	cin.linbuf + cinTable[currentHandle].screenDelta;
1167 			} else {
1168 				cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0];
1169 				RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 );
1170 				cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata );
1171 				cinTable[currentHandle].buf = 	cin.linbuf;
1172 			}
1173 			if (cinTable[currentHandle].numQuads == 0) {		// first frame
1174 				Com_Memcpy(cin.linbuf+cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine*cinTable[currentHandle].ysize);
1175 			}
1176 			cinTable[currentHandle].numQuads++;
1177 			cinTable[currentHandle].dirty = qtrue;
1178 			break;
1179 		case	ROQ_CODEBOOK:
1180 			decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags );
1181 			break;
1182 		case	ZA_SOUND_MONO:
1183 			if (!cinTable[currentHandle].silent) {
1184 				ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
1185                 S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, s_volume->value, qtrue );
1186 			}
1187 			break;
1188 		case	ZA_SOUND_STEREO:
1189 			if (!cinTable[currentHandle].silent) {
1190 				if (cinTable[currentHandle].numQuads == -1) {
1191 					S_Update();
1192 					s_rawend = s_soundtime;
1193 				}
1194 				ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags);
1195                 S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, s_volume->value, qtrue );
1196 			}
1197 			break;
1198 		case	ROQ_QUAD_INFO:
1199 			if (cinTable[currentHandle].numQuads == -1) {
1200 				readQuadInfo( framedata );
1201 				setupQuad( 0, 0 );
1202 				cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = Sys_Milliseconds()*com_timescale->value;
1203 			}
1204 			if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0;
1205 			break;
1206 		case	ROQ_PACKET:
1207 			cinTable[currentHandle].inMemory = (qboolean)cinTable[currentHandle].roq_flags;
1208 			cinTable[currentHandle].RoQFrameSize = 0;           // for header
1209 			break;
1210 		case	ROQ_QUAD_HANG:
1211 			cinTable[currentHandle].RoQFrameSize = 0;
1212 			break;
1213 		case	ROQ_QUAD_JPEG:
1214 			break;
1215 		default:
1216 			cinTable[currentHandle].status = FMV_EOF;
1217 			break;
1218 	}
1219 //
1220 // read in next frame data
1221 //
1222 	if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) {
1223 		if (cinTable[currentHandle].holdAtEnd==qfalse) {
1224 			if (cinTable[currentHandle].looping) {
1225 				RoQReset();
1226 			} else {
1227 				cinTable[currentHandle].status = FMV_EOF;
1228 			}
1229 		} else {
1230 			cinTable[currentHandle].status = FMV_IDLE;
1231 		}
1232 		return;
1233 	}
1234 
1235 	framedata		 += cinTable[currentHandle].RoQFrameSize;
1236 	cinTable[currentHandle].roq_id		 = framedata[0] + framedata[1]*256;
1237 	cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3]*256 + framedata[4]*65536;
1238 	cinTable[currentHandle].roq_flags	 = framedata[6] + framedata[7]*256;
1239 	cinTable[currentHandle].roqF0		 = (signed char)framedata[7];
1240 	cinTable[currentHandle].roqF1		 = (signed char)framedata[6];
1241 
1242 	if (cinTable[currentHandle].RoQFrameSize>65536||cinTable[currentHandle].roq_id==0x1084) {
1243 		Com_DPrintf("roq_size>65536||roq_id==0x1084\n");
1244 		cinTable[currentHandle].status = FMV_EOF;
1245 		if (cinTable[currentHandle].looping) {
1246 			RoQReset();
1247 		}
1248 		return;
1249 	}
1250 	if (cinTable[currentHandle].inMemory && (cinTable[currentHandle].status != FMV_EOF))
1251 	{
1252 		cinTable[currentHandle].inMemory = (qboolean)(((int)cinTable[currentHandle].inMemory)-1);
1253 		framedata += 8;
1254 		goto redump;
1255 	}
1256 //
1257 // one more frame hits the dust
1258 //
1259 //	assert(cinTable[currentHandle].RoQFrameSize <= 65536);
1260 //	r = FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile );
1261 	cinTable[currentHandle].RoQPlayed	+= cinTable[currentHandle].RoQFrameSize+8;
1262 }
1263 
1264 /******************************************************************************
1265 *
1266 * Function:
1267 *
1268 * Description:
1269 *
1270 ******************************************************************************/
1271 
RoQ_init(void)1272 static void RoQ_init( void )
1273 {
1274 
1275 	cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = Sys_Milliseconds()*com_timescale->value;
1276 
1277 	cinTable[currentHandle].RoQPlayed = 24;
1278 
1279 /*	get frame rate */
1280 	cinTable[currentHandle].roqFPS	 = cin.file[ 6] + cin.file[ 7]*256;
1281 
1282 	if (!cinTable[currentHandle].roqFPS) cinTable[currentHandle].roqFPS = 30;
1283 
1284 
1285 	cinTable[currentHandle].numQuads = -1;
1286 
1287 	cinTable[currentHandle].roq_id		= cin.file[ 8] + cin.file[ 9]*256;
1288 	cinTable[currentHandle].RoQFrameSize	= cin.file[10] + cin.file[11]*256 + cin.file[12]*65536;
1289 	cinTable[currentHandle].roq_flags	= cin.file[14] + cin.file[15]*256;
1290 
1291 	if (cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize) {
1292 		return;
1293 	}
1294 
1295 	if (cinTable[currentHandle].hSFX)
1296 	{
1297 		S_StartLocalSound(cinTable[currentHandle].hSFX, CHAN_AUTO);
1298 	}
1299 }
1300 
1301 /******************************************************************************
1302 *
1303 * Function:
1304 *
1305 * Description:
1306 *
1307 ******************************************************************************/
1308 
RoQShutdown(void)1309 static void RoQShutdown( void ) {
1310 	const char *s;
1311 
1312 	if (!cinTable[currentHandle].buf) {
1313 		if (cinTable[currentHandle].iFile) {
1314 //			assert( 0 && "ROQ handle leak-prevention WAS needed!");
1315 			FS_FCloseFile( cinTable[currentHandle].iFile );
1316 			cinTable[currentHandle].iFile = 0;
1317 			if (cinTable[currentHandle].hSFX) {
1318 				S_CIN_StopSound( cinTable[currentHandle].hSFX );
1319 			}
1320 		}
1321 		return;
1322 	}
1323 
1324 	if (cinTable[currentHandle].status == FMV_IDLE) {
1325 		return;
1326 	}
1327 
1328 	Com_DPrintf("finished cinematic\n");
1329 	cinTable[currentHandle].status = FMV_IDLE;
1330 
1331 	if (cinTable[currentHandle].iFile) {
1332 		FS_FCloseFile( cinTable[currentHandle].iFile );
1333 		cinTable[currentHandle].iFile = 0;
1334 		if (cinTable[currentHandle].hSFX) {
1335 			S_CIN_StopSound( cinTable[currentHandle].hSFX );
1336 		}
1337 	}
1338 
1339 	if (cinTable[currentHandle].alterGameState) {
1340 		cls.state = CA_DISCONNECTED;
1341 		// we can't just do a vstr nextmap, because
1342 		// if we are aborting the intro cinematic with
1343 		// a devmap command, nextmap would be valid by
1344 		// the time it was referenced
1345 		s = Cvar_VariableString( "nextmap" );
1346 		if ( s[0] ) {
1347 			Cbuf_ExecuteText( EXEC_APPEND, va("%s\n", s) );
1348 			Cvar_Set( "nextmap", "" );
1349 		}
1350 		CL_handle = -1;
1351 	}
1352 	cinTable[currentHandle].fileName[0] = 0;
1353 	currentHandle = -1;
1354 }
1355 
1356 /*
1357 ==================
1358 CIN_StopCinematic
1359 ==================
1360 */
1361 
CIN_StopCinematic(int handle)1362 e_status CIN_StopCinematic(int handle) {
1363 
1364 	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
1365 	currentHandle = handle;
1366 
1367 	Com_DPrintf("trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName);
1368 
1369 	if (!cinTable[currentHandle].buf) {
1370 		if (cinTable[currentHandle].iFile) {
1371 //			assert( 0 && "ROQ handle leak-prevention WAS needed!");
1372 			FS_FCloseFile( cinTable[currentHandle].iFile );
1373 			cinTable[currentHandle].iFile = 0;
1374 			cinTable[currentHandle].fileName[0] = 0;
1375 			if (cinTable[currentHandle].hSFX) {
1376 				S_CIN_StopSound( cinTable[currentHandle].hSFX );
1377 			}
1378 		}
1379 		return FMV_EOF;
1380 	}
1381 
1382 	if (cinTable[currentHandle].alterGameState) {
1383 		if ( cls.state != CA_CINEMATIC ) {
1384 			return cinTable[currentHandle].status;
1385 		}
1386 	}
1387 	cinTable[currentHandle].status = FMV_EOF;
1388 	RoQShutdown();
1389 
1390 	return FMV_EOF;
1391 }
1392 
1393 /*
1394 ==================
1395 SCR_RunCinematic
1396 
1397 Fetch and decompress the pending frame
1398 ==================
1399 */
1400 
1401 
CIN_RunCinematic(int handle)1402 e_status CIN_RunCinematic (int handle)
1403 {
1404 	int	start = 0;
1405 	int     thisTime = 0;
1406 
1407 	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return FMV_EOF;
1408 
1409 	if (cin.currentHandle != handle) {
1410 		currentHandle = handle;
1411 		cin.currentHandle = currentHandle;
1412 		cinTable[currentHandle].status = FMV_EOF;
1413 		RoQReset();
1414 	}
1415 
1416 	if (cinTable[handle].playonwalls < -1)
1417 	{
1418 		return cinTable[handle].status;
1419 	}
1420 
1421 	currentHandle = handle;
1422 
1423 	if (cinTable[currentHandle].alterGameState) {
1424 		if ( cls.state != CA_CINEMATIC ) {
1425 			return cinTable[currentHandle].status;
1426 		}
1427 	}
1428 
1429 	if (cinTable[currentHandle].status == FMV_IDLE) {
1430 		return cinTable[currentHandle].status;
1431 	}
1432 
1433 	thisTime = Sys_Milliseconds()*com_timescale->value;
1434 	if (cinTable[currentHandle].shader && (abs(thisTime - (double)cinTable[currentHandle].lastTime))>100) {
1435 		cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime;
1436 	}
1437 	cinTable[currentHandle].tfps = ((((Sys_Milliseconds()*com_timescale->value) - cinTable[currentHandle].startTime)*cinTable[currentHandle].roqFPS)/1000);
1438 
1439 	start = cinTable[currentHandle].startTime;
1440 	while(  (cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads)
1441 		&& (cinTable[currentHandle].status == FMV_PLAY) )
1442 	{
1443 		RoQInterrupt();
1444 		if ((unsigned)start != cinTable[currentHandle].startTime) {
1445 		  cinTable[currentHandle].tfps = ((((Sys_Milliseconds()*com_timescale->value)
1446 							  - cinTable[currentHandle].startTime)*cinTable[currentHandle].roqFPS)/1000);
1447 			start = cinTable[currentHandle].startTime;
1448 		}
1449 	}
1450 
1451 	cinTable[currentHandle].lastTime = thisTime;
1452 
1453 	if (cinTable[currentHandle].status == FMV_LOOPED) {
1454 		cinTable[currentHandle].status = FMV_PLAY;
1455 	}
1456 
1457 	if (cinTable[currentHandle].status == FMV_EOF) {
1458 	  if (cinTable[currentHandle].looping) {
1459 		RoQReset();
1460 	  } else {
1461 		RoQShutdown();
1462 	  }
1463 	}
1464 
1465 	return cinTable[currentHandle].status;
1466 }
1467 
1468 void		Menus_CloseAll(void);
1469 void		UI_Cursor_Show(qboolean flag);
1470 
1471 /*
1472 ==================
1473 CL_PlayCinematic
1474 
1475 ==================
1476 */
CIN_PlayCinematic(const char * arg,int x,int y,int w,int h,int systemBits,const char * psAudioFile)1477 int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits, const char *psAudioFile /* = NULL */ )
1478 {
1479 	unsigned short RoQID;
1480 	char	name[MAX_OSPATH];
1481 	int		i;
1482 
1483 	if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) {
1484 		Com_sprintf (name, sizeof(name), "video/%s", arg);
1485 	} else {
1486 		Com_sprintf (name, sizeof(name), "%s", arg);
1487 	}
1488 	COM_DefaultExtension(name,sizeof(name),".roq");
1489 
1490 	if (!(systemBits & CIN_system)) {
1491 		for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) {
1492 			if (!strcmp(cinTable[i].fileName, name) ) {
1493 				return i;
1494 			}
1495 		}
1496 	}
1497 
1498 	Com_DPrintf("CIN_PlayCinematic( %s )\n", arg);
1499 
1500 	memset(&cin, 0, sizeof(cinematics_t) );
1501 	currentHandle = CIN_HandleForVideo();
1502 
1503 	cin.currentHandle = currentHandle;
1504 
1505 	Q_strncpyz(cinTable[currentHandle].fileName, name, MAX_OSPATH);
1506 
1507 	cinTable[currentHandle].ROQSize = 0;
1508 	cinTable[currentHandle].ROQSize = FS_FOpenFileRead (cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue);
1509 
1510 	if (cinTable[currentHandle].ROQSize<=0) {
1511 		Com_Printf(S_COLOR_RED"ERROR: playCinematic: %s not found!\n", arg);
1512 		cinTable[currentHandle].fileName[0] = 0;
1513 		return -1;
1514 	}
1515 
1516 	CIN_SetExtents(currentHandle, x, y, w, h);
1517 	CIN_SetLooping(currentHandle, (qboolean)((systemBits & CIN_loop) != 0));
1518 
1519 	cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT;
1520 	cinTable[currentHandle].CIN_WIDTH  =  DEFAULT_CIN_WIDTH;
1521 	cinTable[currentHandle].holdAtEnd = (qboolean)((systemBits & CIN_hold) != 0);
1522 	cinTable[currentHandle].alterGameState = (qboolean)((systemBits & CIN_system) != 0);
1523 	cinTable[currentHandle].playonwalls = 1;
1524 	cinTable[currentHandle].silent = (qboolean)((systemBits & CIN_silent) != 0);
1525 	cinTable[currentHandle].shader = (qboolean)((systemBits & CIN_shader) != 0);
1526 	if (psAudioFile)
1527 	{
1528 		cinTable[currentHandle].hSFX = S_RegisterSound(psAudioFile);
1529 	}
1530 	else
1531 	{
1532 		cinTable[currentHandle].hSFX = 0;
1533 	}
1534 	cinTable[currentHandle].hCRAWLTEXT = 0;
1535 
1536 	if (cinTable[currentHandle].alterGameState)
1537 	{
1538 		// close the menu
1539 		Con_Close();
1540 		if (cls.uiStarted)
1541 		{
1542 			UI_Cursor_Show(qfalse);
1543 			Menus_CloseAll();
1544 		}
1545 	}
1546 	else
1547 	{
1548 		cinTable[currentHandle].playonwalls = cl_inGameVideo->integer;
1549 	}
1550 
1551 	initRoQ();
1552 
1553 	FS_Read (cin.file, 16, cinTable[currentHandle].iFile);
1554 
1555 	RoQID = (unsigned short)(cin.file[0]) + (unsigned short)(cin.file[1])*256;
1556 	if (RoQID == 0x1084)
1557 	{
1558 		RoQ_init();
1559 //		FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile);
1560 
1561 		cinTable[currentHandle].status = FMV_PLAY;
1562 		Com_DPrintf("trFMV::play(), playing %s\n", arg);
1563 
1564 		if (cinTable[currentHandle].alterGameState) {
1565 			cls.state = CA_CINEMATIC;
1566 		}
1567 
1568 		Con_Close();
1569 
1570 		if ( !cinTable[currentHandle].silent )
1571 			s_rawend = s_soundtime;
1572 
1573 		return currentHandle;
1574 	}
1575 	Com_DPrintf("trFMV::play(), invalid RoQ ID\n");
1576 
1577 	RoQShutdown();
1578 	return -1;
1579 }
1580 
CIN_SetExtents(int handle,int x,int y,int w,int h)1581 void CIN_SetExtents (int handle, int x, int y, int w, int h) {
1582 	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1583 	cinTable[handle].xpos = x;
1584 	cinTable[handle].ypos = y;
1585 	cinTable[handle].width = w;
1586 	cinTable[handle].height = h;
1587 	cinTable[handle].dirty = qtrue;
1588 }
1589 
CIN_SetLooping(int handle,qboolean loop)1590 void CIN_SetLooping(int handle, qboolean loop) {
1591 	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1592 	cinTable[handle].looping = loop;
1593 }
1594 
1595 // Text crawl defines
1596 #define TC_PLANE_WIDTH	250
1597 #define TC_PLANE_NEAR	90
1598 #define TC_PLANE_FAR	715
1599 #define TC_PLANE_TOP	0
1600 #define TC_PLANE_BOTTOM	1100
1601 
1602 #define TC_DELAY 9000
1603 #define TC_STOPTIME 81000
CIN_AddTextCrawl()1604 static void CIN_AddTextCrawl()
1605 {
1606 	refdef_t	refdef;
1607 	polyVert_t	verts[4];
1608 
1609 	// Set up refdef
1610 	memset( &refdef, 0, sizeof( refdef ));
1611 
1612 	refdef.rdflags = RDF_NOWORLDMODEL;
1613 	AxisClear( refdef.viewaxis );
1614 
1615 	refdef.fov_x = 130;
1616 	refdef.fov_y = 130;
1617 
1618 	refdef.x = 0;
1619 	refdef.y = -50;
1620 	refdef.width = cls.glconfig.vidWidth;
1621 	refdef.height = cls.glconfig.vidHeight * 2; // deliberately extend off the bottom of the screen
1622 
1623 	// use to set shaderTime for scrolling shaders
1624 	refdef.time = 0;
1625 
1626 	// Set up the poly verts
1627 	float fadeDown = 1.0;
1628 	if (cls.realtime-CL_iPlaybackStartTime >= (TC_STOPTIME-2500))
1629 	{
1630 		fadeDown = (TC_STOPTIME - (cls.realtime-CL_iPlaybackStartTime))/ 2480.0f;
1631 		if (fadeDown < 0)
1632 		{
1633 			fadeDown = 0;
1634 		}
1635 		if (fadeDown > 1)
1636 		{
1637 			fadeDown = 1;
1638 		}
1639 	}
1640 	for ( int i = 0; i < 4; i++ )
1641 	{
1642 		verts[i].modulate[0] = 255*fadeDown; // gold color?
1643 		verts[i].modulate[1] = 235*fadeDown;
1644 		verts[i].modulate[2] = 127*fadeDown;
1645 		verts[i].modulate[3] = 255*fadeDown;
1646 	}
1647 
1648 	VectorScaleM( verts[2].modulate, 0.1f, verts[2].modulate ); // darken at the top??
1649 	VectorScaleM( verts[3].modulate, 0.1f, verts[3].modulate );
1650 
1651 #define TIMEOFFSET  +(cls.realtime-CL_iPlaybackStartTime-TC_DELAY)*0.000015f -1
1652 	VectorSet( verts[0].xyz, TC_PLANE_NEAR, -TC_PLANE_WIDTH, TC_PLANE_TOP );
1653 	verts[0].st[0] = 1;
1654 	verts[0].st[1] = 1 TIMEOFFSET;
1655 
1656 	VectorSet( verts[1].xyz, TC_PLANE_NEAR, TC_PLANE_WIDTH, TC_PLANE_TOP );
1657 	verts[1].st[0] = 0;
1658 	verts[1].st[1] = 1 TIMEOFFSET;
1659 
1660 	VectorSet( verts[2].xyz, TC_PLANE_FAR, TC_PLANE_WIDTH, TC_PLANE_BOTTOM );
1661 	verts[2].st[0] = 0;
1662 	verts[2].st[1] = 0 TIMEOFFSET;
1663 
1664 	VectorSet( verts[3].xyz, TC_PLANE_FAR, -TC_PLANE_WIDTH, TC_PLANE_BOTTOM );
1665 	verts[3].st[0] = 1;
1666 	verts[3].st[1] = 0 TIMEOFFSET;
1667 
1668 	// render it out
1669 	re.ClearScene();
1670 	re.AddPolyToScene( cinTable[CL_handle].hCRAWLTEXT, 4, verts );
1671 	re.RenderScene( &refdef );
1672 
1673 	//time's up
1674 	if (cls.realtime-CL_iPlaybackStartTime >= TC_STOPTIME)
1675 	{
1676 //		cinTable[currentHandle].holdAtEnd = qfalse;
1677 		cinTable[CL_handle].status = FMV_EOF;
1678 		RoQShutdown();
1679 		SCR_StopCinematic();	// change ROQ from FMV_IDLE to FMV_EOF, and clear some other vars
1680 	}
1681 }
1682 
1683 /*
1684 ==================
1685 CIN_ResampleCinematic
1686 
1687 Resample cinematic to 256x256 and store in buf2
1688 ==================
1689 */
CIN_ResampleCinematic(int handle,int * buf2)1690 void CIN_ResampleCinematic(int handle, int *buf2) {
1691 	int ix, iy, *buf3, xm, ym, ll;
1692 	byte	*buf;
1693 
1694 	buf = cinTable[handle].buf;
1695 
1696 	xm = cinTable[handle].CIN_WIDTH/256;
1697 	ym = cinTable[handle].CIN_HEIGHT/256;
1698 	ll = 8;
1699 	if (cinTable[handle].CIN_WIDTH==512) {
1700 		ll = 9;
1701 	}
1702 
1703 	buf3 = (int*)buf;
1704 	if (xm==2 && ym==2) {
1705 		byte *bc2, *bc3;
1706 		int	ic, iiy;
1707 
1708 		bc2 = (byte *)buf2;
1709 		bc3 = (byte *)buf3;
1710 		for (iy = 0; iy<256; iy++) {
1711 			iiy = iy<<12;
1712 			for (ix = 0; ix<2048; ix+=8) {
1713 				for(ic = ix;ic<(ix+4);ic++) {
1714 					*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic]+bc3[iiy+2048+ic]+bc3[iiy+2048+4+ic])>>2;
1715 					bc2++;
1716 				}
1717 			}
1718 		}
1719 	} else if (xm==2 && ym==1) {
1720 		byte *bc2, *bc3;
1721 		int	ic, iiy;
1722 
1723 		bc2 = (byte *)buf2;
1724 		bc3 = (byte *)buf3;
1725 		for (iy = 0; iy<256; iy++) {
1726 			iiy = iy<<11;
1727 			for (ix = 0; ix<2048; ix+=8) {
1728 				for(ic = ix;ic<(ix+4);ic++) {
1729 					*bc2=(bc3[iiy+ic]+bc3[iiy+4+ic])>>1;
1730 					bc2++;
1731 				}
1732 			}
1733 		}
1734 	} else {
1735 		for (iy = 0; iy<256; iy++) {
1736 			for (ix = 0; ix<256; ix++) {
1737 					buf2[(iy<<8)+ix] = buf3[((iy*ym)<<ll) + (ix*xm)];
1738 			}
1739 		}
1740 	}
1741 }
1742 
1743 /*
1744 ==================
1745 CIN_DrawCinematic
1746 
1747 ==================
1748 */
CIN_DrawCinematic(int handle)1749 void CIN_DrawCinematic (int handle) {
1750 	float	x, y, w, h;
1751 	byte	*buf;
1752 
1753 	if (handle < 0 || handle>= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return;
1754 
1755 	if (!cinTable[handle].buf) {
1756 		return;
1757 	}
1758 
1759 	x = cinTable[handle].xpos;
1760 	y = cinTable[handle].ypos;
1761 	w = cinTable[handle].width;
1762 	h = cinTable[handle].height;
1763 	buf = cinTable[handle].buf;
1764 
1765 	if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) {
1766 		int *buf2;
1767 
1768 		//buf2 = (int *)Hunk_AllocateTempMemory( 256*256*4 );
1769 		buf2 = (int*)Z_Malloc( 256*256*4, TAG_TEMP_WORKSPACE, qfalse );
1770 
1771 		CIN_ResampleCinematic(handle, buf2);
1772 
1773 		re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue);
1774 		cinTable[handle].dirty = qfalse;
1775 		Z_Free(buf2); //Hunk_FreeTempMemory(buf2);
1776 		return;
1777 	}
1778 
1779 	re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty);
1780 	cinTable[handle].dirty = qfalse;
1781 }
1782 
1783 // external vars so I can check if the game is setup enough that I can play the intro video...
1784 //
1785 extern qboolean	com_fullyInitialized;
1786 extern qboolean s_soundStarted, s_soundMuted;
1787 //
1788 // ... and if the app isn't ready yet (which should only apply for the intro video), then I use these...
1789 //
1790 static char	 sPendingCinematic_Arg	[256]={0};
1791 static char	 sPendingCinematic_s	[256]={0};
1792 static qboolean gbPendingCinematic = qfalse;
1793 //
1794 // This stuff is for EF1-type ingame cinematics...
1795 //
1796 static qboolean qbPlayingInGameCinematic = qfalse;
1797 static qboolean qbInGameCinematicOnStandBy = qfalse;
1798 static char	 sInGameCinematicStandingBy[MAX_QPATH];
1799 static char	 sTextCrawlFixedCinematic[MAX_QPATH];
1800 static qboolean qbTextCrawlFixed = qfalse;
1801 static int	 stopCinematicCallCount = 0;
1802 
1803 
1804 
CIN_HardwareReadyToPlayVideos(void)1805 static qboolean CIN_HardwareReadyToPlayVideos(void)
1806 {
1807 	if (com_fullyInitialized && cls.rendererStarted &&
1808 								cls.soundStarted	&&
1809 								cls.soundRegistered
1810 		)
1811 	{
1812 		return qtrue;
1813 	}
1814 
1815 	return qfalse;
1816 }
1817 
1818 
PlayCinematic(const char * arg,const char * s,qboolean qbInGame)1819 static void PlayCinematic(const char *arg, const char *s, qboolean qbInGame)
1820 {
1821 	qboolean bFailed = qfalse;
1822 
1823 	Cvar_Set( "timescale", "1" );			// jic we were skipping a scripted cinematic, return to normal after playing video
1824 	Cvar_Set( "skippingCinematic", "0" );	// ""
1825 
1826 	if(qbInGameCinematicOnStandBy == qfalse)
1827 	{
1828 		qbTextCrawlFixed = qfalse;
1829 	}
1830 	else
1831 	{
1832 		qbInGameCinematicOnStandBy = qfalse;
1833 	}
1834 
1835 	int bits = qbInGame?0:CIN_system;
1836 
1837 	Com_DPrintf("CL_PlayCinematic_f\n");
1838 
1839 	char sTemp[1024];
1840 	if (strstr(arg, "/") == NULL && strstr(arg, "\\") == NULL) {
1841 		Com_sprintf (sTemp, sizeof(sTemp), "video/%s", arg);
1842 	} else {
1843 		Com_sprintf (sTemp, sizeof(sTemp), "%s", arg);
1844 	}
1845 	COM_DefaultExtension(sTemp,sizeof(sTemp),".roq");
1846 	arg = &sTemp[0];
1847 
1848 	extern qboolean S_FileExists( const char *psFilename );
1849 	if (S_FileExists( arg ))
1850 	{
1851 		SCR_StopCinematic();
1852 		// command-line hack to avoid problems when playing intro video before app is fully setup...
1853 		//
1854 		if (!CIN_HardwareReadyToPlayVideos())
1855 		{
1856 			Q_strncpyz(sPendingCinematic_Arg,arg, 256);
1857 			Q_strncpyz(sPendingCinematic_s , (s&&s[0])?s:"", 256);
1858 			gbPendingCinematic = qtrue;
1859 			return;
1860 		}
1861 
1862 		qbPlayingInGameCinematic = qbInGame;
1863 
1864 		if ((s && s[0] == '1') || Q_stricmp(arg,"video/end.roq")==0) {
1865 			bits |= CIN_hold;
1866 		}
1867 		if (s && s[0] == '2') {
1868 			bits |= CIN_loop;
1869 		}
1870 
1871 		S_StopAllSounds ();
1872 
1873 
1874 		////////////////////////////////////////////////////////////////////
1875 		//
1876 		// work out associated audio-overlay file, if any...
1877 		//
1878 		extern cvar_t *s_language;
1879 		qboolean	bIsForeign	= (qboolean)(s_language && Q_stricmp(s_language->string,"english") && Q_stricmp(s_language->string,""));
1880 		const char *psAudioFile	= NULL;
1881 		qhandle_t	hCrawl = 0;
1882 		if (!Q_stricmp(arg,"video/jk0101_sw.roq"))
1883 		{
1884 			psAudioFile = "music/cinematic_1";
1885 #ifdef JK2_MODE
1886 			hCrawl = re.RegisterShaderNoMip( va("menu/video/tc_%d", sp_language->integer) );
1887 			if(!hCrawl)
1888 			{
1889 				// failed, so go back to english
1890 				hCrawl = re.RegisterShaderNoMip( "menu/video/tc_0" );
1891 			}
1892 #else
1893 			hCrawl = re.RegisterShaderNoMip( va("menu/video/tc_%s",se_language->string) );
1894 			if (!hCrawl)
1895 			{
1896 				hCrawl = re.RegisterShaderNoMip( "menu/video/tc_english" );//failed, so go back to english
1897 			}
1898 #endif
1899 			bits |= CIN_hold;
1900 		}
1901 		else
1902 		if (bIsForeign)
1903 		{
1904 			if (!Q_stricmp(arg,"video/jk05.roq"))
1905 			{
1906 				psAudioFile = "sound/chars/video/cinematic_5";
1907 				bits |= CIN_silent;	// knock out existing english track
1908 			}
1909 			else
1910 			if (!Q_stricmp(arg,"video/jk06.roq"))
1911 			{
1912 				psAudioFile = "sound/chars/video/cinematic_6";
1913 				bits |= CIN_silent;	// knock out existing english track
1914 			}
1915 		}
1916 		//
1917 		////////////////////////////////////////////////////////////////////
1918 
1919 		CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits, psAudioFile );
1920 		if (CL_handle >= 0)
1921 		{
1922 			cinTable[CL_handle].hCRAWLTEXT = hCrawl;
1923 			do
1924 			{
1925 				SCR_RunCinematic();
1926 			}
1927 			while (cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY);		// wait for first frame (load codebook and sound)
1928 
1929 			if (qbInGame)
1930 			{
1931 				Cvar_SetValue( "cl_paused", 1);	// remove-menu call will have unpaused us, so we sometimes need to re-pause
1932 			}
1933 
1934 			CL_iPlaybackStartTime = cls.realtime;	// special use to avoid accidentally skipping ingame videos via fast-firing
1935 		}
1936 		else
1937 		{
1938 			// failed to open video...
1939 			//
1940 			bFailed = qtrue;
1941 		}
1942 	}
1943 	else
1944 	{
1945 		// failed to open video...
1946 		//
1947 		bFailed = qtrue;
1948 	}
1949 
1950 	if (bFailed)
1951 	{
1952 		Com_Printf(S_COLOR_RED "PlayCinematic(): Failed to open \"%s\"\n",arg);
1953 		//S_RestartMusic();	//restart the level music
1954 		SCR_StopCinematic();	// I know this seems pointless, but it clears a bunch of vars as well
1955 	}
1956 	else
1957 	{
1958 		// this doesn't work for now...
1959 		//
1960 //		if (cls.state == CA_ACTIVE){
1961 //			re.InitDissolve(qfalse);	// so we get a dissolve between previous screen image and cinematic
1962 //		}
1963 	}
1964 }
1965 
1966 
CL_CheckPendingCinematic(void)1967 qboolean CL_CheckPendingCinematic(void)
1968 {
1969 	if ( gbPendingCinematic && CIN_HardwareReadyToPlayVideos() )
1970 	{
1971 		gbPendingCinematic = qfalse;	// BEFORE next line, or we get recursion
1972 		PlayCinematic(sPendingCinematic_Arg,sPendingCinematic_s[0]?sPendingCinematic_s:NULL,qfalse);
1973 		return qtrue;
1974 	}
1975 	return qfalse;
1976 }
1977 
1978 /*
1979 ==================
1980 CL_CompleteCinematic
1981 ==================
1982 */
CL_CompleteCinematic(char * args,int argNum)1983 void CL_CompleteCinematic( char *args, int argNum ) {
1984 	if ( argNum == 2 )
1985 		Field_CompleteFilename( "video", "roq", qtrue, qfalse );
1986 }
1987 
CL_PlayCinematic_f(void)1988 void CL_PlayCinematic_f(void)
1989 {
1990 	const char	*arg, *s;
1991 
1992 	arg = Cmd_Argv( 1 );
1993 	s = Cmd_Argv(2);
1994 	PlayCinematic(arg,s,qfalse);
1995 }
1996 
CL_PlayInGameCinematic_f(void)1997 void CL_PlayInGameCinematic_f(void)
1998 {
1999 	const char *arg = Cmd_Argv( 1 );
2000 	if (cls.state == CA_ACTIVE)
2001 	{
2002 		PlayCinematic(arg,NULL,qtrue);
2003 	}
2004 	else if( !qbInGameCinematicOnStandBy )
2005 	{
2006 		Q_strncpyz(sInGameCinematicStandingBy, arg, MAX_QPATH);
2007 		qbInGameCinematicOnStandBy = qtrue;
2008 	}
2009 	else
2010 	{
2011 		// hack in order to fix text crawl --eez
2012 		Q_strncpyz(sTextCrawlFixedCinematic, arg, MAX_QPATH);
2013 		qbTextCrawlFixed = qtrue;
2014 	}
2015 }
2016 
2017 
2018 // Externally-called only, and only if cls.state == CA_CINEMATIC (or CL_IsRunningInGameCinematic() == true now)
2019 //
SCR_DrawCinematic(void)2020 void SCR_DrawCinematic (void)
2021 {
2022 	if (CL_InGameCinematicOnStandBy())
2023 	{
2024 		PlayCinematic(sInGameCinematicStandingBy,NULL,qtrue);
2025 	}
2026 	else if( qbTextCrawlFixed && stopCinematicCallCount > 1)
2027 	{
2028 		PlayCinematic(sTextCrawlFixedCinematic, NULL, qtrue);
2029 	}
2030 
2031 	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
2032 		CIN_DrawCinematic(CL_handle);
2033 		if (cinTable[CL_handle].hCRAWLTEXT && (cls.realtime - CL_iPlaybackStartTime >= TC_DELAY))
2034 		{
2035 			CIN_AddTextCrawl();
2036 		}
2037 	}
2038 }
2039 
SCR_RunCinematic(void)2040 void SCR_RunCinematic (void)
2041 {
2042 	CL_CheckPendingCinematic();
2043 
2044 	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES) {
2045 		e_status Status = CIN_RunCinematic(CL_handle);
2046 
2047 		if (CL_IsRunningInGameCinematic() && Status == FMV_IDLE  && !cinTable[CL_handle].holdAtEnd)
2048 		{
2049 			SCR_StopCinematic();	// change ROQ from FMV_IDLE to FMV_EOF, and clear some other vars
2050 		}
2051 	}
2052 }
2053 
SCR_StopCinematic(qboolean bAllowRefusal)2054 void SCR_StopCinematic( qboolean bAllowRefusal /* = qfalse */ )
2055 {
2056 	if (bAllowRefusal)
2057 	{
2058 		if ( (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES)
2059 			&&
2060 			cls.realtime < CL_iPlaybackStartTime + 1200	// 1.2 seconds have to have elapsed
2061 			)
2062 		{
2063 			return;
2064 		}
2065 	}
2066 
2067 	if ( CL_IsRunningInGameCinematic())
2068 	{
2069 		Com_DPrintf("In-game Cinematic Stopped\n");
2070 	}
2071 
2072 	if (CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES &&
2073 		stopCinematicCallCount != 1) {			// hello no, don't want this plz
2074 		CIN_StopCinematic(CL_handle);
2075 		S_StopAllSounds ();
2076 		CL_handle = -1;
2077 		if (CL_IsRunningInGameCinematic()){
2078 			re.InitDissolve(qfalse);	// dissolve from cinematic to underlying ingame
2079 		}
2080 	}
2081 
2082 	if (cls.state == CA_CINEMATIC)
2083 	{
2084 		Com_DPrintf("Cinematic Stopped\n");
2085 		cls.state =  CA_DISCONNECTED;
2086 	}
2087 
2088 	if(sInGameCinematicStandingBy[0] &&
2089 		qbTextCrawlFixed)
2090 	{
2091 		// Hacky fix to help deal with broken text crawl..
2092 		// If we are skipping past the one on standby, DO NOT SKIP THE OTHER ONES!
2093 		stopCinematicCallCount++;
2094 	}
2095 	else if(stopCinematicCallCount == 1)
2096 	{
2097 		stopCinematicCallCount++;
2098 	}
2099 	else
2100 	{
2101 		// Skipping the last one in the list, go ahead and kill it.
2102 		qbTextCrawlFixed = qfalse;
2103 		sTextCrawlFixedCinematic[0] = 0;
2104 		stopCinematicCallCount = 0;
2105 	}
2106 
2107 	if(stopCinematicCallCount != 2)
2108 	{
2109 		qbPlayingInGameCinematic = qfalse;
2110 		qbInGameCinematicOnStandBy = qfalse;
2111 		sInGameCinematicStandingBy[0]=0;
2112 		Cvar_SetValue( "cl_paused", 0 );
2113 	}
2114 	if (cls.state != CA_DISCONNECTED)	// cut down on needless calls to music code
2115 	{
2116 		S_RestartMusic();	//restart the level music
2117 	}
2118 }
2119 
2120 
CIN_UploadCinematic(int handle)2121 void CIN_UploadCinematic(int handle) {
2122 	if (handle >= 0 && handle < MAX_VIDEO_HANDLES) {
2123 		if (!cinTable[handle].buf) {
2124 			return;
2125 		}
2126 		if (cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty) {
2127 			if (cinTable[handle].playonwalls == 0) {
2128 				cinTable[handle].playonwalls = -1;
2129 			} else {
2130 				if (cinTable[handle].playonwalls == -1) {
2131 					cinTable[handle].playonwalls = -2;
2132 				} else {
2133 					cinTable[handle].dirty = qfalse;
2134 				}
2135 			}
2136 		}
2137 
2138 		// Resample the video if needed
2139 		if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY))  {
2140 			int *buf2;
2141 
2142 			buf2 = (int *)Z_Malloc(256*256*4, TAG_TEMP_WORKSPACE, qfalse);
2143 
2144 			CIN_ResampleCinematic(handle, buf2);
2145 
2146 			re.UploadCinematic( 256, 256, (byte *)buf2, handle, qtrue);
2147 			cinTable[handle].dirty = qfalse;
2148 			Z_Free(buf2);
2149 		} else {
2150 			// Upload video at normal resolution
2151 			re.UploadCinematic( cinTable[handle].drawX, cinTable[handle].drawY,
2152 					cinTable[handle].buf, handle, cinTable[handle].dirty);
2153 			cinTable[handle].dirty = qfalse;
2154 		}
2155 
2156 		if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) {
2157 			cinTable[handle].playonwalls--;
2158 		}
2159 		else if (cl_inGameVideo->integer != 0 && cinTable[handle].playonwalls != 1) {
2160 			cinTable[handle].playonwalls = 1;
2161 		}
2162 	}
2163 }
2164 
2165 
CL_IsRunningInGameCinematic(void)2166 qboolean CL_IsRunningInGameCinematic(void)
2167 {
2168 	return qbPlayingInGameCinematic;
2169 }
2170 
CL_InGameCinematicOnStandBy(void)2171 qboolean CL_InGameCinematicOnStandBy(void)
2172 {
2173 	return qbInGameCinematicOnStandBy;
2174 }
2175 
2176 
2177