]> icculus.org git repositories - btb/d2x.git/blob - main/mvelib.c
enable mveplay audio
[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     if (! (file->stream = filehandle))
302         return 0;
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 }