]> icculus.org git repositories - taylor/freespace2.git/blob - src/movie/mvelib.cpp
const-char warning fixes
[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 cf_opened = 0;
62         int mve_valid = 1;
63         char lower_name[MAX_FILENAME_LEN];
64         char upper_name[MAX_FILENAME_LEN];
65         char buffer[20];
66         MVEFILE *file;
67
68         // create the file
69         file = (MVEFILE *)malloc(sizeof(MVEFILE));
70
71         // set defaults
72         file->stream = NULL;
73         file->cur_chunk = NULL;
74         file->buf_size = 0;
75         file->cur_fill = 0;
76         file->next_segment = 0;
77
78         // lower case filename for checking
79         strncpy(lower_name, filename, strlen(filename)+1);
80         strlwr(lower_name);
81         // upper case filename for checking
82         strncpy(upper_name, filename, strlen(filename)+1);
83         strupr(upper_name);
84
85         // NOTE: CF_TYPE *must* be ANY to get movies off of the CDs
86         while (1) {
87                 // lower case filename check - off of HD/CD-ROM
88                 if ( (file->stream = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_MOVIES)) ) {
89                         cf_opened = 1;
90                         break;
91                 }
92
93                 // upper case filename check - off of CD-ROM (or HD if case not changed)
94                 if ( (file->stream = cfopen(upper_name, "rb", CFILE_NORMAL, CF_TYPE_ANY)) ) {
95                         cf_opened = 1;
96                         break;
97                 }
98
99                 // passed filename check - just because
100                 if ( (file->stream = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_ANY)) ) {
101                         cf_opened = 1;
102                         break;
103                 }
104                 
105                 // uh-oh, couldn't open
106                 cf_opened = 0;
107                 break;
108         }
109
110         if (!cf_opened) {
111                 mvefile_close(file);
112                 return NULL;
113         }
114
115         // initialize the buffer
116         file->cur_chunk = (ubyte *)malloc(100 + 1024);
117         file->buf_size = 100 + 1024;
118
119         // verify the file's header
120         cfread_string(buffer, 20, file->stream);
121         
122         if (strcmp(buffer, MVE_HEADER))
123                 mve_valid = 0;
124
125         if (cfread_short(file->stream) != MVE_HDRCONST1)
126                 mve_valid = 0;
127
128         if (cfread_short(file->stream) != MVE_HDRCONST2)
129                 mve_valid = 0;
130
131         if (cfread_short(file->stream) != MVE_HDRCONST3)
132                 mve_valid = 0;
133
134         if (!mve_valid) {
135                 mvefile_close(file);
136                 return NULL;
137         }
138
139         // now, prefetch the next chunk
140         mvefile_fetch_next_chunk(file);
141
142         return file;
143 }
144
145 // close a MVE file
146 void mvefile_close(MVEFILE *file)
147 {
148         // free the stream
149         if (file->stream)
150                 cfclose(file->stream);
151
152         file->stream = NULL;
153
154         // free the buffer
155         if (file->cur_chunk)
156                 free(file->cur_chunk);
157
158         file->cur_chunk = NULL;
159
160         // not strictly necessary
161         file->buf_size = 0;
162         file->cur_fill = 0;
163         file->next_segment = 0;
164
165         // free the struct
166         free(file);
167 }
168
169 // get the size of the next segment
170 int mvefile_get_next_segment_size(MVEFILE *file)
171 {
172         // if nothing is cached, fail
173         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
174                 return -1;
175
176         // if we don't have enough data to get a segment, fail
177         if (file->cur_fill - file->next_segment < 4)
178                 return -1;
179
180         // otherwise, get the data length
181         return mve_get_short(file->cur_chunk + file->next_segment);
182 }
183
184 // get type of next segment in chunk (0xff if no more segments in chunk)
185 ubyte mvefile_get_next_segment_major(MVEFILE *file)
186 {
187         // if nothing is cached, fail
188         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
189                 return 0xff;
190
191         // if we don't have enough data to get a segment, fail
192         if (file->cur_fill - file->next_segment < 4)
193                 return 0xff;
194
195         // otherwise, get the data length
196         return file->cur_chunk[file->next_segment + 2];
197 }
198
199 // get subtype (version) of next segment in chunk (0xff if no more segments in chunk)
200 ubyte mvefile_get_next_segment_minor(MVEFILE *file)
201 {
202         // if nothing is cached, fail
203         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
204                 return 0xff;
205
206         // if we don't have enough data to get a segment, fail
207         if (file->cur_fill - file->next_segment < 4)
208                 return 0xff;
209
210         // otherwise, get the data length
211         return file->cur_chunk[file->next_segment + 3];
212 }
213
214 // see next segment (return NULL if no next segment)
215 ubyte *mvefile_get_next_segment(MVEFILE *file)
216 {
217         // if nothing is cached, fail
218         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
219                 return NULL;
220
221         // if we don't have enough data to get a segment, fail
222         if (file->cur_fill - file->next_segment < 4)
223                 return NULL;
224
225         // otherwise, get the data length
226         return file->cur_chunk + file->next_segment + 4;
227 }
228
229 // advance to next segment
230 void mvefile_advance_segment(MVEFILE *file)
231 {
232         // if nothing is cached, fail
233         if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
234                 return;
235
236         // if we don't have enough data to get a segment, fail
237         if (file->cur_fill - file->next_segment < 4)
238                 return;
239
240         // else, advance to next segment
241         file->next_segment += (4 + mve_get_ushort(file->cur_chunk + file->next_segment));
242 }
243
244 // fetch the next chunk (return 0 if at end of stream)
245 int mvefile_fetch_next_chunk(MVEFILE *file)
246 {
247         ubyte buffer[4];
248         ubyte *new_buffer;
249         ushort length;
250
251         // fail if not open
252         if (file->stream == NULL)
253                 return 0;
254
255         // fail if we can't read the next segment descriptor
256         if (cfread(buffer, 1, 4, file->stream) != 4)
257                 return 0;
258
259         // pull out the next length
260         length = mve_get_ushort(buffer);
261
262         // setup a new buffer if needed --
263         // only allocate new buffer is old one is too small
264         if (length > file->buf_size) {
265                 // allocate new buffer
266                 new_buffer = (ubyte *)malloc(100 + length);
267
268                 // copy old data
269                 if (file->cur_chunk && file->cur_fill)
270                         memcpy(new_buffer, file->cur_chunk, file->cur_fill);
271
272                 // free old buffer
273                 if (file->cur_chunk) {
274                         free(file->cur_chunk);
275                         file->cur_chunk = NULL;
276                 }
277
278                 // install new buffer
279                 file->cur_chunk = new_buffer;
280                 file->buf_size = 100 + length;
281         }
282
283         // read the chunk
284         if (length > 0) {
285                 if (cfread(file->cur_chunk, 1, length, file->stream) != length)
286                         return 0;
287         }
288
289         file->cur_fill = length;
290         file->next_segment = 0;
291
292         return 1;
293 }
294
295 // -----------------------------------------------------------
296 // public MVESTREAM functions
297 // -----------------------------------------------------------
298
299 // open an MVE stream
300 MVESTREAM *mve_open(const char *filename)
301 {
302         MVESTREAM *stream;
303
304         // allocate
305         stream = (MVESTREAM *)malloc(sizeof(MVESTREAM));
306
307         // defaults
308         stream->movie = NULL;
309
310         // open
311         stream->movie = mvefile_open(filename);
312
313         if (stream->movie == NULL) {
314                 mve_close(stream);
315                 return NULL;
316         }
317
318         return stream;
319 }
320
321 // close an MVE stream
322 void mve_close(MVESTREAM *stream)
323 {
324         // close MVEFILE
325         if (stream->movie)
326                 mvefile_close(stream->movie);
327
328         stream->movie = NULL;
329
330         free(stream);
331 }
332
333 // play next chunk
334 int mve_play_next_chunk(MVESTREAM *stream)
335 {
336         ubyte major, minor;
337         ubyte *data;
338         int len;
339
340         // loop over segments
341         major = mvefile_get_next_segment_major(stream->movie);
342
343         while (major != 0xff) {
344                 // check whether to handle the segment
345                 if (major < 32) {
346                         minor = mvefile_get_next_segment_minor(stream->movie);
347                         len = mvefile_get_next_segment_size(stream->movie);
348                         data = mvefile_get_next_segment(stream->movie);
349
350                         switch (major) {
351                                 case 0x00:
352                                         mve_end_movie();
353                                         break;
354                                 case 0x01:
355                                         mve_end_chunk();
356                                         break;
357                                 case 0x02:
358                                         mve_timer_create(data);
359                                         break;
360                                 case 0x03:
361                                         mve_audio_createbuf(minor, data);
362                                         break;
363                                 case 0x04:
364                                         mve_audio_play();
365                                         break;
366                                 case 0x05:
367                                         if (!mve_video_createbuf(minor, data))
368                                                 return 0;
369                                         break;
370                                 case 0x07:
371                                         mve_video_display();
372                                         break;
373                                 case 0x08:
374                                         mve_audio_data(major, data);
375                                         break;
376                                 case 0x09:
377                                         mve_audio_data(major, data);
378                                         break;
379                                 case 0x0a:
380                                         if (!mve_video_init(data))
381                                                 return 0;
382                                         break;
383                                 case 0x0c:
384                                         mve_video_palette(data);
385                                         break;
386                                 case 0x0f:
387                                         mve_video_codemap(data, len);
388                                         break;
389                                 case 0x11:
390                                         mve_video_data(data, len);
391                                         break;
392                                 default:
393                                         break;
394                         }
395                 }
396
397                 // advance to next segment
398                 mvefile_advance_segment(stream->movie);
399                 major = mvefile_get_next_segment_major(stream->movie);
400         }
401
402         if (!mvefile_fetch_next_chunk(stream->movie))
403                 return 0;
404
405         // return status
406         return 1;
407 }