summaryrefslogtreecommitdiff
path: root/src/client/cl_cin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/cl_cin.cpp')
-rw-r--r--src/client/cl_cin.cpp1937
1 files changed, 1937 insertions, 0 deletions
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 <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+/*****************************************************************************
+ * 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;
+ }
+ }
+}