Coverage Report

Created: 2025-08-28 07:12

/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
128k
#define HNM4_CHUNK_ID_PL 19536
35
26.8k
#define HNM4_CHUNK_ID_IZ 23113
36
21.1k
#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
319k
{
53
319k
    int ret;
54
55
319k
    if (!*bits) {
56
13.4k
        *bitbuf = bytestream2_get_le32(gb);
57
13.4k
        *bits = 32;
58
13.4k
    }
59
60
319k
    ret = *bitbuf >> 31;
61
319k
    *bitbuf <<= 1;
62
319k
    (*bits)--;
63
64
319k
    return ret;
65
319k
}
66
67
static void unpack_intraframe(AVCodecContext *avctx, const uint8_t *src,
68
                              uint32_t size)
69
5.19k
{
70
5.19k
    Hnm4VideoContext *hnm = avctx->priv_data;
71
5.19k
    GetByteContext gb;
72
5.19k
    uint32_t bitbuf = 0, writeoffset = 0, count = 0;
73
5.19k
    uint16_t word;
74
5.19k
    int32_t offset;
75
5.19k
    int bits = 0;
76
77
5.19k
    bytestream2_init(&gb, src, size);
78
79
194k
    while (bytestream2_tell(&gb) < size) {
80
192k
        if (getbit(&gb, &bitbuf, &bits)) {
81
137k
            if (writeoffset >= hnm->width * hnm->height) {
82
227
                av_log(avctx, AV_LOG_ERROR,
83
227
                       "Attempting to write out of bounds\n");
84
227
                break;
85
227
            }
86
136k
            hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
87
136k
        } else {
88
55.3k
            if (getbit(&gb, &bitbuf, &bits)) {
89
19.3k
                word   = bytestream2_get_le16(&gb);
90
19.3k
                count  = word & 0x07;
91
19.3k
                offset = (word >> 3) - 0x2000;
92
19.3k
                if (!count)
93
3.88k
                    count = bytestream2_get_byte(&gb);
94
19.3k
                if (!count)
95
261
                    return;
96
35.9k
            } else {
97
35.9k
                count  = getbit(&gb, &bitbuf, &bits) * 2;
98
35.9k
                count += getbit(&gb, &bitbuf, &bits);
99
35.9k
                offset = bytestream2_get_byte(&gb) - 0x0100;
100
35.9k
            }
101
55.0k
            count  += 2;
102
55.0k
            offset += writeoffset;
103
55.0k
            if (offset < 0 || offset + count >= hnm->width * hnm->height) {
104
2.77k
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
105
2.77k
                break;
106
52.2k
            } else if (writeoffset + count >= hnm->width * hnm->height) {
107
213
                av_log(avctx, AV_LOG_ERROR,
108
213
                       "Attempting to write out of bounds\n");
109
213
                break;
110
213
            }
111
921k
            while (count--) {
112
869k
                hnm->current[writeoffset++] = hnm->current[offset++];
113
869k
            }
114
52.0k
        }
115
192k
    }
116
5.19k
}
117
118
static void postprocess_current_frame(AVCodecContext *avctx)
119
10.9k
{
120
10.9k
    Hnm4VideoContext *hnm = avctx->priv_data;
121
10.9k
    uint32_t x, y, src_y;
122
10.9k
    int width = hnm->width;
123
124
149M
    for (y = 0; y < hnm->height; y++) {
125
149M
        uint8_t *dst = hnm->processed + y * width;
126
149M
        const uint8_t *src = hnm->current;
127
149M
        src_y = y - (y % 2);
128
149M
        src += src_y * width + (y % 2);
129
12.3G
        for (x = 0; x < width; x++) {
130
12.2G
            dst[x] = *src;
131
12.2G
            src += 2;
132
12.2G
        }
133
149M
    }
134
10.9k
}
135
136
static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
137
15.3k
{
138
15.3k
    Hnm4VideoContext *hnm = avctx->priv_data;
139
15.3k
    uint8_t *src = hnm->processed;
140
15.3k
    uint8_t *dst = frame->data[0];
141
15.3k
    int y;
142
143
159M
    for (y = 0; y < hnm->height; y++) {
144
159M
        memcpy(dst, src, hnm->width);
145
159M
        src += hnm->width;
146
159M
        dst += frame->linesize[0];
147
159M
    }
148
15.3k
}
149
150
static int decode_interframe_v4(AVCodecContext *avctx, const uint8_t *src, uint32_t size)
151
10.8k
{
152
10.8k
    Hnm4VideoContext *hnm = avctx->priv_data;
153
10.8k
    GetByteContext gb;
154
10.8k
    uint32_t writeoffset = 0;
155
10.8k
    int count, left, offset;
156
10.8k
    uint8_t tag, previous, backline, backward, swap;
157
158
10.8k
    bytestream2_init(&gb, src, size);
159
160
266k
    while (bytestream2_tell(&gb) < size) {
161
260k
        count = bytestream2_peek_byte(&gb) & 0x1F;
162
260k
        if (count == 0) {
163
170k
            tag = bytestream2_get_byte(&gb) & 0xE0;
164
170k
            tag = tag >> 5;
165
166
170k
            if (tag == 0) {
167
136k
                if (writeoffset + 2 > hnm->width * hnm->height) {
168
200
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
169
200
                    return AVERROR_INVALIDDATA;
170
200
                }
171
136k
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
172
136k
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
173
136k
            } else if (tag == 1) {
174
1.66k
                writeoffset += bytestream2_get_byte(&gb) * 2;
175
32.9k
            } else if (tag == 2) {
176
10.6k
                count = bytestream2_get_le16(&gb);
177
10.6k
                count *= 2;
178
10.6k
                writeoffset += count;
179
22.2k
            } else if (tag == 3) {
180
21.8k
                count = bytestream2_get_byte(&gb) * 2;
181
21.8k
                if (writeoffset + count > hnm->width * hnm->height) {
182
225
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
183
225
                    return AVERROR_INVALIDDATA;
184
225
                }
185
4.25M
                while (count > 0) {
186
4.23M
                    hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
187
4.23M
                    count--;
188
4.23M
                }
189
21.6k
                bytestream2_skip(&gb, 1);
190
21.6k
            } else {
191
412
                break;
192
412
            }
193
170k
            if (writeoffset > hnm->width * hnm->height) {
194
444
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
195
444
                return AVERROR_INVALIDDATA;
196
444
            }
197
170k
        } else {
198
89.6k
            previous = bytestream2_peek_byte(&gb) & 0x20;
199
89.6k
            backline = bytestream2_peek_byte(&gb) & 0x40;
200
89.6k
            backward = bytestream2_peek_byte(&gb) & 0x80;
201
89.6k
            bytestream2_skip(&gb, 1);
202
89.6k
            swap   = bytestream2_peek_byte(&gb) & 0x01;
203
89.6k
            offset = bytestream2_get_le16(&gb);
204
89.6k
            offset = (offset >> 1) & 0x7FFF;
205
89.6k
            offset = writeoffset + (offset * 2) - 0x8000;
206
207
89.6k
            left = count;
208
209
89.6k
            if (!backward && offset + 2*count > hnm->width * hnm->height) {
210
284
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
211
284
                return AVERROR_INVALIDDATA;
212
89.3k
            } else if (backward && offset + 1 >= hnm->width * hnm->height) {
213
249
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
214
249
                return AVERROR_INVALIDDATA;
215
89.0k
            } else if (writeoffset + 2*count > hnm->width * hnm->height) {
216
249
                av_log(avctx, AV_LOG_ERROR,
217
249
                       "Attempting to write out of bounds\n");
218
249
                return AVERROR_INVALIDDATA;
219
220
249
            }
221
88.8k
            if(backward) {
222
37.4k
                if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) {
223
354
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
224
354
                    return AVERROR_INVALIDDATA;
225
354
                }
226
51.3k
            } else {
227
51.3k
                if (offset < (!!backline)*(2 * hnm->width - 1)) {
228
2.60k
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
229
2.60k
                    return AVERROR_INVALIDDATA;
230
2.60k
                }
231
51.3k
            }
232
233
85.8k
            if (previous) {
234
984k
                while (left > 0) {
235
940k
                    if (backline) {
236
720k
                        hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
237
720k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
238
720k
                        offset++;
239
720k
                    } else {
240
220k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
241
220k
                        hnm->current[writeoffset++] = hnm->previous[offset++];
242
220k
                    }
243
940k
                    if (backward)
244
672k
                        offset -= 4;
245
940k
                    left--;
246
940k
                }
247
44.1k
            } else {
248
664k
                while (left > 0) {
249
622k
                    if (backline) {
250
401k
                        hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
251
401k
                        hnm->current[writeoffset++] = hnm->current[offset++];
252
401k
                        offset++;
253
401k
                    } else {
254
220k
                        hnm->current[writeoffset++] = hnm->current[offset++];
255
220k
                        hnm->current[writeoffset++] = hnm->current[offset++];
256
220k
                    }
257
622k
                    if (backward)
258
212k
                        offset -= 4;
259
622k
                    left--;
260
622k
                }
261
41.7k
            }
262
263
85.8k
            if (swap) {
264
35.6k
                left         = count;
265
35.6k
                writeoffset -= count * 2;
266
803k
                while (left > 0) {
267
768k
                    swap = hnm->current[writeoffset];
268
768k
                    hnm->current[writeoffset] = hnm->current[writeoffset + 1];
269
768k
                    hnm->current[writeoffset + 1] = swap;
270
768k
                    left--;
271
768k
                    writeoffset += 2;
272
768k
                }
273
35.6k
            }
274
85.8k
        }
275
260k
    }
276
6.20k
    return 0;
277
10.8k
}
278
279
static void decode_interframe_v4a(AVCodecContext *avctx, const uint8_t *src,
280
                                  uint32_t size)
281
3.97k
{
282
3.97k
    Hnm4VideoContext *hnm = avctx->priv_data;
283
3.97k
    GetByteContext gb;
284
3.97k
    uint32_t writeoffset = 0, offset;
285
3.97k
    uint8_t tag, count, previous, delta;
286
287
3.97k
    bytestream2_init(&gb, src, size);
288
289
173k
    while (bytestream2_tell(&gb) < size) {
290
171k
        count = bytestream2_peek_byte(&gb) & 0x3F;
291
171k
        if (count == 0) {
292
94.3k
            tag = bytestream2_get_byte(&gb) & 0xC0;
293
94.3k
            tag = tag >> 6;
294
94.3k
            if (tag == 0) {
295
89.9k
                writeoffset += bytestream2_get_byte(&gb);
296
89.9k
            } else if (tag == 1) {
297
2.74k
                if (writeoffset + hnm->width >= hnm->width * hnm->height) {
298
210
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
299
210
                    break;
300
210
                }
301
2.53k
                hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
302
2.53k
                hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
303
2.53k
                writeoffset++;
304
2.53k
            } else if (tag == 2) {
305
983
                writeoffset += hnm->width;
306
983
            } else if (tag == 3) {
307
768
                break;
308
768
            }
309
93.4k
            if (writeoffset > hnm->width * hnm->height) {
310
262
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
311
262
                break;
312
262
            }
313
93.4k
        } else {
314
77.5k
            delta    = bytestream2_peek_byte(&gb) & 0x80;
315
77.5k
            previous = bytestream2_peek_byte(&gb) & 0x40;
316
77.5k
            bytestream2_skip(&gb, 1);
317
318
77.5k
            offset  = writeoffset;
319
77.5k
            offset += bytestream2_get_le16(&gb);
320
321
77.5k
            if (delta) {
322
62.5k
                if (offset < 0x10000) {
323
397
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
324
397
                    break;
325
397
                }
326
62.1k
                offset -= 0x10000;
327
62.1k
            }
328
329
77.1k
            if (offset + hnm->width + count >= hnm->width * hnm->height) {
330
363
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
331
363
                break;
332
76.8k
            } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
333
243
                av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n");
334
243
                break;
335
243
            }
336
337
76.5k
            if (previous) {
338
3.79M
                while (count > 0) {
339
3.72M
                    hnm->current[writeoffset]              = hnm->previous[offset];
340
3.72M
                    hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
341
3.72M
                    writeoffset++;
342
3.72M
                    offset++;
343
3.72M
                    count--;
344
3.72M
                }
345
67.4k
            } else {
346
312k
                while (count > 0) {
347
303k
                    hnm->current[writeoffset]              = hnm->current[offset];
348
303k
                    hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
349
303k
                    writeoffset++;
350
303k
                    offset++;
351
303k
                    count--;
352
303k
                }
353
9.13k
            }
354
76.5k
        }
355
171k
    }
356
3.97k
}
357
358
static void hnm_update_palette(AVCodecContext *avctx, const uint8_t *src,
359
                               uint32_t size)
360
101k
{
361
101k
    Hnm4VideoContext *hnm = avctx->priv_data;
362
101k
    GetByteContext gb;
363
101k
    uint8_t start, writeoffset;
364
101k
    uint16_t count;
365
101k
    int eight_bit_colors;
366
367
101k
    eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
368
369
    // skip first 8 bytes
370
101k
    bytestream2_init(&gb, src + 8, size - 8);
371
372
156k
    while (bytestream2_tell(&gb) < size - 8) {
373
56.1k
        start = bytestream2_get_byte(&gb);
374
56.1k
        count = bytestream2_get_byte(&gb);
375
56.1k
        if (start == 255 && count == 255)
376
422
            break;
377
55.6k
        if (count == 0)
378
12.8k
            count = 256;
379
55.6k
        writeoffset = start;
380
13.4M
        while (count > 0) {
381
13.3M
            hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
382
13.3M
            if (!eight_bit_colors)
383
13.3M
                hnm->palette[writeoffset] <<= 2;
384
13.3M
            hnm->palette[writeoffset] |= (0xFFU << 24);
385
13.3M
            count--;
386
13.3M
            writeoffset++;
387
13.3M
        }
388
55.6k
    }
389
101k
}
390
391
static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame,
392
                            int *got_frame, AVPacket *avpkt)
393
136k
{
394
136k
    Hnm4VideoContext *hnm = avctx->priv_data;
395
136k
    int ret;
396
136k
    uint16_t chunk_id;
397
398
136k
    if (avpkt->size < 8) {
399
7.97k
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
400
7.97k
        return AVERROR_INVALIDDATA;
401
7.97k
    }
402
403
128k
    chunk_id = AV_RL16(avpkt->data + 4);
404
405
128k
    if (chunk_id == HNM4_CHUNK_ID_PL) {
406
101k
        hnm_update_palette(avctx, avpkt->data, avpkt->size);
407
101k
    } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
408
5.63k
        if (avpkt->size < 12) {
409
308
            av_log(avctx, AV_LOG_ERROR, "packet too small\n");
410
308
            return AVERROR_INVALIDDATA;
411
308
        }
412
5.32k
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
413
131
            return ret;
414
415
5.19k
        unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
416
5.19k
        memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
417
5.19k
        if (hnm->version == 0x4a)
418
474
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
419
4.72k
        else
420
4.72k
            postprocess_current_frame(avctx);
421
5.19k
        copy_processed_frame(avctx, frame);
422
5.19k
        frame->pict_type = AV_PICTURE_TYPE_I;
423
5.19k
        frame->flags |= AV_FRAME_FLAG_KEY;
424
5.19k
        memcpy(frame->data[1], hnm->palette, 256 * 4);
425
5.19k
        *got_frame = 1;
426
21.1k
    } else if (chunk_id == HNM4_CHUNK_ID_IU) {
427
14.9k
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
428
169
            return ret;
429
430
14.7k
        if (hnm->version == 0x4a) {
431
3.97k
            decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
432
3.97k
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
433
10.8k
        } else {
434
10.8k
            int ret = decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
435
10.8k
            if (ret < 0)
436
4.61k
                return ret;
437
6.20k
            postprocess_current_frame(avctx);
438
6.20k
        }
439
10.1k
        copy_processed_frame(avctx, frame);
440
10.1k
        frame->pict_type = AV_PICTURE_TYPE_P;
441
10.1k
        frame->flags &= ~AV_FRAME_FLAG_KEY;
442
10.1k
        memcpy(frame->data[1], hnm->palette, 256 * 4);
443
10.1k
        *got_frame = 1;
444
10.1k
        FFSWAP(uint8_t *, hnm->current, hnm->previous);
445
10.1k
    } else {
446
6.21k
        av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
447
6.21k
        return AVERROR_INVALIDDATA;
448
6.21k
    }
449
450
116k
    return avpkt->size;
451
128k
}
452
453
static av_cold int hnm_decode_init(AVCodecContext *avctx)
454
1.05k
{
455
1.05k
    Hnm4VideoContext *hnm = avctx->priv_data;
456
1.05k
    int ret;
457
458
1.05k
    if (avctx->extradata_size < 1) {
459
178
        av_log(avctx, AV_LOG_ERROR,
460
178
               "Extradata missing, decoder requires version number\n");
461
178
        return AVERROR_INVALIDDATA;
462
178
    }
463
464
877
    ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
465
877
    if (ret < 0)
466
15
        return ret;
467
862
    if (avctx->height & 1)
468
2
        return AVERROR(EINVAL);
469
470
860
    hnm->version   = avctx->extradata[0];
471
860
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
472
860
    hnm->width     = avctx->width;
473
860
    hnm->height    = avctx->height;
474
860
    hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
475
860
    hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
476
860
    hnm->processed = av_mallocz(avctx->width * avctx->height);
477
478
860
    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
860
    hnm->current  = hnm->buffer1;
484
860
    hnm->previous = hnm->buffer2;
485
486
860
    return 0;
487
860
}
488
489
static av_cold int hnm_decode_end(AVCodecContext *avctx)
490
1.05k
{
491
1.05k
    Hnm4VideoContext *hnm = avctx->priv_data;
492
493
1.05k
    av_freep(&hnm->buffer1);
494
1.05k
    av_freep(&hnm->buffer2);
495
1.05k
    av_freep(&hnm->processed);
496
497
1.05k
    return 0;
498
1.05k
}
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
};