Coverage Report

Created: 2024-09-06 07:53

/src/ffmpeg/libavcodec/hnm4video.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Cryo Interactive Entertainment HNM4 video decoder
3
 *
4
 * Copyright (c) 2012 David Kment
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#include <string.h>
24
25
#include "libavutil/imgutils.h"
26
#include "libavutil/internal.h"
27
#include "libavutil/intreadwrite.h"
28
#include "libavutil/mem.h"
29
#include "avcodec.h"
30
#include "bytestream.h"
31
#include "codec_internal.h"
32
#include "decode.h"
33
34
149k
#define HNM4_CHUNK_ID_PL 19536
35
27.8k
#define HNM4_CHUNK_ID_IZ 23113
36
22.7k
#define HNM4_CHUNK_ID_IU 21833
37
#define HNM4_CHUNK_ID_SD 17491
38
39
typedef struct Hnm4VideoContext {
40
    uint8_t version;
41
    int width;
42
    int height;
43
    uint8_t *current;
44
    uint8_t *previous;
45
    uint8_t *buffer1;
46
    uint8_t *buffer2;
47
    uint8_t *processed;
48
    uint32_t palette[256];
49
} Hnm4VideoContext;
50
51
static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
52
558k
{
53
558k
    int ret;
54
55
558k
    if (!*bits) {
56
20.6k
        *bitbuf = bytestream2_get_le32(gb);
57
20.6k
        *bits = 32;
58
20.6k
    }
59
60
558k
    ret = *bitbuf >> 31;
61
558k
    *bitbuf <<= 1;
62
558k
    (*bits)--;
63
64
558k
    return ret;
65
558k
}
66
67
static void unpack_intraframe(AVCodecContext *avctx, const uint8_t *src,
68
                              uint32_t size)
69
4.70k
{
70
4.70k
    Hnm4VideoContext *hnm = avctx->priv_data;
71
4.70k
    GetByteContext gb;
72
4.70k
    uint32_t bitbuf = 0, writeoffset = 0, count = 0;
73
4.70k
    uint16_t word;
74
4.70k
    int32_t offset;
75
4.70k
    int bits = 0;
76
77
4.70k
    bytestream2_init(&gb, src, size);
78
79
326k
    while (bytestream2_tell(&gb) < size) {
80
325k
        if (getbit(&gb, &bitbuf, &bits)) {
81
217k
            if (writeoffset >= hnm->width * hnm->height) {
82
229
                av_log(avctx, AV_LOG_ERROR,
83
229
                       "Attempting to write out of bounds\n");
84
229
                break;
85
229
            }
86
217k
            hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
87
217k
        } else {
88
108k
            if (getbit(&gb, &bitbuf, &bits)) {
89
45.5k
                word   = bytestream2_get_le16(&gb);
90
45.5k
                count  = word & 0x07;
91
45.5k
                offset = (word >> 3) - 0x2000;
92
45.5k
                if (!count)
93
8.34k
                    count = bytestream2_get_byte(&gb);
94
45.5k
                if (!count)
95
256
                    return;
96
62.5k
            } else {
97
62.5k
                count  = getbit(&gb, &bitbuf, &bits) * 2;
98
62.5k
                count += getbit(&gb, &bitbuf, &bits);
99
62.5k
                offset = bytestream2_get_byte(&gb) - 0x0100;
100
62.5k
            }
101
107k
            count  += 2;
102
107k
            offset += writeoffset;
103
107k
            if (offset < 0 || offset + count >= hnm->width * hnm->height) {
104
2.46k
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
105
2.46k
                break;
106
105k
            } else if (writeoffset + count >= hnm->width * hnm->height) {
107
206
                av_log(avctx, AV_LOG_ERROR,
108
206
                       "Attempting to write out of bounds\n");
109
206
                break;
110
206
            }
111
1.91M
            while (count--) {
112
1.81M
                hnm->current[writeoffset++] = hnm->current[offset++];
113
1.81M
            }
114
105k
        }
115
325k
    }
116
4.70k
}
117
118
static void postprocess_current_frame(AVCodecContext *avctx)
119
9.25k
{
120
9.25k
    Hnm4VideoContext *hnm = avctx->priv_data;
121
9.25k
    uint32_t x, y, src_y;
122
9.25k
    int width = hnm->width;
123
124
110M
    for (y = 0; y < hnm->height; y++) {
125
110M
        uint8_t *dst = hnm->processed + y * width;
126
110M
        const uint8_t *src = hnm->current;
127
110M
        src_y = y - (y % 2);
128
110M
        src += src_y * width + (y % 2);
129
10.6G
        for (x = 0; x < width; x++) {
130
10.5G
            dst[x] = *src;
131
10.5G
            src += 2;
132
10.5G
        }
133
110M
    }
134
9.25k
}
135
136
static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
137
12.6k
{
138
12.6k
    Hnm4VideoContext *hnm = avctx->priv_data;
139
12.6k
    uint8_t *src = hnm->processed;
140
12.6k
    uint8_t *dst = frame->data[0];
141
12.6k
    int y;
142
143
115M
    for (y = 0; y < hnm->height; y++) {
144
115M
        memcpy(dst, src, hnm->width);
145
115M
        src += hnm->width;
146
115M
        dst += frame->linesize[0];
147
115M
    }
148
12.6k
}
149
150
static int decode_interframe_v4(AVCodecContext *avctx, const uint8_t *src, uint32_t size)
151
8.92k
{
152
8.92k
    Hnm4VideoContext *hnm = avctx->priv_data;
153
8.92k
    GetByteContext gb;
154
8.92k
    uint32_t writeoffset = 0;
155
8.92k
    int count, left, offset;
156
8.92k
    uint8_t tag, previous, backline, backward, swap;
157
158
8.92k
    bytestream2_init(&gb, src, size);
159
160
217k
    while (bytestream2_tell(&gb) < size) {
161
213k
        count = bytestream2_peek_byte(&gb) & 0x1F;
162
213k
        if (count == 0) {
163
137k
            tag = bytestream2_get_byte(&gb) & 0xE0;
164
137k
            tag = tag >> 5;
165
166
137k
            if (tag == 0) {
167
110k
                if (writeoffset + 2 > hnm->width * hnm->height) {
168
198
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
169
198
                    return AVERROR_INVALIDDATA;
170
198
                }
171
110k
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
172
110k
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
173
110k
            } else if (tag == 1) {
174
1.36k
                writeoffset += bytestream2_get_byte(&gb) * 2;
175
26.0k
            } else if (tag == 2) {
176
8.87k
                count = bytestream2_get_le16(&gb);
177
8.87k
                count *= 2;
178
8.87k
                writeoffset += count;
179
17.1k
            } else if (tag == 3) {
180
16.8k
                count = bytestream2_get_byte(&gb) * 2;
181
16.8k
                if (writeoffset + count > hnm->width * hnm->height) {
182
206
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
183
206
                    return AVERROR_INVALIDDATA;
184
206
                }
185
3.25M
                while (count > 0) {
186
3.23M
                    hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
187
3.23M
                    count--;
188
3.23M
                }
189
16.6k
                bytestream2_skip(&gb, 1);
190
16.6k
            } else {
191
366
                break;
192
366
            }
193
137k
            if (writeoffset > hnm->width * hnm->height) {
194
457
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
195
457
                return AVERROR_INVALIDDATA;
196
457
            }
197
137k
        } else {
198
75.2k
            previous = bytestream2_peek_byte(&gb) & 0x20;
199
75.2k
            backline = bytestream2_peek_byte(&gb) & 0x40;
200
75.2k
            backward = bytestream2_peek_byte(&gb) & 0x80;
201
75.2k
            bytestream2_skip(&gb, 1);
202
75.2k
            swap   = bytestream2_peek_byte(&gb) & 0x01;
203
75.2k
            offset = bytestream2_get_le16(&gb);
204
75.2k
            offset = (offset >> 1) & 0x7FFF;
205
75.2k
            offset = writeoffset + (offset * 2) - 0x8000;
206
207
75.2k
            left = count;
208
209
75.2k
            if (!backward && offset + 2*count > hnm->width * hnm->height) {
210
293
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
211
293
                return AVERROR_INVALIDDATA;
212
74.9k
            } else if (backward && offset + 1 >= hnm->width * hnm->height) {
213
233
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
214
233
                return AVERROR_INVALIDDATA;
215
74.7k
            } else if (writeoffset + 2*count > hnm->width * hnm->height) {
216
236
                av_log(avctx, AV_LOG_ERROR,
217
236
                       "Attempting to write out of bounds\n");
218
236
                return AVERROR_INVALIDDATA;
219
220
236
            }
221
74.5k
            if(backward) {
222
30.2k
                if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) {
223
327
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
224
327
                    return AVERROR_INVALIDDATA;
225
327
                }
226
44.2k
            } else {
227
44.2k
                if (offset < (!!backline)*(2 * hnm->width - 1)) {
228
2.05k
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
229
2.05k
                    return AVERROR_INVALIDDATA;
230
2.05k
                }
231
44.2k
            }
232
233
72.1k
            if (previous) {
234
787k
                while (left > 0) {
235
750k
                    if (backline) {
236
570k
                        hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
237
570k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
238
570k
                        offset++;
239
570k
                    } else {
240
180k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
241
180k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
242
180k
                    }
243
750k
                    if (backward)
244
526k
                        offset -= 4;
245
750k
                    left--;
246
750k
                }
247
36.4k
            } else {
248
565k
                while (left > 0) {
249
530k
                    if (backline) {
250
359k
                        hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
251
359k
                        hnm->current[writeoffset++] = hnm->current[offset++];
252
359k
                        offset++;
253
359k
                    } else {
254
171k
                        hnm->current[writeoffset++] = hnm->current[offset++];
255
171k
                        hnm->current[writeoffset++] = hnm->current[offset++];
256
171k
                    }
257
530k
                    if (backward)
258
167k
                        offset -= 4;
259
530k
                    left--;
260
530k
                }
261
35.6k
            }
262
263
72.1k
            if (swap) {
264
28.3k
                left         = count;
265
28.3k
                writeoffset -= count * 2;
266
620k
                while (left > 0) {
267
591k
                    swap = hnm->current[writeoffset];
268
591k
                    hnm->current[writeoffset] = hnm->current[writeoffset + 1];
269
591k
                    hnm->current[writeoffset + 1] = swap;
270
591k
                    left--;
271
591k
                    writeoffset += 2;
272
591k
                }
273
28.3k
            }
274
72.1k
        }
275
213k
    }
276
4.92k
    return 0;
277
8.92k
}
278
279
static void decode_interframe_v4a(AVCodecContext *avctx, const uint8_t *src,
280
                                  uint32_t size)
281
3.01k
{
282
3.01k
    Hnm4VideoContext *hnm = avctx->priv_data;
283
3.01k
    GetByteContext gb;
284
3.01k
    uint32_t writeoffset = 0, offset;
285
3.01k
    uint8_t tag, count, previous, delta;
286
287
3.01k
    bytestream2_init(&gb, src, size);
288
289
192k
    while (bytestream2_tell(&gb) < size) {
290
190k
        count = bytestream2_peek_byte(&gb) & 0x3F;
291
190k
        if (count == 0) {
292
65.2k
            tag = bytestream2_get_byte(&gb) & 0xC0;
293
65.2k
            tag = tag >> 6;
294
65.2k
            if (tag == 0) {
295
60.5k
                writeoffset += bytestream2_get_byte(&gb);
296
60.5k
            } else if (tag == 1) {
297
3.28k
                if (writeoffset + hnm->width >= hnm->width * hnm->height) {
298
229
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
299
229
                    break;
300
229
                }
301
3.05k
                hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
302
3.05k
                hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
303
3.05k
                writeoffset++;
304
3.05k
            } else if (tag == 2) {
305
1.14k
                writeoffset += hnm->width;
306
1.14k
            } else if (tag == 3) {
307
231
                break;
308
231
            }
309
64.7k
            if (writeoffset > hnm->width * hnm->height) {
310
246
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
311
246
                break;
312
246
            }
313
125k
        } else {
314
125k
            delta    = bytestream2_peek_byte(&gb) & 0x80;
315
125k
            previous = bytestream2_peek_byte(&gb) & 0x40;
316
125k
            bytestream2_skip(&gb, 1);
317
318
125k
            offset  = writeoffset;
319
125k
            offset += bytestream2_get_le16(&gb);
320
321
125k
            if (delta) {
322
109k
                if (offset < 0x10000) {
323
337
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
324
337
                    break;
325
337
                }
326
108k
                offset -= 0x10000;
327
108k
            }
328
329
125k
            if (offset + hnm->width + count >= hnm->width * hnm->height) {
330
292
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
331
292
                break;
332
124k
            } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
333
216
                av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n");
334
216
                break;
335
216
            }
336
337
124k
            if (previous) {
338
6.12M
                while (count > 0) {
339
6.01M
                    hnm->current[writeoffset]              = hnm->previous[offset];
340
6.01M
                    hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
341
6.01M
                    writeoffset++;
342
6.01M
                    offset++;
343
6.01M
                    count--;
344
6.01M
                }
345
107k
            } else {
346
647k
                while (count > 0) {
347
630k
                    hnm->current[writeoffset]              = hnm->current[offset];
348
630k
                    hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
349
630k
                    writeoffset++;
350
630k
                    offset++;
351
630k
                    count--;
352
630k
                }
353
17.1k
            }
354
124k
        }
355
190k
    }
356
3.01k
}
357
358
static void hnm_update_palette(AVCodecContext *avctx, const uint8_t *src,
359
                               uint32_t size)
360
121k
{
361
121k
    Hnm4VideoContext *hnm = avctx->priv_data;
362
121k
    GetByteContext gb;
363
121k
    uint8_t start, writeoffset;
364
121k
    uint16_t count;
365
121k
    int eight_bit_colors;
366
367
121k
    eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
368
369
    // skip first 8 bytes
370
121k
    bytestream2_init(&gb, src + 8, size - 8);
371
372
190k
    while (bytestream2_tell(&gb) < size - 8) {
373
69.3k
        start = bytestream2_get_byte(&gb);
374
69.3k
        count = bytestream2_get_byte(&gb);
375
69.3k
        if (start == 255 && count == 255)
376
515
            break;
377
68.8k
        if (count == 0)
378
21.1k
            count = 256;
379
68.8k
        writeoffset = start;
380
16.6M
        while (count > 0) {
381
16.5M
            hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
382
16.5M
            if (!eight_bit_colors)
383
16.5M
                hnm->palette[writeoffset] <<= 2;
384
16.5M
            hnm->palette[writeoffset] |= (0xFFU << 24);
385
16.5M
            count--;
386
16.5M
            writeoffset++;
387
16.5M
        }
388
68.8k
    }
389
121k
}
390
391
static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
392
                            int *got_frame, AVPacket *avpkt)
393
198k
{
394
198k
    Hnm4VideoContext *hnm = avctx->priv_data;
395
198k
    int ret;
396
198k
    uint16_t chunk_id;
397
398
198k
    if (avpkt->size < 8) {
399
48.4k
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
400
48.4k
        return AVERROR_INVALIDDATA;
401
48.4k
    }
402
403
149k
    chunk_id = AV_RL16(avpkt->data + 4);
404
405
149k
    if (chunk_id == HNM4_CHUNK_ID_PL) {
406
121k
        hnm_update_palette(avctx, avpkt->data, avpkt->size);
407
121k
    } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
408
5.06k
        if (avpkt->size < 12) {
409
264
            av_log(avctx, AV_LOG_ERROR, "packet too small\n");
410
264
            return AVERROR_INVALIDDATA;
411
264
        }
412
4.79k
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
413
93
            return ret;
414
415
4.70k
        unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
416
4.70k
        memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
417
4.70k
        if (hnm->version == 0x4a)
418
371
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
419
4.33k
        else
420
4.33k
            postprocess_current_frame(avctx);
421
4.70k
        copy_processed_frame(avctx, frame);
422
4.70k
        frame->pict_type = AV_PICTURE_TYPE_I;
423
4.70k
        frame->flags |= AV_FRAME_FLAG_KEY;
424
4.70k
        memcpy(frame->data[1], hnm->palette, 256 * 4);
425
4.70k
        *got_frame = 1;
426
22.7k
    } else if (chunk_id == HNM4_CHUNK_ID_IU) {
427
12.1k
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
428
190
            return ret;
429
430
11.9k
        if (hnm->version == 0x4a) {
431
3.01k
            decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
432
3.01k
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
433
8.92k
        } else {
434
8.92k
            int ret = decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
435
8.92k
            if (ret < 0)
436
4.00k
                return ret;
437
4.92k
            postprocess_current_frame(avctx);
438
4.92k
        }
439
7.94k
        copy_processed_frame(avctx, frame);
440
7.94k
        frame->pict_type = AV_PICTURE_TYPE_P;
441
7.94k
        frame->flags &= ~AV_FRAME_FLAG_KEY;
442
7.94k
        memcpy(frame->data[1], hnm->palette, 256 * 4);
443
7.94k
        *got_frame = 1;
444
7.94k
        FFSWAP(uint8_t *, hnm->current, hnm->previous);
445
10.6k
    } else {
446
10.6k
        av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
447
10.6k
        return AVERROR_INVALIDDATA;
448
10.6k
    }
449
450
134k
    return avpkt->size;
451
149k
}
452
453
static av_cold int hnm_decode_init(AVCodecContext *avctx)
454
975
{
455
975
    Hnm4VideoContext *hnm = avctx->priv_data;
456
975
    int ret;
457
458
975
    if (avctx->extradata_size < 1) {
459
173
        av_log(avctx, AV_LOG_ERROR,
460
173
               "Extradata missing, decoder requires version number\n");
461
173
        return AVERROR_INVALIDDATA;
462
173
    }
463
464
802
    ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
465
802
    if (ret < 0)
466
19
        return ret;
467
783
    if (avctx->height & 1)
468
1
        return AVERROR(EINVAL);
469
470
782
    hnm->version   = avctx->extradata[0];
471
782
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
472
782
    hnm->width     = avctx->width;
473
782
    hnm->height    = avctx->height;
474
782
    hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
475
782
    hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
476
782
    hnm->processed = av_mallocz(avctx->width * avctx->height);
477
478
782
    if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) {
479
0
        av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
480
0
        return AVERROR(ENOMEM);
481
0
    }
482
483
782
    hnm->current  = hnm->buffer1;
484
782
    hnm->previous = hnm->buffer2;
485
486
782
    return 0;
487
782
}
488
489
static av_cold int hnm_decode_end(AVCodecContext *avctx)
490
975
{
491
975
    Hnm4VideoContext *hnm = avctx->priv_data;
492
493
975
    av_freep(&hnm->buffer1);
494
975
    av_freep(&hnm->buffer2);
495
975
    av_freep(&hnm->processed);
496
497
975
    return 0;
498
975
}
499
500
const FFCodec ff_hnm4_video_decoder = {
501
    .p.name         = "hnm4video",
502
    CODEC_LONG_NAME("HNM 4 video"),
503
    .p.type         = AVMEDIA_TYPE_VIDEO,
504
    .p.id           = AV_CODEC_ID_HNM4_VIDEO,
505
    .priv_data_size = sizeof(Hnm4VideoContext),
506
    .init           = hnm_decode_init,
507
    .close          = hnm_decode_end,
508
    FF_CODEC_DECODE_CB(hnm_decode_frame),
509
    .p.capabilities = AV_CODEC_CAP_DR1,
510
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
511
};