/* * parse-ipmve.c: A simple parser for Interplay MVE multimedia files * by Mike Melanson (mike at multimedia.cx) * * Use this program however you see fit. * * The most common use for this program is to compile it as: * cc parse-ipmve.c -o parse-ipmve * and then run it against an Interplay MVE files: * parse-ipmve * * For more information on the Interplay MVE file format, visit: * http://www.multimedia.cx/codecs/ */ #include #include #define SIGNATURE "Interplay MVE File\x1A\x00" #define SIGNATURE_SIZE 20 #define CHUNK_PREAMBLE_SIZE 4 #define OPCODE_PREAMBLE_SIZE 4 #define CHUNK_INIT_AUDIO 0x0000 #define CHUNK_AUDIO_ONLY 0x0001 #define CHUNK_INIT_VIDEO 0x0002 #define CHUNK_VIDEO 0x0003 #define CHUNK_SHUTDOWN 0x0004 #define CHUNK_END 0x0005 #define OPCODE_END_OF_STREAM 0x00 #define OPCODE_END_OF_CHUNK 0x01 #define OPCODE_CREATE_TIMER 0x02 #define OPCODE_INIT_AUDIO_BUFFERS 0x03 #define OPCODE_START_STOP_AUDIO 0x04 #define OPCODE_INIT_VIDEO_BUFFERS 0x05 #define OPCODE_UNKNOWN_06 0x06 #define OPCODE_SEND_BUFFER 0x07 #define OPCODE_AUDIO_FRAME 0x08 #define OPCODE_SILENCE_FRAME 0x09 #define OPCODE_INIT_VIDEO_MODE 0x0A #define OPCODE_CREATE_GRADIENT 0x0B #define OPCODE_SET_PALETTE 0x0C #define OPCODE_SET_PALETTE_COMPRESSED 0x0D #define OPCODE_UNKNOWN_0E 0x0E #define OPCODE_SET_DECODING_MAP 0x0F #define OPCODE_UNKNOWN_10 0x10 #define OPCODE_VIDEO_DATA 0x11 #define OPCODE_UNKNOWN_12 0x12 #define OPCODE_UNKNOWN_13 0x13 #define OPCODE_UNKNOWN_14 0x14 #define OPCODE_UNKNOWN_15 0x15 /* macro to fetch 16-bit little-endian words from a bytestream */ #define LE_16(x) ((*x) | ((*(x+1)) << 8)) int main(int argc, char *argv[]) { FILE *f; unsigned char signature[SIGNATURE_SIZE]; unsigned char chunk_preamble[CHUNK_PREAMBLE_SIZE]; int chunk_type; int chunk_size; unsigned char opcode_preamble[OPCODE_PREAMBLE_SIZE]; unsigned char opcode_type; unsigned char opcode_version; int opcode_size; printf ("Interplay MVE file analysis tool\n"); printf (" by Mike Melanson (mike at multimedia.cx)\n\n"); if (argc < 2) { printf ("usage: parse-ipmve \n"); return 0; } f = fopen(argv[1], "r"); if (!f) { perror(argv[1]); return 0; } /* check the header */ if (fread(signature, SIGNATURE_SIZE, 1, f) != 1) { perror(argv[1]); fclose(f); return 0; } if (memcmp(signature, SIGNATURE, SIGNATURE_SIZE) != 0) { printf ("invalid signature, %s is not an Interplay MVE file\n", argv[1]); fclose(f); return 0; } printf ("Signature is valid, %s is an Interplay MVE file\n", argv[1]); /* skip the next 6 bytes */ fseek(f, 6, SEEK_CUR); /* iterate through the chunks in the file */ while(chunk_type != CHUNK_END) { if (fread(chunk_preamble, CHUNK_PREAMBLE_SIZE, 1, f) != 1) { printf ("could not read from file (EOF?)\n"); fclose(f); return 0; } chunk_size = LE_16(&chunk_preamble[0]); chunk_type = LE_16(&chunk_preamble[2]); printf ("\nchunk type %04X, 0x%04X bytes: ", chunk_type, chunk_size); switch (chunk_type) { case CHUNK_INIT_AUDIO: printf ("initialize audio\n"); break; case CHUNK_AUDIO_ONLY: printf ("audio only\n"); break; case CHUNK_INIT_VIDEO: printf ("initialize video\n"); break; case CHUNK_VIDEO: printf ("video (and audio)\n"); break; case CHUNK_SHUTDOWN: printf ("shutdown\n"); break; case CHUNK_END: printf ("end\n"); break; default: printf (" *** unknown chunk type\n"); break; } printf ("------------------------------------------------------\n"); /* iterate through individual opcodes */ while (chunk_size > 0) { if (fread(opcode_preamble, OPCODE_PREAMBLE_SIZE, 1, f) != 1) { printf ("could not read from file (EOF?)\n"); fclose(f); return 0; } chunk_size -= 4; opcode_size = LE_16(&opcode_preamble[0]); opcode_type = opcode_preamble[2]; opcode_version = opcode_preamble[3]; chunk_size -= opcode_size; printf (" opcode type %02X, version %d, 0x%04X bytes: ", opcode_type, opcode_version, opcode_size); switch (opcode_type) { case OPCODE_END_OF_STREAM: printf ("end of stream\n"); break; case OPCODE_END_OF_CHUNK: printf ("end of chunk\n"); break; case OPCODE_CREATE_TIMER: printf ("create timer\n"); break; case OPCODE_INIT_AUDIO_BUFFERS: printf ("initialize audio buffers\n"); break; case OPCODE_START_STOP_AUDIO: printf ("start/stop audio\n"); break; case OPCODE_INIT_VIDEO_BUFFERS: printf ("initialize video buffers\n"); break; case OPCODE_UNKNOWN_06: case OPCODE_UNKNOWN_0E: case OPCODE_UNKNOWN_10: case OPCODE_UNKNOWN_12: case OPCODE_UNKNOWN_13: case OPCODE_UNKNOWN_14: case OPCODE_UNKNOWN_15: printf ("unknown (but documented) opcode %02X\n", opcode_type); break; case OPCODE_SEND_BUFFER: printf ("send buffer\n"); break; case OPCODE_AUDIO_FRAME: printf ("audio frame\n"); break; case OPCODE_SILENCE_FRAME: printf ("silence frame\n"); break; case OPCODE_INIT_VIDEO_MODE: printf ("initialize video mode\n"); break; case OPCODE_CREATE_GRADIENT: printf ("create gradient\n"); break; case OPCODE_SET_PALETTE: printf ("set palette\n"); break; case OPCODE_SET_PALETTE_COMPRESSED: printf ("set palette compressed\n"); break; case OPCODE_SET_DECODING_MAP: printf ("set decoding map\n"); break; case OPCODE_VIDEO_DATA: printf ("set video data\n"); break; default: printf (" *** unknown opcode type\n"); break; } /* skip over the meat of the opcode */ fseek(f, opcode_size, SEEK_CUR); } } fclose(f); return 0; }