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