2 * Discworld II BMV video decoder
3 * Copyright (c) 2011 Konstantin Shishkov
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
22 #include "libavutil/avassert.h"
23 #include "libavutil/common.h"
26 #include "bytestream.h"
43 #define SCREEN_WIDE 640
44 #define SCREEN_HIGH 429
46 typedef struct BMVDecContext
{
47 AVCodecContext
*avctx
;
49 uint8_t *frame
, frame_base
[SCREEN_WIDE
* (SCREEN_HIGH
+ 1)];
51 const uint8_t *stream
;
54 #define NEXT_BYTE(v) v = forward ? v + 1 : v - 1;
56 static int decode_bmv_frame(const uint8_t *source
, int src_len
, uint8_t *frame
, int frame_off
)
58 unsigned val
, saved_val
= 0;
60 const uint8_t *src
, *source_end
= source
+ src_len
;
61 uint8_t *frame_end
= frame
+ SCREEN_WIDE
* SCREEN_HIGH
;
62 uint8_t *dst
, *dst_end
;
64 int forward
= (frame_off
<= -SCREEN_WIDE
) || (frame_off
>= 0);
65 int read_two_nibbles
, flag
;
71 return AVERROR_INVALIDDATA
;
78 src
= source
+ src_len
- 1;
86 /* The mode/len decoding is a bit strange:
87 * values are coded as variable-length codes with nibble units,
88 * code end is signalled by two top bits in the nibble being nonzero.
89 * And since data is bytepacked and we read two nibbles at a time,
90 * we may get a nibble belonging to the next code.
91 * Hence this convoluted loop.
93 if (!mode
|| (tmplen
== 4)) {
94 if (src
< source
|| src
>= source_end
)
95 return AVERROR_INVALIDDATA
;
100 read_two_nibbles
= 0;
106 if (!read_two_nibbles
) {
107 if (src
< source
|| src
>= source_end
)
108 return AVERROR_INVALIDDATA
;
110 val
|= *src
<< shift
;
114 // two upper bits of the nibble is zero,
115 // so shift top nibble value down into their place
116 read_two_nibbles
= 0;
118 mask
= (1 << shift
) - 1;
119 val
= ((val
>> 2) & ~mask
) | (val
& mask
);
121 if ((val
& (0xC << shift
))) {
132 saved_val
= val
>> (4 + shift
);
134 val
&= (1 << (shift
+ 4)) - 1;
137 advance_mode
= val
& 1;
138 len
= (val
>> 1) - 1;
140 mode
+= 1 + advance_mode
;
143 if (len
<= 0 || FFABS(dst_end
- dst
) < len
)
144 return AVERROR_INVALIDDATA
;
148 if (dst
- frame
+ SCREEN_WIDE
< frame_off
||
149 dst
- frame
+ SCREEN_WIDE
+ frame_off
< 0 ||
150 frame_end
- dst
< frame_off
+ len
||
151 frame_end
- dst
< len
)
152 return AVERROR_INVALIDDATA
;
153 for (i
= 0; i
< len
; i
++)
154 dst
[i
] = dst
[frame_off
+ i
];
158 if (dst
- frame
+ SCREEN_WIDE
< frame_off
||
159 dst
- frame
+ SCREEN_WIDE
+ frame_off
< 0 ||
160 frame_end
- dst
< frame_off
+ len
||
161 frame_end
- dst
< len
)
162 return AVERROR_INVALIDDATA
;
163 for (i
= len
- 1; i
>= 0; i
--)
164 dst
[i
] = dst
[frame_off
+ i
];
169 if (source
+ src_len
- src
< len
)
170 return AVERROR_INVALIDDATA
;
171 memcpy(dst
, src
, len
);
175 if (src
- source
< len
)
176 return AVERROR_INVALIDDATA
;
179 memcpy(dst
, src
, len
);
183 val
= forward
? dst
[-1] : dst
[1];
185 memset(dst
, val
, len
);
189 memset(dst
, val
, len
);
199 static int decode_frame(AVCodecContext
*avctx
, void *data
, int *got_frame
,
202 BMVDecContext
* const c
= avctx
->priv_data
;
203 AVFrame
*frame
= data
;
206 uint8_t *srcptr
, *outptr
;
208 c
->stream
= pkt
->data
;
209 type
= bytestream_get_byte(&c
->stream
);
210 if (type
& BMV_AUDIO
) {
211 int blobs
= bytestream_get_byte(&c
->stream
);
212 if (pkt
->size
< blobs
* 65 + 2) {
213 av_log(avctx
, AV_LOG_ERROR
, "Audio data doesn't fit in frame\n");
214 return AVERROR_INVALIDDATA
;
216 c
->stream
+= blobs
* 65;
218 if (type
& BMV_COMMAND
) {
219 int command_size
= (type
& BMV_PRINT
) ? 8 : 10;
220 if (c
->stream
- pkt
->data
+ command_size
> pkt
->size
) {
221 av_log(avctx
, AV_LOG_ERROR
, "Command data doesn't fit in frame\n");
222 return AVERROR_INVALIDDATA
;
224 c
->stream
+= command_size
;
226 if (type
& BMV_PALETTE
) {
227 if (c
->stream
- pkt
->data
> pkt
->size
- 768) {
228 av_log(avctx
, AV_LOG_ERROR
, "Palette data doesn't fit in frame\n");
229 return AVERROR_INVALIDDATA
;
231 for (i
= 0; i
< 256; i
++)
232 c
->pal
[i
] = 0xFFU
<< 24 | bytestream_get_be24(&c
->stream
);
234 if (type
& BMV_SCROLL
) {
235 if (c
->stream
- pkt
->data
> pkt
->size
- 2) {
236 av_log(avctx
, AV_LOG_ERROR
, "Screen offset data doesn't fit in frame\n");
237 return AVERROR_INVALIDDATA
;
239 scr_off
= (int16_t)bytestream_get_le16(&c
->stream
);
240 } else if ((type
& BMV_INTRA
) == BMV_INTRA
) {
246 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
249 if (decode_bmv_frame(c
->stream
, pkt
->size
- (c
->stream
- pkt
->data
), c
->frame
, scr_off
)) {
250 av_log(avctx
, AV_LOG_ERROR
, "Error decoding frame data\n");
251 return AVERROR_INVALIDDATA
;
254 memcpy(frame
->data
[1], c
->pal
, AVPALETTE_SIZE
);
255 frame
->palette_has_changed
= type
& BMV_PALETTE
;
257 outptr
= frame
->data
[0];
260 for (i
= 0; i
< avctx
->height
; i
++) {
261 memcpy(outptr
, srcptr
, avctx
->width
);
262 srcptr
+= avctx
->width
;
263 outptr
+= frame
->linesize
[0];
268 /* always report that the buffer was completely consumed */
272 static av_cold
int decode_init(AVCodecContext
*avctx
)
274 BMVDecContext
* const c
= avctx
->priv_data
;
277 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
279 if (avctx
->width
!= SCREEN_WIDE
|| avctx
->height
!= SCREEN_HIGH
) {
280 av_log(avctx
, AV_LOG_ERROR
, "Invalid dimension %dx%d\n", avctx
->width
, avctx
->height
);
281 return AVERROR_INVALIDDATA
;
284 c
->frame
= c
->frame_base
+ 640;
289 AVCodec ff_bmv_video_decoder
= {
291 .long_name
= NULL_IF_CONFIG_SMALL("Discworld II BMV video"),
292 .type
= AVMEDIA_TYPE_VIDEO
,
293 .id
= AV_CODEC_ID_BMV_VIDEO
,
294 .priv_data_size
= sizeof(BMVDecContext
),
296 .decode
= decode_frame
,
297 .capabilities
= CODEC_CAP_DR1
,