]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mveplayer.cpp
Update to newest movie code with much better video support and audio support from
[taylor/freespace2.git] / src / movie / mveplayer.cpp
1 /*
2  * $Logfile: /Freespace2/code/movie/mveplayer.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * MVE movie playing routines
8  *
9  * $Log$
10  * Revision 1.3  2005/03/29 07:50:34  taylor
11  * Update to newest movie code with much better video support and audio support from
12  *   Pierre Willenbrock.  Movies are enabled always now (no longer a build option)
13  *   and but can be skipped with the "--nomovies" or "-n" cmdline options.
14  *
15  *
16  * $NoKeywords: $
17  */
18  
19 #ifdef PLAT_UNIX
20 #ifdef __APPLE__
21 #include <OpenGL/gl.h>
22 #include <al.h>
23 #include <alc.h>
24 #include <alut.h>
25 #else
26 #include <GL/gl.h>
27 #include <AL/al.h>
28 #include <AL/alc.h>
29 #include <AL/alut.h>
30 #endif  // __APPLE__
31 #endif  // PLAT_UNIX
32
33 #include "pstypes.h"
34 #include "mvelib.h"
35 #include "movie.h"
36 #include "2d.h"
37 #include "key.h"
38 #include "osapi.h"
39 #include "timer.h"
40 #include "sound.h"
41 #include "bmpman.h"
42
43
44 static int mve_playing;
45
46 // timer variables
47 static int g_spdFactorNum = 0;
48 static int g_spdFactorDenom = 10;
49 static int micro_frame_delay = 0;
50 static int timer_started = 0;
51 static fix timer_expire;
52
53 // audio variables
54 #define MVE_AUDIO_BUFFERS 64  // total buffers to interact with stream
55 static int mve_audio_curbuf_curpos = 0;
56 static int mve_audio_bufhead = 0;
57 static int mve_audio_buftail = 0;
58 static int mve_audio_playing = 0;
59 static int mve_audio_canplay = 0;
60 static int mve_audio_compressed = 0;
61 static int audiobuf_created;
62
63 #ifdef PLAT_UNIX
64
65 // struct for the audio stream information
66 typedef struct MVE_AUDIO_T {
67         ALenum format;
68         int sample_rate;
69         int bytes_per_sec;
70         int channels;
71         int bitsize;
72         ALuint audio_data[MVE_AUDIO_BUFFERS];
73         ALuint source_id;
74         ALuint audio_buffer[MVE_AUDIO_BUFFERS];
75 } mve_audio_t;
76
77 mve_audio_t *mas;  // mve_audio_stream
78
79 #ifndef NDEBUG
80 #define OpenAL_ErrorCheck(errcode)      do {            \
81         int i = alGetError();                   \
82         if (i != AL_NO_ERROR) {                 \
83                 while(i != AL_NO_ERROR) {       \
84                         fprintf(stderr, "%s/%s:%d - OpenAL error %s\n", __FUNCTION__, __FILE__, __LINE__, alGetString(i)); \
85                         i = alGetError();       \
86                 }                               \
87                 errcode;                        \
88         }                                       \
89 } while (0);
90 #else
91 #define OpenAL_ErrorCheck(errocode)
92 #endif  // !NDEBUG
93
94 #endif  // PLAT_UNIX
95
96
97 // video variables
98 int g_width, g_height;
99 void *g_vBuffers = NULL;
100 void *g_vBackBuf1, *g_vBackBuf2;
101 ushort *pixelbuf = NULL;
102 static int g_screenWidth, g_screenHeight;
103 static ubyte g_palette[768];
104 static ubyte *g_pCurMap=NULL;
105 static int g_nMapLength=0;
106 static int videobuf_created, video_inited;
107 static int hp2, wp2;
108 static uint mve_video_skiptimer = 0;
109 #ifdef PLAT_UNIX
110 static GLuint tex = 0;
111 #endif
112
113 // the decoder
114 void decodeFrame16(ubyte *pFrame, ubyte *pMap, int mapRemain, ubyte *pData, int dataRemain);
115
116 /*************************
117  * general handlers
118  *************************/
119 void mve_end_movie()
120 {
121         mve_playing = 0;
122 }
123
124 /*************************
125  * timer handlers
126  *************************/
127
128 int mve_timer_create(ubyte *data)
129 {
130         longlong temp;
131
132         micro_frame_delay = mve_get_int(data) * (int)mve_get_short(data+4);
133
134         if (g_spdFactorNum != 0) {
135                 temp = micro_frame_delay;
136                 temp *= g_spdFactorNum;
137                 temp /= g_spdFactorDenom;
138                 micro_frame_delay = (int)temp;
139         }
140
141         return 1;
142 }
143
144 static void timer_start(void)
145 {
146         timer_expire = timer_get_microseconds();
147         timer_expire += micro_frame_delay;
148
149         timer_started=1;
150 }
151
152 static void do_timer_wait(void)
153 {
154         fix tv, ts, ts2;
155
156         tv = timer_get_microseconds();
157
158         if (tv > timer_expire)
159                 goto end;
160
161         ts = timer_expire - tv;
162
163         ts2 = ts/1000;
164
165         Sleep(ts2);
166
167 end:
168         timer_expire += micro_frame_delay;
169 }
170
171 /*************************
172  * audio handlers
173  *************************/
174
175 // setup the audio information from the data stream
176 void mve_audio_createbuf(ubyte minor, ubyte *data)
177 {
178         if (audiobuf_created)
179                 return;
180
181         // if game sound disabled don't try and play movie audio
182         if (!Sound_enabled) {
183                 mve_audio_canplay = 0;
184                 return;
185         }
186
187 #ifdef PLAT_UNIX
188     int i, flags, desired_buffer, sample_rate;
189
190     mas = (mve_audio_t *) malloc ( sizeof(mve_audio_t) );
191         memset(mas, 0, sizeof(mve_audio_t));
192
193         mas->format = AL_INVALID;
194
195     flags = mve_get_ushort(data + 2);
196     sample_rate = mve_get_ushort(data + 4);
197     desired_buffer = mve_get_int(data + 6);
198
199     mas->channels = (flags & 0x0001) ? 2 : 1;
200         mas->bitsize = (flags & 0x0002) ? 16 : 8;
201
202         mas->sample_rate = sample_rate;
203
204     if (minor > 0) {
205         mve_audio_compressed = flags & 0x0004 ? 1 : 0;
206     } else {
207                 mve_audio_compressed = 0;
208     }
209
210     if (mas->bitsize == 16) {
211                 if (mas->channels == 2) {
212                         mas->format = AL_FORMAT_STEREO16;
213                 } else if (mas->channels == 1) {
214                         mas->format = AL_FORMAT_MONO16;
215                 }
216         } else if (mas->bitsize == 8) {
217                 if (mas->channels == 2) {
218                         mas->format = AL_FORMAT_STEREO8;
219                 } else if (mas->channels == 1) {
220                         mas->format = AL_FORMAT_MONO8;
221                 }
222         }
223
224         // somethings wrong, bail now
225         if (mas->format == AL_INVALID) {
226                 mve_audio_canplay = 0;
227                 audiobuf_created = 1;
228                 return;
229         }
230
231     alGenSources(1, &mas->source_id);
232
233     if ((i = alGetError()) == AL_NO_ERROR) {
234                 mve_audio_canplay = 1;
235         } else {
236         mve_audio_canplay = 0;
237     }
238
239         alSourcef(mas->source_id, AL_GAIN, 1.0f);
240         alSource3f(mas->source_id, AL_POSITION, 0.0f, 0.0f, 0.0f);
241         alSource3f(mas->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
242         alSource3f(mas->source_id, AL_DIRECTION, 0.0f, 0.0f, 0.0f);
243         alSourcef(mas->source_id, AL_ROLLOFF_FACTOR, 0.0f );
244         alSourcei(mas->source_id, AL_SOURCE_RELATIVE, AL_TRUE );
245
246         memset(mas->audio_buffer, 0, MVE_AUDIO_BUFFERS * sizeof(ALuint));
247
248     mve_audio_bufhead = 0;
249     mve_audio_buftail = 0;
250
251         audiobuf_created = 1;
252 #endif
253 }
254
255 // play and stream the audio
256 void mve_audio_play()
257 {
258 #ifdef PLAT_UNIX
259         if (mve_audio_canplay) {
260                 ALint status, bqueued;
261
262                 alGetSourceiv(mas->source_id, AL_SOURCE_STATE, &status);
263
264                 OpenAL_ErrorCheck(return);
265         
266                 alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
267
268                 OpenAL_ErrorCheck(return);
269         
270                 mve_audio_playing = 1;
271
272                 if (status != AL_PLAYING && bqueued > 0) {
273                         alSourcePlay(mas->source_id);
274
275                         OpenAL_ErrorCheck(return);
276                 }
277         }
278 #endif
279 }
280
281 // call this in shutdown to stop and close audio
282 static void mve_audio_stop()
283 {
284         if (!audiobuf_created)
285                 return;
286
287 #ifdef PLAT_UNIX
288         mve_audio_playing = 0;
289
290         alSourceStop(mas->source_id);
291         alSourceUnqueueBuffers(mas->source_id, MVE_AUDIO_BUFFERS, mas->audio_buffer);
292         alDeleteBuffers(MVE_AUDIO_BUFFERS, mas->audio_buffer);
293         alDeleteSources(1, &mas->source_id);
294
295         if (mas != NULL) {
296                 free(mas);
297                 mas = NULL;
298         }
299 #endif
300 }
301
302 int mve_audio_data(ubyte major, ubyte *data)
303 {
304 #ifdef PLAT_UNIX
305         static const int selected_chan=1;
306         int chan;
307         int nsamp;
308
309         if (mve_audio_canplay) {
310                 chan = mve_get_ushort(data + 2);
311                 nsamp = mve_get_ushort(data + 4);
312
313                 if (chan & selected_chan) {
314                         ALint bprocessed, bqueued, status;
315                         ALuint bid;
316
317                         alGetSourcei(mas->source_id, AL_BUFFERS_PROCESSED, &bprocessed);
318
319                         OpenAL_ErrorCheck(return 0);
320
321                         while (bprocessed-- > 2) {
322                                 alSourceUnqueueBuffers(mas->source_id, 1, &bid);
323                         //      fprintf(stderr,"Unqueued buffer %d(%d)\n", mve_audio_bufhead, bid);
324                 
325                                 if (++mve_audio_bufhead == MVE_AUDIO_BUFFERS)
326                                         mve_audio_bufhead = 0;
327                         }
328
329                         alGetSourcei(mas->source_id, AL_BUFFERS_QUEUED, &bqueued);
330
331                         OpenAL_ErrorCheck(return 0);
332                     
333                         if (bqueued == 0) 
334                                 mprintf(("MVE: Buffer underun (First is normal)\n"));
335
336                         alGetSourceiv(mas->source_id, AL_SOURCE_STATE, &status);
337
338                         OpenAL_ErrorCheck(return 0);
339
340                         if (mve_audio_playing && status != AL_PLAYING && bqueued > 0) {
341                                 alSourcePlay(mas->source_id);
342
343                                 OpenAL_ErrorCheck(return 0);
344                         }
345
346                         if (bqueued < MVE_AUDIO_BUFFERS) {
347                                 short *buf = NULL;
348
349                                 /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
350                                 if (major == 8) {
351                                     if (mve_audio_compressed) {
352                                                 nsamp += 4;
353
354                                                 buf = (short *)malloc(nsamp);
355                                                 mveaudio_uncompress(buf, data, -1); /* XXX */
356                                         } else {
357                                                 nsamp -= 8;
358                                                 data += 8;
359
360                                                 buf = (short *)malloc(nsamp);
361                                                 memcpy(buf, data, nsamp);
362                                         }                     
363                                 } else {
364                                         buf = (short *)malloc(nsamp);
365
366                                         memset(buf, 0, nsamp); /* XXX */
367                                 }
368
369
370                                 if (!mas->audio_buffer[mve_audio_buftail]) {
371                                         alGenBuffers(1,&mas->audio_buffer[mve_audio_buftail]);
372
373                                         OpenAL_ErrorCheck( {free(buf); return 0;} );
374                                 }
375
376                                 alBufferData(mas->audio_buffer[mve_audio_buftail], mas->format, buf, nsamp, mas->sample_rate);
377
378                                 OpenAL_ErrorCheck( {free(buf); return 0;} );
379             
380                                 alSourceQueueBuffers(mas->source_id, 1, &mas->audio_buffer[mve_audio_buftail]);
381
382                         //      fprintf(stderr,"Queued buffer %d(%d)\n", mve_audio_buftail, mas->audio_buffer[mve_audio_buftail]);
383                                 OpenAL_ErrorCheck( {free(buf); return 0;} );
384
385                                 if (++mve_audio_buftail == MVE_AUDIO_BUFFERS)
386                                         mve_audio_buftail = 0;
387
388                                 bqueued++;
389                                 free(buf);
390                         } else {
391                                 mprintf(("MVE: Buffer overrun: Queue full\n"));
392                         }
393
394                 //      fprintf(stderr,"Buffers queued: %d\n", bqueued);
395                 }
396         }
397 #endif
398
399         return 1;
400 }
401
402 /*************************
403  * video handlers
404  *************************/
405
406 int mve_video_createbuf(ubyte minor, ubyte *data)
407 {
408         if (videobuf_created)
409                 return 1;
410
411         short w, h;
412         short count, truecolor;
413         w = mve_get_short(data);
414         h = mve_get_short(data+2);
415         
416         if (minor > 0) {
417                 count = mve_get_short(data+4);
418         } else {
419                 count = 1;
420         }
421         
422         if (minor > 1) {
423                 truecolor = mve_get_short(data+6);
424         } else {
425                 truecolor = 0;
426         }
427
428         g_width = w << 3;
429         g_height = h << 3;
430
431         /* TODO: * 4 causes crashes on some files */
432         g_vBackBuf1 = g_vBuffers = malloc(g_width * g_height * 8);
433
434         if (g_vBackBuf1 == NULL) {
435                 mprintf(("MOVIE", "ERROR: Can't allocate video buffer"));
436                 videobuf_created = 1;
437                 return 0;
438         }
439
440         g_vBackBuf2 = (ushort *)g_vBackBuf1 + (g_width * g_height);
441                 
442         memset(g_vBackBuf1, 0, g_width * g_height * 8);
443
444         videobuf_created = 1;
445
446         return 1;
447 }
448
449 static void mve_convert_and_draw()
450 {
451         ushort *pDests;
452         ushort *pSrcs;
453         ushort *pixels = (ushort *)g_vBackBuf1;
454         int x, y;
455
456         pSrcs = pixels;
457
458         pDests = pixelbuf;
459
460         if (g_screenWidth > g_width) {
461                 pDests += ((g_screenWidth - g_width) / 2) / 2;
462         }
463         if (g_screenHeight > g_height) {
464                 pDests += ((g_screenHeight - g_height) / 2) * g_screenWidth;
465         }
466
467         for (y=0; y<g_height; y++) {
468                 for (x = 0; x < g_width; x++) {
469                         pDests[x] = (1<<15)|*pSrcs;
470
471                         pSrcs++;
472                 }
473                 pDests += g_screenWidth;
474         }
475 }
476
477 void mve_video_display()
478 {
479         fix t1 = timer_get_fixed_seconds();
480         mve_convert_and_draw();
481         
482         // centers on 1024x768, fills on 640x480
483         int x = ((gr_screen.max_w - g_screenWidth) / 2);
484         int y = ((gr_screen.max_h - g_screenHeight) / 2);
485         int h = g_screenHeight;
486         int w = g_screenWidth;
487
488 #ifdef PLAT_UNIX
489         // micro_frame_delay is divided by 10 to match mve_video_skiptimer overflow catch
490         if ( mve_video_skiptimer > (uint)(micro_frame_delay/10) ) {
491                 // we are running slow so subtract desired time from actual and skip this frame
492                 mve_video_skiptimer -= (micro_frame_delay/10);
493                 return;
494         } else {
495                 // zero out so we can get a new count
496                 mve_video_skiptimer = 0;
497         }
498
499         glBindTexture(GL_TEXTURE_2D, tex);
500         
501         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, pixelbuf);
502
503         // 0, 0
504         glBegin(GL_QUADS);
505                 glTexCoord2f(0,0);                                                                              glVertex2i(x,y);
506                 glTexCoord2f(0,i2fl(256)/i2fl(hp2));                                    glVertex2i(x,y+256);
507                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
508                 glTexCoord2f(i2fl(256)/i2fl(wp2),0);                                    glVertex2i(x+256,y);
509         glEnd();
510
511         // 0, 256
512         glBegin(GL_QUADS);
513                 glTexCoord2f(0,i2fl(256)/i2fl(hp2));                                    glVertex2i(x,y+256);
514                 glTexCoord2f(0,i2fl(h)/i2fl(hp2));                                              glVertex2i(x,y+h);
515                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+256,y+h);
516                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
517         glEnd();
518
519         // 256, 0
520         glBegin(GL_QUADS);
521                 glTexCoord2f(i2fl(256)/i2fl(wp2),0);                                    glVertex2i(x+256,y);
522                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
523                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
524                 glTexCoord2f(i2fl(512)/i2fl(wp2),0);                                    glVertex2i(x+512,y);
525         glEnd();
526
527         // 256, 256
528         glBegin(GL_QUADS);
529                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+256,y+256);
530                 glTexCoord2f(i2fl(256)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+256,y+h);
531                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+512,y+h);
532                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
533         glEnd();
534
535         // 512, 0
536         glBegin(GL_QUADS);
537                 glTexCoord2f(i2fl(512)/i2fl(wp2),0);                                    glVertex2i(x+512,y);
538                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
539                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2));    glVertex2i(x+w,y+256);
540                 glTexCoord2f(i2fl(w)/i2fl(wp2),0);                                              glVertex2i(x+w,y);
541         glEnd();
542
543         // 512, 256
544         glBegin(GL_QUADS);
545                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(256)/i2fl(hp2));  glVertex2i(x+512,y+256);
546                 glTexCoord2f(i2fl(512)/i2fl(wp2),i2fl(h)/i2fl(hp2));    glVertex2i(x+512,y+h);
547                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(h)/i2fl(hp2));              glVertex2i(x+w,y+h);
548                 glTexCoord2f(i2fl(w)/i2fl(wp2),i2fl(256)/i2fl(hp2));    glVertex2i(x+w,y+256);
549         glEnd();
550 #else 
551         // DDOI - This is probably really fricking slow
552         int bitmap = bm_create (16, w, h, pixelbuf, 0);
553         gr_set_bitmap (bitmap);
554         gr_bitmap (x, y);
555         bm_release (bitmap);
556 #endif
557
558         gr_flip ();
559 #ifdef PLAT_UNIX
560         os_poll ();     /* DDOI - run event loop(s) */
561 #endif
562
563         fix t2 = timer_get_fixed_seconds();
564
565         // only get a new count if we are definitely through with old count
566         if ( mve_video_skiptimer == 0 ) {
567                 // for a more accurate count convert the frame rate to a float and multiply
568                 // by one-hundred-thousand before converting to an uint.
569                 mve_video_skiptimer = (uint)(f2fl(t2-t1) * 100000);
570         }
571
572         int k = key_inkey();
573         if ( k == KEY_ESC ) {
574                 mve_playing = 0;
575         }
576
577 //      fprintf(stderr, "mve frame took this long: %.6f\n", f2fl(t2-t1));
578 }
579
580 int mve_video_init(ubyte *data)
581 {
582         if (video_inited)
583                 return 1;
584
585         short width, height;
586
587         width = mve_get_short(data);
588         height = mve_get_short(data+2);
589
590         // DDOI - Allocate RGB565 pixel buffer
591         pixelbuf = (ushort *)malloc (width * height * 2);
592
593         if (pixelbuf == NULL) {
594                 mprintf(("MOVIE", "ERROR: Can't allocate memory for pixelbuf"));
595                 video_inited = 1;
596                 return 0;
597         }
598
599         memset(pixelbuf, 0, width * height * 2);
600
601         g_screenWidth = width;
602         g_screenHeight = height;
603
604 #ifdef PLAT_UNIX
605         int i, tex_w, tex_h;
606
607         tex_w = g_screenWidth;
608         tex_h = g_screenHeight;
609
610         // set height and width to a power of 2
611         for (i=0; i<16; i++ )   {
612                 if ( (tex_w > (1<<i)) && (tex_w <= (1<<(i+1))) )        {
613                         tex_w = 1 << (i+1);
614                         break;
615                 }
616         }
617
618         for (i=0; i<16; i++ )   {
619                 if ( (tex_h > (1<<i)) && (tex_h <= (1<<(i+1))) )        {
620                         tex_h = 1 << (i+1);
621                         break;
622                 }
623         }
624
625         // try to keep an 8:1 size ratio
626         if (tex_w/tex_h > 8)
627                 tex_h = tex_w/8;
628         if (tex_h/tex_w > 8)
629                 tex_w = tex_h/8;
630
631         wp2 = tex_w;
632         hp2 = tex_h;
633
634         glGenTextures(1, &tex);
635
636         Assert(tex != 0);
637
638         if ( tex == 0 ) {
639                 mprintf(("MOVIE", "ERROR: Can't create a GL texture"));
640                 video_inited = 1;
641                 return 0;
642         }
643
644         glBindTexture(GL_TEXTURE_2D, tex);
645         
646         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
647         glDepthFunc(GL_ALWAYS);
648         glDepthMask(GL_FALSE);
649         glDisable(GL_DEPTH_TEST);
650         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
651         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
652
653         // NOTE: using NULL instead of pixelbuf crashes some drivers, but then so does pixelbuf so less of two evils...
654         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, wp2, hp2, 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
655 #endif
656
657         memset(g_palette, 0, 768);
658         
659         video_inited = 1;
660         
661         return 1;
662 }
663
664 void mve_video_palette(ubyte *data)
665 {
666         short start, count;
667         start = mve_get_short(data);
668         count = mve_get_short(data+2);
669         memcpy(g_palette + 3*start, data+4, 3*count);
670 }
671
672 void mve_video_codemap(ubyte *data, int len)
673 {
674         g_pCurMap = data;
675         g_nMapLength = len;
676 }
677
678 void mve_video_data(ubyte *data, int len)
679 {
680         short nFrameHot, nFrameCold;
681         short nXoffset, nYoffset;
682         short nXsize, nYsize;
683         ushort nFlags;
684         ubyte *temp;
685
686         nFrameHot = mve_get_short(data);
687         nFrameCold = mve_get_short(data+2);
688         nXoffset = mve_get_short(data+4);
689         nYoffset = mve_get_short(data+6);
690         nXsize = mve_get_short(data+8);
691         nYsize = mve_get_short(data+10);
692         nFlags = mve_get_ushort(data+12);
693
694         if (nFlags & 1) {
695                 temp = (ubyte *)g_vBackBuf1;
696                 g_vBackBuf1 = g_vBackBuf2;
697                 g_vBackBuf2 = temp;
698         }
699
700         decodeFrame16((ubyte *)g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
701 }
702
703 void mve_end_chunk()
704 {
705         g_pCurMap = NULL;
706 }
707
708 void mve_init(MVESTREAM *mve)
709 {
710         // reset to default values
711         mve_audio_curbuf_curpos = 0;
712         mve_audio_bufhead = 0;
713         mve_audio_buftail = 0;
714         mve_audio_playing = 0;
715         mve_audio_canplay = 0;
716         mve_audio_compressed = 0;
717         audiobuf_created = 0;
718
719         videobuf_created = 0;
720         video_inited = 0;
721
722         mve_playing = 1;
723 }
724
725 void mve_play(MVESTREAM *mve)
726 {
727         int init_timer = 0;
728         int cont = 1;
729         
730         if (micro_frame_delay && !init_timer) {
731                 timer_start();
732                 init_timer = 1;
733         }
734
735         while (cont && mve_playing) {
736                 cont = mve_play_next_chunk(mve);
737
738                 do_timer_wait();
739         }
740 }
741
742 void mve_shutdown()
743 {
744         mve_audio_stop();
745
746         if (pixelbuf != NULL) {
747                 free(pixelbuf);
748                 pixelbuf = NULL;
749         }
750
751         if (g_vBuffers != NULL) {
752                 free(g_vBuffers);
753                 g_vBuffers = NULL;
754         }
755
756 #ifdef PLAT_UNIX
757         glDeleteTextures(1, &tex);
758         tex = 0;
759         
760         glEnable(GL_DEPTH_TEST);
761 #endif
762 }