X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fswfextract.c;h=1e1f2046962a10a98e1b2849af223c3ef5ea2ec9;hb=9306fefe043d1517b6a38c6b0d6d08b045a1440e;hp=005f166aaac99e292359ae7acaed9ba2a919f0a0;hpb=03a812845a29458fc8d55c2f172b1677b23a0734;p=swftools.git diff --git a/src/swfextract.c b/src/swfextract.c index 005f166..1e1f204 100644 --- a/src/swfextract.c +++ b/src/swfextract.c @@ -13,6 +13,12 @@ #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"; @@ -20,6 +26,8 @@ int verbose = 2; char* extractids = 0; char* extractframes = 0; +char* extractjpegids = 0; +char* extractpngids = 0; char* extractname = 0; @@ -31,6 +39,8 @@ struct options_t options[] = {"w","hollow"}, {"v","verbose"}, {"i","id"}, + {"j","jpegs"}, + {"p","pngs"}, {"n","name"}, {"f","frame"}, {"V","version"}, @@ -67,6 +77,24 @@ int args_callback_option(char*name,char*val) verbose ++; 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); + } + 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); + } + extractpngids = val; + return 1; + } +#endif else if(!strcmp(name, "f")) { extractframes = val; return 1; @@ -88,13 +116,17 @@ 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-o , --output filename\t\t set output filename\n"); - printf("\t-i , --id ID\t\t\t ID of the object to extract\n"); printf("\t-n , --name name\t\t instance name of the object to extract\n"); - printf("\t-f , --frame frame\t\t frame number to extract\n"); - printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\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"); +#ifdef _ZLIB_INCLUDED_ + printf("\t-p , --pngs IDs\t\t\t IDs of the PNG pictures to extract\n"); +#endif + 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"); } int args_callback_command(char*name,char*val) @@ -133,13 +165,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; + *(U16*)data = SWAP16((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; + *(U16*)data = SWAP16((tag->id<<6)+tag->len); memcpy(&data[2], tag->data, tag->len); } map_ids_mem(data, len, callback); @@ -253,9 +288,9 @@ void listObjects(SWF*swf) char first; int t; int frame = 0; - char*names[] = {"Shapes","MovieClips","Bitmaps","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) { @@ -279,20 +314,26 @@ void listObjects(SWF*swf) tag = tag->next; } - if(t == 2 && (tag->id == ST_DEFINEBITSLOSSLESS || + if(t == 2 && (tag->id == ST_DEFINEBITS || tag->id == ST_DEFINEBITSJPEG2 || - tag->id == ST_DEFINEBITSLOSSLESS2 || tag->id == ST_DEFINEBITSJPEG3)) { show = 1; 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 ++; @@ -313,6 +354,300 @@ void listObjects(SWF*swf) } } +U8*jpegtables = 0; +int jpegtablessize; + +void handlejpegtables(TAG*tag) +{ + if(tag->id == ST_JPEGTABLES) { + jpegtables = tag->data; + jpegtablessize = tag->len; + } +} + +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); + /* 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_DEFINEBITS && tag->len>2 && jpegtables) { + fi = save_fopen(name, "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) + fclose(fi); + } + if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) { + int end = tag->len; + int pos = findjpegboundary(&tag->data[2], tag->len-2); + if(pos<0) + return; + pos+=2; + fi = save_fopen(name, "wb"); + fwrite(&tag->data[2], pos-2, 1, fi); + fwrite(&tag->data[pos+4], end-(pos+4), 1, fi); + fclose(fi); + } + if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) { + U32 end = *(U32*)&tag->data[2]+6; + int pos = findjpegboundary(&tag->data[6], tag->len-6); + if(pos<0) + return; + pos+=6; + fi = save_fopen(name, "wb"); + fwrite(&tag->data[6], pos-6, 1, fi); + fwrite(&tag->data[pos+4], end-(pos+4), 1, fi); + fclose(fi); + } +} + +#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 + int main (int argc,char ** argv) { TAG*tag; @@ -325,7 +660,7 @@ int main (int argc,char ** argv) char listavailable = 0; processargs(argc, argv); - if(!extractframes && !extractids && ! extractname) + if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids) listavailable = 1; if(!filename) @@ -398,6 +733,9 @@ int main (int argc,char ** argv) } } + if(tag->id == ST_JPEGTABLES) + handlejpegtables(tag); + if(swf_isDefiningTag(tag)) { int id = swf_GetDefineID(tag); tags[id] = tag; @@ -405,6 +743,14 @@ int main (int argc,char ** argv) used[id] = 5; found = 1; } + 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];