/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW MP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ /***************************************************************************** * name: cl_cin.c * * desc: video and cinematic playback * * * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256 * *****************************************************************************/ //#define ADAPTED_TO_STREAMING_SOUND // (SA) MISSIONPACK MERGE // s_rawend for wolf is [] and for q3 is just a single value // I need to ask Ryan if it's as simple as a constant index or // if some more coding needs to be done. #include "client.h" #include "snd_local.h" #define MAXSIZE 8 #define MINSIZE 4 #define DEFAULT_CIN_WIDTH 512 #define DEFAULT_CIN_HEIGHT 512 #define ROQ_QUAD 0x1000 #define ROQ_QUAD_INFO 0x1001 #define ROQ_CODEBOOK 0x1002 #define ROQ_QUAD_VQ 0x1011 #define ROQ_QUAD_JPEG 0x1012 #define ROQ_QUAD_HANG 0x1013 #define ROQ_PACKET 0x1030 #define ZA_SOUND_MONO 0x1020 #define ZA_SOUND_STEREO 0x1021 #define MAX_VIDEO_HANDLES 16 extern glconfig_t glConfig; static void RoQ_init( void ); /****************************************************************************** * * Class: trFMV * * Description: RoQ/RnR manipulation routines * not entirely complete for first run * ******************************************************************************/ static long ROQ_YY_tab[256]; static long ROQ_UB_tab[256]; static long ROQ_UG_tab[256]; static long ROQ_VG_tab[256]; static long ROQ_VR_tab[256]; static unsigned short vq2[256 * 16 * 4]; static unsigned short vq4[256 * 64 * 4]; static unsigned short vq8[256 * 256 * 4]; typedef struct { byte linbuf[DEFAULT_CIN_WIDTH * DEFAULT_CIN_HEIGHT * 4 * 2]; byte file[65536]; short sqrTable[256]; unsigned int mcomp[256]; byte *qStatus[2][32768]; long oldXOff, oldYOff, oldysize, oldxsize; int currentHandle; } cinematics_t; typedef struct { char fileName[MAX_OSPATH]; int CIN_WIDTH, CIN_HEIGHT; int xpos, ypos, width, height; qboolean looping, holdAtEnd, dirty, alterGameState, silent, shader; fileHandle_t iFile; e_status status; unsigned int startTime; unsigned int lastTime; long tfps; long RoQPlayed; long ROQSize; unsigned int RoQFrameSize; long onQuad; long numQuads; long samplesPerLine; unsigned int roq_id; long screenDelta; void ( *VQ0 )( byte *status, void *qdata ); void ( *VQ1 )( byte *status, void *qdata ); void ( *VQNormal )( byte *status, void *qdata ); void ( *VQBuffer )( byte *status, void *qdata ); long samplesPerPixel; // defaults to 2 byte* gray; unsigned int xsize, ysize, maxsize, minsize; qboolean half, smootheddouble, inMemory; long normalBuffer0; long roq_flags; long roqF0; long roqF1; long t[2]; long roqFPS; int playonwalls; byte* buf; long drawX, drawY; } cin_cache; static cinematics_t cin; static cin_cache cinTable[MAX_VIDEO_HANDLES]; static int currentHandle = -1; static int CL_handle = -1; void CIN_CloseAllVideos( void ) { int i; for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if ( cinTable[i].fileName[0] != 0 ) { CIN_StopCinematic( i ); } } } static int CIN_HandleForVideo( void ) { int i; for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if ( cinTable[i].fileName[0] == 0 ) { return i; } } Com_Error( ERR_DROP, "CIN_HandleForVideo: none free" ); return -1; } extern int CL_ScaledMilliseconds( void ); //----------------------------------------------------------------------------- // RllSetupTable // // Allocates and initializes the square table. // // Parameters: None // // Returns: Nothing //----------------------------------------------------------------------------- static void RllSetupTable() { int z; for ( z = 0; z < 128; z++ ) { cin.sqrTable[z] = (short)( z * z ); cin.sqrTable[z + 128] = (short)( -cin.sqrTable[z] ); } } //----------------------------------------------------------------------------- // RllDecodeMonoToMono // // Decode mono source data into a mono buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= # of shorts of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeMonoToMono( unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag ) { unsigned int z; int prev; if ( signedOutput ) { prev = flag - 0x8000; } else { prev = flag; } for ( z = 0; z < size; z++ ) { prev = to[z] = (short)( prev + cin.sqrTable[from[z]] ); } return size; //*sizeof(short)); } //----------------------------------------------------------------------------- // RllDecodeMonoToStereo // // Decode mono source data into a stereo buffer. Output is 4 times the number // of bytes in the input. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= 1/4 # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeMonoToStereo( unsigned char *from,short *to,unsigned int size,char signedOutput,unsigned short flag ) { unsigned int z; int prev; if ( signedOutput ) { prev = flag - 0x8000; } else { prev = flag; } for ( z = 0; z < size; z++ ) { prev = (short)( prev + cin.sqrTable[from[z]] ); to[z * 2 + 0] = to[z * 2 + 1] = (short)( prev ); } return size; // * 2 * sizeof(short)); } //----------------------------------------------------------------------------- // RllDecodeStereoToStereo // // Decode stereo source data into a stereo buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= 1/2 # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeStereoToStereo( unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag ) { unsigned int z; unsigned char *zz = from; int prevL, prevR; if ( signedOutput ) { prevL = ( flag & 0xff00 ) - 0x8000; prevR = ( ( flag & 0x00ff ) << 8 ) - 0x8000; } else { prevL = flag & 0xff00; prevR = ( flag & 0x00ff ) << 8; } for ( z = 0; z < size; z += 2 ) { prevL = (short)( prevL + cin.sqrTable[*zz++] ); prevR = (short)( prevR + cin.sqrTable[*zz++] ); to[z + 0] = (short)( prevL ); to[z + 1] = (short)( prevR ); } return ( size >> 1 ); //*sizeof(short)); } //----------------------------------------------------------------------------- // RllDecodeStereoToMono // // Decode stereo source data into a mono buffer. // // Parameters: from -> buffer holding encoded data // to -> buffer to hold decoded data // size = number of bytes of input (= # of bytes of output) // signedOutput = 0 for unsigned output, non-zero for signed output // flag = flags from asset header // // Returns: Number of samples placed in output buffer //----------------------------------------------------------------------------- long RllDecodeStereoToMono( unsigned char *from,short *to,unsigned int size,char signedOutput, unsigned short flag ) { unsigned int z; int prevL,prevR; if ( signedOutput ) { prevL = ( flag & 0xff00 ) - 0x8000; prevR = ( ( flag & 0x00ff ) << 8 ) - 0x8000; } else { prevL = flag & 0xff00; prevR = ( flag & 0x00ff ) << 8; } for ( z = 0; z < size; z += 1 ) { prevL = prevL + cin.sqrTable[from[z * 2]]; prevR = prevR + cin.sqrTable[from[z * 2 + 1]]; to[z] = (short)( ( prevL + prevR ) / 2 ); } return size; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void move8_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void move4_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += dspl; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blit8_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; dsrc += 4; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; ddst[2] = dsrc[2]; ddst[3] = dsrc[3]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ #define movs double static void blit4_32( byte *src, byte *dst, int spl ) { movs *dsrc, *ddst; int dspl; dsrc = (movs *)src; ddst = (movs *)dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; dsrc += 2; ddst += dspl; ddst[0] = dsrc[0]; ddst[1] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blit2_32( byte *src, byte *dst, int spl ) { double *dsrc, *ddst; int dspl; dsrc = (double *)src; ddst = (double *)dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[dspl] = dsrc[1]; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void blitVQQuad32fs( byte **status, unsigned char *data ) { unsigned short newd, celdata, code; unsigned int index, i; int spl; newd = 0; celdata = 0; index = 0; spl = cinTable[currentHandle].samplesPerLine; do { if ( !newd ) { newd = 7; celdata = data[0] + data[1] * 256; data += 2; } else { newd--; } code = ( unsigned short )( celdata & 0xc000 ); celdata <<= 2; switch ( code ) { case 0x8000: // vq code blit8_32( (byte *)&vq8[( *data ) * 128], status[index], spl ); data++; index += 5; break; case 0xc000: // drop index++; // skip 8x8 for ( i = 0; i < 4; i++ ) { if ( !newd ) { newd = 7; celdata = data[0] + data[1] * 256; data += 2; } else { newd--; } code = ( unsigned short )( celdata & 0xc000 ); celdata <<= 2; switch ( code ) { // code in top two bits of code case 0x8000: // 4x4 vq code blit4_32( (byte *)&vq4[( *data ) * 32], status[index], spl ); data++; break; case 0xc000: // 2x2 vq code blit2_32( (byte *)&vq2[( *data ) * 8], status[index], spl ); data++; blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + 8, spl ); data++; blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + spl * 2, spl ); data++; blit2_32( (byte *)&vq2[( *data ) * 8], status[index] + spl * 2 + 8, spl ); data++; break; case 0x4000: // motion compensation move4_32( status[index] + cin.mcomp[( *data )], status[index], spl ); data++; break; } index++; } break; case 0x4000: // motion compensation move8_32( status[index] + cin.mcomp[( *data )], status[index], spl ); data++; index += 5; break; case 0x0000: index += 5; break; } } while ( status[index] != NULL ); } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void ROQ_GenYUVTables( void ) { float t_ub,t_vr,t_ug,t_vg; long i; t_ub = ( 1.77200f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; t_vr = ( 1.40200f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; t_ug = ( 0.34414f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; t_vg = ( 0.71414f / 2.0f ) * (float)( 1 << 6 ) + 0.5f; for ( i = 0; i < 256; i++ ) { float x = (float)( 2 * i - 255 ); ROQ_UB_tab[i] = (long)( ( t_ub * x ) + ( 1 << 5 ) ); ROQ_VR_tab[i] = (long)( ( t_vr * x ) + ( 1 << 5 ) ); ROQ_UG_tab[i] = (long)( ( -t_ug * x ) ); ROQ_VG_tab[i] = (long)( ( -t_vg * x ) + ( 1 << 5 ) ); ROQ_YY_tab[i] = (long)( ( i << 6 ) | ( i >> 2 ) ); } } #define VQ2TO4( a,b,c,d ) { \ *c++ = a[0]; \ *d++ = a[0]; \ *d++ = a[0]; \ *c++ = a[1]; \ *d++ = a[1]; \ *d++ = a[1]; \ *c++ = b[0]; \ *d++ = b[0]; \ *d++ = b[0]; \ *c++ = b[1]; \ *d++ = b[1]; \ *d++ = b[1]; \ *d++ = a[0]; \ *d++ = a[0]; \ *d++ = a[1]; \ *d++ = a[1]; \ *d++ = b[0]; \ *d++ = b[0]; \ *d++ = b[1]; \ *d++ = b[1]; \ a += 2; b += 2; } #define VQ2TO2( a,b,c,d ) { \ *c++ = *a; \ *d++ = *a; \ *d++ = *a; \ *c++ = *b; \ *d++ = *b; \ *d++ = *b; \ *d++ = *a; \ *d++ = *a; \ *d++ = *b; \ *d++ = *b; \ a++; b++; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static unsigned short yuv_to_rgb( long y, long u, long v ) { long r,g,b,YY = (long)( ROQ_YY_tab[( y )] ); r = ( YY + ROQ_VR_tab[v] ) >> 9; g = ( YY + ROQ_UG_tab[u] + ROQ_VG_tab[v] ) >> 8; b = ( YY + ROQ_UB_tab[u] ) >> 9; if ( r < 0 ) { r = 0; } if ( g < 0 ) { g = 0; } if ( b < 0 ) { b = 0; } if ( r > 31 ) { r = 31; } if ( g > 63 ) { g = 63; } if ( b > 31 ) { b = 31; } return ( unsigned short )( ( r << 11 ) + ( g << 5 ) + ( b ) ); } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ #if defined( MACOS_X ) static inline unsigned int yuv_to_rgb24( long y, long u, long v ) { long r,g,b,YY; YY = (long)( ROQ_YY_tab[( y )] ); r = ( YY + ROQ_VR_tab[v] ) >> 6; g = ( YY + ROQ_UG_tab[u] + ROQ_VG_tab[v] ) >> 6; b = ( YY + ROQ_UB_tab[u] ) >> 6; if ( r < 0 ) { r = 0; } if ( g < 0 ) { g = 0; } if ( b < 0 ) { b = 0; } if ( r > 255 ) { r = 255; } if ( g > 255 ) { g = 255; } if ( b > 255 ) { b = 255; } return ( ( r << 24 ) | ( g << 16 ) | ( b << 8 ) ) | ( 255 ); //+(255<<24)); } #else static unsigned int yuv_to_rgb24( long y, long u, long v ) { long r,g,b,YY = (long)( ROQ_YY_tab[( y )] ); r = ( YY + ROQ_VR_tab[v] ) >> 6; g = ( YY + ROQ_UG_tab[u] + ROQ_VG_tab[v] ) >> 6; b = ( YY + ROQ_UB_tab[u] ) >> 6; if ( r < 0 ) { r = 0; } if ( g < 0 ) { g = 0; } if ( b < 0 ) { b = 0; } if ( r > 255 ) { r = 255; } if ( g > 255 ) { g = 255; } if ( b > 255 ) { b = 255; } return LittleLong( ( r ) | ( g << 8 ) | ( b << 16 ) | ( 255 << 24 ) ); } #endif /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void decodeCodeBook( byte *input, unsigned short roq_flags ) { long i, j, two, four; unsigned short *aptr, *bptr, *cptr, *dptr; long y0,y1,y2,y3,cr,cb; byte *bbptr, *baptr, *bcptr, *bdptr; unsigned int *iaptr, *ibptr, *icptr, *idptr; if ( !roq_flags ) { two = four = 256; } else { two = roq_flags >> 8; if ( !two ) { two = 256; } four = roq_flags & 0xff; } four *= 2; bptr = (unsigned short *)vq2; if ( !cinTable[currentHandle].half ) { if ( !cinTable[currentHandle].smootheddouble ) { // // normal height // if ( cinTable[currentHandle].samplesPerPixel == 2 ) { for ( i = 0; i < two; i++ ) { y0 = (long)*input++; y1 = (long)*input++; y2 = (long)*input++; y3 = (long)*input++; cr = (long)*input++; cb = (long)*input++; *bptr++ = yuv_to_rgb( y0, cr, cb ); *bptr++ = yuv_to_rgb( y1, cr, cb ); *bptr++ = yuv_to_rgb( y2, cr, cb ); *bptr++ = yuv_to_rgb( y3, cr, cb ); } cptr = (unsigned short *)vq4; dptr = (unsigned short *)vq8; for ( i = 0; i < four; i++ ) { aptr = (unsigned short *)vq2 + ( *input++ ) * 4; bptr = (unsigned short *)vq2 + ( *input++ ) * 4; for ( j = 0; j < 2; j++ ) VQ2TO4( aptr,bptr,cptr,dptr ); } } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { ibptr = (unsigned int *)bptr; for ( i = 0; i < two; i++ ) { y0 = (long)*input++; y1 = (long)*input++; y2 = (long)*input++; y3 = (long)*input++; cr = (long)*input++; cb = (long)*input++; *ibptr++ = yuv_to_rgb24( y0, cr, cb ); *ibptr++ = yuv_to_rgb24( y1, cr, cb ); *ibptr++ = yuv_to_rgb24( y2, cr, cb ); *ibptr++ = yuv_to_rgb24( y3, cr, cb ); } icptr = (unsigned int *)vq4; idptr = (unsigned int *)vq8; for ( i = 0; i < four; i++ ) { iaptr = (unsigned int *)vq2 + ( *input++ ) * 4; ibptr = (unsigned int *)vq2 + ( *input++ ) * 4; for ( j = 0; j < 2; j++ ) VQ2TO4( iaptr, ibptr, icptr, idptr ); } } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { bbptr = (byte *)bptr; for ( i = 0; i < two; i++ ) { *bbptr++ = cinTable[currentHandle].gray[*input++]; *bbptr++ = cinTable[currentHandle].gray[*input++]; *bbptr++ = cinTable[currentHandle].gray[*input++]; *bbptr++ = cinTable[currentHandle].gray[*input]; input += 3; } bcptr = (byte *)vq4; bdptr = (byte *)vq8; for ( i = 0; i < four; i++ ) { baptr = (byte *)vq2 + ( *input++ ) * 4; bbptr = (byte *)vq2 + ( *input++ ) * 4; for ( j = 0; j < 2; j++ ) VQ2TO4( baptr,bbptr,bcptr,bdptr ); } } } else { // // double height, smoothed // if ( cinTable[currentHandle].samplesPerPixel == 2 ) { for ( i = 0; i < two; i++ ) { y0 = (long)*input++; y1 = (long)*input++; y2 = (long)*input++; y3 = (long)*input++; cr = (long)*input++; cb = (long)*input++; *bptr++ = yuv_to_rgb( y0, cr, cb ); *bptr++ = yuv_to_rgb( y1, cr, cb ); *bptr++ = yuv_to_rgb( ( ( y0 * 3 ) + y2 ) / 4, cr, cb ); *bptr++ = yuv_to_rgb( ( ( y1 * 3 ) + y3 ) / 4, cr, cb ); *bptr++ = yuv_to_rgb( ( y0 + ( y2 * 3 ) ) / 4, cr, cb ); *bptr++ = yuv_to_rgb( ( y1 + ( y3 * 3 ) ) / 4, cr, cb ); *bptr++ = yuv_to_rgb( y2, cr, cb ); *bptr++ = yuv_to_rgb( y3, cr, cb ); } cptr = (unsigned short *)vq4; dptr = (unsigned short *)vq8; for ( i = 0; i < four; i++ ) { aptr = (unsigned short *)vq2 + ( *input++ ) * 8; bptr = (unsigned short *)vq2 + ( *input++ ) * 8; for ( j = 0; j < 2; j++ ) { VQ2TO4( aptr,bptr,cptr,dptr ); VQ2TO4( aptr,bptr,cptr,dptr ); } } } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { ibptr = (unsigned int *)bptr; for ( i = 0; i < two; i++ ) { y0 = (long)*input++; y1 = (long)*input++; y2 = (long)*input++; y3 = (long)*input++; cr = (long)*input++; cb = (long)*input++; *ibptr++ = yuv_to_rgb24( y0, cr, cb ); *ibptr++ = yuv_to_rgb24( y1, cr, cb ); *ibptr++ = yuv_to_rgb24( ( ( y0 * 3 ) + y2 ) / 4, cr, cb ); *ibptr++ = yuv_to_rgb24( ( ( y1 * 3 ) + y3 ) / 4, cr, cb ); *ibptr++ = yuv_to_rgb24( ( y0 + ( y2 * 3 ) ) / 4, cr, cb ); *ibptr++ = yuv_to_rgb24( ( y1 + ( y3 * 3 ) ) / 4, cr, cb ); *ibptr++ = yuv_to_rgb24( y2, cr, cb ); *ibptr++ = yuv_to_rgb24( y3, cr, cb ); } icptr = (unsigned int *)vq4; idptr = (unsigned int *)vq8; for ( i = 0; i < four; i++ ) { iaptr = (unsigned int *)vq2 + ( *input++ ) * 8; ibptr = (unsigned int *)vq2 + ( *input++ ) * 8; for ( j = 0; j < 2; j++ ) { VQ2TO4( iaptr, ibptr, icptr, idptr ); VQ2TO4( iaptr, ibptr, icptr, idptr ); } } } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { bbptr = (byte *)bptr; for ( i = 0; i < two; i++ ) { y0 = (long)*input++; y1 = (long)*input++; y2 = (long)*input++; y3 = (long)*input; input += 3; *bbptr++ = cinTable[currentHandle].gray[y0]; *bbptr++ = cinTable[currentHandle].gray[y1]; *bbptr++ = cinTable[currentHandle].gray[( ( y0 * 3 ) + y2 ) / 4]; *bbptr++ = cinTable[currentHandle].gray[( ( y1 * 3 ) + y3 ) / 4]; *bbptr++ = cinTable[currentHandle].gray[( y0 + ( y2 * 3 ) ) / 4]; *bbptr++ = cinTable[currentHandle].gray[( y1 + ( y3 * 3 ) ) / 4]; *bbptr++ = cinTable[currentHandle].gray[y2]; *bbptr++ = cinTable[currentHandle].gray[y3]; } bcptr = (byte *)vq4; bdptr = (byte *)vq8; for ( i = 0; i < four; i++ ) { baptr = (byte *)vq2 + ( *input++ ) * 8; bbptr = (byte *)vq2 + ( *input++ ) * 8; for ( j = 0; j < 2; j++ ) { VQ2TO4( baptr,bbptr,bcptr,bdptr ); VQ2TO4( baptr,bbptr,bcptr,bdptr ); } } } } } else { // // 1/4 screen // if ( cinTable[currentHandle].samplesPerPixel == 2 ) { for ( i = 0; i < two; i++ ) { y0 = (long)*input; input += 2; y2 = (long)*input; input += 2; cr = (long)*input++; cb = (long)*input++; *bptr++ = yuv_to_rgb( y0, cr, cb ); *bptr++ = yuv_to_rgb( y2, cr, cb ); } cptr = (unsigned short *)vq4; dptr = (unsigned short *)vq8; for ( i = 0; i < four; i++ ) { aptr = (unsigned short *)vq2 + ( *input++ ) * 2; bptr = (unsigned short *)vq2 + ( *input++ ) * 2; for ( j = 0; j < 2; j++ ) { VQ2TO2( aptr,bptr,cptr,dptr ); } } } else if ( cinTable[currentHandle].samplesPerPixel == 1 ) { bbptr = (byte *)bptr; for ( i = 0; i < two; i++ ) { *bbptr++ = cinTable[currentHandle].gray[*input]; input += 2; *bbptr++ = cinTable[currentHandle].gray[*input]; input += 4; } bcptr = (byte *)vq4; bdptr = (byte *)vq8; for ( i = 0; i < four; i++ ) { baptr = (byte *)vq2 + ( *input++ ) * 2; bbptr = (byte *)vq2 + ( *input++ ) * 2; for ( j = 0; j < 2; j++ ) { VQ2TO2( baptr,bbptr,bcptr,bdptr ); } } } else if ( cinTable[currentHandle].samplesPerPixel == 4 ) { ibptr = (unsigned int *) bptr; for ( i = 0; i < two; i++ ) { y0 = (long)*input; input += 2; y2 = (long)*input; input += 2; cr = (long)*input++; cb = (long)*input++; *ibptr++ = yuv_to_rgb24( y0, cr, cb ); *ibptr++ = yuv_to_rgb24( y2, cr, cb ); } icptr = (unsigned int *)vq4; idptr = (unsigned int *)vq8; for ( i = 0; i < four; i++ ) { iaptr = (unsigned int *)vq2 + ( *input++ ) * 2; ibptr = (unsigned int *)vq2 + ( *input++ ) * 2; for ( j = 0; j < 2; j++ ) { VQ2TO2( iaptr,ibptr,icptr,idptr ); } } } } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void recurseQuad( long startX, long startY, long quadSize, long xOff, long yOff ) { byte *scroff; long bigx, bigy, lowx, lowy, useY; long offset; offset = cinTable[currentHandle].screenDelta; lowx = lowy = 0; bigx = cinTable[currentHandle].xsize; bigy = cinTable[currentHandle].ysize; if ( bigx > cinTable[currentHandle].CIN_WIDTH ) { bigx = cinTable[currentHandle].CIN_WIDTH; } if ( bigy > cinTable[currentHandle].CIN_HEIGHT ) { bigy = cinTable[currentHandle].CIN_HEIGHT; } if ( ( startX >= lowx ) && ( startX + quadSize ) <= ( bigx ) && ( startY + quadSize ) <= ( bigy ) && ( startY >= lowy ) && quadSize <= MAXSIZE ) { useY = startY; scroff = cin.linbuf + ( useY + ( ( cinTable[currentHandle].CIN_HEIGHT - bigy ) >> 1 ) + yOff ) * ( cinTable[currentHandle].samplesPerLine ) + ( ( ( startX + xOff ) ) * cinTable[currentHandle].samplesPerPixel ); cin.qStatus[0][cinTable[currentHandle].onQuad ] = scroff; cin.qStatus[1][cinTable[currentHandle].onQuad++] = scroff + offset; } if ( quadSize != MINSIZE ) { quadSize >>= 1; recurseQuad( startX, startY, quadSize, xOff, yOff ); recurseQuad( startX + quadSize, startY, quadSize, xOff, yOff ); recurseQuad( startX, startY + quadSize, quadSize, xOff, yOff ); recurseQuad( startX + quadSize, startY + quadSize, quadSize, xOff, yOff ); } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void setupQuad( long xOff, long yOff ) { long numQuadCels, i,x,y; byte *temp; if ( xOff == cin.oldXOff && yOff == cin.oldYOff && cinTable[currentHandle].ysize == cin.oldysize && cinTable[currentHandle].xsize == cin.oldxsize ) { return; } cin.oldXOff = xOff; cin.oldYOff = yOff; cin.oldysize = cinTable[currentHandle].ysize; cin.oldxsize = cinTable[currentHandle].xsize; numQuadCels = ( cinTable[currentHandle].CIN_WIDTH * cinTable[currentHandle].CIN_HEIGHT ) / ( 16 ); numQuadCels += numQuadCels / 4 + numQuadCels / 16; numQuadCels += 64; // for overflow numQuadCels = ( cinTable[currentHandle].xsize * cinTable[currentHandle].ysize ) / ( 16 ); numQuadCels += numQuadCels / 4; numQuadCels += 64; // for overflow cinTable[currentHandle].onQuad = 0; for ( y = 0; y < (long)cinTable[currentHandle].ysize; y += 16 ) for ( x = 0; x < (long)cinTable[currentHandle].xsize; x += 16 ) recurseQuad( x, y, 16, xOff, yOff ); temp = NULL; for ( i = ( numQuadCels - 64 ); i < numQuadCels; i++ ) { cin.qStatus[0][i] = temp; // eoq cin.qStatus[1][i] = temp; // eoq } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void readQuadInfo( byte *qData ) { if ( currentHandle < 0 ) { return; } cinTable[currentHandle].xsize = qData[0] + qData[1] * 256; cinTable[currentHandle].ysize = qData[2] + qData[3] * 256; cinTable[currentHandle].maxsize = qData[4] + qData[5] * 256; cinTable[currentHandle].minsize = qData[6] + qData[7] * 256; cinTable[currentHandle].CIN_HEIGHT = cinTable[currentHandle].ysize; cinTable[currentHandle].CIN_WIDTH = cinTable[currentHandle].xsize; cinTable[currentHandle].samplesPerLine = cinTable[currentHandle].CIN_WIDTH * cinTable[currentHandle].samplesPerPixel; cinTable[currentHandle].screenDelta = cinTable[currentHandle].CIN_HEIGHT * cinTable[currentHandle].samplesPerLine; cinTable[currentHandle].half = qfalse; cinTable[currentHandle].smootheddouble = qfalse; cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal; cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer; cinTable[currentHandle].t[0] = ( 0 - (unsigned int)cin.linbuf ) + (unsigned int)cin.linbuf + cinTable[currentHandle].screenDelta; cinTable[currentHandle].t[1] = ( 0 - ( (unsigned int)cin.linbuf + cinTable[currentHandle].screenDelta ) ) + (unsigned int)cin.linbuf; cinTable[currentHandle].drawX = cinTable[currentHandle].CIN_WIDTH; cinTable[currentHandle].drawY = cinTable[currentHandle].CIN_HEIGHT; // rage pro is very slow at 512 wide textures, voodoo can't do it at all if ( glConfig.hardwareType == GLHW_RAGEPRO || glConfig.maxTextureSize <= 256 ) { if ( cinTable[currentHandle].drawX > 256 ) { cinTable[currentHandle].drawX = 256; } if ( cinTable[currentHandle].drawY > 256 ) { cinTable[currentHandle].drawY = 256; } if ( cinTable[currentHandle].CIN_WIDTH != 256 || cinTable[currentHandle].CIN_HEIGHT != 256 ) { Com_Printf( "HACK: approxmimating cinematic for Rage Pro or Voodoo\n" ); } } //#ifdef __MACOS__ // cinTable[currentHandle].drawX = 256; // cinTable[currentHandle].drawX = 256; //#endif } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQPrepMcomp( long xoff, long yoff ) { long i, j, x, y, temp, temp2; i = cinTable[currentHandle].samplesPerLine; j = cinTable[currentHandle].samplesPerPixel; if ( cinTable[currentHandle].xsize == ( cinTable[currentHandle].ysize * 4 ) && !cinTable[currentHandle].half ) { j = j + j; i = i + i; } for ( y = 0; y < 16; y++ ) { temp2 = ( y + yoff - 8 ) * i; for ( x = 0; x < 16; x++ ) { temp = ( x + xoff - 8 ) * j; cin.mcomp[( x * 16 ) + y] = cinTable[currentHandle].normalBuffer0 - ( temp2 + temp ); } } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void initRoQ() { if ( currentHandle < 0 ) { return; } cinTable[currentHandle].VQNormal = ( void( * ) ( byte *, void * ) )blitVQQuad32fs; cinTable[currentHandle].VQBuffer = ( void( * ) ( byte *, void * ) )blitVQQuad32fs; cinTable[currentHandle].samplesPerPixel = 4; ROQ_GenYUVTables(); RllSetupTable(); } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ /* static byte* RoQFetchInterlaced( byte *source ) { int x, *src, *dst; if (currentHandle < 0) return NULL; src = (int *)source; dst = (int *)cinTable[currentHandle].buf2; for(x=0;x<256*256;x++) { *dst = *src; dst++; src += 2; } return cinTable[currentHandle].buf2; } */ static void RoQReset() { if ( currentHandle < 0 ) { return; } Sys_EndStreamedFile( cinTable[currentHandle].iFile ); // DHM - Properly close file so we don't run out of handles FS_FCloseFile( cinTable[currentHandle].iFile ); cinTable[currentHandle].iFile = 0; // dhm - end FS_FOpenFileRead( cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue ); // let the background thread start reading ahead Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 ); Sys_StreamedRead( cin.file, 16, 1, cinTable[currentHandle].iFile ); RoQ_init(); cinTable[currentHandle].status = FMV_LOOPED; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQInterrupt( void ) { byte *framedata; short sbuf[32768]; int ssize; if ( currentHandle < 0 ) { return; } Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize + 8, 1, cinTable[currentHandle].iFile ); if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { if ( cinTable[currentHandle].holdAtEnd == qfalse ) { if ( cinTable[currentHandle].looping ) { RoQReset(); } else { cinTable[currentHandle].status = FMV_EOF; } } else { cinTable[currentHandle].status = FMV_IDLE; } return; } framedata = cin.file; // // new frame is ready // redump: switch ( cinTable[currentHandle].roq_id ) { case ROQ_QUAD_VQ: if ( ( cinTable[currentHandle].numQuads & 1 ) ) { cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[1]; RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); cinTable[currentHandle].VQ1( (byte *)cin.qStatus[1], framedata ); cinTable[currentHandle].buf = cin.linbuf + cinTable[currentHandle].screenDelta; } else { cinTable[currentHandle].normalBuffer0 = cinTable[currentHandle].t[0]; RoQPrepMcomp( cinTable[currentHandle].roqF0, cinTable[currentHandle].roqF1 ); cinTable[currentHandle].VQ0( (byte *)cin.qStatus[0], framedata ); cinTable[currentHandle].buf = cin.linbuf; } if ( cinTable[currentHandle].numQuads == 0 ) { // first frame Com_Memcpy( cin.linbuf + cinTable[currentHandle].screenDelta, cin.linbuf, cinTable[currentHandle].samplesPerLine * cinTable[currentHandle].ysize ); } cinTable[currentHandle].numQuads++; cinTable[currentHandle].dirty = qtrue; break; case ROQ_CODEBOOK: decodeCodeBook( framedata, (unsigned short)cinTable[currentHandle].roq_flags ); break; case ZA_SOUND_MONO: if ( !cinTable[currentHandle].silent ) { ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags ); S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, 1.0f, 0 ); } break; case ZA_SOUND_STEREO: if ( !cinTable[currentHandle].silent ) { ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags ); S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, 1.0f, 0 ); } break; case ROQ_QUAD_INFO: if ( cinTable[currentHandle].numQuads == -1 ) { readQuadInfo( framedata ); setupQuad( 0, 0 ); // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds() * com_timescale->value; } if ( cinTable[currentHandle].numQuads != 1 ) { cinTable[currentHandle].numQuads = 0; } break; case ROQ_PACKET: cinTable[currentHandle].inMemory = cinTable[currentHandle].roq_flags; cinTable[currentHandle].RoQFrameSize = 0; // for header break; case ROQ_QUAD_HANG: cinTable[currentHandle].RoQFrameSize = 0; break; case ROQ_QUAD_JPEG: break; default: cinTable[currentHandle].status = FMV_EOF; break; } // // read in next frame data // if ( cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize ) { if ( cinTable[currentHandle].holdAtEnd == qfalse ) { if ( cinTable[currentHandle].looping ) { RoQReset(); } else { cinTable[currentHandle].status = FMV_EOF; } } else { cinTable[currentHandle].status = FMV_IDLE; } return; } framedata += cinTable[currentHandle].RoQFrameSize; cinTable[currentHandle].roq_id = framedata[0] + framedata[1] * 256; cinTable[currentHandle].RoQFrameSize = framedata[2] + framedata[3] * 256 + framedata[4] * 65536; cinTable[currentHandle].roq_flags = framedata[6] + framedata[7] * 256; cinTable[currentHandle].roqF0 = (char)framedata[7]; cinTable[currentHandle].roqF1 = (char)framedata[6]; if ( cinTable[currentHandle].RoQFrameSize > 65536 || cinTable[currentHandle].roq_id == 0x1084 ) { Com_DPrintf( "roq_size>65536||roq_id==0x1084\n" ); cinTable[currentHandle].status = FMV_EOF; if ( cinTable[currentHandle].looping ) { RoQReset(); } return; } if ( cinTable[currentHandle].inMemory && ( cinTable[currentHandle].status != FMV_EOF ) ) { cinTable[currentHandle].inMemory--; framedata += 8; goto redump; } // // one more frame hits the dust // // assert(cinTable[currentHandle].RoQFrameSize <= 65536); // r = Sys_StreamedRead( cin.file, cinTable[currentHandle].RoQFrameSize+8, 1, cinTable[currentHandle].iFile ); cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize + 8; } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQ_init( void ) { // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds() * com_timescale->value; cinTable[currentHandle].RoQPlayed = 24; /* get frame rate */ cinTable[currentHandle].roqFPS = cin.file[ 6] + cin.file[ 7] * 256; if ( !cinTable[currentHandle].roqFPS ) { cinTable[currentHandle].roqFPS = 30; } cinTable[currentHandle].numQuads = -1; cinTable[currentHandle].roq_id = cin.file[ 8] + cin.file[ 9] * 256; cinTable[currentHandle].RoQFrameSize = cin.file[10] + cin.file[11] * 256 + cin.file[12] * 65536; cinTable[currentHandle].roq_flags = cin.file[14] + cin.file[15] * 256; if ( cinTable[currentHandle].RoQFrameSize > 65536 || !cinTable[currentHandle].RoQFrameSize ) { return; } } /****************************************************************************** * * Function: * * Description: * ******************************************************************************/ static void RoQShutdown( void ) { const char *s; if ( !cinTable[currentHandle].buf ) { return; } if ( cinTable[currentHandle].status == FMV_IDLE ) { return; } Com_DPrintf( "finished cinematic\n" ); cinTable[currentHandle].status = FMV_IDLE; if ( cinTable[currentHandle].iFile ) { Sys_EndStreamedFile( cinTable[currentHandle].iFile ); FS_FCloseFile( cinTable[currentHandle].iFile ); cinTable[currentHandle].iFile = 0; } if ( cinTable[currentHandle].alterGameState ) { cls.state = CA_DISCONNECTED; // we can't just do a vstr nextmap, because // if we are aborting the intro cinematic with // a devmap command, nextmap would be valid by // the time it was referenced s = Cvar_VariableString( "nextmap" ); if ( s[0] ) { Cbuf_ExecuteText( EXEC_APPEND, va( "%s\n", s ) ); Cvar_Set( "nextmap", "" ); } CL_handle = -1; } cinTable[currentHandle].fileName[0] = 0; currentHandle = -1; } /* ================== SCR_StopCinematic ================== */ e_status CIN_StopCinematic( int handle ) { if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { return FMV_EOF; } currentHandle = handle; Com_DPrintf( "trFMV::stop(), closing %s\n", cinTable[currentHandle].fileName ); if ( !cinTable[currentHandle].buf ) { return FMV_EOF; } if ( cinTable[currentHandle].alterGameState ) { if ( cls.state != CA_CINEMATIC ) { return cinTable[currentHandle].status; } } cinTable[currentHandle].status = FMV_EOF; RoQShutdown(); return FMV_EOF; } /* ================== SCR_RunCinematic Fetch and decompress the pending frame ================== */ e_status CIN_RunCinematic( int handle ) { // bk001204 - init int start = 0; int thisTime = 0; if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { return FMV_EOF; } if ( cin.currentHandle != handle ) { currentHandle = handle; cin.currentHandle = currentHandle; cinTable[currentHandle].status = FMV_EOF; RoQReset(); } if ( cinTable[handle].playonwalls < -1 ) { return cinTable[handle].status; } currentHandle = handle; if ( cinTable[currentHandle].alterGameState ) { if ( cls.state != CA_CINEMATIC ) { return cinTable[currentHandle].status; } } if ( cinTable[currentHandle].status == FMV_IDLE ) { return cinTable[currentHandle].status; } // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer thisTime = CL_ScaledMilliseconds() * com_timescale->value; if ( cinTable[currentHandle].shader && ( abs( thisTime - cinTable[currentHandle].lastTime ) ) > 100 ) { cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; } // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].tfps = ( ( ( ( CL_ScaledMilliseconds() * com_timescale->value ) - cinTable[currentHandle].startTime ) * 3 ) / 100 ); start = cinTable[currentHandle].startTime; while ( ( cinTable[currentHandle].tfps != cinTable[currentHandle].numQuads ) && ( cinTable[currentHandle].status == FMV_PLAY ) ) { RoQInterrupt(); if ( start != cinTable[currentHandle].startTime ) { // we need to use CL_ScaledMilliseconds because of the smp mode calls from the renderer cinTable[currentHandle].tfps = ( ( ( ( CL_ScaledMilliseconds() * com_timescale->value ) - cinTable[currentHandle].startTime ) * 3 ) / 100 ); start = cinTable[currentHandle].startTime; } } cinTable[currentHandle].lastTime = thisTime; if ( cinTable[currentHandle].status == FMV_LOOPED ) { cinTable[currentHandle].status = FMV_PLAY; } if ( cinTable[currentHandle].status == FMV_EOF ) { if ( cinTable[currentHandle].looping ) { RoQReset(); } else { RoQShutdown(); } } return cinTable[currentHandle].status; } /* ================== CL_PlayCinematic ================== */ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBits ) { unsigned short RoQID; char name[MAX_OSPATH]; int i; if ( strstr( arg, "/" ) == NULL && strstr( arg, "\\" ) == NULL ) { Com_sprintf( name, sizeof( name ), "video/%s", arg ); } else { Com_sprintf( name, sizeof( name ), "%s", arg ); } if ( !( systemBits & CIN_system ) ) { for ( i = 0 ; i < MAX_VIDEO_HANDLES ; i++ ) { if ( !strcmp( cinTable[i].fileName, name ) ) { return i; } } } Com_DPrintf( "SCR_PlayCinematic( %s )\n", arg ); Com_Memset( &cin, 0, sizeof( cinematics_t ) ); currentHandle = CIN_HandleForVideo(); cin.currentHandle = currentHandle; strcpy( cinTable[currentHandle].fileName, name ); cinTable[currentHandle].ROQSize = 0; cinTable[currentHandle].ROQSize = FS_FOpenFileRead( cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, qtrue ); if ( cinTable[currentHandle].ROQSize <= 0 ) { Com_DPrintf( "play(%s), ROQSize<=0\n", arg ); cinTable[currentHandle].fileName[0] = 0; return -1; } CIN_SetExtents( currentHandle, x, y, w, h ); CIN_SetLooping( currentHandle, ( systemBits & CIN_loop ) != 0 ); cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; cinTable[currentHandle].holdAtEnd = ( systemBits & CIN_hold ) != 0; cinTable[currentHandle].alterGameState = ( systemBits & CIN_system ) != 0; cinTable[currentHandle].playonwalls = 1; cinTable[currentHandle].silent = ( systemBits & CIN_silent ) != 0; cinTable[currentHandle].shader = ( systemBits & CIN_shader ) != 0; if ( cinTable[currentHandle].alterGameState ) { // close the menu if ( uivm ) { VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); } } else { cinTable[currentHandle].playonwalls = cl_inGameVideo->integer; } initRoQ(); FS_Read( cin.file, 16, cinTable[currentHandle].iFile ); RoQID = ( unsigned short )( cin.file[0] ) + ( unsigned short )( cin.file[1] ) * 256; if ( RoQID == 0x1084 ) { RoQ_init(); // FS_Read (cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile); // let the background thread start reading ahead Sys_BeginStreamedFile( cinTable[currentHandle].iFile, 0x10000 ); cinTable[currentHandle].status = FMV_PLAY; Com_DPrintf( "trFMV::play(), playing %s\n", arg ); if ( cinTable[currentHandle].alterGameState ) { cls.state = CA_CINEMATIC; } Con_Close(); // s_rawend = s_soundtime; return currentHandle; } Com_DPrintf( "trFMV::play(), invalid RoQ ID\n" ); RoQShutdown(); return -1; } void CIN_SetExtents( int handle, int x, int y, int w, int h ) { if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { return; } cinTable[handle].xpos = x; cinTable[handle].ypos = y; cinTable[handle].width = w; cinTable[handle].height = h; cinTable[handle].dirty = qtrue; } void CIN_SetLooping( int handle, qboolean loop ) { if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { return; } cinTable[handle].looping = loop; } /* ================== SCR_DrawCinematic ================== */ void CIN_DrawCinematic( int handle ) { float x, y, w, h; byte *buf; if ( handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF ) { return; } if ( !cinTable[handle].buf ) { return; } x = cinTable[handle].xpos; y = cinTable[handle].ypos; w = cinTable[handle].width; h = cinTable[handle].height; buf = cinTable[handle].buf; SCR_AdjustFrom640( &x, &y, &w, &h ); if ( cinTable[handle].dirty && ( cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY ) ) { int ix, iy, *buf2, *buf3, xm, ym, ll; xm = cinTable[handle].CIN_WIDTH / 256; ym = cinTable[handle].CIN_HEIGHT / 256; ll = 8; if ( cinTable[handle].CIN_WIDTH == 512 ) { ll = 9; } buf3 = (int*)buf; buf2 = Hunk_AllocateTempMemory( 256 * 256 * 4 ); if ( xm == 2 && ym == 2 ) { byte *bc2, *bc3; int ic, iiy; bc2 = (byte *)buf2; bc3 = (byte *)buf3; for ( iy = 0; iy < 256; iy++ ) { iiy = iy << 12; for ( ix = 0; ix < 2048; ix += 8 ) { for ( ic = ix; ic < ( ix + 4 ); ic++ ) { *bc2 = ( bc3[iiy + ic] + bc3[iiy + 4 + ic] + bc3[iiy + 2048 + ic] + bc3[iiy + 2048 + 4 + ic] ) >> 2; bc2++; } } } } else if ( xm == 2 && ym == 1 ) { byte *bc2, *bc3; int ic, iiy; bc2 = (byte *)buf2; bc3 = (byte *)buf3; for ( iy = 0; iy < 256; iy++ ) { iiy = iy << 11; for ( ix = 0; ix < 2048; ix += 8 ) { for ( ic = ix; ic < ( ix + 4 ); ic++ ) { *bc2 = ( bc3[iiy + ic] + bc3[iiy + 4 + ic] ) >> 1; bc2++; } } } } else { for ( iy = 0; iy < 256; iy++ ) { for ( ix = 0; ix < 256; ix++ ) { buf2[( iy << 8 ) + ix] = buf3[( ( iy * ym ) << ll ) + ( ix * xm )]; } } } re.DrawStretchRaw( x, y, w, h, 256, 256, (byte *)buf2, handle, qtrue ); cinTable[handle].dirty = qfalse; Hunk_FreeTempMemory( buf2 ); return; } re.DrawStretchRaw( x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, buf, handle, cinTable[handle].dirty ); cinTable[handle].dirty = qfalse; } void CL_PlayCinematic_f( void ) { char *arg, *s; qboolean holdatend; int bits = CIN_system; Com_DPrintf( "CL_PlayCinematic_f\n" ); if ( cls.state == CA_CINEMATIC ) { SCR_StopCinematic(); } arg = Cmd_Argv( 1 ); s = Cmd_Argv( 2 ); holdatend = qfalse; if ( ( s && s[0] == '1' ) || Q_stricmp( arg,"demoend.roq" ) == 0 || Q_stricmp( arg,"end.roq" ) == 0 ) { bits |= CIN_hold; } if ( s && s[0] == '2' ) { bits |= CIN_loop; } S_StopAllSounds(); CL_handle = CIN_PlayCinematic( arg, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, bits ); if ( CL_handle >= 0 ) { do { SCR_RunCinematic(); } while ( cinTable[currentHandle].buf == NULL && cinTable[currentHandle].status == FMV_PLAY ); // wait for first frame (load codebook and sound) } } void SCR_DrawCinematic( void ) { if ( CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES ) { CIN_DrawCinematic( CL_handle ); } } void SCR_RunCinematic( void ) { if ( CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES ) { CIN_RunCinematic( CL_handle ); } } void SCR_StopCinematic( void ) { if ( CL_handle >= 0 && CL_handle < MAX_VIDEO_HANDLES ) { CIN_StopCinematic( CL_handle ); S_StopAllSounds(); CL_handle = -1; } } void CIN_UploadCinematic( int handle ) { if ( handle >= 0 && handle < MAX_VIDEO_HANDLES ) { if ( !cinTable[handle].buf ) { return; } if ( cinTable[handle].playonwalls <= 0 && cinTable[handle].dirty ) { if ( cinTable[handle].playonwalls == 0 ) { cinTable[handle].playonwalls = -1; } else { if ( cinTable[handle].playonwalls == -1 ) { cinTable[handle].playonwalls = -2; } else { cinTable[handle].dirty = qfalse; } } } re.UploadCinematic( 256, 256, 256, 256, cinTable[handle].buf, handle, cinTable[handle].dirty ); if ( cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1 ) { cinTable[handle].playonwalls--; } } }