1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // cl_cin.c
22 // FIXME TODO:
23 // - Needs to resample the texture data if the image/cinematic size exceeds the video card maximum
24 //
25 
26 #include "cl_local.h"
27 
28 #define MAX_CIN_HBUFFER 0x20000
29 #define MAX_CIN_SNDBUFF 0x40000
30 
31 typedef struct huffBlock_s {
32 	byte				*data;
33 	int					count;
34 } huffBlock_t;
35 
36 /*
37 =============================================================================
38 
39 	RoQ DECOMPRESSION
40 
41 =============================================================================
42 */
43 
44 static int16	roq_sndSqrArr[256];
45 
46 /*
47 =============
48 RoQ_Init
49 
50 Called by the client to init, refresh also utilizes
51 =============
52 */
RoQ_Init(void)53 void RoQ_Init (void)
54 {
55 	int		i;
56 
57 	for (i=0 ; i<128 ; i++) {
58 		roq_sndSqrArr[i] = i * i;
59 		roq_sndSqrArr[i+128] = i * i * -1;
60 	}
61 }
62 
63 
64 /*
65 =============
66 RoQ_ReadChunk
67 =============
68 */
RoQ_ReadChunk(cinematic_t * cin)69 void RoQ_ReadChunk (cinematic_t *cin)
70 {
71 	roqChunk_t	*chunk = &cin->roqChunk;
72 
73 	FS_Read (&chunk->id, sizeof(int16), cin->fileNum);
74 	FS_Read (&chunk->size, sizeof(int), cin->fileNum);
75 	FS_Read (&chunk->arg, sizeof(int16), cin->fileNum);
76 
77 	chunk->id = LittleLong (chunk->id);
78 	chunk->size = LittleLong (chunk->size);
79 	chunk->arg = LittleLong (chunk->arg);
80 }
81 
82 
83 /*
84 =============
85 RoQ_SkipBlock
86 =============
87 */
RoQ_SkipBlock(cinematic_t * cin,int size)88 void RoQ_SkipBlock (cinematic_t *cin, int size)
89 {
90 	FS_Seek (cin->fileNum, size, FS_SEEK_CUR);
91 }
92 
93 
94 /*
95 =============
96 RoQ_SkipChunk
97 =============
98 */
RoQ_SkipChunk(cinematic_t * cin)99 void RoQ_SkipChunk (cinematic_t *cin)
100 {
101 	FS_Seek (cin->fileNum, cin->roqChunk.size, FS_SEEK_CUR);
102 }
103 
104 
105 /*
106 =============
107 RoQ_ReadInfo
108 =============
109 */
RoQ_ReadInfo(cinematic_t * cin)110 void RoQ_ReadInfo (cinematic_t *cin)
111 {
112 	int16	t[4];
113 	int		i;
114 
115 	FS_Read (t, sizeof(int16)*4, cin->fileNum);
116 	for (i=0 ; i<4 ; i++)
117 		t[i] = LittleLong (t[i]);
118 
119 	if (cin->width != t[0] || cin->height != t[1]) {
120 		cin->width = t[0];
121 		cin->height = t[1];
122 
123 		if (cin->roqBuffer)
124 			Mem_Free (cin->roqBuffer);
125 		cin->roqBuffer = Mem_PoolAllocExt (cin->width*cin->height*4 * 2, qFalse, cl_cinSysPool, 0);
126 		memset (cin->roqBuffer, 255, cin->width*cin->height*4 * 2);
127 
128 		cin->frames[0] = cin->roqBuffer;
129 		cin->frames[1] = cin->roqBuffer + (cin->width*cin->height*4);
130 	}
131 }
132 
133 
134 /*
135 =============
136 RoQ_ReadCodeBook
137 =============
138 */
RoQ_ReadCodeBook(cinematic_t * cin)139 void RoQ_ReadCodeBook (cinematic_t *cin)
140 {
141 	roqChunk_t	*chunk = &cin->roqChunk;
142 	uint32		nv[2];
143 
144 	nv[0] = (chunk->arg >> 8) & 255;
145 	if (!nv[0])
146 		nv[0] = 256;
147 
148 	nv[1] = chunk->arg & 255;
149 	if (!nv[1] && nv[0] * 6 < chunk->size)
150 		nv[1] = 256;
151 
152 	FS_Read (cin->roqCells, sizeof(roqCell_t)*nv[0], cin->fileNum);
153 	FS_Read (cin->roqQCells, sizeof(roqQCell_t)*nv[1], cin->fileNum);
154 }
155 
156 
157 /*
158 =============
159 RoQ_DecodeBlock
160 =============
161 */
RoQ_DecodeBlock(byte * dst0,byte * dst1,const byte * src0,const byte * src1,float u,float v)162 static void RoQ_DecodeBlock (byte *dst0, byte *dst1, const byte *src0, const byte *src1, float u, float v)
163 {
164 	int		c[3];
165 
166 	// Convert YCbCr to RGB
167 	Vec3Set (c, 1.402f * v, -0.34414f * u - 0.71414f * v, 1.772f * u);
168 
169 	// First pixel
170 	dst0[0] = bound (0, c[0] + src0[0], 255);
171 	dst0[1] = bound (0, c[1] + src0[0], 255);
172 	dst0[2] = bound (0, c[2] + src0[0], 255);
173 
174 	// Second pixel
175 	dst0[4] = bound (0, c[0] + src0[1], 255);
176 	dst0[5] = bound (0, c[1] + src0[1], 255);
177 	dst0[6] = bound (0, c[2] + src0[1], 255);
178 
179 	// Third pixel
180 	dst1[0] = bound (0, c[0] + src1[0], 255);
181 	dst1[1] = bound (0, c[1] + src1[0], 255);
182 	dst1[2] = bound (0, c[2] + src1[0], 255);
183 
184 	// Fourth pixel
185 	dst1[4] = bound (0, c[0] + src1[1], 255);
186 	dst1[5] = bound (0, c[1] + src1[1], 255);
187 	dst1[6] = bound (0, c[2] + src1[1], 255);
188 }
189 
190 
191 /*
192 =============
193 RoQ_ApplyVector2x2
194 =============
195 */
RoQ_ApplyVector2x2(cinematic_t * cin,int x,int y,const roqCell_t * cell)196 static void RoQ_ApplyVector2x2 (cinematic_t *cin, int x, int y, const roqCell_t *cell)
197 {
198 	byte	*dst0, *dst1;
199 
200 	dst0 = cin->frames[0] + (y * cin->width + x) * 4;
201 	dst1 = dst0 + cin->width * 4;
202 
203 	RoQ_DecodeBlock (dst0, dst1, cell->y, cell->y+2, (float)((int)cell->u-128), (float)((int)cell->v-128));
204 }
205 
206 
207 /*
208 =============
209 RoQ_ApplyVector4x4
210 =============
211 */
RoQ_ApplyVector4x4(cinematic_t * cin,int x,int y,const roqCell_t * cell)212 static void RoQ_ApplyVector4x4 (cinematic_t *cin, int x, int y, const roqCell_t *cell)
213 {
214 	byte	*dst0, *dst1;
215 	byte	p[4];
216 	float	u, v;
217 
218 	u = (float)((int)cell->u - 128);
219 	v = (float)((int)cell->v - 128);
220 
221 	p[0] = p[1] = cell->y[0];
222 	p[2] = p[3] = cell->y[1];
223 	dst0 = cin->frames[0] + (y * cin->width + x) * 4;
224 	dst1 = dst0 + cin->width * 4;
225 	RoQ_DecodeBlock (dst0, dst0+8, p, p+2, u, v);
226 	RoQ_DecodeBlock (dst1, dst1+8, p, p+2, u, v);
227 
228 	p[0] = p[1] = cell->y[2];
229 	p[2] = p[3] = cell->y[3];
230 	dst0 += cin->width * 4 * 2;
231 	dst1 +=	cin->width * 4 * 2;
232 	RoQ_DecodeBlock (dst0, dst0+8, p, p+2, u, v);
233 	RoQ_DecodeBlock (dst1, dst1+8, p, p+2, u, v);
234 }
235 
236 
237 /*
238 =============
239 RoQ_ApplyMotion4x4
240 =============
241 */
RoQ_ApplyMotion4x4(cinematic_t * cin,int x,int y,byte mv,char meanX,char meanY)242 static void RoQ_ApplyMotion4x4 (cinematic_t *cin, int x, int y, byte mv, char meanX, char meanY)
243 {
244 	byte	*src, *dst;
245 	int		x0, y0;
246 
247 	// Find the source coords
248 	x0 = x + 8 - (mv >> 4) - meanX;
249 	y0 = y + 8 - (mv & 0xf) - meanY;
250 
251 	src = cin->frames[1] + (y0 * cin->width + x0) * 4;
252 	dst = cin->frames[0] + (y * cin->width + x) * 4;
253 
254 	for (y=0 ; y<4 ; y++, src+=cin->width*4, dst+=cin->width*4)
255 		memcpy (dst, src, 4*4);	// FIXME
256 }
257 
258 
259 /*
260 =============
261 RoQ_ApplyMotion8x8
262 =============
263 */
RoQ_ApplyMotion8x8(cinematic_t * cin,int x,int y,byte mv,char meanX,char meanY)264 static void RoQ_ApplyMotion8x8 (cinematic_t *cin, int x, int y, byte mv, char meanX, char meanY)
265 {
266 	byte	*src, *dst;
267 	int		x0, y0;
268 
269 	// Find the source coords
270 	x0 = x + 8 - (mv >> 4) - meanX;
271 	y0 = y + 8 - (mv & 0xf) - meanY;
272 
273 	src = cin->frames[1] + (y0 * cin->width + x0) * 4;
274 	dst = cin->frames[0] + (y * cin->width + x) * 4;
275 
276 	for (y=0 ; y<8 ; y++, src+=cin->width*4, dst+=cin->width*4)
277 		memcpy (dst, src, 8*4);	// FIXME
278 }
279 
280 /*
281 =============================================================================
282 
283 	STATIC PCX LOADING
284 
285 =============================================================================
286 */
287 
288 /*
289 =============
290 CIN_LoadPCX
291 =============
292 */
CIN_LoadPCX(char * name,byte ** pic,byte ** palette,int * width,int * height)293 static qBool CIN_LoadPCX (char *name, byte **pic, byte **palette, int *width, int *height)
294 {
295 	byte		*raw;
296 	pcxHeader_t	*pcx;
297 	int			x, y, fileLen;
298 	int			dataByte, runLength;
299 	byte		*out, *pix;
300 
301 	if (pic)
302 		*pic = NULL;
303 	if (palette)
304 		*palette = NULL;
305 
306 	// Load the file
307 	fileLen = FS_LoadFile (name, (void **)&raw, NULL);
308 	if (!raw || fileLen <= 0)
309 		return qFalse;
310 
311 	// Parse the PCX file
312 	pcx = (pcxHeader_t *)raw;
313 
314 	pcx->xMin = LittleShort (pcx->xMin);
315 	pcx->yMin = LittleShort (pcx->yMin);
316 	pcx->xMax = LittleShort (pcx->xMax);
317 	pcx->yMax = LittleShort (pcx->yMax);
318 	pcx->hRes = LittleShort (pcx->hRes);
319 	pcx->vRes = LittleShort (pcx->vRes);
320 	pcx->bytesPerLine = LittleShort (pcx->bytesPerLine);
321 	pcx->paletteType = LittleShort (pcx->paletteType);
322 
323 	raw = &pcx->data;
324 
325 	// Sanity checks
326 	if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1) {
327 		Com_DevPrintf (PRNT_WARNING, "CIN_LoadPCX: %s: Invalid PCX header\n", name);
328 		return qFalse;
329 	}
330 
331 	if (pcx->bitsPerPixel != 8 || pcx->colorPlanes != 1) {
332 		Com_DevPrintf (PRNT_WARNING, "CIN_LoadPCX: %s: Only 8-bit PCX images are supported\n", name);
333 		return qFalse;
334 	}
335 
336 	if (pcx->xMax >= 640 || pcx->xMax <= 0 || pcx->yMax >= 480 || pcx->yMax <= 0) {
337 		Com_DevPrintf (PRNT_WARNING, "CIN_LoadPCX: %s: Bad PCX file dimensions: %i x %i\n", name, pcx->xMax, pcx->yMax);
338 		return qFalse;
339 	}
340 
341 	// FIXME: Some images with weird dimensions will crash if I don't do this...
342 	x = max (pcx->yMax+1, pcx->xMax+1);
343 	pix = out = Mem_PoolAllocExt (x * x, qFalse, cl_cinSysPool, 0);
344 	if (pic)
345 		*pic = out;
346 
347 	if (palette) {
348 		*palette = Mem_PoolAllocExt (768, qFalse, cl_cinSysPool, 0);
349 		memcpy (*palette, (byte *)pcx + fileLen - 768, 768);
350 	}
351 
352 	if (width)
353 		*width = pcx->xMax+1;
354 	if (height)
355 		*height = pcx->yMax+1;
356 
357 	for (y=0 ; y<=pcx->yMax ; y++, pix+=pcx->xMax+1) {
358 		for (x=0 ; x<=pcx->xMax ; ) {
359 			dataByte = *raw++;
360 
361 			if ((dataByte & 0xC0) == 0xC0) {
362 				runLength = dataByte & 0x3F;
363 				dataByte = *raw++;
364 			}
365 			else
366 				runLength = 1;
367 
368 			while (runLength-- > 0)
369 				pix[x++] = dataByte;
370 		}
371 	}
372 
373 	if (raw - (byte *)pcx > fileLen) {
374 		Com_DevPrintf (PRNT_WARNING, "CIN_LoadPCX: PCX file %s was malformed", name);
375 		Mem_Free (out);
376 		out = NULL;
377 		pix = NULL;
378 
379 		if (pic)
380 			*pic = NULL;
381 		if (palette) {
382 			Mem_Free (palette);
383 			*palette = NULL;
384 		}
385 
386 		return qFalse;
387 	}
388 
389 	if (!pic)
390 		Mem_Free (out);
391 	FS_FreeFile (pcx);
392 
393 	return qTrue;
394 }
395 
396 /*
397 =============================================================================
398 
399 	CINEMATIC DECOMPRESSION
400 
401 =============================================================================
402 */
403 
404 /*
405 ==================
406 CIN_SmallestHuffNode
407 ==================
408 */
CIN_SmallestHuffNode(int numNodes)409 static int CIN_SmallestHuffNode (int numNodes)
410 {
411 	int		best, bestNode;
412 	int		i;
413 
414 	best = 99999999;
415 	bestNode = -1;
416 	for (i=0 ; i<numNodes ; i++) {
417 		if (cl.cin.hUsed[i] || !cl.cin.hCount[i])
418 			continue;
419 
420 		if (cl.cin.hCount[i] < best) {
421 			best = cl.cin.hCount[i];
422 			bestNode = i;
423 		}
424 	}
425 
426 	if (bestNode == -1)
427 		return -1;
428 
429 	cl.cin.hUsed[bestNode] = qTrue;
430 	return bestNode;
431 }
432 
433 
434 /*
435 ==================
436 CIN_TableInit
437 
438 Reads the 64k counts table and initializes the node trees
439 ==================
440 */
CIN_TableInit(void)441 static void CIN_TableInit (void)
442 {
443 	int		*node, *nodeBase;
444 	byte	counts[256];
445 	int		numNodes;
446 	int		prev, j;
447 
448 	cl.cin.hBuffer = Mem_PoolAllocExt (MAX_CIN_HBUFFER, qFalse, cl_cinSysPool, 0);
449 	cl.cin.hNodes = Mem_PoolAlloc (256*256*2*4, cl_cinSysPool, 0);
450 
451 	for (prev=0 ; prev<256 ; prev++) {
452 		memset (cl.cin.hCount, 0, sizeof (cl.cin.hCount));
453 		memset (cl.cin.hUsed, 0, sizeof (cl.cin.hUsed));
454 
455 		// Read a row of counts
456 		FS_Read (counts, sizeof (counts), cl.cin.fileNum);
457 		for (j=0 ; j<256 ; j++)
458 			cl.cin.hCount[j] = counts[j];
459 
460 		// Build the nodes
461 		numNodes = 256;
462 		nodeBase = cl.cin.hNodes + prev*256*2;
463 
464 		while (numNodes != 511) {
465 			node = nodeBase + (numNodes-256)*2;
466 
467 			// Pick two lowest counts
468 			node[0] = CIN_SmallestHuffNode (numNodes);
469 			if (node[0] == -1)
470 				break;	// no more
471 
472 			node[1] = CIN_SmallestHuffNode (numNodes);
473 			if (node[1] == -1)
474 				break;
475 
476 			cl.cin.hCount[numNodes] = cl.cin.hCount[node[0]] + cl.cin.hCount[node[1]];
477 			numNodes++;
478 		}
479 
480 		cl.cin.hNumNodes[prev] = numNodes-1;
481 	}
482 }
483 
484 
485 /*
486 ==================
487 CIN_DecompressFrame
488 ==================
489 */
CIN_DecompressFrame(huffBlock_t in)490 static huffBlock_t CIN_DecompressFrame (huffBlock_t in)
491 {
492 	byte		*input, *out_p;
493 	huffBlock_t	out;
494 	int			nodenum, count;
495 	int			inbyte;
496 	int			*hnodes, *hnodesbase;
497 	int			i;
498 
499 	// Get decompressed count
500 	count = in.data[0] + (in.data[1]<<8) + (in.data[2]<<16) + (in.data[3]<<24);
501 	input = in.data + 4;
502 	out_p = out.data = Mem_PoolAlloc (count, cl_cinSysPool, 0);
503 
504 	// Read bits
505 	hnodesbase = cl.cin.hNodes - 256*2;	// nodes 0-255 aren't stored
506 
507 	hnodes = hnodesbase;
508 	nodenum = cl.cin.hNumNodes[0];
509 	while (count) {
510 		inbyte = *input++;
511 
512 		for (i=0 ; i<8 ; i++) {
513 			if (nodenum < 256) {
514 				hnodes = hnodesbase + (nodenum<<9);
515 				*out_p++ = nodenum;
516 				if (!--count)
517 					break;
518 				nodenum = cl.cin.hNumNodes[nodenum];
519 			}
520 			nodenum = hnodes[nodenum*2 + (inbyte&1)];
521 			inbyte >>=1;
522 		}
523 	}
524 
525 	if (input-in.data != in.count && input-in.data != in.count+1)
526 		Com_Printf (PRNT_WARNING, "Decompression overread by %i", (input - in.data) - in.count);
527 
528 	out.count = out_p - out.data;
529 
530 	return out;
531 }
532 
533 
534 /*
535 ==================
536 CIN_ResamplePalette
537 ==================
538 */
CIN_ResamplePalette(byte * in,uint32 * out)539 static void CIN_ResamplePalette (byte *in, uint32 *out)
540 {
541 	int		i;
542 	byte	*bOut;
543 
544 	bOut = (byte *)out;
545 	for (i=0 ; i<256 ; i++) {
546 		bOut[i*4+0] = in[i*3+0];
547 		bOut[i*4+1] = in[i*3+1];
548 		bOut[i*4+2] = in[i*3+2];
549 		bOut[i*4+3] = 255;
550 	}
551 }
552 
553 
554 /*
555 ==================
556 CIN_ReadNextFrame
557 ==================
558 */
CIN_ReadNextFrame(void)559 static byte *CIN_ReadNextFrame (void)
560 {
561 	int			command;
562 	int			size;
563 	huffBlock_t	in, out;
564 	int			start, end, samples;
565 	byte		palette[768];
566 
567 	// Read the next frame
568 	FS_Read (&command, sizeof (command), cl.cin.fileNum);
569 	command = LittleLong (command);
570 	switch (command) {
571 	case 2:
572 		// Last frame marker
573 		return NULL;
574 
575 	case 1:
576 		// Read palette
577 		FS_Read (palette, sizeof (palette), cl.cin.fileNum);
578 		CIN_ResamplePalette (palette, cl.cin.hPalette);
579 		break;
580 	}
581 
582 	// Decompress the next frame
583 	FS_Read (&size, 4, cl.cin.fileNum);
584 	size = LittleLong (size);
585 	if (size > MAX_CIN_HBUFFER || size < 1)
586 		Com_Error (ERR_DROP, "Bad compressed frame size");
587 	FS_Read (cl.cin.hBuffer, size, cl.cin.fileNum);
588 
589 	// Read sound
590 	start = cl.cin.frameNum * cl.cin.sndRate / 14;
591 	end = (cl.cin.frameNum+1) * cl.cin.sndRate / 14;
592 	samples = end - start;
593 
594 	// FIXME: HACK: disgusting, but necessary for sync with OpenAL
595 	if (!cl.cin.frameNum && cl.cin.sndAL) {
596 		samples += 4096;
597 		if (cl.cin.sndWidth == 2)
598 			memset (cl.cin.sndBuffer, 0x00, 4096*cl.cin.sndWidth*cl.cin.sndChannels);
599 		else
600 			memset (cl.cin.sndBuffer, 0x80, 4096*cl.cin.sndWidth*cl.cin.sndChannels);
601 		FS_Read (cl.cin.sndBuffer+(4096*cl.cin.sndWidth*cl.cin.sndChannels), (samples-4096)*cl.cin.sndWidth*cl.cin.sndChannels, cl.cin.fileNum);
602 	}
603 	else
604 		FS_Read (cl.cin.sndBuffer, samples*cl.cin.sndWidth*cl.cin.sndChannels, cl.cin.fileNum);
605 
606 	Snd_RawSamples (cl.cin.sndRawChannel, samples, cl.cin.sndRate, cl.cin.sndWidth, cl.cin.sndChannels, cl.cin.sndBuffer);
607 
608 	in.data = cl.cin.hBuffer;
609 	in.count = size;
610 
611 	out = CIN_DecompressFrame (in);
612 
613 	cl.cin.frameNum++;
614 	return out.data;
615 }
616 
617 /*
618 =============================================================================
619 
620 	PUBLIC FUNCTIONALITY
621 
622 =============================================================================
623 */
624 
625 /*
626 ==================
627 CIN_RunCinematic
628 ==================
629 */
CIN_RunCinematic(void)630 void CIN_RunCinematic (void)
631 {
632 	int		frame;
633 
634 	if (cl.cin.time <= 0) {
635 		CIN_StopCinematic ();
636 		return;
637 	}
638 
639 	if (cl.cin.frameNum == -1)
640 		return;		// Static image
641 
642 	frame = (cls.realTime - cl.cin.time)*14.0f/1000;
643 	if (frame <= cl.cin.frameNum)
644 		return;
645 
646 	if (frame > cl.cin.frameNum+1) {
647 		Com_Printf (PRNT_WARNING, "Dropped frame: %i > %i\n", frame, cl.cin.frameNum+1);
648 		cl.cin.time = cls.realTime - cl.cin.frameNum*1000/14;
649 	}
650 
651 	if (cl.cin.frames[0])
652 		Mem_Free (cl.cin.frames[0]);
653 
654 	cl.cin.frames[0] = cl.cin.frames[1];
655 	cl.cin.frames[1] = CIN_ReadNextFrame ();
656 
657 	if (!cl.cin.frames[1]) {
658 		CIN_StopCinematic ();
659 		CIN_FinishCinematic ();
660 
661 		SCR_BeginLoadingPlaque ();
662 		return;
663 	}
664 }
665 
666 
667 /*
668 ==================
669 CIN_DrawCinematic
670 ==================
671 */
CIN_DrawCinematic(void)672 void CIN_DrawCinematic (void)
673 {
674 	uint32		*dest;
675 	int			i, j, outRows, row;
676 	int			frac, fracStep;
677 	float		hScale;
678 	byte		*source;
679 
680 	// Fill the background with black
681 	CL_DrawFill (0, 0, cls.refConfig.vidWidth, cls.refConfig.vidHeight, Q_colorBlack);
682 
683 	// No cinematic image to render!
684 	if (!cl.cin.frames[0])
685 		return;
686 
687 	// Resample
688 	// FIXME: only do this if 1) needed and 2) this frame isn't the last one (high framerate situation)
689 	memset (cl.cin.vidBuffer, 0, sizeof(cl.cin.vidBuffer));
690 	if (cl.cin.height <= 256) {
691 		hScale = 1;
692 		outRows = cl.cin.height;
693 	}
694 	else {
695 		hScale = cl.cin.height/256.0f;
696 		outRows = 256;
697 	}
698 
699 	for (i=0 ; i<outRows ; i++) {
700 		row = (int)(i*hScale);
701 		if (row > cl.cin.height)
702 			break;
703 
704 		source = cl.cin.frames[0] + cl.cin.width*row;
705 		dest = &cl.cin.vidBuffer[i*256];
706 		fracStep = cl.cin.width*0x10000/256;
707 		frac = fracStep >> 1;
708 
709 		for (j=0 ; j<256 ; j++) {
710 			dest[j] = cl.cin.hPalette[source[frac>>16]];
711 			frac += fracStep;
712 		}
713 	}
714 
715 	// Update the texture
716 	if (R_UpdateTexture ("***r_cinTexture***", (byte *)cl.cin.vidBuffer, 256, 256))
717 		R_DrawPic (clMedia.cinMaterial, 0, 0, 0, cls.refConfig.vidWidth, cls.refConfig.vidHeight, 0, 0, 1, cl.cin.height*hScale/256, Q_colorWhite);
718 }
719 
720 
721 /*
722 ==================
723 CIN_PlayCinematic
724 ==================
725 */
CIN_PlayCinematic(char * name)726 void CIN_PlayCinematic (char *name)
727 {
728 	byte	*palette;
729 	char	bareName[MAX_OSPATH];
730 	char	loadName[MAX_OSPATH];
731 	int		old_khz;
732 
733 	// Check the name
734 	if (!name || !name[0])
735 		Com_Error (ERR_DROP, "CIN_PlayCinematic: NULL name!\n");
736 	if (strlen(name) >= MAX_OSPATH)
737 		Com_Error (ERR_DROP, "CIN_PlayCinematic: name length exceeds MAX_OSPATH!\n");
738 
739 	// Normalize, strip
740 	Com_NormalizePath (bareName, sizeof(bareName), name);
741 	Com_StripExtension (bareName, sizeof(bareName), bareName);
742 
743 	// Make sure CD isn't playing music
744 	CDAudio_Stop ();
745 
746 	cl.cin.frameNum = 0;
747 
748 	// Static pcx image
749 	Q_snprintfz (loadName, sizeof(loadName), "pics/%s.pcx", bareName);
750 	if (FS_FileExists (loadName) != -1) {
751 		cl.cin.frameNum = -1;
752 		cl.cin.time = 1;
753 
754 		if (!CIN_LoadPCX (loadName, &cl.cin.frames[0], &palette, &cl.cin.width, &cl.cin.height) || !cl.cin.frames[0]) {
755 			cl.cin.time = 0;
756 			return;
757 		}
758 
759 		SCR_EndLoadingPlaque ();
760 		CL_SetState (CA_ACTIVE);
761 
762 		CIN_ResamplePalette (palette, cl.cin.hPalette);
763 		Mem_Free (palette);
764 		return;
765 	}
766 
767 	// Load the video
768 	Q_snprintfz (loadName, sizeof(loadName), "video/%s.cin", bareName);
769 	FS_OpenFile (loadName, &cl.cin.fileNum, FS_MODE_READ_BINARY);
770 	if (!cl.cin.fileNum) {
771 		Com_Printf (PRNT_WARNING, "CIN_PlayCinematic: %s: not found!\n", loadName);
772 		CIN_FinishCinematic ();
773 		cl.cin.time = 0;
774 		return;
775 	}
776 
777 	// Set the prep state
778 	SCR_EndLoadingPlaque ();
779 	CL_SetState (CA_ACTIVE);
780 
781 	// Read and byte swap values
782 	FS_Read (&cl.cin.width, 4, cl.cin.fileNum);
783 	FS_Read (&cl.cin.height, 4, cl.cin.fileNum);
784 	cl.cin.width = LittleLong (cl.cin.width);
785 	cl.cin.height = LittleLong (cl.cin.height);
786 
787 	FS_Read (&cl.cin.sndRate, 4, cl.cin.fileNum);
788 	FS_Read (&cl.cin.sndWidth, 4, cl.cin.fileNum);
789 	FS_Read (&cl.cin.sndChannels, 4, cl.cin.fileNum);
790 	cl.cin.sndRate = LittleLong (cl.cin.sndRate);
791 	cl.cin.sndWidth = LittleLong (cl.cin.sndWidth);
792 	cl.cin.sndChannels = LittleLong (cl.cin.sndChannels);
793 
794 	// Setup the streaming channel
795 	cl.cin.sndBuffer = Mem_PoolAllocExt (MAX_CIN_SNDBUFF, qFalse, cl_cinSysPool, 0);
796 	cl.cin.sndRawChannel = Snd_RawStart ();
797 	cl.cin.sndAL = (cl.cin.sndRawChannel) ? qTrue : qFalse;
798 	cl.cin.vidBuffer = Mem_PoolAllocExt (256*256*sizeof(uint32), qFalse, cl_cinSysPool, 0);
799 
800 	// Setup the huff table
801 	CIN_TableInit ();
802 
803 	// Switch up to 22 khz sound if necessary
804 	if (!cl.cin.sndAL) {
805 		old_khz = Cvar_GetIntegerValue ("s_khz");
806 		if (old_khz != cl.cin.sndRate/1000) {
807 			cl.cin.sndRestart = qTrue;
808 			Cvar_VariableSetValue (s_khz, cl.cin.sndRate/1000, qTrue);
809 			Cbuf_AddText ("snd_restart\n");
810 			Cvar_VariableSetValue (s_khz, old_khz, qTrue);
811 		}
812 	}
813 
814 	// Start
815 	cl.cin.frameNum = 0;
816 	cl.cin.frames[0] = CIN_ReadNextFrame ();
817 	cl.cin.time = Sys_Milliseconds ();
818 }
819 
820 
821 /*
822 ==================
823 CIN_StopCinematic
824 ==================
825 */
CIN_StopCinematic(void)826 void CIN_StopCinematic (void)
827 {
828 	// Stop streaming
829 	Snd_RawStop (cl.cin.sndRawChannel);
830 
831 	// Release memory
832 	Mem_FreePool (cl_cinSysPool);
833 
834 	// Close the file
835 	if (cl.cin.fileNum)
836 		FS_CloseFile (cl.cin.fileNum);
837 
838 	// Switch sound back if necessary
839 	if (cl.cin.sndRestart)
840 		Cbuf_AddText ("snd_restart\n");
841 
842 	memset (&cl.cin, 0, sizeof (cinematic_t));
843 }
844 
845 
846 /*
847 ====================
848 CIN_FinishCinematic
849 
850 Called when either the cinematic completes, or it is aborted
851 ====================
852 */
CIN_FinishCinematic(void)853 void CIN_FinishCinematic (void)
854 {
855 	// Tell the server to advance to the next map / cinematic
856 	MSG_WriteByte (&cls.netChan.message, CLC_STRINGCMD);
857 	MSG_WriteStringCat (&cls.netChan.message, Q_VarArgs ("nextserver %i\n", cl.serverCount));
858 
859 	CL_SetState (CA_CONNECTED);
860 }
861