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