1 #include <string.h> // for mem* functions
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 
7 #include "mvelib.h"
8 
9 static const char  MVE_HEADER[]  = "Interplay MVE File\x1A";
10 static const short MVE_HDRCONST1 = 0x001A;
11 static const short MVE_HDRCONST2 = 0x0100;
12 static const short MVE_HDRCONST3 = 0x1133;
13 
14 /*
15  * private utility functions
16  */
17 static short _mve_get_short(unsigned char *data);
18 static unsigned short _mve_get_ushort(unsigned char *data);
19 
20 /*
21  * private functions for mvefile
22  */
23 static MVEFILE *_mvefile_alloc(void);
24 static void _mvefile_free(MVEFILE *movie);
25 static void _mvefile_free_filehandle(MVEFILE *movie);
26 static int _mvefile_open(MVEFILE *movie, const char *filename);
27 static int _mvefile_open_filehandle(MVEFILE *movie, int filehandle);
28 static void _mvefile_reset(MVEFILE *movie);
29 static int  _mvefile_read_header(MVEFILE *movie);
30 static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size);
31 static int _mvefile_fetch_next_chunk(MVEFILE *movie);
32 
33 /*
34  * private functions for mvestream
35  */
36 static MVESTREAM *_mvestream_alloc(void);
37 static void _mvestream_free(MVESTREAM *movie);
38 static void _mvestream_free_filehandle(MVESTREAM *movie);
39 static int _mvestream_open(MVESTREAM *movie, const char *filename);
40 static int _mvestream_open_filehandle(MVESTREAM *movie, int filehandle);
41 static void _mvestream_reset(MVESTREAM *movie);
42 
43 /************************************************************
44  * public MVEFILE functions
45  ************************************************************/
46 
47 /*
48  * open an MVE file
49  */
mvefile_open(const char * filename)50 MVEFILE *mvefile_open(const char *filename)
51 {
52     MVEFILE *file;
53 
54     /* create the file */
55     file = _mvefile_alloc();
56     if (! _mvefile_open(file, filename))
57     {
58         _mvefile_free(file);
59         return NULL;
60     }
61 
62     /* initialize the file */
63     _mvefile_set_buffer_size(file, 1024);
64 
65     /* verify the file's header */
66     if (! _mvefile_read_header(file))
67     {
68         _mvefile_free(file);
69         return NULL;
70     }
71 
72     /* now, prefetch the next chunk */
73     _mvefile_fetch_next_chunk(file);
74 
75     return file;
76 }
77 
mvefile_open_filehandle(int filehandle)78 MVEFILE *mvefile_open_filehandle(int filehandle)
79 {
80     MVEFILE *file;
81 
82     /* create the file */
83     file = _mvefile_alloc();
84     if (! _mvefile_open_filehandle(file, filehandle))
85     {
86         _mvefile_free_filehandle(file);
87         return NULL;
88     }
89 
90     /* initialize the file */
91     _mvefile_set_buffer_size(file, 1024);
92 
93     /* verify the file's header */
94     if (! _mvefile_read_header(file))
95     {
96         _mvefile_free_filehandle(file);
97         return NULL;
98     }
99 
100     /* now, prefetch the next chunk */
101     _mvefile_fetch_next_chunk(file);
102 
103     return file;
104 }
105 
106 /*
107  * close a MVE file
108  */
mvefile_close(MVEFILE * movie)109 void mvefile_close(MVEFILE *movie)
110 {
111     _mvefile_free(movie);
112 }
113 
mvefile_close_filehandle(MVEFILE * movie)114 void mvefile_close_filehandle(MVEFILE *movie)
115 {
116     _mvefile_free_filehandle(movie);
117 }
118 
119 /*
120  * reset a MVE file
121  */
mvefile_reset(MVEFILE * file)122 void mvefile_reset(MVEFILE *file)
123 {
124 	_mvefile_reset(file);
125 
126     /* initialize the file */
127     _mvefile_set_buffer_size(file, 1024);
128 
129     /* verify the file's header */
130     if (! _mvefile_read_header(file))
131     {
132         _mvefile_free_filehandle(file);
133 		//return NULL;
134     }
135 
136     /* now, prefetch the next chunk */
137     _mvefile_fetch_next_chunk(file);
138 }
139 
140 /*
141  * get the size of the next segment
142  */
mvefile_get_next_segment_size(MVEFILE * movie)143 int mvefile_get_next_segment_size(MVEFILE *movie)
144 {
145     /* if nothing is cached, fail */
146     if (movie->cur_chunk == NULL  ||  movie->next_segment >= movie->cur_fill)
147         return -1;
148 
149     /* if we don't have enough data to get a segment, fail */
150     if (movie->cur_fill - movie->next_segment < 4)
151         return -1;
152 
153     /* otherwise, get the data length */
154     return _mve_get_short(movie->cur_chunk + movie->next_segment);
155 }
156 
157 /*
158  * get type of next segment in chunk (0xff if no more segments in chunk)
159  */
mvefile_get_next_segment_major(MVEFILE * movie)160 unsigned char mvefile_get_next_segment_major(MVEFILE *movie)
161 {
162     /* if nothing is cached, fail */
163     if (movie->cur_chunk == NULL  ||  movie->next_segment >= movie->cur_fill)
164         return 0xff;
165 
166     /* if we don't have enough data to get a segment, fail */
167     if (movie->cur_fill - movie->next_segment < 4)
168         return 0xff;
169 
170     /* otherwise, get the data length */
171     return movie->cur_chunk[movie->next_segment + 2];
172 }
173 
174 /*
175  * get subtype (version) of next segment in chunk (0xff if no more segments in
176  * chunk)
177  */
mvefile_get_next_segment_minor(MVEFILE * movie)178 unsigned char mvefile_get_next_segment_minor(MVEFILE *movie)
179 {
180     /* if nothing is cached, fail */
181     if (movie->cur_chunk == NULL  ||  movie->next_segment >= movie->cur_fill)
182         return 0xff;
183 
184     /* if we don't have enough data to get a segment, fail */
185     if (movie->cur_fill - movie->next_segment < 4)
186         return 0xff;
187 
188     /* otherwise, get the data length */
189     return movie->cur_chunk[movie->next_segment + 3];
190 }
191 
192 /*
193  * see next segment (return NULL if no next segment)
194  */
mvefile_get_next_segment(MVEFILE * movie)195 unsigned char *mvefile_get_next_segment(MVEFILE *movie)
196 {
197     /* if nothing is cached, fail */
198     if (movie->cur_chunk == NULL  ||  movie->next_segment >= movie->cur_fill)
199         return NULL;
200 
201     /* if we don't have enough data to get a segment, fail */
202     if (movie->cur_fill - movie->next_segment < 4)
203         return NULL;
204 
205     /* otherwise, get the data length */
206     return movie->cur_chunk + movie->next_segment + 4;
207 }
208 
209 /*
210  * advance to next segment
211  */
mvefile_advance_segment(MVEFILE * movie)212 void mvefile_advance_segment(MVEFILE *movie)
213 {
214     /* if nothing is cached, fail */
215     if (movie->cur_chunk == NULL  ||  movie->next_segment >= movie->cur_fill)
216         return;
217 
218     /* if we don't have enough data to get a segment, fail */
219     if (movie->cur_fill - movie->next_segment < 4)
220         return;
221 
222     /* else, advance to next segment */
223     movie->next_segment +=
224         (4 + _mve_get_ushort(movie->cur_chunk + movie->next_segment));
225 }
226 
227 /*
228  * fetch the next chunk (return 0 if at end of stream)
229  */
mvefile_fetch_next_chunk(MVEFILE * movie)230 int mvefile_fetch_next_chunk(MVEFILE *movie)
231 {
232     return _mvefile_fetch_next_chunk(movie);
233 }
234 
235 /************************************************************
236  * public MVESTREAM functions
237  ************************************************************/
238 
239 /*
240  * open an MVE stream
241  */
mve_open(const char * filename)242 MVESTREAM *mve_open(const char *filename)
243 {
244     MVESTREAM *movie;
245 
246     /* allocate */
247     movie = _mvestream_alloc();
248 
249     /* open */
250     if (! _mvestream_open(movie, filename))
251     {
252         _mvestream_free(movie);
253         return NULL;
254     }
255 
256     return movie;
257 }
258 
mve_open_filehandle(int filehandle)259 MVESTREAM *mve_open_filehandle(int filehandle)
260 {
261     MVESTREAM *movie;
262 
263     /* allocate */
264     movie = _mvestream_alloc();
265 
266     /* open */
267     if (! _mvestream_open_filehandle(movie, filehandle))
268     {
269         _mvestream_free_filehandle(movie);
270         return NULL;
271     }
272 
273     return movie;
274 }
275 
276 /*
277  * close an MVE stream
278  */
mve_close(MVESTREAM * movie)279 void mve_close(MVESTREAM *movie)
280 {
281     _mvestream_free(movie);
282 }
283 
mve_close_filehandle(MVESTREAM * movie)284 void mve_close_filehandle(MVESTREAM *movie)
285 {
286     _mvestream_free_filehandle(movie);
287 }
288 
289 /*
290  * reset an MVE stream
291  */
mve_reset(MVESTREAM * movie)292 void mve_reset(MVESTREAM *movie)
293 {
294 	_mvestream_reset(movie);
295 }
296 
297 /*
298  * set segment type handler
299  */
mve_set_handler(MVESTREAM * movie,unsigned char major,MVESEGMENTHANDLER handler)300 void mve_set_handler(MVESTREAM *movie, unsigned char major, MVESEGMENTHANDLER handler)
301 {
302     if (major < 32)
303         movie->handlers[major] = handler;
304 }
305 
306 /*
307  * set segment handler context
308  */
mve_set_handler_context(MVESTREAM * movie,void * context)309 void mve_set_handler_context(MVESTREAM *movie, void *context)
310 {
311     movie->context = context;
312 }
313 
314 /*
315  * play next chunk
316  */
mve_play_next_chunk(MVESTREAM * movie)317 int mve_play_next_chunk(MVESTREAM *movie)
318 {
319     unsigned char major, minor;
320     unsigned char *data;
321     int len;
322 
323     /* loop over segments */
324     major = mvefile_get_next_segment_major(movie->movie);
325     while (major != 0xff)
326     {
327         /* check whether to handle the segment */
328         if (major < 32  &&  movie->handlers[major] != NULL)
329         {
330             minor = mvefile_get_next_segment_minor(movie->movie);
331             len = mvefile_get_next_segment_size(movie->movie);
332             data = mvefile_get_next_segment(movie->movie);
333 
334             if (! movie->handlers[major](major, minor, data, len, movie->context))
335                 return 0;
336         }
337 
338         /* advance to next segment */
339         mvefile_advance_segment(movie->movie);
340         major = mvefile_get_next_segment_major(movie->movie);
341     }
342 
343     if (! mvefile_fetch_next_chunk(movie->movie))
344         return 0;
345 
346     /* return status */
347     return 1;
348 }
349 
350 /************************************************************
351  * private functions
352  ************************************************************/
353 
354 /*
355  * allocate an MVEFILE
356  */
_mvefile_alloc(void)357 static MVEFILE *_mvefile_alloc(void)
358 {
359     MVEFILE *file = (MVEFILE *)malloc(sizeof(MVEFILE));
360     file->stream = -1;
361     file->cur_chunk = NULL;
362     file->buf_size = 0;
363     file->cur_fill = 0;
364     file->next_segment = 0;
365 
366     return file;
367 }
368 
369 /*
370  * free an MVE file
371  */
_mvefile_free(MVEFILE * movie)372 static void _mvefile_free(MVEFILE *movie)
373 {
374     /* free the stream */
375     if (movie->stream != -1)
376         close(movie->stream);
377     movie->stream = -1;
378 
379     /* free the buffer */
380     if (movie->cur_chunk)
381         free(movie->cur_chunk);
382     movie->cur_chunk = NULL;
383 
384     /* not strictly necessary */
385     movie->buf_size = 0;
386     movie->cur_fill = 0;
387     movie->next_segment = 0;
388 
389     /* free the struct */
390     free(movie);
391 }
392 
_mvefile_free_filehandle(MVEFILE * movie)393 static void _mvefile_free_filehandle(MVEFILE *movie)
394 {
395     /* free the stream */
396 	movie->stream = -1;
397 
398     /* free the buffer */
399     if (movie->cur_chunk)
400         free(movie->cur_chunk);
401     movie->cur_chunk = NULL;
402 
403     /* not strictly necessary */
404     movie->buf_size = 0;
405     movie->cur_fill = 0;
406     movie->next_segment = 0;
407 
408     /* free the struct */
409     free(movie);
410 }
411 
412 /*
413  * open the file stream in thie object
414  */
_mvefile_open(MVEFILE * file,const char * filename)415 static int _mvefile_open(MVEFILE *file, const char *filename)
416 {
417 #ifdef O_BINARY
418     file->stream = open(filename, O_RDONLY | O_BINARY);
419 #else
420     file->stream = open(filename, O_RDONLY);
421 #endif
422     if (file->stream == -1)
423         return 0;
424 
425     return 1;
426 }
427 
_mvefile_open_filehandle(MVEFILE * file,int filehandle)428 static int _mvefile_open_filehandle(MVEFILE *file, int filehandle)
429 {
430     file->stream = filehandle;
431     if (file->stream == -1)
432         return 0;
433 
434     return 1;
435 }
436 
437 /*
438  * allocate an MVEFILE
439  */
_mvefile_reset(MVEFILE * file)440 static void _mvefile_reset(MVEFILE *file)
441 {
442 #if 0
443     file->cur_chunk = NULL;
444     file->buf_size = 0;
445     file->cur_fill = 0;
446     file->next_segment = 0;
447 #endif
448 }
449 
450 /*
451  * read and verify the header of the recently opened file
452  */
_mvefile_read_header(MVEFILE * movie)453 static int _mvefile_read_header(MVEFILE *movie)
454 {
455     unsigned char buffer[26];
456 
457     /* check the file is open */
458     if (movie->stream == -1)
459         return 0;
460 
461     /* check the file is long enough */
462     if (read(movie->stream, buffer, 26) < 26)
463         return 0;
464 
465     /* check the signature */
466     if (memcmp(buffer, MVE_HEADER, 20))
467         return 0;
468 
469     /* check the hard-coded constants */
470     if (_mve_get_short(buffer+20) != MVE_HDRCONST1)
471         return 0;
472     if (_mve_get_short(buffer+22) != MVE_HDRCONST2)
473         return 0;
474     if (_mve_get_short(buffer+24) != MVE_HDRCONST3)
475         return 0;
476 
477     return 1;
478 }
479 
_mvefile_set_buffer_size(MVEFILE * movie,int buf_size)480 static void _mvefile_set_buffer_size(MVEFILE *movie, int buf_size)
481 {
482     unsigned char *new_buffer;
483     int new_len;
484 
485     /* check if this would be a redundant operation */
486     if (buf_size  <=  movie->buf_size)
487         return;
488 
489     /* allocate new buffer */
490     new_len = 100 + buf_size;
491     new_buffer = (unsigned char *)malloc(new_len);
492 
493     /* copy old data */
494     if (movie->cur_chunk  &&  movie->cur_fill)
495         memcpy(new_buffer, movie->cur_chunk, movie->cur_fill);
496 
497     /* free old buffer */
498     if (movie->cur_chunk)
499     {
500         free(movie->cur_chunk);
501         movie->cur_chunk = 0;
502     }
503 
504     /* install new buffer */
505     movie->cur_chunk = new_buffer;
506     movie->buf_size = new_len;
507 }
508 
_mvefile_fetch_next_chunk(MVEFILE * movie)509 static int _mvefile_fetch_next_chunk(MVEFILE *movie)
510 {
511     unsigned char buffer[4];
512     unsigned short length;
513 
514     /* fail if not open */
515     if (movie->stream == -1)
516         return 0;
517 
518     /* fail if we can't read the next segment descriptor */
519     if (read(movie->stream, buffer, 4) < 4)
520         return 0;
521 
522     /* pull out the next length */
523     length = _mve_get_short(buffer);
524 
525     /* make sure we've got sufficient space */
526     _mvefile_set_buffer_size(movie, length);
527 
528     /* read the chunk */
529     if (read(movie->stream, movie->cur_chunk, length) < length)
530         return 0;
531     movie->cur_fill = length;
532     movie->next_segment = 0;
533 
534     return 1;
535 }
536 
_mve_get_short(unsigned char * data)537 static short _mve_get_short(unsigned char *data)
538 {
539     short value;
540     value = data[0] | (data[1] << 8);
541     return value;
542 }
543 
_mve_get_ushort(unsigned char * data)544 static unsigned short _mve_get_ushort(unsigned char *data)
545 {
546     unsigned short value;
547     value = data[0] | (data[1] << 8);
548     return value;
549 }
550 
551 /*
552  * allocate an MVESTREAM
553  */
_mvestream_alloc(void)554 static MVESTREAM *_mvestream_alloc(void)
555 {
556     MVESTREAM *movie;
557 
558     /* allocate and zero-initialize everything */
559     movie = (MVESTREAM *)malloc(sizeof(MVESTREAM));
560     movie->movie = NULL;
561     movie->context = 0;
562     memset(movie->handlers, 0, sizeof(movie->handlers));
563 
564     return movie;
565 }
566 
567 /*
568  * free an MVESTREAM
569  */
_mvestream_free(MVESTREAM * movie)570 static void _mvestream_free(MVESTREAM *movie)
571 {
572     /* close MVEFILE */
573     if (movie->movie)
574         mvefile_close(movie->movie);
575     movie->movie = NULL;
576 
577     /* clear context and handlers */
578     movie->context = NULL;
579     memset(movie->handlers, 0, sizeof(movie->handlers));
580 }
581 
_mvestream_free_filehandle(MVESTREAM * movie)582 static void _mvestream_free_filehandle(MVESTREAM *movie)
583 {
584     /* close MVEFILE */
585     if (movie->movie)
586         mvefile_close_filehandle(movie->movie);
587     movie->movie = NULL;
588 
589     /* clear context and handlers */
590     movie->context = NULL;
591     memset(movie->handlers, 0, sizeof(movie->handlers));
592 }
593 
594 /*
595  * open an MVESTREAM object
596  */
_mvestream_open(MVESTREAM * movie,const char * filename)597 static int _mvestream_open(MVESTREAM *movie, const char *filename)
598 {
599     movie->movie = mvefile_open(filename);
600 
601     return (movie->movie == NULL) ? 0 : 1;
602 }
603 
_mvestream_open_filehandle(MVESTREAM * movie,int filehandle)604 static int _mvestream_open_filehandle(MVESTREAM *movie, int filehandle)
605 {
606     movie->movie = mvefile_open_filehandle(filehandle);
607 
608     return (movie->movie == NULL) ? 0 : 1;
609 }
610 
611 /*
612  * reset an MVESTREAM
613  */
_mvestream_reset(MVESTREAM * movie)614 static void _mvestream_reset(MVESTREAM *movie)
615 {
616 	mvefile_reset(movie->movie);
617 }
618