multiply overflow fixes
[swftools.git] / src / swfextract.c
1 /* swfextract.c
2    Allows to extract parts of the swf into a new file.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7  
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.
12
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.
17
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 */
21
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
36
37 char * filename = 0;
38 char * destfilename = "output.swf";
39 int verbose = 3;
40
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* extractbinaryids = 0;
48 char* extractanyids = 0;
49 char extractmp3 = 0;
50
51 char* extractname = 0;
52
53 char hollow = 0;
54 char originalplaceobjects = 0;
55 char movetozero = 0;
56
57 int numextracts = 0;
58 char *outputformat = NULL;
59
60 struct options_t options[] =
61 {
62  {"o","output"},
63  {"w","hollow"},
64  {"v","verbose"},
65  {"i","id"},
66  {"j","jpegs"},
67  {"p","pngs"},
68  {"a","any"},
69  {"P","placeobject"},
70  {"0","movetozero"},
71  {"m","mp3"},
72  {"s","sound"},
73  {"n","name"},
74  {"f","frame"},
75  {"F","font"},
76  {"V","version"},
77  {"b","binary"},
78  {"O","outputformat"},
79  {0,0}
80 };
81
82
83 int args_callback_option(char*name,char*val)
84 {
85     if(!strcmp(name, "V")) {
86         printf("swfextract - part of %s %s\n", PACKAGE, VERSION);
87         exit(0);
88     } 
89     else if(!strcmp(name, "o")) {
90         destfilename = val;
91         return 1;
92     } 
93     else if(!strcmp(name, "i")) {
94         extractids = val;
95         numextracts++;
96         if(extractname) {
97             fprintf(stderr, "You can only supply either name or id\n");
98             exit(1);
99         }
100         return 1;
101     } 
102     else if(!strcmp(name, "n")) {
103         extractname = val;
104         numextracts++;
105         if(extractids) {
106             fprintf(stderr, "You can only supply either name or id\n");
107             exit(1);
108         }
109         return 1;
110     } 
111     else if(!strcmp(name, "v")) {
112         verbose ++;
113         return 0;
114     } 
115     else if(!strcmp(name, "m")) {
116         extractmp3 = 1;
117         numextracts++;
118         return 0;
119     }
120     else if(!strcmp(name, "j")) {
121         if(extractjpegids) {
122             fprintf(stderr, "Only one --jpegs argument is allowed. (Try to use a range, e.g. -j 1,2,3)\n");
123             exit(1);
124         }
125         /* TODO: count number of IDs in val range */
126         numextracts++;
127         extractjpegids = val;
128         return 1;
129     } 
130     else if(!strcmp(name, "F")) {
131         if(extractfontids) {
132             fprintf(stderr, "Only one --font argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
133             exit(1);
134         }
135         numextracts++;
136         extractfontids = val;
137         return 1;
138     } 
139     else if(!strcmp(name, "s")) {
140         if(extractsoundids) {
141             fprintf(stderr, "Only one --sound argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
142             exit(1);
143         }
144         numextracts++;
145         extractsoundids = val;
146         return 1;
147     } 
148     else if(!strcmp(name, "b")) {
149         if(extractbinaryids) {
150             fprintf(stderr, "Only one --binary argument is allowed. (Try to use a range, e.g. -s 1,2,3)\n");
151             exit(1);
152         }
153         numextracts++;
154         extractbinaryids = val;
155         return 1;
156     } 
157 #ifdef _ZLIB_INCLUDED_
158     else if(!strcmp(name, "p")) {
159         if(extractpngids) {
160             fprintf(stderr, "Only one --png argument is allowed. (Try to use a range, e.g. -p 1,2,3)\n");
161             exit(1);
162         }
163         numextracts++;
164         extractpngids = val;
165         return 1;
166     } 
167 #endif
168     else if(!strcmp(name, "a")) {
169         numextracts++;
170         extractanyids = val;
171         return 1;
172     }
173     else if(!strcmp(name, "f")) {
174         numextracts++;
175         extractframes = val;
176         return 1;
177     }
178     else if(!strcmp(name, "P")) {
179         originalplaceobjects = 1;
180         return 0;
181     }
182     else if(!strcmp(name, "0")) {
183         movetozero = 1;
184         return 0;
185     }
186     else if(!strcmp(name, "w")) {
187         hollow = 1;
188         return 0;
189     }
190     else if (!strcmp(name, "O")) {
191       outputformat = val;
192         return 1;
193     }
194     else {
195         printf("Unknown option: -%s\n", name);
196         exit(1);
197     }
198
199     return 0;
200 }
201 int args_callback_longoption(char*name,char*val)
202 {
203     return args_long2shortoption(options, name, val);
204 }
205 void args_callback_usage(char*name)
206 {    
207     printf("Usage: %s [-v] [-n name] [-ijf ids] file.swf\n", name);
208     printf("\t-v , --verbose\t\t\t Be more verbose\n");
209     printf("\t-o , --output filename\t\t set output filename\n");
210     printf("\t-V , --version\t\t\t Print program version and exit\n\n");
211     printf("SWF Subelement extraction:\n");
212     printf("\t-n , --name name\t\t instance name of the object (SWF Define) to extract\n");
213     printf("\t-i , --id ID\t\t\t ID of the object, shape or movieclip to extract\n");
214     printf("\t-f , --frame frames\t\t frame numbers to extract\n");
215     printf("\t-w , --hollow\t\t\t hollow mode: don't remove empty frames\n"); 
216     printf("\t             \t\t\t (use with -f)\n");
217     printf("\t-P , --placeobject\t\t\t Insert original placeobject into output file\n"); 
218     printf("\t             \t\t\t (use with -i)\n");
219     printf("SWF Font/Text extraction:\n");
220     printf("\t-F , --font ID\t\t\t Extract font(s)\n");
221     printf("Picture extraction:\n");
222     printf("\t-j , --jpeg ID\t\t\t Extract JPEG picture(s)\n");
223 #ifdef _ZLIB_INCLUDED_
224     printf("\t-p , --pngs ID\t\t\t Extract PNG picture(s)\n");
225 #endif
226     printf("\n");
227     printf("Sound extraction:\n");
228     printf("\t-m , --mp3\t\t\t Extract main mp3 stream\n");
229     printf("\t-s , --sound ID\t\t\t Extract Sound(s)\n");
230 }
231 int args_callback_command(char*name,char*val)
232 {
233     if(filename) {
234         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
235                  filename, name);
236     }
237     filename = name;
238     return 0;
239 }
240
241 void prepare_name(char *buf, size_t len, const char *prefix, 
242                   const char *suffix, int idx) {
243   if (outputformat!=NULL) {
244     // override default file name formatting
245     // make sure single-file behavior is not used
246     numextracts = -1;
247     // Other parts of codebase use vsnprintf, so I assume snprintf
248     // is available on all platforms that swftools currently works on.
249     // We need to check for buffer overflows now that the user is 
250     // supplying the format string.
251     snprintf(buf,len,outputformat,idx,suffix);
252   } else {
253     // use default file name formatting, unchanged
254     sprintf(buf,"%s%d.%s",prefix,idx,suffix);
255   }
256 }
257
258 U8 mainr,maing,mainb;
259 /* 1 = used, not expanded,
260    3 = used, expanded
261    5 = wanted, not expanded
262    7 = wanted, expanded
263  */
264 char used[65536];
265 TAG*tags[65536];
266 int changed;
267 char * tagused;
268 int extractname_id = -1;
269
270 void idcallback(void*data)
271 {
272     if(!(used[GET16(data)]&1)) {
273         changed = 1;
274         used[GET16(data)] |= 1;
275     }
276 }
277
278 void enumerateIDs(TAG*tag, void(*callback)(void*))
279 {
280 /*    U8*data;
281     int len = tag->len;
282     if(tag->len>=64) {
283         len += 6;
284         data = (U8*)malloc(len);
285         PUT16(data, (tag->id<<6)+63);
286         *(U8*)&data[2] = tag->len;
287         *(U8*)&data[3] = tag->len>>8;
288         *(U8*)&data[4] = tag->len>>16;
289         *(U8*)&data[5] = tag->len>>24;
290         memcpy(&data[6], tag->data, tag->len);
291     } else {
292         len += 2;
293         data = (U8*)malloc(len);
294         PUT16(data, (tag->id<<6)+tag->len);
295         memcpy(&data[2], tag->data, tag->len);
296     }
297     map_ids_mem(data, len, callback);
298  */
299     int num = swf_GetNumUsedIDs(tag);
300     int *ptr = malloc(sizeof(int)*num);
301     int t;
302     swf_GetUsedIDs(tag, ptr);
303     for(t=0;t<num;t++)
304         callback(&tag->data[ptr[t]]);
305 }
306
307 void moveToZero(TAG*tag)
308 {
309     if(!swf_isPlaceTag(tag))
310         return;
311     SWFPLACEOBJECT obj;
312     swf_GetPlaceObject(tag, &obj);
313     obj.matrix.tx = 0;
314     obj.matrix.ty = 0;
315     swf_ResetTag(tag, tag->id);
316     swf_SetPlaceObject(tag, &obj);
317 }
318
319 void extractTag(SWF*swf, char*filename)
320 {
321     SWF newswf;
322     TAG*desttag;
323     TAG*srctag;
324     RGBA rgb;
325     SRECT objectbbox;
326     char sprite;
327     int f;
328     int t;
329     int tagnum;
330     int copy = 0;
331     memset(&newswf,0x00,sizeof(SWF));        // set global movie parameters
332
333     newswf.fileVersion    = swf->fileVersion;
334     newswf.frameRate      = swf->frameRate;
335     newswf.movieSize      = swf->movieSize;
336     if(movetozero && originalplaceobjects) {
337         newswf.movieSize.xmax = swf->movieSize.xmax - swf->movieSize.xmin;
338         newswf.movieSize.ymax = swf->movieSize.ymax - swf->movieSize.ymin;
339         newswf.movieSize.xmin = 0;
340         newswf.movieSize.ymin = 0;
341     }
342                 
343     newswf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
344     desttag = newswf.firstTag;
345     rgb.r = mainr;
346     rgb.g = maing;
347     rgb.b = mainb;
348     swf_SetRGB(desttag,&rgb);
349
350     swf_GetRect(0, &objectbbox);
351
352     do {
353        changed = 0;
354        for(t=0;t<65536;t++) {
355            if(used[t] && !(used[t]&2)) {
356              if(tags[t]==0) {
357                  msg("<warning> ID %d is referenced, but never defined.", t);
358              } else if(tags[t]->id==ST_DEFINESPRITE) {
359                  TAG*tag = tags[t];
360                  while(tag->id != ST_END)
361                  {
362                      enumerateIDs(tag, idcallback);
363                      tag = tag->next;
364                  }
365              }
366              else 
367                enumerateIDs(tags[t], idcallback);
368              used[t] |= 2;
369            }
370        }
371     }
372     while(changed);
373
374     srctag = swf->firstTag;
375     tagnum = 0;
376     sprite = 0;
377     while(srctag && (srctag->id || sprite)) {
378         int reset = 0;
379         if(!sprite) {
380             copy = 0;
381         }
382         if(srctag->id == ST_END) {
383             sprite = 0;
384         }
385         if(srctag->id == ST_DEFINESPRITE)
386             sprite = 1;
387         if(srctag->id == ST_JPEGTABLES)
388             copy = 1;
389         if(swf_isDefiningTag(srctag)) {
390             int id = swf_GetDefineID(srctag);
391             if(used[id])  {
392                 SRECT b;
393                 copy = 1;
394                 b = swf_GetDefineBBox(srctag);
395                 swf_ExpandRect2(&objectbbox, &b);
396             }
397         } else 
398         if ((((swf_isPlaceTag(srctag) && originalplaceobjects)
399               || srctag->id == ST_STARTSOUND) && (used[swf_GetPlaceID(srctag)]&4) ) ||
400               (swf_isPseudoDefiningTag(srctag) && used[swf_GetDefineID(srctag)]) ||
401               (tagused[tagnum])) 
402         {
403                 if(copy == 0)
404                     reset = 1;
405                 copy = 1;
406         } 
407         if(srctag->id == ST_REMOVEOBJECT) {
408             if(!used[swf_GetPlaceID(srctag)])
409                 copy = 0;
410         }
411
412         if(copy) {
413             TAG*ttag = (TAG*)malloc(sizeof(TAG));
414             desttag = swf_InsertTag(desttag, srctag->id);
415             desttag->len = desttag->memsize = srctag->len;
416             desttag->data = malloc(srctag->len);
417             memcpy(desttag->data, srctag->data, srctag->len);
418             if(movetozero && swf_isPlaceTag(desttag)) {
419                 moveToZero(desttag);
420             }
421             if(reset)
422                 copy = 0;
423         }
424         
425         srctag = srctag->next;
426         tagnum ++;
427     }
428     if(!extractframes && !hollow) {
429         if(!originalplaceobjects && (extractids||extractname_id>=0)) {
430             int number = 0;
431             int id = 0;
432             int t;
433             TAG* objtag = 0;
434             SRECT bbox;
435             memset(&bbox, 0, sizeof(SRECT));
436             if(extractids) {
437                 for(t=0;t<65536;t++) {
438                     if(is_in_range(t, extractids)) {
439                         id = t;
440                         number++;
441                     }
442                 }
443             }
444             if(number>=2) {
445                 printf("warning! You should use the -P when extracting multiple objects\n");
446             }
447
448             if(number == 1) {
449                 /* if there is only one object, we will scale it.
450                    So let's figure out its bounding box */
451                 TAG*tag = swf->firstTag;
452                 while(tag) {
453                     if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) {
454                         if(swf_GetDefineID(tag) == id)
455                             bbox = swf_GetDefineBBox(tag);
456                         objtag = tag;
457                     }
458                     tag = tag->next;
459                 }
460                 newswf.movieSize.xmin = 0;
461                 newswf.movieSize.ymin = 0;
462                 newswf.movieSize.xmax = 512*20;
463                 newswf.movieSize.ymax = 512*20;
464             } else {
465                 if((objectbbox.xmin|objectbbox.ymin|objectbbox.xmax|objectbbox.ymax)!=0)
466                     newswf.movieSize = objectbbox;
467             }
468
469             if(extractname_id>=0) {
470                 desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
471                 swf_ObjectPlace(desttag, extractname_id, extractname_id, 0,0,extractname);
472             } else {
473                 for(t=0;t<65536;t++) {
474                     if(is_in_range(t, extractids)) {
475                         MATRIX m;
476                         desttag = swf_InsertTag(desttag, ST_PLACEOBJECT2);
477                         swf_GetMatrix(0, &m);
478                         if(objtag) {
479                             int width = bbox.xmax - bbox.xmin;
480                             int height = bbox.ymax - bbox.ymin;
481                             int max = width>height?width:height;
482                             m.tx = -bbox.xmin;
483                             m.ty = -bbox.ymin;
484                             if(max) {
485                                 m.sx = (512*20*65536)/max;
486                                 m.sy = (512*20*65536)/max;
487                             }
488                             //newswf.movieSize = swf_TurnRect(newswf.movieSize, &m);
489                         }
490                         swf_ObjectPlace(desttag, t, t, &m,0,0);
491                     }
492                 }
493             }
494         }
495         desttag = swf_InsertTag(desttag,ST_SHOWFRAME);
496     }
497     desttag = swf_InsertTag(desttag,ST_END);
498     
499     f = open(filename, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0644);
500     if FAILED(swf_WriteSWF(f,&newswf)) fprintf(stderr,"WriteSWF() failed.\n");
501     close(f);
502
503     swf_FreeTags(&newswf);                       // cleanup
504 }
505
506 int isOfType(int t, TAG*tag)
507 {
508     int show = 0;
509     if(t == 0 && (tag->id == ST_DEFINESHAPE ||
510         tag->id == ST_DEFINESHAPE2 ||
511         tag->id == ST_DEFINESHAPE3)) {
512         show = 1;
513     }
514     if(t==1 && tag->id == ST_DEFINESPRITE) {
515         show = 1;
516     }
517     if(t == 2 && (tag->id == ST_DEFINEBITS ||
518         tag->id == ST_DEFINEBITSJPEG2 ||
519         tag->id == ST_DEFINEBITSJPEG3)) {
520         show = 1;
521     }
522     if(t == 3 && (tag->id == ST_DEFINEBITSLOSSLESS ||
523         tag->id == ST_DEFINEBITSLOSSLESS2)) {
524         show = 1;
525     }
526     if(t == 4 && (tag->id == ST_DEFINESOUND)) {
527         show = 1;
528     }
529     if(t == 5 && (tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2 || tag->id == ST_DEFINEFONT3)) {
530         show = 1;
531     }
532     if (t== 6 && (tag->id == ST_DEFINEBINARY)) {
533         show = 1;
534     }
535     return show;
536 }
537
538 void listObjects(SWF*swf)
539 {
540     TAG*tag;
541     char first;
542     int t;
543     int frame = 0;
544     char*names[] = {"Shape", "MovieClip", "JPEG", "PNG", "Sound", "Font", "Binary"};
545     char*options[] = {"-i", "-i", "-j", "-p", "-s", "-F","-b"};
546     int mp3=0;
547     printf("Objects in file %s:\n",filename);
548     swf_FoldAll(swf);
549     for(t=0;t<sizeof(names)/sizeof(names[0]);t++) {
550         int nr=0;
551         int lastid = -2, lastprint=-1;
552         int follow=0;
553         tag = swf->firstTag;
554         first = 1;
555         while(tag) {
556             if(tag->id == ST_SOUNDSTREAMHEAD || tag->id == ST_SOUNDSTREAMHEAD2)
557                 mp3 = 1;
558             if(isOfType(t,tag))
559                 nr++;
560             tag = tag->next;
561         }
562         if(!nr)
563             continue;
564         
565         printf(" [%s] %d %s%s: ID(s) ", options[t], nr, names[t], nr>1?"s":"");
566
567         tag = swf->firstTag;
568         while(tag) {
569             char text[80];
570             char show = isOfType(t,tag);
571             int id;
572             if(!show) {
573                 tag = tag->next;
574                 continue;
575             }
576             id = swf_GetDefineID(tag);
577
578             if(id == lastid+1) {
579                 follow=1;
580             } else {
581                 if(first || !follow) {
582                     if(!first)
583                         printf(", ");
584                     printf("%d", id);
585                 } else {
586                     if(lastprint + 1 == lastid) 
587                         printf(", %d, %d", lastid, id);
588                     else
589                         printf("-%d, %d", lastid, id);
590                 }
591                 lastprint = id;
592                 first = 0;
593                 follow = 0;
594             }
595             lastid = id;
596             tag=tag->next;
597         }
598         if(follow) {
599             if(lastprint + 1 == lastid)
600                 printf(", %d", lastid);
601             else
602                 printf("-%d", lastid);
603         }
604         printf("\n");
605     }
606
607     if(frame)
608         printf(" [-f] %d Frames: ID(s) 0-%d\n", frame, frame);
609     else
610         printf(" [-f] 1 Frame: ID(s) 0\n");
611
612     if(mp3)
613         printf(" [-m] 1 MP3 Soundstream\n");
614 }
615
616 int handlefont(SWF*swf, TAG*tag)
617 {
618     SWFFONT* f=0;
619     U16 id;
620     char name[80];
621     char*filename = name;
622     int t;
623
624     id = swf_GetDefineID(tag);
625     prepare_name(name, sizeof(name), "font", "swf", id);
626     if(numextracts==1) {
627         filename = destfilename;
628     }
629
630     swf_FontExtract(swf, id, &f);
631     if(!f) {
632         if (!extractanyids) {
633            printf("Couldn't extract font %d\n", id);
634         }
635         return 0;
636     }
637
638     swf_WriteFont(f, filename);
639     swf_FontFree(f);
640     return 1;
641 }
642
643 static char has_jpegtables=0;
644 static U8*jpegtables = 0;
645 static int jpegtablessize = 0;
646
647 void handlejpegtables(TAG*tag)
648 {
649     if(tag->id == ST_JPEGTABLES) {
650         jpegtables = tag->data;
651         jpegtablessize = tag->len;
652         has_jpegtables = 1;
653     }
654 }
655
656 FILE* save_fopen(char* name, char* mode)
657 {
658     FILE*fi = fopen(name, mode);
659     if(!fi) {
660         fprintf(stderr, "Error: Couldn't open %s\n", name);
661         exit(1);
662     }
663     return fi;
664 }
665
666 int findjpegboundary(U8*data, int len)
667 {
668     int t;
669     int pos=-1;
670     for(t=0;t<len-4;t++) {
671         if(data[t  ]==0xff &&
672            data[t+1]==0xd9 &&
673            data[t+2]==0xff &&
674            data[t+3]==0xd8) {
675             pos = t;
676         }
677     }
678     return pos;
679 }
680
681 /* extract jpeg data out of a tag */
682 int handlejpeg(TAG*tag)
683 {
684     char name[80];
685     char*filename = name;
686     FILE*fi;
687    
688     if(tag->id != ST_DEFINEBITSJPEG3) {
689         prepare_name(name, sizeof(name), "pic", "jpg", GET16(tag->data));
690         if(numextracts==1) {
691             filename = destfilename;
692             if(!strcmp(filename,"output.swf"))
693                 filename = "output.jpg";
694         }
695     } else {
696         prepare_name(name, sizeof(name), "pic", "png", GET16(tag->data));
697         if(numextracts==1) {
698             filename = destfilename;
699             if(!strcmp(filename,"output.swf"))
700                 filename = "output.png";
701         }
702     }
703
704     /* swf jpeg images have two streams, which both start with ff d8 and
705        end with ff d9. The following code handles sorting the middle
706        <ff d9 ff d8> bytes out, so that one stream remains */
707     if(tag->id == ST_DEFINEBITSJPEG && tag->len>2 && has_jpegtables) {
708         fi = save_fopen(filename, "wb");
709         if(jpegtablessize>=2) {
710             fwrite(jpegtables, 1, jpegtablessize-2, fi); //don't write end tag (ff,d8)
711             fwrite(&tag->data[2+2], tag->len-2-2, 1, fi); //don't write start tag (ff,d9)
712         } else {
713             fwrite(tag->data+2, tag->len-2, 1, fi);
714         }
715         fclose(fi);
716     }
717     else if(tag->id == ST_DEFINEBITSJPEG2 && tag->len>2) {
718         int end = tag->len;
719         int pos = findjpegboundary(&tag->data[2], tag->len-2);
720         if(pos>=0) {
721             pos+=2;
722             fi = save_fopen(filename, "wb");
723             fwrite(&tag->data[2], pos-2, 1, fi);
724             fwrite(&tag->data[pos+4], end-(pos+4), 1, fi);
725             fclose(fi);
726         } else {
727             fi = save_fopen(filename, "wb");
728             fwrite(&tag->data[2], end-2, 1, fi);
729             fclose(fi);
730         }
731     }
732     else if(tag->id == ST_DEFINEBITSJPEG3 && tag->len>6) {
733         U32 end = GET32(&tag->data[2])+6;
734         int pos = findjpegboundary(&tag->data[6], end);
735         if(end >= tag->len) {
736             msg("<error> zlib data out of bounds in definebitsjpeg3");
737             return 0;
738         }
739         if(pos) {
740             /* TODO: do we actually need this? */
741             memmove(&tag->data[pos], &tag->data[pos+4], end-(pos+4));
742         }
743         unsigned char*image;
744         unsigned width=0, height=0;
745         jpeg_load_from_mem(&tag->data[6], end-6, &image, &width, &height);
746
747         uLongf datalen = width*height;
748         Bytef *data = malloc(datalen);
749
750         int error = uncompress(data, &datalen, &tag->data[end], (uLong)(tag->len - end));
751         if(error != Z_OK) {
752           fprintf(stderr, "Zlib error %d\n", error);
753           return 0;
754         }
755         int t, size = width*height;
756         for(t=0;t<size;t++) {
757             image[t*4+0] = data[t];
758         }
759         free(data);
760         writePNG(filename, image, width, height);
761         free(image);
762     }
763     else {
764         int id = GET16(tag->data);
765         if (!extractanyids) {
766           fprintf(stderr, "Object %d is not a JPEG picture!\n", id);
767           exit(1);
768         }
769         return 0;
770     }
771     return 1;
772 }
773
774 #ifdef _ZLIB_INCLUDED_
775 static U32 mycrc32;
776
777 static U32*crc32_table = 0;
778 static void make_crc32_table(void)
779 {
780   int t;
781   if(crc32_table) 
782       return;
783   crc32_table = (U32*)malloc(1024);
784
785   for (t = 0; t < 256; t++) {
786     U32 c = t;
787     int s;
788     for (s = 0; s < 8; s++) {
789       c = (0xedb88320L*(c&1)) ^ (c >> 1);
790     }
791     crc32_table[t] = c;
792   }
793 }
794 static inline void png_write_byte(FILE*fi, U8 byte)
795 {
796     fwrite(&byte,1,1,fi);
797     mycrc32 = crc32_table[(mycrc32 ^ byte) & 0xff] ^ (mycrc32 >> 8);
798 }
799 static void png_start_chunk(FILE*fi, char*type, int len)
800 {
801     U8 mytype[4]={0,0,0,0};
802     U32 mylen = BE_32_TO_NATIVE(len);
803     memcpy(mytype,type,strlen(type));
804     fwrite(&mylen, 4, 1, fi);
805     mycrc32=0xffffffff;
806     png_write_byte(fi,mytype[0]);
807     png_write_byte(fi,mytype[1]);
808     png_write_byte(fi,mytype[2]);
809     png_write_byte(fi,mytype[3]);
810 }
811 static void png_write_bytes(FILE*fi, U8*bytes, int len)
812 {
813     int t;
814     for(t=0;t<len;t++)
815         png_write_byte(fi,bytes[t]);
816 }
817 static void png_write_dword(FILE*fi, U32 dword)
818 {
819     png_write_byte(fi,dword>>24);
820     png_write_byte(fi,dword>>16);
821     png_write_byte(fi,dword>>8);
822     png_write_byte(fi,dword);
823 }
824 static void png_end_chunk(FILE*fi)
825 {
826     U32 tmp = BE_32_TO_NATIVE((mycrc32^0xffffffff));
827     fwrite(&tmp,4,1,fi);
828 }
829
830
831 /* extract a lossless image (png) out of a tag 
832    This routine was originally meant to be a one-pager. I just
833    didn't know png is _that_ much fun. :) -mk
834  */
835 int handlelossless(TAG*tag)
836 {
837     char name[80];
838     char*filename = name;
839     FILE*fi;
840     int width, height;
841     int crc;
842     int id;
843     int t;
844     U8 bpp = 1;
845     U8 format;
846     U8 tmp;
847     Bytef* data=0;
848     U8* data2=0;
849     U8* data3=0;
850     uLongf datalen;
851     uLongf datalen2;
852     U32 datalen3;
853     U8 head[] = {137,80,78,71,13,10,26,10};
854     int cols;
855     char alpha = tag->id == ST_DEFINEBITSLOSSLESS2;
856     RGBA* palette;
857     int pos;
858     int error;
859     U32 tmp32;
860
861     make_crc32_table();
862
863     if(tag->id != ST_DEFINEBITSLOSSLESS &&
864        tag->id != ST_DEFINEBITSLOSSLESS2) {
865         int id = GET16(tag->data);
866         if (!extractanyids) {
867           fprintf(stderr, "Object %d is not a PNG picture!\n",id);
868           exit(1);
869         }
870         return 0;
871     }
872
873     id =swf_GetU16(tag);
874     format = swf_GetU8(tag);
875     if(format == 3) bpp = 8;
876     if(format == 4) bpp = 16;
877     if(format == 5) bpp = 32;
878     if(format!=3 && format!=5) {
879         if(format==4)
880         fprintf(stderr, "Can't handle 16-bit palette images yet (image %d)\n",id);
881         else 
882         fprintf(stderr, "Unknown image type %d in image %d\n", format, id);
883         return 0;
884     }
885     width = swf_GetU16(tag);
886     height = swf_GetU16(tag);
887     if(format == 3) cols = swf_GetU8(tag) + 1;
888 // this is what format means according to the flash specification. (which is
889 // clearly wrong)
890 //    if(format == 4) cols = swf_GetU16(tag) + 1;
891 //    if(format == 5) cols = swf_GetU32(tag) + 1;
892     else cols = 0;
893
894     msg("<verbose> Width %d", width);
895     msg("<verbose> Height %d", height);
896     msg("<verbose> Format %d", format);
897     msg("<verbose> Cols %d", cols);
898     msg("<verbose> Bpp %d", bpp);
899
900     datalen = (width*height*bpp/8+cols*8);
901     do {
902         if(data)
903             free(data);
904         datalen+=4096;
905         data = malloc(datalen);
906         error = uncompress (data, &datalen, &tag->data[tag->pos], tag->len-tag->pos);
907     } while(error == Z_BUF_ERROR);
908     if(error != Z_OK) {
909         fprintf(stderr, "Zlib error %d (image %d)\n", error, id);
910         return 0;
911     }
912     msg("<verbose> Uncompressed image is %d bytes (%d colormap)", datalen, (3+alpha)*cols);
913     pos = 0;
914     datalen2 = datalen+16;
915     data2 = malloc(datalen2);
916     palette = (RGBA*)malloc(cols*sizeof(RGBA));
917
918     for(t=0;t<cols;t++) {
919         palette[t].r = data[pos++];
920         palette[t].g = data[pos++];
921         palette[t].b = data[pos++];
922         if(alpha) {
923             palette[t].a = data[pos++];
924         }
925     }
926
927     prepare_name(name, sizeof(name), "pic", "png", id);
928     if(numextracts==1) {
929         filename = destfilename;
930         if(!strcmp(filename,"output.swf"))
931             filename = "output.png";
932     }
933     fi = save_fopen(filename, "wb");
934     fwrite(head,sizeof(head),1,fi);     
935
936     png_start_chunk(fi, "IHDR", 13);
937      png_write_dword(fi,width);
938      png_write_dword(fi,height);
939      png_write_byte(fi,8);
940      if(format == 3)
941      png_write_byte(fi,3); //indexed
942      else if(format == 5 && alpha==0)
943      png_write_byte(fi,2); //rgb
944      else if(format == 5 && alpha==1)
945      png_write_byte(fi,6); //rgba
946      else return 0;
947
948      png_write_byte(fi,0); //compression mode
949      png_write_byte(fi,0); //filter mode
950      png_write_byte(fi,0); //interlace mode
951     png_end_chunk(fi);
952    
953     if(format == 3) {
954         png_start_chunk(fi, "PLTE", 768);
955          
956          for(t=0;t<256;t++) {
957              png_write_byte(fi,palette[t].r);
958              png_write_byte(fi,palette[t].g);
959              png_write_byte(fi,palette[t].b);
960          }
961         png_end_chunk(fi);
962
963         if(alpha) {
964             /* write alpha palette */
965             png_start_chunk(fi, "tRNS", 256);
966             for(t=0;t<256;t++) {
967                 png_write_byte(fi,palette[t].a);
968             }
969             png_end_chunk(fi);
970         }
971     }
972     {
973         int pos2 = 0;
974         int x,y;
975         int srcwidth = width * (bpp/8);
976         datalen3 = (width*4+5)*height;
977         data3 = (U8*)malloc(datalen3);
978         for(y=0;y<height;y++)
979         {
980            data3[pos2++]=0; //filter type
981            if(bpp==32) {
982             if(!alpha) {
983                 // 32 bit to 24 bit "conversion"
984                 for(x=0;x<width;x++) {
985                     data3[pos2++]=data[pos+1];
986                     data3[pos2++]=data[pos+2];
987                     data3[pos2++]=data[pos+3];
988                     pos+=4; //ignore padding byte
989                 }
990             } else {
991                 for(x=0;x<width;x++) {
992                     data3[pos2++]=data[pos+1];
993                     data3[pos2++]=data[pos+2];
994                     data3[pos2++]=data[pos+3];
995                     data3[pos2++]=data[pos+0]; //alpha
996                     pos+=4;
997                 }
998             }
999            }
1000            else {
1001                 for(x=0;x<srcwidth;x++)
1002                     data3[pos2++]=data[pos++];
1003            }
1004            
1005            pos+=((srcwidth+3)&~3)-srcwidth; //align
1006         }
1007         datalen3=pos2;
1008     }
1009
1010     if(compress (data2, &datalen2, data3, datalen3) != Z_OK) {
1011         fprintf(stderr, "zlib error in pic %d\n", id);
1012         return 0;
1013     }
1014     msg("<verbose> Compressed data is %d bytes", datalen2);
1015     png_start_chunk(fi, "IDAT", datalen2);
1016     png_write_bytes(fi,data2,datalen2);
1017     png_end_chunk(fi);
1018     png_start_chunk(fi, "IEND", 0);
1019     png_end_chunk(fi);
1020
1021     free(data);
1022     free(data2);
1023     free(data3);
1024     return 1;
1025 }
1026 #endif
1027
1028 static FILE*mp3file=0;
1029 void handlesoundstream(TAG*tag)
1030 {
1031     char*filename = "output.mp3";
1032     if(numextracts==1) {
1033         filename = destfilename;
1034         if(!strcmp(filename,"output.swf"))
1035             filename = "output.mp3";
1036     }
1037     switch(tag->id) {
1038         case ST_SOUNDSTREAMHEAD:
1039             if((tag->data[1]&0x30) == 0x20) { //mp3 compression
1040                 mp3file = fopen(filename, "wb");
1041                 msg("<notice> Writing mp3 data to %s",filename);
1042             }
1043             else
1044                 msg("<error> Soundstream is not mp3");
1045         break;
1046         case ST_SOUNDSTREAMHEAD2:
1047             if((tag->data[1]&0x30) == 0x20) {//mp3 compression
1048                 mp3file = fopen(filename, "wb");
1049                 msg("<notice> Writing mp3 data to %s",filename);
1050             }
1051             else
1052                 msg("<error> Soundstream is not mp3 (2)");
1053         break;
1054         case ST_SOUNDSTREAMBLOCK:
1055             if(mp3file)
1056                 fwrite(&tag->data[4],tag->len-4,1,mp3file);
1057         break;
1058     }
1059 }
1060
1061 int handledefinesound(TAG*tag)
1062 {
1063     U8 flags;
1064     U32 samples;
1065     char buf[128];
1066     char*filename = buf;
1067     FILE*fi;
1068     char*extension = 0;
1069     int format;
1070     U16 id;
1071     int rate,bits,stereo;
1072     char*rates[] = {"5500","11025","22050","44100"};
1073     id = swf_GetU16(tag); //id
1074     
1075     flags = swf_GetU8(tag);
1076     format = flags>>4;
1077     rate = (flags>>2)&3;
1078     bits = flags&2?16:8;
1079     stereo = flags&1;
1080     
1081     samples = swf_GetU32(tag);
1082
1083     extension = "raw";
1084
1085     if(format == 2) { // mp3
1086         swf_GetU16(tag); //numsamples_seek
1087         extension = "mp3";
1088     } else if(format == 0) { // raw
1089         printf("Sound is RAW, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1090         // TODO: convert to WAV
1091         extension = "raw";
1092     } else if(format == 1) { // adpcm
1093         printf("Sound is ADPCM, format: %s samples/sec, %d bit, %s\n", rates[rate], bits, stereo?"stereo":"mono");
1094         extension = "adpcm";
1095     } else {
1096         return 0;
1097     }
1098     prepare_name(buf, sizeof(buf), "sound", extension, id);
1099     if(numextracts==1) {
1100         filename = destfilename;
1101         if(!strcmp(filename,"output.swf")) {
1102             sprintf(buf, "output.%s", extension);
1103             filename = buf;
1104         }
1105     }
1106     fi = save_fopen(filename, "wb");
1107     fwrite(&tag->data[tag->pos], tag->len - tag->pos, 1, fi);
1108     fclose(fi);
1109     return 1;
1110 }
1111
1112 int handlebinary(TAG*tag) {
1113     FILE *fout = NULL;
1114     char buf[100];
1115     char *filename = buf;
1116     int len = tag->memsize;
1117     int dx = 6; // offset to binary data
1118     if (tag->id!=ST_DEFINEBINARY) {
1119         if (!extractanyids) {
1120           fprintf(stderr, "Object %d is not a binary entity!\n",
1121                           GET16(tag->data));
1122         }
1123         return 0;
1124     }
1125     prepare_name(buf, sizeof(buf), "binary", "bin", GET16(tag->data));
1126     if(numextracts==1) {
1127         filename = destfilename;
1128         if(!strcmp(filename,"output.swf")) {
1129             sprintf(buf, "output.bin");
1130             filename = buf;
1131         }
1132     }
1133     fout = fopen(filename, "wb");
1134     fwrite(tag->data+dx,len-dx,1,fout);
1135     fclose(fout);
1136     return 1;
1137 }
1138
1139 int main (int argc,char ** argv)
1140
1141     TAG*tag;
1142     SWF swf;
1143     int f;
1144     int found = 0;
1145     int frame = 0;
1146     int tagnum = 0;
1147     char depths[65536];
1148     char listavailable = 0;
1149     processargs(argc, argv);
1150
1151     if(!extractframes && !extractids && ! extractname && !extractjpegids && !extractpngids
1152         && !extractmp3 && !extractsoundids && !extractfontids && !extractbinaryids && !extractanyids)
1153         listavailable = 1;
1154
1155     if(!originalplaceobjects && movetozero) {
1156         fprintf(stderr, "Error: -0 (--movetozero) can only be used in conjunction with -P (--placeobject)\n");
1157         return 0;
1158     }
1159
1160     if(!filename)
1161     {
1162         fprintf(stderr, "You must supply a filename.\n");
1163         return 1;
1164     }
1165     initLog(0,-1,0,0,-1, verbose);
1166
1167     f = open(filename,O_RDONLY|O_BINARY);
1168
1169     if (f<0)
1170     { 
1171         perror("Couldn't open file: ");
1172         exit(1);
1173     }
1174     if (swf_ReadSWF(f,&swf) < 0)
1175     { 
1176         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
1177         close(f);
1178         exit(1);
1179     }
1180     close(f);
1181
1182     if(listavailable) {
1183         listObjects(&swf);
1184         swf_FreeTags(&swf);
1185         return 0;
1186     }
1187
1188     tag = swf.firstTag;
1189     tagnum = 0;
1190     while(tag) {
1191         tagnum ++;
1192         tag = tag->next;
1193     }
1194
1195     tagused = (char*)malloc(tagnum);
1196     memset(tagused, 0, tagnum);
1197     memset(used, 0, 65536);
1198     memset(depths, 0, 65536);
1199
1200     tag = swf.firstTag;
1201     tagnum = 0;
1202     while(tag) {
1203         if(swf_isAllowedSpriteTag(tag)) {
1204             int write = 0;
1205             if(extractframes && is_in_range(frame, extractframes)) {
1206                 write = 1;
1207                 if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
1208                     depths[swf_GetDepth(tag)] = 1;
1209                 }
1210                 if(tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) {
1211                     int depth = swf_GetDepth(tag);
1212                     if(!depths[depth]) 
1213                         write = 0;
1214                     depths[swf_GetDepth(tag)] = 0;
1215                 }
1216             } else {
1217                 if((tag->id == ST_REMOVEOBJECT || tag->id == ST_REMOVEOBJECT2) && 
1218                     (depths[swf_GetDepth(tag)]) && hollow) {
1219                     write = 1;
1220                     depths[swf_GetDepth(tag)] = 0;
1221                 }
1222             }
1223             if(write) {
1224                 enumerateIDs(tag, idcallback);
1225                 found = 1;
1226                 tagused[tagnum] = 1;
1227             }
1228         }
1229
1230         if(tag->id == ST_SOUNDSTREAMHEAD ||
1231            tag->id == ST_SOUNDSTREAMHEAD2 ||
1232            tag->id == ST_SOUNDSTREAMBLOCK) {
1233             if(extractmp3)
1234                 handlesoundstream(tag);
1235         }
1236
1237         if(tag->id == ST_JPEGTABLES) {
1238             handlejpegtables(tag);
1239         }
1240
1241         if(swf_isDefiningTag(tag)) {
1242             int id = swf_GetDefineID(tag);
1243             tags[id] = tag;
1244             if(extractids && is_in_range(id, extractids)) {
1245                 used[id] = 5;
1246                 found = 1;
1247             }
1248             if(extractfontids && is_in_range(id, extractfontids)) {
1249                 handlefont(&swf, tag);
1250             }
1251             if(extractjpegids && is_in_range(id, extractjpegids)) {
1252                 handlejpeg(tag);
1253             }
1254             if(extractsoundids && is_in_range(id, extractsoundids)) {
1255                 handledefinesound(tag);
1256             }
1257             if(extractbinaryids && is_in_range(id, extractbinaryids)) {
1258                 handlebinary(tag);
1259             }
1260 #ifdef _ZLIB_INCLUDED_
1261             if(extractpngids && is_in_range(id, extractpngids)) {
1262                 handlelossless(tag);
1263             }
1264 #endif
1265             if(extractanyids && is_in_range(id, extractanyids)) {
1266                 if (handlefont(&swf,tag)) {
1267                     // pass
1268                 } else if (handlejpeg(tag)) {
1269                     // pass
1270                 } else if (handlebinary(tag)) {
1271                     // pass
1272 #ifdef _ZLIB_INCLUDED_
1273                 } else if (handlelossless(tag)) {
1274                     // pass
1275 #endif
1276                 } else if (handledefinesound(tag)) {
1277                     // Not sure if sound code checks carefully for type.
1278                     // pass
1279                 } else {
1280                     printf("#%d not processed\n", id);
1281                 }
1282             }
1283         }
1284         else if (tag->id == ST_SETBACKGROUNDCOLOR) {
1285             mainr = tag->data[0];
1286             maing = tag->data[1];
1287             mainb = tag->data[2];
1288         }
1289         else if(swf_isPlaceTag(tag) && tag->id != ST_PLACEOBJECT ) {
1290             char*name = swf_GetName(tag);
1291             if(name && extractname && !strcmp(name, extractname)) {
1292                 int id = swf_GetPlaceID(tag); 
1293                 used[id] = 5;
1294                 found = 1;
1295                 if(originalplaceobjects) {
1296                     tagused[tagnum] = 1;
1297                 }
1298                 depths[swf_GetDepth(tag)] = 1;
1299                 extractname_id = id;
1300             }
1301         }
1302         else if(tag->id == ST_SHOWFRAME) {
1303             frame ++;
1304             if(hollow) {
1305                 tagused[tagnum] = 1;
1306                 found = 1;
1307             }
1308         }
1309         
1310         if(tag->id == ST_DEFINESPRITE) {
1311             while(tag->id != ST_END) { 
1312                 tag = tag->next;
1313                 tagnum ++;
1314             }
1315         }
1316         tag = tag->next;
1317         tagnum ++;
1318     }
1319     if (found)
1320         extractTag(&swf, destfilename);
1321
1322     if(mp3file) {
1323         fclose(mp3file);
1324     } else {
1325         if(extractmp3) {
1326             msg("<error> Didn't find a soundstream in file");
1327         }
1328     }
1329
1330     swf_FreeTags(&swf);
1331     return 0;
1332 }
1333