2
* Discworld II BMV video decoder
3
* Copyright (c) 2011 Konstantin Shishkov
5
* This file is part of Libav.
7
* Libav 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
* Libav 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 Libav; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
#include "libavutil/common.h"
25
#include "bytestream.h"
42
#define SCREEN_WIDE 640
43
#define SCREEN_HIGH 429
45
typedef struct BMVDecContext {
46
AVCodecContext *avctx;
48
uint8_t *frame, frame_base[SCREEN_WIDE * (SCREEN_HIGH + 1)];
50
const uint8_t *stream;
53
#define NEXT_BYTE(v) v = forward ? v + 1 : v - 1;
55
static int decode_bmv_frame(const uint8_t *source, int src_len, uint8_t *frame, int frame_off)
57
unsigned val, saved_val = 0;
59
const uint8_t *src, *source_end = source + src_len;
60
uint8_t *frame_end = frame + SCREEN_WIDE * SCREEN_HIGH;
61
uint8_t *dst, *dst_end;
63
int forward = (frame_off <= -SCREEN_WIDE) || (frame_off >= 0);
64
int read_two_nibbles, flag;
70
return AVERROR_INVALIDDATA;
77
src = source + src_len - 1;
85
/* The mode/len decoding is a bit strange:
86
* values are coded as variable-length codes with nibble units,
87
* code end is signalled by two top bits in the nibble being nonzero.
88
* And since data is bytepacked and we read two nibbles at a time,
89
* we may get a nibble belonging to the next code.
90
* Hence this convoluted loop.
92
if (!mode || (tmplen == 4)) {
93
if (src < source || src >= source_end)
94
return AVERROR_INVALIDDATA;
103
if (!read_two_nibbles) {
104
if (src < source || src >= source_end)
105
return AVERROR_INVALIDDATA;
107
val |= *src << shift;
111
// two upper bits of the nibble is zero,
112
// so shift top nibble value down into their place
113
read_two_nibbles = 0;
115
mask = (1 << shift) - 1;
116
val = ((val >> 2) & ~mask) | (val & mask);
118
if ((val & (0xC << shift))) {
129
saved_val = val >> (4 + shift);
131
val &= (1 << (shift + 4)) - 1;
134
advance_mode = val & 1;
135
len = (val >> 1) - 1;
136
mode += 1 + advance_mode;
139
if (len <= 0 || FFABS(dst_end - dst) < len)
140
return AVERROR_INVALIDDATA;
144
if (dst - frame + SCREEN_WIDE < frame_off ||
145
dst - frame + SCREEN_WIDE + frame_off < 0 ||
146
frame_end - dst < frame_off + len ||
147
frame_end - dst < len)
148
return AVERROR_INVALIDDATA;
149
for (i = 0; i < len; i++)
150
dst[i] = dst[frame_off + i];
154
if (dst - frame + SCREEN_WIDE < frame_off ||
155
dst - frame + SCREEN_WIDE + frame_off < 0 ||
156
frame_end - dst < frame_off + len ||
157
frame_end - dst < len)
158
return AVERROR_INVALIDDATA;
159
for (i = len - 1; i >= 0; i--)
160
dst[i] = dst[frame_off + i];
165
if (source + src_len - src < len)
166
return AVERROR_INVALIDDATA;
167
memcpy(dst, src, len);
171
if (src - source < len)
172
return AVERROR_INVALIDDATA;
175
memcpy(dst, src, len);
179
val = forward ? dst[-1] : dst[1];
181
memset(dst, val, len);
185
memset(dst, val, len);
197
static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame,
200
BMVDecContext * const c = avctx->priv_data;
201
AVFrame *frame = data;
204
uint8_t *srcptr, *outptr;
206
c->stream = pkt->data;
207
type = bytestream_get_byte(&c->stream);
208
if (type & BMV_AUDIO) {
209
int blobs = bytestream_get_byte(&c->stream);
210
if (pkt->size < blobs * 65 + 2) {
211
av_log(avctx, AV_LOG_ERROR, "Audio data doesn't fit in frame\n");
212
return AVERROR_INVALIDDATA;
214
c->stream += blobs * 65;
216
if (type & BMV_COMMAND) {
217
int command_size = (type & BMV_PRINT) ? 8 : 10;
218
if (c->stream - pkt->data + command_size > pkt->size) {
219
av_log(avctx, AV_LOG_ERROR, "Command data doesn't fit in frame\n");
220
return AVERROR_INVALIDDATA;
222
c->stream += command_size;
224
if (type & BMV_PALETTE) {
225
if (c->stream - pkt->data > pkt->size - 768) {
226
av_log(avctx, AV_LOG_ERROR, "Palette data doesn't fit in frame\n");
227
return AVERROR_INVALIDDATA;
229
for (i = 0; i < 256; i++)
230
c->pal[i] = bytestream_get_be24(&c->stream);
232
if (type & BMV_SCROLL) {
233
if (c->stream - pkt->data > pkt->size - 2) {
234
av_log(avctx, AV_LOG_ERROR, "Screen offset data doesn't fit in frame\n");
235
return AVERROR_INVALIDDATA;
237
scr_off = (int16_t)bytestream_get_le16(&c->stream);
238
} else if ((type & BMV_INTRA) == BMV_INTRA) {
244
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
245
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
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
c->frame = c->frame_base + 640;
284
AVCodec ff_bmv_video_decoder = {
286
.long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV video"),
287
.type = AVMEDIA_TYPE_VIDEO,
288
.id = AV_CODEC_ID_BMV_VIDEO,
289
.priv_data_size = sizeof(BMVDecContext),
291
.decode = decode_frame,
292
.capabilities = CODEC_CAP_DR1,