2 * Wing Commander III Movie (.mve) File Demuxer
3 * Copyright (c) 2003 The ffmpeg Project
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * @file libavformat/wc3movie.c
24 * Wing Commander III Movie file demuxer
25 * by Mike Melanson (melanson@pcisys.net)
26 * for more information on the WC3 .mve file format, visit:
27 * http://www.pcisys.net/~melanson/codecs/
30 #include "libavutil/intreadwrite.h"
33 #define WC3_PREAMBLE_SIZE 8
35 #define FORM_TAG MKTAG('F', 'O', 'R', 'M')
36 #define MOVE_TAG MKTAG('M', 'O', 'V', 'E')
37 #define PC__TAG MKTAG('_', 'P', 'C', '_')
38 #define SOND_TAG MKTAG('S', 'O', 'N', 'D')
39 #define BNAM_TAG MKTAG('B', 'N', 'A', 'M')
40 #define SIZE_TAG MKTAG('S', 'I', 'Z', 'E')
41 #define PALT_TAG MKTAG('P', 'A', 'L', 'T')
42 #define INDX_TAG MKTAG('I', 'N', 'D', 'X')
43 #define BRCH_TAG MKTAG('B', 'R', 'C', 'H')
44 #define SHOT_TAG MKTAG('S', 'H', 'O', 'T')
45 #define VGA__TAG MKTAG('V', 'G', 'A', ' ')
46 #define TEXT_TAG MKTAG('T', 'E', 'X', 'T')
47 #define AUDI_TAG MKTAG('A', 'U', 'D', 'I')
49 /* video resolution unless otherwise specified */
50 #define WC3_DEFAULT_WIDTH 320
51 #define WC3_DEFAULT_HEIGHT 165
53 /* always use the same PCM audio parameters */
54 #define WC3_SAMPLE_RATE 22050
55 #define WC3_AUDIO_CHANNELS 1
56 #define WC3_AUDIO_BITS 16
58 /* nice, constant framerate */
59 #define WC3_FRAME_FPS 15
61 #define PALETTE_SIZE (256 * 3)
62 #define PALETTE_COUNT 256
64 typedef struct Wc3DemuxContext {
67 unsigned char *palettes;
70 int video_stream_index;
71 int audio_stream_index;
73 AVPaletteControl palette_control;
77 /* bizarre palette lookup table */
78 static const unsigned char wc3_pal_lookup[] = {
79 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0E,
80 0x10, 0x12, 0x13, 0x15, 0x16, 0x18, 0x19, 0x1A,
81 0x1C, 0x1D, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25,
82 0x27, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E, 0x2F,
83 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38, 0x39,
84 0x3A, 0x3B, 0x3C, 0x3D, 0x3F, 0x40, 0x41, 0x42,
85 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B,
86 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
87 0x54, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C,
88 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64,
89 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C,
90 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74,
91 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
92 0x7D, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83,
93 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B,
94 0x8C, 0x8D, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
95 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x99,
96 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1,
97 0xA2, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
98 0xA9, 0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
99 0xB0, 0xB1, 0xB2, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
100 0xB7, 0xB8, 0xB9, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD,
101 0xBE, 0xBF, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4,
102 0xC5, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB,
103 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD0, 0xD1,
104 0xD2, 0xD3, 0xD4, 0xD5, 0xD5, 0xD6, 0xD7, 0xD8,
105 0xD9, 0xDA, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
106 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE4, 0xE5,
107 0xE6, 0xE7, 0xE8, 0xE9, 0xE9, 0xEA, 0xEB, 0xEC,
108 0xED, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2,
109 0xF3, 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9,
110 0xFA, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD
114 static int wc3_probe(AVProbeData *p)
116 if (p->buf_size < 12)
119 if ((AV_RL32(&p->buf[0]) != FORM_TAG) ||
120 (AV_RL32(&p->buf[8]) != MOVE_TAG))
123 return AVPROBE_SCORE_MAX;
126 static int wc3_read_header(AVFormatContext *s,
127 AVFormatParameters *ap)
129 Wc3DemuxContext *wc3 = s->priv_data;
130 ByteIOContext *pb = s->pb;
131 unsigned int fourcc_tag;
134 unsigned char preamble[WC3_PREAMBLE_SIZE];
136 int current_palette = 0;
139 unsigned char rotate;
141 /* default context members */
142 wc3->width = WC3_DEFAULT_WIDTH;
143 wc3->height = WC3_DEFAULT_HEIGHT;
144 wc3->palettes = NULL;
145 wc3->palette_count = 0;
147 wc3->video_stream_index = wc3->audio_stream_index = 0;
149 /* skip the first 3 32-bit numbers */
150 url_fseek(pb, 12, SEEK_CUR);
152 /* traverse through the chunks and load the header information before
153 * the first BRCH tag */
154 if ((ret = get_buffer(pb, preamble, WC3_PREAMBLE_SIZE)) !=
157 fourcc_tag = AV_RL32(&preamble[0]);
158 size = (AV_RB32(&preamble[4]) + 1) & (~1);
161 switch (fourcc_tag) {
165 /* SOND unknown, INDX unnecessary; ignore both */
166 url_fseek(pb, size, SEEK_CUR);
170 /* need the number of palettes */
171 url_fseek(pb, 8, SEEK_CUR);
172 if ((ret = get_buffer(pb, preamble, 4)) != 4)
174 wc3->palette_count = AV_RL32(&preamble[0]);
175 if((unsigned)wc3->palette_count >= UINT_MAX / PALETTE_SIZE){
176 wc3->palette_count= 0;
179 wc3->palettes = av_malloc(wc3->palette_count * PALETTE_SIZE);
183 /* load up the name */
184 if ((unsigned)size < 512)
185 bytes_to_read = size;
188 if ((ret = get_buffer(pb, s->title, bytes_to_read)) != bytes_to_read)
193 /* video resolution override */
194 if ((ret = get_buffer(pb, preamble, WC3_PREAMBLE_SIZE)) !=
197 wc3->width = AV_RL32(&preamble[0]);
198 wc3->height = AV_RL32(&preamble[4]);
202 /* one of several palettes */
203 if ((unsigned)current_palette >= wc3->palette_count)
204 return AVERROR_INVALIDDATA;
205 if ((ret = get_buffer(pb,
206 &wc3->palettes[current_palette * PALETTE_SIZE],
207 PALETTE_SIZE)) != PALETTE_SIZE)
210 /* transform the current palette in place */
211 for (i = current_palette * PALETTE_SIZE;
212 i < (current_palette + 1) * PALETTE_SIZE; i++) {
213 /* rotate each palette component left by 2 and use the result
214 * as an index into the color component table */
215 rotate = ((wc3->palettes[i] << 2) & 0xFF) |
216 ((wc3->palettes[i] >> 6) & 0xFF);
217 wc3->palettes[i] = wc3_pal_lookup[rotate];
223 av_log(s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
224 preamble[0], preamble[1], preamble[2], preamble[3],
225 preamble[0], preamble[1], preamble[2], preamble[3]);
226 return AVERROR_INVALIDDATA;
230 if ((ret = get_buffer(pb, preamble, WC3_PREAMBLE_SIZE)) !=
233 fourcc_tag = AV_RL32(&preamble[0]);
234 /* chunk sizes are 16-bit aligned */
235 size = (AV_RB32(&preamble[4]) + 1) & (~1);
237 } while (fourcc_tag != BRCH_TAG);
239 /* initialize the decoder streams */
240 st = av_new_stream(s, 0);
242 return AVERROR(ENOMEM);
243 av_set_pts_info(st, 33, 1, WC3_FRAME_FPS);
244 wc3->video_stream_index = st->index;
245 st->codec->codec_type = CODEC_TYPE_VIDEO;
246 st->codec->codec_id = CODEC_ID_XAN_WC3;
247 st->codec->codec_tag = 0; /* no fourcc */
248 st->codec->width = wc3->width;
249 st->codec->height = wc3->height;
251 /* palette considerations */
252 st->codec->palctrl = &wc3->palette_control;
254 st = av_new_stream(s, 0);
256 return AVERROR(ENOMEM);
257 av_set_pts_info(st, 33, 1, WC3_FRAME_FPS);
258 wc3->audio_stream_index = st->index;
259 st->codec->codec_type = CODEC_TYPE_AUDIO;
260 st->codec->codec_id = CODEC_ID_PCM_S16LE;
261 st->codec->codec_tag = 1;
262 st->codec->channels = WC3_AUDIO_CHANNELS;
263 st->codec->bits_per_coded_sample = WC3_AUDIO_BITS;
264 st->codec->sample_rate = WC3_SAMPLE_RATE;
265 st->codec->bit_rate = st->codec->channels * st->codec->sample_rate *
266 st->codec->bits_per_coded_sample;
267 st->codec->block_align = WC3_AUDIO_BITS * WC3_AUDIO_CHANNELS;
272 static int wc3_read_packet(AVFormatContext *s,
275 Wc3DemuxContext *wc3 = s->priv_data;
276 ByteIOContext *pb = s->pb;
277 unsigned int fourcc_tag;
281 unsigned char preamble[WC3_PREAMBLE_SIZE];
282 unsigned char text[1024];
283 unsigned int palette_number;
285 unsigned char r, g, b;
286 int base_palette_index;
288 while (!packet_read) {
290 /* get the next chunk preamble */
291 if ((ret = get_buffer(pb, preamble, WC3_PREAMBLE_SIZE)) !=
295 fourcc_tag = AV_RL32(&preamble[0]);
296 /* chunk sizes are 16-bit aligned */
297 size = (AV_RB32(&preamble[4]) + 1) & (~1);
299 switch (fourcc_tag) {
306 /* load up new palette */
307 if ((ret = get_buffer(pb, preamble, 4)) != 4)
309 palette_number = AV_RL32(&preamble[0]);
310 if (palette_number >= wc3->palette_count)
311 return AVERROR_INVALIDDATA;
312 base_palette_index = palette_number * PALETTE_COUNT * 3;
313 for (i = 0; i < PALETTE_COUNT; i++) {
314 r = wc3->palettes[base_palette_index + i * 3 + 0];
315 g = wc3->palettes[base_palette_index + i * 3 + 1];
316 b = wc3->palettes[base_palette_index + i * 3 + 2];
317 wc3->palette_control.palette[i] = (r << 16) | (g << 8) | (b);
319 wc3->palette_control.palette_changed = 1;
323 /* send out video chunk */
324 ret= av_get_packet(pb, pkt, size);
325 pkt->stream_index = wc3->video_stream_index;
335 url_fseek(pb, size, SEEK_CUR);
337 if ((unsigned)size > sizeof(text) || (ret = get_buffer(pb, text, size)) != size)
341 av_log (s, AV_LOG_DEBUG, "Subtitle time!\n");
342 av_log (s, AV_LOG_DEBUG, " inglish: %s\n", &text[i + 1]);
344 av_log (s, AV_LOG_DEBUG, " doytsch: %s\n", &text[i + 1]);
346 av_log (s, AV_LOG_DEBUG, " fronsay: %s\n", &text[i + 1]);
352 /* send out audio chunk */
353 ret= av_get_packet(pb, pkt, size);
354 pkt->stream_index = wc3->audio_stream_index;
359 /* time to advance pts */
366 av_log (s, AV_LOG_ERROR, " unrecognized WC3 chunk: %c%c%c%c (0x%02X%02X%02X%02X)\n",
367 preamble[0], preamble[1], preamble[2], preamble[3],
368 preamble[0], preamble[1], preamble[2], preamble[3]);
369 ret = AVERROR_INVALIDDATA;
378 static int wc3_read_close(AVFormatContext *s)
380 Wc3DemuxContext *wc3 = s->priv_data;
382 av_free(wc3->palettes);
387 AVInputFormat wc3_demuxer = {
389 NULL_IF_CONFIG_SMALL("Wing Commander III movie format"),
390 sizeof(Wc3DemuxContext),