Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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. }
  800.