]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mvelib.cpp
clean up and simplify effects and updating
[taylor/freespace2.git] / src / movie / mvelib.cpp
1 /*
2  * $Logfile: /Freespace2/src/movie/mvelib.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Lib functions for MVE player
8  *
9  * $Log$
10  * Revision 1.2  2005/03/29 07:50:34  taylor
11  * Update to newest movie code with much better video support and audio support from
12  *   Pierre Willenbrock.  Movies are enabled always now (no longer a build option)
13  *   and but can be skipped with the "--nomovies" or "-n" cmdline options.
14  *
15  *
16  *
17  * $NoKeywords: $
18  *
19  */
20  
21 #include "pstypes.h"
22 #include "mvelib.h"
23 #include "cfile.h"
24
25
26 static const char MVE_HEADER[]  = "Interplay MVE File\x1A";
27 static const short MVE_HDRCONST1 = 0x001A;
28 static const short MVE_HDRCONST2 = 0x0100;
29 static const short MVE_HDRCONST3 = 0x1133;
30
31
32 // -----------------------------------------------------------
33 // public MVEFILE functions
34 // -----------------------------------------------------------
35
36 // utility functions for mvefile and mveplayer
37 short mve_get_short(ubyte *data)
38 {
39         short value;
40         value = data[0] | (data[1] << 8);
41         return value;
42 }
43
44 ushort mve_get_ushort(ubyte *data)
45 {
46         ushort value;
47         value = data[0] | (data[1] << 8);
48         return value;
49 }
50
51 int mve_get_int(ubyte *data)
52 {
53         int value;
54         value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
55         return value;
56 }
57
58 // open an MVE file
59 MVEFILE *mvefile_open(const char *filename)
60 {
61         int mve_valid = 1;
62         char buffer[20];
63         MVEFILE *file;
64
65         // create the file
66         file = (MVEFILE *)malloc(sizeof(MVEFILE));
67
68         // set defaults
69         file->stream = NULL;
70         file->cur_chunk = NULL;
71         file->buf_size = 0;
72         file->cur_fill = 0;
73         file->next_segment = 0;
74
75         // NOTE: CF_TYPE *must* be ANY to get movies off of the CDs
76
77         // lower case filename check - off of HD/CD-ROM
78         file->stream = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MOVIES);
79
80         // upper case filename check - off of CD-ROM (or HD if case not changed)
81         if ( !file->stream ) {
82                 char upper_name[MAX_FILENAME_LEN];
83
84                 // upper case filename for checking
85                 SDL_strlcpy(upper_name, filename, SDL_arraysize(upper_name));
86                 SDL_strupr(upper_name);
87
88                 file->stream = cfopen(upper_name, "rb", CFILE_NORMAL, CF_TYPE_ANY);
89         }
90
91         if ( !file->stream ) {
92                 mvefile_close(file);
93                 return NULL;
94         }
95
96         // initialize the buffer
97         file->cur_chunk = (ubyte *)malloc(100 + 1024);
98         file->buf_size = 100 + 1024;
99
100         // verify the file's header
101         cfread_string(buffer, 20, file->stream);
102         
103         if (strcmp(buffer, MVE_HEADER))
104                 mve_valid = 0;
105
106         if (cfread_short(file->stream) != MVE_HDRCONST1)
107                 mve_valid = 0;
108
109         if (cfread_short(file->stream) != MVE_HDRCONST2)
110                 mve_valid = 0;
111
112         if (cfread_short(file->stream) != MVE_HDRCONST3)
113                 mve_valid = 0;
114
115         if (!mve_valid) {
116                 mvefile_close(file);
117                 return NULL;
118         }
119
120         // now, prefetch the next chunk
121         mvefile_fetch_next_chunk(file);
122
123         return file;
124 }
125
126 // close a MVE file
127 void mvefile_close(MVEFILE *file)
128 {
129         // free the stream
130         if (file->stream)
131                 cfclose(file->stream);
132
133         file->stream = NULL;
134
135         // free the buffer
136         if (file->cur_chunk)
137                 free(file->cur_chunk);
138
139         file->cur_chunk = NULL;
140
141         // not strictly necessary
142         file->buf_size = 0;
143         file->cur_fill = 0;
144         file->next_segment = 0;
145
146         // free the struct
147         free(file);
148 }
149
150 // get the size of the next segment
151 int mvefile_get_next_segment_size(MVEFILE *file)
152 {
153         // if nothing is cached, fail
154         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
155                 return -1;
156
157         // if we don't have enough data to get a segment, fail
158         if (file->cur_fill - file->next_segment < 4)
159                 return -1;
160
161         // otherwise, get the data length
162         return mve_get_short(file->cur_chunk + file->next_segment);
163 }
164
165 // get type of next segment in chunk (0xff if no more segments in chunk)
166 ubyte mvefile_get_next_segment_major(MVEFILE *file)
167 {
168         // if nothing is cached, fail
169         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
170                 return 0xff;
171
172         // if we don't have enough data to get a segment, fail
173         if (file->cur_fill - file->next_segment < 4)
174                 return 0xff;
175
176         // otherwise, get the data length
177         return file->cur_chunk[file->next_segment + 2];
178 }
179
180 // get subtype (version) of next segment in chunk (0xff if no more segments in chunk)
181 ubyte mvefile_get_next_segment_minor(MVEFILE *file)
182 {
183         // if nothing is cached, fail
184         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
185                 return 0xff;
186
187         // if we don't have enough data to get a segment, fail
188         if (file->cur_fill - file->next_segment < 4)
189                 return 0xff;
190
191         // otherwise, get the data length
192         return file->cur_chunk[file->next_segment + 3];
193 }
194
195 // see next segment (return NULL if no next segment)
196 ubyte *mvefile_get_next_segment(MVEFILE *file)
197 {
198         // if nothing is cached, fail
199         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
200                 return NULL;
201
202         // if we don't have enough data to get a segment, fail
203         if (file->cur_fill - file->next_segment < 4)
204                 return NULL;
205
206         // otherwise, get the data length
207         return file->cur_chunk + file->next_segment + 4;
208 }
209
210 // advance to next segment
211 void mvefile_advance_segment(MVEFILE *file)
212 {
213         // if nothing is cached, fail
214         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
215                 return;
216
217         // if we don't have enough data to get a segment, fail
218         if (file->cur_fill - file->next_segment < 4)
219                 return;
220
221         // else, advance to next segment
222         file->next_segment += (4 + mve_get_ushort(file->cur_chunk + file->next_segment));
223 }
224
225 // fetch the next chunk (return 0 if at end of stream)
226 int mvefile_fetch_next_chunk(MVEFILE *file)
227 {
228         ubyte buffer[4];
229         ubyte *new_buffer;
230         ushort length;
231
232         // fail if not open
233         if (file->stream == NULL)
234                 return 0;
235
236         // fail if we can't read the next segment descriptor
237         if (cfread(buffer, 1, 4, file->stream) != 4)
238                 return 0;
239
240         // pull out the next length
241         length = mve_get_ushort(buffer);
242
243         // setup a new buffer if needed --
244         // only allocate new buffer is old one is too small
245         if (length > file->buf_size) {
246                 // allocate new buffer
247                 new_buffer = (ubyte *)malloc(100 + length);
248
249                 // copy old data
250                 if (file->cur_chunk && file->cur_fill)
251                         memcpy(new_buffer, file->cur_chunk, file->cur_fill);
252
253                 // free old buffer
254                 if (file->cur_chunk) {
255                         free(file->cur_chunk);
256                         file->cur_chunk = NULL;
257                 }
258
259                 // install new buffer
260                 file->cur_chunk = new_buffer;
261                 file->buf_size = 100 + length;
262         }
263
264         // read the chunk
265         if (length > 0) {
266                 if (cfread(file->cur_chunk, 1, length, file->stream) != length)
267                         return 0;
268         }
269
270         file->cur_fill = length;
271         file->next_segment = 0;
272
273         return 1;
274 }
275
276 // -----------------------------------------------------------
277 // public MVESTREAM functions
278 // -----------------------------------------------------------
279
280 // open an MVE stream
281 MVESTREAM *mve_open(const char *filename)
282 {
283         MVESTREAM *stream;
284
285         // allocate
286         stream = (MVESTREAM *)malloc(sizeof(MVESTREAM));
287
288         // defaults
289         stream->movie = NULL;
290
291         // open
292         stream->movie = mvefile_open(filename);
293
294         if (stream->movie == NULL) {
295                 mve_close(stream);
296                 return NULL;
297         }
298
299         return stream;
300 }
301
302 // close an MVE stream
303 void mve_close(MVESTREAM *stream)
304 {
305         // close MVEFILE
306         if (stream->movie)
307                 mvefile_close(stream->movie);
308
309         stream->movie = NULL;
310
311         free(stream);
312 }
313
314 // play next chunk
315 int mve_play_next_chunk(MVESTREAM *stream)
316 {
317         ubyte major, minor;
318         ubyte *data;
319         int len;
320
321         // loop over segments
322         major = mvefile_get_next_segment_major(stream->movie);
323
324         while (major != 0xff) {
325                 // check whether to handle the segment
326                 if (major < 32) {
327                         minor = mvefile_get_next_segment_minor(stream->movie);
328                         len = mvefile_get_next_segment_size(stream->movie);
329                         data = mvefile_get_next_segment(stream->movie);
330
331                         switch (major) {
332                                 case 0x00:
333                                         mve_end_movie();
334                                         break;
335                                 case 0x01:
336                                         mve_end_chunk();
337                                         break;
338                                 case 0x02:
339                                         mve_timer_create(data);
340                                         break;
341                                 case 0x03:
342                                         mve_audio_createbuf(minor, data);
343                                         break;
344                                 case 0x04:
345                                         mve_audio_play();
346                                         break;
347                                 case 0x05:
348                                         if (!mve_video_createbuf(minor, data))
349                                                 return 0;
350                                         break;
351                                 case 0x07:
352                                         mve_video_display();
353                                         break;
354                                 case 0x08:
355                                         mve_audio_data(major, data);
356                                         break;
357                                 case 0x09:
358                                         mve_audio_data(major, data);
359                                         break;
360                                 case 0x0f:
361                                         mve_video_codemap(data, len);
362                                         break;
363                                 case 0x11:
364                                         mve_video_data(data, len);
365                                         break;
366                                 default:
367                                         break;
368                         }
369                 }
370
371                 // advance to next segment
372                 mvefile_advance_segment(stream->movie);
373                 major = mvefile_get_next_segment_major(stream->movie);
374         }
375
376         if (!mvefile_fetch_next_chunk(stream->movie))
377                 return 0;
378
379         // return status
380         return 1;
381 }