X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=src%2Fswfextract.c;h=47bcbe778b8cecdcb8d14f89519da2f6af68bfc8;hp=aca4d970bc62f95fad7e630c33b8faf4999affc2;hb=9b328ffcb7522c4c0d021a1a8c68e95390e1b2a7;hpb=addd269469480d86c20849c4fcdb504c2f7ab233 diff --git a/src/swfextract.c b/src/swfextract.c index aca4d97..47bcbe7 100644 --- a/src/swfextract.c +++ b/src/swfextract.c @@ -25,6 +25,8 @@ #include "../lib/rfxswf.h" #include "../lib/args.h" #include "../lib/log.h" +#include "../lib/jpeg.h" +#include "../lib/png.h" #ifdef HAVE_ZLIB_H #ifdef HAVE_LIBZ #include "zlib.h" @@ -42,14 +44,18 @@ char* extractjpegids = 0; char* extractfontids = 0; char* extractpngids = 0; char* extractsoundids = 0; +char* extractbinaryids = 0; +char* extractanyids = 0; char extractmp3 = 0; char* extractname = 0; char hollow = 0; char originalplaceobjects = 0; +char movetozero = 0; int numextracts = 0; +char *outputformat = NULL; struct options_t options[] = { @@ -59,13 +65,17 @@ struct options_t options[] = {"i","id"}, {"j","jpegs"}, {"p","pngs"}, + {"a","any"}, {"P","placeobject"}, + {"0","movetozero"}, {"m","mp3"}, {"s","sound"}, {"n","name"}, {"f","frame"}, {"F","font"}, {"V","version"}, + {"b","binary"}, + {"O","outputformat"}, {0,0} }; @@ -112,6 +122,7 @@ int args_callback_option(char*name,char*val) 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; @@ -134,6 +145,15 @@ int args_callback_option(char*name,char*val) 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) { @@ -145,6 +165,11 @@ int args_callback_option(char*name,char*val) return 1; } #endif + else if(!strcmp(name, "a")) { + numextracts++; + extractanyids = val; + return 1; + } else if(!strcmp(name, "f")) { numextracts++; extractframes = val; @@ -154,10 +179,18 @@ int args_callback_option(char*name,char*val) originalplaceobjects = 1; return 0; } + else if(!strcmp(name, "0")) { + movetozero = 1; + return 0; + } else if(!strcmp(name, "w")) { hollow = 1; return 0; } + else if (!strcmp(name, "O")) { + outputformat = val; + return 1; + } else { printf("Unknown option: -%s\n", name); exit(1); @@ -205,6 +238,23 @@ int args_callback_command(char*name,char*val) return 0; } +void prepare_name(char *buf, size_t len, const char *prefix, + const char *suffix, int idx) { + if (outputformat!=NULL) { + // override default file name formatting + // make sure single-file behavior is not used + numextracts = -1; + // Other parts of codebase use vsnprintf, so I assume snprintf + // is available on all platforms that swftools currently works on. + // We need to check for buffer overflows now that the user is + // supplying the format string. + snprintf(buf,len,outputformat,idx,suffix); + } else { + // use default file name formatting, unchanged + sprintf(buf,"%s%d.%s",prefix,idx,suffix); + } +} + U8 mainr,maing,mainb; /* 1 = used, not expanded, 3 = used, expanded @@ -254,6 +304,18 @@ void enumerateIDs(TAG*tag, void(*callback)(void*)) callback(&tag->data[ptr[t]]); } +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; @@ -271,6 +333,12 @@ void extractTag(SWF*swf, char*filename) 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; @@ -316,6 +384,8 @@ void extractTag(SWF*swf, 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]) { @@ -325,7 +395,7 @@ void extractTag(SWF*swf, char*filename) swf_ExpandRect2(&objectbbox, &b); } } else - if (((((srctag->id == ST_PLACEOBJECT || srctag->id == ST_PLACEOBJECT2) && originalplaceobjects) + if ((((swf_isPlaceTag(srctag) && originalplaceobjects) || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) || (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) || (tagused[tagnum])) @@ -345,6 +415,9 @@ void extractTag(SWF*swf, 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; } @@ -354,21 +427,67 @@ void extractTag(SWF*swf, char*filename) } if(!extractframes && !hollow) { if(!originalplaceobjects && (extractids||extractname_id>=0)) { + int number = 0; + int id = 0; int t; - int s=0; - if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0) - newswf.movieSize = objectbbox; + 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_ObjectPlace(desttag, t, t, 0,0,0); - s++; - if(s==2) - printf("warning! You should use the -P when extracting multiple objects\n"); + 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); } } } @@ -407,9 +526,12 @@ int isOfType(int t, TAG*tag) if(t == 4 && (tag->id == ST_DEFINESOUND)) { show = 1; } - if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2)) { + 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; } @@ -419,7 +541,9 @@ void listObjects(SWF*swf) char first; int t; int frame = 0; - char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font"}; + 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; @@ -436,7 +562,7 @@ void listObjects(SWF*swf) if(!nr) continue; - printf(" %d %s%s: ID(s) ", nr, names[t], nr>1?"s":""); + printf(" [%s] %d %s%s: ID(s) ", options[t], nr, names[t], nr>1?"s":""); tag = swf->firstTag; while(tag) { @@ -479,12 +605,15 @@ void listObjects(SWF*swf) } if(frame) - printf(" %d Frames: ID(s) 0-%d\n", frame, frame); + printf(" [-f] %d Frames: ID(s) 0-%d\n", frame, frame); else - printf(" 1 Frame: ID(s) 0\n"); + printf(" [-f] 1 Frame: ID(s) 0\n"); + + if(mp3) + printf(" [-m] 1 MP3 Soundstream\n"); } -void handlefont(SWF*swf, TAG*tag) +int handlefont(SWF*swf, TAG*tag) { SWFFONT* f=0; U16 id; @@ -493,31 +622,34 @@ void handlefont(SWF*swf, TAG*tag) int t; id = swf_GetDefineID(tag); - sprintf(name, "font%d.swf", id); + prepare_name(name, sizeof(name), "font", "swf", id); if(numextracts==1) { filename = destfilename; } swf_FontExtract(swf, id, &f); if(!f) { - printf("Couldn't extract font %d\n", id); - return; + if (!extractanyids) { + printf("Couldn't extract font %d\n", id); + } + return 0; } - if(!f->layout) - swf_FontCreateLayout(f); swf_WriteFont(f, filename); swf_FontFree(f); + return 1; } -U8*jpegtables = 0; -int jpegtablessize; +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; } } @@ -535,7 +667,7 @@ 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"; + + if(tag->id != ST_DEFINEBITSJPEG3) { + prepare_name(name, sizeof(name), "pic", "jpg", GET16(tag->data)); + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) + filename = "output.jpg"; + } + } else { + prepare_name(name, sizeof(name), "pic", "png", GET16(tag->data)); + if(numextracts==1) { + filename = destfilename; + if(!strcmp(filename,"output.swf")) + filename = "output.png"; + } } + /* 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 && jpegtables) { + if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) { fi = save_fopen(filename, "wb"); - 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) + 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) { @@ -585,20 +731,44 @@ void handlejpeg(TAG*tag) } 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) - return; - 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); + int pos = findjpegboundary(&tag->data[6], end); + if(end >= tag->len) { + msg(" zlib data out of bounds in definebitsjpeg3"); + return 0; + } + if(pos) { + /* TODO: do we actually need this? */ + memmove(&tag->data[pos], &tag->data[pos+4], end-(pos+4)); + } + unsigned char*image; + int width=0, height=0; + jpeg_load_from_mem(&tag->data[6], end-6, &image, &width, &height); + + uLongf datalen = width*height; + Bytef *data = malloc(datalen); + + int error = uncompress(data, &datalen, &tag->data[end], (uLong)(tag->len - end)); + if(error != Z_OK) { + fprintf(stderr, "Zlib error %d\n", error); + return 0; + } + int t, size = width*height; + for(t=0;tdata); - fprintf(stderr, "Object %d is not a JPEG picture!\n",id); - exit(1); + if (!extractanyids) { + fprintf(stderr, "Object %d is not a JPEG picture!\n", id); + exit(1); + } + return 0; } + return 1; } #ifdef _ZLIB_INCLUDED_ @@ -629,7 +799,7 @@ static inline void png_write_byte(FILE*fi, U8 byte) static void png_start_chunk(FILE*fi, char*type, int len) { U8 mytype[4]={0,0,0,0}; - U32 mylen = REVERSESWAP32(len); + U32 mylen = BE_32_TO_NATIVE(len); memcpy(mytype,type,strlen(type)); fwrite(&mylen, 4, 1, fi); mycrc32=0xffffffff; @@ -653,7 +823,7 @@ static void png_write_dword(FILE*fi, U32 dword) } static void png_end_chunk(FILE*fi) { - U32 tmp = REVERSESWAP32((mycrc32^0xffffffff)); + U32 tmp = BE_32_TO_NATIVE((mycrc32^0xffffffff)); fwrite(&tmp,4,1,fi); } @@ -662,7 +832,7 @@ static void png_end_chunk(FILE*fi) 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) +int handlelossless(TAG*tag) { char name[80]; char*filename = name; @@ -674,11 +844,11 @@ void handlelossless(TAG*tag) U8 bpp = 1; U8 format; U8 tmp; - U8* data=0; + Bytef* data=0; U8* data2=0; U8* data3=0; - U32 datalen; - U32 datalen2; + uLongf datalen; + uLongf datalen2; U32 datalen3; U8 head[] = {137,80,78,71,13,10,26,10}; int cols; @@ -693,8 +863,11 @@ void handlelossless(TAG*tag) 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); + if (!extractanyids) { + fprintf(stderr, "Object %d is not a PNG picture!\n",id); + exit(1); + } + return 0; } id =swf_GetU16(tag); @@ -707,7 +880,7 @@ void handlelossless(TAG*tag) 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; + return 0; } width = swf_GetU16(tag); height = swf_GetU16(tag); @@ -734,11 +907,11 @@ void handlelossless(TAG*tag) } while(error == Z_BUF_ERROR); if(error != Z_OK) { fprintf(stderr, "Zlib error %d (image %d)\n", error, id); - return; + return 0; } msg(" Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols); pos = 0; - datalen2 = datalen; + datalen2 = datalen+16; data2 = malloc(datalen2); palette = (RGBA*)malloc(cols*sizeof(RGBA)); @@ -751,7 +924,7 @@ void handlelossless(TAG*tag) } } - sprintf(name, "pic%d.png", id); + prepare_name(name, sizeof(name), "pic", "png", id); if(numextracts==1) { filename = destfilename; if(!strcmp(filename,"output.swf")) @@ -770,7 +943,7 @@ void handlelossless(TAG*tag) png_write_byte(fi,2); //rgb else if(format == 5 && alpha==1) png_write_byte(fi,6); //rgba - else return; + else return 0; png_write_byte(fi,0); //compression mode png_write_byte(fi,0); //filter mode @@ -786,12 +959,21 @@ void handlelossless(TAG*tag) png_write_byte(fi,palette[t].b); } png_end_chunk(fi); + + if(alpha) { + /* write alpha palette */ + png_start_chunk(fi, "tRNS", 256); + for(t=0;t<256;t++) { + png_write_byte(fi,palette[t].a); + } + png_end_chunk(fi); + } } { int pos2 = 0; int x,y; int srcwidth = width * (bpp/8); - datalen3 = width*height*4; + datalen3 = (width*4+5)*height; data3 = (U8*)malloc(datalen3); for(y=0;y Compressed data is %d bytes", datalen2); png_start_chunk(fi, "IDAT", datalen2); @@ -839,10 +1021,11 @@ void handlelossless(TAG*tag) free(data); free(data2); free(data3); + return 1; } #endif -FILE*mp3file; +static FILE*mp3file=0; void handlesoundstream(TAG*tag) { char*filename = "output.mp3"; @@ -875,7 +1058,7 @@ void handlesoundstream(TAG*tag) } } -void handledefinesound(TAG*tag) +int handledefinesound(TAG*tag) { U8 flags; U32 samples; @@ -909,8 +1092,10 @@ void handledefinesound(TAG*tag) } 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"; + } else { + return 0; } - sprintf(buf, "sound%d.%s", id, extension); + prepare_name(buf, sizeof(buf), "sound", extension, id); if(numextracts==1) { filename = destfilename; if(!strcmp(filename,"output.swf")) { @@ -921,6 +1106,34 @@ void handledefinesound(TAG*tag) fi = save_fopen(filename, "wb"); fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi); fclose(fi); + return 1; +} + +int 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) { + if (!extractanyids) { + fprintf(stderr, "Object %d is not a binary entity!\n", + GET16(tag->data)); + } + return 0; + } + prepare_name(buf, sizeof(buf), "binary", "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); + return 1; } int main (int argc,char ** argv) @@ -936,9 +1149,14 @@ int main (int argc,char ** argv) processargs(argc, argv); if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids - && !extractmp3 && !extractsoundids && !extractfontids) + && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids && !extractanyids) 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"); @@ -1016,8 +1234,9 @@ int main (int argc,char ** argv) handlesoundstream(tag); } - if(tag->id == ST_JPEGTABLES) + if(tag->id == ST_JPEGTABLES) { handlejpegtables(tag); + } if(swf_isDefiningTag(tag)) { int id = swf_GetDefineID(tag); @@ -1035,24 +1254,47 @@ int main (int argc,char ** argv) 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 + if(extractanyids && is_in_range(id, extractanyids)) { + if (handlefont(&swf,tag)) { + // pass + } else if (handlejpeg(tag)) { + // pass + } else if (handlebinary(tag)) { + // pass +#ifdef _ZLIB_INCLUDED_ + } else if (handlelossless(tag)) { + // pass +#endif + } else if (handledefinesound(tag)) { + // Not sure if sound code checks carefully for type. + // pass + } else { + printf("#%d not processed\n", id); + } + } } 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); used[id] = 5; found = 1; - tagused[tagnum] = 1; + if(originalplaceobjects) { + tagused[tagnum] = 1; + } depths[swf_GetDepth(tag)] = 1; extractname_id = id; } @@ -1077,8 +1319,13 @@ int main (int argc,char ** argv) if (found) extractTag(&swf, destfilename); - if(mp3file) + if(mp3file) { fclose(mp3file); + } else { + if(extractmp3) { + msg(" Didn't find a soundstream in file"); + } + } swf_FreeTags(&swf); return 0;