1/* 2 * American Laser Games MM Video Decoder 3 * Copyright (c) 2006,2008 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 Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file libavcodec/mmvideo.c 24 * American Laser Games MM Video Decoder 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 "libavutil/intreadwrite.h" 35#include "avcodec.h" 36 37#define MM_PREAMBLE_SIZE 6 38 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_PALETTE 0x31 46 47typedef struct MmContext { 48 AVCodecContext *avctx; 49 AVFrame frame; 50 int palette[AVPALETTE_COUNT]; 51} MmContext; 52 53static av_cold int mm_decode_init(AVCodecContext *avctx) 54{ 55 MmContext *s = avctx->priv_data; 56 57 s->avctx = avctx; 58 59 avctx->pix_fmt = PIX_FMT_PAL8; 60 61 if (avcodec_check_dimensions(avctx, avctx->width, avctx->height)) 62 return -1; 63 64 s->frame.reference = 1; 65 if (avctx->get_buffer(avctx, &s->frame)) { 66 av_log(s->avctx, AV_LOG_ERROR, "mmvideo: get_buffer() failed\n"); 67 return -1; 68 } 69 70 return 0; 71} 72 73static void mm_decode_pal(MmContext *s, const uint8_t *buf, const uint8_t *buf_end) 74{ 75 int i; 76 buf += 4; 77 for (i=0; i<128 && buf+2<buf_end; i++) { 78 s->palette[i] = AV_RB24(buf); 79 s->palette[i+128] = s->palette[i]<<2; 80 buf += 3; 81 } 82} 83 84static void mm_decode_intra(MmContext * s, int half_horiz, int half_vert, const uint8_t *buf, int buf_size) 85{ 86 int i, x, y; 87 i=0; x=0; y=0; 88 89 while(i<buf_size) { 90 int run_length, color; 91 92 if (buf[i] & 0x80) { 93 run_length = 1; 94 color = buf[i]; 95 i++; 96 }else{ 97 run_length = (buf[i] & 0x7f) + 2; 98 color = buf[i+1]; 99 i+=2; 100 } 101 102 if (half_horiz) 103 run_length *=2; 104 105 if (color) { 106 memset(s->frame.data[0] + y*s->frame.linesize[0] + x, color, run_length); 107 if (half_vert) 108 memset(s->frame.data[0] + (y+1)*s->frame.linesize[0] + x, color, run_length); 109 } 110 x+= run_length; 111 112 if (x >= s->avctx->width) { 113 x=0; 114 y += half_vert ? 2 : 1; 115 } 116 } 117} 118 119static void mm_decode_inter(MmContext * s, int half_horiz, int half_vert, const uint8_t *buf, int buf_size) 120{ 121 const int data_ptr = 2 + AV_RL16(&buf[0]); 122 int d, r, y; 123 d = data_ptr; r = 2; y = 0; 124 125 while(r < data_ptr) { 126 int i, j; 127 int length = buf[r] & 0x7f; 128 int x = buf[r+1] + ((buf[r] & 0x80) << 1); 129 r += 2; 130 131 if (length==0) { 132 y += x; 133 continue; 134 } 135 136 for(i=0; i<length; i++) { 137 for(j=0; j<8; j++) { 138 int replace = (buf[r+i] >> (7-j)) & 1; 139 if (replace) { 140 int color = buf[d]; 141 s->frame.data[0][y*s->frame.linesize[0] + x] = color; 142 if (half_horiz) 143 s->frame.data[0][y*s->frame.linesize[0] + x + 1] = color; 144 if (half_vert) { 145 s->frame.data[0][(y+1)*s->frame.linesize[0] + x] = color; 146 if (half_horiz) 147 s->frame.data[0][(y+1)*s->frame.linesize[0] + x + 1] = color; 148 } 149 d++; 150 } 151 x += half_horiz ? 2 : 1; 152 } 153 } 154 155 r += length; 156 y += half_vert ? 2 : 1; 157 } 158} 159 160static int mm_decode_frame(AVCodecContext *avctx, 161 void *data, int *data_size, 162 const uint8_t *buf, int buf_size) 163{ 164 MmContext *s = avctx->priv_data; 165 const uint8_t *buf_end = buf+buf_size; 166 int type; 167 168 type = AV_RL16(&buf[0]); 169 buf += MM_PREAMBLE_SIZE; 170 buf_size -= MM_PREAMBLE_SIZE; 171 172 switch(type) { 173 case MM_TYPE_PALETTE : mm_decode_pal(s, buf, buf_end); return buf_size; 174 case MM_TYPE_INTRA : mm_decode_intra(s, 0, 0, buf, buf_size); break; 175 case MM_TYPE_INTRA_HH : mm_decode_intra(s, 1, 0, buf, buf_size); break; 176 case MM_TYPE_INTRA_HHV : mm_decode_intra(s, 1, 1, buf, buf_size); break; 177 case MM_TYPE_INTER : mm_decode_inter(s, 0, 0, buf, buf_size); break; 178 case MM_TYPE_INTER_HH : mm_decode_inter(s, 1, 0, buf, buf_size); break; 179 case MM_TYPE_INTER_HHV : mm_decode_inter(s, 1, 1, buf, buf_size); break; 180 default : 181 return -1; 182 } 183 184 memcpy(s->frame.data[1], s->palette, AVPALETTE_SIZE); 185 186 *data_size = sizeof(AVFrame); 187 *(AVFrame*)data = s->frame; 188 189 return buf_size; 190} 191 192static av_cold int mm_decode_end(AVCodecContext *avctx) 193{ 194 MmContext *s = avctx->priv_data; 195 196 if(s->frame.data[0]) 197 avctx->release_buffer(avctx, &s->frame); 198 199 return 0; 200} 201 202AVCodec mmvideo_decoder = { 203 "mmvideo", 204 CODEC_TYPE_VIDEO, 205 CODEC_ID_MMVIDEO, 206 sizeof(MmContext), 207 mm_decode_init, 208 NULL, 209 mm_decode_end, 210 mm_decode_frame, 211 CODEC_CAP_DR1, 212 .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM Video"), 213}; 214