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