X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=src%2Fswfextract.c;h=e385f1f1accd12941ffdc4ae53111c1580677e4a;hp=d0b0c6cec2cc831fcee887c833dd3c48c71836cf;hb=2391d7ae5d8a145a250a8b80ab8c93ba74eba030;hpb=9bcba8d81cea72b4922d398cf6e6957fcacb20b6 diff --git a/src/swfextract.c b/src/swfextract.c index d0b0c6c..e385f1f 100644 --- a/src/swfextract.c +++ b/src/swfextract.c @@ -4,65 +4,181 @@ Part of the swftools package. Copyright (c) 2001 Matthias Kramm + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. - This file is distributed under the GPL, see file COPYING for details */ + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "../lib/rfxswf.h" #include "../lib/args.h" -#include "reloc.h" +#include "../lib/log.h" +#ifdef HAVE_ZLIB_H +#ifdef HAVE_LIBZ +#include "zlib.h" +#define _ZLIB_INCLUDED_ +#endif +#endif char * filename = 0; char * destfilename = "output.swf"; -int verbose = 2; -int extractid = -1; +int verbose = 3; + +char* extractids = 0; +char* extractframes = 0; +char* extractjpegids = 0; +char* extractfontids = 0; +char* extractpngids = 0; +char* extractsoundids = 0; +char* extractbinaryids = 0; +char extractmp3 = 0; + char* extractname = 0; +char hollow = 0; +char originalplaceobjects = 0; +char movetozero = 0; + +int numextracts = 0; + struct options_t options[] = { {"o","output"}, + {"w","hollow"}, {"v","verbose"}, {"i","id"}, + {"j","jpegs"}, + {"p","pngs"}, + {"P","placeobject"}, + {"0","movetozero"}, + {"m","mp3"}, + {"s","sound"}, {"n","name"}, + {"f","frame"}, + {"F","font"}, {"V","version"}, + {"b","binary"}, {0,0} }; + int args_callback_option(char*name,char*val) { if(!strcmp(name, "V")) { printf("swfextract - part of %s %s\n", PACKAGE, VERSION); exit(0); } - if(!strcmp(name, "o")) { + else if(!strcmp(name, "o")) { destfilename = val; return 1; } - if(!strcmp(name, "i")) { - extractid = atoi(val); + else if(!strcmp(name, "i")) { + extractids = val; + numextracts++; if(extractname) { fprintf(stderr, "You can only supply either name or id\n"); exit(1); } return 1; } - if(!strcmp(name, "n")) { + else if(!strcmp(name, "n")) { extractname = val; - if(extractid>=0) { + numextracts++; + if(extractids) { fprintf(stderr, "You can only supply either name or id\n"); exit(1); } return 1; } - if(!strcmp(name, "v")) { + else if(!strcmp(name, "v")) { verbose ++; return 0; } + else if(!strcmp(name, "m")) { + extractmp3 = 1; + numextracts++; + return 0; + } + else if(!strcmp(name, "j")) { + if(extractjpegids) { + fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n"); + exit(1); + } + /* TODO: count number of IDs in val range */ + numextracts++; + extractjpegids = val; + return 1; + } + else if(!strcmp(name, "F")) { + if(extractfontids) { + fprintf(stderr, "Only one --font argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n"); + exit(1); + } + numextracts++; + extractfontids = val; + return 1; + } + else if(!strcmp(name, "s")) { + if(extractsoundids) { + fprintf(stderr, "Only one --sound argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n"); + exit(1); + } + numextracts++; + extractsoundids = val; + return 1; + } + else if(!strcmp(name, "b")) { + if(extractbinaryids) { + fprintf(stderr, "Only one --binary argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n"); + exit(1); + } + numextracts++; + extractbinaryids = val; + return 1; + } +#ifdef _ZLIB_INCLUDED_ + else if(!strcmp(name, "p")) { + if(extractpngids) { + fprintf(stderr, "Only one --png argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n"); + exit(1); + } + numextracts++; + extractpngids = val; + return 1; + } +#endif + else if(!strcmp(name, "f")) { + numextracts++; + extractframes = val; + return 1; + } + else if(!strcmp(name, "P")) { + originalplaceobjects = 1; + return 0; + } + else if(!strcmp(name, "0")) { + movetozero = 1; + return 0; + } + else if(!strcmp(name, "w")) { + hollow = 1; + return 0; + } else { printf("Unknown option: -%s\n", name); - return 0; + exit(1); } return 0; @@ -73,11 +189,29 @@ int args_callback_longoption(char*name,char*val) } void args_callback_usage(char*name) { - printf("Usage: %s [-v] [-i id] file.swf\n", name); + printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name); printf("\t-v , --verbose\t\t\t Be more verbose\n"); - printf("\t-i , --id ID\t\t\t ID of the object to extract\n"); - printf("\t-n , --name name\t\t\t instance name of the object to extract\n"); - printf("\t-V , --version\t\t\t Print program version and exit\n"); + printf("\t-o , --output filename\t\t set output filename\n"); + printf("\t-V , --version\t\t\t Print program version and exit\n\n"); + printf("SWF Subelement extraction:\n"); + printf("\t-n , --name name\t\t instance name of the object (SWF Define) to extract\n"); + printf("\t-i , --id ID\t\t\t ID of the object, shape or movieclip to extract\n"); + printf("\t-f , --frame frames\t\t frame numbers to extract\n"); + printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n"); + printf("\t \t\t\t (use with -f)\n"); + printf("\t-P , --placeobject\t\t\t Insert original placeobject into output file\n"); + printf("\t \t\t\t (use with -i)\n"); + printf("SWF Font/Text extraction:\n"); + printf("\t-F , --font ID\t\t\t Extract font(s)\n"); + printf("Picture extraction:\n"); + printf("\t-j , --jpeg ID\t\t\t Extract JPEG picture(s)\n"); +#ifdef _ZLIB_INCLUDED_ + printf("\t-p , --pngs ID\t\t\t Extract PNG picture(s)\n"); +#endif + printf("\n"); + printf("Sound extraction:\n"); + printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n"); + printf("\t-s , --sound ID\t\t\t Extract Sound(s)\n"); } int args_callback_command(char*name,char*val) { @@ -90,53 +224,89 @@ int args_callback_command(char*name,char*val) } U8 mainr,maing,mainb; +/* 1 = used, not expanded, + 3 = used, expanded + 5 = wanted, not expanded + 7 = wanted, expanded + */ char used[65536]; TAG*tags[65536]; int changed; +char * tagused; +int extractname_id = -1; void idcallback(void*data) { - if(!used[*(U16*)data]) { + if(!(used[GET16(data)]&1)) { changed = 1; - used[*(U16*)data] = 1; + used[GET16(data)] |= 1; } } void enumerateIDs(TAG*tag, void(*callback)(void*)) { - U8*data; +/* U8*data; int len = tag->len; if(tag->len>=64) { len += 6; data = (U8*)malloc(len); - *(U16*)data = (tag->id<<6)+63; - *(U32*)&data[2] = tag->len; + PUT16(data, (tag->id<<6)+63); + *(U8*)&data[2] = tag->len; + *(U8*)&data[3] = tag->len>>8; + *(U8*)&data[4] = tag->len>>16; + *(U8*)&data[5] = tag->len>>24; memcpy(&data[6], tag->data, tag->len); } else { len += 2; data = (U8*)malloc(len); - *(U16*)data = (tag->id<<6)+tag->len; + PUT16(data, (tag->id<<6)+tag->len); memcpy(&data[2], tag->data, tag->len); } map_ids_mem(data, len, callback); + */ + int num = swf_GetNumUsedIDs(tag); + int *ptr = malloc(sizeof(int)*num); + int t; + swf_GetUsedIDs(tag, ptr); + for(t=0;tdata[ptr[t]]); } -void extractTag(SWF*swf, int defineid, char*filename) +void moveToZero(TAG*tag) +{ + if(!swf_isPlaceTag(tag)) + return; + SWFPLACEOBJECT obj; + swf_GetPlaceObject(tag, &obj); + obj.matrix.tx = 0; + obj.matrix.ty = 0; + swf_ResetTag(tag, tag->id); + swf_SetPlaceObject(tag, &obj); +} + +void extractTag(SWF*swf, char*filename) { SWF newswf; TAG*desttag; TAG*srctag; RGBA rgb; + SRECT objectbbox; char sprite; int f; int t; + int tagnum; int copy = 0; memset(&newswf,0x00,sizeof(SWF)); // set global movie parameters - memset(used, 0,65536); newswf.fileVersion = swf->fileVersion; newswf.frameRate = swf->frameRate; newswf.movieSize = swf->movieSize; + if(movetozero && originalplaceobjects) { + newswf.movieSize.xmax = swf->movieSize.xmax - swf->movieSize.xmin; + newswf.movieSize.ymax = swf->movieSize.ymax - swf->movieSize.ymin; + newswf.movieSize.xmin = 0; + newswf.movieSize.ymin = 0; + } newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR); desttag = newswf.firstTag; @@ -145,12 +315,15 @@ void extractTag(SWF*swf, int defineid, char*filename) rgb.b = mainb; swf_SetRGB(desttag,&rgb); - used[defineid] = 1; + swf_GetRect(0, &objectbbox); + do { changed = 0; for(t=0;t<65536;t++) { - if(used[t]==1) { - if(tags[t]->id==ST_DEFINESPRITE) { + if(used[t] && !(used[t]&2)) { + if(tags[t]==0) { + msg(" ID %d is referenced, but never defined.", t); + } else if(tags[t]->id==ST_DEFINESPRITE) { TAG*tag = tags[t]; while(tag->id != ST_END) { @@ -158,15 +331,17 @@ void extractTag(SWF*swf, int defineid, char*filename) tag = tag->next; } } - else - enumerateIDs(tags[t], idcallback); - used[t] = 2; + else + enumerateIDs(tags[t], idcallback); + used[t] |= 2; } } } while(changed); srctag = swf->firstTag; + tagnum = 0; + sprite = 0; while(srctag && (srctag->id || sprite)) { int reset = 0; if(!sprite) { @@ -177,19 +352,30 @@ void extractTag(SWF*swf, int defineid, char*filename) } if(srctag->id == ST_DEFINESPRITE) sprite = 1; + if(srctag->id == ST_JPEGTABLES) + copy = 1; if(swf_isDefiningTag(srctag)) { int id = swf_GetDefineID(srctag); - if(used[id]) + if(used[id]) { + SRECT b; copy = 1; + b = swf_GetDefineBBox(srctag); + swf_ExpandRect2(&objectbbox, &b); + } } else - if (((srctag->id == ST_PLACEOBJECT || - srctag->id == ST_PLACEOBJECT2) && swf_GetPlaceID(srctag) == defineid) || - (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)])) + if ((((swf_isPlaceTag(srctag) && originalplaceobjects) + || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) || + (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) || + (tagused[tagnum])) { if(copy == 0) reset = 1; copy = 1; } + if(srctag->id == ST_REMOVEOBJECT) { + if(!used[swf_GetPlaceID(srctag)]) + copy = 0; + } if(copy) { TAG*ttag = (TAG*)malloc(sizeof(TAG)); @@ -197,29 +383,705 @@ void extractTag(SWF*swf, int defineid, char*filename) desttag->len = desttag->memsize = srctag->len; desttag->data = malloc(srctag->len); memcpy(desttag->data, srctag->data, srctag->len); + if(movetozero && swf_isPlaceTag(desttag)) { + moveToZero(desttag); + } if(reset) copy = 0; } srctag = srctag->next; + tagnum ++; + } + if(!extractframes && !hollow) { + if(!originalplaceobjects && (extractids||extractname_id>=0)) { + int number = 0; + int id = 0; + int t; + TAG* objtag = 0; + SRECT bbox; + memset(&bbox, 0, sizeof(SRECT)); + if(extractids) { + for(t=0;t<65536;t++) { + if(is_in_range(t, extractids)) { + id = t; + number++; + } + } + } + if(number>=2) { + printf("warning! You should use the -P when extracting multiple objects\n"); + } + + if(number == 1) { + /* if there is only one object, we will scale it. + So let's figure out its bounding box */ + TAG*tag = swf->firstTag; + while(tag) { + if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) { + if(swf_GetDefineID(tag) == id) + bbox = swf_GetDefineBBox(tag); + objtag = tag; + } + tag = tag->next; + } + newswf.movieSize.xmin = 0; + newswf.movieSize.ymin = 0; + newswf.movieSize.xmax = 512*20; + newswf.movieSize.ymax = 512*20; + } else { + if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0) + newswf.movieSize = objectbbox; + } + + if(extractname_id>=0) { + desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2); + swf_ObjectPlace(desttag, extractname_id, extractname_id, 0,0,extractname); + } else { + for(t=0;t<65536;t++) { + if(is_in_range(t, extractids)) { + MATRIX m; + desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2); + swf_GetMatrix(0, &m); + if(objtag) { + int width = bbox.xmax - bbox.xmin; + int height = bbox.ymax - bbox.ymin; + int max = width>height?width:height; + m.tx = -bbox.xmin; + m.ty = -bbox.ymin; + if(max) { + m.sx = (512*20*65536)/max; + m.sy = (512*20*65536)/max; + } + //newswf.movieSize = swf_TurnRect(newswf.movieSize, &m); + } + swf_ObjectPlace(desttag, t, t, &m,0,0); + } + } + } + } + desttag = swf_InsertTag(desttag,ST_SHOWFRAME); } - desttag = swf_InsertTag(desttag,ST_SHOWFRAME); desttag = swf_InsertTag(desttag,ST_END); - f = open(filename, O_TRUNC|O_WRONLY|O_CREAT, 0644); + f = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0644); if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n"); close(f); swf_FreeTags(&newswf); // cleanup } +int isOfType(int t, TAG*tag) +{ + int show = 0; + if(t == 0 && (tag->id == ST_DEFINESHAPE || + tag->id == ST_DEFINESHAPE2 || + tag->id == ST_DEFINESHAPE3)) { + show = 1; + } + if(t==1 && tag->id == ST_DEFINESPRITE) { + show = 1; + } + if(t == 2 && (tag->id == ST_DEFINEBITS || + tag->id == ST_DEFINEBITSJPEG2 || + tag->id == ST_DEFINEBITSJPEG3)) { + show = 1; + } + if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS || + tag->id == ST_DEFINEBITSLOSSLESS2)) { + show = 1; + } + if(t == 4 && (tag->id == ST_DEFINESOUND)) { + show = 1; + } + if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2 || tag->id == ST_DEFINEFONT3)) { + show = 1; + } + if (t== 6 && (tag->id == ST_DEFINEBINARY)) { + show = 1; + } + return show; +} + +void listObjects(SWF*swf) +{ + TAG*tag; + char first; + int t; + int frame = 0; + char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font", "Binary"}; + char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F","-b"}; + int mp3=0; + printf("Objects in file %s:\n",filename); + swf_FoldAll(swf); + for(t=0;tfirstTag; + first = 1; + while(tag) { + if(tag->id == ST_SOUNDSTREAMHEAD || tag->id == ST_SOUNDSTREAMHEAD2) + mp3 = 1; + if(isOfType(t,tag)) + nr++; + tag = tag->next; + } + if(!nr) + continue; + + printf(" [%s] %d %s%s: ID(s) ", options[t], nr, names[t], nr>1?"s":""); + + tag = swf->firstTag; + while(tag) { + char text[80]; + char show = isOfType(t,tag); + int id; + if(!show) { + tag = tag->next; + continue; + } + id = swf_GetDefineID(tag); + + if(id == lastid+1) { + follow=1; + } else { + if(first || !follow) { + if(!first) + printf(", "); + printf("%d", id); + } else { + if(lastprint + 1 == lastid) + printf(", %d, %d", lastid, id); + else + printf("-%d, %d", lastid, id); + } + lastprint = id; + first = 0; + follow = 0; + } + lastid = id; + tag=tag->next; + } + if(follow) { + if(lastprint + 1 == lastid) + printf(", %d", lastid); + else + printf("-%d", lastid); + } + printf("\n"); + } + + if(frame) + printf(" [-f] %d Frames: ID(s) 0-%d\n", frame, frame); + else + printf(" [-f] 1 Frame: ID(s) 0\n"); + + if(mp3) + printf(" [-m] 1 MP3 Soundstream\n"); +} + +void handlefont(SWF*swf, TAG*tag) +{ + SWFFONT* f=0; + U16 id; + char name[80]; + char*filename = name; + int t; + + id = swf_GetDefineID(tag); + sprintf(name, "font%d.swf", id); + if(numextracts==1) { + filename = destfilename; + } + + swf_FontExtract(swf, id, &f); + if(!f) { + printf("Couldn't extract font %d\n", id); + return; + } + + swf_WriteFont(f, filename); + swf_FontFree(f); +} + +static char has_jpegtables=0; +static U8*jpegtables = 0; +static int jpegtablessize = 0; + +void handlejpegtables(TAG*tag) +{ + if(tag->id == ST_JPEGTABLES) { + jpegtables = tag->data; + jpegtablessize = tag->len; + has_jpegtables = 1; + } +} + +FILE* save_fopen(char* name, char* mode) +{ + FILE*fi = fopen(name, mode); + if(!fi) { + fprintf(stderr, "Error: Couldn't open %s\n", name); + exit(1); + } + return fi; +} + +int findjpegboundary(U8*data, int len) +{ + int t; + int pos=-1; + for(t=0;tdata)); + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) + filename = "output.jpg"; + } + /* swf jpeg images have two streams, which both start with ff d8 and + end with ff d9. The following code handles sorting the middle + bytes out, so that one stream remains */ + if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) { + fi = save_fopen(filename, "wb"); + if(jpegtablessize>=2) { + fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8) + fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9) + } else { + fwrite(tag->data+2, tag->len-2, 1, fi); + } + fclose(fi); + } + else if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) { + int end = tag->len; + int pos = findjpegboundary(&tag->data[2], tag->len-2); + if(pos>=0) { + pos+=2; + fi = save_fopen(filename, "wb"); + fwrite(&tag->data[2], pos-2, 1, fi); + fwrite(&tag->data[pos+4], end-(pos+4), 1, fi); + fclose(fi); + } else { + fi = save_fopen(filename, "wb"); + fwrite(&tag->data[2], end-2, 1, fi); + fclose(fi); + } + } + else if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) { + U32 end = GET32(&tag->data[2])+6; + int pos = findjpegboundary(&tag->data[6], tag->len-6); + if(pos<0) { + fi = save_fopen(filename, "wb"); + fwrite(&tag->data[6], end-6, 1, fi); + fclose(fi); + } else { + pos+=6; + fi = save_fopen(filename, "wb"); + fwrite(&tag->data[6], pos-6, 1, fi); + fwrite(&tag->data[pos+4], end-(pos+4), 1, fi); + fclose(fi); + } + } + else { + int id = GET16(tag->data); + fprintf(stderr, "Object %d is not a JPEG picture!\n", id); + exit(1); + } +} + +#ifdef _ZLIB_INCLUDED_ +static U32 mycrc32; + +static U32*crc32_table = 0; +static void make_crc32_table(void) +{ + int t; + if(crc32_table) + return; + crc32_table = (U32*)malloc(1024); + + for (t = 0; t < 256; t++) { + U32 c = t; + int s; + for (s = 0; s < 8; s++) { + c = (0xedb88320L*(c&1)) ^ (c >> 1); + } + crc32_table[t] = c; + } +} +static inline void png_write_byte(FILE*fi, U8 byte) +{ + fwrite(&byte,1,1,fi); + mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8); +} +static void png_start_chunk(FILE*fi, char*type, int len) +{ + U8 mytype[4]={0,0,0,0}; + U32 mylen = REVERSESWAP32(len); + memcpy(mytype,type,strlen(type)); + fwrite(&mylen, 4, 1, fi); + mycrc32=0xffffffff; + png_write_byte(fi,mytype[0]); + png_write_byte(fi,mytype[1]); + png_write_byte(fi,mytype[2]); + png_write_byte(fi,mytype[3]); +} +static void png_write_bytes(FILE*fi, U8*bytes, int len) +{ + int t; + for(t=0;t>24); + png_write_byte(fi,dword>>16); + png_write_byte(fi,dword>>8); + png_write_byte(fi,dword); +} +static void png_end_chunk(FILE*fi) +{ + U32 tmp = REVERSESWAP32((mycrc32^0xffffffff)); + fwrite(&tmp,4,1,fi); +} + + +/* extract a lossless image (png) out of a tag + This routine was originally meant to be a one-pager. I just + didn't know png is _that_ much fun. :) -mk + */ +void handlelossless(TAG*tag) +{ + char name[80]; + char*filename = name; + FILE*fi; + int width, height; + int crc; + int id; + int t; + U8 bpp = 1; + U8 format; + U8 tmp; + Bytef* data=0; + U8* data2=0; + U8* data3=0; + uLongf datalen; + uLongf datalen2; + U32 datalen3; + U8 head[] = {137,80,78,71,13,10,26,10}; + int cols; + char alpha = tag->id == ST_DEFINEBITSLOSSLESS2; + RGBA* palette; + int pos; + int error; + U32 tmp32; + + make_crc32_table(); + + if(tag->id != ST_DEFINEBITSLOSSLESS && + tag->id != ST_DEFINEBITSLOSSLESS2) { + int id = GET16(tag->data); + fprintf(stderr, "Object %d is not a PNG picture!\n",id); + exit(1); + } + + id =swf_GetU16(tag); + format = swf_GetU8(tag); + if(format == 3) bpp = 8; + if(format == 4) bpp = 16; + if(format == 5) bpp = 32; + if(format!=3 && format!=5) { + if(format==4) + fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id); + else + fprintf(stderr, "Unknown image type %d in image %d\n", format, id); + return; + } + width = swf_GetU16(tag); + height = swf_GetU16(tag); + if(format == 3) cols = swf_GetU8(tag) + 1; +// this is what format means according to the flash specification. (which is +// clearly wrong) +// if(format == 4) cols = swf_GetU16(tag) + 1; +// if(format == 5) cols = swf_GetU32(tag) + 1; + else cols = 0; + + msg(" Width %d", width); + msg(" Height %d", height); + msg(" Format %d", format); + msg(" Cols %d", cols); + msg(" Bpp %d", bpp); + + datalen = (width*height*bpp/8+cols*8); + do { + if(data) + free(data); + datalen+=4096; + data = malloc(datalen); + error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos); + } while(error == Z_BUF_ERROR); + if(error != Z_OK) { + fprintf(stderr, "Zlib error %d (image %d)\n", error, id); + return; + } + msg(" Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols); + pos = 0; + datalen2 = datalen+16; + data2 = malloc(datalen2); + palette = (RGBA*)malloc(cols*sizeof(RGBA)); + + for(t=0;t Compressed data is %d bytes", datalen2); + png_start_chunk(fi, "IDAT", datalen2); + png_write_bytes(fi,data2,datalen2); + png_end_chunk(fi); + png_start_chunk(fi, "IEND", 0); + png_end_chunk(fi); + + free(data); + free(data2); + free(data3); +} +#endif + +static FILE*mp3file=0; +void handlesoundstream(TAG*tag) +{ + char*filename = "output.mp3"; + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) + filename = "output.mp3"; + } + switch(tag->id) { + case ST_SOUNDSTREAMHEAD: + if((tag->data[1]&0x30) == 0x20) { //mp3 compression + mp3file = fopen(filename, "wb"); + msg(" Writing mp3 data to %s",filename); + } + else + msg(" Soundstream is not mp3"); + break; + case ST_SOUNDSTREAMHEAD2: + if((tag->data[1]&0x30) == 0x20) {//mp3 compression + mp3file = fopen(filename, "wb"); + msg(" Writing mp3 data to %s",filename); + } + else + msg(" Soundstream is not mp3 (2)"); + break; + case ST_SOUNDSTREAMBLOCK: + if(mp3file) + fwrite(&tag->data[4],tag->len-4,1,mp3file); + break; + } +} + +void handledefinesound(TAG*tag) +{ + U8 flags; + U32 samples; + char buf[128]; + char*filename = buf; + FILE*fi; + char*extension = 0; + int format; + U16 id; + int rate,bits,stereo; + char*rates[] = {"5500","11025","22050","44100"}; + id = swf_GetU16(tag); //id + + flags = swf_GetU8(tag); + format = flags>>4; + rate = (flags>>2)&3; + bits = flags&2?16:8; + stereo = flags&1; + + samples = swf_GetU32(tag); + + extension = "raw"; + + if(format == 2) { // mp3 + swf_GetU16(tag); //numsamples_seek + extension = "mp3"; + } else if(format == 0) { // raw + printf("Sound is RAW, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono"); + // TODO: convert to WAV + extension = "raw"; + } else if(format == 1) { // adpcm + printf("Sound is ADPCM, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono"); + extension = "adpcm"; + } + sprintf(buf, "sound%d.%s", id, extension); + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) { + sprintf(buf, "output.%s", extension); + filename = buf; + } + } + fi = save_fopen(filename, "wb"); + fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi); + fclose(fi); +} + +void handlebinary(TAG*tag) { + FILE *fout = NULL; + char buf[100]; + char *filename = buf; + int len = tag->memsize; + int dx = 6; // offset to binary data + if (tag->id!=ST_DEFINEBINARY) { + fprintf(stderr, "Object %d is not a binary entity!\n", + GET16(tag->data)); + return; + } + sprintf(buf, "binary%d.bin", GET16(tag->data)); + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) { + sprintf(buf, "output.bin"); + filename = buf; + } + } + fout = fopen(filename, "wb"); + fwrite(tag->data+dx,len-dx,1,fout); + fclose(fout); +} + int main (int argc,char ** argv) { TAG*tag; SWF swf; int f; + int found = 0; + int frame = 0; + int tagnum = 0; + char depths[65536]; + char listavailable = 0; processargs(argc, argv); + if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids + && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids) + listavailable = 1; + + if(!originalplaceobjects && movetozero) { + fprintf(stderr, "Error: -0 (--movetozero) can only be used in conjunction with -P (--placeobject)\n"); + return 0; + } + if(!filename) { fprintf(stderr, "You must supply a filename.\n"); @@ -227,47 +1089,150 @@ int main (int argc,char ** argv) } initLog(0,-1,0,0,-1, verbose); - f = open(filename,O_RDONLY); + f = open(filename,O_RDONLY|O_BINARY); if (f<0) { perror("Couldn't open file: "); exit(1); } - if FAILED(swf_ReadSWF(f,&swf)) + if (swf_ReadSWF(f,&swf) < 0) { fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename); close(f); exit(1); } + close(f); + + if(listavailable) { + listObjects(&swf); + swf_FreeTags(&swf); + return 0; + } tag = swf.firstTag; + tagnum = 0; + while(tag) { + tagnum ++; + tag = tag->next; + } + tagused = (char*)malloc(tagnum); + memset(tagused, 0, tagnum); + memset(used, 0, 65536); + memset(depths, 0, 65536); + + tag = swf.firstTag; + tagnum = 0; while(tag) { + if(swf_isAllowedSpriteTag(tag)) { + int write = 0; + if(extractframes && is_in_range(frame, extractframes)) { + write = 1; + if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) { + depths[swf_GetDepth(tag)] = 1; + } + if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) { + int depth = swf_GetDepth(tag); + if(!depths[depth]) + write = 0; + depths[swf_GetDepth(tag)] = 0; + } + } else { + if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) && + (depths[swf_GetDepth(tag)]) && hollow) { + write = 1; + depths[swf_GetDepth(tag)] = 0; + } + } + if(write) { + enumerateIDs(tag, idcallback); + found = 1; + tagused[tagnum] = 1; + } + } + + if(tag->id == ST_SOUNDSTREAMHEAD || + tag->id == ST_SOUNDSTREAMHEAD2 || + tag->id == ST_SOUNDSTREAMBLOCK) { + if(extractmp3) + handlesoundstream(tag); + } + + if(tag->id == ST_JPEGTABLES) { + handlejpegtables(tag); + } + if(swf_isDefiningTag(tag)) { int id = swf_GetDefineID(tag); tags[id] = tag; + if(extractids && is_in_range(id, extractids)) { + used[id] = 5; + found = 1; + } + if(extractfontids && is_in_range(id, extractfontids)) { + handlefont(&swf, tag); + } + if(extractjpegids && is_in_range(id, extractjpegids)) { + handlejpeg(tag); + } + if(extractsoundids && is_in_range(id, extractsoundids)) { + handledefinesound(tag); + } + if(extractbinaryids && is_in_range(id, extractbinaryids)) { + handlebinary(tag); + } +#ifdef _ZLIB_INCLUDED_ + if(extractpngids && is_in_range(id, extractpngids)) { + handlelossless(tag); + } +#endif } else if (tag->id == ST_SETBACKGROUNDCOLOR) { mainr = tag->data[0]; maing = tag->data[1]; mainb = tag->data[2]; } - else if(tag->id == ST_PLACEOBJECT2) { + else if(swf_isPlaceTag(tag) && tag->id != ST_PLACEOBJECT ) { char*name = swf_GetName(tag); if(name && extractname && !strcmp(name, extractname)) { int id = swf_GetPlaceID(tag); - if(extractid>=0 && id != extractid) { - fprintf(stderr, "Error: More than one instance with name \"%s\"", name); - exit(1); + used[id] = 5; + found = 1; + if(originalplaceobjects) { + tagused[tagnum] = 1; } - extractid = swf_GetPlaceID(tag); + depths[swf_GetDepth(tag)] = 1; + extractname_id = id; + } + } + else if(tag->id == ST_SHOWFRAME) { + frame ++; + if(hollow) { + tagused[tagnum] = 1; + found = 1; + } + } + + if(tag->id == ST_DEFINESPRITE) { + while(tag->id != ST_END) { + tag = tag->next; + tagnum ++; } } tag = tag->next; + tagnum ++; + } + if (found) + extractTag(&swf, destfilename); + + if(mp3file) { + fclose(mp3file); + } else { + if(extractmp3) { + msg(" Didn't find a soundstream in file"); + } } - if(tags[extractid]) - extractTag(&swf, extractid, destfilename); swf_FreeTags(&swf); return 0;