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