2 Allows to extract parts of the swf into a new file.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This file is distributed under the GPL, see file COPYING for details */
13 #include "../lib/rfxswf.h"
14 #include "../lib/args.h"
19 #define _ZLIB_INCLUDED_
24 char * destfilename = "output.swf";
28 char* extractframes = 0;
29 char* extractjpegids = 0;
30 char* extractpngids = 0;
32 char* extractname = 0;
36 struct options_t options[] =
50 int args_callback_option(char*name,char*val)
52 if(!strcmp(name, "V")) {
53 printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
56 else if(!strcmp(name, "o")) {
60 else if(!strcmp(name, "i")) {
63 fprintf(stderr, "You can only supply either name or id\n");
68 else if(!strcmp(name, "n")) {
71 fprintf(stderr, "You can only supply either name or id\n");
76 else if(!strcmp(name, "v")) {
80 else if(!strcmp(name, "j")) {
82 fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
88 #ifdef _ZLIB_INCLUDED_
89 else if(!strcmp(name, "p")) {
91 fprintf(stderr, "Only one --pngs argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
98 else if(!strcmp(name, "f")) {
102 else if(!strcmp(name, "w")) {
107 printf("Unknown option: -%s\n", name);
113 int args_callback_longoption(char*name,char*val)
115 return args_long2shortoption(options, name, val);
117 void args_callback_usage(char*name)
119 printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name);
120 printf("\t-v , --verbose\t\t\t Be more verbose\n");
121 printf("\t-o , --output filename\t\t set output filename\n");
122 printf("\t-n , --name name\t\t instance name of the object to extract\n");
123 printf("\t-i , --id IDs\t\t\t ID of the object to extract\n");
124 printf("\t-j , --jpeg IDs\t\t\t IDs of the JPEG pictures to extract\n");
125 #ifdef _ZLIB_INCLUDED_
126 printf("\t-p , --pngs IDs\t\t\t IDs of the PNG pictures to extract\n");
128 printf("\t-f , --frame frames\t\t frame numbers to extract\n");
129 printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames (use with -f)\n");
130 printf("\t-V , --version\t\t\t Print program version and exit\n");
132 int args_callback_command(char*name,char*val)
135 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
142 U8 mainr,maing,mainb;
143 /* 1 = used, not expanded,
145 5 = wanted, not expanded
153 void idcallback(void*data)
155 if(!(used[*(U16*)data]&1)) {
157 used[*(U16*)data] |= 1;
161 void enumerateIDs(TAG*tag, void(*callback)(void*))
167 data = (U8*)malloc(len);
168 *(U16*)data = SWAP16((tag->id<<6)+63);
169 *(U8*)&data[2] = tag->len;
170 *(U8*)&data[3] = tag->len>>8;
171 *(U8*)&data[4] = tag->len>>16;
172 *(U8*)&data[5] = tag->len>>24;
173 memcpy(&data[6], tag->data, tag->len);
176 data = (U8*)malloc(len);
177 *(U16*)data = SWAP16((tag->id<<6)+tag->len);
178 memcpy(&data[2], tag->data, tag->len);
180 map_ids_mem(data, len, callback);
183 void extractTag(SWF*swf, char*filename)
194 memset(&newswf,0x00,sizeof(SWF)); // set global movie parameters
196 newswf.fileVersion = swf->fileVersion;
197 newswf.frameRate = swf->frameRate;
198 newswf.movieSize = swf->movieSize;
200 newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
201 desttag = newswf.firstTag;
205 swf_SetRGB(desttag,&rgb);
209 for(t=0;t<65536;t++) {
210 if(used[t] && !(used[t]&2)) {
211 if(tags[t]->id==ST_DEFINESPRITE) {
213 while(tag->id != ST_END)
215 enumerateIDs(tag, idcallback);
220 enumerateIDs(tags[t], idcallback);
227 srctag = swf->firstTag;
230 while(srctag && (srctag->id || sprite)) {
235 if(srctag->id == ST_END) {
238 if(srctag->id == ST_DEFINESPRITE)
240 if(swf_isDefiningTag(srctag)) {
241 int id = swf_GetDefineID(srctag);
245 if (((srctag->id == ST_PLACEOBJECT ||
246 srctag->id == ST_PLACEOBJECT2 ||
247 srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
248 (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
255 if(srctag->id == ST_REMOVEOBJECT) {
256 if(!used[swf_GetPlaceID(srctag)])
261 TAG*ttag = (TAG*)malloc(sizeof(TAG));
262 desttag = swf_InsertTag(desttag, srctag->id);
263 desttag->len = desttag->memsize = srctag->len;
264 desttag->data = malloc(srctag->len);
265 memcpy(desttag->data, srctag->data, srctag->len);
270 srctag = srctag->next;
273 if(!extractframes && !hollow)
274 desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
276 desttag = swf_InsertTag(desttag,ST_END);
278 f = open(filename, O_TRUNC|O_WRONLY|O_CREAT, 0644);
279 if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
282 swf_FreeTags(&newswf); // cleanup
285 void listObjects(SWF*swf)
291 char*names[] = {"Shapes","MovieClips","JPEGs","PNGs","Sounds","Frames"};
292 printf("Objects in file %s:\n",filename);
300 (tag->id == ST_DEFINESHAPE ||
301 tag->id == ST_DEFINESHAPE2 ||
302 tag->id == ST_DEFINESHAPE3)) {
304 sprintf(text,"%d", swf_GetDefineID(tag));
307 if(tag->id == ST_DEFINESPRITE) {
310 sprintf(text,"%d", swf_GetDefineID(tag));
313 while(tag->id != ST_END)
317 if(t == 2 && (tag->id == ST_DEFINEBITS ||
318 tag->id == ST_DEFINEBITSJPEG2 ||
319 tag->id == ST_DEFINEBITSJPEG3)) {
321 sprintf(text,"%d", swf_GetDefineID(tag));
324 if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
325 tag->id == ST_DEFINEBITSLOSSLESS2)) {
327 sprintf(text,"%d", swf_GetDefineID(tag));
331 if(t == 4 && (tag->id == ST_DEFINESOUND)) {
333 sprintf(text,"%d", swf_GetDefineID(tag));
336 if(t == 5 && (tag->id == ST_SHOWFRAME)) {
338 sprintf(text,"%d", frame);
346 printf("%s: ", names[t]);
360 void handlejpegtables(TAG*tag)
362 if(tag->id == ST_JPEGTABLES) {
363 jpegtables = tag->data;
364 jpegtablessize = tag->len;
368 FILE* save_fopen(char* name, char* mode)
370 FILE*fi = fopen(name, mode);
372 fprintf(stderr, "Error: Couldn't open %s\n", name);
378 int findjpegboundary(U8*data, int len)
393 /* extract jpeg data out of a tag */
394 void handlejpeg(TAG*tag)
398 sprintf(name, "pic%d.jpeg", *(U16*)tag->data);
399 /* swf jpeg images have two streams, which both start with ff d8 and
400 end with ff d9. The following code handles sorting the middle
401 <ff d9 ff d8> bytes out, so that one stream remains */
402 if(tag->id == ST_DEFINEBITS && tag->len>2 && jpegtables) {
403 fi = save_fopen(name, "wb");
404 fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
405 fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
408 if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
410 int pos = findjpegboundary(&tag->data[2], tag->len-2);
414 fi = save_fopen(name, "wb");
415 fwrite(&tag->data[2], pos-2, 1, fi);
416 fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
419 if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
420 U32 end = *(U32*)&tag->data[2]+6;
421 int pos = findjpegboundary(&tag->data[6], tag->len-6);
425 fi = save_fopen(name, "wb");
426 fwrite(&tag->data[6], pos-6, 1, fi);
427 fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
432 #ifdef _ZLIB_INCLUDED_
435 static U32*crc32_table = 0;
436 static void make_crc32_table(void)
441 crc32_table = (U32*)malloc(1024);
443 for (t = 0; t < 256; t++) {
446 for (s = 0; s < 8; s++) {
447 c = (0xedb88320L*(c&1)) ^ (c >> 1);
452 static void png_write_byte(FILE*fi, U8 byte)
454 fwrite(&byte,1,1,fi);
455 mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
457 static void png_start_chunk(FILE*fi, char*type, int len)
459 U8 mytype[4]={0,0,0,0};
460 U32 mylen = REVERSESWAP32(len);
461 memcpy(mytype,type,strlen(type));
462 fwrite(&mylen, 4, 1, fi);
464 png_write_byte(fi,mytype[0]);
465 png_write_byte(fi,mytype[1]);
466 png_write_byte(fi,mytype[2]);
467 png_write_byte(fi,mytype[3]);
469 static void png_write_bytes(FILE*fi, U8*bytes, int len)
473 png_write_byte(fi,bytes[t]);
475 static void png_write_dword(FILE*fi, U32 dword)
477 png_write_byte(fi,dword>>24);
478 png_write_byte(fi,dword>>16);
479 png_write_byte(fi,dword>>8);
480 png_write_byte(fi,dword);
482 static void png_end_chunk(FILE*fi)
484 U32 tmp = REVERSESWAP32((mycrc32^0xffffffff));
489 /* extract a lossless image (png) out of a tag
490 This routine was originally meant to be a one-pager. I just
491 didn't know png is _that_ much fun. :) -mk
493 void handlelossless(TAG*tag)
510 U8 head[] = {137,80,78,71,13,10,26,10};
512 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
520 if(tag->id != ST_DEFINEBITSLOSSLESS &&
521 tag->id != ST_DEFINEBITSLOSSLESS2)
525 format = swf_GetU8(tag);
526 if(format == 3) bpp = 8;
527 if(format == 4) bpp = 16;
528 if(format == 5) bpp = 32;
529 if(format!=3 && format!=5) {
531 fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
533 fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
536 width = swf_GetU16(tag);
537 height = swf_GetU16(tag);
538 if(format == 3) cols = swf_GetU8(tag) + 1;
539 // this is what format means according to the flash specification. (which is
541 // if(format == 4) cols = swf_GetU16(tag) + 1;
542 // if(format == 5) cols = swf_GetU32(tag) + 1;
545 logf("<verbose> Width %d", width);
546 logf("<verbose> Height %d", height);
547 logf("<verbose> Format %d", format);
548 logf("<verbose> Cols %d", cols);
549 logf("<verbose> Bpp %d", bpp);
551 datalen = (width*height*bpp/8+cols*8);
556 data = malloc(datalen);
557 error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
558 } while(error == Z_BUF_ERROR);
560 fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
563 logf("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
566 data2 = malloc(datalen2);
567 palette = (RGBA*)malloc(cols*sizeof(RGBA));
569 for(t=0;t<cols;t++) {
570 palette[t].r = data[pos++];
571 palette[t].g = data[pos++];
572 palette[t].b = data[pos++];
574 palette[t].a = data[pos++];
578 sprintf(name, "pic%d.png", id);
579 fi = save_fopen(name, "wb");
580 fwrite(head,sizeof(head),1,fi);
582 png_start_chunk(fi, "IHDR", 13);
583 png_write_dword(fi,width);
584 png_write_dword(fi,height);
585 png_write_byte(fi,8);
587 png_write_byte(fi,3); //indexed
589 png_write_byte(fi,2); //rgb
592 png_write_byte(fi,0); //compression mode
593 png_write_byte(fi,0); //filter mode
594 png_write_byte(fi,0); //interlace mode
598 png_start_chunk(fi, "PLTE", 768);
600 png_write_byte(fi,palette[t].r);
601 png_write_byte(fi,palette[t].g);
602 png_write_byte(fi,palette[t].b);
609 int srcwidth = width * (bpp/8);
610 datalen3 = width*height*4;
611 data3 = (U8*)malloc(datalen3);
612 for(y=0;y<height;y++)
614 data3[pos2++]=0; //filter type
616 // 32 bit to 24 bit "conversion"
617 for(x=0;x<width;x++) {
618 data3[pos2++]=data[pos+1];
619 data3[pos2++]=data[pos+2];
620 data3[pos2++]=data[pos+3];
621 pos+=4; //ignore padding byte
625 for(x=0;x<srcwidth;x++)
626 data3[pos2++]=data[pos++];
629 pos+=((srcwidth+3)&~3)-srcwidth; //align
634 if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
635 fprintf(stderr, "zlib error in pic %d\n", id);
638 logf("<verbose> Compressed data is %d bytes", datalen2);
639 png_start_chunk(fi, "IDAT", datalen2);
640 png_write_bytes(fi,data2,datalen2);
642 png_start_chunk(fi, "IEND", 0);
651 int main (int argc,char ** argv)
660 char listavailable = 0;
661 processargs(argc, argv);
663 if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids)
668 fprintf(stderr, "You must supply a filename.\n");
671 initLog(0,-1,0,0,-1, verbose);
673 f = open(filename,O_RDONLY);
677 perror("Couldn't open file: ");
680 if (swf_ReadSWF(f,&swf) < 0)
682 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
701 tagused = (char*)malloc(tagnum);
702 memset(tagused, 0, tagnum);
703 memset(used, 0, 65536);
704 memset(depths, 0, 65536);
709 if(swf_isAllowedSpriteTag(tag)) {
711 if(extractframes && is_in_range(frame, extractframes)) {
713 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
714 depths[swf_GetDepth(tag)] = 1;
716 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
717 int depth = swf_GetDepth(tag);
720 depths[swf_GetDepth(tag)] = 0;
723 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) &&
724 (depths[swf_GetDepth(tag)]) && hollow) {
726 depths[swf_GetDepth(tag)] = 0;
730 enumerateIDs(tag, idcallback);
736 if(tag->id == ST_JPEGTABLES)
737 handlejpegtables(tag);
739 if(swf_isDefiningTag(tag)) {
740 int id = swf_GetDefineID(tag);
742 if(extractids && is_in_range(id, extractids)) {
746 if(extractjpegids && is_in_range(id, extractjpegids)) {
749 #ifdef _ZLIB_INCLUDED_
750 if(extractpngids && is_in_range(id, extractpngids)) {
755 else if (tag->id == ST_SETBACKGROUNDCOLOR) {
756 mainr = tag->data[0];
757 maing = tag->data[1];
758 mainb = tag->data[2];
760 else if(tag->id == ST_PLACEOBJECT2) {
761 char*name = swf_GetName(tag);
762 if(name && extractname && !strcmp(name, extractname)) {
763 int id = swf_GetPlaceID(tag);
767 depths[swf_GetDepth(tag)] = 1;
770 else if(tag->id == ST_SHOWFRAME) {
778 if(tag->id == ST_DEFINESPRITE) {
779 while(tag->id != ST_END) {
788 extractTag(&swf, destfilename);