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