1/* 2 * Deluxe Paint Animation demuxer 3 * Copyright (c) 2009 Peter Ross 4 * 5 * This file is part of Libav. 6 * 7 * Libav is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * Libav is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with Libav; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * Deluxe Paint Animation demuxer 25 */ 26 27#include "libavutil/intreadwrite.h" 28#include "avformat.h" 29#include "internal.h" 30 31typedef struct { 32 int base_record; 33 unsigned int nb_records; 34 int size; 35} Page; 36 37typedef struct { 38 unsigned int nb_pages; /**< total pages in file */ 39 unsigned int nb_records; /**< total records in file */ 40 int page_table_offset; 41#define MAX_PAGES 256 /**< Deluxe Paint hardcoded value */ 42 Page pt[MAX_PAGES]; /**< page table */ 43 int page; /**< current page (or AVERROR_xxx code) */ 44 int record; /**< current record (with in page) */ 45} AnmDemuxContext; 46 47#define LPF_TAG MKTAG('L','P','F',' ') 48#define ANIM_TAG MKTAG('A','N','I','M') 49 50static int probe(AVProbeData *p) 51{ 52 /* verify tags and video dimensions */ 53 if (AV_RL32(&p->buf[0]) == LPF_TAG && 54 AV_RL32(&p->buf[16]) == ANIM_TAG && 55 AV_RL16(&p->buf[20]) && AV_RL16(&p->buf[22])) 56 return AVPROBE_SCORE_MAX; 57 return 0; 58} 59 60/** 61 * @return page containing the requested record or AVERROR_XXX 62 */ 63static int find_record(const AnmDemuxContext *anm, int record) 64{ 65 int i; 66 67 if (record >= anm->nb_records) 68 return AVERROR_EOF; 69 70 for (i = 0; i < MAX_PAGES; i++) { 71 const Page *p = &anm->pt[i]; 72 if (p->nb_records > 0 && record >= p->base_record && record < p->base_record + p->nb_records) 73 return i; 74 } 75 76 return AVERROR_INVALIDDATA; 77} 78 79static int read_header(AVFormatContext *s, 80 AVFormatParameters *ap) 81{ 82 AnmDemuxContext *anm = s->priv_data; 83 AVIOContext *pb = s->pb; 84 AVStream *st; 85 int i, ret; 86 87 avio_skip(pb, 4); /* magic number */ 88 if (avio_rl16(pb) != MAX_PAGES) { 89 av_log_ask_for_sample(s, "max_pages != " AV_STRINGIFY(MAX_PAGES) "\n"); 90 return AVERROR_INVALIDDATA; 91 } 92 93 anm->nb_pages = avio_rl16(pb); 94 anm->nb_records = avio_rl32(pb); 95 avio_skip(pb, 2); /* max records per page */ 96 anm->page_table_offset = avio_rl16(pb); 97 if (avio_rl32(pb) != ANIM_TAG) 98 return AVERROR_INVALIDDATA; 99 100 /* video stream */ 101 st = avformat_new_stream(s, NULL); 102 if (!st) 103 return AVERROR(ENOMEM); 104 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 105 st->codec->codec_id = CODEC_ID_ANM; 106 st->codec->codec_tag = 0; /* no fourcc */ 107 st->codec->width = avio_rl16(pb); 108 st->codec->height = avio_rl16(pb); 109 if (avio_r8(pb) != 0) 110 goto invalid; 111 avio_skip(pb, 1); /* frame rate multiplier info */ 112 113 /* ignore last delta record (used for looping) */ 114 if (avio_r8(pb)) /* has_last_delta */ 115 anm->nb_records = FFMAX(anm->nb_records - 1, 0); 116 117 avio_skip(pb, 1); /* last_delta_valid */ 118 119 if (avio_r8(pb) != 0) 120 goto invalid; 121 122 if (avio_r8(pb) != 1) 123 goto invalid; 124 125 avio_skip(pb, 1); /* other recs per frame */ 126 127 if (avio_r8(pb) != 1) 128 goto invalid; 129 130 avio_skip(pb, 32); /* record_types */ 131 st->nb_frames = avio_rl32(pb); 132 avpriv_set_pts_info(st, 64, 1, avio_rl16(pb)); 133 avio_skip(pb, 58); 134 135 /* color cycling and palette data */ 136 st->codec->extradata_size = 16*8 + 4*256; 137 st->codec->extradata = av_mallocz(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); 138 if (!st->codec->extradata) { 139 ret = AVERROR(ENOMEM); 140 goto fail; 141 } 142 ret = avio_read(pb, st->codec->extradata, st->codec->extradata_size); 143 if (ret < 0) 144 goto fail; 145 146 /* read page table */ 147 ret = avio_seek(pb, anm->page_table_offset, SEEK_SET); 148 if (ret < 0) 149 goto fail; 150 151 for (i = 0; i < MAX_PAGES; i++) { 152 Page *p = &anm->pt[i]; 153 p->base_record = avio_rl16(pb); 154 p->nb_records = avio_rl16(pb); 155 p->size = avio_rl16(pb); 156 } 157 158 /* find page of first frame */ 159 anm->page = find_record(anm, 0); 160 if (anm->page < 0) { 161 ret = anm->page; 162 goto fail; 163 } 164 165 anm->record = -1; 166 return 0; 167 168invalid: 169 av_log_ask_for_sample(s, NULL); 170 ret = AVERROR_INVALIDDATA; 171 172fail: 173 return ret; 174} 175 176static int read_packet(AVFormatContext *s, 177 AVPacket *pkt) 178{ 179 AnmDemuxContext *anm = s->priv_data; 180 AVIOContext *pb = s->pb; 181 Page *p; 182 int tmp, record_size; 183 184 if (s->pb->eof_reached) 185 return AVERROR(EIO); 186 187 if (anm->page < 0) 188 return anm->page; 189 190repeat: 191 p = &anm->pt[anm->page]; 192 193 /* parse page header */ 194 if (anm->record < 0) { 195 avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16), SEEK_SET); 196 avio_skip(pb, 8 + 2*p->nb_records); 197 anm->record = 0; 198 } 199 200 /* if we have fetched all records in this page, then find the 201 next page and repeat */ 202 if (anm->record >= p->nb_records) { 203 anm->page = find_record(anm, p->base_record + p->nb_records); 204 if (anm->page < 0) 205 return anm->page; 206 anm->record = -1; 207 goto repeat; 208 } 209 210 /* fetch record size */ 211 tmp = avio_tell(pb); 212 avio_seek(pb, anm->page_table_offset + MAX_PAGES*6 + (anm->page<<16) + 213 8 + anm->record * 2, SEEK_SET); 214 record_size = avio_rl16(pb); 215 avio_seek(pb, tmp, SEEK_SET); 216 217 /* fetch record */ 218 pkt->size = av_get_packet(s->pb, pkt, record_size); 219 if (pkt->size < 0) 220 return pkt->size; 221 if (p->base_record + anm->record == 0) 222 pkt->flags |= AV_PKT_FLAG_KEY; 223 224 anm->record++; 225 return 0; 226} 227 228AVInputFormat ff_anm_demuxer = { 229 .name = "anm", 230 .long_name = NULL_IF_CONFIG_SMALL("Deluxe Paint Animation"), 231 .priv_data_size = sizeof(AnmDemuxContext), 232 .read_probe = probe, 233 .read_header = read_header, 234 .read_packet = read_packet, 235}; 236