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