X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fswfextract.c;h=fa612fc70fe864616264bb9495bd2b73032f6390;hb=0c3006e417a705dcacc7dad7083de0e4adb41971;hp=a04ae29310b52f0ff3c6ee0318d91d664e6cfc2c;hpb=2047985872449cedddbd3446092c31f03af8f74c;p=swftools.git diff --git a/src/swfextract.c b/src/swfextract.c index a04ae29..fa612fc 100644 --- a/src/swfextract.c +++ b/src/swfextract.c @@ -13,19 +13,29 @@ #include "../lib/rfxswf.h" #include "../lib/args.h" #include "reloc.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 verbose = 3; char* extractids = 0; char* extractframes = 0; char* extractjpegids = 0; +char* extractpngids = 0; +char extractmp3 = 0; char* extractname = 0; char hollow = 0; +int numextracts = 0; + struct options_t options[] = { {"o","output"}, @@ -33,12 +43,14 @@ struct options_t options[] = {"v","verbose"}, {"i","id"}, {"j","jpegs"}, + {"p","pngs"}, {"n","name"}, {"f","frame"}, {"V","version"}, {0,0} }; + int args_callback_option(char*name,char*val) { if(!strcmp(name, "V")) { @@ -51,6 +63,7 @@ int args_callback_option(char*name,char*val) } else if(!strcmp(name, "i")) { extractids = val; + numextracts++; if(extractname) { fprintf(stderr, "You can only supply either name or id\n"); exit(1); @@ -59,6 +72,7 @@ int args_callback_option(char*name,char*val) } else if(!strcmp(name, "n")) { extractname = val; + numextracts++; if(extractids) { fprintf(stderr, "You can only supply either name or id\n"); exit(1); @@ -69,15 +83,33 @@ int args_callback_option(char*name,char*val) 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); } + numextracts++; extractjpegids = val; return 1; } +#ifdef _ZLIB_INCLUDED_ + else if(!strcmp(name, "p")) { + if(extractpngids) { + fprintf(stderr, "Only one --pngs 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; } @@ -103,7 +135,11 @@ void args_callback_usage(char*name) printf("\t-o , --output filename\t\t set output filename\n"); printf("\t-n , --name name\t\t instance name of the object to extract\n"); printf("\t-i , --id IDs\t\t\t ID of the object to extract\n"); - printf("\t-j , --jpeg IDs\t\t\t IDs of the jpeg pictures to extract\n"); + printf("\t-j , --jpeg IDs\t\t\t IDs of the JPEG pictures to extract\n"); +#ifdef _ZLIB_INCLUDED_ + printf("\t-p , --pngs IDs\t\t\t IDs of the PNG pictures to extract\n"); +#endif + printf("\t-m , --mp3\t\t\t Extract main mp3 stream\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 (use with -f)\n"); printf("\t-V , --version\t\t\t Print program version and exit\n"); @@ -131,9 +167,9 @@ char * tagused; void idcallback(void*data) { - if(!(used[*(U16*)data]&1)) { + if(!(used[GET16(data)]&1)) { changed = 1; - used[*(U16*)data] |= 1; + used[GET16(data)] |= 1; } } @@ -144,13 +180,16 @@ void enumerateIDs(TAG*tag, void(*callback)(void*)) 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); @@ -264,9 +303,9 @@ void listObjects(SWF*swf) char first; int t; int frame = 0; - char*names[] = {"Shapes","MovieClips","JPEGs","Sounds","Frames"}; + char*names[] = {"Shapes","MovieClips","JPEGs","PNGs","Sounds","Frames"}; printf("Objects in file %s:\n",filename); - for(t=0;t<5;t++) { + for(t=0;t<6;t++) { tag = swf->firstTag; first = 1; while(tag) { @@ -297,12 +336,19 @@ void listObjects(SWF*swf) sprintf(text,"%d", swf_GetDefineID(tag)); } - if(t == 3 && (tag->id == ST_DEFINESOUND)) { + if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS || + tag->id == ST_DEFINEBITSLOSSLESS2)) { + show = 1; + sprintf(text,"%d", swf_GetDefineID(tag)); + } + + + if(t == 4 && (tag->id == ST_DEFINESOUND)) { show = 1; sprintf(text,"%d", swf_GetDefineID(tag)); } - if(t == 4 && (tag->id == ST_SHOWFRAME)) { + if(t == 5 && (tag->id == ST_SHOWFRAME)) { show = 1; sprintf(text,"%d", frame); frame ++; @@ -364,7 +410,7 @@ void handlejpeg(TAG*tag) { char name[80]; FILE*fi; - sprintf(name, "pic%d.jpeg", *(U16*)tag->data); + sprintf(name, "pic%d.jpeg", GET16(tag->data)); /* 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 */ @@ -386,7 +432,7 @@ void handlejpeg(TAG*tag) fclose(fi); } if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) { - U32 end = *(U32*)&tag->data[2]+6; + U32 end = GET32(&tag->data[2])+6; int pos = findjpegboundary(&tag->data[6], tag->len-6); if(pos<0) return; @@ -398,6 +444,258 @@ void handlejpeg(TAG*tag) } } +#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 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]; + FILE*fi; + int width, height; + int crc; + int id; + int t; + U8 bpp = 1; + U8 format; + U8 tmp; + U8* data=0; + U8* data2=0; + U8* data3=0; + U32 datalen; + U32 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) + return; + + 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; + + logf(" Width %d", width); + logf(" Height %d", height); + logf(" Format %d", format); + logf(" Cols %d", cols); + logf(" 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; + } + logf(" Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols); + pos = 0; + datalen2 = datalen; + 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 + +FILE*mp3file; +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"); + logf(" Writing mp3 data to %s",filename); + } + else + logf(" Soundstream is not mp3"); + break; + case ST_SOUNDSTREAMHEAD2: + if((tag->data[1]&0x30) == 0x20) {//mp3 compression + mp3file = fopen("mainstream.mp3", "wb"); + logf(" Writing mp3 data to %s",filename); + } + else + logf(" Soundstream is not mp3 (2)"); + break; + case ST_SOUNDSTREAMBLOCK: + if(mp3file) + fwrite(&tag->data[4],tag->len-4,1,mp3file); + break; + } +} + int main (int argc,char ** argv) { TAG*tag; @@ -410,7 +708,8 @@ int main (int argc,char ** argv) char listavailable = 0; processargs(argc, argv); - if(!extractframes && !extractids && ! extractname && !extractjpegids) + if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids + && !extractmp3) listavailable = 1; if(!filename) @@ -483,6 +782,12 @@ int main (int argc,char ** argv) } } + if(tag->id == ST_SOUNDSTREAMHEAD || + tag->id == ST_SOUNDSTREAMHEAD2 || + tag->id == ST_SOUNDSTREAMBLOCK) { + handlesoundstream(tag); + } + if(tag->id == ST_JPEGTABLES) handlejpegtables(tag); @@ -496,6 +801,11 @@ int main (int argc,char ** argv) if(extractjpegids && is_in_range(id, extractjpegids)) { handlejpeg(tag); } +#ifdef _ZLIB_INCLUDED_ + if(extractpngids && is_in_range(id, extractpngids)) { + handlelossless(tag); + } +#endif } else if (tag->id == ST_SETBACKGROUNDCOLOR) { mainr = tag->data[0]; @@ -532,6 +842,9 @@ int main (int argc,char ** argv) if (found) extractTag(&swf, destfilename); + if(mp3file) + fclose(mp3file); + swf_FreeTags(&swf); return 0; }