1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * CDToons video decoder 3cabdff1aSopenharmony_ci * Copyright (C) 2020 Alyssa Milburn <amilburn@zall.org> 4cabdff1aSopenharmony_ci * 5cabdff1aSopenharmony_ci * This file is part of FFmpeg. 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 11cabdff1aSopenharmony_ci * 12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15cabdff1aSopenharmony_ci * Lesser General Public License for more details. 16cabdff1aSopenharmony_ci * 17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20cabdff1aSopenharmony_ci */ 21cabdff1aSopenharmony_ci 22cabdff1aSopenharmony_ci/** 23cabdff1aSopenharmony_ci * @file 24cabdff1aSopenharmony_ci * CDToons video decoder 25cabdff1aSopenharmony_ci * @author Alyssa Milburn <amilburn@zall.org> 26cabdff1aSopenharmony_ci */ 27cabdff1aSopenharmony_ci 28cabdff1aSopenharmony_ci#include <stdint.h> 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "libavutil/attributes.h" 31cabdff1aSopenharmony_ci#include "libavutil/internal.h" 32cabdff1aSopenharmony_ci#include "avcodec.h" 33cabdff1aSopenharmony_ci#include "bytestream.h" 34cabdff1aSopenharmony_ci#include "codec_internal.h" 35cabdff1aSopenharmony_ci#include "internal.h" 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_ci#define CDTOONS_HEADER_SIZE 44 38cabdff1aSopenharmony_ci#define CDTOONS_MAX_SPRITES 1200 39cabdff1aSopenharmony_ci 40cabdff1aSopenharmony_citypedef struct CDToonsSprite { 41cabdff1aSopenharmony_ci uint16_t flags; 42cabdff1aSopenharmony_ci uint16_t owner_frame; 43cabdff1aSopenharmony_ci uint16_t start_frame; 44cabdff1aSopenharmony_ci uint16_t end_frame; 45cabdff1aSopenharmony_ci unsigned int alloc_size; 46cabdff1aSopenharmony_ci uint32_t size; 47cabdff1aSopenharmony_ci uint8_t *data; 48cabdff1aSopenharmony_ci int active; 49cabdff1aSopenharmony_ci} CDToonsSprite; 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_citypedef struct CDToonsContext { 52cabdff1aSopenharmony_ci AVFrame *frame; 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_ci uint16_t last_pal_id; ///< The index of the active palette sprite. 55cabdff1aSopenharmony_ci uint32_t pal[256]; ///< The currently-used palette data. 56cabdff1aSopenharmony_ci CDToonsSprite sprites[CDTOONS_MAX_SPRITES]; 57cabdff1aSopenharmony_ci} CDToonsContext; 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_cistatic int cdtoons_render_sprite(AVCodecContext *avctx, const uint8_t *data, 60cabdff1aSopenharmony_ci uint32_t data_size, 61cabdff1aSopenharmony_ci int dst_x, int dst_y, int width, int height) 62cabdff1aSopenharmony_ci{ 63cabdff1aSopenharmony_ci CDToonsContext *c = avctx->priv_data; 64cabdff1aSopenharmony_ci const uint8_t *next_line = data; 65cabdff1aSopenharmony_ci const uint8_t *end = data + data_size; 66cabdff1aSopenharmony_ci uint16_t line_size; 67cabdff1aSopenharmony_ci uint8_t *dest; 68cabdff1aSopenharmony_ci int skip = 0, to_skip, x; 69cabdff1aSopenharmony_ci 70cabdff1aSopenharmony_ci if (dst_x + width > avctx->width) 71cabdff1aSopenharmony_ci width = avctx->width - dst_x; 72cabdff1aSopenharmony_ci if (dst_y + height > avctx->height) 73cabdff1aSopenharmony_ci height = avctx->height - dst_y; 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_ci if (dst_x < 0) { 76cabdff1aSopenharmony_ci /* we need to skip the start of the scanlines */ 77cabdff1aSopenharmony_ci skip = -dst_x; 78cabdff1aSopenharmony_ci if (width <= skip) 79cabdff1aSopenharmony_ci return 0; 80cabdff1aSopenharmony_ci dst_x = 0; 81cabdff1aSopenharmony_ci } 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_ci for (int y = 0; y < height; y++) { 84cabdff1aSopenharmony_ci /* one scanline at a time, size is provided */ 85cabdff1aSopenharmony_ci data = next_line; 86cabdff1aSopenharmony_ci if (end - data < 2) 87cabdff1aSopenharmony_ci return 1; 88cabdff1aSopenharmony_ci line_size = bytestream_get_be16(&data); 89cabdff1aSopenharmony_ci if (end - data < line_size) 90cabdff1aSopenharmony_ci return 1; 91cabdff1aSopenharmony_ci next_line = data + line_size; 92cabdff1aSopenharmony_ci if (dst_y + y < 0) 93cabdff1aSopenharmony_ci continue; 94cabdff1aSopenharmony_ci 95cabdff1aSopenharmony_ci dest = c->frame->data[0] + (dst_y + y) * c->frame->linesize[0] + dst_x; 96cabdff1aSopenharmony_ci 97cabdff1aSopenharmony_ci to_skip = skip; 98cabdff1aSopenharmony_ci x = 0; 99cabdff1aSopenharmony_ci while (x < width - skip) { 100cabdff1aSopenharmony_ci int raw, size, step; 101cabdff1aSopenharmony_ci uint8_t val; 102cabdff1aSopenharmony_ci 103cabdff1aSopenharmony_ci if (data >= end) 104cabdff1aSopenharmony_ci return 1; 105cabdff1aSopenharmony_ci 106cabdff1aSopenharmony_ci val = bytestream_get_byte(&data); 107cabdff1aSopenharmony_ci raw = !(val & 0x80); 108cabdff1aSopenharmony_ci size = (int)(val & 0x7F) + 1; 109cabdff1aSopenharmony_ci 110cabdff1aSopenharmony_ci /* skip the start of a scanline if it is off-screen */ 111cabdff1aSopenharmony_ci if (to_skip >= size) { 112cabdff1aSopenharmony_ci to_skip -= size; 113cabdff1aSopenharmony_ci if (raw) { 114cabdff1aSopenharmony_ci step = size; 115cabdff1aSopenharmony_ci } else { 116cabdff1aSopenharmony_ci step = 1; 117cabdff1aSopenharmony_ci } 118cabdff1aSopenharmony_ci if (next_line - data < step) 119cabdff1aSopenharmony_ci return 1; 120cabdff1aSopenharmony_ci data += step; 121cabdff1aSopenharmony_ci continue; 122cabdff1aSopenharmony_ci } else if (to_skip) { 123cabdff1aSopenharmony_ci size -= to_skip; 124cabdff1aSopenharmony_ci if (raw) { 125cabdff1aSopenharmony_ci if (next_line - data < to_skip) 126cabdff1aSopenharmony_ci return 1; 127cabdff1aSopenharmony_ci data += to_skip; 128cabdff1aSopenharmony_ci } 129cabdff1aSopenharmony_ci to_skip = 0; 130cabdff1aSopenharmony_ci } 131cabdff1aSopenharmony_ci 132cabdff1aSopenharmony_ci if (x + size >= width - skip) 133cabdff1aSopenharmony_ci size = width - skip - x; 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci /* either raw data, or a run of a single color */ 136cabdff1aSopenharmony_ci if (raw) { 137cabdff1aSopenharmony_ci if (next_line - data < size) 138cabdff1aSopenharmony_ci return 1; 139cabdff1aSopenharmony_ci memcpy(dest + x, data, size); 140cabdff1aSopenharmony_ci data += size; 141cabdff1aSopenharmony_ci } else { 142cabdff1aSopenharmony_ci uint8_t color = bytestream_get_byte(&data); 143cabdff1aSopenharmony_ci /* ignore transparent runs */ 144cabdff1aSopenharmony_ci if (color) 145cabdff1aSopenharmony_ci memset(dest + x, color, size); 146cabdff1aSopenharmony_ci } 147cabdff1aSopenharmony_ci x += size; 148cabdff1aSopenharmony_ci } 149cabdff1aSopenharmony_ci } 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci return 0; 152cabdff1aSopenharmony_ci} 153cabdff1aSopenharmony_ci 154cabdff1aSopenharmony_cistatic int cdtoons_decode_frame(AVCodecContext *avctx, AVFrame *rframe, 155cabdff1aSopenharmony_ci int *got_frame, AVPacket *avpkt) 156cabdff1aSopenharmony_ci{ 157cabdff1aSopenharmony_ci CDToonsContext *c = avctx->priv_data; 158cabdff1aSopenharmony_ci const uint8_t *buf = avpkt->data; 159cabdff1aSopenharmony_ci const uint8_t *eod = avpkt->data + avpkt->size; 160cabdff1aSopenharmony_ci const int buf_size = avpkt->size; 161cabdff1aSopenharmony_ci uint16_t frame_id; 162cabdff1aSopenharmony_ci uint8_t background_color; 163cabdff1aSopenharmony_ci uint16_t sprite_count, sprite_offset; 164cabdff1aSopenharmony_ci uint8_t referenced_count; 165cabdff1aSopenharmony_ci uint16_t palette_id; 166cabdff1aSopenharmony_ci uint8_t palette_set; 167cabdff1aSopenharmony_ci int ret; 168cabdff1aSopenharmony_ci int saw_embedded_sprites = 0; 169cabdff1aSopenharmony_ci 170cabdff1aSopenharmony_ci if (buf_size < CDTOONS_HEADER_SIZE) 171cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 172cabdff1aSopenharmony_ci 173cabdff1aSopenharmony_ci if ((ret = ff_reget_buffer(avctx, c->frame, 0)) < 0) 174cabdff1aSopenharmony_ci return ret; 175cabdff1aSopenharmony_ci 176cabdff1aSopenharmony_ci /* a lot of the header is useless junk in the absence of 177cabdff1aSopenharmony_ci * dirty rectangling etc */ 178cabdff1aSopenharmony_ci buf += 2; /* version? (always 9?) */ 179cabdff1aSopenharmony_ci frame_id = bytestream_get_be16(&buf); 180cabdff1aSopenharmony_ci buf += 2; /* blocks_valid_until */ 181cabdff1aSopenharmony_ci buf += 1; 182cabdff1aSopenharmony_ci background_color = bytestream_get_byte(&buf); 183cabdff1aSopenharmony_ci buf += 16; /* clip rect, dirty rect */ 184cabdff1aSopenharmony_ci buf += 4; /* flags */ 185cabdff1aSopenharmony_ci sprite_count = bytestream_get_be16(&buf); 186cabdff1aSopenharmony_ci sprite_offset = bytestream_get_be16(&buf); 187cabdff1aSopenharmony_ci buf += 2; /* max block id? */ 188cabdff1aSopenharmony_ci referenced_count = bytestream_get_byte(&buf); 189cabdff1aSopenharmony_ci buf += 1; 190cabdff1aSopenharmony_ci palette_id = bytestream_get_be16(&buf); 191cabdff1aSopenharmony_ci palette_set = bytestream_get_byte(&buf); 192cabdff1aSopenharmony_ci buf += 5; 193cabdff1aSopenharmony_ci 194cabdff1aSopenharmony_ci if (sprite_offset > buf_size) 195cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 196cabdff1aSopenharmony_ci 197cabdff1aSopenharmony_ci /* read new sprites introduced in this frame */ 198cabdff1aSopenharmony_ci buf = avpkt->data + sprite_offset; 199cabdff1aSopenharmony_ci while (sprite_count--) { 200cabdff1aSopenharmony_ci uint32_t size; 201cabdff1aSopenharmony_ci uint16_t sprite_id; 202cabdff1aSopenharmony_ci 203cabdff1aSopenharmony_ci if (buf + 14 > eod) 204cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 205cabdff1aSopenharmony_ci 206cabdff1aSopenharmony_ci sprite_id = bytestream_get_be16(&buf); 207cabdff1aSopenharmony_ci if (sprite_id >= CDTOONS_MAX_SPRITES) { 208cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 209cabdff1aSopenharmony_ci "Sprite ID %d is too high.\n", sprite_id); 210cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 211cabdff1aSopenharmony_ci } 212cabdff1aSopenharmony_ci if (c->sprites[sprite_id].active) { 213cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 214cabdff1aSopenharmony_ci "Sprite ID %d is a duplicate.\n", sprite_id); 215cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 216cabdff1aSopenharmony_ci } 217cabdff1aSopenharmony_ci 218cabdff1aSopenharmony_ci c->sprites[sprite_id].flags = bytestream_get_be16(&buf); 219cabdff1aSopenharmony_ci size = bytestream_get_be32(&buf); 220cabdff1aSopenharmony_ci if (size < 14) { 221cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 222cabdff1aSopenharmony_ci "Sprite only has %d bytes of data.\n", size); 223cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 224cabdff1aSopenharmony_ci } 225cabdff1aSopenharmony_ci size -= 14; 226cabdff1aSopenharmony_ci c->sprites[sprite_id].size = size; 227cabdff1aSopenharmony_ci c->sprites[sprite_id].owner_frame = frame_id; 228cabdff1aSopenharmony_ci c->sprites[sprite_id].start_frame = bytestream_get_be16(&buf); 229cabdff1aSopenharmony_ci c->sprites[sprite_id].end_frame = bytestream_get_be16(&buf); 230cabdff1aSopenharmony_ci buf += 2; 231cabdff1aSopenharmony_ci 232cabdff1aSopenharmony_ci if (size > buf_size || buf + size > eod) 233cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 234cabdff1aSopenharmony_ci 235cabdff1aSopenharmony_ci av_fast_padded_malloc(&c->sprites[sprite_id].data, &c->sprites[sprite_id].alloc_size, size); 236cabdff1aSopenharmony_ci if (!c->sprites[sprite_id].data) 237cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 238cabdff1aSopenharmony_ci 239cabdff1aSopenharmony_ci c->sprites[sprite_id].active = 1; 240cabdff1aSopenharmony_ci 241cabdff1aSopenharmony_ci bytestream_get_buffer(&buf, c->sprites[sprite_id].data, size); 242cabdff1aSopenharmony_ci } 243cabdff1aSopenharmony_ci 244cabdff1aSopenharmony_ci /* render any embedded sprites */ 245cabdff1aSopenharmony_ci while (buf < eod) { 246cabdff1aSopenharmony_ci uint32_t tag, size; 247cabdff1aSopenharmony_ci if (buf + 8 > eod) { 248cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for embedded sprites.\n"); 249cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 250cabdff1aSopenharmony_ci } 251cabdff1aSopenharmony_ci tag = bytestream_get_be32(&buf); 252cabdff1aSopenharmony_ci size = bytestream_get_be32(&buf); 253cabdff1aSopenharmony_ci if (tag == MKBETAG('D', 'i', 'f', 'f')) { 254cabdff1aSopenharmony_ci uint16_t diff_count; 255cabdff1aSopenharmony_ci if (buf + 10 > eod) { 256cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame.\n"); 257cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 258cabdff1aSopenharmony_ci } 259cabdff1aSopenharmony_ci diff_count = bytestream_get_be16(&buf); 260cabdff1aSopenharmony_ci buf += 8; /* clip rect? */ 261cabdff1aSopenharmony_ci for (int i = 0; i < diff_count; i++) { 262cabdff1aSopenharmony_ci int16_t top, left; 263cabdff1aSopenharmony_ci uint16_t diff_size, width, height; 264cabdff1aSopenharmony_ci 265cabdff1aSopenharmony_ci if (buf + 16 > eod) { 266cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame header.\n"); 267cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 268cabdff1aSopenharmony_ci } 269cabdff1aSopenharmony_ci 270cabdff1aSopenharmony_ci top = bytestream_get_be16(&buf); 271cabdff1aSopenharmony_ci left = bytestream_get_be16(&buf); 272cabdff1aSopenharmony_ci buf += 4; /* bottom, right */ 273cabdff1aSopenharmony_ci diff_size = bytestream_get_be32(&buf); 274cabdff1aSopenharmony_ci width = bytestream_get_be16(&buf); 275cabdff1aSopenharmony_ci height = bytestream_get_be16(&buf); 276cabdff1aSopenharmony_ci if (diff_size < 8 || diff_size - 4 > eod - buf) { 277cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data for Diff frame data.\n"); 278cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 279cabdff1aSopenharmony_ci } 280cabdff1aSopenharmony_ci if (cdtoons_render_sprite(avctx, buf + 4, diff_size - 8, 281cabdff1aSopenharmony_ci left, top, width, height)) { 282cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran beyond end of sprite while rendering.\n"); 283cabdff1aSopenharmony_ci } 284cabdff1aSopenharmony_ci buf += diff_size - 4; 285cabdff1aSopenharmony_ci } 286cabdff1aSopenharmony_ci saw_embedded_sprites = 1; 287cabdff1aSopenharmony_ci } else { 288cabdff1aSopenharmony_ci /* we don't care about any other entries */ 289cabdff1aSopenharmony_ci if (size < 8 || size - 8 > eod - buf) { 290cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran out of data for ignored entry (size %X, %d left).\n", size, (int)(eod - buf)); 291cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 292cabdff1aSopenharmony_ci } 293cabdff1aSopenharmony_ci buf += (size - 8); 294cabdff1aSopenharmony_ci } 295cabdff1aSopenharmony_ci } 296cabdff1aSopenharmony_ci 297cabdff1aSopenharmony_ci /* was an intra frame? */ 298cabdff1aSopenharmony_ci if (saw_embedded_sprites) 299cabdff1aSopenharmony_ci goto done; 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_ci /* render any referenced sprites */ 302cabdff1aSopenharmony_ci buf = avpkt->data + CDTOONS_HEADER_SIZE; 303cabdff1aSopenharmony_ci eod = avpkt->data + sprite_offset; 304cabdff1aSopenharmony_ci for (int i = 0; i < referenced_count; i++) { 305cabdff1aSopenharmony_ci const uint8_t *block_data; 306cabdff1aSopenharmony_ci uint16_t sprite_id, width, height; 307cabdff1aSopenharmony_ci int16_t top, left, right; 308cabdff1aSopenharmony_ci 309cabdff1aSopenharmony_ci if (buf + 10 > eod) { 310cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran (seriously) out of data when rendering.\n"); 311cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 312cabdff1aSopenharmony_ci } 313cabdff1aSopenharmony_ci 314cabdff1aSopenharmony_ci sprite_id = bytestream_get_be16(&buf); 315cabdff1aSopenharmony_ci top = bytestream_get_be16(&buf); 316cabdff1aSopenharmony_ci left = bytestream_get_be16(&buf); 317cabdff1aSopenharmony_ci buf += 2; /* bottom */ 318cabdff1aSopenharmony_ci right = bytestream_get_be16(&buf); 319cabdff1aSopenharmony_ci 320cabdff1aSopenharmony_ci if ((i == 0) && (sprite_id == 0)) { 321cabdff1aSopenharmony_ci /* clear background */ 322cabdff1aSopenharmony_ci memset(c->frame->data[0], background_color, 323cabdff1aSopenharmony_ci c->frame->linesize[0] * avctx->height); 324cabdff1aSopenharmony_ci } 325cabdff1aSopenharmony_ci 326cabdff1aSopenharmony_ci if (!right) 327cabdff1aSopenharmony_ci continue; 328cabdff1aSopenharmony_ci if (sprite_id >= CDTOONS_MAX_SPRITES) { 329cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 330cabdff1aSopenharmony_ci "Sprite ID %d is too high.\n", sprite_id); 331cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 332cabdff1aSopenharmony_ci } 333cabdff1aSopenharmony_ci 334cabdff1aSopenharmony_ci block_data = c->sprites[sprite_id].data; 335cabdff1aSopenharmony_ci if (!c->sprites[sprite_id].active) { 336cabdff1aSopenharmony_ci /* this can happen when seeking around */ 337cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Sprite %d is missing.\n", sprite_id); 338cabdff1aSopenharmony_ci continue; 339cabdff1aSopenharmony_ci } 340cabdff1aSopenharmony_ci if (c->sprites[sprite_id].size < 14) { 341cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "Sprite %d is too small.\n", sprite_id); 342cabdff1aSopenharmony_ci continue; 343cabdff1aSopenharmony_ci } 344cabdff1aSopenharmony_ci 345cabdff1aSopenharmony_ci height = bytestream_get_be16(&block_data); 346cabdff1aSopenharmony_ci width = bytestream_get_be16(&block_data); 347cabdff1aSopenharmony_ci block_data += 10; 348cabdff1aSopenharmony_ci if (cdtoons_render_sprite(avctx, block_data, 349cabdff1aSopenharmony_ci c->sprites[sprite_id].size - 14, 350cabdff1aSopenharmony_ci left, top, width, height)) { 351cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "Ran beyond end of sprite while rendering.\n"); 352cabdff1aSopenharmony_ci } 353cabdff1aSopenharmony_ci } 354cabdff1aSopenharmony_ci 355cabdff1aSopenharmony_ci if (palette_id && (palette_id != c->last_pal_id)) { 356cabdff1aSopenharmony_ci if (palette_id >= CDTOONS_MAX_SPRITES) { 357cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 358cabdff1aSopenharmony_ci "Palette ID %d is too high.\n", palette_id); 359cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 360cabdff1aSopenharmony_ci } 361cabdff1aSopenharmony_ci if (!c->sprites[palette_id].active) { 362cabdff1aSopenharmony_ci /* this can happen when seeking around */ 363cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, 364cabdff1aSopenharmony_ci "Palette ID %d is missing.\n", palette_id); 365cabdff1aSopenharmony_ci goto done; 366cabdff1aSopenharmony_ci } 367cabdff1aSopenharmony_ci if (c->sprites[palette_id].size != 256 * 2 * 3) { 368cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, 369cabdff1aSopenharmony_ci "Palette ID %d is wrong size (%d).\n", 370cabdff1aSopenharmony_ci palette_id, c->sprites[palette_id].size); 371cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 372cabdff1aSopenharmony_ci } 373cabdff1aSopenharmony_ci c->last_pal_id = palette_id; 374cabdff1aSopenharmony_ci if (!palette_set) { 375cabdff1aSopenharmony_ci uint8_t *palette_data = c->sprites[palette_id].data; 376cabdff1aSopenharmony_ci for (int i = 0; i < 256; i++) { 377cabdff1aSopenharmony_ci /* QuickTime-ish palette: 16-bit RGB components */ 378cabdff1aSopenharmony_ci unsigned r, g, b; 379cabdff1aSopenharmony_ci r = *palette_data; 380cabdff1aSopenharmony_ci g = *(palette_data + 2); 381cabdff1aSopenharmony_ci b = *(palette_data + 4); 382cabdff1aSopenharmony_ci c->pal[i] = (0xFFU << 24) | (r << 16) | (g << 8) | b; 383cabdff1aSopenharmony_ci palette_data += 6; 384cabdff1aSopenharmony_ci } 385cabdff1aSopenharmony_ci /* first palette entry indicates transparency */ 386cabdff1aSopenharmony_ci c->pal[0] = 0; 387cabdff1aSopenharmony_ci c->frame->palette_has_changed = 1; 388cabdff1aSopenharmony_ci } 389cabdff1aSopenharmony_ci } 390cabdff1aSopenharmony_ci 391cabdff1aSopenharmony_cidone: 392cabdff1aSopenharmony_ci /* discard outdated blocks */ 393cabdff1aSopenharmony_ci for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) { 394cabdff1aSopenharmony_ci if (c->sprites[i].end_frame > frame_id) 395cabdff1aSopenharmony_ci continue; 396cabdff1aSopenharmony_ci c->sprites[i].active = 0; 397cabdff1aSopenharmony_ci } 398cabdff1aSopenharmony_ci 399cabdff1aSopenharmony_ci memcpy(c->frame->data[1], c->pal, AVPALETTE_SIZE); 400cabdff1aSopenharmony_ci 401cabdff1aSopenharmony_ci if ((ret = av_frame_ref(rframe, c->frame)) < 0) 402cabdff1aSopenharmony_ci return ret; 403cabdff1aSopenharmony_ci 404cabdff1aSopenharmony_ci *got_frame = 1; 405cabdff1aSopenharmony_ci 406cabdff1aSopenharmony_ci /* always report that the buffer was completely consumed */ 407cabdff1aSopenharmony_ci return buf_size; 408cabdff1aSopenharmony_ci} 409cabdff1aSopenharmony_ci 410cabdff1aSopenharmony_cistatic av_cold int cdtoons_decode_init(AVCodecContext *avctx) 411cabdff1aSopenharmony_ci{ 412cabdff1aSopenharmony_ci CDToonsContext *c = avctx->priv_data; 413cabdff1aSopenharmony_ci 414cabdff1aSopenharmony_ci avctx->pix_fmt = AV_PIX_FMT_PAL8; 415cabdff1aSopenharmony_ci c->last_pal_id = 0; 416cabdff1aSopenharmony_ci c->frame = av_frame_alloc(); 417cabdff1aSopenharmony_ci if (!c->frame) 418cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 419cabdff1aSopenharmony_ci 420cabdff1aSopenharmony_ci return 0; 421cabdff1aSopenharmony_ci} 422cabdff1aSopenharmony_ci 423cabdff1aSopenharmony_cistatic void cdtoons_flush(AVCodecContext *avctx) 424cabdff1aSopenharmony_ci{ 425cabdff1aSopenharmony_ci CDToonsContext *c = avctx->priv_data; 426cabdff1aSopenharmony_ci 427cabdff1aSopenharmony_ci c->last_pal_id = 0; 428cabdff1aSopenharmony_ci for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) 429cabdff1aSopenharmony_ci c->sprites[i].active = 0; 430cabdff1aSopenharmony_ci} 431cabdff1aSopenharmony_ci 432cabdff1aSopenharmony_cistatic av_cold int cdtoons_decode_end(AVCodecContext *avctx) 433cabdff1aSopenharmony_ci{ 434cabdff1aSopenharmony_ci CDToonsContext *c = avctx->priv_data; 435cabdff1aSopenharmony_ci 436cabdff1aSopenharmony_ci for (int i = 0; i < CDTOONS_MAX_SPRITES; i++) { 437cabdff1aSopenharmony_ci av_freep(&c->sprites[i].data); 438cabdff1aSopenharmony_ci c->sprites[i].active = 0; 439cabdff1aSopenharmony_ci } 440cabdff1aSopenharmony_ci 441cabdff1aSopenharmony_ci av_frame_free(&c->frame); 442cabdff1aSopenharmony_ci 443cabdff1aSopenharmony_ci return 0; 444cabdff1aSopenharmony_ci} 445cabdff1aSopenharmony_ci 446cabdff1aSopenharmony_ciconst FFCodec ff_cdtoons_decoder = { 447cabdff1aSopenharmony_ci .p.name = "cdtoons", 448cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("CDToons video"), 449cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 450cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_CDTOONS, 451cabdff1aSopenharmony_ci .priv_data_size = sizeof(CDToonsContext), 452cabdff1aSopenharmony_ci .init = cdtoons_decode_init, 453cabdff1aSopenharmony_ci .close = cdtoons_decode_end, 454cabdff1aSopenharmony_ci FF_CODEC_DECODE_CB(cdtoons_decode_frame), 455cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_DR1, 456cabdff1aSopenharmony_ci .flush = cdtoons_flush, 457cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 458cabdff1aSopenharmony_ci}; 459