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