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