2 * $Logfile: /Freespace2/src/movie/mvelib.cpp $
7 * Lib functions for MVE player
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.
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;
32 // -----------------------------------------------------------
33 // public MVEFILE functions
34 // -----------------------------------------------------------
36 // utility functions for mvefile and mveplayer
37 short mve_get_short(ubyte *data)
40 value = data[0] | (data[1] << 8);
44 ushort mve_get_ushort(ubyte *data)
47 value = data[0] | (data[1] << 8);
51 int mve_get_int(ubyte *data)
54 value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
59 MVEFILE *mvefile_open(const char *filename)
63 char lower_name[MAX_FILENAME_LEN];
64 char upper_name[MAX_FILENAME_LEN];
69 file = (MVEFILE *)malloc(sizeof(MVEFILE));
73 file->cur_chunk = NULL;
76 file->next_segment = 0;
78 // lower case filename for checking
79 strncpy(lower_name, filename, strlen(filename)+1);
80 SDL_strlwr(lower_name);
81 // upper case filename for checking
82 strncpy(upper_name, filename, strlen(filename)+1);
83 SDL_strupr(upper_name);
85 // NOTE: CF_TYPE *must* be ANY to get movies off of the CDs
87 // lower case filename check - off of HD/CD-ROM
88 if ( (file->stream = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_MOVIES)) ) {
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)) ) {
99 // passed filename check - just because
100 if ( (file->stream = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_ANY)) ) {
105 // uh-oh, couldn't open
115 // initialize the buffer
116 file->cur_chunk = (ubyte *)malloc(100 + 1024);
117 file->buf_size = 100 + 1024;
119 // verify the file's header
120 cfread_string(buffer, 20, file->stream);
122 if (strcmp(buffer, MVE_HEADER))
125 if (cfread_short(file->stream) != MVE_HDRCONST1)
128 if (cfread_short(file->stream) != MVE_HDRCONST2)
131 if (cfread_short(file->stream) != MVE_HDRCONST3)
139 // now, prefetch the next chunk
140 mvefile_fetch_next_chunk(file);
146 void mvefile_close(MVEFILE *file)
150 cfclose(file->stream);
156 free(file->cur_chunk);
158 file->cur_chunk = NULL;
160 // not strictly necessary
163 file->next_segment = 0;
169 // get the size of the next segment
170 int mvefile_get_next_segment_size(MVEFILE *file)
172 // if nothing is cached, fail
173 if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
176 // if we don't have enough data to get a segment, fail
177 if (file->cur_fill - file->next_segment < 4)
180 // otherwise, get the data length
181 return mve_get_short(file->cur_chunk + file->next_segment);
184 // get type of next segment in chunk (0xff if no more segments in chunk)
185 ubyte mvefile_get_next_segment_major(MVEFILE *file)
187 // if nothing is cached, fail
188 if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
191 // if we don't have enough data to get a segment, fail
192 if (file->cur_fill - file->next_segment < 4)
195 // otherwise, get the data length
196 return file->cur_chunk[file->next_segment + 2];
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)
202 // if nothing is cached, fail
203 if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
206 // if we don't have enough data to get a segment, fail
207 if (file->cur_fill - file->next_segment < 4)
210 // otherwise, get the data length
211 return file->cur_chunk[file->next_segment + 3];
214 // see next segment (return NULL if no next segment)
215 ubyte *mvefile_get_next_segment(MVEFILE *file)
217 // if nothing is cached, fail
218 if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
221 // if we don't have enough data to get a segment, fail
222 if (file->cur_fill - file->next_segment < 4)
225 // otherwise, get the data length
226 return file->cur_chunk + file->next_segment + 4;
229 // advance to next segment
230 void mvefile_advance_segment(MVEFILE *file)
232 // if nothing is cached, fail
233 if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
236 // if we don't have enough data to get a segment, fail
237 if (file->cur_fill - file->next_segment < 4)
240 // else, advance to next segment
241 file->next_segment += (4 + mve_get_ushort(file->cur_chunk + file->next_segment));
244 // fetch the next chunk (return 0 if at end of stream)
245 int mvefile_fetch_next_chunk(MVEFILE *file)
252 if (file->stream == NULL)
255 // fail if we can't read the next segment descriptor
256 if (cfread(buffer, 1, 4, file->stream) != 4)
259 // pull out the next length
260 length = mve_get_ushort(buffer);
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);
269 if (file->cur_chunk && file->cur_fill)
270 memcpy(new_buffer, file->cur_chunk, file->cur_fill);
273 if (file->cur_chunk) {
274 free(file->cur_chunk);
275 file->cur_chunk = NULL;
278 // install new buffer
279 file->cur_chunk = new_buffer;
280 file->buf_size = 100 + length;
285 if (cfread(file->cur_chunk, 1, length, file->stream) != length)
289 file->cur_fill = length;
290 file->next_segment = 0;
295 // -----------------------------------------------------------
296 // public MVESTREAM functions
297 // -----------------------------------------------------------
299 // open an MVE stream
300 MVESTREAM *mve_open(const char *filename)
305 stream = (MVESTREAM *)malloc(sizeof(MVESTREAM));
308 stream->movie = NULL;
311 stream->movie = mvefile_open(filename);
313 if (stream->movie == NULL) {
321 // close an MVE stream
322 void mve_close(MVESTREAM *stream)
326 mvefile_close(stream->movie);
328 stream->movie = NULL;
334 int mve_play_next_chunk(MVESTREAM *stream)
340 // loop over segments
341 major = mvefile_get_next_segment_major(stream->movie);
343 while (major != 0xff) {
344 // check whether to handle the segment
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);
358 mve_timer_create(data);
361 mve_audio_createbuf(minor, data);
367 if (!mve_video_createbuf(minor, data))
374 mve_audio_data(major, data);
377 mve_audio_data(major, data);
380 if (!mve_video_init(data))
384 mve_video_codemap(data, len);
387 mve_video_data(data, len);
394 // advance to next segment
395 mvefile_advance_segment(stream->movie);
396 major = mvefile_get_next_segment_major(stream->movie);
399 if (!mvefile_fetch_next_chunk(stream->movie))