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. #include <string.h> // for mem* functions
  8. #if !defined(_WIN32) && !defined(macintosh)
  9. #include <unistd.h>
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.h>
  13. #endif
  14.  
  15. #include "mvelib.h"
  16. #include <memory>
  17.  
  18. static const char  MVE_HEADER[]  = "Interplay MVE File\x1A";
  19. constexpr short MVE_HDRCONST1 = 0x001A;
  20. constexpr short MVE_HDRCONST2 = 0x0100;
  21. constexpr short MVE_HDRCONST3 = 0x1133;
  22.  
  23. mve_cb_Read mve_read;
  24. mve_cb_Alloc mve_alloc;
  25. mve_cb_Free mve_free;
  26. mve_cb_ShowFrame mve_showframe;
  27. mve_cb_SetPalette mve_setpalette;
  28.  
  29. /*
  30.  * private utility functions
  31.  */
  32. static int16_t _mve_get_short(const unsigned char *data);
  33. static uint16_t _mve_get_ushort(const unsigned char *data);
  34.  
  35. /*
  36.  * private functions for mvefile
  37.  */
  38. static int _mvefile_open(MVEFILE *movie, void *stream);
  39. static int  _mvefile_read_header(const MVEFILE *movie);
  40. static void _mvefile_set_buffer_size(MVEFILE *movie, std::size_t buf_size);
  41. static int _mvefile_fetch_next_chunk(MVEFILE *movie);
  42.  
  43. /*
  44.  * private functions for mvestream
  45.  */
  46. static int _mvestream_open(MVESTREAM *movie, void *stream);
  47. static void _mvestream_reset(MVESTREAM *movie);
  48.  
  49. /************************************************************
  50.  * public MVEFILE functions
  51.  ************************************************************/
  52.  
  53. /*
  54.  * open an MVE file
  55.  */
  56. std::unique_ptr<MVEFILE> mvefile_open(void *stream)
  57. {
  58.     /* create the file */
  59.         auto file = std::make_unique<MVEFILE>();
  60.         if (! _mvefile_open(file.get(), stream))
  61.     {
  62.                 return nullptr;
  63.     }
  64.  
  65.     /* initialize the file */
  66.         _mvefile_set_buffer_size(file.get(), 1024);
  67.  
  68.     /* verify the file's header */
  69.         if (! _mvefile_read_header(file.get()))
  70.     {
  71.                 return nullptr;
  72.     }
  73.  
  74.     /* now, prefetch the next chunk */
  75.         _mvefile_fetch_next_chunk(file.get());
  76.     return file;
  77. }
  78.  
  79. /*
  80.  * reset a MVE file
  81.  */
  82. static void mvefile_reset(MVEFILE *file)
  83. {
  84.     /* initialize the file */
  85.     _mvefile_set_buffer_size(file, 1024);
  86.  
  87.     /* verify the file's header */
  88.     if (! _mvefile_read_header(file))
  89.     {
  90.                 *file = {};
  91.     }
  92.  
  93.     /* now, prefetch the next chunk */
  94.     _mvefile_fetch_next_chunk(file);
  95. }
  96.  
  97. static bool have_segment_header(const MVEFILE *movie)
  98. {
  99.         /* if nothing is cached, fail */
  100.         if (movie->next_segment >= movie->cur_chunk.size())
  101.                 return false;
  102.         /* if we don't have enough data to get a segment, fail */
  103.         if (movie->cur_chunk.size() - movie->next_segment <= 4)
  104.                 return false;
  105.         return true;
  106. }
  107.  
  108. /*
  109.  * get the size of the next segment
  110.  */
  111. int_fast32_t mvefile_get_next_segment_size(const MVEFILE *movie)
  112. {
  113.         if (!have_segment_header(movie))
  114.         return -1;
  115.     /* otherwise, get the data length */
  116.     return _mve_get_short(&movie->cur_chunk[movie->next_segment]);
  117. }
  118.  
  119. /*
  120.  * get type of next segment in chunk (0xff if no more segments in chunk)
  121.  */
  122. unsigned char mvefile_get_next_segment_major(const MVEFILE *movie)
  123. {
  124.         if (!have_segment_header(movie))
  125.         return 0xff;
  126.     /* otherwise, get the data length */
  127.     return movie->cur_chunk[movie->next_segment + 2];
  128. }
  129.  
  130. /*
  131.  * get subtype (version) of next segment in chunk (0xff if no more segments in
  132.  * chunk)
  133.  */
  134. unsigned char mvefile_get_next_segment_minor(const MVEFILE *movie)
  135. {
  136.         if (!have_segment_header(movie))
  137.         return 0xff;
  138.     /* otherwise, get the data length */
  139.     return movie->cur_chunk[movie->next_segment + 3];
  140. }
  141.  
  142. /*
  143.  * see next segment (return NULL if no next segment)
  144.  */
  145. const unsigned char *mvefile_get_next_segment(const MVEFILE *movie)
  146. {
  147.         if (!have_segment_header(movie))
  148.         return NULL;
  149.  
  150.     /* otherwise, get the data length */
  151.     return &movie->cur_chunk[movie->next_segment + 4];
  152. }
  153.  
  154. /*
  155.  * advance to next segment
  156.  */
  157. void mvefile_advance_segment(MVEFILE *movie)
  158. {
  159.     if (!have_segment_header(movie))
  160.         return;
  161.     /* else, advance to next segment */
  162.     movie->next_segment +=
  163.         (4 + _mve_get_ushort(&movie->cur_chunk[movie->next_segment]));
  164. }
  165.  
  166. /*
  167.  * fetch the next chunk (return 0 if at end of stream)
  168.  */
  169. int mvefile_fetch_next_chunk(MVEFILE *movie)
  170. {
  171.     return _mvefile_fetch_next_chunk(movie);
  172. }
  173.  
  174. /************************************************************
  175.  * public MVESTREAM functions
  176.  ************************************************************/
  177.  
  178. /*
  179.  * open an MVE stream
  180.  */
  181. MVESTREAM_ptr_t mve_open(void *stream)
  182. {
  183.     /* allocate */
  184.         auto movie = std::make_unique<MVESTREAM>();
  185.  
  186.     /* open */
  187.     if (! _mvestream_open(movie.get(), stream))
  188.     {
  189.         return nullptr;
  190.     }
  191.     return MVESTREAM_ptr_t(movie.release());
  192. }
  193.  
  194. /*
  195.  * reset an MVE stream
  196.  */
  197. void mve_reset(MVESTREAM *movie)
  198. {
  199.         _mvestream_reset(movie);
  200. }
  201.  
  202. /*
  203.  * set segment type handler
  204.  */
  205. void mve_set_handler(MVESTREAM &movie, unsigned char major, MVESEGMENTHANDLER handler)
  206. {
  207.     if (major < 32)
  208.         movie.handlers[major] = handler;
  209. }
  210.  
  211. /*
  212.  * set segment handler context
  213.  */
  214. void mve_set_handler_context(MVESTREAM *movie, void *context)
  215. {
  216.     movie->context = context;
  217. }
  218.  
  219. /*
  220.  * play next chunk
  221.  */
  222. int mve_play_next_chunk(MVESTREAM &movie)
  223. {
  224.         const auto m = movie.movie.get();
  225.     /* loop over segments */
  226.         /* advance to next segment */
  227.         for (;; mvefile_advance_segment(m))
  228.     {
  229.                 const auto major = mvefile_get_next_segment_major(m);
  230.                 if (major == 0xff)
  231.                         break;
  232.                 if (major >= movie.handlers.size())
  233.                         continue;
  234.         /* check whether to handle the segment */
  235.                 if (const auto handler = movie.handlers[major])
  236.         {
  237.                         const auto minor = mvefile_get_next_segment_minor(m);
  238.                         const auto len = mvefile_get_next_segment_size(m);
  239.                         const auto data = mvefile_get_next_segment(m);
  240.  
  241.             if (!handler(major, minor, data, len, movie.context))
  242.                 return 0;
  243.         }
  244.     }
  245.  
  246.         if (!mvefile_fetch_next_chunk(m))
  247.         return 0;
  248.  
  249.     /* return status */
  250.     return 1;
  251. }
  252.  
  253. /************************************************************
  254.  * private functions
  255.  ************************************************************/
  256.  
  257. /*
  258.  * allocate an MVEFILE
  259.  */
  260. MVEFILE::MVEFILE()
  261. {
  262. }
  263.  
  264. /*
  265.  * free an MVE file
  266.  */
  267. MVEFILE::~MVEFILE()
  268. {
  269. }
  270.  
  271. /*
  272.  * open the file stream in thie object
  273.  */
  274. static int _mvefile_open(MVEFILE *file, void *stream)
  275. {
  276.     file->stream = stream;
  277.     if (! file->stream)
  278.         return 0;
  279.  
  280.     return 1;
  281. }
  282.  
  283. /*
  284.  * read and verify the header of the recently opened file
  285.  */
  286. static int _mvefile_read_header(const MVEFILE *movie)
  287. {
  288.     unsigned char buffer[26];
  289.  
  290.     /* check the file is open */
  291.     if (! movie->stream)
  292.         return 0;
  293.  
  294.     /* check the file is long enough */
  295.     if (! mve_read(movie->stream, buffer, 26))
  296.         return 0;
  297.  
  298.     /* check the signature */
  299.     if (memcmp(buffer, MVE_HEADER, 20))
  300.         return 0;
  301.  
  302.     /* check the hard-coded constants */
  303.     if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
  304.         return 0;
  305.     if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
  306.         return 0;
  307.     if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
  308.         return 0;
  309.  
  310.     return 1;
  311. }
  312.  
  313. static void _mvefile_set_buffer_size(MVEFILE *movie, std::size_t buf_size)
  314. {
  315.     /* check if this would be a redundant operation */
  316.         if (buf_size <= movie->cur_chunk.size())
  317.         return;
  318.  
  319.     /* allocate new buffer */
  320.     /* copy old data */
  321.     /* free old buffer */
  322.     /* install new buffer */
  323.         movie->cur_chunk.resize(100 + buf_size);
  324. }
  325.  
  326. static int _mvefile_fetch_next_chunk(MVEFILE *movie)
  327. {
  328.     unsigned char buffer[4];
  329.     unsigned short length;
  330.  
  331.     /* fail if not open */
  332.     if (! movie->stream)
  333.         return 0;
  334.  
  335.     /* fail if we can't read the next segment descriptor */
  336.     if (! mve_read(movie->stream, buffer, 4))
  337.         return 0;
  338.  
  339.     /* pull out the next length */
  340.     length = _mve_get_short(buffer);
  341.  
  342.     /* make sure we've got sufficient space */
  343.     _mvefile_set_buffer_size(movie, length);
  344.  
  345.     /* read the chunk */
  346.     if (! mve_read(movie->stream, &movie->cur_chunk[0], length))
  347.         return 0;
  348.     movie->cur_chunk.resize(length);
  349.     movie->next_segment = 0;
  350.  
  351.     return 1;
  352. }
  353.  
  354. static int16_t _mve_get_short(const unsigned char *data)
  355. {
  356.     short value;
  357.     value = data[0] | (data[1] << 8);
  358.     return value;
  359. }
  360.  
  361. static uint16_t _mve_get_ushort(const unsigned char *data)
  362. {
  363.     unsigned short value;
  364.     value = data[0] | (data[1] << 8);
  365.     return value;
  366. }
  367.  
  368. /*
  369.  * allocate an MVESTREAM
  370.  */
  371. MVESTREAM::MVESTREAM()
  372.     /* allocate and zero-initialize everything */
  373. {
  374. }
  375.  
  376. MVESTREAM::~MVESTREAM()
  377. {
  378. }
  379.  
  380. /*
  381.  * open an MVESTREAM object
  382.  */
  383. static int _mvestream_open(MVESTREAM *movie, void *stream)
  384. {
  385.     movie->movie = mvefile_open(stream);
  386.  
  387.     return (movie->movie == NULL) ? 0 : 1;
  388. }
  389.  
  390. /*
  391.  * reset an MVESTREAM
  392.  */
  393. static void _mvestream_reset(MVESTREAM *movie)
  394. {
  395.         mvefile_reset(movie->movie.get());
  396. }
  397.