| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / Scene Management sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/nodes_mpeg4.h> | |
| #include <gpac/internal/swf_dev.h> | |
| #include <gpac/avparse.h> | |
| #ifndef GPAC_DISABLE_SWF_IMPORT | |
| #include <zlib.h> | |
| enum | |
| { | |
| SWF_END = 0, | |
| SWF_SHOWFRAME = 1, | |
| SWF_DEFINESHAPE = 2, | |
| SWF_FREECHARACTER = 3, | |
| SWF_PLACEOBJECT = 4, | |
| SWF_REMOVEOBJECT = 5, | |
| SWF_DEFINEBITS = 6, | |
| SWF_DEFINEBITSJPEG = 6, | |
| SWF_DEFINEBUTTON = 7, | |
| SWF_JPEGTABLES = 8, | |
| SWF_SETBACKGROUNDCOLOR = 9, | |
| SWF_DEFINEFONT = 10, | |
| SWF_DEFINETEXT = 11, | |
| SWF_DOACTION = 12, | |
| SWF_DEFINEFONTINFO = 13, | |
| SWF_DEFINESOUND = 14, | |
| SWF_STARTSOUND = 15, | |
| SWF_DEFINEBUTTONSOUND = 17, | |
| SWF_SOUNDSTREAMHEAD = 18, | |
| SWF_SOUNDSTREAMBLOCK = 19, | |
| SWF_DEFINEBITSLOSSLESS = 20, | |
| SWF_DEFINEBITSJPEG2 = 21, | |
| SWF_DEFINESHAPE2 = 22, | |
| SWF_DEFINEBUTTONCXFORM = 23, | |
| SWF_PROTECT = 24, | |
| SWF_PLACEOBJECT2 = 26, | |
| SWF_REMOVEOBJECT2 = 28, | |
| SWF_DEFINESHAPE3 = 32, | |
| SWF_DEFINETEXT2 = 33, | |
| SWF_DEFINEBUTTON2 = 34, | |
| SWF_DEFINEBITSJPEG3 = 35, | |
| SWF_DEFINEBITSLOSSLESS2 = 36, | |
| SWF_DEFINEEDITTEXT = 37, | |
| SWF_DEFINEMOVIE = 38, | |
| SWF_DEFINESPRITE = 39, | |
| SWF_NAMECHARACTER = 40, | |
| SWF_SERIALNUMBER = 41, | |
| SWF_GENERATORTEXT = 42, | |
| SWF_FRAMELABEL = 43, | |
| SWF_SOUNDSTREAMHEAD2 = 45, | |
| SWF_DEFINEMORPHSHAPE = 46, | |
| SWF_DEFINEFONT2 = 48, | |
| SWF_TEMPLATECOMMAND = 49, | |
| SWF_GENERATOR3 = 51, | |
| SWF_EXTERNALFONT = 52, | |
| SWF_EXPORTASSETS = 56, | |
| SWF_IMPORTASSETS = 57, | |
| SWF_ENABLEDEBUGGER = 58, | |
| SWF_MX0 = 59, | |
| SWF_MX1 = 60, | |
| SWF_MX2 = 61, | |
| SWF_MX3 = 62, | |
| SWF_MX4 = 63, | |
| SWF_REFLEX = 777 | |
| }; | |
| static void swf_init_decompress(SWFReader *read) | |
| { | |
| u32 size, dst_size; | |
| uLongf destLen; | |
| char *src, *dst; | |
| size = (u32) gf_bs_get_size(read->bs)-8; | |
| if (gf_bs_get_size(read->bs) - 8 >= (u64)1<<31) { | |
| gf_bs_del(read->bs); | |
| read->bs = NULL; | |
| return; | |
| } | |
| dst_size = read->length; | |
| if (dst_size < 8) { | |
| return; | |
| } | |
| //we use 500MB as max size | |
| if (dst_size > 0x1FFFFFFF) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SWF Parsing] Decompressed size too big %u, max 500M\n", dst_size)); | |
| gf_bs_del(read->bs); | |
| read->bs = NULL; | |
| return; | |
| } | |
| src = gf_malloc(sizeof(char)*size); | |
| dst = gf_malloc(sizeof(char)*dst_size); | |
| memset(dst, 0, sizeof(char)*8); | |
| gf_bs_read_data(read->bs, src, size); | |
| dst_size -= 8; | |
| destLen = (uLongf)dst_size; | |
| int uncompress_res = uncompress((Bytef *) dst+8, &destLen, (Bytef *) src, size) ; | |
| if ( uncompress_res==Z_OK ) { | |
| dst_size += 8; | |
| gf_free(src); | |
| read->mem = dst; | |
| gf_bs_del(read->bs); | |
| read->bs = gf_bs_new(read->mem, dst_size, GF_BITSTREAM_READ); | |
| gf_bs_skip_bytes(read->bs, 8); | |
| } | |
| else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SWF Parsing] Fail to uncompress data (%s)\n", zError(uncompress_res))); | |
| gf_free(src); | |
| gf_free(dst); | |
| gf_bs_del(read->bs); | |
| read->mem = NULL; | |
| read->bs = NULL; // signal error to caller since we return void | |
| } | |
| } | |
| static GF_Err swf_seek_file_to(SWFReader *read, u32 size) | |
| { | |
| return gf_bs_seek(read->bs, size); | |
| } | |
| static u32 swf_get_file_pos(SWFReader *read) | |
| { | |
| return (u32) gf_bs_get_position(read->bs); | |
| } | |
| static u32 swf_read_data(SWFReader *read, char *data, u32 data_size) | |
| { | |
| return gf_bs_read_data(read->bs, data, data_size); | |
| } | |
| static u32 swf_read_int(SWFReader *read, u32 nbBits) | |
| { | |
| return gf_bs_read_int(read->bs, nbBits); | |
| } | |
| static s32 swf_read_sint(SWFReader *read, u32 nbBits) | |
| { | |
| u32 r = 0; | |
| u32 i; | |
| if (!nbBits)return 0; | |
| r = swf_read_int(read, 1) ? 0xFFFFFFFF : 0; | |
| for (i=1; i<nbBits; i++) { | |
| r <<= 1; | |
| r |= swf_read_int(read, 1); | |
| } | |
| return (s32) r; | |
| } | |
| static u32 swf_align(SWFReader *read) | |
| { | |
| return gf_bs_align(read->bs); | |
| } | |
| static void swf_skip_data(SWFReader *read, u32 size) | |
| { | |
| while (size && !read->ioerr) { | |
| swf_read_int(read, 8); | |
| size --; | |
| } | |
| } | |
| static void swf_get_rec(SWFReader *read, SWFRec *rc) | |
| { | |
| u32 nbbits; | |
| swf_align(read); | |
| nbbits = swf_read_int(read, 5); | |
| rc->x = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); | |
| rc->w = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); | |
| rc->w -= rc->x; | |
| rc->y = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); | |
| rc->h = FLT2FIX( swf_read_sint(read, nbbits) * SWF_TWIP_SCALE ); | |
| rc->h -= rc->y; | |
| } | |
| static u32 swf_get_32(SWFReader *read) | |
| { | |
| return gf_bs_read_u32_le(read->bs); | |
| } | |
| static u16 swf_get_16(SWFReader *read) | |
| { | |
| return gf_bs_read_u16_le(read->bs); | |
| } | |
| static s16 swf_get_s16(SWFReader *read) | |
| { | |
| return (s16) gf_bs_read_u16_le(read->bs); | |
| } | |
| static u32 swf_get_color(SWFReader *read) | |
| { | |
| u32 res; | |
| res = 0xFF00; | |
| res |= swf_read_int(read, 8); | |
| res<<=8; | |
| res |= swf_read_int(read, 8); | |
| res<<=8; | |
| res |= swf_read_int(read, 8); | |
| return res; | |
| } | |
| static u32 swf_get_argb(SWFReader *read) | |
| { | |
| u32 res, al; | |
| res = swf_read_int(read, 8); | |
| res<<=8; | |
| res |= swf_read_int(read, 8); | |
| res<<=8; | |
| res |= swf_read_int(read, 8); | |
| al = swf_read_int(read, 8); | |
| return ((al<<24) | res); | |
| } | |
| static u32 swf_get_matrix(SWFReader *read, GF_Matrix2D *mat) | |
| { | |
| u32 bits_read; | |
| u32 flag, nb_bits; | |
| memset(mat, 0, sizeof(GF_Matrix2D)); | |
| mat->m[0] = mat->m[4] = FIX_ONE; | |
| bits_read = swf_align(read); | |
| flag = swf_read_int(read, 1); | |
| bits_read += 1; | |
| if (flag) { | |
| nb_bits = swf_read_int(read, 5); | |
| #ifdef GPAC_FIXED_POINT | |
| mat->m[0] = swf_read_sint(read, nb_bits); | |
| mat->m[4] = swf_read_sint(read, nb_bits); | |
| #else | |
| mat->m[0] = (Float) swf_read_sint(read, nb_bits); | |
| mat->m[0] /= 0x10000; | |
| mat->m[4] = (Float) swf_read_sint(read, nb_bits); | |
| mat->m[4] /= 0x10000; | |
| #endif | |
| bits_read += 5 + 2*nb_bits; | |
| } | |
| flag = swf_read_int(read, 1); | |
| bits_read += 1; | |
| if (flag) { | |
| nb_bits = swf_read_int(read, 5); | |
| /*WATCHOUT FOR ORDER*/ | |
| #ifdef GPAC_FIXED_POINT | |
| mat->m[3] = swf_read_sint(read, nb_bits); | |
| mat->m[1] = swf_read_sint(read, nb_bits); | |
| #else | |
| mat->m[3] = (Float) swf_read_sint(read, nb_bits); | |
| mat->m[3] /= 0x10000; | |
| mat->m[1] = (Float) swf_read_sint(read, nb_bits); | |
| mat->m[1] /= 0x10000; | |
| #endif | |
| bits_read += 5 + 2*nb_bits; | |
| } | |
| nb_bits = swf_read_int(read, 5); | |
| bits_read += 5 + 2*nb_bits; | |
| if (nb_bits) { | |
| mat->m[2] = FLT2FIX( swf_read_sint(read, nb_bits) * SWF_TWIP_SCALE ); | |
| mat->m[5] = FLT2FIX( swf_read_sint(read, nb_bits) * SWF_TWIP_SCALE ); | |
| } | |
| return bits_read; | |
| } | |
| #define SWF_COLOR_SCALE (1/256.0f) | |
| static void swf_get_colormatrix(SWFReader *read, GF_ColorMatrix *cmat) | |
| { | |
| Bool has_add, has_mul; | |
| u32 nbbits; | |
| memset(cmat, 0, sizeof(GF_ColorMatrix)); | |
| cmat->m[0] = cmat->m[6] = cmat->m[12] = cmat->m[18] = FIX_ONE; | |
| has_add = swf_read_int(read, 1); | |
| has_mul = swf_read_int(read, 1); | |
| nbbits = swf_read_int(read, 4); | |
| if (has_mul) { | |
| cmat->m[0] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[6] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[12] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[18] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| } | |
| if (has_add) { | |
| cmat->m[4] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[9] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[14] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| cmat->m[19] = FLT2FIX( swf_read_sint(read, nbbits) * SWF_COLOR_SCALE ); | |
| } | |
| cmat->identity = 0; | |
| if ((cmat->m[0] == cmat->m[6]) | |
| && (cmat->m[0] == cmat->m[12]) | |
| && (cmat->m[0] == cmat->m[18]) | |
| && (cmat->m[0] == FIX_ONE) | |
| && (cmat->m[4] == cmat->m[9]) | |
| && (cmat->m[4] == cmat->m[14]) | |
| && (cmat->m[4] == cmat->m[19]) | |
| && (cmat->m[4] == 0)) | |
| cmat->identity = 1; | |
| } | |
| static char *swf_get_string(SWFReader *read) | |
| { | |
| char szName[1024]; | |
| char *name; | |
| u32 i = 0; | |
| if (read->size>1024) { | |
| name = gf_malloc(sizeof(char)*read->size); | |
| } else { | |
| name = szName; | |
| } | |
| while (1) { | |
| if (i>=read->size) { | |
| read->ioerr = GF_NON_COMPLIANT_BITSTREAM; | |
| name[read->size ? read->size-1 : 0] = 0; | |
| break; | |
| } | |
| name[i] = swf_read_int(read, 8); | |
| if (!name[i]) break; | |
| i++; | |
| } | |
| if (read->size>1024) { | |
| return gf_realloc(name, sizeof(char)*(strlen(name)+1)); | |
| } else { | |
| return gf_strdup(szName); | |
| } | |
| } | |
| static SWFShapeRec *swf_new_shape_rec() | |
| { | |
| SWFShapeRec *style; | |
| GF_SAFEALLOC(style, SWFShapeRec); | |
| if (!style) return NULL; | |
| GF_SAFEALLOC(style->path, SWFPath); | |
| if (!style->path) { | |
| gf_free(style); | |
| return NULL; | |
| } | |
| return style; | |
| } | |
| static SWFShapeRec *swf_clone_shape_rec(SWFShapeRec *old_sr) | |
| { | |
| SWFShapeRec *new_sr = (SWFShapeRec *)gf_malloc(sizeof(SWFShapeRec)); | |
| memcpy(new_sr, old_sr, sizeof(SWFShapeRec)); | |
| new_sr->path = (SWFPath*)gf_malloc(sizeof(SWFPath)); | |
| memset(new_sr->path, 0, sizeof(SWFPath)); | |
| if (old_sr->nbGrad) { | |
| new_sr->grad_col = (u32*)gf_malloc(sizeof(u32) * old_sr->nbGrad); | |
| memcpy(new_sr->grad_col, old_sr->grad_col, sizeof(u32) * old_sr->nbGrad); | |
| new_sr->grad_ratio = (u8*)gf_malloc(sizeof(u8) * old_sr->nbGrad); | |
| memcpy(new_sr->grad_ratio, old_sr->grad_ratio, sizeof(u8) * old_sr->nbGrad); | |
| } | |
| return new_sr; | |
| } | |
| /*parse/append fill and line styles*/ | |
| static void swf_parse_styles(SWFReader *read, u32 revision, SWFShape *shape, u32 *bits_fill, u32 *bits_line) | |
| { | |
| u32 i, j, count; | |
| SWFShapeRec *style; | |
| swf_align(read); | |
| /*get fill styles*/ | |
| count = swf_read_int(read, 8); | |
| if (revision && (count== 0xFF)) count = swf_get_16(read); | |
| if (count) { | |
| for (i=0; i<count; i++) { | |
| style = swf_new_shape_rec(); | |
| style->solid_col = 0xFF00FF00; | |
| style->type = swf_read_int(read, 8); | |
| /*gradient fill*/ | |
| if (style->type & 0x10) { | |
| swf_get_matrix(read, &style->mat); | |
| swf_align(read); | |
| style->nbGrad = swf_read_int(read, 8); | |
| if (style->nbGrad) { | |
| style->grad_col = (u32 *) gf_malloc(sizeof(u32) * style->nbGrad); | |
| style->grad_ratio = (u8 *) gf_malloc(sizeof(u8) * style->nbGrad); | |
| for (j=0; j<style->nbGrad; j++) { | |
| style->grad_ratio[j] = swf_read_int(read, 8); | |
| if (revision==2) style->grad_col[j] = swf_get_argb(read); | |
| else style->grad_col[j] = swf_get_color(read); | |
| } | |
| style->solid_col = style->grad_col[0]; | |
| /*make sure we have keys between 0 and 1.0 for BIFS (0 and 255 in swf)*/ | |
| if (style->grad_ratio[0] != 0) { | |
| u32 *grad_col; | |
| u8 *grad_ratio; | |
| grad_ratio = (u8 *) gf_malloc(sizeof(u8) * (style->nbGrad+1)); | |
| grad_col = (u32 *) gf_malloc(sizeof(u32) * (style->nbGrad+1)); | |
| grad_col[0] = style->grad_col[0]; | |
| grad_ratio[0] = 0; | |
| for (j=0; j<style->nbGrad; j++) { | |
| grad_col[j+1] = style->grad_col[j]; | |
| grad_ratio[j+1] = style->grad_ratio[j]; | |
| } | |
| gf_free(style->grad_col); | |
| style->grad_col = grad_col; | |
| gf_free(style->grad_ratio); | |
| style->grad_ratio = grad_ratio; | |
| style->nbGrad++; | |
| } | |
| if (style->grad_ratio[style->nbGrad-1] != 255) { | |
| u32 *grad_col = (u32*)gf_malloc(sizeof(u32) * (style->nbGrad+1)); | |
| u8 *grad_ratio = (u8*)gf_malloc(sizeof(u8) * (style->nbGrad+1)); | |
| memcpy(grad_col, style->grad_col, sizeof(u32) * style->nbGrad); | |
| memcpy(grad_ratio, style->grad_ratio, sizeof(u8) * style->nbGrad); | |
| grad_col[style->nbGrad] = style->grad_col[style->nbGrad-1]; | |
| grad_ratio[style->nbGrad] = 255; | |
| gf_free(style->grad_col); | |
| style->grad_col = grad_col; | |
| gf_free(style->grad_ratio); | |
| style->grad_ratio = grad_ratio; | |
| style->nbGrad++; | |
| } | |
| } else { | |
| style->solid_col = 0xFF; | |
| } | |
| } | |
| /*bitmap fill*/ | |
| else if (style->type & 0x40) { | |
| style->img_id = swf_get_16(read); | |
| if (style->img_id == 65535) { | |
| style->img_id = 0; | |
| style->type = 0; | |
| style->solid_col = 0xFF00FFFF; | |
| } | |
| swf_get_matrix(read, &style->mat); | |
| } | |
| /*solid fill*/ | |
| else { | |
| if (revision==2) style->solid_col = swf_get_argb(read); | |
| else style->solid_col = swf_get_color(read); | |
| } | |
| gf_list_add(shape->fill_right, style); | |
| style = swf_clone_shape_rec(style); | |
| gf_list_add(shape->fill_left, style); | |
| } | |
| } | |
| swf_align(read); | |
| /*get line styles*/ | |
| count = swf_read_int(read, 8); | |
| if (revision && (count==0xFF)) count = swf_get_16(read); | |
| if (count) { | |
| for (i=0; i<count; i++) { | |
| style = swf_new_shape_rec(); | |
| gf_list_add(shape->lines, style); | |
| style->width = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| if (revision==2) style->solid_col = swf_get_argb(read); | |
| else style->solid_col = swf_get_color(read); | |
| } | |
| } | |
| swf_align(read); | |
| *bits_fill = swf_read_int(read, 4); | |
| *bits_line = swf_read_int(read, 4); | |
| } | |
| static void swf_path_realloc_pts(SWFPath *path, u32 nbPts) | |
| { | |
| if (path) | |
| path->pts = (SFVec2f*)gf_realloc(path->pts, sizeof(SFVec2f) * (path->nbPts + nbPts)); | |
| } | |
| static void swf_path_add_com(SWFShapeRec *sr, SFVec2f pt, SFVec2f ctr, u32 type) | |
| { | |
| /*not an error*/ | |
| if (!sr) return; | |
| sr->path->types = (u32*)gf_realloc(sr->path->types, sizeof(u32) * (sr->path->nbType+1)); | |
| sr->path->types[sr->path->nbType] = type; | |
| switch (type) { | |
| case 2: | |
| swf_path_realloc_pts(sr->path, 2); | |
| sr->path->pts[sr->path->nbPts] = ctr; | |
| sr->path->pts[sr->path->nbPts+1] = pt; | |
| sr->path->nbPts+=2; | |
| break; | |
| case 1: | |
| default: | |
| swf_path_realloc_pts(sr->path, 1); | |
| sr->path->pts[sr->path->nbPts] = pt; | |
| sr->path->nbPts++; | |
| break; | |
| } | |
| sr->path->nbType++; | |
| } | |
| static void swf_referse_path(SWFPath *path) | |
| { | |
| u32 i, j, pti, ptj; | |
| u32 *types; | |
| SFVec2f *pts; | |
| if (path->nbType<=1) return; | |
| types = (u32 *) gf_malloc(sizeof(u32) * path->nbType); | |
| pts = (SFVec2f *) gf_malloc(sizeof(SFVec2f) * path->nbPts); | |
| /*need first moveTo*/ | |
| types[0] = 0; | |
| pts[0] = path->pts[path->nbPts - 1]; | |
| pti = path->nbPts - 2; | |
| ptj = 1; | |
| j=1; | |
| for (i=0; i<path->nbType-1; i++) { | |
| types[j] = path->types[path->nbType - i - 1]; | |
| switch (types[j]) { | |
| case 2: | |
| if (ptj>path->nbPts-2) { | |
| gf_assert(0); | |
| break; | |
| } | |
| pts[ptj] = path->pts[pti]; | |
| pts[ptj+1] = path->pts[pti-1]; | |
| pti-=2; | |
| ptj+=2; | |
| break; | |
| case 1: | |
| if(ptj>path->nbPts-1) { | |
| gf_assert(0); | |
| break; | |
| } | |
| pts[ptj] = path->pts[pti]; | |
| pti--; | |
| ptj++; | |
| break; | |
| case 0: | |
| if (ptj>path->nbPts-1) { | |
| gf_assert(0); | |
| break; | |
| } | |
| pts[ptj] = path->pts[pti]; | |
| pti--; | |
| ptj++; | |
| break; | |
| } | |
| j++; | |
| } | |
| gf_free(path->pts); | |
| path->pts = pts; | |
| gf_free(path->types); | |
| path->types = types; | |
| } | |
| static void swf_free_shape_rec(SWFShapeRec *ptr) | |
| { | |
| if (ptr->grad_col) gf_free(ptr->grad_col); | |
| if (ptr->grad_ratio) gf_free(ptr->grad_ratio); | |
| if (ptr->path) { | |
| if (ptr->path->pts) gf_free(ptr->path->pts); | |
| if (ptr->path->types) gf_free(ptr->path->types); | |
| if (ptr->path->idx) gf_free(ptr->path->idx); | |
| gf_free(ptr->path); | |
| } | |
| gf_free(ptr); | |
| } | |
| static void swf_reset_rec_list(GF_List *recs) | |
| { | |
| while (gf_list_count(recs)) { | |
| SWFShapeRec *tmp = (SWFShapeRec *)gf_list_get(recs, 0); | |
| gf_list_rem(recs, 0); | |
| swf_free_shape_rec(tmp); | |
| } | |
| } | |
| static void swf_append_path(SWFPath *a, SWFPath *b) | |
| { | |
| if (b->nbType<=1) return; | |
| a->pts = (SFVec2f*)gf_realloc(a->pts, sizeof(SFVec2f) * (a->nbPts + b->nbPts)); | |
| memcpy(&a->pts[a->nbPts], b->pts, sizeof(SFVec2f)*b->nbPts); | |
| a->nbPts += b->nbPts; | |
| a->types = (u32*)gf_realloc(a->types, sizeof(u32)*(a->nbType+ b->nbType)); | |
| memcpy(&a->types[a->nbType], b->types, sizeof(u32)*b->nbType); | |
| a->nbType += b->nbType; | |
| } | |
| static void swf_path_add_type(SWFPath *path, u32 val) | |
| { | |
| path->types = (u32*)gf_realloc(path->types, sizeof(u32) * (path->nbType + 1)); | |
| path->types[path->nbType] = val; | |
| path->nbType++; | |
| } | |
| static void swf_resort_path(SWFPath *a, SWFReader *read) | |
| { | |
| u32 idx, i, j; | |
| GF_List *paths; | |
| SWFPath *sorted, *p, *np; | |
| if (!a->nbType) return; | |
| paths = gf_list_new(); | |
| GF_SAFEALLOC(sorted, SWFPath); | |
| if (!sorted) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SWF Parsing] Fail to allocate path for resorting\n")); | |
| return; | |
| } | |
| swf_path_realloc_pts(sorted, 1); | |
| sorted->pts[sorted->nbPts] = a->pts[0]; | |
| sorted->nbPts++; | |
| swf_path_add_type(sorted, 0); | |
| gf_list_add(paths, sorted); | |
| /*1- split all paths*/ | |
| idx = 1; | |
| for (i=1; i<a->nbType; i++) { | |
| switch (a->types[i]) { | |
| case 2: | |
| swf_path_realloc_pts(sorted, 2); | |
| sorted->pts[sorted->nbPts] = a->pts[idx]; | |
| sorted->pts[sorted->nbPts+1] = a->pts[idx+1]; | |
| sorted->nbPts+=2; | |
| swf_path_add_type(sorted, 2); | |
| idx += 2; | |
| break; | |
| case 1: | |
| swf_path_realloc_pts(sorted, 1); | |
| sorted->pts[sorted->nbPts] = a->pts[idx]; | |
| sorted->nbPts+=1; | |
| swf_path_add_type(sorted, 1); | |
| idx += 1; | |
| break; | |
| case 0: | |
| GF_SAFEALLOC(sorted , SWFPath); | |
| if (!sorted) return; | |
| swf_path_realloc_pts(sorted, 1); | |
| sorted->pts[sorted->nbPts] = a->pts[idx]; | |
| sorted->nbPts++; | |
| swf_path_add_type(sorted, 0); | |
| gf_list_add(paths, sorted); | |
| idx += 1; | |
| break; | |
| } | |
| } | |
| restart: | |
| for (i=0; i<gf_list_count(paths); i++) { | |
| p = (SWFPath*)gf_list_get(paths, i); | |
| for (j=i+1; j < gf_list_count(paths); j++) { | |
| np = (SWFPath*)gf_list_get(paths, j); | |
| /*check if any next subpath ends at the same place we're starting*/ | |
| if ((np->pts[np->nbPts-1].x == p->pts[0].x) && (np->pts[np->nbPts-1].y == p->pts[0].y)) { | |
| u32 k; | |
| idx = 1; | |
| for (k=1; k<p->nbType; k++) { | |
| switch (p->types[k]) { | |
| case 2: | |
| swf_path_realloc_pts(np, 2); | |
| np->pts[np->nbPts] = p->pts[idx]; | |
| np->pts[np->nbPts+1] = p->pts[idx+1]; | |
| np->nbPts+=2; | |
| swf_path_add_type(np, 2); | |
| idx += 2; | |
| break; | |
| case 1: | |
| swf_path_realloc_pts(np, 1); | |
| np->pts[np->nbPts] = p->pts[idx]; | |
| np->nbPts+=1; | |
| swf_path_add_type(np, 1); | |
| idx += 1; | |
| break; | |
| default: | |
| gf_assert(0); | |
| break; | |
| } | |
| } | |
| gf_free(p->pts); | |
| gf_free(p->types); | |
| gf_free(p); | |
| gf_list_rem(paths, i); | |
| goto restart; | |
| } | |
| /*check if any next subpath starts at the same place we're ending*/ | |
| else if ((p->pts[p->nbPts-1].x == np->pts[0].x) && (p->pts[p->nbPts-1].y == np->pts[0].y)) { | |
| u32 k; | |
| idx = 1; | |
| for (k=1; k<np->nbType; k++) { | |
| switch (np->types[k]) { | |
| case 2: | |
| swf_path_realloc_pts(p, 2); | |
| p->pts[p->nbPts] = np->pts[idx]; | |
| p->pts[p->nbPts+1] = np->pts[idx+1]; | |
| p->nbPts+=2; | |
| swf_path_add_type(p, 2); | |
| idx += 2; | |
| break; | |
| case 1: | |
| swf_path_realloc_pts(p, 1); | |
| p->pts[p->nbPts] = np->pts[idx]; | |
| p->nbPts+=1; | |
| swf_path_add_type(p, 1); | |
| idx += 1; | |
| break; | |
| default: | |
| gf_assert(0); | |
| break; | |
| } | |
| } | |
| gf_free(np->pts); | |
| gf_free(np->types); | |
| gf_free(np); | |
| gf_list_rem(paths, j); | |
| j--; | |
| } | |
| } | |
| } | |
| /*reassemble path*/ | |
| gf_free(a->pts); | |
| gf_free(a->types); | |
| memset(a, 0, sizeof(SWFPath)); | |
| while (gf_list_count(paths)) { | |
| sorted = (SWFPath*)gf_list_get(paths, 0); | |
| if (read->flat_limit==0) { | |
| swf_append_path(a, sorted); | |
| } else { | |
| Bool prev_is_line_to = 0; | |
| idx = 0; | |
| for (i=0; i<sorted->nbType; i++) { | |
| switch (sorted->types[i]) { | |
| case 2: | |
| swf_path_realloc_pts(a, 2); | |
| a->pts[a->nbPts] = sorted->pts[idx]; | |
| a->pts[a->nbPts+1] = sorted->pts[idx+1]; | |
| a->nbPts+=2; | |
| swf_path_add_type(a, 2); | |
| idx += 2; | |
| prev_is_line_to = 0; | |
| break; | |
| case 1: | |
| if (prev_is_line_to) { | |
| Fixed angle; | |
| Bool flatten = 0; | |
| SFVec2f v1, v2; | |
| v1.x = a->pts[a->nbPts-1].x - a->pts[a->nbPts-2].x; | |
| v1.y = a->pts[a->nbPts-1].y - a->pts[a->nbPts-2].y; | |
| v2.x = a->pts[a->nbPts-1].x - sorted->pts[idx].x; | |
| v2.y = a->pts[a->nbPts-1].y - sorted->pts[idx].y; | |
| angle = gf_mulfix(v1.x,v2.x) + gf_mulfix(v1.y,v2.y); | |
| /*get magnitudes*/ | |
| v1.x = gf_v2d_len(&v1); | |
| v2.x = gf_v2d_len(&v2); | |
| if (!v1.x || !v2.x) flatten = 1; | |
| else { | |
| Fixed h_pi = GF_PI / 2; | |
| angle = gf_divfix(angle, gf_mulfix(v1.x, v2.x)); | |
| if (angle + FIX_EPSILON >= FIX_ONE) angle = 0; | |
| else if (angle - FIX_EPSILON <= -FIX_ONE) angle = GF_PI; | |
| else angle = gf_acos(angle); | |
| if (angle<0) angle += h_pi; | |
| angle = ABSDIFF(angle, h_pi); | |
| if (angle < read->flat_limit) flatten = 1; | |
| } | |
| if (flatten) { | |
| a->pts[a->nbPts-1] = sorted->pts[idx]; | |
| idx++; | |
| read->flatten_points++; | |
| break; | |
| } | |
| } | |
| swf_path_realloc_pts(a, 1); | |
| a->pts[a->nbPts] = sorted->pts[idx]; | |
| a->nbPts+=1; | |
| swf_path_add_type(a, 1); | |
| idx += 1; | |
| prev_is_line_to = 1; | |
| break; | |
| case 0: | |
| swf_path_realloc_pts(a, 1); | |
| a->pts[a->nbPts] = sorted->pts[idx]; | |
| a->nbPts+=1; | |
| swf_path_add_type(a, 0); | |
| idx += 1; | |
| prev_is_line_to = 0; | |
| break; | |
| } | |
| } | |
| } | |
| gf_free(sorted->pts); | |
| gf_free(sorted->types); | |
| gf_free(sorted); | |
| gf_list_rem(paths, 0); | |
| } | |
| gf_list_del(paths); | |
| } | |
| /* | |
| Notes on SWF->BIFS conversion - some ideas taken from libswfdec | |
| A single fillStyle has 2 associated path, one used for left fill, one for right fill | |
| This is then a 4 step process: | |
| 1- blindly parse swf shape, and add point/lines to the proper left/right path | |
| 2- for each fillStyles, revert the right path so that it becomes a left path | |
| 3- concatenate left and right paths | |
| 4- resort all subelements of the final path, making sure moveTo introduced by the SWF coding (due to style changes) | |
| are removed. | |
| Ex: if path is | |
| A->C, B->A, C->B = moveTo(A), lineTo(C), moveTo(B), lineTo (A), moveTo(C), lineTo(B) | |
| we restort and remove unneeded moves to get | |
| A->C->B = moveTo(A), lineTo(C), lineTo(B), lineTo(A) | |
| */ | |
| static GF_Err swf_flush_shape(SWFReader *read, SWFShape *shape, SWFFont *font, Bool last_shape) | |
| { | |
| GF_Err e; | |
| SWFShapeRec *sf0; | |
| u32 i, count; | |
| count = gf_list_count(shape->fill_left); | |
| for (i=0; i<count; i++) { | |
| SWFShapeRec *sf1; | |
| sf0 = (SWFShapeRec*)gf_list_get(shape->fill_left, i); | |
| sf1 = (SWFShapeRec*)gf_list_get(shape->fill_right, i); | |
| /*reverse right path*/ | |
| swf_referse_path(sf1->path); | |
| /*concatenate with left path*/ | |
| swf_append_path(sf0->path, sf1->path); | |
| /*resort all path curves*/ | |
| swf_resort_path(sf0->path, read); | |
| } | |
| /*remove dummy fill_left*/ | |
| for (i=0; i<gf_list_count(shape->fill_left); i++) { | |
| sf0 = (SWFShapeRec*)gf_list_get(shape->fill_left, i); | |
| if (sf0->path->nbType<=1) { | |
| gf_list_rem(shape->fill_left, i); | |
| swf_free_shape_rec(sf0); | |
| i--; | |
| } | |
| } | |
| /*remove dummy lines*/ | |
| for (i=0; i<gf_list_count(shape->lines); i++) { | |
| SWFShapeRec *sl = (SWFShapeRec*)gf_list_get(shape->lines, i); | |
| if (sl->path->nbType<1) { | |
| gf_list_rem(shape->lines, i); | |
| swf_free_shape_rec(sl); | |
| i--; | |
| } else { | |
| swf_resort_path(sl->path, read); | |
| } | |
| } | |
| /*now translate a flash shape record into BIFS*/ | |
| e = read->define_shape(read, shape, font, last_shape); | |
| /*delete shape*/ | |
| swf_reset_rec_list(shape->fill_left); | |
| swf_reset_rec_list(shape->fill_right); | |
| swf_reset_rec_list(shape->lines); | |
| return e; | |
| } | |
| static GF_Err swf_parse_shape_def(SWFReader *read, SWFFont *font, u32 revision) | |
| { | |
| u32 nbBits, comType; | |
| s32 x, y; | |
| SFVec2f orig, ctrl, end; | |
| Bool flag; | |
| u32 fill0, fill1, strike; | |
| u32 bits_fill, bits_line; | |
| SWFShape shape; | |
| Bool is_empty; | |
| SWFShapeRec *sf0, *sf1, *sl; | |
| memset(&shape, 0, sizeof(SWFShape)); | |
| shape.fill_left = gf_list_new(); | |
| shape.fill_right = gf_list_new(); | |
| shape.lines = gf_list_new(); | |
| ctrl.x = ctrl.y = 0; | |
| swf_align(read); | |
| /*regular shape - get initial styles*/ | |
| if (!font) { | |
| shape.ID = swf_get_16(read); | |
| swf_get_rec(read, &shape.rc); | |
| swf_parse_styles(read, revision, &shape, &bits_fill, &bits_line); | |
| } | |
| /*glyph*/ | |
| else { | |
| bits_fill = swf_read_int(read, 4); | |
| bits_line = swf_read_int(read, 4); | |
| /*fonts are usually defined without styles*/ | |
| if ((read->tag == SWF_DEFINEFONT) || (read->tag==SWF_DEFINEFONT2)) { | |
| sf0 = swf_new_shape_rec(); | |
| gf_list_add(shape.fill_right, sf0); | |
| sf0 = swf_new_shape_rec(); | |
| gf_list_add(shape.fill_left, sf0); | |
| sf0->solid_col = 0xFF000000; | |
| sf0->type = 0; | |
| } | |
| } | |
| is_empty = 1; | |
| /*parse all points*/ | |
| fill0 = fill1 = strike = 0; | |
| sf0 = sf1 = sl = NULL; | |
| x = y = 0; | |
| while (1) { | |
| flag = swf_read_int(read, 1); | |
| if (!flag) { | |
| Bool new_style = swf_read_int(read, 1); | |
| Bool set_strike = swf_read_int(read, 1); | |
| Bool set_fill1 = swf_read_int(read, 1); | |
| Bool set_fill0 = swf_read_int(read, 1); | |
| Bool move_to = swf_read_int(read, 1); | |
| /*end of shape*/ | |
| if (!new_style && !set_strike && !set_fill0 && !set_fill1 && !move_to) break; | |
| is_empty = 0; | |
| if (move_to) { | |
| nbBits = swf_read_int(read, 5); | |
| x = swf_read_sint(read, nbBits); | |
| y = swf_read_sint(read, nbBits); | |
| } | |
| if (set_fill0) fill0 = swf_read_int(read, bits_fill); | |
| if (set_fill1) fill1 = swf_read_int(read, bits_fill); | |
| if (set_strike) strike = swf_read_int(read, bits_line); | |
| /*looks like newStyle does not append styles but define a new set - old styles can no | |
| longer be referenced*/ | |
| if (new_style) { | |
| /*flush current shape record into BIFS*/ | |
| swf_flush_shape(read, &shape, font, 0); | |
| swf_parse_styles(read, revision, &shape, &bits_fill, &bits_line); | |
| } | |
| if (read->flags & GF_SM_SWF_NO_LINE) strike = 0; | |
| /*moveto*/ | |
| orig.x = FLT2FIX( x * SWF_TWIP_SCALE ); | |
| orig.y = FLT2FIX( y * SWF_TWIP_SCALE ); | |
| end = orig; | |
| sf0 = fill0 ? (SWFShapeRec*)gf_list_get(shape.fill_left, fill0 - 1) : NULL; | |
| sf1 = fill1 ? (SWFShapeRec*)gf_list_get(shape.fill_right, fill1 - 1) : NULL; | |
| sl = strike ? (SWFShapeRec*)gf_list_get(shape.lines, strike - 1) : NULL; | |
| if (move_to) { | |
| swf_path_add_com(sf0, end, ctrl, 0); | |
| swf_path_add_com(sf1, end, ctrl, 0); | |
| swf_path_add_com(sl, end, ctrl, 0); | |
| } else { | |
| if (set_fill0) swf_path_add_com(sf0, end, ctrl, 0); | |
| if (set_fill1) swf_path_add_com(sf1, end, ctrl, 0); | |
| if (set_strike) swf_path_add_com(sl, end, ctrl, 0); | |
| } | |
| } else { | |
| flag = swf_read_int(read, 1); | |
| /*quadratic curve*/ | |
| if (!flag) { | |
| nbBits = 2 + swf_read_int(read, 4); | |
| x += swf_read_sint(read, nbBits); | |
| y += swf_read_sint(read, nbBits); | |
| ctrl.x = FLT2FIX( x * SWF_TWIP_SCALE ); | |
| ctrl.y = FLT2FIX( y * SWF_TWIP_SCALE ); | |
| x += swf_read_sint(read, nbBits); | |
| y += swf_read_sint(read, nbBits); | |
| end.x = FLT2FIX( x * SWF_TWIP_SCALE ); | |
| end.y = FLT2FIX( y * SWF_TWIP_SCALE ); | |
| /*curveTo*/ | |
| comType = 2; | |
| } | |
| /*straight line*/ | |
| else { | |
| nbBits = 2 + swf_read_int(read, 4); | |
| flag = swf_read_int(read, 1); | |
| if (flag) { | |
| x += swf_read_sint(read, nbBits); | |
| y += swf_read_sint(read, nbBits); | |
| } else { | |
| flag = swf_read_int(read, 1); | |
| if (flag) { | |
| y += swf_read_sint(read, nbBits); | |
| } else { | |
| x += swf_read_sint(read, nbBits); | |
| } | |
| } | |
| /*lineTo*/ | |
| comType = 1; | |
| end.x = FLT2FIX( x * SWF_TWIP_SCALE ); | |
| end.y = FLT2FIX( y * SWF_TWIP_SCALE ); | |
| } | |
| swf_path_add_com(sf0, end, ctrl, comType); | |
| swf_path_add_com(sf1, end, ctrl, comType); | |
| swf_path_add_com(sl, end, ctrl, comType); | |
| } | |
| } | |
| if (is_empty) { | |
| swf_reset_rec_list(shape.fill_left); | |
| swf_reset_rec_list(shape.fill_right); | |
| swf_reset_rec_list(shape.lines); | |
| } | |
| swf_align(read); | |
| /*now translate a flash shape record*/ | |
| swf_flush_shape(read, &shape, font, 1); | |
| /*delete shape*/ | |
| swf_reset_rec_list(shape.fill_left); | |
| swf_reset_rec_list(shape.fill_right); | |
| swf_reset_rec_list(shape.lines); | |
| gf_list_del(shape.fill_left); | |
| gf_list_del(shape.fill_right); | |
| gf_list_del(shape.lines); | |
| return GF_OK; | |
| } | |
| SWFFont *swf_find_font(SWFReader *read, u32 ID) | |
| { | |
| u32 i, count; | |
| count = gf_list_count(read->fonts); | |
| for (i=0; i<count; i++) { | |
| SWFFont *ft = (SWFFont *)gf_list_get(read->fonts, i); | |
| if (ft->fontID==ID) return ft; | |
| } | |
| return NULL; | |
| } | |
| static DispShape *swf_get_depth_entry(SWFReader *read, u32 Depth, Bool create) | |
| { | |
| u32 i; | |
| DispShape *tmp; | |
| i=0; | |
| while ((tmp = (DispShape *)gf_list_enum(read->display_list, &i))) { | |
| if (tmp->depth == Depth) return tmp; | |
| } | |
| if (!create) return NULL; | |
| GF_SAFEALLOC(tmp , DispShape); | |
| if (!tmp) return NULL; | |
| tmp->depth = Depth; | |
| tmp->char_id = 0; | |
| gf_list_add(read->display_list, tmp); | |
| memset(&tmp->mat, 0, sizeof(GF_Matrix2D)); | |
| tmp->mat.m[0] = tmp->mat.m[4] = FIX_ONE; | |
| memset(&tmp->cmat, 0, sizeof(GF_ColorMatrix)); | |
| tmp->cmat.m[0] = tmp->cmat.m[6] = tmp->cmat.m[12] = tmp->cmat.m[18] = FIX_ONE; | |
| tmp->cmat.identity = 1; | |
| return tmp; | |
| } | |
| static GF_Err swf_func_skip(SWFReader *read) | |
| { | |
| if (!read) return GF_OK; | |
| swf_skip_data(read, read->size); | |
| return read->ioerr; | |
| } | |
| static GF_Err swf_set_backcol(SWFReader *read) | |
| { | |
| u32 col = swf_get_color(read); | |
| return read->set_backcol(read, col); | |
| } | |
| static GF_Err swf_actions(SWFReader *read, u32 mask, u32 key) | |
| { | |
| u32 skip_actions = 0; | |
| u8 action_code = swf_read_int(read, 8); | |
| read->has_interact = 1; | |
| #define DO_ACT(_code) { act.type = _code; read->action(read, &act); break; } | |
| while (action_code) { | |
| u16 length; | |
| if (action_code > 0x80) length = swf_get_16(read); | |
| else length = 0; | |
| if (read->no_as || skip_actions) { | |
| swf_skip_data(read, length); | |
| if (skip_actions) skip_actions--; | |
| } else { | |
| SWFAction act; | |
| memset(&act, 0, sizeof(SWFAction)); | |
| act.button_mask = mask; | |
| act.button_key = key; | |
| switch (action_code) { | |
| /* SWF 3 Action Model */ | |
| case 0x81: /* goto frame */ | |
| act.type = GF_SWF_AS3_GOTO_FRAME; | |
| act.frame_number = swf_get_16(read); | |
| read->action(read, &act); | |
| break; | |
| case 0x83: /* get URL */ | |
| act.type = GF_SWF_AS3_GET_URL; | |
| act.url = swf_get_string(read); | |
| act.target = swf_get_string(read); | |
| read->action(read, &act); | |
| if (act.url) gf_free(act.url); | |
| if (act.target) gf_free(act.target); | |
| break; | |
| /* next frame */ | |
| case 0x04: | |
| DO_ACT(GF_SWF_AS3_NEXT_FRAME) | |
| /* previous frame */ | |
| case 0x05: | |
| DO_ACT(GF_SWF_AS3_PREV_FRAME) | |
| /* play */ | |
| case 0x06: | |
| DO_ACT(GF_SWF_AS3_PLAY) | |
| /* stop */ | |
| case 0x07: | |
| DO_ACT(GF_SWF_AS3_STOP) | |
| /* toggle quality */ | |
| case 0x08: | |
| DO_ACT(GF_SWF_AS3_TOGGLE_QUALITY) | |
| /* stop sounds*/ | |
| case 0x09: | |
| DO_ACT(GF_SWF_AS3_STOP_SOUNDS) | |
| /* wait for frame */ | |
| case 0x8A: | |
| act.type = GF_SWF_AS3_WAIT_FOR_FRAME; | |
| act.frame_number = swf_get_16(read); | |
| skip_actions = swf_read_int(read, 8); | |
| if (read->action(read, &act)) skip_actions = 0; | |
| break; | |
| /* set target */ | |
| case 0x8B: | |
| act.type = GF_SWF_AS3_SET_TARGET; | |
| act.target = swf_get_string(read); | |
| read->action(read, &act); | |
| if (act.target) gf_free(act.target); | |
| break; | |
| /* goto label */ | |
| case 0x8C: | |
| act.type = GF_SWF_AS3_GOTO_LABEL; | |
| act.target = swf_get_string(read); | |
| read->action(read, &act); | |
| if (act.target) gf_free(act.target); | |
| break; | |
| default: | |
| // swf_report(read, GF_OK, "Skipping unsupported action %x", action_code); | |
| if (length) swf_skip_data(read, length); | |
| break; | |
| } | |
| } | |
| if (gf_bs_is_overflow(read->bs)) | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| action_code = swf_read_int(read, 8); | |
| } | |
| #undef DO_ACT | |
| return GF_OK; | |
| } | |
| static GF_Err swf_def_button(SWFReader *read, u32 revision) | |
| { | |
| SWF_Button button; | |
| Bool has_actions; | |
| memset(&button, 0, sizeof(SWF_Button)); | |
| has_actions = 0; | |
| button.count = 0; | |
| button.ID = swf_get_16(read); | |
| if (revision==1) { | |
| gf_bs_read_int(read->bs, 7); | |
| gf_bs_read_int(read->bs, 1); | |
| has_actions = swf_get_16(read); | |
| } | |
| while (1) { | |
| SWF_ButtonRecord *rec = &button.buttons[button.count]; | |
| gf_bs_read_int(read->bs, 4); | |
| rec->hitTest = gf_bs_read_int(read->bs, 1); | |
| rec->down = gf_bs_read_int(read->bs, 1); | |
| rec->over = gf_bs_read_int(read->bs, 1); | |
| rec->up = gf_bs_read_int(read->bs, 1); | |
| if (!rec->hitTest && !rec->up && !rec->over && !rec->down) break; | |
| rec->character_id = swf_get_16(read); | |
| rec->depth = swf_get_16(read); | |
| swf_get_matrix(read, &rec->mx); | |
| if (revision==1) { | |
| swf_align(read); | |
| swf_get_colormatrix(read, &rec->cmx); | |
| } | |
| else gf_cmx_init(&rec->cmx); | |
| gf_bs_align(read->bs); | |
| button.count++; | |
| if (button.count>=40) return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| read->define_button(read, &button); | |
| if (revision==0) { | |
| swf_actions(read, GF_SWF_COND_OVERUP_TO_OVERDOWN, 0); | |
| } else { | |
| while (has_actions) { | |
| u32 i, mask, key; | |
| has_actions = swf_get_16(read); | |
| mask = 0; | |
| for (i=0; i<8; i++) { | |
| if (swf_read_int(read, 1)) | |
| mask |= 1<<i; | |
| } | |
| key = swf_read_int(read, 7); | |
| if (swf_read_int(read, 1)) | |
| mask |= GF_SWF_COND_OVERDOWN_TO_IDLE; | |
| swf_actions(read, mask, key); | |
| } | |
| } | |
| read->define_button(read, NULL); | |
| return GF_OK; | |
| } | |
| static Bool swf_mat_is_identity(GF_Matrix2D *mat) | |
| { | |
| if (mat->m[0] != FIX_ONE) return 0; | |
| if (mat->m[4] != FIX_ONE) return 0; | |
| if (mat->m[1]) return 0; | |
| if (mat->m[2]) return 0; | |
| if (mat->m[3]) return 0; | |
| if (mat->m[5]) return 0; | |
| return 1; | |
| } | |
| static GF_Err swf_place_obj(SWFReader *read, u32 revision) | |
| { | |
| GF_Err e; | |
| u32 shape_id; | |
| u32 ID, bitsize; | |
| u32 clip_depth; | |
| GF_Matrix2D mat; | |
| GF_ColorMatrix cmat; | |
| DispShape *ds; | |
| char *name; | |
| u32 depth, type; | |
| Bool had_depth; | |
| /*SWF flags*/ | |
| Bool has_clip_actions, has_clip, has_name, has_ratio, has_cmat, has_mat, has_id, has_move; | |
| name = NULL; | |
| clip_depth = 0; | |
| ID = 0; | |
| depth = 0; | |
| has_cmat = has_mat = has_move = 0; | |
| gf_cmx_init(&cmat); | |
| gf_mx2d_init(mat); | |
| /*place*/ | |
| type = SWF_PLACE; | |
| /*SWF 1.0*/ | |
| if (revision==0) { | |
| ID = swf_get_16(read); | |
| depth = swf_get_16(read); | |
| bitsize = 32; | |
| bitsize += swf_get_matrix(read, &mat); | |
| has_mat = 1; | |
| bitsize += swf_align(read); | |
| /*size exceeds matrix, parse col mat*/ | |
| if (bitsize < read->size*8) { | |
| swf_get_colormatrix(read, &cmat); | |
| has_cmat = 1; | |
| swf_align(read); | |
| } | |
| } | |
| /*SWF 3.0*/ | |
| else if (revision==1) { | |
| /*reserved*/ | |
| has_clip_actions = swf_read_int(read, 1); | |
| has_clip = swf_read_int(read, 1); | |
| has_name = swf_read_int(read, 1); | |
| has_ratio = swf_read_int(read, 1); | |
| has_cmat = swf_read_int(read, 1); | |
| has_mat = swf_read_int(read, 1); | |
| has_id = swf_read_int(read, 1); | |
| has_move = swf_read_int(read, 1); | |
| depth = swf_get_16(read); | |
| if (has_id) ID = swf_get_16(read); | |
| if (has_mat) { | |
| swf_get_matrix(read, &mat); | |
| swf_align(read); | |
| } | |
| if (has_cmat) { | |
| swf_align(read); | |
| swf_get_colormatrix(read, &cmat); | |
| swf_align(read); | |
| } | |
| if (has_ratio) /*ratio = */swf_get_16(read); | |
| if (has_clip) clip_depth = swf_get_16(read); | |
| if (has_name) { | |
| name = swf_get_string(read); | |
| gf_free(name); | |
| } | |
| if (has_clip_actions) { | |
| swf_get_16(read); | |
| swf_get_16(read); | |
| } | |
| /*replace*/ | |
| if (has_id && has_move) type = SWF_REPLACE; | |
| /*move*/ | |
| else if (!has_id && has_move) type = SWF_MOVE; | |
| /*place*/ | |
| else type = SWF_PLACE; | |
| } | |
| if (clip_depth) { | |
| swf_report(read, GF_NOT_SUPPORTED, "Clipping not supported - ignoring"); | |
| return GF_OK; | |
| } | |
| /*1: check depth of display list*/ | |
| had_depth = read->allocate_depth(read, depth); | |
| /*check validity*/ | |
| if ((type==SWF_MOVE) && !had_depth) swf_report(read, GF_BAD_PARAM, "Accessing empty depth level %d", depth); | |
| ds = NULL; | |
| /*usual case: (re)place depth level*/ | |
| switch (type) { | |
| case SWF_MOVE: | |
| ds = swf_get_depth_entry(read, depth, 0); | |
| shape_id = ds ? ds->char_id : 0; | |
| break; | |
| case SWF_REPLACE: | |
| case SWF_PLACE: | |
| default: | |
| shape_id = ID; | |
| break; | |
| } | |
| if (!shape_id) { | |
| swf_report(read, GF_BAD_PARAM, "%s unfound object (ID %d)", (type==SWF_MOVE) ? "Moving" : ((type==SWF_PLACE) ? "Placing" : "Replacing"), ID); | |
| return GF_OK; | |
| } | |
| /*restore prev matrix if needed*/ | |
| if (type==SWF_REPLACE) { | |
| if (!ds) ds = swf_get_depth_entry(read, depth, 0); | |
| if (ds) { | |
| if (!has_mat) { | |
| memcpy(&mat, &ds->mat, sizeof(GF_Matrix2D)); | |
| has_mat = 1; | |
| } | |
| if (!has_cmat) { | |
| memcpy(&cmat, &ds->cmat, sizeof(GF_ColorMatrix)); | |
| has_cmat = 1; | |
| } | |
| } | |
| } | |
| /*check for identity matrices*/ | |
| if (has_cmat && cmat.identity) has_cmat = 0; | |
| if (has_mat && swf_mat_is_identity(&mat)) has_mat = 0; | |
| /*store in display list*/ | |
| ds = swf_get_depth_entry(read, depth, 1); | |
| e = read->place_obj(read, depth, shape_id, ds->char_id, type, | |
| has_mat ? &mat : NULL, | |
| has_cmat ? &cmat : NULL, | |
| swf_mat_is_identity(&ds->mat) ? NULL : &ds->mat, | |
| ds->cmat.identity ? NULL : &ds->cmat); | |
| /*remember matrices*/ | |
| memcpy(&ds->mat, &mat, sizeof(GF_Matrix2D)); | |
| memcpy(&ds->cmat, &cmat, sizeof(GF_ColorMatrix)); | |
| ds->char_id = shape_id; | |
| if (e) swf_report(read, e, "Error %s object ID %d", (type==SWF_MOVE) ? "Moving" : ((type==SWF_PLACE) ? "Placing" : "Replacing"), shape_id); | |
| return GF_OK; | |
| } | |
| static GF_Err swf_remove_obj(SWFReader *read, u32 revision) | |
| { | |
| GF_Err e; | |
| DispShape *ds; | |
| u32 depth; | |
| if (revision==0) swf_get_16(read); | |
| depth = swf_get_16(read); | |
| ds = swf_get_depth_entry(read, depth, 0); | |
| /*this happens if a placeObject has failed*/ | |
| if (!ds) return GF_OK; | |
| e = read->remove_obj(read, depth, ds->char_id); | |
| ds->char_id = 0; | |
| return e; | |
| } | |
| static GF_Err swf_show_frame(SWFReader *read) | |
| { | |
| GF_Err e; | |
| e = read->show_frame(read); | |
| read->current_frame ++; | |
| return e; | |
| } | |
| static GF_Err swf_def_font(SWFReader *read, u32 revision) | |
| { | |
| u32 i, count; | |
| GF_Err e; | |
| SWFFont *ft; | |
| u32 *offset_table = NULL; | |
| u32 start; | |
| GF_SAFEALLOC(ft, SWFFont); | |
| if (!ft) return GF_OUT_OF_MEM; | |
| ft->glyphs = gf_list_new(); | |
| ft->fontID = swf_get_16(read); | |
| e = GF_OK; | |
| gf_list_add(read->fonts, ft); | |
| if (revision==0) { | |
| start = swf_get_file_pos(read); | |
| count = swf_get_16(read); | |
| ft->nbGlyphs = count / 2; | |
| offset_table = (u32*)gf_malloc(sizeof(u32) * ft->nbGlyphs); | |
| if (ft->nbGlyphs) offset_table[0] = 0; | |
| for (i=1; i<ft->nbGlyphs; i++) offset_table[i] = swf_get_16(read); | |
| for (i=0; i<ft->nbGlyphs; i++) { | |
| swf_align(read); | |
| e = swf_seek_file_to(read, start + offset_table[i]); | |
| if (e) break; | |
| swf_parse_shape_def(read, ft, 0); | |
| } | |
| gf_free(offset_table); | |
| if (e) return e; | |
| } else if (revision==1) { | |
| SWFRec rc; | |
| Bool wide_offset, wide_codes; | |
| u32 code_offset, checkpos; | |
| ft->has_layout = swf_read_int(read, 1); | |
| ft->has_shiftJIS = swf_read_int(read, 1); | |
| ft->is_unicode = swf_read_int(read, 1); | |
| ft->is_ansi = swf_read_int(read, 1); | |
| wide_offset = swf_read_int(read, 1); | |
| wide_codes = swf_read_int(read, 1); | |
| ft->is_italic = swf_read_int(read, 1); | |
| ft->is_bold = swf_read_int(read, 1); | |
| swf_read_int(read, 8); | |
| count = swf_read_int(read, 8); | |
| ft->fontName = (char*)gf_malloc(sizeof(u8)*count+1); | |
| ft->fontName[count] = 0; | |
| for (i=0; i<count; i++) ft->fontName[i] = swf_read_int(read, 8); | |
| ft->nbGlyphs = swf_get_16(read); | |
| start = swf_get_file_pos(read); | |
| if (ft->nbGlyphs) { | |
| offset_table = (u32*)gf_malloc(sizeof(u32) * ft->nbGlyphs); | |
| for (i=0; i<ft->nbGlyphs; i++) { | |
| if (wide_offset) offset_table[i] = swf_get_32(read); | |
| else offset_table[i] = swf_get_16(read); | |
| } | |
| } | |
| if (wide_offset) { | |
| code_offset = swf_get_32(read); | |
| } else { | |
| code_offset = swf_get_16(read); | |
| } | |
| if (ft->nbGlyphs) { | |
| for (i=0; i<ft->nbGlyphs; i++) { | |
| swf_align(read); | |
| e = swf_seek_file_to(read, start + offset_table[i]); | |
| if (e) break; | |
| swf_parse_shape_def(read, ft, 0); | |
| } | |
| gf_free(offset_table); | |
| if (e) return e; | |
| checkpos = swf_get_file_pos(read); | |
| if (checkpos != start + code_offset) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[SWF Parsing] bad code offset in font\n")); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| ft->glyph_codes = (u16*)gf_malloc(sizeof(u16) * ft->nbGlyphs); | |
| for (i=0; i<ft->nbGlyphs; i++) { | |
| if (wide_codes) ft->glyph_codes[i] = swf_get_16(read); | |
| else ft->glyph_codes[i] = swf_read_int(read, 8); | |
| } | |
| } | |
| if (ft->has_layout) { | |
| ft->ascent = swf_get_s16(read); | |
| ft->descent = swf_get_s16(read); | |
| ft->leading = swf_get_s16(read); | |
| if (ft->nbGlyphs) { | |
| ft->glyph_adv = (s16*)gf_malloc(sizeof(s16) * ft->nbGlyphs); | |
| for (i=0; i<ft->nbGlyphs; i++) ft->glyph_adv[i] = swf_get_s16(read); | |
| for (i=0; i<ft->nbGlyphs; i++) swf_get_rec(read, &rc); | |
| } | |
| /*kerning info*/ | |
| count = swf_get_16(read); | |
| for (i=0; i<count; i++) { | |
| if (wide_codes) { | |
| swf_get_16(read); | |
| swf_get_16(read); | |
| } else { | |
| swf_read_int(read, 8); | |
| swf_read_int(read, 8); | |
| } | |
| swf_get_s16(read); | |
| } | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err swf_def_font_info(SWFReader *read) | |
| { | |
| SWFFont *ft; | |
| Bool wide_chars; | |
| u32 i, count; | |
| i = swf_get_16(read); | |
| ft = swf_find_font(read, i); | |
| if (!ft) { | |
| swf_report(read, GF_BAD_PARAM, "Cannot locate font ID %d", i); | |
| return GF_BAD_PARAM; | |
| } | |
| /*overwrite font info*/ | |
| if (ft->fontName) gf_free(ft->fontName); | |
| count = swf_read_int(read, 8); | |
| ft->fontName = (char*)gf_malloc(sizeof(char) * (count+1)); | |
| ft->fontName[count] = 0; | |
| for (i=0; i<count; i++) ft->fontName[i] = swf_read_int(read, 8); | |
| swf_read_int(read, 2); | |
| ft->is_unicode = swf_read_int(read, 1); | |
| ft->has_shiftJIS = swf_read_int(read, 1); | |
| ft->is_ansi = swf_read_int(read, 1); | |
| ft->is_italic = swf_read_int(read, 1); | |
| ft->is_bold = swf_read_int(read, 1); | |
| /*TODO - this should be remapped to a font data stream, we currently only assume the glyph code | |
| table is the same as the original font file...*/ | |
| wide_chars = swf_read_int(read, 1); | |
| if (ft->glyph_codes) gf_free(ft->glyph_codes); | |
| ft->glyph_codes = (u16*)gf_malloc(sizeof(u16) * ft->nbGlyphs); | |
| for (i=0; i<ft->nbGlyphs; i++) { | |
| if (wide_chars) ft->glyph_codes[i] = swf_get_16(read); | |
| else ft->glyph_codes[i] = swf_read_int(read, 8); | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err swf_def_text(SWFReader *read, u32 revision) | |
| { | |
| SWFRec rc; | |
| SWFText txt; | |
| Bool flag; | |
| u32 nbits_adv, nbits_glyph, i, col, fontID, count, font_height; | |
| Fixed offX, offY; | |
| GF_Err e; | |
| txt.ID = swf_get_16(read); | |
| swf_get_rec(read, &rc); | |
| swf_get_matrix(read, &txt.mat); | |
| txt.text = gf_list_new(); | |
| swf_align(read); | |
| nbits_glyph = swf_read_int(read, 8); | |
| nbits_adv = swf_read_int(read, 8); | |
| fontID = 0; | |
| offX = offY = 0; | |
| font_height = 0; | |
| col = 0xFF000000; | |
| e = GF_OK; | |
| while (1) { | |
| flag = swf_read_int(read, 1); | |
| /*regular glyph record*/ | |
| if (!flag) { | |
| SWFGlyphRec *gr; | |
| count = swf_read_int(read, 7); | |
| if (!count) break; | |
| if (!fontID) { | |
| e = GF_BAD_PARAM; | |
| swf_report(read, GF_BAD_PARAM, "Defining text %d without assigning font", fontID); | |
| break; | |
| } | |
| GF_SAFEALLOC(gr, SWFGlyphRec); | |
| if (!gr) return GF_OUT_OF_MEM; | |
| gf_list_add(txt.text, gr); | |
| gr->fontID = fontID; | |
| gr->fontSize = font_height; | |
| gr->col = col; | |
| gr->orig_x = offX; | |
| gr->orig_y = offY; | |
| gr->nbGlyphs = count; | |
| gr->indexes = (u32*)gf_malloc(sizeof(u32) * gr->nbGlyphs); | |
| gr->dx = (Fixed*)gf_malloc(sizeof(Fixed) * gr->nbGlyphs); | |
| for (i=0; i<gr->nbGlyphs; i++) { | |
| gr->indexes[i] = swf_read_int(read, nbits_glyph); | |
| gr->dx[i] = FLT2FIX( swf_read_int(read, nbits_adv) * SWF_TWIP_SCALE ); | |
| } | |
| swf_align(read); | |
| } | |
| /*text state change*/ | |
| else { | |
| Bool has_font, has_col, has_y_off, has_x_off; | |
| /*reserved*/ | |
| swf_read_int(read, 3); | |
| has_font = swf_read_int(read, 1); | |
| has_col = swf_read_int(read, 1); | |
| has_y_off = swf_read_int(read, 1); | |
| has_x_off = swf_read_int(read, 1); | |
| /*end of rec*/ | |
| if (!has_font && !has_col && !has_y_off && !has_x_off) break; | |
| if (has_font) fontID = swf_get_16(read); | |
| if (has_col) { | |
| if (revision==0) col = swf_get_color(read); | |
| else col = swf_get_argb(read); | |
| } | |
| /*openSWF spec seems to have wrong order here*/ | |
| if (has_x_off) offX = FLT2FIX( swf_get_s16(read) * SWF_TWIP_SCALE ); | |
| if (has_y_off) offY = FLT2FIX( swf_get_s16(read) * SWF_TWIP_SCALE ); | |
| if (has_font) font_height = swf_get_16(read); | |
| } | |
| } | |
| if (e) goto exit; | |
| if (! (read->flags & GF_SM_SWF_NO_TEXT) ) { | |
| e = read->define_text(read, &txt); | |
| } | |
| exit: | |
| while (gf_list_count(txt.text)) { | |
| SWFGlyphRec *gr = (SWFGlyphRec *)gf_list_get(txt.text, 0); | |
| gf_list_rem(txt.text, 0); | |
| if (gr->indexes) gf_free(gr->indexes); | |
| if (gr->dx) gf_free(gr->dx); | |
| gf_free(gr); | |
| } | |
| gf_list_del(txt.text); | |
| return e; | |
| } | |
| static GF_Err swf_def_edit_text(SWFReader *read) | |
| { | |
| GF_Err e; | |
| SWFEditText txt; | |
| char *var_name; | |
| Bool has_text, has_text_color, has_max_length, has_font; | |
| memset(&txt, 0, sizeof(SWFEditText)); | |
| txt.color = 0xFF000000; | |
| txt.ID = swf_get_16(read); | |
| swf_get_rec(read, &txt.bounds); | |
| swf_align(read); | |
| has_text = swf_read_int(read, 1); | |
| txt.word_wrap = swf_read_int(read, 1); | |
| txt.multiline = swf_read_int(read, 1); | |
| txt.password = swf_read_int(read, 1); | |
| txt.read_only = swf_read_int(read, 1); | |
| has_text_color = swf_read_int(read, 1); | |
| has_max_length = swf_read_int(read, 1); | |
| has_font = swf_read_int(read, 1); | |
| /*reserved*/swf_read_int(read, 1); | |
| txt.auto_size = swf_read_int(read, 1); | |
| txt.has_layout = swf_read_int(read, 1); | |
| txt.no_select = swf_read_int(read, 1); | |
| txt.border = swf_read_int(read, 1); | |
| /*reserved*/swf_read_int(read, 1); | |
| txt.html = swf_read_int(read, 1); | |
| txt.outlines = swf_read_int(read, 1); | |
| if (has_font) { | |
| txt.fontID = swf_get_16(read); | |
| txt.font_height = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| } | |
| if (has_text_color) txt.color = swf_get_argb(read); | |
| if (has_max_length) txt.max_length = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| if (txt.has_layout) { | |
| txt.align = swf_read_int(read, 8); | |
| txt.left = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| txt.right = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| txt.indent = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| txt.leading = FLT2FIX( swf_get_16(read) * SWF_TWIP_SCALE ); | |
| } | |
| var_name = swf_get_string(read); | |
| if (has_text) txt.init_value = swf_get_string(read); | |
| e = GF_OK; | |
| if (! (read->flags & GF_SM_SWF_NO_TEXT) ) { | |
| e = read->define_edit_text(read, &txt); | |
| } | |
| gf_free(var_name); | |
| if (txt.init_value) gf_free(txt.init_value); | |
| return e; | |
| } | |
| static void swf_delete_sound_stream(SWFReader *read) | |
| { | |
| if (!read->sound_stream) return; | |
| if (read->sound_stream->output) gf_fclose(read->sound_stream->output); | |
| if (read->sound_stream->szFileName) gf_free(read->sound_stream->szFileName); | |
| gf_free(read->sound_stream); | |
| read->sound_stream = NULL; | |
| } | |
| static GF_Err swf_def_sprite(SWFReader *read) | |
| { | |
| GF_Err e; | |
| GF_List *prev_dlist; | |
| u32 frame_count; | |
| Bool prev_sprite; | |
| u32 prev_frame, prev_depth; | |
| SWFSound *snd; | |
| prev_sprite = read->current_sprite_id; | |
| read->current_sprite_id = swf_get_16(read); | |
| frame_count = swf_get_16(read); | |
| /*store frame state*/ | |
| prev_frame = read->current_frame; | |
| read->current_frame = 0; | |
| /*store soundStream state*/ | |
| snd = read->sound_stream; | |
| read->sound_stream = NULL; | |
| /*store depth state*/ | |
| prev_depth = read->max_depth; | |
| read->max_depth = 0; | |
| prev_dlist = read->display_list; | |
| read->display_list = gf_list_new(); | |
| e = read->define_sprite(read, frame_count); | |
| while (gf_list_count(read->display_list)) { | |
| DispShape *s = (DispShape *)gf_list_get(read->display_list, 0); | |
| gf_list_rem(read->display_list, 0); | |
| gf_free(s); | |
| } | |
| gf_list_del(read->display_list); | |
| read->display_list = prev_dlist; | |
| if (e) return e; | |
| /*close sprite soundStream*/ | |
| /*restore sound stream*/ | |
| read->sound_stream = snd; | |
| read->max_depth = prev_depth; | |
| read->current_frame = prev_frame; | |
| read->current_sprite_id = prev_sprite; | |
| read->tag = SWF_DEFINESPRITE; | |
| return GF_OK; | |
| } | |
| static GF_Err swf_def_sound(SWFReader *read) | |
| { | |
| SWFSound *snd; | |
| GF_SAFEALLOC(snd , SWFSound); | |
| if (!snd) return GF_OUT_OF_MEM; | |
| snd->ID = swf_get_16(read); | |
| snd->format = swf_read_int(read, 4); | |
| snd->sound_rate = swf_read_int(read, 2); | |
| snd->bits_per_sample = swf_read_int(read, 1) ? 16 : 8; | |
| snd->stereo = swf_read_int(read, 1); | |
| snd->sample_count = swf_get_32(read); | |
| switch (snd->format) { | |
| /*raw PCM*/ | |
| case 0: | |
| swf_report(read, GF_NOT_SUPPORTED, "Raw PCM Audio not supported"); | |
| gf_free(snd); | |
| break; | |
| /*ADPCM*/ | |
| case 1: | |
| swf_report(read, GF_NOT_SUPPORTED, "AD-PCM Audio not supported"); | |
| gf_free(snd); | |
| break; | |
| /*MP3*/ | |
| case 2: | |
| { | |
| char szName[1024]; | |
| u32 alloc_size, tot_size; | |
| char *frame; | |
| GF_Err e=GF_OK; | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "swf_sound_%d.mp3", snd->ID); | |
| if (read->localPath) { | |
| snd->szFileName = gf_strdup(read->localPath); | |
| gf_dynstrcat(&snd->szFileName, szName, NULL); | |
| } else { | |
| snd->szFileName = gf_strdup(szName); | |
| } | |
| snd->output = gf_fopen(snd->szFileName, "wb"); | |
| alloc_size = 4096; | |
| frame = (char*)gf_malloc(sizeof(char)*4096); | |
| /*snd->frame_delay_ms =*/ swf_get_16(read); | |
| snd->frame_delay_ms = read->current_frame*1000; | |
| snd->frame_delay_ms /= read->frame_rate; | |
| tot_size = 9; | |
| /*parse all frames*/ | |
| while (tot_size<read->size) { | |
| u32 toread = read->size - tot_size; | |
| if (toread>alloc_size) toread = alloc_size; | |
| if (swf_read_data(read, frame, toread) != toread) { | |
| e = GF_IO_ERR; | |
| } else { | |
| if (gf_fwrite(frame, sizeof(char)*toread, snd->output) != toread) | |
| e = GF_IO_ERR; | |
| } | |
| tot_size += toread; | |
| } | |
| gf_free(frame); | |
| if (e) { | |
| if (snd->szFileName) gf_free(snd->szFileName); | |
| gf_free(snd); | |
| return e; | |
| } | |
| return gf_list_add(read->sounds, snd); | |
| } | |
| case 3: | |
| default: | |
| swf_report(read, GF_NOT_SUPPORTED, "Unrecognized sound format"); | |
| gf_free(snd); | |
| break; | |
| } | |
| return GF_OK; | |
| } | |
| typedef struct | |
| { | |
| u32 sync_flags; | |
| u32 in_point, out_point; | |
| u32 nb_loops; | |
| } SoundInfo; | |
| static SoundInfo swf_skip_soundinfo(SWFReader *read) | |
| { | |
| SoundInfo si; | |
| u32 sync_flags = swf_read_int(read, 4); | |
| Bool has_env = swf_read_int(read, 1); | |
| Bool has_loops = swf_read_int(read, 1); | |
| Bool has_out_pt = swf_read_int(read, 1); | |
| Bool has_in_pt = swf_read_int(read, 1); | |
| memset(&si, 0, sizeof(SoundInfo)); | |
| si.sync_flags = sync_flags; | |
| if (has_in_pt) si.in_point = swf_get_32(read); | |
| if (has_out_pt) si.out_point = swf_get_32(read); | |
| if (has_loops) si.nb_loops = swf_get_16(read); | |
| /*we ignore the envelope*/ | |
| if (has_env) { | |
| u32 i; | |
| u32 nb_ctrl = swf_read_int(read, 8); | |
| for (i=0; i<nb_ctrl; i++) { | |
| swf_read_int(read, 32); /*mark44*/ | |
| swf_read_int(read, 16); /*l0*/ | |
| swf_read_int(read, 16); /*l1*/ | |
| } | |
| } | |
| return si; | |
| } | |
| static SWFSound *sndswf_get_sound(SWFReader *read, u32 ID) | |
| { | |
| u32 i; | |
| SWFSound *snd; | |
| i=0; | |
| while ((snd = (SWFSound *)gf_list_enum(read->sounds, &i))) { | |
| if (snd->ID==ID) return snd; | |
| } | |
| return NULL; | |
| } | |
| static GF_Err swf_start_sound(SWFReader *read) | |
| { | |
| SWFSound *snd; | |
| u32 ID = swf_get_16(read); | |
| SoundInfo si; | |
| si = swf_skip_soundinfo(read); | |
| snd = sndswf_get_sound(read, ID); | |
| if (!snd) { | |
| swf_report(read, GF_BAD_PARAM, "Cannot find sound with ID %d", ID); | |
| return GF_OK; | |
| } | |
| if (!snd->is_setup) { | |
| GF_Err e = read->setup_sound(read, snd, 0); | |
| if (e) return e; | |
| snd->is_setup = 1; | |
| } | |
| return read->start_sound(read, snd, (si.sync_flags & 0x2) ? 1 : 0); | |
| } | |
| static GF_Err swf_soundstream_hdr(SWFReader *read) | |
| { | |
| char szName[1024]; | |
| SWFSound *snd; | |
| if (read->sound_stream) { | |
| swf_report(read, GF_BAD_PARAM, "More than one sound stream for current timeline!!"); | |
| return swf_func_skip(read); | |
| } | |
| GF_SAFEALLOC(snd, SWFSound); | |
| if (!snd) return GF_OUT_OF_MEM; | |
| /*rec_mix = */swf_read_int(read, 8); | |
| /*0: uncompressed, 1: ADPCM, 2: MP3*/ | |
| snd->format = swf_read_int(read, 4); | |
| /*0: 5.5k, 1: 11k, 2: 2: 22k, 3: 44k*/ | |
| snd->sound_rate = swf_read_int(read, 2); | |
| /*0: 8 bit, 1: 16 bit*/ | |
| snd->bits_per_sample = swf_read_int(read, 1) ? 16 : 8; | |
| /*0: mono, 8 1: stereo*/ | |
| snd->stereo = swf_read_int(read, 1); | |
| /*samplesperframe hint*/ | |
| swf_read_int(read, 16); | |
| switch (snd->format) { | |
| /*raw PCM*/ | |
| case 0: | |
| swf_report(read, GF_NOT_SUPPORTED, "Raw PCM Audio not supported"); | |
| gf_free(snd); | |
| break; | |
| /*ADPCM*/ | |
| case 1: | |
| swf_report(read, GF_NOT_SUPPORTED, "AD-PCM Audio not supported"); | |
| gf_free(snd); | |
| break; | |
| /*MP3*/ | |
| case 2: | |
| read->sound_stream = snd; | |
| if (read->localPath) { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "%s/swf_soundstream_%d.mp3", read->localPath, read->current_sprite_id); | |
| } else { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "swf_soundstream_%d.mp3", read->current_sprite_id); | |
| } | |
| read->sound_stream->szFileName = gf_strdup(szName); | |
| read->setup_sound(read, read->sound_stream, 0); | |
| break; | |
| default: | |
| swf_report(read, GF_NOT_SUPPORTED, "Unrecognized sound format"); | |
| gf_free(snd); | |
| break; | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err swf_soundstream_block(SWFReader *read) | |
| { | |
| #ifdef GPAC_DISABLE_AV_PARSERS | |
| return swf_func_skip(read); | |
| #else | |
| unsigned char bytes[4]; | |
| u32 hdr, alloc_size, size, tot_size, samplesPerFrame; | |
| char *frame; | |
| GF_Err e = GF_OK; | |
| /*note we're doing only MP3*/ | |
| if (!read->sound_stream) return swf_func_skip(read); | |
| samplesPerFrame = swf_get_16(read); | |
| /*delay = */swf_get_16(read); | |
| if (!read->sound_stream->is_setup) { | |
| /*error at setup*/ | |
| if (!read->sound_stream->output) { | |
| read->sound_stream->output = gf_fopen(read->sound_stream->szFileName, "wb"); | |
| if (!read->sound_stream->output) | |
| return swf_func_skip(read); | |
| } | |
| /*store TS of first AU*/ | |
| read->sound_stream->frame_delay_ms = read->current_frame*1000; | |
| read->sound_stream->frame_delay_ms /= read->frame_rate; | |
| read->setup_sound(read, read->sound_stream, 1); | |
| read->sound_stream->is_setup = 1; | |
| } | |
| if (!samplesPerFrame) return GF_OK; | |
| alloc_size = 1; | |
| frame = (char*)gf_malloc(sizeof(char)); | |
| tot_size = 4; | |
| /*parse all frames*/ | |
| while (1) { | |
| bytes[0] = swf_read_int(read, 8); | |
| bytes[1] = swf_read_int(read, 8); | |
| bytes[2] = swf_read_int(read, 8); | |
| bytes[3] = swf_read_int(read, 8); | |
| hdr = GF_4CC(bytes[0], bytes[1], bytes[2], bytes[3]); | |
| size = gf_mp3_frame_size(hdr); | |
| if (!size || size < 4 || tot_size >= read->size) { | |
| e = GF_ISOM_INVALID_MEDIA; | |
| break; | |
| } | |
| if (alloc_size<size-4) { | |
| frame = (char*)gf_realloc(frame, sizeof(char)*(size-4)); | |
| alloc_size = size-4; | |
| } | |
| /*watchout for truncated framesif */ | |
| if (tot_size + size >= read->size) size = read->size - tot_size; | |
| if (size > 4) { | |
| u32 size_read = swf_read_data(read, frame, size-4); | |
| if (size_read == size-4) { | |
| if (gf_fwrite(bytes, sizeof(char)*4, read->sound_stream->output)!=4) e = GF_IO_ERR; | |
| if (gf_fwrite(frame, sizeof(char)*(size-4), read->sound_stream->output) != size-4) e = GF_IO_ERR; | |
| } | |
| else | |
| e = GF_IO_ERR; | |
| } | |
| if (tot_size + size >= read->size) break; | |
| tot_size += size; | |
| } | |
| gf_free(frame); | |
| return e; | |
| #endif | |
| } | |
| static GF_Err swf_def_hdr_jpeg(SWFReader *read) | |
| { | |
| if (!read) return GF_OK; | |
| if (read->jpeg_hdr) { | |
| swf_report(read, GF_NON_COMPLIANT_BITSTREAM, "JPEG Table already defined in file"); | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| read->jpeg_hdr_size = read->size; | |
| if (read->size) { | |
| read->jpeg_hdr = gf_malloc(sizeof(char)*read->size); | |
| swf_read_data(read, (char *) read->jpeg_hdr, read->size); | |
| } | |
| return GF_OK; | |
| } | |
| static GF_Err swf_def_bits_jpeg(SWFReader *read, u32 version) | |
| { | |
| u32 ID; | |
| FILE *file = NULL; | |
| char szName[1024]; | |
| u8 *buf; | |
| u32 skip = 0; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| u32 AlphaPlaneSize = 0; | |
| #endif | |
| u32 size = read->size; | |
| GF_Err e=GF_OK; | |
| ID = swf_get_16(read); | |
| size -= 2; | |
| if (version==3) { | |
| u32 offset = swf_get_32(read); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| size -= 4; | |
| AlphaPlaneSize = size - offset; | |
| #endif | |
| size = offset; | |
| } | |
| /*dump file*/ | |
| if (read->localPath) { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "%s/swf_jpeg_%d.jpg", read->localPath, ID); | |
| } else { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "swf_jpeg_%d.jpg", ID); | |
| } | |
| if (version!=3) { | |
| file = gf_fopen(szName, "wb"); | |
| if (!file) | |
| return GF_IO_ERR; | |
| } | |
| if (version==1 && read->jpeg_hdr_size >= 2) { | |
| /*remove JPEG EOI*/ | |
| if (gf_fwrite(read->jpeg_hdr, read->jpeg_hdr_size-2, file)!=read->jpeg_hdr_size-2) | |
| return GF_IO_ERR; | |
| /*remove JPEG SOI*/ | |
| swf_get_16(read); | |
| size-=2; | |
| } | |
| buf = gf_malloc(sizeof(u8)*size); | |
| if (!buf) return GF_OUT_OF_MEM; | |
| if (swf_read_data(read, (char *) buf, size) != size) | |
| e = GF_IO_ERR; | |
| else { | |
| if (version==1) { | |
| if (gf_fwrite(buf, size, file)!=size) | |
| e = GF_IO_ERR; | |
| } else { | |
| u32 i; | |
| for (i=0; i<size; i++) { | |
| if ((i+4<size) | |
| && (buf[i]==0xFF) && (buf[i+1]==0xD9) | |
| && (buf[i+2]==0xFF) && (buf[i+3]==0xD8) | |
| ) { | |
| memmove(buf+i, buf+i+4, sizeof(char)*(size-i-4)); | |
| size -= 4; | |
| break; | |
| } | |
| } | |
| if ((size>3) && (buf[0]==0xFF) && (buf[1]==0xD8) && (buf[2]==0xFF) && (buf[3]==0xD8)) { | |
| skip = 2; | |
| } | |
| if (version==2) { | |
| if (gf_fwrite(buf+skip, size-skip, file) != size-skip) e = GF_IO_ERR; | |
| } | |
| } | |
| } | |
| if (version!=3) | |
| gf_fclose(file); | |
| if (e) { | |
| gf_free(buf); | |
| return e; | |
| } | |
| if (version==3) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| char *dst, *raw; | |
| u32 codecid; | |
| u32 osize, w, h, j, pf; | |
| uLongf destLen; | |
| GF_BitStream *bs; | |
| /*decompress jpeg*/ | |
| bs = gf_bs_new( (char *) buf+skip, size-skip, GF_BITSTREAM_READ); | |
| gf_img_parse(bs, &codecid, &w, &h, NULL, NULL); | |
| gf_bs_del(bs); | |
| osize = w*h*4; | |
| raw = gf_malloc(sizeof(char)*osize); | |
| if (!raw) { | |
| gf_free(buf); | |
| return GF_OUT_OF_MEM; | |
| } | |
| memset(raw, 0, sizeof(char)*osize); | |
| e = gf_img_jpeg_dec(buf+skip, size-skip, &w, &h, &pf, raw, &osize, 4); | |
| if (e != GF_OK) { | |
| swf_report(read, e, "Cannot decode JPEG image"); | |
| } | |
| /*read alpha map and decompress it*/ | |
| if (size<AlphaPlaneSize) buf = gf_realloc(buf, sizeof(u8)*AlphaPlaneSize); | |
| if (swf_read_data(read, (char *) buf, AlphaPlaneSize) == AlphaPlaneSize) { | |
| osize = w*h; | |
| dst = gf_malloc(sizeof(char)*osize); | |
| destLen = (uLongf)osize; | |
| uncompress((Bytef *) dst, &destLen, buf, AlphaPlaneSize); | |
| /*write alpha channel*/ | |
| for (j=0; j<(u32)destLen; j++) { | |
| raw[4*j + 3] = dst[j]; | |
| } | |
| gf_free(dst); | |
| /*write png*/ | |
| if (read->localPath) { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "%s/swf_png_%d.png", read->localPath, ID); | |
| } else { | |
| snprintf(szName, GF_ARRAY_LENGTH(szName), "swf_png_%d.png", ID); | |
| } | |
| osize = w*h*4; | |
| buf = gf_realloc(buf, sizeof(char)*osize); | |
| gf_img_png_enc(raw, w, h, w*4, GF_PIXEL_RGBA, (char *)buf, &osize); | |
| file = gf_fopen(szName, "wb"); | |
| if (gf_fwrite(buf, osize, file)!=osize) e = GF_IO_ERR; | |
| gf_fclose(file); | |
| } | |
| gf_free(raw); | |
| #endif //GPAC_DISABLE_AV_PARSERS | |
| } | |
| gf_free(buf); | |
| if (e) return e; | |
| return read->setup_image(read, ID, szName); | |
| } | |
| static const char *swf_get_tag_name(u32 tag) | |
| { | |
| switch (tag) { | |
| case SWF_END: | |
| return "End"; | |
| case SWF_SHOWFRAME: | |
| return "ShowFrame"; | |
| case SWF_DEFINESHAPE: | |
| return "DefineShape"; | |
| case SWF_FREECHARACTER: | |
| return "FreeCharacter"; | |
| case SWF_PLACEOBJECT: | |
| return "PlaceObject"; | |
| case SWF_REMOVEOBJECT: | |
| return "RemoveObject"; | |
| case SWF_DEFINEBITSJPEG: | |
| return "DefineBitsJPEG"; | |
| case SWF_DEFINEBUTTON: | |
| return "DefineButton"; | |
| case SWF_JPEGTABLES: | |
| return "JPEGTables"; | |
| case SWF_SETBACKGROUNDCOLOR: | |
| return "SetBackgroundColor"; | |
| case SWF_DEFINEFONT: | |
| return "DefineFont"; | |
| case SWF_DEFINETEXT: | |
| return "DefineText"; | |
| case SWF_DOACTION: | |
| return "DoAction"; | |
| case SWF_DEFINEFONTINFO: | |
| return "DefineFontInfo"; | |
| case SWF_DEFINESOUND: | |
| return "DefineSound"; | |
| case SWF_STARTSOUND: | |
| return "StartSound"; | |
| case SWF_DEFINEBUTTONSOUND: | |
| return "DefineButtonSound"; | |
| case SWF_SOUNDSTREAMHEAD: | |
| return "SoundStreamHead"; | |
| case SWF_SOUNDSTREAMBLOCK: | |
| return "SoundStreamBlock"; | |
| case SWF_DEFINEBITSLOSSLESS: | |
| return "DefineBitsLossless"; | |
| case SWF_DEFINEBITSJPEG2: | |
| return "DefineBitsJPEG2"; | |
| case SWF_DEFINESHAPE2: | |
| return "DefineShape2"; | |
| case SWF_DEFINEBUTTONCXFORM: | |
| return "DefineButtonCXForm"; | |
| case SWF_PROTECT: | |
| return "Protect"; | |
| case SWF_PLACEOBJECT2: | |
| return "PlaceObject2"; | |
| case SWF_REMOVEOBJECT2: | |
| return "RemoveObject2"; | |
| case SWF_DEFINESHAPE3: | |
| return "DefineShape3"; | |
| case SWF_DEFINETEXT2: | |
| return "DefineText2"; | |
| case SWF_DEFINEBUTTON2: | |
| return "DefineButton2"; | |
| case SWF_DEFINEBITSJPEG3: | |
| return "DefineBitsJPEG3"; | |
| case SWF_DEFINEBITSLOSSLESS2: | |
| return "DefineBitsLossless2"; | |
| case SWF_DEFINEEDITTEXT: | |
| return "DefineEditText"; | |
| case SWF_DEFINEMOVIE: | |
| return "DefineMovie"; | |
| case SWF_DEFINESPRITE: | |
| return "DefineSprite"; | |
| case SWF_NAMECHARACTER: | |
| return "NameCharacter"; | |
| case SWF_SERIALNUMBER: | |
| return "SerialNumber"; | |
| case SWF_GENERATORTEXT: | |
| return "GeneratorText"; | |
| case SWF_FRAMELABEL: | |
| return "FrameLabel"; | |
| case SWF_SOUNDSTREAMHEAD2: | |
| return "SoundStreamHead2"; | |
| case SWF_DEFINEMORPHSHAPE: | |
| return "DefineMorphShape"; | |
| case SWF_DEFINEFONT2: | |
| return "DefineFont2"; | |
| case SWF_TEMPLATECOMMAND: | |
| return "TemplateCommand"; | |
| case SWF_GENERATOR3: | |
| return "Generator3"; | |
| case SWF_EXTERNALFONT: | |
| return "ExternalFont"; | |
| case SWF_EXPORTASSETS: | |
| return "ExportAssets"; | |
| case SWF_IMPORTASSETS: | |
| return "ImportAssets"; | |
| case SWF_ENABLEDEBUGGER: | |
| return "EnableDebugger"; | |
| case SWF_MX0: | |
| return "MX0"; | |
| case SWF_MX1: | |
| return "MX1"; | |
| case SWF_MX2: | |
| return "MX2"; | |
| case SWF_MX3: | |
| return "MX3"; | |
| case SWF_MX4: | |
| return "MX4"; | |
| default: | |
| return "UnknownTag"; | |
| } | |
| } | |
| static GF_Err swf_unknown_tag(SWFReader *read) | |
| { | |
| if (!read) return GF_OK; | |
| swf_report(read, GF_NOT_SUPPORTED, "Tag %s (0x%2x) not implemented - skipping", swf_get_tag_name(read->tag), read->tag); | |
| return swf_func_skip(read); | |
| } | |
| static GF_Err swf_process_tag(SWFReader *read) | |
| { | |
| switch (read->tag) { | |
| case SWF_END: | |
| return GF_OK; | |
| case SWF_PROTECT: | |
| return GF_OK; | |
| case SWF_SETBACKGROUNDCOLOR: | |
| return swf_set_backcol(read); | |
| case SWF_DEFINESHAPE: | |
| return swf_parse_shape_def(read, NULL, 0); | |
| case SWF_DEFINESHAPE2: | |
| return swf_parse_shape_def(read, NULL, 1); | |
| case SWF_DEFINESHAPE3: | |
| return swf_parse_shape_def(read, NULL, 2); | |
| case SWF_PLACEOBJECT: | |
| return swf_place_obj(read, 0); | |
| case SWF_PLACEOBJECT2: | |
| return swf_place_obj(read, 1); | |
| case SWF_REMOVEOBJECT: | |
| return swf_remove_obj(read, 0); | |
| case SWF_REMOVEOBJECT2: | |
| return swf_remove_obj(read, 1); | |
| case SWF_SHOWFRAME: | |
| return swf_show_frame(read); | |
| case SWF_DEFINEFONT: | |
| return swf_def_font(read, 0); | |
| case SWF_DEFINEFONT2: | |
| return swf_def_font(read, 1); | |
| case SWF_DEFINEFONTINFO: | |
| return swf_def_font_info(read); | |
| case SWF_DEFINETEXT: | |
| return swf_def_text(read, 0); | |
| case SWF_DEFINETEXT2: | |
| return swf_def_text(read, 1); | |
| case SWF_DEFINEEDITTEXT: | |
| return swf_def_edit_text(read); | |
| case SWF_DEFINESPRITE: | |
| return swf_def_sprite(read); | |
| /*no revision needed*/ | |
| case SWF_SOUNDSTREAMHEAD: | |
| case SWF_SOUNDSTREAMHEAD2: | |
| return swf_soundstream_hdr(read); | |
| case SWF_DEFINESOUND: | |
| return swf_def_sound(read); | |
| case SWF_STARTSOUND: | |
| return swf_start_sound(read); | |
| case SWF_SOUNDSTREAMBLOCK: | |
| return swf_soundstream_block(read); | |
| case SWF_DEFINEBUTTON: | |
| return swf_def_button(read, 0); | |
| case SWF_DEFINEBUTTON2: | |
| return swf_def_button(read, 1); | |
| // case SWF_DEFINEBUTTONSOUND: | |
| case SWF_DOACTION: | |
| return swf_actions(read, 0, 0); | |
| case SWF_FRAMELABEL: | |
| { | |
| char *framelabel = swf_get_string(read); | |
| gf_free(framelabel); | |
| return GF_OK; | |
| } | |
| case SWF_JPEGTABLES: | |
| return swf_def_hdr_jpeg(read); | |
| case SWF_DEFINEBITSJPEG: | |
| return swf_def_bits_jpeg(read, 1); | |
| case SWF_DEFINEBITSJPEG2: | |
| return swf_def_bits_jpeg(read, 2); | |
| case SWF_DEFINEBITSJPEG3: | |
| return swf_def_bits_jpeg(read, 3); | |
| default: | |
| return swf_unknown_tag(read); | |
| } | |
| } | |
| GF_Err swf_parse_tag(SWFReader *read) | |
| { | |
| GF_Err e; | |
| s32 diff; | |
| u16 hdr; | |
| u32 pos; | |
| hdr = swf_get_16(read); | |
| read->tag = hdr>>6; | |
| read->size = hdr & 0x3f; | |
| if (read->size == 0x3f) { | |
| swf_align(read); | |
| read->size = swf_get_32(read); | |
| } | |
| pos = swf_get_file_pos(read); | |
| diff = pos + read->size; | |
| gf_set_progress("SWF Parsing", pos, read->length); | |
| read->ioerr = GF_OK; | |
| e = swf_process_tag(read); | |
| swf_align(read); | |
| if (!e) e = read->ioerr; | |
| diff -= swf_get_file_pos(read); | |
| if (diff<0) { | |
| swf_report(read, GF_IO_ERR, "tag %s over-read of %d bytes (size %d)", swf_get_tag_name(read->tag), -1*diff, read->size); | |
| return GF_IO_ERR; | |
| } else { | |
| gf_bs_skip_bytes(read->bs, diff); | |
| } | |
| if (!e && !read->tag) { | |
| return GF_EOS; | |
| } | |
| if (read->ioerr) { | |
| swf_report(read, GF_IO_ERR, "bitstream IO err (tag size %d)", read->size); | |
| return read->ioerr; | |
| } | |
| return e; | |
| } | |
| GF_Err swf_parse_sprite(SWFReader *read) | |
| { | |
| /*parse*/ | |
| while (1) { | |
| GF_Err e = swf_parse_tag(read); | |
| if (e<0) { | |
| swf_report(read, e, "Error parsing tag %s", swf_get_tag_name(read->tag)); | |
| return e; | |
| } | |
| /*done with sprite*/ | |
| if (read->tag==SWF_END) break; | |
| } | |
| return GF_OK; | |
| } | |
| void swf_report(SWFReader *read, GF_Err e, char *format, ...) | |
| { | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_PARSER, e ? GF_LOG_ERROR : GF_LOG_WARNING)) { | |
| char szMsg[2048]; | |
| va_list args; | |
| va_start(args, format); | |
| vsnprintf(szMsg, 2048, format, args); | |
| va_end(args); | |
| GF_LOG((u32) (e ? GF_LOG_ERROR : GF_LOG_WARNING), GF_LOG_PARSER, ("[SWF Parsing] %s (frame %d)\n", szMsg, read->current_frame+1) ); | |
| } | |
| #endif | |
| } | |
| static void swf_io_error(void *par) | |
| { | |
| SWFReader *read = (SWFReader *)par; | |
| if (read) read->ioerr = GF_IO_ERR; | |
| } | |
| GF_Err gf_sm_load_run_swf(GF_SceneLoader *load) | |
| { | |
| GF_Err e; | |
| SWFReader *read = (SWFReader *)load->loader_priv; | |
| if (!read) return GF_BAD_PARAM; | |
| /*parse all tags*/ | |
| while (1) { | |
| e = swf_parse_tag(read); | |
| if (e) break; | |
| } | |
| gf_set_progress("SWF Parsing", read->length, read->length); | |
| if (e==GF_EOS) { | |
| if (read->finalize) | |
| read->finalize(read, GF_FALSE); | |
| e = GF_OK; | |
| } | |
| if (!e) { | |
| if (read->flat_limit != 0) | |
| swf_report(read, GF_OK, "%d points removed while parsing shapes (Flattening limit %.4f)", read->flatten_points, read->flat_limit); | |
| if (read->no_as && read->has_interact) swf_report(read, GF_OK, "ActionScripts and interactions have been removed"); | |
| } else | |
| swf_report(read, e, "Error parsing tag %s", swf_get_tag_name(read->tag)); | |
| return e; | |
| } | |
| void gf_swf_reader_del(SWFReader *read) | |
| { | |
| if (!read) return; | |
| gf_bs_del(read->bs); | |
| if (read->mem) gf_free(read->mem); | |
| if (read->finalize) { | |
| read->finalize(read, GF_TRUE); | |
| } | |
| while (gf_list_count(read->display_list)) { | |
| DispShape *s = (DispShape *)gf_list_get(read->display_list, 0); | |
| gf_list_rem(read->display_list, 0); | |
| gf_free(s); | |
| } | |
| gf_list_del(read->display_list); | |
| while (gf_list_count(read->fonts)) { | |
| SWFFont *ft = (SWFFont *)gf_list_get(read->fonts, 0); | |
| gf_list_rem(read->fonts, 0); | |
| if (ft->glyph_adv) gf_free(ft->glyph_adv); | |
| if (ft->glyph_codes) gf_free(ft->glyph_codes); | |
| if (ft->fontName) gf_free(ft->fontName); | |
| gf_list_del(ft->glyphs); | |
| gf_free(ft); | |
| } | |
| gf_list_del(read->fonts); | |
| gf_list_del(read->apps); | |
| while (gf_list_count(read->sounds)) { | |
| SWFSound *snd = (SWFSound *)gf_list_get(read->sounds, 0); | |
| gf_list_rem(read->sounds, 0); | |
| if (snd->output) gf_fclose(snd->output); | |
| if (snd->szFileName) gf_free(snd->szFileName); | |
| gf_free(snd); | |
| } | |
| gf_list_del(read->sounds); | |
| swf_delete_sound_stream(read); | |
| if (read->jpeg_hdr) gf_free(read->jpeg_hdr); | |
| if (read->localPath) gf_free(read->localPath); | |
| gf_fclose(read->input); | |
| gf_free(read->inputName); | |
| gf_free(read); | |
| } | |
| void gf_sm_load_done_swf(GF_SceneLoader *load) | |
| { | |
| SWFReader *read = (SWFReader *) load->loader_priv; | |
| if (!read) return; | |
| if (read->svg_file) { | |
| gf_fclose(read->svg_file); | |
| read->svg_file = NULL; | |
| } | |
| gf_swf_reader_del(read); | |
| load->loader_priv = NULL; | |
| } | |
| SWFReader *gf_swf_reader_new(const char *localPath, const char *inputName) | |
| { | |
| SWFReader *read; | |
| FILE *input; | |
| input = gf_fopen(inputName, "rb"); | |
| if (!input) return NULL; | |
| GF_SAFEALLOC(read, SWFReader); | |
| if (!read) return NULL; | |
| read->inputName = gf_strdup(inputName); | |
| read->input = input; | |
| read->bs = gf_bs_from_file(input, GF_BITSTREAM_READ); | |
| gf_bs_set_eos_callback(read->bs, swf_io_error, read); | |
| read->display_list = gf_list_new(); | |
| read->fonts = gf_list_new(); | |
| read->apps = gf_list_new(); | |
| read->sounds = gf_list_new(); | |
| if (localPath) { | |
| read->localPath = gf_strdup(localPath); | |
| } else { | |
| char *c; | |
| read->localPath = gf_strdup(inputName); | |
| c = strrchr(read->localPath, GF_PATH_SEPARATOR); | |
| if (c) c[1] = 0; | |
| else { | |
| gf_free(read->localPath); | |
| read->localPath = NULL; | |
| } | |
| } | |
| return read; | |
| } | |
| GF_Err gf_swf_reader_set_user_mode(SWFReader *read, void *user, | |
| GF_Err (*add_sample)(void *user, const u8 *data, u32 length, u64 timestamp, Bool isRap), | |
| GF_Err (*add_header)(void *user, const u8 *data, u32 length, Bool isHeader)) | |
| { | |
| if (!read) return GF_BAD_PARAM; | |
| read->user = user; | |
| read->add_header = add_header; | |
| read->add_sample = add_sample; | |
| return GF_OK; | |
| } | |
| GF_Err gf_swf_read_header(SWFReader *read) | |
| { | |
| SWFRec rc; | |
| u8 sig[3]; | |
| /*get signature*/ | |
| sig[0] = gf_bs_read_u8(read->bs); | |
| sig[1] = gf_bs_read_u8(read->bs); | |
| sig[2] = gf_bs_read_u8(read->bs); | |
| /*"FWS" or "CWS"*/ | |
| if ( ((sig[0] != 'F') && (sig[0] != 'C')) || (sig[1] != 'W') || (sig[2] != 'S') ) { | |
| return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| /*version = */gf_bs_read_u8(read->bs); | |
| read->length = swf_get_32(read); | |
| /*if compressed decompress the whole file*/ | |
| if (sig[0] == 'C') { | |
| swf_init_decompress(read); | |
| if (!read->bs) return GF_NON_COMPLIANT_BITSTREAM; | |
| } | |
| swf_get_rec(read, &rc); | |
| read->width = rc.w; | |
| read->height = rc.h; | |
| swf_align(read); | |
| read->frame_rate = swf_get_16(read)>>8; | |
| read->frame_count = swf_get_16(read); | |
| GF_LOG(GF_LOG_INFO, GF_LOG_PARSER, ("SWF Import - Scene Size %gx%g - %d frames @ %d FPS\n", read->width, read->height, read->frame_count, read->frame_rate)); | |
| if (!read->frame_rate) read->frame_rate = 1; | |
| return GF_OK; | |
| } | |
| GF_Err gf_swf_get_duration(SWFReader *read, u32 *frame_rate, u32 *frame_count) | |
| { | |
| *frame_rate = read->frame_rate; | |
| *frame_count = read->frame_count; | |
| return GF_OK; | |
| } | |
| GF_Err gf_sm_load_init_swf(GF_SceneLoader *load) | |
| { | |
| SWFReader *read; | |
| GF_Err e; | |
| if (!load->ctx || !load->scene_graph || !load->fileName) return GF_BAD_PARAM; | |
| #ifdef GPAC_ENABLE_COVERAGE | |
| if (gf_sys_is_cov_mode()) { | |
| swf_func_skip(NULL); | |
| swf_def_hdr_jpeg(NULL); | |
| swf_get_tag_name(SWF_FREECHARACTER); | |
| swf_unknown_tag(NULL); | |
| swf_io_error(NULL); | |
| } | |
| #endif | |
| read = gf_swf_reader_new(load->localPath, load->fileName); | |
| if (!read) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("SWF Loader - Error opening file %s\n", load->fileName)); | |
| e = GF_IO_ERR; | |
| goto exit; | |
| } | |
| read->load = load; | |
| read->flags = load->swf_import_flags; | |
| read->flat_limit = FLT2FIX(load->swf_flatten_limit); | |
| load->loader_priv = read; | |
| e = gf_swf_read_header(read); | |
| if (e) goto exit; | |
| load->ctx->scene_width = FIX2INT(read->width); | |
| load->ctx->scene_height = FIX2INT(read->height); | |
| load->ctx->is_pixel_metrics = 1; | |
| if (!(load->swf_import_flags & GF_SM_SWF_SPLIT_TIMELINE) ) { | |
| swf_report(read, GF_OK, "ActionScript disabled"); | |
| read->no_as = 1; | |
| } | |
| if (!(load->swf_import_flags & GF_SM_SWF_USE_SVG)) { | |
| #ifndef GPAC_DISABLE_VRML | |
| e = swf_to_bifs_init(read); | |
| #else | |
| e = GF_NOT_SUPPORTED; | |
| #endif | |
| } else { | |
| #ifndef GPAC_DISABLE_SVG | |
| FILE *svgFile; | |
| if (load->svgOutFile) { | |
| char svgFileName[GF_MAX_PATH]; | |
| if (load->localPath) { | |
| snprintf(svgFileName, GF_ARRAY_LENGTH(svgFileName), "%s%c%s.svg", load->localPath, GF_PATH_SEPARATOR, load->svgOutFile); | |
| } else { | |
| snprintf(svgFileName, GF_ARRAY_LENGTH(svgFileName), "%s.svg", load->svgOutFile); | |
| } | |
| svgFile = gf_fopen(svgFileName, "wt"); | |
| if (!svgFile) return GF_BAD_PARAM; | |
| read->svg_file = svgFile; | |
| } else { | |
| svgFile = stdout; | |
| } | |
| gf_swf_reader_set_user_mode(read, svgFile, swf_svg_write_text_sample, swf_svg_write_text_header); | |
| e = swf_to_svg_init(read, read->flags, load->swf_flatten_limit); | |
| #else | |
| e = GF_NOT_SUPPORTED; | |
| #endif | |
| } | |
| if (e) goto exit; | |
| /*parse all tags*/ | |
| while (e == GF_OK) { | |
| e = swf_parse_tag(read); | |
| if (read->current_frame==1) break; | |
| } | |
| if (e==GF_EOS) e = GF_OK; | |
| load->done = gf_sm_load_done_swf; | |
| load->process = gf_sm_load_run_swf; | |
| exit: | |
| if (e) gf_sm_load_done_swf(load); | |
| return e; | |
| } | |
| #endif /*GPAC_DISABLE_SWF_IMPORT*/ |