mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 13:01:29 -10:00
372 lines
7.9 KiB
C++
372 lines
7.9 KiB
C++
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "cutscene/mvelib.h"
|
|
#include "cfile/cfile.h"
|
|
|
|
|
|
static const char MVE_HEADER[] = "Interplay MVE File\x1A";
|
|
static const short MVE_HDRCONST1 = 0x001A;
|
|
static const short MVE_HDRCONST2 = 0x0100;
|
|
static const short MVE_HDRCONST3 = 0x1133;
|
|
|
|
|
|
// -----------------------------------------------------------
|
|
// public MVEFILE functions
|
|
// -----------------------------------------------------------
|
|
|
|
// utility functions for mvefile and mveplayer
|
|
short mve_get_short(ubyte *data)
|
|
{
|
|
short value;
|
|
value = data[0] | (data[1] << 8);
|
|
return value;
|
|
}
|
|
|
|
ushort mve_get_ushort(ubyte *data)
|
|
{
|
|
ushort value;
|
|
value = data[0] | (data[1] << 8);
|
|
return value;
|
|
}
|
|
|
|
int mve_get_int(ubyte *data)
|
|
{
|
|
int value;
|
|
value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
|
return value;
|
|
}
|
|
|
|
// open an MVE file
|
|
MVEFILE *mvefile_open(char *filename)
|
|
{
|
|
int cf_opened = 0;
|
|
int mve_valid = 1;
|
|
char lower_name[MAX_FILENAME_LEN];
|
|
char buffer[20];
|
|
MVEFILE *file;
|
|
|
|
// create the file
|
|
file = (MVEFILE *)vm_malloc(sizeof(MVEFILE));
|
|
|
|
// set defaults
|
|
file->stream = NULL;
|
|
file->cur_chunk = NULL;
|
|
file->buf_size = 0;
|
|
file->cur_fill = 0;
|
|
file->next_segment = 0;
|
|
|
|
// lower case filename for checking
|
|
strncpy(lower_name, filename, strlen(filename)+1);
|
|
strlwr(lower_name);
|
|
|
|
char *p = strchr( lower_name, '.' );
|
|
if ( p ) *p = 0;
|
|
|
|
strcat_s( lower_name, ".mve" );
|
|
|
|
// NOTE: CF_TYPE *must* be ANY to get movies off of the CDs
|
|
// assume lower case filename for *nix
|
|
file->stream = cfopen(lower_name, "rb", CFILE_NORMAL, CF_TYPE_ANY);
|
|
if ( file->stream ) {
|
|
cf_opened = 1;
|
|
}
|
|
|
|
if (!cf_opened) {
|
|
mvefile_close(file);
|
|
return NULL;
|
|
}
|
|
|
|
// initialize the buffer
|
|
file->cur_chunk = (ubyte *)vm_malloc(100 + 1024);
|
|
file->buf_size = 100 + 1024;
|
|
|
|
// verify the file's header
|
|
cfread_string(buffer, 20, file->stream);
|
|
|
|
if (strcmp(buffer, MVE_HEADER))
|
|
mve_valid = 0;
|
|
|
|
if (cfread_short(file->stream) != MVE_HDRCONST1)
|
|
mve_valid = 0;
|
|
|
|
if (cfread_short(file->stream) != MVE_HDRCONST2)
|
|
mve_valid = 0;
|
|
|
|
if (cfread_short(file->stream) != MVE_HDRCONST3)
|
|
mve_valid = 0;
|
|
|
|
if (!mve_valid) {
|
|
mvefile_close(file);
|
|
return NULL;
|
|
}
|
|
|
|
// now, prefetch the next chunk
|
|
mvefile_fetch_next_chunk(file);
|
|
|
|
return file;
|
|
}
|
|
|
|
// close a MVE file
|
|
void mvefile_close(MVEFILE *file)
|
|
{
|
|
// free the stream
|
|
if (file->stream)
|
|
cfclose(file->stream);
|
|
|
|
file->stream = NULL;
|
|
|
|
// free the buffer
|
|
if (file->cur_chunk)
|
|
vm_free(file->cur_chunk);
|
|
|
|
file->cur_chunk = NULL;
|
|
|
|
// not strictly necessary
|
|
file->buf_size = 0;
|
|
file->cur_fill = 0;
|
|
file->next_segment = 0;
|
|
|
|
// free the struct
|
|
vm_free(file);
|
|
}
|
|
|
|
// get the size of the next segment
|
|
int mvefile_get_next_segment_size(MVEFILE *file)
|
|
{
|
|
// if nothing is cached, fail
|
|
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
|
return -1;
|
|
|
|
// if we don't have enough data to get a segment, fail
|
|
if (file->cur_fill - file->next_segment < 4)
|
|
return -1;
|
|
|
|
// otherwise, get the data length
|
|
return mve_get_short(file->cur_chunk + file->next_segment);
|
|
}
|
|
|
|
// get type of next segment in chunk (0xff if no more segments in chunk)
|
|
ubyte mvefile_get_next_segment_major(MVEFILE *file)
|
|
{
|
|
// if nothing is cached, fail
|
|
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
|
return 0xff;
|
|
|
|
// if we don't have enough data to get a segment, fail
|
|
if (file->cur_fill - file->next_segment < 4)
|
|
return 0xff;
|
|
|
|
// otherwise, get the data length
|
|
return file->cur_chunk[file->next_segment + 2];
|
|
}
|
|
|
|
// get subtype (version) of next segment in chunk (0xff if no more segments in chunk)
|
|
ubyte mvefile_get_next_segment_minor(MVEFILE *file)
|
|
{
|
|
// if nothing is cached, fail
|
|
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
|
return 0xff;
|
|
|
|
// if we don't have enough data to get a segment, fail
|
|
if (file->cur_fill - file->next_segment < 4)
|
|
return 0xff;
|
|
|
|
// otherwise, get the data length
|
|
return file->cur_chunk[file->next_segment + 3];
|
|
}
|
|
|
|
// see next segment (return NULL if no next segment)
|
|
ubyte *mvefile_get_next_segment(MVEFILE *file)
|
|
{
|
|
// if nothing is cached, fail
|
|
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
|
return NULL;
|
|
|
|
// if we don't have enough data to get a segment, fail
|
|
if (file->cur_fill - file->next_segment < 4)
|
|
return NULL;
|
|
|
|
// otherwise, get the data length
|
|
return file->cur_chunk + file->next_segment + 4;
|
|
}
|
|
|
|
// advance to next segment
|
|
void mvefile_advance_segment(MVEFILE *file)
|
|
{
|
|
// if nothing is cached, fail
|
|
if (file->cur_chunk == NULL || file->next_segment >= file->cur_fill)
|
|
return;
|
|
|
|
// if we don't have enough data to get a segment, fail
|
|
if (file->cur_fill - file->next_segment < 4)
|
|
return;
|
|
|
|
// else, advance to next segment
|
|
file->next_segment += (4 + mve_get_ushort(file->cur_chunk + file->next_segment));
|
|
}
|
|
|
|
// fetch the next chunk (return 0 if at end of stream)
|
|
int mvefile_fetch_next_chunk(MVEFILE *file)
|
|
{
|
|
ubyte buffer[4];
|
|
ubyte *new_buffer;
|
|
ushort length;
|
|
|
|
// fail if not open
|
|
if (file->stream == NULL)
|
|
return 0;
|
|
|
|
// fail if we can't read the next segment descriptor
|
|
if (cfread(buffer, 1, 4, file->stream) < 4)
|
|
return 0;
|
|
|
|
// pull out the next length
|
|
length = mve_get_short(buffer);
|
|
|
|
// setup a new buffer if needed --
|
|
// only allocate new buffer is old one is too small
|
|
if (length > file->buf_size) {
|
|
// allocate new buffer
|
|
new_buffer = (ubyte *)vm_malloc(100 + length);
|
|
|
|
// copy old data
|
|
if (file->cur_chunk && file->cur_fill)
|
|
memcpy(new_buffer, file->cur_chunk, file->cur_fill);
|
|
|
|
// free old buffer
|
|
if (file->cur_chunk) {
|
|
vm_free(file->cur_chunk);
|
|
file->cur_chunk = NULL;
|
|
}
|
|
|
|
// install new buffer
|
|
file->cur_chunk = new_buffer;
|
|
file->buf_size = 100 + length;
|
|
}
|
|
|
|
// read the chunk
|
|
if (length > 0) {
|
|
if (cfread(file->cur_chunk, 1, length, file->stream) < length)
|
|
return 0;
|
|
}
|
|
|
|
file->cur_fill = length;
|
|
file->next_segment = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// -----------------------------------------------------------
|
|
// public MVESTREAM functions
|
|
// -----------------------------------------------------------
|
|
|
|
// open an MVE stream
|
|
MVESTREAM *mve_open(char *filename)
|
|
{
|
|
MVESTREAM *stream;
|
|
|
|
// allocate
|
|
stream = (MVESTREAM *)vm_malloc(sizeof(MVESTREAM));
|
|
|
|
// defaults
|
|
stream->movie = NULL;
|
|
|
|
// open
|
|
stream->movie = mvefile_open(filename);
|
|
|
|
if (stream->movie == NULL) {
|
|
mve_close(stream);
|
|
return NULL;
|
|
}
|
|
|
|
return stream;
|
|
}
|
|
|
|
// close an MVE stream
|
|
void mve_close(MVESTREAM *stream)
|
|
{
|
|
// close MVEFILE
|
|
if (stream->movie)
|
|
mvefile_close(stream->movie);
|
|
|
|
stream->movie = NULL;
|
|
|
|
vm_free(stream);
|
|
}
|
|
|
|
// play next chunk
|
|
int mve_play_next_chunk(MVESTREAM *stream)
|
|
{
|
|
ubyte major, minor;
|
|
ubyte *data;
|
|
int len;
|
|
|
|
// loop over segments
|
|
major = mvefile_get_next_segment_major(stream->movie);
|
|
while (major != 0xff) {
|
|
// check whether to handle the segment
|
|
if (major < 32) {
|
|
minor = mvefile_get_next_segment_minor(stream->movie);
|
|
len = mvefile_get_next_segment_size(stream->movie);
|
|
data = mvefile_get_next_segment(stream->movie);
|
|
|
|
switch (major) {
|
|
case 0x00:
|
|
mve_end_movie();
|
|
break;
|
|
case 0x01:
|
|
mve_end_chunk();
|
|
break;
|
|
case 0x02:
|
|
mve_timer_create(data);
|
|
break;
|
|
case 0x03:
|
|
mve_audio_createbuf(minor, data);
|
|
break;
|
|
case 0x04:
|
|
mve_audio_play();
|
|
break;
|
|
case 0x05:
|
|
if (!mve_video_createbuf(minor, data))
|
|
return 0;
|
|
break;
|
|
case 0x07:
|
|
mve_video_display();
|
|
break;
|
|
case 0x08:
|
|
mve_audio_data(major, data);
|
|
break;
|
|
case 0x09:
|
|
mve_audio_data(major, data);
|
|
break;
|
|
case 0x0a:
|
|
if (!mve_video_init(data))
|
|
return 0;
|
|
break;
|
|
case 0x0c:
|
|
mve_video_palette(data);
|
|
break;
|
|
case 0x0f:
|
|
mve_video_codemap(data, len);
|
|
break;
|
|
case 0x11:
|
|
mve_video_data(data, len);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// advance to next segment
|
|
mvefile_advance_segment(stream->movie);
|
|
major = mvefile_get_next_segment_major(stream->movie);
|
|
}
|
|
|
|
if (!mvefile_fetch_next_chunk(stream->movie))
|
|
return 0;
|
|
|
|
// return status
|
|
return 1;
|
|
}
|