fix crashes reported by Debian Cylab Mayhem Team
[swftools.git] / src / swfextract.c
blob4791124c1db8b94a05e23df4fed9099c7f76c5a5
1 /* swfextract.c
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 program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include "../lib/rfxswf.h"
26 #include "../lib/args.h"
27 #include "../lib/log.h"
28 #include "../lib/jpeg.h"
29 #include "../lib/png.h"
30 #ifdef HAVE_ZLIB_H
31 #ifdef HAVE_LIBZ
32 #include "zlib.h"
33 #define _ZLIB_INCLUDED_
34 #endif
35 #endif
37 char * filename = 0;
38 char * destfilename = "output.swf";
39 int verbose = 3;
41 char* extractids = 0;
42 char* extractframes = 0;
43 char* extractjpegids = 0;
44 char* extractfontids = 0;
45 char* extractpngids = 0;
46 char* extractsoundids = 0;
47 char* extractmp3ids = 0;
48 char* extractbinaryids = 0;
49 char* extractanyids = 0;
50 char extractmp3 = 0;
52 char* extractname = 0;
54 char hollow = 0;
55 char originalplaceobjects = 0;
56 char movetozero = 0;
58 int numextracts = 0;
59 char *outputformat = NULL;
61 struct options_t options[] =
63 {"o","output"},
64 {"w","hollow"},
65 {"v","verbose"},
66 {"i","id"},
67 {"j","jpegs"},
68 {"p","pngs"},
69 {"a","any"},
70 {"P","placeobject"},
71 {"0","movetozero"},
72 {"m","mp3"},
73 {"M","embeddedmp3"},
74 {"s","sound"},
75 {"n","name"},
76 {"f","frame"},
77 {"F","font"},
78 {"V","version"},
79 {"b","binary"},
80 {"O","outputformat"},
81 {0,0}
85 int args_callback_option(char*name,char*val)
87 if(!strcmp(name, "V")) {
88 printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
89 exit(0);
91 else if(!strcmp(name, "o")) {
92 destfilename = val;
93 return 1;
95 else if(!strcmp(name, "i")) {
96 extractids = val;
97 numextracts++;
98 if(extractname) {
99 fprintf(stderr, "You can only supply either name or id\n");
100 exit(1);
102 return 1;
104 else if(!strcmp(name, "n")) {
105 extractname = val;
106 numextracts++;
107 if(extractids) {
108 fprintf(stderr, "You can only supply either name or id\n");
109 exit(1);
111 return 1;
113 else if(!strcmp(name, "v")) {
114 verbose ++;
115 return 0;
117 else if(!strcmp(name, "m")) {
118 extractmp3 = 1;
119 numextracts++;
120 return 0;
122 else if(!strcmp(name, "M")) {
123 if(extractsoundids) {
124 fprintf(stderr, "Only one --embeddedmp3 argument is allowed. (Try to use a range, e.g. -M 1,2,3)\n");
125 exit(1);
127 numextracts++;
128 extractmp3ids = val;
129 return 1;
131 else if(!strcmp(name, "j")) {
132 if(extractjpegids) {
133 fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
134 exit(1);
136 /* TODO: count number of IDs in val range */
137 numextracts++;
138 extractjpegids = val;
139 return 1;
141 else if(!strcmp(name, "F")) {
142 if(extractfontids) {
143 fprintf(stderr, "Only one --font argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
144 exit(1);
146 numextracts++;
147 extractfontids = val;
148 return 1;
150 else if(!strcmp(name, "s")) {
151 if(extractsoundids) {
152 fprintf(stderr, "Only one --sound argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
153 exit(1);
155 numextracts++;
156 extractsoundids = val;
157 return 1;
159 else if(!strcmp(name, "b")) {
160 if(extractbinaryids) {
161 fprintf(stderr, "Only one --binary argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
162 exit(1);
164 numextracts++;
165 extractbinaryids = val;
166 return 1;
168 #ifdef _ZLIB_INCLUDED_
169 else if(!strcmp(name, "p")) {
170 if(extractpngids) {
171 fprintf(stderr, "Only one --png argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
172 exit(1);
174 numextracts++;
175 extractpngids = val;
176 return 1;
178 #endif
179 else if(!strcmp(name, "a")) {
180 numextracts++;
181 extractanyids = val;
182 return 1;
184 else if(!strcmp(name, "f")) {
185 numextracts++;
186 extractframes = val;
187 return 1;
189 else if(!strcmp(name, "P")) {
190 originalplaceobjects = 1;
191 return 0;
193 else if(!strcmp(name, "0")) {
194 movetozero = 1;
195 return 0;
197 else if(!strcmp(name, "w")) {
198 hollow = 1;
199 return 0;
201 else if (!strcmp(name, "O")) {
202 outputformat = val;
203 return 1;
205 else {
206 printf("Unknown option: -%s\n", name);
207 exit(1);
210 return 0;
212 int args_callback_longoption(char*name,char*val)
214 return args_long2shortoption(options, name, val);
216 void args_callback_usage(char*name)
218 printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name);
219 printf("\t-v , --verbose\t\t\t Be more verbose\n");
220 printf("\t-o , --output filename\t\t set output filename\n");
221 printf("\t-V , --version\t\t\t Print program version and exit\n\n");
222 printf("SWF Subelement extraction:\n");
223 printf("\t-n , --name name\t\t instance name of the object (SWF Define) to extract\n");
224 printf("\t-i , --id ID\t\t\t ID of the object, shape or movieclip to extract\n");
225 printf("\t-f , --frame frames\t\t frame numbers to extract\n");
226 printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n");
227 printf("\t \t\t\t (use with -f)\n");
228 printf("\t-P , --placeobject\t\t\t Insert original placeobject into output file\n");
229 printf("\t \t\t\t (use with -i)\n");
230 printf("SWF Font/Text extraction:\n");
231 printf("\t-F , --font ID\t\t\t Extract font(s)\n");
232 printf("Picture extraction:\n");
233 printf("\t-j , --jpeg ID\t\t\t Extract JPEG picture(s)\n");
234 #ifdef _ZLIB_INCLUDED_
235 printf("\t-p , --pngs ID\t\t\t Extract PNG picture(s)\n");
236 #endif
237 printf("\n");
238 printf("Sound extraction:\n");
239 printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n");
240 printf("\t-M , --embeddedmp3\t\t\t Extract embedded mp3 stream(s)\n");
241 printf("\t-s , --sound ID\t\t\t Extract Sound(s)\n");
243 int args_callback_command(char*name,char*val)
245 if(filename) {
246 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
247 filename, name);
249 filename = name;
250 return 0;
253 void prepare_name(char *buf, size_t len, const char *prefix,
254 const char *suffix, int idx) {
255 if (outputformat!=NULL) {
256 // override default file name formatting
257 // make sure single-file behavior is not used
258 numextracts = -1;
259 // Other parts of codebase use vsnprintf, so I assume snprintf
260 // is available on all platforms that swftools currently works on.
261 // We need to check for buffer overflows now that the user is
262 // supplying the format string.
263 snprintf(buf,len,outputformat,idx,suffix);
264 } else {
265 // use default file name formatting, unchanged
266 sprintf(buf,"%s%d.%s",prefix,idx,suffix);
270 U8 mainr,maing,mainb;
271 /* 1 = used, not expanded,
272 3 = used, expanded
273 5 = wanted, not expanded
274 7 = wanted, expanded
276 char used[65536];
277 TAG*tags[65536];
278 int changed;
279 char * tagused;
280 int extractname_id = -1;
282 void idcallback(void*data)
284 if(!(used[GET16(data)]&1)) {
285 changed = 1;
286 used[GET16(data)] |= 1;
290 void enumerateIDs(TAG*tag, void(*callback)(void*))
292 /* U8*data;
293 int len = tag->len;
294 if(tag->len>=64) {
295 len += 6;
296 data = (U8*)malloc(len);
297 PUT16(data, (tag->id<<6)+63);
298 *(U8*)&data[2] = tag->len;
299 *(U8*)&data[3] = tag->len>>8;
300 *(U8*)&data[4] = tag->len>>16;
301 *(U8*)&data[5] = tag->len>>24;
302 memcpy(&data[6], tag->data, tag->len);
303 } else {
304 len += 2;
305 data = (U8*)malloc(len);
306 PUT16(data, (tag->id<<6)+tag->len);
307 memcpy(&data[2], tag->data, tag->len);
309 map_ids_mem(data, len, callback);
311 int num = swf_GetNumUsedIDs(tag);
312 int *ptr = malloc(sizeof(int)*num);
313 int t;
314 swf_GetUsedIDs(tag, ptr);
315 for(t=0;t<num;t++)
316 callback(&tag->data[ptr[t]]);
319 void moveToZero(TAG*tag)
321 if(!swf_isPlaceTag(tag))
322 return;
323 SWFPLACEOBJECT obj;
324 swf_GetPlaceObject(tag, &obj);
325 obj.matrix.tx = 0;
326 obj.matrix.ty = 0;
327 swf_ResetTag(tag, tag->id);
328 swf_SetPlaceObject(tag, &obj);
331 void extractTag(SWF*swf, char*filename)
333 SWF newswf;
334 TAG*desttag;
335 TAG*srctag;
336 RGBA rgb;
337 SRECT objectbbox;
338 char sprite;
339 int f;
340 int t;
341 int tagnum;
342 int copy = 0;
343 memset(&newswf,0x00,sizeof(SWF)); // set global movie parameters
345 newswf.fileVersion = swf->fileVersion;
346 newswf.frameRate = swf->frameRate;
347 newswf.movieSize = swf->movieSize;
348 if(movetozero && originalplaceobjects) {
349 newswf.movieSize.xmax = swf->movieSize.xmax - swf->movieSize.xmin;
350 newswf.movieSize.ymax = swf->movieSize.ymax - swf->movieSize.ymin;
351 newswf.movieSize.xmin = 0;
352 newswf.movieSize.ymin = 0;
355 newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
356 desttag = newswf.firstTag;
357 rgb.r = mainr;
358 rgb.g = maing;
359 rgb.b = mainb;
360 swf_SetRGB(desttag,&rgb);
362 swf_GetRect(0, &objectbbox);
364 do {
365 changed = 0;
366 for(t=0;t<65536;t++) {
367 if(used[t] && !(used[t]&2)) {
368 if(tags[t]==0) {
369 msg("<warning> ID %d is referenced, but never defined.", t);
370 } else if(tags[t]->id==ST_DEFINESPRITE) {
371 TAG*tag = tags[t];
372 while(tag->id != ST_END)
374 enumerateIDs(tag, idcallback);
375 tag = tag->next;
378 else
379 enumerateIDs(tags[t], idcallback);
380 used[t] |= 2;
384 while(changed);
386 srctag = swf->firstTag;
387 tagnum = 0;
388 sprite = 0;
389 while(srctag && (srctag->id || sprite)) {
390 int reset = 0;
391 if(!sprite) {
392 copy = 0;
394 if(srctag->id == ST_END) {
395 sprite = 0;
397 if(srctag->id == ST_DEFINESPRITE)
398 sprite = 1;
399 if(srctag->id == ST_JPEGTABLES)
400 copy = 1;
401 if(swf_isDefiningTag(srctag)) {
402 int id = swf_GetDefineID(srctag);
403 if(used[id]) {
404 SRECT b;
405 copy = 1;
406 b = swf_GetDefineBBox(srctag);
407 swf_ExpandRect2(&objectbbox, &b);
409 } else
410 if ((((swf_isPlaceTag(srctag) && originalplaceobjects)
411 || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
412 (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
413 (tagused[tagnum]))
415 if(copy == 0)
416 reset = 1;
417 copy = 1;
419 if(srctag->id == ST_REMOVEOBJECT) {
420 if(!used[swf_GetPlaceID(srctag)])
421 copy = 0;
424 if(copy) {
425 TAG*ttag = (TAG*)malloc(sizeof(TAG));
426 desttag = swf_InsertTag(desttag, srctag->id);
427 desttag->len = desttag->memsize = srctag->len;
428 desttag->data = malloc(srctag->len);
429 memcpy(desttag->data, srctag->data, srctag->len);
430 if(movetozero && swf_isPlaceTag(desttag)) {
431 moveToZero(desttag);
433 if(reset)
434 copy = 0;
437 srctag = srctag->next;
438 tagnum ++;
440 if(!extractframes && !hollow) {
441 if(!originalplaceobjects && (extractids||extractname_id>=0)) {
442 int number = 0;
443 int id = 0;
444 int t;
445 TAG* objtag = 0;
446 SRECT bbox;
447 memset(&bbox, 0, sizeof(SRECT));
448 if(extractids) {
449 for(t=0;t<65536;t++) {
450 if(is_in_range(t, extractids)) {
451 id = t;
452 number++;
456 if(number>=2) {
457 printf("warning! You should use the -P when extracting multiple objects\n");
460 if(number == 1) {
461 /* if there is only one object, we will scale it.
462 So let's figure out its bounding box */
463 TAG*tag = swf->firstTag;
464 while(tag) {
465 if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) {
466 if(swf_GetDefineID(tag) == id)
467 bbox = swf_GetDefineBBox(tag);
468 objtag = tag;
470 tag = tag->next;
472 newswf.movieSize.xmin = 0;
473 newswf.movieSize.ymin = 0;
474 newswf.movieSize.xmax = 512*20;
475 newswf.movieSize.ymax = 512*20;
476 } else {
477 if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0)
478 newswf.movieSize = objectbbox;
481 if(extractname_id>=0) {
482 desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
483 swf_ObjectPlace(desttag, extractname_id, extractname_id, 0,0,extractname);
484 } else {
485 for(t=0;t<65536;t++) {
486 if(is_in_range(t, extractids)) {
487 MATRIX m;
488 desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
489 swf_GetMatrix(0, &m);
490 if(objtag) {
491 int width = bbox.xmax - bbox.xmin;
492 int height = bbox.ymax - bbox.ymin;
493 int max = width>height?width:height;
494 m.tx = -bbox.xmin;
495 m.ty = -bbox.ymin;
496 if(max) {
497 m.sx = (512*20*65536)/max;
498 m.sy = (512*20*65536)/max;
500 //newswf.movieSize = swf_TurnRect(newswf.movieSize, &m);
502 swf_ObjectPlace(desttag, t, t, &m,0,0);
507 desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
509 desttag = swf_InsertTag(desttag,ST_END);
511 f = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0644);
512 if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
513 close(f);
515 swf_FreeTags(&newswf); // cleanup
518 int isOfType(int t, TAG*tag)
520 int show = 0;
521 if(t == 0 && (tag->id == ST_DEFINESHAPE ||
522 tag->id == ST_DEFINESHAPE2 ||
523 tag->id == ST_DEFINESHAPE3)) {
524 show = 1;
526 if(t==1 && tag->id == ST_DEFINESPRITE) {
527 show = 1;
529 if(t == 2 && (tag->id == ST_DEFINEBITS ||
530 tag->id == ST_DEFINEBITSJPEG2 ||
531 tag->id == ST_DEFINEBITSJPEG3)) {
532 show = 1;
534 if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
535 tag->id == ST_DEFINEBITSLOSSLESS2)) {
536 show = 1;
538 if(t == 4 && (tag->id == ST_DEFINESOUND)) {
539 show = 1;
541 if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2 || tag->id == ST_DEFINEFONT3)) {
542 show = 1;
544 if (t== 6 && (tag->id == ST_DEFINEBINARY)) {
545 show = 1;
547 if (t== 7 && (tag->id == ST_DEFINESPRITE)) {
548 int wasFolded = swf_IsFolded(tag);
549 TAG *toFold = tag;
551 if(wasFolded)
552 swf_UnFoldSprite(tag);
554 while(tag->id != ST_END) {
555 tag = tag->next;
556 if(tag->id == ST_SOUNDSTREAMHEAD || tag->id == ST_SOUNDSTREAMHEAD2) {
557 show = 1;
558 break;
562 if(wasFolded)
563 swf_FoldSprite(toFold);
566 return show;
569 void listObjects(SWF*swf)
571 TAG*tag;
572 char first;
573 int t;
574 int frame = 0;
575 char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font", "Binary", "Embedded MP3"};
576 char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F","-b","-M"};
577 int mp3=0;
578 printf("Objects in file %s:\n",filename);
579 swf_FoldAll(swf);
580 for(t=0;t<sizeof(names)/sizeof(names[0]);t++) {
581 int nr=0;
582 int lastid = -2, lastprint=-1;
583 int follow=0;
584 tag = swf->firstTag;
585 first = 1;
586 while(tag) {
587 if(tag->id == ST_SOUNDSTREAMHEAD || tag->id == ST_SOUNDSTREAMHEAD2)
588 mp3 = 1;
589 if(isOfType(t,tag))
590 nr++;
591 tag = tag->next;
593 if(!nr)
594 continue;
596 printf(" [%s] %d %s%s: ID(s) ", options[t], nr, names[t], nr>1?"s":"");
598 tag = swf->firstTag;
599 while(tag) {
600 char text[80];
601 char show = isOfType(t,tag);
602 int id;
603 if(!show) {
604 tag = tag->next;
605 continue;
607 id = swf_GetDefineID(tag);
609 if(id == lastid+1) {
610 follow=1;
611 } else {
612 if(first || !follow) {
613 if(!first)
614 printf(", ");
615 printf("%d", id);
616 } else {
617 if(lastprint + 1 == lastid)
618 printf(", %d, %d", lastid, id);
619 else
620 printf("-%d, %d", lastid, id);
622 lastprint = id;
623 first = 0;
624 follow = 0;
626 lastid = id;
627 tag=tag->next;
629 if(follow) {
630 if(lastprint + 1 == lastid)
631 printf(", %d", lastid);
632 else
633 printf("-%d", lastid);
635 printf("\n");
638 if(frame)
639 printf(" [-f] %d Frames: ID(s) 0-%d\n", frame, frame);
640 else
641 printf(" [-f] 1 Frame: ID(s) 0\n");
643 if(mp3)
644 printf(" [-m] 1 MP3 Soundstream\n");
647 int handlefont(SWF*swf, TAG*tag)
649 SWFFONT* f=0;
650 U16 id;
651 char name[80];
652 char*filename = name;
653 int t;
655 id = swf_GetDefineID(tag);
656 prepare_name(name, sizeof(name), "font", "swf", id);
657 if(numextracts==1) {
658 filename = destfilename;
661 swf_FontExtract(swf, id, &f);
662 if(!f) {
663 if (!extractanyids) {
664 printf("Couldn't extract font %d\n", id);
666 return 0;
669 swf_WriteFont(f, filename);
670 swf_FontFree(f);
671 return 1;
674 static char has_jpegtables=0;
675 static U8*jpegtables = 0;
676 static int jpegtablessize = 0;
678 void handlejpegtables(TAG*tag)
680 if(tag->id == ST_JPEGTABLES) {
681 jpegtables = tag->data;
682 jpegtablessize = tag->len;
683 has_jpegtables = 1;
687 FILE* save_fopen(char* name, char* mode)
689 FILE*fi = fopen(name, mode);
690 if(!fi) {
691 fprintf(stderr, "Error: Couldn't open %s\n", name);
692 exit(1);
694 return fi;
697 int findjpegboundary(U8*data, int len)
699 int t;
700 int pos=-1;
701 for(t=0;t<len-4;t++) {
702 if(data[t ]==0xff &&
703 data[t+1]==0xd9 &&
704 data[t+2]==0xff &&
705 data[t+3]==0xd8) {
706 pos = t;
709 return pos;
712 /* extract jpeg data out of a tag */
713 int handlejpeg(TAG*tag)
715 char name[80];
716 char*filename = name;
717 FILE*fi;
719 if(tag->id != ST_DEFINEBITSJPEG3) {
720 prepare_name(name, sizeof(name), "pic", "jpg", GET16(tag->data));
721 if(numextracts==1) {
722 filename = destfilename;
723 if(!strcmp(filename,"output.swf"))
724 filename = "output.jpg";
726 } else {
727 prepare_name(name, sizeof(name), "pic", "png", GET16(tag->data));
728 if(numextracts==1) {
729 filename = destfilename;
730 if(!strcmp(filename,"output.swf"))
731 filename = "output.png";
735 /* swf jpeg images have two streams, which both start with ff d8 and
736 end with ff d9. The following code handles sorting the middle
737 <ff d9 ff d8> bytes out, so that one stream remains */
738 if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) {
739 fi = save_fopen(filename, "wb");
740 if(jpegtablessize>=2) {
741 fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
742 fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
743 } else {
744 fwrite(tag->data+2, tag->len-2, 1, fi);
746 fclose(fi);
748 else if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
749 int end = tag->len;
750 int pos = findjpegboundary(&tag->data[2], tag->len-2);
751 if(pos>=0) {
752 pos+=2;
753 fi = save_fopen(filename, "wb");
754 fwrite(&tag->data[2], pos-2, 1, fi);
755 fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
756 fclose(fi);
757 } else {
758 fi = save_fopen(filename, "wb");
759 fwrite(&tag->data[2], end-2, 1, fi);
760 fclose(fi);
763 else if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
764 U32 end = GET32(&tag->data[2])+6;
765 int pos = findjpegboundary(&tag->data[6], end);
766 if(end >= tag->len) {
767 msg("<error> zlib data out of bounds in definebitsjpeg3");
768 return 0;
770 if(pos) {
771 /* TODO: do we actually need this? */
772 memmove(&tag->data[pos], &tag->data[pos+4], end-(pos+4));
774 unsigned char*image;
775 unsigned width=0, height=0;
776 jpeg_load_from_mem(&tag->data[6], end-6, &image, &width, &height);
778 uLongf datalen = width*height;
779 Bytef *data = malloc(datalen);
781 int error = uncompress(data, &datalen, &tag->data[end], (uLong)(tag->len - end));
782 if(error != Z_OK) {
783 fprintf(stderr, "Zlib error %d\n", error);
784 return 0;
786 int t, size = width*height;
787 for(t=0;t<size;t++) {
788 image[t*4+0] = data[t];
790 free(data);
791 png_write(filename, image, width, height);
792 free(image);
794 else {
795 int id = GET16(tag->data);
796 if (!extractanyids) {
797 fprintf(stderr, "Object %d is not a JPEG picture!\n", id);
798 exit(1);
800 return 0;
802 return 1;
805 #ifdef _ZLIB_INCLUDED_
806 static U32 mycrc32;
808 static U32*crc32_table = 0;
809 static void make_crc32_table(void)
811 int t;
812 if(crc32_table)
813 return;
814 crc32_table = (U32*)malloc(1024);
816 for (t = 0; t < 256; t++) {
817 U32 c = t;
818 int s;
819 for (s = 0; s < 8; s++) {
820 c = (0xedb88320L*(c&1)) ^ (c >> 1);
822 crc32_table[t] = c;
825 static inline void png_write_byte(FILE*fi, U8 byte)
827 fwrite(&byte,1,1,fi);
828 mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
830 static void png_start_chunk(FILE*fi, char*type, int len)
832 U8 mytype[4]={0,0,0,0};
833 U32 mylen = BE_32_TO_NATIVE(len);
834 memcpy(mytype,type,strlen(type));
835 fwrite(&mylen, 4, 1, fi);
836 mycrc32=0xffffffff;
837 png_write_byte(fi,mytype[0]);
838 png_write_byte(fi,mytype[1]);
839 png_write_byte(fi,mytype[2]);
840 png_write_byte(fi,mytype[3]);
842 static void png_write_bytes(FILE*fi, U8*bytes, int len)
844 int t;
845 for(t=0;t<len;t++)
846 png_write_byte(fi,bytes[t]);
848 static void png_write_dword(FILE*fi, U32 dword)
850 png_write_byte(fi,dword>>24);
851 png_write_byte(fi,dword>>16);
852 png_write_byte(fi,dword>>8);
853 png_write_byte(fi,dword);
855 static void png_end_chunk(FILE*fi)
857 U32 tmp = BE_32_TO_NATIVE((mycrc32^0xffffffff));
858 fwrite(&tmp,4,1,fi);
862 /* extract a lossless image (png) out of a tag
863 This routine was originally meant to be a one-pager. I just
864 didn't know png is _that_ much fun. :) -mk
866 int handlelossless(TAG*tag)
868 char name[80];
869 char*filename = name;
870 FILE*fi;
871 int width, height;
872 int crc;
873 int id;
874 int t;
875 U8 bpp = 1;
876 U8 format;
877 U8 tmp;
878 Bytef* data=0;
879 U8* data2=0;
880 U8* data3=0;
881 uLongf datalen;
882 uLongf datalen2;
883 U32 datalen3;
884 U8 head[] = {137,80,78,71,13,10,26,10};
885 int cols;
886 char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
887 RGBA* palette;
888 int pos;
889 int error;
890 U32 tmp32;
892 make_crc32_table();
894 if(tag->id != ST_DEFINEBITSLOSSLESS &&
895 tag->id != ST_DEFINEBITSLOSSLESS2) {
896 int id = GET16(tag->data);
897 if (!extractanyids) {
898 fprintf(stderr, "Object %d is not a PNG picture!\n",id);
899 exit(1);
901 return 0;
904 id =swf_GetU16(tag);
905 format = swf_GetU8(tag);
906 if(format == 3) bpp = 8;
907 if(format == 4) bpp = 16;
908 if(format == 5) bpp = 32;
909 if(format!=3 && format!=5) {
910 if(format==4)
911 fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
912 else
913 fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
914 return 0;
916 width = swf_GetU16(tag);
917 height = swf_GetU16(tag);
918 if(format == 3) cols = swf_GetU8(tag) + 1;
919 // this is what format means according to the flash specification. (which is
920 // clearly wrong)
921 // if(format == 4) cols = swf_GetU16(tag) + 1;
922 // if(format == 5) cols = swf_GetU32(tag) + 1;
923 else cols = 0;
925 msg("<verbose> Width %d", width);
926 msg("<verbose> Height %d", height);
927 msg("<verbose> Format %d", format);
928 msg("<verbose> Cols %d", cols);
929 msg("<verbose> Bpp %d", bpp);
931 datalen = (width*height*bpp/8+cols*8);
932 do {
933 if(data)
934 free(data);
935 datalen+=4096;
936 data = malloc(datalen);
937 error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
938 } while(error == Z_BUF_ERROR);
939 if(error != Z_OK) {
940 fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
941 return 0;
943 msg("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
944 pos = 0;
945 datalen2 = datalen+16;
946 data2 = malloc(datalen2);
947 palette = (RGBA*)malloc(cols*sizeof(RGBA));
949 for(t=0;t<cols;t++) {
950 palette[t].r = data[pos++];
951 palette[t].g = data[pos++];
952 palette[t].b = data[pos++];
953 if(alpha) {
954 palette[t].a = data[pos++];
958 prepare_name(name, sizeof(name), "pic", "png", id);
959 if(numextracts==1) {
960 filename = destfilename;
961 if(!strcmp(filename,"output.swf"))
962 filename = "output.png";
964 fi = save_fopen(filename, "wb");
965 fwrite(head,sizeof(head),1,fi);
967 png_start_chunk(fi, "IHDR", 13);
968 png_write_dword(fi,width);
969 png_write_dword(fi,height);
970 png_write_byte(fi,8);
971 if(format == 3)
972 png_write_byte(fi,3); //indexed
973 else if(format == 5 && alpha==0)
974 png_write_byte(fi,2); //rgb
975 else if(format == 5 && alpha==1)
976 png_write_byte(fi,6); //rgba
977 else return 0;
979 png_write_byte(fi,0); //compression mode
980 png_write_byte(fi,0); //filter mode
981 png_write_byte(fi,0); //interlace mode
982 png_end_chunk(fi);
984 if(format == 3) {
985 png_start_chunk(fi, "PLTE", 768);
987 for(t=0;t<256;t++) {
988 png_write_byte(fi,palette[t].r);
989 png_write_byte(fi,palette[t].g);
990 png_write_byte(fi,palette[t].b);
992 png_end_chunk(fi);
994 if(alpha) {
995 /* write alpha palette */
996 png_start_chunk(fi, "tRNS", 256);
997 for(t=0;t<256;t++) {
998 png_write_byte(fi,palette[t].a);
1000 png_end_chunk(fi);
1004 int pos2 = 0;
1005 int x,y;
1006 int srcwidth = width * (bpp/8);
1007 datalen3 = (width*4+5)*height;
1008 data3 = (U8*)malloc(datalen3);
1009 for(y=0;y<height;y++)
1011 data3[pos2++]=0; //filter type
1012 if(bpp==32) {
1013 if(!alpha) {
1014 // 32 bit to 24 bit "conversion"
1015 for(x=0;x<width;x++) {
1016 data3[pos2++]=data[pos+1];
1017 data3[pos2++]=data[pos+2];
1018 data3[pos2++]=data[pos+3];
1019 pos+=4; //ignore padding byte
1021 } else {
1022 for(x=0;x<width;x++) {
1023 data3[pos2++]=data[pos+1];
1024 data3[pos2++]=data[pos+2];
1025 data3[pos2++]=data[pos+3];
1026 data3[pos2++]=data[pos+0]; //alpha
1027 pos+=4;
1031 else {
1032 for(x=0;x<srcwidth;x++)
1033 data3[pos2++]=data[pos++];
1036 pos+=((srcwidth+3)&~3)-srcwidth; //align
1038 datalen3=pos2;
1041 if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
1042 fprintf(stderr, "zlib error in pic %d\n", id);
1043 return 0;
1045 msg("<verbose> Compressed data is %d bytes", datalen2);
1046 png_start_chunk(fi, "IDAT", datalen2);
1047 png_write_bytes(fi,data2,datalen2);
1048 png_end_chunk(fi);
1049 png_start_chunk(fi, "IEND", 0);
1050 png_end_chunk(fi);
1052 free(data);
1053 free(data2);
1054 free(data3);
1055 return 1;
1057 #endif
1059 static FILE*mp3file=0;
1060 void handlesoundstream(TAG*tag)
1062 char*filename = "output.mp3";
1063 if(numextracts==1) {
1064 filename = destfilename;
1065 if(!strcmp(filename,"output.swf"))
1066 filename = "output.mp3";
1068 switch(tag->id) {
1069 case ST_SOUNDSTREAMHEAD:
1070 if((tag->data[1]&0x30) == 0x20) { //mp3 compression
1071 mp3file = fopen(filename, "wb");
1072 msg("<notice> Writing mp3 data to %s",filename);
1074 else
1075 msg("<error> Soundstream is not mp3");
1076 break;
1077 case ST_SOUNDSTREAMHEAD2:
1078 if((tag->data[1]&0x30) == 0x20) {//mp3 compression
1079 mp3file = fopen(filename, "wb");
1080 msg("<notice> Writing mp3 data to %s",filename);
1082 else
1083 msg("<error> Soundstream is not mp3 (2)");
1084 break;
1085 case ST_SOUNDSTREAMBLOCK:
1086 if(mp3file)
1087 fwrite(&tag->data[4],tag->len-4,1,mp3file);
1088 break;
1092 int handledefinesound(TAG*tag)
1094 U8 flags;
1095 U32 samples;
1096 char buf[128];
1097 char*filename = buf;
1098 FILE*fi;
1099 char*extension = 0;
1100 int format;
1101 U16 id;
1102 int rate,bits,stereo;
1103 char*rates[] = {"5500","11025","22050","44100"};
1104 id = swf_GetU16(tag); //id
1106 flags = swf_GetU8(tag);
1107 format = flags>>4;
1108 rate = (flags>>2)&3;
1109 bits = flags&2?16:8;
1110 stereo = flags&1;
1112 samples = swf_GetU32(tag);
1114 extension = "raw";
1116 if(format == 2) { // mp3
1117 swf_GetU16(tag); //numsamples_seek
1118 extension = "mp3";
1119 } else if(format == 0) { // raw
1120 printf("Sound is RAW, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1121 // TODO: convert to WAV
1122 extension = "raw";
1123 } else if(format == 1) { // adpcm
1124 printf("Sound is ADPCM, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1125 extension = "adpcm";
1126 } else {
1127 return 0;
1129 prepare_name(buf, sizeof(buf), "sound", extension, id);
1130 if(numextracts==1) {
1131 filename = destfilename;
1132 if(!strcmp(filename,"output.swf")) {
1133 sprintf(buf, "output.%s", extension);
1134 filename = buf;
1137 fi = save_fopen(filename, "wb");
1138 fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi);
1139 fclose(fi);
1140 return 1;
1143 int handlebinary(TAG*tag) {
1144 FILE *fout = NULL;
1145 char buf[100];
1146 char *filename = buf;
1147 int len = tag->memsize;
1148 int dx = 6; // offset to binary data
1149 if (tag->id!=ST_DEFINEBINARY) {
1150 if (!extractanyids) {
1151 fprintf(stderr, "Object %d is not a binary entity!\n",
1152 GET16(tag->data));
1154 return 0;
1156 prepare_name(buf, sizeof(buf), "binary", "bin", GET16(tag->data));
1157 if(numextracts==1) {
1158 filename = destfilename;
1159 if(!strcmp(filename,"output.swf")) {
1160 sprintf(buf, "output.bin");
1161 filename = buf;
1164 fout = fopen(filename, "wb");
1165 fwrite(tag->data+dx,len-dx,1,fout);
1166 fclose(fout);
1167 return 1;
1170 int handleembeddedmp3(TAG*tag) {
1171 int wasFolded;
1172 TAG *toFold;
1174 if (tag->id!=ST_DEFINESPRITE) {
1175 if (!extractanyids) {
1176 fprintf(stderr, "Object %d is not a sprite entity!\n",
1177 GET16(tag->data));
1179 return 0;
1182 wasFolded = swf_IsFolded(tag);
1183 toFold = tag;
1185 if(wasFolded)
1186 swf_UnFoldSprite(tag);
1188 while(tag->id != ST_END) {
1189 tag = tag->next;
1190 if(tag->id == ST_SOUNDSTREAMHEAD ||
1191 tag->id == ST_SOUNDSTREAMHEAD2 ||
1192 tag->id == ST_SOUNDSTREAMBLOCK) {
1193 handlesoundstream(tag);
1197 if(wasFolded)
1198 swf_FoldSprite(toFold);
1200 return 1;
1203 int main (int argc,char ** argv)
1205 TAG*tag;
1206 SWF swf;
1207 int f;
1208 int found = 0;
1209 int frame = 0;
1210 int tagnum = 0;
1211 char depths[65536];
1212 char listavailable = 0;
1213 processargs(argc, argv);
1215 if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
1216 && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids
1217 && !extractanyids && !extractmp3ids)
1218 listavailable = 1;
1220 if(!originalplaceobjects && movetozero) {
1221 fprintf(stderr, "Error: -0 (--movetozero) can only be used in conjunction with -P (--placeobject)\n");
1222 return 0;
1225 if(!filename)
1227 fprintf(stderr, "You must supply a filename.\n");
1228 return 1;
1230 initLog(0,-1,0,0,-1, verbose);
1232 f = open(filename,O_RDONLY|O_BINARY);
1234 if (f<0)
1236 perror("Couldn't open file: ");
1237 exit(1);
1239 if (swf_ReadSWF(f,&swf) < 0)
1241 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
1242 close(f);
1243 exit(1);
1245 close(f);
1247 if(listavailable) {
1248 listObjects(&swf);
1249 swf_FreeTags(&swf);
1250 return 0;
1253 tag = swf.firstTag;
1254 tagnum = 0;
1255 while(tag) {
1256 tagnum ++;
1257 tag = tag->next;
1260 tagused = (char*)malloc(tagnum);
1261 memset(tagused, 0, tagnum);
1262 memset(used, 0, 65536);
1263 memset(depths, 0, 65536);
1265 tag = swf.firstTag;
1266 tagnum = 0;
1267 while(tag) {
1268 if(swf_isAllowedSpriteTag(tag)) {
1269 int write = 0;
1270 if(extractframes && is_in_range(frame, extractframes)) {
1271 write = 1;
1272 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
1273 depths[swf_GetDepth(tag)] = 1;
1275 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
1276 int depth = swf_GetDepth(tag);
1277 if(!depths[depth])
1278 write = 0;
1279 depths[swf_GetDepth(tag)] = 0;
1281 } else {
1282 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) &&
1283 (depths[swf_GetDepth(tag)]) && hollow) {
1284 write = 1;
1285 depths[swf_GetDepth(tag)] = 0;
1288 if(write) {
1289 enumerateIDs(tag, idcallback);
1290 found = 1;
1291 tagused[tagnum] = 1;
1295 if(tag->id == ST_SOUNDSTREAMHEAD ||
1296 tag->id == ST_SOUNDSTREAMHEAD2 ||
1297 tag->id == ST_SOUNDSTREAMBLOCK) {
1298 if(extractmp3)
1299 handlesoundstream(tag);
1302 if(tag->id == ST_JPEGTABLES) {
1303 handlejpegtables(tag);
1306 if(swf_isDefiningTag(tag)) {
1307 int id = swf_GetDefineID(tag);
1308 tags[id] = tag;
1309 if(extractids && is_in_range(id, extractids)) {
1310 used[id] = 5;
1311 found = 1;
1313 if(extractfontids && is_in_range(id, extractfontids)) {
1314 handlefont(&swf, tag);
1316 if(extractjpegids && is_in_range(id, extractjpegids)) {
1317 handlejpeg(tag);
1319 if(extractsoundids && is_in_range(id, extractsoundids)) {
1320 handledefinesound(tag);
1322 if(extractmp3ids && is_in_range(id, extractmp3ids)) {
1323 handleembeddedmp3(tag);
1325 if(extractbinaryids && is_in_range(id, extractbinaryids)) {
1326 handlebinary(tag);
1328 #ifdef _ZLIB_INCLUDED_
1329 if(extractpngids && is_in_range(id, extractpngids)) {
1330 handlelossless(tag);
1332 #endif
1333 if(extractanyids && is_in_range(id, extractanyids)) {
1334 if (handlefont(&swf,tag)) {
1335 // pass
1336 } else if (handlejpeg(tag)) {
1337 // pass
1338 } else if (handlebinary(tag)) {
1339 // pass
1340 #ifdef _ZLIB_INCLUDED_
1341 } else if (handlelossless(tag)) {
1342 // pass
1343 #endif
1344 } else if (handledefinesound(tag)) {
1345 // Not sure if sound code checks carefully for type.
1346 // pass
1347 } else if (handleembeddedmp3(tag)) {
1348 // pass
1349 } else {
1350 printf("#%d not processed\n", id);
1354 else if (tag->id == ST_SETBACKGROUNDCOLOR) {
1355 mainr = tag->data[0];
1356 maing = tag->data[1];
1357 mainb = tag->data[2];
1359 else if(swf_isPlaceTag(tag) && tag->id != ST_PLACEOBJECT ) {
1360 char*name = swf_GetName(tag);
1361 if(name && extractname && !strcmp(name, extractname)) {
1362 int id = swf_GetPlaceID(tag);
1363 used[id] = 5;
1364 found = 1;
1365 if(originalplaceobjects) {
1366 tagused[tagnum] = 1;
1368 depths[swf_GetDepth(tag)] = 1;
1369 extractname_id = id;
1372 else if(tag->id == ST_SHOWFRAME) {
1373 frame ++;
1374 if(hollow) {
1375 tagused[tagnum] = 1;
1376 found = 1;
1380 if(tag->id == ST_DEFINESPRITE) {
1381 while(tag->id != ST_END) {
1382 tag = tag->next;
1383 tagnum ++;
1386 tag = tag->next;
1387 tagnum ++;
1389 if (found)
1390 extractTag(&swf, destfilename);
1392 if(mp3file) {
1393 fclose(mp3file);
1394 } else {
1395 if(extractmp3) {
1396 msg("<error> Didn't find a soundstream in file");
1400 swf_FreeTags(&swf);
1401 return 0;