This commit was manufactured by cvs2svn to create branch
[btb/d2x.git] / libmve / mvelib.c
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  */
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
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  */
109 void mvefile_close(MVEFILE *movie)
110 {
111     _mvefile_free(movie);
112 }
113
114 void mvefile_close_filehandle(MVEFILE *movie)
115 {
116     _mvefile_free_filehandle(movie);
117 }
118
119 /*
120  * reset a MVE file
121  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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
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  */
279 void mve_close(MVESTREAM *movie)
280 {
281     _mvestream_free(movie);
282 }
283
284 void mve_close_filehandle(MVESTREAM *movie)
285 {
286     _mvestream_free_filehandle(movie);
287 }
288
289 /*
290  * reset an MVE stream
291  */
292 void mve_reset(MVESTREAM *movie)
293 {
294         _mvestream_reset(movie);
295 }
296
297 /*
298  * set segment type handler
299  */
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  */
309 void mve_set_handler_context(MVESTREAM *movie, void *context)
310 {
311     movie->context = context;
312 }
313
314 /*
315  * play next chunk
316  */
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  */
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  */
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
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  */
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
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  */
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  */
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
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
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
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
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  */
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  */
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
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  */
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
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  */
614 static void _mvestream_reset(MVESTREAM *movie)
615 {
616         mvefile_reset(movie->movie);
617 }