/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. Copyright (C) 2013-2014 Robert Beckebans Copyright (C) 2014 Carl Kenner This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition 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. Doom 3 BFG Edition 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition 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 Doom 3 BFG Edition 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. =========================================================================== */ #pragma hdrstop #include "precompiled.h" extern idCVar s_noSound; #define JPEG_INTERNALS //extern "C" { #include "../libs/jpeg-6/jpeglib.h" //} #include "tr_local.h" #define CIN_system 1 #define CIN_loop 2 #define CIN_hold 4 #define CIN_silent 8 #define CIN_shader 16 #if defined(USE_FFMPEG) // Carl: ffmpg for bink video files extern "C" { //#ifdef WIN32 #ifndef INT64_C #define INT64_C(c) (c ## LL) #define UINT64_C(c) (c ## ULL) #endif #include //#endif #include #include #include } #endif class idCinematicLocal : public idCinematic { public: idCinematicLocal(); virtual ~idCinematicLocal(); virtual bool InitFromFile( const char* qpath, bool looping ); virtual cinData_t ImageForTime( int milliseconds ); virtual int AnimationLength(); virtual void Close(); virtual void ResetTime( int time ); private: #if defined(USE_FFMPEG) int video_stream_index; AVFormatContext* fmt_ctx; AVFrame* frame; AVFrame* frame2; AVCodec* dec; AVCodecContext* dec_ctx; SwsContext* img_convert_ctx; bool hasFrame; long FramePos; cinData_t ImageForTimeFFMPEG( int milliseconds ); bool InitFromFFMPEGFile( const char* qpath, bool looping ); void FFMPEGReset(); #endif idImage* img; bool isRoQ; // RB: 64 bit fixes, changed long to int size_t mcomp[256]; // RB end byte** qStatus[2]; idStr fileName; int CIN_WIDTH, CIN_HEIGHT; idFile* iFile; cinStatus_t status; // RB: 64 bit fixes, changed long to int int tfps; int RoQPlayed; int ROQSize; unsigned int RoQFrameSize; int onQuad; int numQuads; int samplesPerLine; unsigned int roq_id; int screenDelta; byte* buf; int samplesPerPixel; // defaults to 2 unsigned int xsize, ysize, maxsize, minsize; int normalBuffer0; int roq_flags; int roqF0; int roqF1; int t[2]; int roqFPS; int drawX, drawY; // RB end int animationLength; int startTime; float frameRate; byte* image; bool looping; bool dirty; bool half; bool smootheddouble; bool inMemory; void RoQ_init(); void blitVQQuad32fs( byte** status, unsigned char* data ); void RoQShutdown(); void RoQInterrupt(); void move8_32( byte* src, byte* dst, int spl ); void move4_32( byte* src, byte* dst, int spl ); void blit8_32( byte* src, byte* dst, int spl ); void blit4_32( byte* src, byte* dst, int spl ); void blit2_32( byte* src, byte* dst, int spl ); // RB: 64 bit fixes, changed long to int unsigned short yuv_to_rgb( int y, int u, int v ); unsigned int yuv_to_rgb24( int y, int u, int v ); void decodeCodeBook( byte* input, unsigned short roq_flags ); void recurseQuad( int startX, int startY, int quadSize, int xOff, int yOff ); void setupQuad( int xOff, int yOff ); void readQuadInfo( byte* qData ); void RoQPrepMcomp( int xoff, int yoff ); void RoQReset(); // RB end }; // Carl: ROQ files from original Doom 3 const int DEFAULT_CIN_WIDTH = 512; const int DEFAULT_CIN_HEIGHT = 512; const int MAXSIZE = 8; const int MINSIZE = 4; const int ROQ_FILE = 0x1084; const int ROQ_QUAD = 0x1000; const int ROQ_QUAD_INFO = 0x1001; const int ROQ_CODEBOOK = 0x1002; const int ROQ_QUAD_VQ = 0x1011; const int ROQ_QUAD_JPEG = 0x1012; const int ROQ_QUAD_HANG = 0x1013; const int ROQ_PACKET = 0x1030; const int ZA_SOUND_MONO = 0x1020; const int ZA_SOUND_STEREO = 0x1021; // temporary buffers used by all cinematics // RB: 64 bit fixes, changed long to int static int ROQ_YY_tab[256]; static int ROQ_UB_tab[256]; static int ROQ_UG_tab[256]; static int ROQ_VG_tab[256]; static int ROQ_VR_tab[256]; // RB end static byte* file = NULL; static unsigned short* vq2 = NULL; static unsigned short* vq4 = NULL; static unsigned short* vq8 = NULL; //=========================================== /* ============== idCinematicLocal::InitCinematic ============== */ // RB: 64 bit fixes, changed long to int void idCinematic::InitCinematic() { #if defined(USE_FFMPEG) // Carl: ffmpeg for Bink and regular video files common->Warning( "Loading FFMPEG...\n" ); avcodec_register_all(); av_register_all(); #endif // Carl: Doom 3 ROQ: float t_ub, t_vr, t_ug, t_vg; int i; // generate YUV tables 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] = ( int )( ( t_ub * x ) + ( 1 << 5 ) ); ROQ_VR_tab[i] = ( int )( ( t_vr * x ) + ( 1 << 5 ) ); ROQ_UG_tab[i] = ( int )( ( -t_ug * x ) ); ROQ_VG_tab[i] = ( int )( ( -t_vg * x ) + ( 1 << 5 ) ); ROQ_YY_tab[i] = ( int )( ( i << 6 ) | ( i >> 2 ) ); } file = ( byte* )Mem_Alloc( 65536, TAG_CINEMATIC ); vq2 = ( word* )Mem_Alloc( 256 * 16 * 4 * sizeof( word ), TAG_CINEMATIC ); vq4 = ( word* )Mem_Alloc( 256 * 64 * 4 * sizeof( word ), TAG_CINEMATIC ); vq8 = ( word* )Mem_Alloc( 256 * 256 * 4 * sizeof( word ), TAG_CINEMATIC ); } /* ============== idCinematicLocal::ShutdownCinematic ============== */ void idCinematic::ShutdownCinematic() { // Carl: Original Doom 3 RoQ files: Mem_Free( file ); file = NULL; Mem_Free( vq2 ); vq2 = NULL; Mem_Free( vq4 ); vq4 = NULL; Mem_Free( vq8 ); vq8 = NULL; } /* ============== idCinematic::Alloc ============== */ idCinematic* idCinematic::Alloc() { return new idCinematicLocal; //Carl: Use the proper class like in Doom 3, not just the unimplemented abstract one. } /* ============== idCinematic::~idCinematic ============== */ idCinematic::~idCinematic( ) { Close(); } /* ============== idCinematic::InitFromFile ============== */ bool idCinematic::InitFromFile( const char* qpath, bool looping ) { return false; //Carl: this is just the abstract virtual method } /* ============== idCinematic::AnimationLength ============== */ int idCinematic::AnimationLength() { return 0; } /* ============== idCinematic::GetStartTime ============== */ int idCinematic::GetStartTime() { return -1; } /* ============== idCinematic::ResetTime ============== */ void idCinematic::ResetTime( int milliseconds ) { } /* ============== idCinematic::ImageForTime ============== */ cinData_t idCinematic::ImageForTime( int milliseconds ) { cinData_t c; memset( &c, 0, sizeof( c ) ); declManager->FindMaterial( "doom64.tga" ); c.image = globalImages->GetImage( "doom64.tga" ); return c; } /* ============== idCinematic::ExportToTGA ============== */ void idCinematic::ExportToTGA( bool skipExisting ) { } /* ============== idCinematic::GetFrameRate ============== */ float idCinematic::GetFrameRate() const { return 30.0f; } /* ============== idCinematic::Close ============== */ void idCinematic::Close() { } //=========================================== /* ============== idCinematicLocal::idCinematicLocal ============== */ idCinematicLocal::idCinematicLocal() { qStatus[0] = ( byte** )Mem_Alloc( 32768 * sizeof( byte* ), TAG_CINEMATIC ); qStatus[1] = ( byte** )Mem_Alloc( 32768 * sizeof( byte* ), TAG_CINEMATIC ); #if defined(USE_FFMPEG) // Carl: ffmpeg stuff, for bink and normal video files: isRoQ = false; fmt_ctx = avformat_alloc_context(); frame = avcodec_alloc_frame(); frame2 = avcodec_alloc_frame(); dec_ctx = NULL; fmt_ctx = NULL; video_stream_index = -1; img_convert_ctx = NULL; hasFrame = false; #endif // Carl: Original Doom 3 RoQ files: image = NULL; status = FMV_EOF; buf = NULL; iFile = NULL; img = globalImages->AllocStandaloneImage( "_cinematic" ); if( img != NULL ) { idImageOpts opts; opts.format = FMT_RGBA8; opts.colorFormat = CFM_DEFAULT; opts.width = 32; opts.height = 32; opts.numLevels = 1; img->AllocImage( opts, TF_LINEAR, TR_REPEAT ); } } /* ============== idCinematicLocal::~idCinematicLocal ============== */ idCinematicLocal::~idCinematicLocal() { Close(); // Carl: Original Doom 3 RoQ files: Mem_Free( qStatus[0] ); qStatus[0] = NULL; Mem_Free( qStatus[1] ); qStatus[1] = NULL; #if defined(USE_FFMPEG) // Carl: ffmpeg for bink and other video files: // RB: TODO double check this. It seems we have different versions of ffmpeg on Kubuntu 13.10 and the win32 development files #if defined(_WIN32) || defined(_WIN64) avcodec_free_frame( &frame ); avcodec_free_frame( &frame2 ); #else av_freep( &frame ); av_freep( &frame2 ); #endif if( fmt_ctx ) { avformat_free_context( fmt_ctx ); } if( img_convert_ctx ) { sws_freeContext( img_convert_ctx ); } #endif delete img; img = NULL; } /* ============== idCinematicLocal::InitFromFFMPEGFile ============== */ #if defined(USE_FFMPEG) bool idCinematicLocal::InitFromFFMPEGFile( const char* qpath, bool amilooping ) { int ret; looping = amilooping; startTime = 0; isRoQ = false; CIN_HEIGHT = DEFAULT_CIN_HEIGHT; CIN_WIDTH = DEFAULT_CIN_WIDTH; idStr fullpath = fileSystem->RelativePathToOSPath( qpath, "fs_basepath" ); if( !fmt_ctx ) { return false; } if( ( ret = avformat_open_input( &fmt_ctx, fullpath, NULL, NULL ) ) < 0 ) { common->Warning( "idCinematic: Cannot open FFMPEG video file: '%s', %d\n", qpath, looping ); return false; } if( ( ret = avformat_find_stream_info( fmt_ctx, NULL ) ) < 0 ) { common->Warning( "idCinematic: Cannot find stream info: '%s', %d\n", qpath, looping ); return false; } /* select the video stream */ ret = av_find_best_stream( fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0 ); if( ret < 0 ) { common->Warning( "idCinematic: Cannot find a video stream in: '%s', %d\n", qpath, looping ); return false; } video_stream_index = ret; dec_ctx = fmt_ctx->streams[video_stream_index]->codec; /* init the video decoder */ if( ( ret = avcodec_open2( dec_ctx, dec, NULL ) ) < 0 ) { common->Warning( "idCinematic: Cannot open video decoder for: '%s', %d\n", qpath, looping ); return false; } common->Warning( "Loaded FFMPEG file: '%s', looping=%d\n", qpath, looping ); CIN_WIDTH = dec_ctx->width; CIN_HEIGHT = dec_ctx->height; /** Calculate Duration in seconds * This is the fundamental unit of time (in seconds) in terms * of which frame timestamps are represented. For fixed-fps content, * timebase should be 1/framerate and timestamp increments should be * identically 1. * - encoding: MUST be set by user. * - decoding: Set by libavcodec. */ AVRational avr = dec_ctx->time_base; /** * For some codecs, the time base is closer to the field rate than the frame rate. * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration * if no telecine is used ... * * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. */ int ticksPerFrame = dec_ctx->ticks_per_frame; float durationSec = static_cast( fmt_ctx->streams[video_stream_index]->duration ) * static_cast( ticksPerFrame ) / static_cast( avr.den ); animationLength = durationSec * 1000; frameRate = av_q2d( fmt_ctx->streams[video_stream_index]->r_frame_rate ); startTime = 0; buf = NULL; hasFrame = false; FramePos = -1; common->Warning( "%dx%d, %f FPS, %f sec", CIN_WIDTH, CIN_HEIGHT, frameRate, durationSec ); image = ( byte* )Mem_Alloc( CIN_WIDTH * CIN_HEIGHT * 4 * 2, TAG_CINEMATIC ); avpicture_fill( ( AVPicture* )frame2, image, PIX_FMT_BGR32, CIN_WIDTH, CIN_HEIGHT ); if( img_convert_ctx ) sws_freeContext( img_convert_ctx ); img_convert_ctx = sws_getContext( dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, CIN_WIDTH, CIN_HEIGHT, PIX_FMT_BGR32, SWS_BICUBIC, NULL, NULL, NULL ); status = FMV_PLAY; ImageForTime( 0 ); status = ( looping ) ? FMV_PLAY : FMV_IDLE; return true; } #endif /* ============== idCinematicLocal::FFMPEGReset ============== */ #if defined(USE_FFMPEG) void idCinematicLocal::FFMPEGReset() { startTime = 0; FramePos = -1; av_seek_frame( fmt_ctx, video_stream_index, 0, 0 ); status = FMV_LOOPED; } #endif /* ============== idCinematicLocal::InitFromFile ============== */ bool idCinematicLocal::InitFromFile( const char* qpath, bool amilooping ) { unsigned short RoQID; Close(); inMemory = 0; animationLength = 15000; //Carl: We can't tell how long an RoQ file is, so say it's 15 seconds // Carl: if no folder is specified, look in the video folder if( strstr( qpath, "/" ) == NULL && strstr( qpath, "\\" ) == NULL ) { sprintf( fileName, "video/%s", qpath ); } else { sprintf( fileName, "%s", qpath ); } // Carl: Look for original Doom 3 RoQ files first: idStr ext; fileName.ExtractFileExtension( ext ); fileName = fileName.StripFileExtension(); fileName = fileName + ".roq"; //if (fileName == "video\\loadvideo.roq") { // fileName = "video\\idlogo.roq"; //} iFile = fileSystem->OpenFileRead( fileName ); // Carl: If the RoQ file doesn't exist, try using ffmpeg instead: if( !iFile ) { #if defined(USE_FFMPEG) idLib::Warning( "Original Doom 3 RoQ Cinematic not found: '%s'\n", fileName.c_str() ); idStr temp = fileName.StripFileExtension() + ".bik"; animationLength = 0; hasFrame = false; RoQShutdown(); fileName = temp; idLib::Warning( "New filename: '%s'\n", fileName.c_str() ); return InitFromFFMPEGFile( fileName.c_str(), amilooping ); #else return false; #endif } // Carl: The rest of this function is for original Doom 3 RoQ files: isRoQ = true; ROQSize = iFile->Length(); looping = amilooping; CIN_HEIGHT = DEFAULT_CIN_HEIGHT; CIN_WIDTH = DEFAULT_CIN_WIDTH; samplesPerPixel = 4; startTime = 0; //Sys_Milliseconds(); buf = NULL; iFile->Read( file, 16 ); RoQID = ( unsigned short )( file[0] ) + ( unsigned short )( file[1] ) * 256; frameRate = file[6]; if( frameRate == 32.0f ) { frameRate = 1000.0f / 32.0f; } if( RoQID == ROQ_FILE ) { RoQ_init(); status = FMV_PLAY; ImageForTime( 0 ); status = ( looping ) ? FMV_PLAY : FMV_IDLE; return true; } RoQShutdown(); return false; } /* ============== idCinematicLocal::Close ============== */ void idCinematicLocal::Close() { if( image ) { Mem_Free( ( void* )image ); image = NULL; buf = NULL; status = FMV_EOF; } RoQShutdown(); #if defined(USE_FFMPEG) hasFrame = false; if( !isRoQ ) { if( img_convert_ctx ) sws_freeContext( img_convert_ctx ); img_convert_ctx = NULL; if( dec_ctx ) avcodec_close( dec_ctx ); avformat_close_input( &fmt_ctx ); status = FMV_EOF; } #endif } /* ============== idCinematicLocal::AnimationLength ============== */ int idCinematicLocal::AnimationLength() { return animationLength; } /* ============== idCinematicLocal::ResetTime ============== */ void idCinematicLocal::ResetTime( int time ) { startTime = time; //originally this was: ( backEnd.viewDef ) ? 1000 * backEnd.viewDef->floatTime : -1; status = FMV_PLAY; } /* ============== idCinematicLocal::ImageForTime ============== */ cinData_t idCinematicLocal::ImageForTime( int thisTime ) { #if defined(USE_FFMPEG) // Carl: Handle BFG format BINK videos separately if( !isRoQ ) return ImageForTimeFFMPEG( thisTime ); #endif // Carl: Handle original Doom 3 RoQ video files cinData_t cinData; if( thisTime == 0 ) { thisTime = Sys_Milliseconds(); } if( thisTime < 0 ) { thisTime = 0; } memset( &cinData, 0, sizeof( cinData ) ); // if ( r_skipROQ.GetBool() ) { if( r_skipDynamicTextures.GetBool() ) { return cinData; } if( status == FMV_EOF || status == FMV_IDLE ) { return cinData; } if( buf == NULL || startTime == -1 ) { if( startTime == -1 ) { RoQReset(); } startTime = thisTime; } tfps = ( ( thisTime - startTime ) * frameRate ) / 1000; if( tfps < 0 ) { tfps = 0; } if( tfps < numQuads ) { RoQReset(); buf = NULL; status = FMV_PLAY; } if( buf == NULL ) { while( buf == NULL ) { RoQInterrupt(); } } else { while( ( tfps != numQuads && status == FMV_PLAY ) ) { RoQInterrupt(); } } if( status == FMV_LOOPED ) { status = FMV_PLAY; while( buf == NULL && status == FMV_PLAY ) { RoQInterrupt(); } startTime = thisTime; } if( status == FMV_EOF ) { if( looping ) { RoQReset(); buf = NULL; if( status == FMV_LOOPED ) { status = FMV_PLAY; } while( buf == NULL && status == FMV_PLAY ) { RoQInterrupt(); } startTime = thisTime; } else { status = FMV_IDLE; RoQShutdown(); } } cinData.imageWidth = CIN_WIDTH; cinData.imageHeight = CIN_HEIGHT; cinData.status = status; img->UploadScratch( image, CIN_WIDTH, CIN_HEIGHT ); cinData.image = img; return cinData; } /* ============== idCinematicLocal::ImageForTimeFFMPEG ============== */ #if defined(USE_FFMPEG) cinData_t idCinematicLocal::ImageForTimeFFMPEG( int thisTime ) { cinData_t cinData; if( thisTime <= 0 ) { thisTime = Sys_Milliseconds(); } memset( &cinData, 0, sizeof( cinData ) ); if( r_skipDynamicTextures.GetBool() || status == FMV_EOF || status == FMV_IDLE ) { return cinData; } if( ( !hasFrame ) || startTime == -1 ) { if( startTime == -1 ) { FFMPEGReset(); } startTime = thisTime; } long DesiredFrame = ( ( thisTime - startTime ) * frameRate ) / 1000; if( DesiredFrame < 0 ) DesiredFrame = 0; if( DesiredFrame < FramePos ) FFMPEGReset(); if( hasFrame && DesiredFrame == FramePos ) { cinData.imageWidth = CIN_WIDTH; cinData.imageHeight = CIN_HEIGHT; cinData.status = status; cinData.image = img; return cinData; } AVPacket packet; while( FramePos < DesiredFrame ) { int frameFinished = 0; //Do a single frame by getting packets until we have a full frame while( !frameFinished ) { //if we got to the end or failed if( av_read_frame( fmt_ctx, &packet ) < 0 ) { //can't read any more, set to EOF status = FMV_EOF; if( looping ) { DesiredFrame = 0; FFMPEGReset(); FramePos = -1; startTime = thisTime; if( av_read_frame( fmt_ctx, &packet ) < 0 ) { status = FMV_IDLE; return cinData; } status = FMV_PLAY; } else { status = FMV_IDLE; return cinData; } } // Is this a packet from the video stream? if( packet.stream_index == video_stream_index ) { // Decode video frame avcodec_decode_video2( dec_ctx, frame, &frameFinished, &packet ); } // Free the packet that was allocated by av_read_frame av_free_packet( &packet ); } FramePos++; } // We have reached the desired frame // Convert the image from its native format to RGB sws_scale( img_convert_ctx, frame->data, frame->linesize, 0, dec_ctx->height, frame2->data, frame2->linesize ); cinData.imageWidth = CIN_WIDTH; cinData.imageHeight = CIN_HEIGHT; cinData.status = status; img->UploadScratch( image, CIN_WIDTH, CIN_HEIGHT ); hasFrame = true; cinData.image = img; return cinData; } #endif /* ============== idCinematicLocal::move8_32 ============== */ void idCinematicLocal::move8_32( byte* src, byte* dst, int spl ) { #if 1 int* dsrc, *ddst; int dspl; dsrc = ( int* )src; ddst = ( int* )dst; dspl = spl >> 2; ddst[0 * dspl + 0] = dsrc[0 * dspl + 0]; ddst[0 * dspl + 1] = dsrc[0 * dspl + 1]; ddst[0 * dspl + 2] = dsrc[0 * dspl + 2]; ddst[0 * dspl + 3] = dsrc[0 * dspl + 3]; ddst[0 * dspl + 4] = dsrc[0 * dspl + 4]; ddst[0 * dspl + 5] = dsrc[0 * dspl + 5]; ddst[0 * dspl + 6] = dsrc[0 * dspl + 6]; ddst[0 * dspl + 7] = dsrc[0 * dspl + 7]; ddst[1 * dspl + 0] = dsrc[1 * dspl + 0]; ddst[1 * dspl + 1] = dsrc[1 * dspl + 1]; ddst[1 * dspl + 2] = dsrc[1 * dspl + 2]; ddst[1 * dspl + 3] = dsrc[1 * dspl + 3]; ddst[1 * dspl + 4] = dsrc[1 * dspl + 4]; ddst[1 * dspl + 5] = dsrc[1 * dspl + 5]; ddst[1 * dspl + 6] = dsrc[1 * dspl + 6]; ddst[1 * dspl + 7] = dsrc[1 * dspl + 7]; ddst[2 * dspl + 0] = dsrc[2 * dspl + 0]; ddst[2 * dspl + 1] = dsrc[2 * dspl + 1]; ddst[2 * dspl + 2] = dsrc[2 * dspl + 2]; ddst[2 * dspl + 3] = dsrc[2 * dspl + 3]; ddst[2 * dspl + 4] = dsrc[2 * dspl + 4]; ddst[2 * dspl + 5] = dsrc[2 * dspl + 5]; ddst[2 * dspl + 6] = dsrc[2 * dspl + 6]; ddst[2 * dspl + 7] = dsrc[2 * dspl + 7]; ddst[3 * dspl + 0] = dsrc[3 * dspl + 0]; ddst[3 * dspl + 1] = dsrc[3 * dspl + 1]; ddst[3 * dspl + 2] = dsrc[3 * dspl + 2]; ddst[3 * dspl + 3] = dsrc[3 * dspl + 3]; ddst[3 * dspl + 4] = dsrc[3 * dspl + 4]; ddst[3 * dspl + 5] = dsrc[3 * dspl + 5]; ddst[3 * dspl + 6] = dsrc[3 * dspl + 6]; ddst[3 * dspl + 7] = dsrc[3 * dspl + 7]; ddst[4 * dspl + 0] = dsrc[4 * dspl + 0]; ddst[4 * dspl + 1] = dsrc[4 * dspl + 1]; ddst[4 * dspl + 2] = dsrc[4 * dspl + 2]; ddst[4 * dspl + 3] = dsrc[4 * dspl + 3]; ddst[4 * dspl + 4] = dsrc[4 * dspl + 4]; ddst[4 * dspl + 5] = dsrc[4 * dspl + 5]; ddst[4 * dspl + 6] = dsrc[4 * dspl + 6]; ddst[4 * dspl + 7] = dsrc[4 * dspl + 7]; ddst[5 * dspl + 0] = dsrc[5 * dspl + 0]; ddst[5 * dspl + 1] = dsrc[5 * dspl + 1]; ddst[5 * dspl + 2] = dsrc[5 * dspl + 2]; ddst[5 * dspl + 3] = dsrc[5 * dspl + 3]; ddst[5 * dspl + 4] = dsrc[5 * dspl + 4]; ddst[5 * dspl + 5] = dsrc[5 * dspl + 5]; ddst[5 * dspl + 6] = dsrc[5 * dspl + 6]; ddst[5 * dspl + 7] = dsrc[5 * dspl + 7]; ddst[6 * dspl + 0] = dsrc[6 * dspl + 0]; ddst[6 * dspl + 1] = dsrc[6 * dspl + 1]; ddst[6 * dspl + 2] = dsrc[6 * dspl + 2]; ddst[6 * dspl + 3] = dsrc[6 * dspl + 3]; ddst[6 * dspl + 4] = dsrc[6 * dspl + 4]; ddst[6 * dspl + 5] = dsrc[6 * dspl + 5]; ddst[6 * dspl + 6] = dsrc[6 * dspl + 6]; ddst[6 * dspl + 7] = dsrc[6 * dspl + 7]; ddst[7 * dspl + 0] = dsrc[7 * dspl + 0]; ddst[7 * dspl + 1] = dsrc[7 * dspl + 1]; ddst[7 * dspl + 2] = dsrc[7 * dspl + 2]; ddst[7 * dspl + 3] = dsrc[7 * dspl + 3]; ddst[7 * dspl + 4] = dsrc[7 * dspl + 4]; ddst[7 * dspl + 5] = dsrc[7 * dspl + 5]; ddst[7 * dspl + 6] = dsrc[7 * dspl + 6]; ddst[7 * dspl + 7] = dsrc[7 * dspl + 7]; #else 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]; #endif } /* ============== idCinematicLocal::move4_32 ============== */ void idCinematicLocal::move4_32( byte* src, byte* dst, int spl ) { #if 1 int* dsrc, *ddst; int dspl; dsrc = ( int* )src; ddst = ( int* )dst; dspl = spl >> 2; ddst[0 * dspl + 0] = dsrc[0 * dspl + 0]; ddst[0 * dspl + 1] = dsrc[0 * dspl + 1]; ddst[0 * dspl + 2] = dsrc[0 * dspl + 2]; ddst[0 * dspl + 3] = dsrc[0 * dspl + 3]; ddst[1 * dspl + 0] = dsrc[1 * dspl + 0]; ddst[1 * dspl + 1] = dsrc[1 * dspl + 1]; ddst[1 * dspl + 2] = dsrc[1 * dspl + 2]; ddst[1 * dspl + 3] = dsrc[1 * dspl + 3]; ddst[2 * dspl + 0] = dsrc[2 * dspl + 0]; ddst[2 * dspl + 1] = dsrc[2 * dspl + 1]; ddst[2 * dspl + 2] = dsrc[2 * dspl + 2]; ddst[2 * dspl + 3] = dsrc[2 * dspl + 3]; ddst[3 * dspl + 0] = dsrc[3 * dspl + 0]; ddst[3 * dspl + 1] = dsrc[3 * dspl + 1]; ddst[3 * dspl + 2] = dsrc[3 * dspl + 2]; ddst[3 * dspl + 3] = dsrc[3 * dspl + 3]; #else 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]; #endif } /* ============== idCinematicLocal::blit8_32 ============== */ void idCinematicLocal::blit8_32( byte* src, byte* dst, int spl ) { #if 1 int* dsrc, *ddst; int dspl; dsrc = ( int* )src; ddst = ( int* )dst; dspl = spl >> 2; ddst[0 * dspl + 0] = dsrc[ 0]; ddst[0 * dspl + 1] = dsrc[ 1]; ddst[0 * dspl + 2] = dsrc[ 2]; ddst[0 * dspl + 3] = dsrc[ 3]; ddst[0 * dspl + 4] = dsrc[ 4]; ddst[0 * dspl + 5] = dsrc[ 5]; ddst[0 * dspl + 6] = dsrc[ 6]; ddst[0 * dspl + 7] = dsrc[ 7]; ddst[1 * dspl + 0] = dsrc[ 8]; ddst[1 * dspl + 1] = dsrc[ 9]; ddst[1 * dspl + 2] = dsrc[10]; ddst[1 * dspl + 3] = dsrc[11]; ddst[1 * dspl + 4] = dsrc[12]; ddst[1 * dspl + 5] = dsrc[13]; ddst[1 * dspl + 6] = dsrc[14]; ddst[1 * dspl + 7] = dsrc[15]; ddst[2 * dspl + 0] = dsrc[16]; ddst[2 * dspl + 1] = dsrc[17]; ddst[2 * dspl + 2] = dsrc[18]; ddst[2 * dspl + 3] = dsrc[19]; ddst[2 * dspl + 4] = dsrc[20]; ddst[2 * dspl + 5] = dsrc[21]; ddst[2 * dspl + 6] = dsrc[22]; ddst[2 * dspl + 7] = dsrc[23]; ddst[3 * dspl + 0] = dsrc[24]; ddst[3 * dspl + 1] = dsrc[25]; ddst[3 * dspl + 2] = dsrc[26]; ddst[3 * dspl + 3] = dsrc[27]; ddst[3 * dspl + 4] = dsrc[28]; ddst[3 * dspl + 5] = dsrc[29]; ddst[3 * dspl + 6] = dsrc[30]; ddst[3 * dspl + 7] = dsrc[31]; ddst[4 * dspl + 0] = dsrc[32]; ddst[4 * dspl + 1] = dsrc[33]; ddst[4 * dspl + 2] = dsrc[34]; ddst[4 * dspl + 3] = dsrc[35]; ddst[4 * dspl + 4] = dsrc[36]; ddst[4 * dspl + 5] = dsrc[37]; ddst[4 * dspl + 6] = dsrc[38]; ddst[4 * dspl + 7] = dsrc[39]; ddst[5 * dspl + 0] = dsrc[40]; ddst[5 * dspl + 1] = dsrc[41]; ddst[5 * dspl + 2] = dsrc[42]; ddst[5 * dspl + 3] = dsrc[43]; ddst[5 * dspl + 4] = dsrc[44]; ddst[5 * dspl + 5] = dsrc[45]; ddst[5 * dspl + 6] = dsrc[46]; ddst[5 * dspl + 7] = dsrc[47]; ddst[6 * dspl + 0] = dsrc[48]; ddst[6 * dspl + 1] = dsrc[49]; ddst[6 * dspl + 2] = dsrc[50]; ddst[6 * dspl + 3] = dsrc[51]; ddst[6 * dspl + 4] = dsrc[52]; ddst[6 * dspl + 5] = dsrc[53]; ddst[6 * dspl + 6] = dsrc[54]; ddst[6 * dspl + 7] = dsrc[55]; ddst[7 * dspl + 0] = dsrc[56]; ddst[7 * dspl + 1] = dsrc[57]; ddst[7 * dspl + 2] = dsrc[58]; ddst[7 * dspl + 3] = dsrc[59]; ddst[7 * dspl + 4] = dsrc[60]; ddst[7 * dspl + 5] = dsrc[61]; ddst[7 * dspl + 6] = dsrc[62]; ddst[7 * dspl + 7] = dsrc[63]; #else 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]; #endif } /* ============== idCinematicLocal::blit4_32 ============== */ void idCinematicLocal::blit4_32( byte* src, byte* dst, int spl ) { #if 1 int* dsrc, *ddst; int dspl; dsrc = ( int* )src; ddst = ( int* )dst; dspl = spl >> 2; ddst[0 * dspl + 0] = dsrc[ 0]; ddst[0 * dspl + 1] = dsrc[ 1]; ddst[0 * dspl + 2] = dsrc[ 2]; ddst[0 * dspl + 3] = dsrc[ 3]; ddst[1 * dspl + 0] = dsrc[ 4]; ddst[1 * dspl + 1] = dsrc[ 5]; ddst[1 * dspl + 2] = dsrc[ 6]; ddst[1 * dspl + 3] = dsrc[ 7]; ddst[2 * dspl + 0] = dsrc[ 8]; ddst[2 * dspl + 1] = dsrc[ 9]; ddst[2 * dspl + 2] = dsrc[10]; ddst[2 * dspl + 3] = dsrc[11]; ddst[3 * dspl + 0] = dsrc[12]; ddst[3 * dspl + 1] = dsrc[13]; ddst[3 * dspl + 2] = dsrc[14]; ddst[3 * dspl + 3] = dsrc[15]; #else double* dsrc, *ddst; int dspl; dsrc = ( double* )src; ddst = ( double* )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]; #endif } /* ============== idCinematicLocal::blit2_32 ============== */ void idCinematicLocal::blit2_32( byte* src, byte* dst, int spl ) { #if 1 int* dsrc, *ddst; int dspl; dsrc = ( int* )src; ddst = ( int* )dst; dspl = spl >> 2; ddst[0 * dspl + 0] = dsrc[0]; ddst[0 * dspl + 1] = dsrc[1]; ddst[1 * dspl + 0] = dsrc[2]; ddst[1 * dspl + 1] = dsrc[3]; #else double* dsrc, *ddst; int dspl; dsrc = ( double* )src; ddst = ( double* )dst; dspl = spl >> 3; ddst[0] = dsrc[0]; ddst[dspl] = dsrc[1]; #endif } /* ============== idCinematicLocal::blitVQQuad32fs ============== */ void idCinematicLocal::blitVQQuad32fs( byte** status, unsigned char* data ) { unsigned short newd, celdata, code; unsigned int index, i; newd = 0; celdata = 0; index = 0; 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], samplesPerLine ); 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], samplesPerLine ); data++; break; case 0xc000: // 2x2 vq code blit2_32( ( byte* )&vq2[( *data ) * 8], status[index], samplesPerLine ); data++; blit2_32( ( byte* )&vq2[( *data ) * 8], status[index] + 8, samplesPerLine ); data++; blit2_32( ( byte* )&vq2[( *data ) * 8], status[index] + samplesPerLine * 2, samplesPerLine ); data++; blit2_32( ( byte* )&vq2[( *data ) * 8], status[index] + samplesPerLine * 2 + 8, samplesPerLine ); data++; break; case 0x4000: // motion compensation move4_32( status[index] + mcomp[( *data )], status[index], samplesPerLine ); data++; break; } index++; } break; case 0x4000: // motion compensation move8_32( status[index] + mcomp[( *data )], status[index], samplesPerLine ); data++; index += 5; break; case 0x0000: index += 5; break; } } while( status[index] != NULL ); } #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++; } /* ============== idCinematicLocal::yuv_to_rgb ============== */ // RB: 64 bit fixes, changed long to int unsigned short idCinematicLocal::yuv_to_rgb( int y, int u, int v ) { int r, g, b, YY = ( int )( 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 ) ); } // RB end /* ============== idCinematicLocal::yuv_to_rgb24 ============== */ // RB: 64 bit fixes, changed long to int unsigned int idCinematicLocal::yuv_to_rgb24( int y, int u, int v ) { int r, g, b, YY = ( int )( 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 ) ); } // RB end /* ============== idCinematicLocal::decodeCodeBook ============== */ // RB: 64 bit fixes, changed long to int void idCinematicLocal::decodeCodeBook( byte* input, unsigned short roq_flags ) { int i, j, two, four; unsigned short* aptr, *bptr, *cptr, *dptr; int y0, y1, y2, y3, cr, cb; 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( !half ) { if( !smootheddouble ) { // // normal height // if( samplesPerPixel == 2 ) { for( i = 0; i < two; i++ ) { y0 = ( int ) * input++; y1 = ( int ) * input++; y2 = ( int ) * input++; y3 = ( int ) * input++; cr = ( int ) * input++; cb = ( int ) * 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( samplesPerPixel == 4 ) { ibptr = ( unsigned int* )bptr; for( i = 0; i < two; i++ ) { y0 = ( int ) * input++; y1 = ( int ) * input++; y2 = ( int ) * input++; y3 = ( int ) * input++; cr = ( int ) * input++; cb = ( int ) * 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 { // // double height, smoothed // if( samplesPerPixel == 2 ) { for( i = 0; i < two; i++ ) { y0 = ( int ) * input++; y1 = ( int ) * input++; y2 = ( int ) * input++; y3 = ( int ) * input++; cr = ( int ) * input++; cb = ( int ) * 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( samplesPerPixel == 4 ) { ibptr = ( unsigned int* )bptr; for( i = 0; i < two; i++ ) { y0 = ( int ) * input++; y1 = ( int ) * input++; y2 = ( int ) * input++; y3 = ( int ) * input++; cr = ( int ) * input++; cb = ( int ) * 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 { // // 1/4 screen // if( samplesPerPixel == 2 ) { for( i = 0; i < two; i++ ) { y0 = ( int ) * input; input += 2; y2 = ( int ) * input; input += 2; cr = ( int ) * input++; cb = ( int ) * 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( samplesPerPixel == 4 ) { ibptr = ( unsigned int* ) bptr; for( i = 0; i < two; i++ ) { y0 = ( int ) * input; input += 2; y2 = ( int ) * input; input += 2; cr = ( int ) * input++; cb = ( int ) * 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 ); } } } } } // RB end /* ============== idCinematicLocal::recurseQuad ============== */ // RB: 64 bit fixes, changed long to int void idCinematicLocal::recurseQuad( int startX, int startY, int quadSize, int xOff, int yOff ) { byte* scroff; int bigx, bigy, lowx, lowy, useY; int offset; offset = screenDelta; lowx = lowy = 0; bigx = xsize; bigy = ysize; if( bigx > CIN_WIDTH ) bigx = CIN_WIDTH; if( bigy > CIN_HEIGHT ) bigy = CIN_HEIGHT; if( ( startX >= lowx ) && ( startX + quadSize ) <= ( bigx ) && ( startY + quadSize ) <= ( bigy ) && ( startY >= lowy ) && quadSize <= MAXSIZE ) { useY = startY; scroff = image + ( useY + ( ( CIN_HEIGHT - bigy ) >> 1 ) + yOff ) * ( samplesPerLine ) + ( ( ( startX + xOff ) ) * samplesPerPixel ); qStatus[0][onQuad ] = scroff; qStatus[1][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 ); } } // RB end /* ============== idCinematicLocal::setupQuad ============== */ // RB: 64 bit fixes, changed long to int void idCinematicLocal::setupQuad( int xOff, int yOff ) { int numQuadCels, i, x, y; byte* temp; numQuadCels = ( CIN_WIDTH * CIN_HEIGHT ) / ( 16 ); numQuadCels += numQuadCels / 4 + numQuadCels / 16; numQuadCels += 64; // for overflow numQuadCels = ( xsize * ysize ) / ( 16 ); numQuadCels += numQuadCels / 4; numQuadCels += 64; // for overflow onQuad = 0; for( y = 0; y < ( int )ysize; y += 16 ) for( x = 0; x < ( int )xsize; x += 16 ) recurseQuad( x, y, 16, xOff, yOff ); temp = NULL; for( i = ( numQuadCels - 64 ); i < numQuadCels; i++ ) { qStatus[0][i] = temp; // eoq qStatus[1][i] = temp; // eoq } } // RB end /* ============== idCinematicLocal::readQuadInfo ============== */ void idCinematicLocal::readQuadInfo( byte* qData ) { xsize = qData[0] + qData[1] * 256; ysize = qData[2] + qData[3] * 256; maxsize = qData[4] + qData[5] * 256; minsize = qData[6] + qData[7] * 256; CIN_HEIGHT = ysize; CIN_WIDTH = xsize; samplesPerLine = CIN_WIDTH * samplesPerPixel; screenDelta = CIN_HEIGHT * samplesPerLine; if( !image ) { image = ( byte* )Mem_Alloc( CIN_WIDTH * CIN_HEIGHT * samplesPerPixel * 2, TAG_CINEMATIC ); } half = false; smootheddouble = false; // RB: 64 bit fixes, changed unsigned int to ptrdiff_t t[0] = ( 0 - ( ptrdiff_t )image ) + ( ptrdiff_t )image + screenDelta; t[1] = ( 0 - ( ( ptrdiff_t )image + screenDelta ) ) + ( ptrdiff_t )image; // RB end drawX = CIN_WIDTH; drawY = CIN_HEIGHT; } /* ============== idCinematicLocal::RoQPrepMcomp ============== */ // RB: 64 bit fixes, changed long to int void idCinematicLocal::RoQPrepMcomp( int xoff, int yoff ) { int i, j, x, y, temp, temp2; i = samplesPerLine; j = samplesPerPixel; if( xsize == ( ysize * 4 ) && !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; mcomp[( x * 16 ) + y] = normalBuffer0 - ( temp2 + temp ); } } } // RB end /* ============== idCinematicLocal::RoQReset ============== */ void idCinematicLocal::RoQReset() { iFile->Seek( 0, FS_SEEK_SET ); iFile->Read( file, 16 ); RoQ_init(); status = FMV_LOOPED; } typedef struct { struct jpeg_source_mgr pub; /* public fields */ byte* infile; /* source stream */ JOCTET* buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ int memsize; } my_source_mgr; typedef my_source_mgr* my_src_ptr; #define INPUT_BUF_SIZE 32768 /* choose an efficiently fread'able size */ /* jpeg error handling */ struct jpeg_error_mgr jerr; /* * Fill the input buffer --- called whenever buffer is emptied. * * In typical applications, this should read fresh data into the buffer * (ignoring the current state of next_input_byte & bytes_in_buffer), * reset the pointer & count to the start of the buffer, and return TRUE * indicating that the buffer has been reloaded. It is not necessary to * fill the buffer entirely, only to obtain at least one more byte. * * There is no such thing as an EOF return. If the end of the file has been * reached, the routine has a choice of ERREXIT() or inserting fake data into * the buffer. In most cases, generating a warning message and inserting a * fake EOI marker is the best course of action --- this will allow the * decompressor to output however much of the image is there. However, * the resulting error message is misleading if the real problem is an empty * input file, so we handle that case specially. * * In applications that need to be able to suspend compression due to input * not being available yet, a FALSE return indicates that no more data can be * obtained right now, but more may be forthcoming later. In this situation, * the decompressor will return to its caller (with an indication of the * number of scanlines it has read, if any). The application should resume * decompression after it has loaded more data into the input buffer. Note * that there are substantial restrictions on the use of suspension --- see * the documentation. * * When suspending, the decompressor will back up to a convenient restart point * (typically the start of the current MCU). next_input_byte & bytes_in_buffer * indicate where the restart point will be if the current call returns FALSE. * Data beyond this point must be rescanned after resumption, so move it to * the front of the buffer rather than discarding it. */ METHODDEF boolean fill_input_buffer( j_decompress_ptr cinfo ) { my_src_ptr src = ( my_src_ptr ) cinfo->src; int nbytes; nbytes = INPUT_BUF_SIZE; if( nbytes > src->memsize ) nbytes = src->memsize; if( nbytes == 0 ) { /* Insert a fake EOI marker */ src->buffer[0] = ( JOCTET ) 0xFF; src->buffer[1] = ( JOCTET ) JPEG_EOI; nbytes = 2; } else { memcpy( src->buffer, src->infile, INPUT_BUF_SIZE ); src->infile = src->infile + nbytes; src->memsize = src->memsize - INPUT_BUF_SIZE; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; src->start_of_file = FALSE; return TRUE; } /* * Initialize source --- called by jpeg_read_header * before any data is actually read. */ METHODDEF void init_source( j_decompress_ptr cinfo ) { my_src_ptr src = ( my_src_ptr ) cinfo->src; /* We reset the empty-input-file flag for each image, * but we don't clear the input buffer. * This is correct behavior for reading a series of images from one source. */ src->start_of_file = TRUE; } /* * Skip data --- used to skip over a potentially large amount of * uninteresting data (such as an APPn marker). * * Writers of suspendable-input applications must note that skip_input_data * is not granted the right to give a suspension return. If the skip extends * beyond the data currently in the buffer, the buffer can be marked empty so * that the next read will cause a fill_input_buffer call that can suspend. * Arranging for additional bytes to be discarded before reloading the input * buffer is the application writer's problem. */ METHODDEF void skip_input_data( j_decompress_ptr cinfo, long num_bytes ) { my_src_ptr src = ( my_src_ptr ) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if( num_bytes > 0 ) { src->infile = src->infile + num_bytes; src->pub.next_input_byte += ( size_t ) num_bytes; src->pub.bytes_in_buffer -= ( size_t ) num_bytes; } } /* * An additional method that can be provided by data source modules is the * resync_to_restart method for error recovery in the presence of RST markers. * For the moment, this source module just uses the default resync method * provided by the JPEG library. That method assumes that no backtracking * is possible. */ /* * Terminate source --- called by jpeg_finish_decompress * after all data has been read. Often a no-op. * * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding * application must deal with any cleanup that should happen even * for error exit. */ METHODDEF void term_source( j_decompress_ptr cinfo ) { cinfo = cinfo; /* no work necessary here */ } GLOBAL void jpeg_memory_src( j_decompress_ptr cinfo, byte* infile, int size ) { my_src_ptr src; /* The source object and input buffer are made permanent so that a series * of JPEG images can be read from the same file by calling jpeg_stdio_src * only before the first one. (If we discarded the buffer at the end of * one image, we'd likely lose the start of the next one.) * This makes it unsafe to use this manager and a different source * manager serially with the same JPEG object. Caveat programmer. */ if( cinfo->src == NULL ) /* first time for this JPEG object? */ { cinfo->src = ( struct jpeg_source_mgr* ) ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_PERMANENT, sizeof( my_source_mgr ) ); src = ( my_src_ptr ) cinfo->src; src->buffer = ( JOCTET* ) ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof( JOCTET ) ); } src = ( my_src_ptr ) cinfo->src; src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->pub.term_source = term_source; src->infile = infile; src->memsize = size; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ } int JPEGBlit( byte* wStatus, byte* data, int datasize ) { /* This struct contains the JPEG decompression parameters and pointers to * working space (which is allocated as needed by the JPEG library). */ struct jpeg_decompress_struct cinfo; /* We use our private extension JPEG error handler. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ /* More stuff */ JSAMPARRAY buffer; /* Output row buffer */ int row_stride; /* physical row width in output buffer */ /* Step 1: allocate and initialize JPEG decompression object */ /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error( &jerr ); /* Now we can initialize the JPEG decompression object. */ jpeg_create_decompress( &cinfo ); /* Step 2: specify data source (eg, a file) */ jpeg_memory_src( &cinfo, data, datasize ); /* Step 3: read file parameters with jpeg_read_header() */ jpeg_read_header( &cinfo, TRUE ); /* We can ignore the return value from jpeg_read_header since * (a) suspension is not possible with the stdio data source, and * (b) we passed TRUE to reject a tables-only JPEG file as an error. * See libjpeg.doc for more info. */ /* Step 4: set parameters for decompression */ /* In this example, we don't need to change any of the defaults set by * jpeg_read_header(), so we do nothing here. */ /* Step 5: Start decompressor */ cinfo.dct_method = JDCT_IFAST; cinfo.dct_method = JDCT_FASTEST; cinfo.dither_mode = JDITHER_NONE; cinfo.do_fancy_upsampling = FALSE; // cinfo.out_color_space = JCS_GRAYSCALE; jpeg_start_decompress( &cinfo ); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* We may need to do some setup of our own at this point before reading * the data. After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. * In this example, we need to make an output work buffer of the right size. */ /* JSAMPLEs per row in output buffer */ row_stride = cinfo.output_width * cinfo.output_components; /* Make a one-row-high sample array that will go away when done with image */ buffer = ( *cinfo.mem->alloc_sarray ) ( ( j_common_ptr ) &cinfo, JPOOL_IMAGE, row_stride, 1 ); /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ wStatus += ( cinfo.output_height - 1 ) * row_stride; while( cinfo.output_scanline < cinfo.output_height ) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ jpeg_read_scanlines( &cinfo, &buffer[0], 1 ); /* Assume put_scanline_someplace wants a pointer and sample count. */ memcpy( wStatus, &buffer[0][0], row_stride ); /* int x; unsigned int *buf = (unsigned int *)&buffer[0][0]; unsigned int *out = (unsigned int *)wStatus; for(x=0;xRead( file, RoQFrameSize + 8 ); if( RoQPlayed >= ROQSize ) { if( looping ) { RoQReset(); } else { status = FMV_EOF; } return; } framedata = file; // // new frame is ready // redump: switch( roq_id ) { case ROQ_QUAD_VQ: if( ( numQuads & 1 ) ) { normalBuffer0 = t[1]; RoQPrepMcomp( roqF0, roqF1 ); blitVQQuad32fs( qStatus[1], framedata ); buf = image + screenDelta; } else { normalBuffer0 = t[0]; RoQPrepMcomp( roqF0, roqF1 ); blitVQQuad32fs( qStatus[0], framedata ); buf = image; } if( numQuads == 0 ) // first frame { memcpy( image + screenDelta, image, samplesPerLine * ysize ); } numQuads++; dirty = true; break; case ROQ_CODEBOOK: decodeCodeBook( framedata, ( unsigned short )roq_flags ); break; case ZA_SOUND_MONO: break; case ZA_SOUND_STEREO: break; case ROQ_QUAD_INFO: if( numQuads == -1 ) { readQuadInfo( framedata ); setupQuad( 0, 0 ); } if( numQuads != 1 ) numQuads = 0; break; case ROQ_PACKET: inMemory = ( roq_flags != 0 ); RoQFrameSize = 0; // for header break; case ROQ_QUAD_HANG: RoQFrameSize = 0; break; case ROQ_QUAD_JPEG: if( !numQuads ) { normalBuffer0 = t[0]; JPEGBlit( image, framedata, RoQFrameSize ); memcpy( image + screenDelta, image, samplesPerLine * ysize ); numQuads++; } break; default: status = FMV_EOF; break; } // // read in next frame data // if( RoQPlayed >= ROQSize ) { if( looping ) { RoQReset(); } else { status = FMV_EOF; } return; } framedata += RoQFrameSize; roq_id = framedata[0] + framedata[1] * 256; RoQFrameSize = framedata[2] + framedata[3] * 256 + framedata[4] * 65536; roq_flags = framedata[6] + framedata[7] * 256; roqF0 = ( char )framedata[7]; roqF1 = ( char )framedata[6]; if( RoQFrameSize > 65536 || roq_id == 0x1084 ) { common->DPrintf( "roq_size>65536||roq_id==0x1084\n" ); status = FMV_EOF; if( looping ) { RoQReset(); } return; } if( inMemory && ( status != FMV_EOF ) ) { inMemory = false; framedata += 8; goto redump; } // // one more frame hits the dust // // assert(RoQFrameSize <= 65536); // r = Sys_StreamedRead( file, RoQFrameSize+8, 1, iFile ); RoQPlayed += RoQFrameSize + 8; } /* ============== idCinematicLocal::RoQ_init ============== */ void idCinematicLocal::RoQ_init() { RoQPlayed = 24; /* get frame rate */ roqFPS = file[ 6] + file[ 7] * 256; if( !roqFPS ) roqFPS = 30; numQuads = -1; roq_id = file[ 8] + file[ 9] * 256; RoQFrameSize = file[10] + file[11] * 256 + file[12] * 65536; roq_flags = file[14] + file[15] * 256; } /* ============== idCinematicLocal::RoQShutdown ============== */ void idCinematicLocal::RoQShutdown() { if( status == FMV_IDLE ) { return; } status = FMV_IDLE; if( iFile ) { fileSystem->CloseFile( iFile ); iFile = NULL; } fileName = ""; } //=========================================== /* ============== idSndWindow::InitFromFile ============== */ bool idSndWindow::InitFromFile( const char* qpath, bool looping ) { idStr fname = qpath; fname.ToLower(); if( !fname.Icmp( "waveform" ) ) { showWaveform = true; } else { showWaveform = false; } return true; } /* ============== idSndWindow::ImageForTime ============== */ cinData_t idSndWindow::ImageForTime( int milliseconds ) { return soundSystem->ImageForTime( milliseconds, showWaveform ); } /* ============== idSndWindow::AnimationLength ============== */ int idSndWindow::AnimationLength() { return -1; }