/* Copyright (C) 2002-2003 Victor Luchits This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "client.h" static short snd_sqr_arr[256]; /* ================== RoQ_Init ================== */ void RoQ_Init( void ) { int i; for( i = 0; i < 128; i++ ) { snd_sqr_arr[i] = i * i; snd_sqr_arr[i + 128] = -(i * i); } } /* ================== RoQ_ReadChunk ================== */ void RoQ_ReadChunk( cinematics_t *cin ) { roq_chunk_t *chunk = &cin->chunk; FS_Read( &chunk->id, sizeof(short), cin->file ); FS_Read( &chunk->size, sizeof(int), cin->file ); FS_Read( &chunk->argument, sizeof(short), cin->file ); chunk->id = LittleShort( chunk->id ); chunk->size = LittleLong( chunk->size ); chunk->argument = LittleShort( chunk->argument ); } /* ================== RoQ_SkipBlock ================== */ static inline void RoQ_SkipBlock( cinematics_t *cin, int size ) { FS_Seek( cin->file, size, FS_SEEK_CUR ); } /* ================== RoQ_SkipChunk ================== */ void RoQ_SkipChunk( cinematics_t *cin ) { RoQ_SkipBlock( cin, cin->chunk.size ); } /* ================== RoQ_ReadInfo ================== */ void RoQ_ReadInfo( cinematics_t *cin ) { short t[4]; FS_Read( t, sizeof( short ) * 4, cin->file ); if( cin->width != LittleShort( t[0] ) || cin->height != LittleShort( t[1] ) ) { cin->width = LittleShort( t[0] ); cin->height = LittleShort( t[1] ); if( cin->vid_buffer ) Mem_ZoneFree( cin->vid_buffer ); cin->vid_buffer = Mem_ZoneMallocExt( cin->width * cin->height * 4 * 2, 0 ); // default to 255 for alpha memset( cin->vid_buffer, 0xFF, cin->width * cin->height * 4 * 2 ); cin->vid_pic[0] = cin->vid_buffer; cin->vid_pic[1] = cin->vid_buffer + cin->width * cin->height * 4; } } /* ================== RoQ_ReadCodebook ================== */ void RoQ_ReadCodebook( cinematics_t *cin ) { unsigned int nv1, nv2; roq_chunk_t *chunk = &cin->chunk; nv1 = (chunk->argument >> 8) & 0xFF; if( !nv1 ) nv1 = 256; nv2 = chunk->argument & 0xFF; if( !nv2 && (nv1 * 6 < chunk->size) ) nv2 = 256; FS_Read( cin->cells, sizeof(roq_cell_t)*nv1, cin->file ); FS_Read( cin->qcells, sizeof(roq_qcell_t)*nv2, cin->file ); } /* ================== RoQ_ApplyVector2x2 ================== */ static void RoQ_DecodeBlock( qbyte *dst0, qbyte *dst1, const qbyte *src0, const qbyte *src1, float u, float v ) { int c[3]; // convert YCbCr to RGB VectorSet( c, 1.402f * v, -0.34414f * u - 0.71414f * v, 1.772f * u ); // 1st pixel dst0[0] = bound( 0, c[0] + src0[0], 255 ); dst0[1] = bound( 0, c[1] + src0[0], 255 ); dst0[2] = bound( 0, c[2] + src0[0], 255 ); // 2nd pixel dst0[4] = bound( 0, c[0] + src0[1], 255 ); dst0[5] = bound( 0, c[1] + src0[1], 255 ); dst0[6] = bound( 0, c[2] + src0[1], 255 ); // 3rd pixel dst1[0] = bound( 0, c[0] + src1[0], 255 ); dst1[1] = bound( 0, c[1] + src1[0], 255 ); dst1[2] = bound( 0, c[2] + src1[0], 255 ); // 4th pixel dst1[4] = bound( 0, c[0] + src1[1], 255 ); dst1[5] = bound( 0, c[1] + src1[1], 255 ); dst1[6] = bound( 0, c[2] + src1[1], 255 ); } /* ================== RoQ_ApplyVector2x2 ================== */ static void RoQ_ApplyVector2x2( cinematics_t *cin, int x, int y, const roq_cell_t *cell ) { qbyte *dst0, *dst1; dst0 = cin->vid_pic[0] + (y * cin->width + x) * 4; dst1 = dst0 + cin->width * 4; RoQ_DecodeBlock( dst0, dst1, cell->y, cell->y+2, (float)((int)cell->u-128), (float)((int)cell->v-128) ); } /* ================== RoQ_ApplyVector4x4 ================== */ static void RoQ_ApplyVector4x4( cinematics_t *cin, int x, int y, const roq_cell_t *cell ) { qbyte *dst0, *dst1; qbyte p[4]; float u, v; u = (float)((int)cell->u - 128); v = (float)((int)cell->v - 128); p[0] = p[1] = cell->y[0]; p[2] = p[3] = cell->y[1]; dst0 = cin->vid_pic[0] + (y * cin->width + x) * 4; dst1 = dst0 + cin->width * 4; RoQ_DecodeBlock( dst0, dst0+8, p, p+2, u, v ); RoQ_DecodeBlock( dst1, dst1+8, p, p+2, u, v ); p[0] = p[1] = cell->y[2]; p[2] = p[3] = cell->y[3]; dst0 += cin->width * 4 * 2; dst1 += cin->width * 4 * 2; RoQ_DecodeBlock( dst0, dst0+8, p, p+2, u, v ); RoQ_DecodeBlock( dst1, dst1+8, p, p+2, u, v ); } /* ================== RoQ_ApplyMotion4x4 ================== */ static void RoQ_ApplyMotion4x4( cinematics_t *cin, int x, int y, qbyte mv, char mean_x, char mean_y ) { int x0, y0; qbyte *src, *dst; // calc source coords x0 = x + 8 - (mv >> 4) - mean_x; y0 = y + 8 - (mv & 0xF) - mean_y; src = cin->vid_pic[1] + (y0 * cin->width + x0) * 4; dst = cin->vid_pic[0] + (y * cin->width + x) * 4; for( y = 0; y < 4; y++, src += cin->width * 4, dst += cin->width * 4 ) memcpy( dst, src, 4 * 4 ); } /* ================== RoQ_ApplyMotion8x8 ================== */ static void RoQ_ApplyMotion8x8( cinematics_t *cin, int x, int y, qbyte mv, char mean_x, char mean_y ) { int x0, y0; qbyte *src, *dst; // calc source coords x0 = x + 8 - (mv >> 4) - mean_x; y0 = y + 8 - (mv & 0xF) - mean_y; src = cin->vid_pic[1] + (y0 * cin->width + x0) * 4; dst = cin->vid_pic[0] + (y * cin->width + x) * 4; for( y = 0; y < 8; y++, src += cin->width * 4, dst += cin->width * 4 ) memcpy( dst, src, 8 * 4 ); } /* ================== RoQ_ReadVideo ================== */ #define RoQ_READ_BLOCK 0x4000 qbyte *RoQ_ReadVideo( cinematics_t *cin ) { roq_chunk_t *chunk = &cin->chunk; int i, vqflg, vqflg_pos, vqid; int xpos, ypos, x, y, xp, yp; qbyte c, *tp; roq_qcell_t *qcell; qbyte raw[RoQ_READ_BLOCK]; unsigned remaining, bpos, read; vqflg = 0; vqflg_pos = -1; xpos = ypos = 0; #define RoQ_ReadRaw() read = min( sizeof( raw ), remaining ); remaining -= read; FS_Read( raw, read, cin->file ); #define RoQ_ReadByte(x) if( bpos >= read ) { RoQ_ReadRaw (); bpos = 0; } (x) = raw[bpos++]; #define RoQ_ReadShort(x) if( bpos+1 == read ) { c = raw[bpos]; RoQ_ReadRaw (); (x)=(raw[0] << 8)|c; bpos=1; } \ else { if( bpos+1 > read ) { RoQ_ReadRaw (); bpos = 0; } (x)=(raw[bpos+1] << 8)|raw[bpos]; bpos+=2; } #define RoQ_ReadFlag() if( vqflg_pos < 0 ) { RoQ_ReadShort( vqflg ); vqflg_pos = 7; } \ vqid = (vqflg >> (vqflg_pos * 2)) & 0x3; vqflg_pos--; for( bpos = read = 0, remaining = chunk->size; bpos < read || remaining; ) { for( yp = ypos; yp < ypos + 16; yp += 8 ) for( xp = xpos; xp < xpos + 16; xp += 8 ) { RoQ_ReadFlag (); switch( vqid ) { case RoQ_ID_MOT: break; case RoQ_ID_FCC: RoQ_ReadByte( c ); RoQ_ApplyMotion8x8( cin, xp, yp, c, ( char )((chunk->argument >> 8) & 0xff), (char)(chunk->argument & 0xff) ); break; case RoQ_ID_SLD: RoQ_ReadByte( c ); qcell = cin->qcells + c; RoQ_ApplyVector4x4( cin, xp, yp, cin->cells + qcell->idx[0] ); RoQ_ApplyVector4x4( cin, xp+4, yp, cin->cells + qcell->idx[1] ); RoQ_ApplyVector4x4( cin, xp, yp+4, cin->cells + qcell->idx[2] ); RoQ_ApplyVector4x4( cin, xp+4, yp+4, cin->cells + qcell->idx[3] ); break; case RoQ_ID_CCC: for( i = 0; i < 4; i++ ) { x = xp; if( i & 0x01 ) x += 4; y = yp; if( i & 0x02 ) y += 4; RoQ_ReadFlag (); switch( vqid ) { case RoQ_ID_MOT: break; case RoQ_ID_FCC: RoQ_ReadByte( c ); RoQ_ApplyMotion4x4( cin, x, y, c, ( char )((chunk->argument >> 8) & 0xff), (char)(chunk->argument & 0xff) ); break; case RoQ_ID_SLD: RoQ_ReadByte( c ); qcell = cin->qcells + c; RoQ_ApplyVector2x2( cin, x, y, cin->cells + qcell->idx[0] ); RoQ_ApplyVector2x2( cin, x+2, y, cin->cells + qcell->idx[1] ); RoQ_ApplyVector2x2( cin, x, y+2, cin->cells + qcell->idx[2] ); RoQ_ApplyVector2x2( cin, x+2, y+2, cin->cells + qcell->idx[3] ); break; case RoQ_ID_CCC: RoQ_ReadByte( c ); RoQ_ApplyVector2x2( cin, x, y, cin->cells + c ); RoQ_ReadByte( c ); RoQ_ApplyVector2x2( cin, x+2, y, cin->cells + c ); RoQ_ReadByte( c ); RoQ_ApplyVector2x2( cin, x, y+2, cin->cells + c ); RoQ_ReadByte( c ); RoQ_ApplyVector2x2( cin, x+2, y+2, cin->cells + c ); break; default: Com_DPrintf( "Unknown vq code: %d\n", vqid ); break; } } break; default: Com_DPrintf( "Unknown vq code: %d\n", vqid ); break; } } xpos += 16; if( xpos >= cin->width ) { xpos -= cin->width; ypos += 16; if( ypos >= cin->height ) { RoQ_SkipBlock( cin, remaining ); // ignore remaining trash break; } } } if( cin->frame++ == 0 ) { // copy initial values to back buffer for motion memcpy ( cin->vid_pic[1], cin->vid_pic[0], cin->width * cin->height * 4 ); } else { // swap buffers tp = cin->vid_pic[0]; cin->vid_pic[0] = cin->vid_pic[1]; cin->vid_pic[1] = tp; } return cin->vid_pic[1]; } /* ================== RoQ_ReadAudio ================== */ void RoQ_ReadAudio( cinematics_t *cin ) { unsigned int i; int snd_left, snd_right; qbyte raw[RoQ_READ_BLOCK]; short samples[RoQ_READ_BLOCK]; roq_chunk_t *chunk = &cin->chunk; unsigned int remaining, read; if( chunk->id == RoQ_SOUND_MONO ) { snd_left = chunk->argument; snd_right = 0; } else { snd_left = chunk->argument & 0xff00; snd_right = (chunk->argument & 0xff) << 8; } for( remaining = chunk->size; remaining > 0; remaining -= read ) { read = min( sizeof( raw ), remaining ); FS_Read( raw, read, cin->file ); if( chunk->id == RoQ_SOUND_MONO ) { for( i = 0; i < read; i++ ) { snd_left += snd_sqr_arr[raw[i]]; samples[i] = (short)snd_left; snd_left = (short)snd_left; } S_RawSamples( read, cin->s_rate, 2, 1, (qbyte *)samples, qfalse, qfalse ); } else if( chunk->id == RoQ_SOUND_STEREO ) { for( i = 0; i < read; i += 2 ){ snd_left += snd_sqr_arr[raw[i]]; samples[i+0] = (short)snd_left; snd_left = (short)snd_left; snd_right += snd_sqr_arr[raw[i+1]]; samples[i+1] = (short)snd_right; snd_right = (short)snd_right; } S_RawSamples( read / 2, cin->s_rate, 2, 2, (qbyte *)samples, qfalse, qfalse ); } } }