|
808
|
1 /*
|
|
|
2 * American Laser Games MM Format Demuxer
|
|
|
3 * Copyright (c) 2006 Peter Ross
|
|
|
4 *
|
|
|
5 * This file is part of FFmpeg.
|
|
|
6 *
|
|
|
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.
|
|
|
11 *
|
|
|
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.
|
|
|
16 *
|
|
|
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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
20 */
|
|
|
21
|
|
|
22 /**
|
|
|
23 * @file mm.c
|
|
|
24 * American Laser Games MM Format Demuxer
|
|
|
25 * by Peter Ross (suxen_drol at hotmail dot com)
|
|
|
26 *
|
|
|
27 * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games,
|
|
|
28 * including Mad Dog McCree and Crime Patrol.
|
|
|
29 *
|
|
|
30 * Technical details here:
|
|
|
31 * http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM
|
|
|
32 */
|
|
|
33
|
|
|
34 #include "avformat.h"
|
|
|
35
|
|
|
36 #define MM_PREAMBLE_SIZE 6
|
|
|
37
|
|
|
38 #define MM_TYPE_HEADER 0x0
|
|
|
39 #define MM_TYPE_INTER 0x5
|
|
|
40 #define MM_TYPE_INTRA 0x8
|
|
|
41 #define MM_TYPE_INTRA_HH 0xc
|
|
|
42 #define MM_TYPE_INTER_HH 0xd
|
|
|
43 #define MM_TYPE_INTRA_HHV 0xe
|
|
|
44 #define MM_TYPE_INTER_HHV 0xf
|
|
|
45 #define MM_TYPE_AUDIO 0x15
|
|
|
46 #define MM_TYPE_PALETTE 0x31
|
|
|
47
|
|
|
48 #define MM_HEADER_LEN_V 0x16 /* video only */
|
|
|
49 #define MM_HEADER_LEN_AV 0x18 /* video + audio */
|
|
|
50
|
|
|
51 #define MM_PALETTE_COUNT 128
|
|
|
52 #define MM_PALETTE_SIZE (MM_PALETTE_COUNT*3)
|
|
|
53
|
|
|
54 typedef struct {
|
|
|
55 AVPaletteControl palette_control;
|
|
|
56 unsigned int audio_pts, video_pts;
|
|
|
57 } MmDemuxContext;
|
|
|
58
|
|
|
59 static int mm_probe(AVProbeData *p)
|
|
|
60 {
|
|
|
61 /* the first chunk is always the header */
|
|
|
62 if (p->buf_size < MM_PREAMBLE_SIZE)
|
|
|
63 return 0;
|
|
|
64 if (LE_16(&p->buf[0]) != MM_TYPE_HEADER)
|
|
|
65 return 0;
|
|
|
66 if (LE_32(&p->buf[2]) != MM_HEADER_LEN_V && LE_32(&p->buf[2]) != MM_HEADER_LEN_AV)
|
|
|
67 return 0;
|
|
|
68
|
|
|
69 /* only return half certainty since this check is a bit sketchy */
|
|
|
70 return AVPROBE_SCORE_MAX / 2;
|
|
|
71 }
|
|
|
72
|
|
|
73 static int mm_read_header(AVFormatContext *s,
|
|
|
74 AVFormatParameters *ap)
|
|
|
75 {
|
|
|
76 MmDemuxContext *mm = (MmDemuxContext *)s->priv_data;
|
|
|
77 ByteIOContext *pb = &s->pb;
|
|
|
78 AVStream *st;
|
|
|
79
|
|
|
80 unsigned int type, length;
|
|
|
81 unsigned int frame_rate, width, height;
|
|
|
82
|
|
|
83 type = get_le16(pb);
|
|
|
84 length = get_le32(pb);
|
|
|
85
|
|
|
86 if (type != MM_TYPE_HEADER)
|
|
|
87 return AVERROR_INVALIDDATA;
|
|
|
88
|
|
|
89 /* read header */
|
|
|
90 get_le16(pb); /* total number of chunks */
|
|
|
91 frame_rate = get_le16(pb);
|
|
|
92 get_le16(pb); /* ibm-pc video bios mode */
|
|
|
93 width = get_le16(pb);
|
|
|
94 height = get_le16(pb);
|
|
|
95 url_fseek(pb, length - 10, SEEK_CUR); /* unknown data */
|
|
|
96
|
|
|
97 /* video stream */
|
|
|
98 st = av_new_stream(s, 0);
|
|
|
99 if (!st)
|
|
|
100 return AVERROR_NOMEM;
|
|
|
101 st->codec->codec_type = CODEC_TYPE_VIDEO;
|
|
|
102 st->codec->codec_id = CODEC_ID_MMVIDEO;
|
|
|
103 st->codec->codec_tag = 0; /* no fourcc */
|
|
|
104 st->codec->width = width;
|
|
|
105 st->codec->height = height;
|
|
|
106 st->codec->palctrl = &mm->palette_control;
|
|
|
107 av_set_pts_info(st, 64, 1, frame_rate);
|
|
|
108
|
|
|
109 /* audio stream */
|
|
|
110 if (length == MM_HEADER_LEN_AV) {
|
|
|
111 st = av_new_stream(s, 0);
|
|
|
112 if (!st)
|
|
|
113 return AVERROR_NOMEM;
|
|
|
114 st->codec->codec_type = CODEC_TYPE_AUDIO;
|
|
|
115 st->codec->codec_tag = 0; /* no fourcc */
|
|
|
116 st->codec->codec_id = CODEC_ID_PCM_U8;
|
|
|
117 st->codec->channels = 1;
|
|
|
118 st->codec->sample_rate = 8000;
|
|
|
119 av_set_pts_info(st, 64, 1, 8000); /* 8000 hz */
|
|
|
120 }
|
|
|
121
|
|
|
122 mm->palette_control.palette_changed = 0;
|
|
|
123 mm->audio_pts = 0;
|
|
|
124 mm->video_pts = 0;
|
|
|
125 return 0;
|
|
|
126 }
|
|
|
127
|
|
|
128 static int mm_read_packet(AVFormatContext *s,
|
|
|
129 AVPacket *pkt)
|
|
|
130 {
|
|
|
131 MmDemuxContext *mm = (MmDemuxContext *)s->priv_data;
|
|
|
132 ByteIOContext *pb = &s->pb;
|
|
|
133 unsigned char preamble[MM_PREAMBLE_SIZE];
|
|
|
134 unsigned char pal[MM_PALETTE_SIZE];
|
|
|
135 unsigned int type, length;
|
|
|
136 int i;
|
|
|
137
|
|
|
138 while(1) {
|
|
|
139
|
|
|
140 if (get_buffer(pb, preamble, MM_PREAMBLE_SIZE) != MM_PREAMBLE_SIZE) {
|
|
|
141 return AVERROR_IO;
|
|
|
142 }
|
|
|
143
|
|
|
144 type = LE_16(&preamble[0]);
|
|
|
145 length = LE_16(&preamble[2]);
|
|
|
146
|
|
|
147 switch(type) {
|
|
|
148 case MM_TYPE_PALETTE :
|
|
|
149 url_fseek(pb, 4, SEEK_CUR); /* unknown data */
|
|
|
150 if (get_buffer(pb, pal, MM_PALETTE_SIZE) != MM_PALETTE_SIZE)
|
|
|
151 return AVERROR_IO;
|
|
|
152 url_fseek(pb, length - (4 + MM_PALETTE_SIZE), SEEK_CUR);
|
|
|
153
|
|
|
154 for (i=0; i<MM_PALETTE_COUNT; i++) {
|
|
|
155 int r = pal[i*3 + 0];
|
|
|
156 int g = pal[i*3 + 1];
|
|
|
157 int b = pal[i*3 + 2];
|
|
|
158 mm->palette_control.palette[i] = (r << 16) | (g << 8) | (b);
|
|
|
159 /* repeat palette, where each components is multiplied by four */
|
|
|
160 mm->palette_control.palette[i+128] = (r << 18) | (g << 10) | (b<<2);
|
|
|
161 }
|
|
|
162 mm->palette_control.palette_changed = 1;
|
|
|
163 break;
|
|
|
164
|
|
|
165 case MM_TYPE_INTER :
|
|
|
166 case MM_TYPE_INTRA :
|
|
|
167 case MM_TYPE_INTRA_HH :
|
|
|
168 case MM_TYPE_INTER_HH :
|
|
|
169 case MM_TYPE_INTRA_HHV :
|
|
|
170 case MM_TYPE_INTER_HHV :
|
|
|
171 /* output preamble + data */
|
|
|
172 if (av_new_packet(pkt, length + MM_PREAMBLE_SIZE))
|
|
|
173 return AVERROR_NOMEM;
|
|
|
174 memcpy(pkt->data, preamble, MM_PREAMBLE_SIZE);
|
|
|
175 if (get_buffer(pb, pkt->data + MM_PREAMBLE_SIZE, length) != length)
|
|
|
176 return AVERROR_IO;
|
|
|
177 pkt->size = length + MM_PREAMBLE_SIZE;
|
|
|
178 pkt->stream_index = 0;
|
|
|
179 pkt->pts = mm->video_pts++;
|
|
|
180 return 0;
|
|
|
181
|
|
|
182 case MM_TYPE_AUDIO :
|
|
|
183 if (av_get_packet(&s->pb, pkt, length)<0)
|
|
|
184 return AVERROR_NOMEM;
|
|
|
185 pkt->size = length;
|
|
|
186 pkt->stream_index = 1;
|
|
|
187 pkt->pts = mm->audio_pts++;
|
|
|
188 return 0;
|
|
|
189
|
|
|
190 default :
|
|
|
191 av_log(NULL, AV_LOG_INFO, "mm: unknown chunk type 0x%x\n", type);
|
|
|
192 url_fseek(pb, length, SEEK_CUR);
|
|
|
193 }
|
|
|
194 }
|
|
|
195
|
|
|
196 return 0;
|
|
|
197 }
|
|
|
198
|
|
|
199 static int mm_read_close(AVFormatContext *s)
|
|
|
200 {
|
|
|
201 return 0;
|
|
|
202 }
|
|
|
203
|
|
|
204 AVInputFormat mm_demuxer = {
|
|
|
205 "mm",
|
|
|
206 "American Laser Games MM format",
|
|
|
207 sizeof(MmDemuxContext),
|
|
|
208 mm_probe,
|
|
|
209 mm_read_header,
|
|
|
210 mm_read_packet,
|
|
|
211 mm_read_close,
|
|
|
212 };
|