2 * American Laser Games MM Video Decoder
3 * Copyright (c) 2006 Peter Ross
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
24 * American Laser Games MM Video Decoder
25 * by Peter Ross (suxen_drol at hotmail dot com)
27 * The MM format was used by IBM-PC ports of ALG's "arcade shooter" games,
28 * including Mad Dog McCree and Crime Patrol.
30 * Technical details here:
31 * http://wiki.multimedia.cx/index.php?title=American_Laser_Games_MM
36 #define MM_PREAMBLE_SIZE 6
38 #define MM_TYPE_INTER 0x5
39 #define MM_TYPE_INTRA 0x8
40 #define MM_TYPE_INTRA_HH 0xc
41 #define MM_TYPE_INTER_HH 0xd
42 #define MM_TYPE_INTRA_HHV 0xe
43 #define MM_TYPE_INTER_HHV 0xf
45 typedef struct MmContext
{
46 AVCodecContext
*avctx
;
50 static av_cold
int mm_decode_init(AVCodecContext
*avctx
)
52 MmContext
*s
= avctx
->priv_data
;
56 if (s
->avctx
->palctrl
== NULL
) {
57 av_log(avctx
, AV_LOG_ERROR
, "mmvideo: palette expected.\n");
61 avctx
->pix_fmt
= PIX_FMT_PAL8
;
63 if (avcodec_check_dimensions(avctx
, avctx
->width
, avctx
->height
))
66 s
->frame
.reference
= 1;
67 if (avctx
->get_buffer(avctx
, &s
->frame
)) {
68 av_log(s
->avctx
, AV_LOG_ERROR
, "mmvideo: get_buffer() failed\n");
75 static void mm_decode_intra(MmContext
* s
, int half_horiz
, int half_vert
, const uint8_t *buf
, int buf_size
)
81 int run_length
, color
;
88 run_length
= (buf
[i
] & 0x7f) + 2;
97 memset(s
->frame
.data
[0] + y
*s
->frame
.linesize
[0] + x
, color
, run_length
);
99 memset(s
->frame
.data
[0] + (y
+1)*s
->frame
.linesize
[0] + x
, color
, run_length
);
103 if (x
>= s
->avctx
->width
) {
105 y
+= half_vert
? 2 : 1;
110 static void mm_decode_inter(MmContext
* s
, int half_horiz
, int half_vert
, const uint8_t *buf
, int buf_size
)
112 const int data_ptr
= 2 + AV_RL16(&buf
[0]);
114 d
= data_ptr
; r
= 2; y
= 0;
116 while(r
< data_ptr
) {
118 int length
= buf
[r
] & 0x7f;
119 int x
= buf
[r
+1] + ((buf
[r
] & 0x80) << 1);
127 for(i
=0; i
<length
; i
++) {
129 int replace
= (buf
[r
+i
] >> (7-j
)) & 1;
132 s
->frame
.data
[0][y
*s
->frame
.linesize
[0] + x
] = color
;
134 s
->frame
.data
[0][y
*s
->frame
.linesize
[0] + x
+ 1] = color
;
136 s
->frame
.data
[0][(y
+1)*s
->frame
.linesize
[0] + x
] = color
;
138 s
->frame
.data
[0][(y
+1)*s
->frame
.linesize
[0] + x
+ 1] = color
;
142 x
+= half_horiz
? 2 : 1;
147 y
+= half_vert
? 2 : 1;
151 static int mm_decode_frame(AVCodecContext
*avctx
,
152 void *data
, int *data_size
,
153 const uint8_t *buf
, int buf_size
)
155 MmContext
*s
= avctx
->priv_data
;
156 AVPaletteControl
*palette_control
= avctx
->palctrl
;
159 if (palette_control
->palette_changed
) {
160 memcpy(s
->frame
.data
[1], palette_control
->palette
, AVPALETTE_SIZE
);
161 palette_control
->palette_changed
= 0;
164 type
= AV_RL16(&buf
[0]);
165 buf
+= MM_PREAMBLE_SIZE
;
166 buf_size
-= MM_PREAMBLE_SIZE
;
169 case MM_TYPE_INTRA
: mm_decode_intra(s
, 0, 0, buf
, buf_size
); break;
170 case MM_TYPE_INTRA_HH
: mm_decode_intra(s
, 1, 0, buf
, buf_size
); break;
171 case MM_TYPE_INTRA_HHV
: mm_decode_intra(s
, 1, 1, buf
, buf_size
); break;
172 case MM_TYPE_INTER
: mm_decode_inter(s
, 0, 0, buf
, buf_size
); break;
173 case MM_TYPE_INTER_HH
: mm_decode_inter(s
, 1, 0, buf
, buf_size
); break;
174 case MM_TYPE_INTER_HHV
: mm_decode_inter(s
, 1, 1, buf
, buf_size
); break;
179 *data_size
= sizeof(AVFrame
);
180 *(AVFrame
*)data
= s
->frame
;
185 static av_cold
int mm_decode_end(AVCodecContext
*avctx
)
187 MmContext
*s
= avctx
->priv_data
;
190 avctx
->release_buffer(avctx
, &s
->frame
);
195 AVCodec mmvideo_decoder
= {
205 .long_name
= "American Laser Games MM Video",