2 * American Laser Games MM Video Decoder
3 * Copyright (c) 2006,2008 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
44 #define MM_TYPE_PALETTE 0x31
46 typedef struct MmContext
{
47 AVCodecContext
*avctx
;
49 int palette
[AVPALETTE_COUNT
];
52 static av_cold
int mm_decode_init(AVCodecContext
*avctx
)
54 MmContext
*s
= avctx
->priv_data
;
58 avctx
->pix_fmt
= PIX_FMT_PAL8
;
60 if (avcodec_check_dimensions(avctx
, avctx
->width
, avctx
->height
))
63 s
->frame
.reference
= 1;
64 if (avctx
->get_buffer(avctx
, &s
->frame
)) {
65 av_log(s
->avctx
, AV_LOG_ERROR
, "mmvideo: get_buffer() failed\n");
72 static void mm_decode_pal(MmContext
*s
, const uint8_t *buf
, const uint8_t *buf_end
)
76 for (i
=0; i
<128 && buf
+2<buf_end
; i
++) {
77 s
->palette
[i
] = AV_RB24(buf
);
78 s
->palette
[i
+128] = s
->palette
[i
]<<2;
83 static void mm_decode_intra(MmContext
* s
, int half_horiz
, int half_vert
, const uint8_t *buf
, int buf_size
)
89 int run_length
, color
;
96 run_length
= (buf
[i
] & 0x7f) + 2;
105 memset(s
->frame
.data
[0] + y
*s
->frame
.linesize
[0] + x
, color
, run_length
);
107 memset(s
->frame
.data
[0] + (y
+1)*s
->frame
.linesize
[0] + x
, color
, run_length
);
111 if (x
>= s
->avctx
->width
) {
113 y
+= half_vert
? 2 : 1;
118 static void mm_decode_inter(MmContext
* s
, int half_horiz
, int half_vert
, const uint8_t *buf
, int buf_size
)
120 const int data_ptr
= 2 + AV_RL16(&buf
[0]);
122 d
= data_ptr
; r
= 2; y
= 0;
124 while(r
< data_ptr
) {
126 int length
= buf
[r
] & 0x7f;
127 int x
= buf
[r
+1] + ((buf
[r
] & 0x80) << 1);
135 for(i
=0; i
<length
; i
++) {
137 int replace
= (buf
[r
+i
] >> (7-j
)) & 1;
140 s
->frame
.data
[0][y
*s
->frame
.linesize
[0] + x
] = color
;
142 s
->frame
.data
[0][y
*s
->frame
.linesize
[0] + x
+ 1] = color
;
144 s
->frame
.data
[0][(y
+1)*s
->frame
.linesize
[0] + x
] = color
;
146 s
->frame
.data
[0][(y
+1)*s
->frame
.linesize
[0] + x
+ 1] = color
;
150 x
+= half_horiz
? 2 : 1;
155 y
+= half_vert
? 2 : 1;
159 static int mm_decode_frame(AVCodecContext
*avctx
,
160 void *data
, int *data_size
,
161 const uint8_t *buf
, int buf_size
)
163 MmContext
*s
= avctx
->priv_data
;
164 const uint8_t *buf_end
= buf
+buf_size
;
167 type
= AV_RL16(&buf
[0]);
168 buf
+= MM_PREAMBLE_SIZE
;
169 buf_size
-= MM_PREAMBLE_SIZE
;
172 case MM_TYPE_PALETTE
: mm_decode_pal(s
, buf
, buf_end
); return buf_size
;
173 case MM_TYPE_INTRA
: mm_decode_intra(s
, 0, 0, buf
, buf_size
); break;
174 case MM_TYPE_INTRA_HH
: mm_decode_intra(s
, 1, 0, buf
, buf_size
); break;
175 case MM_TYPE_INTRA_HHV
: mm_decode_intra(s
, 1, 1, buf
, buf_size
); break;
176 case MM_TYPE_INTER
: mm_decode_inter(s
, 0, 0, buf
, buf_size
); break;
177 case MM_TYPE_INTER_HH
: mm_decode_inter(s
, 1, 0, buf
, buf_size
); break;
178 case MM_TYPE_INTER_HHV
: mm_decode_inter(s
, 1, 1, buf
, buf_size
); break;
183 memcpy(s
->frame
.data
[1], s
->palette
, AVPALETTE_SIZE
);
185 *data_size
= sizeof(AVFrame
);
186 *(AVFrame
*)data
= s
->frame
;
191 static av_cold
int mm_decode_end(AVCodecContext
*avctx
)
193 MmContext
*s
= avctx
->priv_data
;
196 avctx
->release_buffer(avctx
, &s
->frame
);
201 AVCodec mmvideo_decoder
= {
211 .long_name
= NULL_IF_CONFIG_SMALL("American Laser Games MM Video"),