Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3
 * It is copyright by its individual contributors, as recorded in the
4
 * project's Git history.  See COPYING.txt at the top level for license
5
 * terms and a link to the Git history.
6
 */
7
//#define DEBUG
8
 
9
#include "dxxsconf.h"
10
#include <vector>
11
#include <string.h>
12
#include <time.h>
13
#include <sys/time.h>
14
#ifdef _WIN32
15
# include <windows.h>
16
#else
17
# include <errno.h>
18
# include <fcntl.h>
19
# ifdef macintosh
20
#  include <types.h>
21
#  include <OSUtils.h>
22
# else
23
#  include <sys/types.h>
24
#  include <sys/stat.h>
25
#  include <unistd.h>
26
# endif // macintosh
27
#endif // _WIN32
28
 
29
#include <SDL.h>
30
#if DXX_USE_SDLMIXER
31
#include <SDL_mixer.h>
32
#endif
33
 
34
#include "config.h"
35
#include "mvelib.h"
36
#include "mve_audio.h"
37
#include "byteutil.h"
38
#include "decoders.h"
39
#include "libmve.h"
40
#include "args.h"
41
#include "console.h"
42
#include "u_mem.h"
43
#include <memory>
44
 
45
#define MVE_OPCODE_ENDOFSTREAM          0x00
46
#define MVE_OPCODE_ENDOFCHUNK           0x01
47
#define MVE_OPCODE_CREATETIMER          0x02
48
#define MVE_OPCODE_INITAUDIOBUFFERS     0x03
49
#define MVE_OPCODE_STARTSTOPAUDIO       0x04
50
#define MVE_OPCODE_INITVIDEOBUFFERS     0x05
51
 
52
#define MVE_OPCODE_DISPLAYVIDEO         0x07
53
#define MVE_OPCODE_AUDIOFRAMEDATA       0x08
54
#define MVE_OPCODE_AUDIOFRAMESILENCE    0x09
55
#define MVE_OPCODE_INITVIDEOMODE        0x0A
56
 
57
#define MVE_OPCODE_SETPALETTE           0x0C
58
#define MVE_OPCODE_SETPALETTECOMPRESSED 0x0D
59
 
60
#define MVE_OPCODE_SETDECODINGMAP       0x0F
61
 
62
#define MVE_OPCODE_VIDEODATA            0x11
63
 
64
#define MVE_AUDIO_FLAGS_STEREO     1
65
#define MVE_AUDIO_FLAGS_16BIT      2
66
#define MVE_AUDIO_FLAGS_COMPRESSED 4
67
 
68
int g_spdFactorNum=0;
69
static int g_spdFactorDenom=10;
70
static int g_frameUpdated = 0;
71
 
72
static int16_t get_short(const unsigned char *data)
73
{
74
        short value;
75
        value = data[0] | (data[1] << 8);
76
        return value;
77
}
78
 
79
static uint16_t get_ushort(const unsigned char *data)
80
{
81
        unsigned short value;
82
        value = data[0] | (data[1] << 8);
83
        return value;
84
}
85
 
86
static int32_t get_int(const unsigned char *data)
87
{
88
        int value;
89
        value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
90
        return value;
91
}
92
 
93
/*************************
94
 * general handlers
95
 *************************/
96
static int end_movie_handler(unsigned char, unsigned char, const unsigned char *, int, void *)
97
{
98
        return 0;
99
}
100
 
101
/*************************
102
 * timer handlers
103
 *************************/
104
 
105
/*
106
 * timer variables
107
 */
108
static int timer_created = 0;
109
static int micro_frame_delay=0;
110
static int timer_started=0;
111
static struct timeval timer_expire = {0, 0};
112
 
113
#ifndef DXX_HAVE_STRUCT_TIMESPEC
114
struct timespec
115
{
116
        long int tv_sec;            /* Seconds.  */
117
        long int tv_nsec;           /* Nanoseconds.  */
118
};
119
#endif
120
 
121
#if defined(_WIN32) || defined(macintosh)
122
int gettimeofday(struct timeval *tv, void *)
123
{
124
        static int counter = 0;
125
#ifdef _WIN32
126
        DWORD now = GetTickCount();
127
#else
128
        long now = TickCount();
129
#endif
130
        counter++;
131
 
132
        tv->tv_sec = now / 1000;
133
        tv->tv_usec = (now % 1000) * 1000 + counter;
134
 
135
        return 0;
136
}
137
#endif //  defined(_WIN32) || defined(macintosh)
138
 
139
 
140
static int create_timer_handler(unsigned char, unsigned char, const unsigned char *data, int, void *)
141
{
142
 
143
#if !defined(_WIN32) && !defined(macintosh) // FIXME
144
        __extension__ long long temp;
145
#else
146
        long temp;
147
#endif
148
 
149
        if (timer_created)
150
                return 1;
151
        else
152
                timer_created = 1;
153
 
154
        micro_frame_delay = get_int(data) * static_cast<int>(get_short(data+4));
155
        if (g_spdFactorNum != 0)
156
        {
157
                temp = micro_frame_delay;
158
                temp *= g_spdFactorNum;
159
                temp /= g_spdFactorDenom;
160
                micro_frame_delay = static_cast<int>(temp);
161
        }
162
 
163
        return 1;
164
}
165
 
166
static void timer_stop(void)
167
{
168
        timer_expire.tv_sec = 0;
169
        timer_expire.tv_usec = 0;
170
        timer_started = 0;
171
}
172
 
173
static void timer_start(void)
174
{
175
        int nsec=0;
176
        gettimeofday(&timer_expire, NULL);
177
        timer_expire.tv_usec += micro_frame_delay;
178
        if (timer_expire.tv_usec > 1000000)
179
        {
180
                nsec = timer_expire.tv_usec / 1000000;
181
                timer_expire.tv_sec += nsec;
182
                timer_expire.tv_usec -= nsec*1000000;
183
        }
184
        timer_started=1;
185
}
186
 
187
static void do_timer_wait(void)
188
{
189
        int nsec=0;
190
        struct timespec ts;
191
        struct timeval tv;
192
        if (! timer_started)
193
                return;
194
 
195
        gettimeofday(&tv, NULL);
196
        if (tv.tv_sec > timer_expire.tv_sec)
197
                goto end;
198
        else if (tv.tv_sec == timer_expire.tv_sec  &&  tv.tv_usec >= timer_expire.tv_usec)
199
                goto end;
200
 
201
        ts.tv_sec = timer_expire.tv_sec - tv.tv_sec;
202
        ts.tv_nsec = 1000 * (timer_expire.tv_usec - tv.tv_usec);
203
        if (ts.tv_nsec < 0)
204
        {
205
                ts.tv_nsec += 1000000000UL;
206
                --ts.tv_sec;
207
        }
208
#ifdef _WIN32
209
        Sleep(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
210
#elif defined(macintosh)
211
        Delay(ts.tv_sec * 1000 + ts.tv_nsec / 1000000, NULL);
212
#else
213
        if (nanosleep(&ts, NULL) == -1  &&  errno == EINTR)
214
                exit(1);
215
#endif
216
 
217
 end:
218
        timer_expire.tv_usec += micro_frame_delay;
219
        if (timer_expire.tv_usec > 1000000)
220
        {
221
                nsec = timer_expire.tv_usec / 1000000;
222
                timer_expire.tv_sec += nsec;
223
                timer_expire.tv_usec -= nsec*1000000;
224
        }
225
}
226
 
227
/*************************
228
 * audio handlers
229
 *************************/
230
#define TOTAL_AUDIO_BUFFERS 64
231
 
232
namespace {
233
 
234
class MVE_audio_deleter
235
{
236
public:
237
        void operator()(int16_t *p) const
238
        {
239
                mve_free(p);
240
        }
241
};
242
 
243
template <typename T>
244
struct MVE_audio_clamp
245
{
246
        const unsigned scale;
247
        MVE_audio_clamp(const unsigned DigiVolume) :
248
                scale(DigiVolume)
249
        {
250
        }
251
        T operator()(const T &i) const
252
        {
253
                return (static_cast<int32_t>(i) * scale) / 8;
254
        }
255
};
256
 
257
}
258
 
259
static int audiobuf_created = 0;
260
static void mve_audio_callback(void *userdata, unsigned char *stream, int len);
261
static std::array<std::unique_ptr<short[], MVE_audio_deleter>, TOTAL_AUDIO_BUFFERS> mve_audio_buffers;
262
static std::array<unsigned, TOTAL_AUDIO_BUFFERS> mve_audio_buflens;
263
static int    mve_audio_curbuf_curpos=0;
264
static int mve_audio_bufhead=0;
265
static int mve_audio_buftail=0;
266
static int mve_audio_playing=0;
267
static int mve_audio_canplay=0;
268
static unsigned mve_audio_flags;
269
static int mve_audio_enabled = 1;
270
static std::unique_ptr<SDL_AudioSpec> mve_audio_spec;
271
 
272
static void mve_audio_callback(void *, unsigned char *stream, int len)
273
{
274
        int total=0;
275
        int length;
276
        if (mve_audio_bufhead == mve_audio_buftail)
277
                return /* 0 */;
278
 
279
        //con_printf(CON_CRITICAL, "+ <%d (%d), %d, %d>", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
280
 
281
        while (mve_audio_bufhead != mve_audio_buftail                                           /* while we have more buffers  */
282
                   &&  len > (mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos))        /* and while we need more data */
283
        {
284
                length = mve_audio_buflens[mve_audio_bufhead]-mve_audio_curbuf_curpos;
285
                memcpy(stream,                                                                  /* cur output position */
286
                       (reinterpret_cast<uint8_t *>(mve_audio_buffers[mve_audio_bufhead].get()))+mve_audio_curbuf_curpos,           /* cur input position  */
287
                       length);                                                                 /* cur input length    */
288
 
289
                total += length;
290
                stream += length;                                                               /* advance output */
291
                len -= length;                                                                  /* decrement avail ospace */
292
                mve_audio_buffers[mve_audio_bufhead].reset();                                 /* free the buffer */
293
                mve_audio_buflens[mve_audio_bufhead]=0;                                         /* free the buffer */
294
 
295
                if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS)                                 /* next buffer */
296
                        mve_audio_bufhead = 0;
297
                mve_audio_curbuf_curpos = 0;
298
        }
299
 
300
        //con_printf(CON_CRITICAL, "= <%d (%d), %d, %d>: %d", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len, total);
301
        /*    return total; */
302
 
303
        if (len != 0                                                                        /* ospace remaining  */
304
                &&  mve_audio_bufhead != mve_audio_buftail)                                     /* buffers remaining */
305
        {
306
                memcpy(stream,                                                                  /* dest */
307
                       (reinterpret_cast<uint8_t *>(mve_audio_buffers[mve_audio_bufhead].get()))+mve_audio_curbuf_curpos,         /* src */
308
                           len);                                                                    /* length */
309
 
310
                mve_audio_curbuf_curpos += len;                                                 /* advance input */
311
                stream += len;                                                                  /* advance output (unnecessary) */
312
                len -= len;                                                                     /* advance output (unnecessary) */
313
 
314
                if (mve_audio_curbuf_curpos >= mve_audio_buflens[mve_audio_bufhead])            /* if this ends the current chunk */
315
                {
316
                        mve_audio_buffers[mve_audio_bufhead].reset();                             /* free buffer */
317
                        mve_audio_buflens[mve_audio_bufhead]=0;
318
 
319
                        if (++mve_audio_bufhead == TOTAL_AUDIO_BUFFERS)                             /* next buffer */
320
                                mve_audio_bufhead = 0;
321
                        mve_audio_curbuf_curpos = 0;
322
                }
323
        }
324
 
325
        //con_printf(CON_CRITICAL, "- <%d (%d), %d, %d>", mve_audio_bufhead, mve_audio_curbuf_curpos, mve_audio_buftail, len);
326
}
327
 
328
static int create_audiobuf_handler(unsigned char, unsigned char minor, const unsigned char *data, int, void *)
329
{
330
        int flags;
331
        int sample_rate;
332
        int desired_buffer;
333
 
334
        if (!mve_audio_enabled)
335
                return 1;
336
 
337
        if (audiobuf_created)
338
                return 1;
339
        else
340
                audiobuf_created = 1;
341
 
342
        flags = get_ushort(data + 2);
343
        sample_rate = get_ushort(data + 4);
344
        desired_buffer = get_int(data + 6);
345
 
346
        const unsigned stereo = (flags & MVE_AUDIO_FLAGS_STEREO);
347
        const unsigned bitsize = (flags & MVE_AUDIO_FLAGS_16BIT);
348
        if (!minor)
349
                flags &= ~MVE_AUDIO_FLAGS_COMPRESSED;
350
        const unsigned compressed = flags & MVE_AUDIO_FLAGS_COMPRESSED;
351
        mve_audio_flags = flags;
352
 
353
        const unsigned format = (bitsize)
354
                ? (words_bigendian ? AUDIO_S16MSB : AUDIO_S16LSB)
355
                : AUDIO_U8;
356
 
357
        if (CGameArg.SndDisableSdlMixer)
358
        {
359
                con_puts(CON_CRITICAL, "creating audio buffers:");
360
                con_printf(CON_CRITICAL, "sample rate = %d, desired buffer = %d, stereo = %d, bitsize = %d, compressed = %d",
361
                                sample_rate, desired_buffer, stereo ? 1 : 0, bitsize ? 16 : 8, compressed ? 1 : 0);
362
        }
363
 
364
        mve_audio_spec = std::make_unique<SDL_AudioSpec>();
365
        mve_audio_spec->freq = sample_rate;
366
        mve_audio_spec->format = format;
367
        mve_audio_spec->channels = (stereo) ? 2 : 1;
368
        mve_audio_spec->samples = 4096;
369
        mve_audio_spec->callback = mve_audio_callback;
370
        mve_audio_spec->userdata = NULL;
371
 
372
        // MD2211: if using SDL_Mixer, we never reinit the sound system
373
        if (CGameArg.SndDisableSdlMixer)
374
        {
375
                if (SDL_OpenAudio(mve_audio_spec.get(), NULL) >= 0) {
376
                        con_puts(CON_CRITICAL, "   success");
377
                        mve_audio_canplay = 1;
378
                }
379
                else {
380
                        con_printf(CON_CRITICAL, "   failure : %s", SDL_GetError());
381
                        mve_audio_canplay = 0;
382
                }
383
        }
384
 
385
#if DXX_USE_SDLMIXER
386
        else {
387
                // MD2211: using the same old SDL audio callback as a postmixer in SDL_mixer
388
                Mix_SetPostMix(mve_audio_spec->callback, mve_audio_spec->userdata);
389
                mve_audio_canplay = 1;
390
        }
391
#endif
392
 
393
        mve_audio_buffers = {};
394
        mve_audio_buflens = {};
395
        return 1;
396
}
397
 
398
static int play_audio_handler(unsigned char, unsigned char, const unsigned char *, int, void *)
399
{
400
        if (mve_audio_canplay  &&  !mve_audio_playing  &&  mve_audio_bufhead != mve_audio_buftail)
401
        {
402
                if (CGameArg.SndDisableSdlMixer)
403
                        SDL_PauseAudio(0);
404
#if DXX_USE_SDLMIXER
405
                else
406
                        Mix_Pause(0);
407
#endif
408
                mve_audio_playing = 1;
409
        }
410
        return 1;
411
}
412
 
413
static int audio_data_handler(unsigned char major, unsigned char, const unsigned char *data, int, void *)
414
{
415
        static const int selected_chan=1;
416
        int chan;
417
        int nsamp;
418
        if (mve_audio_canplay)
419
        {
420
                if (mve_audio_playing)
421
                        SDL_LockAudio();
422
 
423
                chan = get_ushort(data + 2);
424
                nsamp = get_ushort(data + 4);
425
                if (chan & selected_chan)
426
                {
427
                        std::unique_ptr<int16_t[], MVE_audio_deleter> p;
428
                        const auto DigiVolume = GameCfg.DigiVolume;
429
                        /* HACK: +4 mveaudio_uncompress adds 4 more bytes */
430
                        /* At volume 0 (minimum), no sound is wanted. */
431
                        if (DigiVolume && major == MVE_OPCODE_AUDIOFRAMEDATA) {
432
                                const auto flags = mve_audio_flags;
433
                                if (flags & MVE_AUDIO_FLAGS_COMPRESSED) {
434
                                        nsamp += 4;
435
 
436
                                        p.reset(reinterpret_cast<int16_t *>(mve_alloc(nsamp)));
437
                                        mveaudio_uncompress(p.get(), data); /* XXX */
438
                                } else {
439
                                        nsamp -= 8;
440
                                        data += 8;
441
 
442
                                        p.reset(reinterpret_cast<int16_t *>(mve_alloc(nsamp)));
443
                                        memcpy(p.get(), data, nsamp);
444
                                }
445
                                if (DigiVolume < 8)
446
                                {
447
                                        /* At volume 8 (maximum), no scaling is needed. */
448
                                        if (flags & MVE_AUDIO_FLAGS_16BIT)
449
                                        {
450
                                                int16_t *const p16 = p.get();
451
                                                std::transform(p16, reinterpret_cast<int16_t *>(reinterpret_cast<uint8_t *>(p16) + nsamp), p16, MVE_audio_clamp<int16_t>(DigiVolume));
452
                                        }
453
                                        else
454
                                        {
455
                                                int8_t *const p8 = reinterpret_cast<int8_t *>(p.get());
456
                                                std::transform(p8, p8 + nsamp, p8, MVE_audio_clamp<int8_t>(DigiVolume));
457
                                        }
458
                                }
459
                        } else {
460
                                p.reset(reinterpret_cast<int16_t *>(mve_alloc(nsamp)));
461
                                memset(p.get(), 0, nsamp); /* XXX */
462
                        }
463
                        unsigned buflen = nsamp;
464
 
465
                        // MD2211: the following block does on-the-fly audio conversion for SDL_mixer
466
#if DXX_USE_SDLMIXER
467
                        if (!CGameArg.SndDisableSdlMixer) {
468
                                // build converter: in = MVE format, out = SDL_mixer output
469
                                int out_freq;
470
                                Uint16 out_format;
471
                                int out_channels;
472
                                Mix_QuerySpec(&out_freq, &out_format, &out_channels); // get current output settings
473
 
474
                                SDL_AudioCVT cvt{};
475
                                SDL_BuildAudioCVT(&cvt, mve_audio_spec->format, mve_audio_spec->channels, mve_audio_spec->freq,
476
                                        out_format, out_channels, out_freq);
477
 
478
                                RAIIdmem<uint8_t[]> cvtbuf;
479
                                MALLOC(cvtbuf, uint8_t[], nsamp * cvt.len_mult);
480
                                cvt.buf = cvtbuf.get();
481
                                cvt.len = nsamp;
482
 
483
                                // read the audio buffer into the conversion buffer
484
                                memcpy(cvt.buf, p.get(), nsamp);
485
 
486
                                // do the conversion
487
                                if (SDL_ConvertAudio(&cvt))
488
                                        con_printf(CON_URGENT, "%s:%u: SDL_ConvertAudio failed: nsamp=%u out_format=%i out_channels=%i out_freq=%i", __FILE__, __LINE__, nsamp, out_format, out_channels, out_freq);
489
                                else
490
                                {
491
                                // copy back to the audio buffer
492
                                        const std::size_t converted_buffer_size = cvt.len_cvt;
493
                                        p.reset(reinterpret_cast<int16_t *>(mve_alloc(converted_buffer_size))); // free the old audio buffer
494
                                        buflen = converted_buffer_size;
495
                                        memcpy(p.get(), cvt.buf, converted_buffer_size);
496
                                }
497
                        }
498
#endif
499
                        mve_audio_buffers[mve_audio_buftail] = std::move(p);
500
                        mve_audio_buflens[mve_audio_buftail] = buflen;
501
 
502
                        if (++mve_audio_buftail == TOTAL_AUDIO_BUFFERS)
503
                                mve_audio_buftail = 0;
504
 
505
                        if (mve_audio_buftail == mve_audio_bufhead)
506
                                con_printf(CON_CRITICAL, "d'oh!  buffer ring overrun (%d)", mve_audio_bufhead);
507
                }
508
 
509
                if (mve_audio_playing)
510
                        SDL_UnlockAudio();
511
        }
512
 
513
        return 1;
514
}
515
 
516
/*************************
517
 * video handlers
518
 *************************/
519
 
520
static int videobuf_created = 0;
521
static int video_initialized = 0;
522
int g_width, g_height;
523
static std::vector<unsigned char> g_vBuffers;
524
unsigned char *g_vBackBuf1, *g_vBackBuf2;
525
 
526
static int g_destX, g_destY;
527
static int g_screenWidth, g_screenHeight;
528
static const unsigned char *g_pCurMap;
529
static int g_nMapLength=0;
530
static int g_truecolor;
531
 
532
static int create_videobuf_handler(unsigned char, unsigned char minor, const unsigned char *data, int, void *)
533
{
534
        short w, h,
535
#ifdef DEBUG
536
                count,
537
#endif
538
                truecolor;
539
 
540
        if (videobuf_created)
541
                return 1;
542
        else
543
                videobuf_created = 1;
544
 
545
        w = get_short(data);
546
        h = get_short(data+2);
547
 
548
#ifdef DEBUG
549
        if (minor > 0) {
550
                count = get_short(data+4);
551
        } else {
552
                count = 1;
553
        }
554
#endif
555
 
556
        if (minor > 1) {
557
                truecolor = get_short(data+6);
558
        } else {
559
                truecolor = 0;
560
        }
561
 
562
        g_width = w << 3;
563
        g_height = h << 3;
564
 
565
        /* TODO: * 4 causes crashes on some files */
566
        /* only malloc once */
567
        g_vBuffers.assign(g_width * g_height * 8, 0);
568
        g_vBackBuf1 = &g_vBuffers[0];
569
        if (truecolor) {
570
                g_vBackBuf2 = reinterpret_cast<uint8_t *>(reinterpret_cast<uint16_t *>(g_vBackBuf1) + (g_width * g_height));
571
        } else {
572
                g_vBackBuf2 = (g_vBackBuf1 + (g_width * g_height));
573
        }
574
#ifdef DEBUG
575
        con_printf(CON_CRITICAL, "DEBUG: w,h=%d,%d count=%d, tc=%d", w, h, count, truecolor);
576
#endif
577
 
578
        g_truecolor = truecolor;
579
 
580
        return 1;
581
}
582
 
583
static int display_video_handler(unsigned char, unsigned char, const unsigned char *, int, void *)
584
{
585
        mve_showframe(g_vBackBuf1, g_destX, g_destY, g_width, g_height, g_screenWidth, g_screenHeight);
586
 
587
        g_frameUpdated = 1;
588
 
589
        return 1;
590
}
591
 
592
static int init_video_handler(unsigned char, unsigned char, const unsigned char *data, int, void *)
593
{
594
        short width, height;
595
 
596
        if (video_initialized)
597
                return 1; /* maybe we actually need to change width/height here? */
598
        else
599
                video_initialized = 1;
600
 
601
        width = get_short(data);
602
        height = get_short(data+2);
603
        g_screenWidth = width;
604
        g_screenHeight = height;
605
 
606
        return 1;
607
}
608
 
609
static int video_palette_handler(unsigned char, unsigned char, const unsigned char *data, int, void *)
610
{
611
        short start, count;
612
        start = get_short(data);
613
        count = get_short(data+2);
614
 
615
        auto p = data + 4;
616
        mve_setpalette(p - 3*start, start, count);
617
        return 1;
618
}
619
 
620
static int video_codemap_handler(unsigned char, unsigned char, const unsigned char *data, int len, void *)
621
{
622
        g_pCurMap = data;
623
        g_nMapLength = len;
624
        return 1;
625
}
626
 
627
static int video_data_handler(unsigned char, unsigned char, const unsigned char *data, int len, void *)
628
{
629
        unsigned short nFlags;
630
 
631
// don't need those but kept for further reference
632
//      nFrameHot  = get_short(data);
633
//      nFrameCold = get_short(data+2);
634
//      nXoffset   = get_short(data+4);
635
//      nYoffset   = get_short(data+6);
636
//      nXsize     = get_short(data+8);
637
//      nYsize     = get_short(data+10);
638
        nFlags     = get_ushort(data+12);
639
 
640
        if (nFlags & 1)
641
        {
642
                std::swap(g_vBackBuf1, g_vBackBuf2);
643
        }
644
 
645
        /* convert the frame */
646
        if (g_truecolor) {
647
                decodeFrame16(g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
648
        } else {
649
                decodeFrame8(g_vBackBuf1, g_pCurMap, g_nMapLength, data+14, len-14);
650
        }
651
 
652
        return 1;
653
}
654
 
655
static int end_chunk_handler(unsigned char, unsigned char, const unsigned char *, int, void *)
656
{
657
        g_pCurMap=NULL;
658
        return 1;
659
}
660
 
661
void MVE_ioCallbacks(mve_cb_Read io_read)
662
{
663
        mve_read = io_read;
664
}
665
 
666
void MVE_memCallbacks(mve_cb_Alloc mem_alloc, mve_cb_Free mem_free)
667
{
668
        mve_alloc = mem_alloc;
669
        mve_free = mem_free;
670
}
671
 
672
void MVE_sfCallbacks(mve_cb_ShowFrame showframe)
673
{
674
        mve_showframe = showframe;
675
}
676
 
677
void MVE_palCallbacks(mve_cb_SetPalette setpalette)
678
{
679
        mve_setpalette = setpalette;
680
}
681
 
682
int MVE_rmPrepMovie(MVESTREAM_ptr_t &pMovie, void *src, int x, int y, int)
683
{
684
        if (pMovie) {
685
                mve_reset(pMovie.get());
686
                return 0;
687
        }
688
 
689
        pMovie = mve_open(src);
690
 
691
        if (!pMovie)
692
                return 1;
693
 
694
        g_destX = x;
695
        g_destY = y;
696
 
697
        auto &mve = *pMovie.get();
698
        mve_set_handler(mve, MVE_OPCODE_ENDOFSTREAM,          end_movie_handler);
699
        mve_set_handler(mve, MVE_OPCODE_ENDOFCHUNK,           end_chunk_handler);
700
        mve_set_handler(mve, MVE_OPCODE_CREATETIMER,          create_timer_handler);
701
        mve_set_handler(mve, MVE_OPCODE_INITAUDIOBUFFERS,     create_audiobuf_handler);
702
        mve_set_handler(mve, MVE_OPCODE_STARTSTOPAUDIO,       play_audio_handler);
703
        mve_set_handler(mve, MVE_OPCODE_INITVIDEOBUFFERS,     create_videobuf_handler);
704
 
705
        mve_set_handler(mve, MVE_OPCODE_DISPLAYVIDEO,         display_video_handler);
706
        mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMEDATA,       audio_data_handler);
707
        mve_set_handler(mve, MVE_OPCODE_AUDIOFRAMESILENCE,    audio_data_handler);
708
        mve_set_handler(mve, MVE_OPCODE_INITVIDEOMODE,        init_video_handler);
709
 
710
        mve_set_handler(mve, MVE_OPCODE_SETPALETTE,           video_palette_handler);
711
        mve_set_handler(mve, MVE_OPCODE_SETDECODINGMAP,       video_codemap_handler);
712
 
713
        mve_set_handler(mve, MVE_OPCODE_VIDEODATA,            video_data_handler);
714
 
715
        mve_play_next_chunk(mve); /* video initialization chunk */
716
        mve_play_next_chunk(mve); /* audio initialization chunk */
717
 
718
        return 0;
719
}
720
 
721
 
722
void MVE_getVideoSpec(MVE_videoSpec *vSpec)
723
{
724
        vSpec->screenWidth = g_screenWidth;
725
        vSpec->screenHeight = g_screenHeight;
726
        vSpec->width = g_width;
727
        vSpec->height = g_height;
728
        vSpec->truecolor = g_truecolor;
729
}
730
 
731
 
732
MVE_StepStatus MVE_rmStepMovie(MVESTREAM &mve)
733
{
734
        static int init_timer=0;
735
        int cont=1;
736
 
737
        if (!timer_started)
738
                timer_start();
739
 
740
        while (cont && !g_frameUpdated) // make a "step" be a frame, not a chunk...
741
                cont = mve_play_next_chunk(mve);
742
        g_frameUpdated = 0;
743
 
744
        if (!cont)
745
                return MVE_StepStatus::EndOfFile;
746
 
747
        if (micro_frame_delay  && !init_timer) {
748
                timer_start();
749
                init_timer = 1;
750
        }
751
 
752
        do_timer_wait();
753
 
754
        return MVE_StepStatus::Continue;
755
}
756
 
757
void MVE_rmEndMovie(std::unique_ptr<MVESTREAM>)
758
{
759
        timer_stop();
760
        timer_created = 0;
761
 
762
        if (mve_audio_canplay) {
763
                // MD2211: if using SDL_Mixer, we never reinit sound, hence never close it
764
                if (CGameArg.SndDisableSdlMixer)
765
                {
766
                        SDL_CloseAudio();
767
                }
768
                mve_audio_canplay = 0;
769
        }
770
        mve_audio_buffers = {};
771
        mve_audio_buflens = {};
772
 
773
        mve_audio_curbuf_curpos=0;
774
        mve_audio_bufhead=0;
775
        mve_audio_buftail=0;
776
        mve_audio_playing=0;
777
        mve_audio_canplay=0;
778
        mve_audio_flags = 0;
779
 
780
        mve_audio_spec.reset();
781
        audiobuf_created = 0;
782
        g_vBuffers.clear();
783
        g_pCurMap=NULL;
784
        g_nMapLength=0;
785
        videobuf_created = 0;
786
        video_initialized = 0;
787
}
788
 
789
 
790
void MVE_rmHoldMovie()
791
{
792
        timer_started = 0;
793
}
794
 
795
 
796
void MVE_sndInit(int x)
797
{
798
        mve_audio_enabled = (x == -1 ? 0 : 1);
799
}