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