From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/client/cl_cin.cpp | 1937 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1937 insertions(+) create mode 100644 src/client/cl_cin.cpp (limited to 'src/client/cl_cin.cpp') diff --git a/src/client/cl_cin.cpp b/src/client/cl_cin.cpp new file mode 100644 index 0000000..6326e89 --- /dev/null +++ b/src/client/cl_cin.cpp @@ -0,0 +1,1937 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub + +This file is part of Tremulous. + +Tremulous 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. + +Tremulous 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 Tremulous; if not, see + +=========================================================================== +*/ + +/***************************************************************************** + * name: cl_cin.c + * + * desc: video and cinematic playback + * + * $Archive: /MissionPack/code/client/cl_cin.c $ + * + * cl_glconfig.hwtype trtypes 3dfx/ragepro need 256x256 + * + *****************************************************************************/ + +#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 + +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]; + +struct cinematics_t +{ + byte linbuf[DEFAULT_CIN_WIDTH * DEFAULT_CIN_HEIGHT * 4 * 2]; + byte file[65536]; + short sqrTable[256]; + + int mcomp[256]; + byte *qStatus[2][32768]; + + long oldXOff; + long oldYOff; + long oldysize; + long oldxsize; + + int currentHandle; +}; + +struct cin_cache +{ + char fileName[MAX_OSPATH]; + + int CIN_WIDTH; + int CIN_HEIGHT; + + int xpos; + int ypos; + int width; + int height; + + bool looping; + bool holdAtEnd; + bool dirty; + bool alterGameState; + bool silent; + bool shader; + + fileHandle_t iFile; + e_status status; + + int startTime; + 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; + + bool half; + bool smootheddouble; + bool inMemory; + long normalBuffer0; + long roq_flags; + long roqF0; + long roqF1; + long t[2]; + long roqFPS; + int playonwalls; + byte *buf; + long drawX; + long drawY; +}; + +static cinematics_t cin; +static cin_cache cinTable[MAX_VIDEO_HANDLES]; +static int currentHandle = -1; +static int CL_handle = -1; + +extern int s_soundtime; // sample PAIRS + +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; +} + +//----------------------------------------------------------------------------- +// RllSetupTable +// +// Allocates and initializes the square table. +// +// Parameters: None +// +// Returns: Nothing +//----------------------------------------------------------------------------- +static void RllSetupTable(void) +{ + int z; + + for (z = 0; z < 128; z++) + { + cin.sqrTable[z] = (short)(z * z); + cin.sqrTable[z + 128] = (short)(-cin.sqrTable[z]); + } +} + +#if 0 +//----------------------------------------------------------------------------- +// 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)); +} +#endif + +//----------------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------------- +static 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 +//----------------------------------------------------------------------------- +static 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)); +} + +#if 0 +//----------------------------------------------------------------------------- +// 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; +} +#endif + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void move8_32(byte *src, byte *dst, int spl) +{ + int i; + + for (i = 0; i < 8; ++i) + { + memcpy(dst, src, 32); + src += spl; + dst += spl; + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void move4_32(byte *src, byte *dst, int spl) +{ + int i; + + for (i = 0; i < 4; ++i) + { + memcpy(dst, src, 16); + src += spl; + dst += spl; + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void blit8_32(byte *src, byte *dst, int spl) +{ + int i; + + for (i = 0; i < 8; ++i) + { + memcpy(dst, src, 32); + src += 32; + dst += spl; + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ +static void blit4_32(byte *src, byte *dst, int spl) +{ + int i; + + for (i = 0; i < 4; ++i) + { + memmove(dst, src, 16); + src += 16; + dst += spl; + } +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void blit2_32(byte *src, byte *dst, int spl) +{ + memcpy(dst, src, 8); + memcpy(dst + spl, src + 8, 8); +} + +/****************************************************************************** +* +* 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: +* +******************************************************************************/ +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)); +} + +/****************************************************************************** +* +* 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; + union { + unsigned int *i; + unsigned short *s; + } 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.s = 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.i++ = yuv_to_rgb24(y0, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y1, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y2, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y3, cr, cb); + } + + icptr.s = vq4; + idptr.s = vq8; + + for (i = 0; i < four; i++) + { + iaptr.s = vq2; + iaptr.i += (*input++) * 4; + ibptr.s = vq2; + ibptr.i += (*input++) * 4; + for (j = 0; j < 2; j++) VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i); + } + } + 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.s = 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.i++ = yuv_to_rgb24(y0, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y1, cr, cb); + *ibptr.i++ = yuv_to_rgb24(((y0 * 3) + y2) / 4, cr, cb); + *ibptr.i++ = yuv_to_rgb24(((y1 * 3) + y3) / 4, cr, cb); + *ibptr.i++ = yuv_to_rgb24((y0 + (y2 * 3)) / 4, cr, cb); + *ibptr.i++ = yuv_to_rgb24((y1 + (y3 * 3)) / 4, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y2, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y3, cr, cb); + } + + icptr.s = vq4; + idptr.s = vq8; + + for (i = 0; i < four; i++) + { + iaptr.s = vq2; + iaptr.i += (*input++) * 8; + ibptr.s = vq2; + ibptr.i += (*input++) * 8; + for (j = 0; j < 2; j++) + { + VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i); + VQ2TO4(iaptr.i, ibptr.i, icptr.i, idptr.i); + } + } + } + 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.s = bptr; + for (i = 0; i < two; i++) + { + y0 = (long)*input; + input += 2; + y2 = (long)*input; + input += 2; + cr = (long)*input++; + cb = (long)*input++; + *ibptr.i++ = yuv_to_rgb24(y0, cr, cb); + *ibptr.i++ = yuv_to_rgb24(y2, cr, cb); + } + + icptr.s = vq4; + idptr.s = vq8; + + for (i = 0; i < four; i++) + { + iaptr.s = vq2; + iaptr.i += (*input++) * 2; + ibptr.s = vq2 + (*input++) * 2; + ibptr.i += (*input++) * 2; + for (j = 0; j < 2; j++) + { + VQ2TO2(iaptr.i, ibptr.i, icptr.i, idptr.i); + } + } + } + } +} + +/****************************************************************************** +* +* 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].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 = false; + cinTable[currentHandle].smootheddouble = false; + + cinTable[currentHandle].VQ0 = cinTable[currentHandle].VQNormal; + cinTable[currentHandle].VQ1 = cinTable[currentHandle].VQBuffer; + + cinTable[currentHandle].t[0] = cinTable[currentHandle].screenDelta; + cinTable[currentHandle].t[1] = -cinTable[currentHandle].screenDelta; + + 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 (cls.glconfig.hardwareType == GLHW_RAGEPRO || cls.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"); + } + } +} + +/****************************************************************************** +* +* 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(void) +{ + 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(void) +{ + if (currentHandle < 0) return; + + FS_FCloseFile(cinTable[currentHandle].iFile); + FS_FOpenFileRead(cinTable[currentHandle].fileName, &cinTable[currentHandle].iFile, true); + // let the background thread start reading ahead + FS_Read(cin.file, 16, 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; + + FS_Read(cin.file, cinTable[currentHandle].RoQFrameSize + 8, cinTable[currentHandle].iFile); + if (cinTable[currentHandle].RoQPlayed >= cinTable[currentHandle].ROQSize) + { + if (cinTable[currentHandle].holdAtEnd == false) + { + 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 + ::memcpy(cin.linbuf + cinTable[currentHandle].screenDelta, cin.linbuf, + cinTable[currentHandle].samplesPerLine * cinTable[currentHandle].ysize); + } + cinTable[currentHandle].numQuads++; + cinTable[currentHandle].dirty = true; + 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(0, ssize, 22050, 2, 1, (byte *)sbuf, 1.0f, -1); + } + break; + case ZA_SOUND_STEREO: + if (!cinTable[currentHandle].silent) + { + if (cinTable[currentHandle].numQuads == -1) + { + S_Update(); + s_rawend[0] = s_soundtime; + } + ssize = RllDecodeStereoToStereo(framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, + (unsigned short)cinTable[currentHandle].roq_flags); + S_RawSamples(0, ssize, 22050, 2, 2, (byte *)sbuf, 1.0f, -1); + } + break; + case ROQ_QUAD_INFO: + if (cinTable[currentHandle].numQuads == -1) + { + readQuadInfo(framedata); + setupQuad(0, 0); + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds(); + } + if (cinTable[currentHandle].numQuads != 1) cinTable[currentHandle].numQuads = 0; + break; + case ROQ_PACKET: + cinTable[currentHandle].inMemory = (bool)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 == false) + { + 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 = (signed char)framedata[7]; + cinTable[currentHandle].roqF1 = (signed 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 = false; + framedata += 8; + goto redump; + } + // + // one more frame hits the dust + // + // assert(cinTable[currentHandle].RoQFrameSize <= 65536); + // r = FS_Read( cin.file, cinTable[currentHandle].RoQFrameSize+8, cinTable[currentHandle].iFile ); + cinTable[currentHandle].RoQPlayed += cinTable[currentHandle].RoQFrameSize + 8; +} + +/****************************************************************************** +* +* Function: +* +* Description: +* +******************************************************************************/ + +static void RoQ_init(void) +{ + cinTable[currentHandle].startTime = cinTable[currentHandle].lastTime = CL_ScaledMilliseconds(); + + 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) + { + FS_FCloseFile(cinTable[currentHandle].iFile); + cinTable[currentHandle].iFile = 0; + } + + if (cinTable[currentHandle].alterGameState) + { + clc.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; + + CL_ProtocolSpecificCommandsInit(); + } + cinTable[currentHandle].fileName[0] = 0; + currentHandle = -1; +} + +/* +================== +CIN_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 (clc.state != CA_CINEMATIC) + { + return cinTable[currentHandle].status; + } + } + cinTable[currentHandle].status = FMV_EOF; + RoQShutdown(); + + return FMV_EOF; +} + +/* +================== +CIN_RunCinematic + +Fetch and decompress the pending frame +================== +*/ + +e_status CIN_RunCinematic(int handle) +{ + 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 (clc.state != CA_CINEMATIC) + { + return cinTable[currentHandle].status; + } + } + + if (cinTable[currentHandle].status == FMV_IDLE) + { + return cinTable[currentHandle].status; + } + + thisTime = CL_ScaledMilliseconds(); + if (cinTable[currentHandle].shader && (abs(thisTime - cinTable[currentHandle].lastTime)) > 100) + { + cinTable[currentHandle].startTime += thisTime - cinTable[currentHandle].lastTime; + } + cinTable[currentHandle].tfps = (((CL_ScaledMilliseconds() - 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) + { + cinTable[currentHandle].tfps = (((CL_ScaledMilliseconds() - 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 FMV_EOF; + } + } + + return cinTable[currentHandle].status; +} + +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 = true; +} + +static void CIN_SetLooping(int handle, bool loop) +{ + if (handle < 0 || handle >= MAX_VIDEO_HANDLES || cinTable[handle].status == FMV_EOF) return; + cinTable[handle].looping = loop; +} + +/* +================== +CIN_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("CIN_PlayCinematic( %s )\n", arg); + + ::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, true); + + 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 (cls.ui) + { + VM_Call(cls.ui, UI_SET_ACTIVE_MENU - cls.uiInterface == 2 ? 2 : 0, 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); + + cinTable[currentHandle].status = FMV_PLAY; + Com_DPrintf("trFMV::play(), playing %s\n", arg); + + if (cinTable[currentHandle].alterGameState) + { + clc.state = CA_CINEMATIC; + } + + if (!cinTable[currentHandle].silent) + { + s_rawend[0] = s_soundtime; + } + + return currentHandle; + } + Com_DPrintf("trFMV::play(), invalid RoQ ID\n"); + + RoQShutdown(); + return -1; +} + +/* +================== +CIN_ResampleCinematic + +Resample cinematic to 256x256 and store in buf2 +================== +*/ +static void CIN_ResampleCinematic(int handle, int *buf2) +{ + int ix, iy, *buf3, xm, ym, ll; + byte *buf; + + buf = cinTable[handle].buf; + + 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; + 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)]; + } + } + } +} + +/* +================== +CIN_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 *buf2 = (int *)Hunk_AllocateTempMemory(256 * 256 * 4); + + CIN_ResampleCinematic(handle, buf2); + + re.DrawStretchRaw(x, y, w, h, 256, 256, (byte *)buf2, handle, true); + cinTable[handle].dirty = false; + Hunk_FreeTempMemory(buf2); + return; + } + + re.DrawStretchRaw(x, y, w, h, cinTable[handle].drawX, cinTable[handle].drawY, + buf, handle, cinTable[handle].dirty); + + cinTable[handle].dirty = false; +} + +void CL_PlayCinematic_f(void) +{ + int bits = CIN_system; + + Com_DPrintf("CL_PlayCinematic_f\n"); + if (clc.state == CA_CINEMATIC) + { + SCR_StopCinematic(); + } + + const char *arg = Cmd_Argv(1); + const char *s = Cmd_Argv(2); + + 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 = false; + } + } + } + + // Resample the video if needed + if (cinTable[handle].dirty && (cinTable[handle].CIN_WIDTH != cinTable[handle].drawX || + cinTable[handle].CIN_HEIGHT != cinTable[handle].drawY)) + { + int *buf2 = (int *)Hunk_AllocateTempMemory(256 * 256 * 4); + + CIN_ResampleCinematic(handle, buf2); + + re.UploadCinematic( + cinTable[handle].CIN_WIDTH, cinTable[handle].CIN_HEIGHT, 256, 256, (byte *)buf2, handle, true); + cinTable[handle].dirty = false; + Hunk_FreeTempMemory(buf2); + } + else + { + // Upload video at normal resolution + re.UploadCinematic(cinTable[handle].CIN_WIDTH, cinTable[handle].CIN_HEIGHT, cinTable[handle].drawX, + cinTable[handle].drawY, cinTable[handle].buf, handle, cinTable[handle].dirty); + cinTable[handle].dirty = false; + } + + if (cl_inGameVideo->integer == 0 && cinTable[handle].playonwalls == 1) + { + cinTable[handle].playonwalls--; + } + else if (cl_inGameVideo->integer != 0 && cinTable[handle].playonwalls != 1) + { + cinTable[handle].playonwalls = 1; + } + } +} -- cgit