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