1 #include <string.h> // for mem* functions
6 static const char MVE_HEADER[] = "Interplay MVE File\x1A";
7 static const short MVE_HDRCONST1 = 0x001A;
8 static const short MVE_HDRCONST2 = 0x0100;
9 static const short MVE_HDRCONST3 = 0x1133;
12 * private utility functions
14 static short _mve_get_short(unsigned char *data);
17 * private functions for mvefile
19 static MVEFILE *_mvefile_alloc(void);
20 static void _mvefile_free(MVEFILE *movie);
21 static int _mvefile_open(MVEFILE *movie, int filehandle);
22 static int _mvefile_read_header(MVEFILE *movie);
23 static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size);
24 static int _mvefile_fetch_next_chunk(MVEFILE *movie);
27 * private functions for mvestream
29 static MVESTREAM *_mvestream_alloc(void);
30 static void _mvestream_free(MVESTREAM *movie);
31 static int _mvestream_open(MVESTREAM *movie, int filehandle);
33 /************************************************************
34 * public MVEFILE functions
35 ************************************************************/
40 MVEFILE *mvefile_open(filehandle)
45 file = _mvefile_alloc();
46 if (! _mvefile_open(file, filehandle))
52 /* initialize the file */
53 _mvefile_set_buffer_size(file, 1024);
55 /* verify the file's header */
56 if (! _mvefile_read_header(file))
62 /* now, prefetch the next chunk */
63 _mvefile_fetch_next_chunk(file);
71 void mvefile_close(MVEFILE *movie)
77 * get the size of the next segment
79 int mvefile_get_next_segment_size(MVEFILE *movie)
81 /* if nothing is cached, fail */
82 if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
85 /* if we don't have enough data to get a segment, fail */
86 if (movie->cur_fill - movie->next_segment < 4)
89 /* otherwise, get the data length */
90 return _mve_get_short(movie->cur_chunk + movie->next_segment);
94 * get type of next segment in chunk (0xff if no more segments in chunk)
96 unsigned char mvefile_get_next_segment_major(MVEFILE *movie)
98 /* if nothing is cached, fail */
99 if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
102 /* if we don't have enough data to get a segment, fail */
103 if (movie->cur_fill - movie->next_segment < 4)
106 /* otherwise, get the data length */
107 return movie->cur_chunk[movie->next_segment + 2];
111 * get subtype (version) of next segment in chunk (0xff if no more segments in
114 unsigned char mvefile_get_next_segment_minor(MVEFILE *movie)
116 /* if nothing is cached, fail */
117 if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
120 /* if we don't have enough data to get a segment, fail */
121 if (movie->cur_fill - movie->next_segment < 4)
124 /* otherwise, get the data length */
125 return movie->cur_chunk[movie->next_segment + 3];
129 * see next segment (return NULL if no next segment)
131 unsigned char *mvefile_get_next_segment(MVEFILE *movie)
133 /* if nothing is cached, fail */
134 if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
137 /* if we don't have enough data to get a segment, fail */
138 if (movie->cur_fill - movie->next_segment < 4)
141 /* otherwise, get the data length */
142 return movie->cur_chunk + movie->next_segment + 4;
146 * advance to next segment
148 void mvefile_advance_segment(MVEFILE *movie)
150 /* if nothing is cached, fail */
151 if (movie->cur_chunk == NULL || movie->next_segment >= movie->cur_fill)
154 /* if we don't have enough data to get a segment, fail */
155 if (movie->cur_fill - movie->next_segment < 4)
158 /* else, advance to next segment */
159 movie->next_segment +=
160 (4 + _mve_get_short(movie->cur_chunk + movie->next_segment));
164 * fetch the next chunk (return 0 if at end of stream)
166 int mvefile_fetch_next_chunk(MVEFILE *movie)
168 return _mvefile_fetch_next_chunk(movie);
171 /************************************************************
172 * public MVESTREAM functions
173 ************************************************************/
178 MVESTREAM *mve_open(int filehandle)
183 movie = _mvestream_alloc();
186 if (! _mvestream_open(movie, filehandle))
188 _mvestream_free(movie);
196 * close an MVE stream
198 void mve_close(MVESTREAM *movie)
200 _mvestream_free(movie);
204 * set segment type handler
206 void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler)
209 movie->handlers[major] = handler;
213 * set segment handler context
215 void mve_set_handler_context(MVESTREAM *movie, void *context)
217 movie->context = context;
223 int mve_play_next_chunk(MVESTREAM *movie)
225 unsigned char major, minor;
229 /* loop over segments */
230 major = mvefile_get_next_segment_major(movie->movie);
231 while (major != 0xff)
233 /* check whether to handle the segment */
234 if (major < 32 && movie->handlers[major] != NULL)
236 minor = mvefile_get_next_segment_minor(movie->movie);
237 len = mvefile_get_next_segment_size(movie->movie);
238 data = mvefile_get_next_segment(movie->movie);
240 if (! movie->handlers[major](major, minor, data, len, movie->context))
244 /* advance to next segment */
245 mvefile_advance_segment(movie->movie);
246 major = mvefile_get_next_segment_major(movie->movie);
249 if (! mvefile_fetch_next_chunk(movie->movie))
256 /************************************************************
258 ************************************************************/
261 * allocate an MVEFILE
263 static MVEFILE *_mvefile_alloc(void)
265 MVEFILE *file = (MVEFILE *)malloc(sizeof(MVEFILE));
267 file->cur_chunk = NULL;
270 file->next_segment = 0;
278 static void _mvefile_free(MVEFILE *movie)
280 /* free the stream */
283 /* free the buffer */
284 if (movie->cur_chunk)
285 free(movie->cur_chunk);
286 movie->cur_chunk = NULL;
288 /* not strictly necessary */
291 movie->next_segment = 0;
293 /* free the struct */
298 * open the file stream in thie object
300 static int _mvefile_open(MVEFILE *file, int filehandle)
302 file->stream = filehandle;
308 * read and verify the header of the recently opened file
310 static int _mvefile_read_header(MVEFILE *movie)
312 unsigned char buffer[26];
314 /* check the file is open */
315 if (movie->stream == 0)
318 /* check the file is long enough */
319 if (read(movie->stream, buffer, 26) < 26)
322 /* check the signature */
323 if (memcmp(buffer, MVE_HEADER, 20))
326 /* check the hard-coded constants */
327 if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
329 if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
331 if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
337 static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size)
339 unsigned char *new_buffer;
342 /* check if this would be a redundant operation */
343 if (buf_size <= movie->buf_size)
346 /* allocate new buffer */
347 new_len = 100 + buf_size;
348 new_buffer = malloc(new_len);
351 if (movie->cur_chunk && movie->cur_fill)
352 memcpy(new_buffer, movie->cur_chunk, movie->cur_fill);
354 /* free old buffer */
355 if (movie->cur_chunk)
357 free(movie->cur_chunk);
358 movie->cur_chunk = 0;
361 /* install new buffer */
362 movie->cur_chunk = new_buffer;
363 movie->buf_size = new_len;
366 static int _mvefile_fetch_next_chunk(MVEFILE *movie)
368 unsigned char buffer[4];
369 unsigned short length;
371 /* fail if not open */
372 if (movie->stream == 0)
375 /* fail if we can't read the next segment descriptor */
376 if (read(movie->stream, buffer, 4) < 4)
379 /* pull out the next length */
380 length = _mve_get_short(buffer);
382 /* make sure we've got sufficient space */
383 _mvefile_set_buffer_size(movie, length);
386 if (read(movie->stream, movie->cur_chunk, length) < length)
388 movie->cur_fill = length;
389 movie->next_segment = 0;
394 static short _mve_get_short(unsigned char *data)
397 value = data[0] | (data[1] << 8);
402 * allocate an MVESTREAM
404 static MVESTREAM *_mvestream_alloc(void)
408 /* allocate and zero-initialize everything */
409 movie = (MVESTREAM *)malloc(sizeof(MVESTREAM));
412 memset(movie->handlers, 0, sizeof(movie->handlers));
420 static void _mvestream_free(MVESTREAM *movie)
424 mvefile_close(movie->movie);
427 /* clear context and handlers */
428 movie->context = NULL;
429 memset(movie->handlers, 0, sizeof(movie->handlers));
433 * open an MVESTREAM object
435 static int _mvestream_open(MVESTREAM *movie, int filehandle)
437 movie->movie = mvefile_open(filehandle);
439 return (movie->movie == NULL) ? 0 : 1;