Coverage Report

Created: 2026-09-14 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/dxa.c
Line
Count
Source
1
/*
2
 * Feeble Files/ScummVM DXA decoder
3
 * Copyright (c) 2007 Konstantin Shishkov
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
24
 * DXA Video decoder
25
 */
26
27
#include "libavutil/attributes.h"
28
#include "libavutil/intreadwrite.h"
29
#include "libavutil/mem.h"
30
#include "bytestream.h"
31
#include "avcodec.h"
32
#include "codec_internal.h"
33
#include "decode.h"
34
35
#include <zlib.h>
36
37
/*
38
 * Decoder context
39
 */
40
typedef struct DxaDecContext {
41
    AVFrame *prev;
42
43
    int dsize;
44
6.46k
#define DECOMP_BUF_PADDING 16
45
    uint8_t *decomp_buf;
46
    uint32_t pal[256];
47
} DxaDecContext;
48
49
static const uint8_t shift1[6] = { 0, 8, 8, 8, 4, 4 };
50
static const uint8_t shift2[6] = { 0, 0, 8, 4, 0, 4 };
51
52
static int decode_13(AVCodecContext *avctx, DxaDecContext *c, uint8_t* dst,
53
                     int stride, uint8_t *src, int srcsize, uint8_t *ref)
54
949
{
55
949
    uint8_t *code, *data, *mv, *msk, *tmp, *tmp2;
56
949
    uint8_t *src_end = src + srcsize;
57
949
    int i, j, k;
58
949
    int type, x, y, d, d2;
59
949
    uint32_t mask;
60
61
949
    if (12ULL  + ((avctx->width * avctx->height) >> 4) + AV_RB32(src + 0) + AV_RB32(src + 4) > srcsize)
62
648
        return AVERROR_INVALIDDATA;
63
64
301
    code = src  + 12;
65
301
    data = code + ((avctx->width * avctx->height) >> 4);
66
301
    mv   = data + AV_RB32(src + 0);
67
301
    msk  = mv   + AV_RB32(src + 4);
68
69
1.27k
    for(j = 0; j < avctx->height; j += 4){
70
4.58k
        for(i = 0; i < avctx->width; i += 4){
71
3.61k
            if (data > src_end || mv > src_end || msk > src_end)
72
0
                return AVERROR_INVALIDDATA;
73
3.61k
            tmp  = dst + i;
74
3.61k
            tmp2 = ref + i;
75
3.61k
            type = *code++;
76
3.61k
            switch(type){
77
0
            case 4: // motion compensation
78
0
                x = (*mv) >> 4;    if(x & 8) x = 8 - x;
79
0
                y = (*mv++) & 0xF; if(y & 8) y = 8 - y;
80
0
                if (i < -x || avctx->width  - i - 4 < x ||
81
0
                    j < -y || avctx->height - j - 4 < y) {
82
0
                    av_log(avctx, AV_LOG_ERROR, "MV %d %d out of bounds\n", x,y);
83
0
                    return AVERROR_INVALIDDATA;
84
0
                }
85
0
                tmp2 += x + y*stride;
86
0
                av_fallthrough;
87
3.61k
            case 0: // skip
88
3.61k
            case 5: // skip in method 12
89
18.0k
                for(y = 0; y < 4; y++){
90
14.4k
                    memcpy(tmp, tmp2, 4);
91
14.4k
                    tmp  += stride;
92
14.4k
                    tmp2 += stride;
93
14.4k
                }
94
3.61k
                break;
95
0
            case 1:  // masked change
96
0
            case 10: // masked change with only half of pixels changed
97
0
            case 11: // cases 10-15 are for method 12 only
98
0
            case 12:
99
0
            case 13:
100
0
            case 14:
101
0
            case 15:
102
0
                if(type == 1){
103
0
                    mask = AV_RB16(msk);
104
0
                    msk += 2;
105
0
                }else{
106
0
                    type -= 10;
107
0
                    mask = ((msk[0] & 0xF0) << shift1[type]) | ((msk[0] & 0xF) << shift2[type]);
108
0
                    msk++;
109
0
                }
110
0
                for(y = 0; y < 4; y++){
111
0
                    for(x = 0; x < 4; x++){
112
0
                        tmp[x] = (mask & 0x8000) ? *data++ : tmp2[x];
113
0
                        mask <<= 1;
114
0
                    }
115
0
                    tmp  += stride;
116
0
                    tmp2 += stride;
117
0
                }
118
0
                break;
119
0
            case 2: // fill block
120
0
                for(y = 0; y < 4; y++){
121
0
                    memset(tmp, data[0], 4);
122
0
                    tmp += stride;
123
0
                }
124
0
                data++;
125
0
                break;
126
0
            case 3: // raw block
127
0
                for(y = 0; y < 4; y++){
128
0
                    memcpy(tmp, data, 4);
129
0
                    data += 4;
130
0
                    tmp  += stride;
131
0
                }
132
0
                break;
133
0
            case 8: // subblocks - method 13 only
134
0
                mask = *msk++;
135
0
                for(k = 0; k < 4; k++){
136
0
                    d  = ((k & 1) << 1) + ((k & 2) * stride);
137
0
                    d2 = ((k & 1) << 1) + ((k & 2) * stride);
138
0
                    tmp2 = ref + i + d2;
139
0
                    switch(mask & 0xC0){
140
0
                    case 0x80: // motion compensation
141
0
                        x = (*mv) >> 4;    if(x & 8) x = 8 - x;
142
0
                        y = (*mv++) & 0xF; if(y & 8) y = 8 - y;
143
0
                        if (i + 2*(k & 1) < -x || avctx->width  - i - 2*(k & 1) - 2 < x ||
144
0
                            j +   (k & 2) < -y || avctx->height - j -   (k & 2) - 2 < y) {
145
0
                            av_log(avctx, AV_LOG_ERROR, "MV %d %d out of bounds\n", x,y);
146
0
                            return AVERROR_INVALIDDATA;
147
0
                        }
148
0
                        tmp2 += x + y*stride;
149
0
                        av_fallthrough;
150
0
                    case 0x00: // skip
151
0
                        tmp[d + 0         ] = tmp2[0];
152
0
                        tmp[d + 1         ] = tmp2[1];
153
0
                        tmp[d + 0 + stride] = tmp2[0 + stride];
154
0
                        tmp[d + 1 + stride] = tmp2[1 + stride];
155
0
                        break;
156
0
                    case 0x40: // fill
157
0
                        tmp[d + 0         ] = data[0];
158
0
                        tmp[d + 1         ] = data[0];
159
0
                        tmp[d + 0 + stride] = data[0];
160
0
                        tmp[d + 1 + stride] = data[0];
161
0
                        data++;
162
0
                        break;
163
0
                    case 0xC0: // raw
164
0
                        tmp[d + 0         ] = *data++;
165
0
                        tmp[d + 1         ] = *data++;
166
0
                        tmp[d + 0 + stride] = *data++;
167
0
                        tmp[d + 1 + stride] = *data++;
168
0
                        break;
169
0
                    }
170
0
                    mask <<= 2;
171
0
                }
172
0
                break;
173
0
            case 32: // vector quantization - 2 colors
174
0
                mask = AV_RB16(msk);
175
0
                msk += 2;
176
0
                for(y = 0; y < 4; y++){
177
0
                    for(x = 0; x < 4; x++){
178
0
                        tmp[x] = data[mask & 1];
179
0
                        mask >>= 1;
180
0
                    }
181
0
                    tmp  += stride;
182
0
                    tmp2 += stride;
183
0
                }
184
0
                data += 2;
185
0
                break;
186
0
            case 33: // vector quantization - 3 or 4 colors
187
0
            case 34:
188
0
                mask = AV_RB32(msk);
189
0
                msk += 4;
190
0
                for(y = 0; y < 4; y++){
191
0
                    for(x = 0; x < 4; x++){
192
0
                        tmp[x] = data[mask & 3];
193
0
                        mask >>= 2;
194
0
                    }
195
0
                    tmp  += stride;
196
0
                    tmp2 += stride;
197
0
                }
198
0
                data += type - 30;
199
0
                break;
200
0
            default:
201
0
                av_log(avctx, AV_LOG_ERROR, "Unknown opcode %d\n", type);
202
0
                return AVERROR_INVALIDDATA;
203
3.61k
            }
204
3.61k
        }
205
971
        dst += stride * 4;
206
971
        ref += stride * 4;
207
971
    }
208
301
    return 0;
209
301
}
210
211
static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
212
                        int *got_frame, AVPacket *avpkt)
213
180k
{
214
180k
    DxaDecContext * const c = avctx->priv_data;
215
180k
    uint8_t *outptr, *srcptr, *tmpptr;
216
180k
    unsigned long dsize;
217
180k
    int i, j, compr, ret;
218
180k
    int stride;
219
180k
    GetByteContext gb;
220
221
180k
    bytestream2_init(&gb, avpkt->data, avpkt->size);
222
223
    /* make the palette available on the way out */
224
180k
    if (bytestream2_peek_le32(&gb) == MKTAG('C','M','A','P')) {
225
3.45k
        bytestream2_skip(&gb, 4);
226
888k
        for(i = 0; i < 256; i++){
227
885k
            c->pal[i] = 0xFFU << 24 | bytestream2_get_be24(&gb);
228
885k
        }
229
3.45k
    }
230
231
180k
    if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
232
11.0k
        return ret;
233
169k
    memcpy(frame->data[1], c->pal, AVPALETTE_SIZE);
234
235
169k
    outptr = frame->data[0];
236
169k
    srcptr = c->decomp_buf;
237
169k
    tmpptr = c->prev->data[0];
238
169k
    stride = frame->linesize[0];
239
240
169k
    if (bytestream2_get_le32(&gb) == MKTAG('N','U','L','L'))
241
75.6k
        compr = -1;
242
93.4k
    else
243
93.4k
        compr = bytestream2_get_byte(&gb);
244
245
169k
    dsize = c->dsize;
246
169k
    if (compr != 4 && compr != -1) {
247
69.8k
        bytestream2_skip(&gb, 4);
248
69.8k
        if (uncompress(c->decomp_buf, &dsize, avpkt->data + bytestream2_tell(&gb),
249
69.8k
                       bytestream2_get_bytes_left(&gb)) != Z_OK) {
250
64.8k
            av_log(avctx, AV_LOG_ERROR, "Uncompress failed!\n");
251
64.8k
            return AVERROR_UNKNOWN;
252
64.8k
        }
253
4.98k
        memset(c->decomp_buf + dsize, 0, DECOMP_BUF_PADDING);
254
4.98k
    }
255
256
104k
    if (avctx->debug & FF_DEBUG_PICT_INFO)
257
0
        av_log(avctx, AV_LOG_DEBUG, "compr:%2d, dsize:%d\n", compr, (int)dsize);
258
259
104k
    switch(compr){
260
75.6k
    case -1:
261
75.6k
        frame->flags &= ~AV_FRAME_FLAG_KEY;
262
75.6k
        frame->pict_type = AV_PICTURE_TYPE_P;
263
75.6k
        if (c->prev->data[0])
264
75.3k
            memcpy(frame->data[0], c->prev->data[0], frame->linesize[0] * avctx->height);
265
318
        else{ // Should happen only when first frame is 'NULL'
266
318
            memset(frame->data[0], 0, frame->linesize[0] * avctx->height);
267
318
            frame->flags |= AV_FRAME_FLAG_KEY;
268
318
            frame->pict_type = AV_PICTURE_TYPE_I;
269
318
        }
270
75.6k
        break;
271
216
    case 2:
272
23.8k
    case 4:
273
23.8k
        frame->flags |= AV_FRAME_FLAG_KEY;
274
23.8k
        frame->pict_type = AV_PICTURE_TYPE_I;
275
99.8M
        for (j = 0; j < avctx->height; j++) {
276
99.8M
                memcpy(outptr, srcptr, avctx->width);
277
99.8M
            outptr += stride;
278
99.8M
            srcptr += avctx->width;
279
99.8M
        }
280
23.8k
        break;
281
1.79k
    case 3:
282
2.31k
    case 5:
283
2.31k
        if (!tmpptr) {
284
1.31k
            av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n");
285
1.31k
            if (!(avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL))
286
1.31k
                return AVERROR_INVALIDDATA;
287
1.31k
        }
288
996
        frame->flags &= ~AV_FRAME_FLAG_KEY;
289
996
        frame->pict_type = AV_PICTURE_TYPE_P;
290
12.2M
        for (j = 0; j < avctx->height; j++) {
291
12.2M
            if(tmpptr){
292
3.06G
                for(i = 0; i < avctx->width; i++)
293
3.05G
                    outptr[i] = srcptr[i] ^ tmpptr[i];
294
12.2M
                tmpptr += stride;
295
12.2M
            }else
296
0
                memcpy(outptr, srcptr, avctx->width);
297
12.2M
            outptr += stride;
298
12.2M
            srcptr += avctx->width;
299
12.2M
        }
300
996
        break;
301
591
    case 12: // ScummVM coding
302
2.06k
    case 13:
303
2.06k
        frame->flags &= ~AV_FRAME_FLAG_KEY;
304
2.06k
        frame->pict_type = AV_PICTURE_TYPE_P;
305
2.06k
        if (!c->prev->data[0]) {
306
1.11k
            av_log(avctx, AV_LOG_ERROR, "Missing reference frame\n");
307
1.11k
            return AVERROR_INVALIDDATA;
308
1.11k
        }
309
949
        decode_13(avctx, c, frame->data[0], frame->linesize[0], srcptr, dsize, c->prev->data[0]);
310
949
        break;
311
394
    default:
312
394
        av_log(avctx, AV_LOG_ERROR, "Unknown/unsupported compression type %d\n", compr);
313
394
        return AVERROR_INVALIDDATA;
314
104k
    }
315
316
101k
    if ((ret = av_frame_replace(c->prev, frame)) < 0)
317
0
        return ret;
318
319
101k
    *got_frame = 1;
320
321
    /* always report that the buffer was completely consumed */
322
101k
    return avpkt->size;
323
101k
}
324
325
static av_cold int decode_init(AVCodecContext *avctx)
326
1.49k
{
327
1.49k
    DxaDecContext * const c = avctx->priv_data;
328
329
1.49k
    if (avctx->width%4 || avctx->height%4) {
330
11
        avpriv_request_sample(avctx, "dimensions are not a multiple of 4");
331
11
        return AVERROR_INVALIDDATA;
332
11
    }
333
334
1.47k
    c->prev = av_frame_alloc();
335
1.47k
    if (!c->prev)
336
0
        return AVERROR(ENOMEM);
337
338
1.47k
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
339
340
1.47k
    c->dsize = avctx->width * avctx->height * 2;
341
1.47k
    c->decomp_buf = av_malloc(c->dsize + DECOMP_BUF_PADDING);
342
1.47k
    if (!c->decomp_buf) {
343
0
        av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
344
0
        return AVERROR(ENOMEM);
345
0
    }
346
347
1.47k
    return 0;
348
1.47k
}
349
350
static av_cold int decode_end(AVCodecContext *avctx)
351
1.49k
{
352
1.49k
    DxaDecContext * const c = avctx->priv_data;
353
354
1.49k
    av_freep(&c->decomp_buf);
355
1.49k
    av_frame_free(&c->prev);
356
357
1.49k
    return 0;
358
1.49k
}
359
360
const FFCodec ff_dxa_decoder = {
361
    .p.name         = "dxa",
362
    CODEC_LONG_NAME("Feeble Files/ScummVM DXA"),
363
    .p.type         = AVMEDIA_TYPE_VIDEO,
364
    .p.id           = AV_CODEC_ID_DXA,
365
    .priv_data_size = sizeof(DxaDecContext),
366
    .init           = decode_init,
367
    .close          = decode_end,
368
    FF_CODEC_DECODE_CB(decode_frame),
369
    .p.capabilities = AV_CODEC_CAP_DR1,
370
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
371
};