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