2 Shows the structure of a swf file
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This file is distributed under the GPL, see file COPYING for details */
10 #include "../config.h"
12 #ifdef HAVE_SYS_STAT_H
18 #ifdef HAVE_SYS_TYPES_H
19 #include <sys/types.h>
28 #include "../lib/rfxswf.h"
29 #include "../lib/args.h"
31 static char * filename = 0;
33 /* idtab stores the ids which are defined in the file. This allows us
34 to detect errors in the file. (i.e. ids which are defined more than
36 static char idtab[65536];
37 static char * indent = " ";
39 static int placements = 0;
40 static int action = 0;
43 static int showtext = 0;
47 struct options_t options[] =
66 int args_callback_option(char*name,char*val)
68 if(!strcmp(name, "V")) {
69 printf("swfdump - part of %s %s\n", PACKAGE, VERSION);
72 else if(name[0]=='a') {
76 else if(name[0]=='p') {
80 else if(name[0]=='t') {
84 else if(name[0]=='e') {
88 else if(name[0]=='X') {
92 else if(name[0]=='Y') {
96 else if(name[0]=='r') {
100 else if(name[0]=='f') {
104 else if(name[0]=='d') {
108 else if(name[0]=='u') {
112 else if(name[0]=='D') {
113 action = placements = showtext = 1;
117 printf("Unknown option: -%s\n", name);
123 int args_callback_longoption(char*name,char*val)
125 return args_long2shortoption(options, name, val);
127 void args_callback_usage(char*name)
129 printf("Usage: %s [-at] file.swf\n", name);
130 printf("\t-h , --help\t\t Print help and exit\n");
131 printf("\t-D , --full\t\t Show everything. The same as -atp\n");
132 printf("\t-e , --html\t\t Create html output embedding the file (simple, but useful)\n");
133 printf("\t-X , --width\t\t Prints out a string of the form \"-X width\"\n");
134 printf("\t-Y , --height\t\t Prints out a string of the form \"-Y height\"\n");
135 printf("\t-r , --rate\t\t Prints out a string of the form \"-r rate\"\n");
136 printf("\t-f , --frames\t\t Prints out a string of the form \"-f framenum\"\n");
137 printf("\t-a , --action\t\t Disassemble action tags\n");
138 printf("\t-p , --placements\t\t Show extra placement information\n");
139 printf("\t-t , --text\t\t Show text data\n");
140 printf("\t-d , --hex\t\t Print hex output of tag data, too\n");
141 printf("\t-u , --used\t\t Show referred IDs for each Tag\n");
142 printf("\t-V , --version\t\t Print program version and exit\n");
144 int args_callback_command(char*name,char*val)
147 fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
155 char* testfunc(char*str)
157 printf("%s: %s\n", what, str);
161 void dumpButton2Actions(TAG*tag, char*prefix)
167 oldTagPos = swf_GetTagPos(tag);
169 // scan DefineButton2 Record
171 swf_GetU16(tag); // Character ID
172 swf_GetU8(tag); // Flags;
174 offsetpos = swf_GetTagPos(tag); // first offset
177 while (swf_GetU8(tag)) // state -> parse ButtonRecord
178 { swf_GetU16(tag); // id
179 swf_GetU16(tag); // layer
180 swf_GetMatrix(tag,NULL); // matrix
181 swf_GetCXForm(tag,NULL,1); // cxform
188 if(tag->pos >= tag->len)
191 offsetpos = swf_GetU16(tag);
192 condition = swf_GetU16(tag); // condition
194 actions = swf_ActionGet(tag);
195 printf("%s condition %04x\n", prefix, condition);
196 swf_DumpActions(actions, prefix);
199 swf_SetTagPos(tag,oldTagPos);
203 void dumpButtonActions(TAG*tag, char*prefix)
206 swf_GetU16(tag); // id
207 while (swf_GetU8(tag)) // state -> parse ButtonRecord
208 { swf_GetU16(tag); // id
209 swf_GetU16(tag); // layer
210 swf_GetMatrix(tag,NULL); // matrix
212 actions = swf_ActionGet(tag);
213 swf_DumpActions(actions, prefix);
216 #define ET_HASTEXT 32768
217 #define ET_WORDWRAP 16384
218 #define ET_MULTILINE 8192
219 #define ET_PASSWORD 4096
220 #define ET_READONLY 2048
221 #define ET_HASTEXTCOLOR 1024
222 #define ET_HASMAXLENGTH 512
223 #define ET_HASFONT 256
226 #define ET_HASLAYOUT 32
227 #define ET_NOSELECT 16
231 #define ET_USEOUTLINES 1
237 void textcallback(int*glyphs, int nr, int fontid)
240 printf(" <%2d glyphs in font %2d> ",nr, fontid);
241 for(t=0;t<fontnum;t++)
243 if(fonts[t]->id == fontid) {
253 if(glyphs[t] >= fonts[font]->numchars /*glyph is in range*/
254 || !fonts[font]->glyph2ascii /* font has ascii<->glyph mapping */
257 a = fonts[font]->glyph2ascii[glyphs[t]];
264 printf("\\x%x", (int)a);
269 void handleText(TAG*tag)
272 swf_FontExtract_DefineTextCallback(-1,0,tag,4, textcallback);
275 void handleDefineSound(TAG*tag)
277 U16 id = swf_GetU16(tag);
278 U8 flags = swf_GetU8(tag);
279 int compression = (flags>>4)&3;
280 int rate = (flags>>2)&3;
281 int bits = flags&2?16:8;
282 int stereo = flags&1;
284 if(compression == 0) printf("Raw ");
285 else if(compression == 1) printf("ADPCM ");
286 else if(compression == 2) printf("MP3 ");
288 if(rate == 0) printf("5.5Khz ");
289 if(rate == 1) printf("11Khz ");
290 if(rate == 2) printf("22Khz ");
291 if(rate == 3) printf("44Khz ");
292 printf("%dBit ", bits);
293 if(stereo) printf("stereo");
298 void handleDefineBits(TAG*tag)
304 id = swf_GetU16(tag);
305 mode = swf_GetU8(tag);
306 width = swf_GetU16(tag);
307 height = swf_GetU16(tag);
308 printf(" image %dx%d",width,height);
309 if(mode == 3) printf(" (8 bpp)");
310 else if(mode == 4) printf(" (16 bpp)");
311 else if(mode == 5) printf(" (32 bpp)");
312 else printf(" (? bpp)");
315 void handleEditText(TAG*tag)
320 id = swf_GetU16(tag);
322 //swf_ResetReadBits(tag);
327 flags = swf_GetBits(tag,16);
328 if(flags & ET_HASFONT) {
329 swf_GetU16(tag); //font
330 swf_GetU16(tag); //fontheight
332 if(flags & ET_HASTEXTCOLOR) {
333 swf_GetU8(tag); //rgba
338 if(flags & ET_HASMAXLENGTH) {
339 swf_GetU16(tag); //maxlength
341 if(flags & ET_HASLAYOUT) {
342 swf_GetU8(tag); //align
343 swf_GetU16(tag); //left margin
344 swf_GetU16(tag); //right margin
345 swf_GetU16(tag); //indent
346 swf_GetU16(tag); //leading
348 printf(" variable \"%s\"", &tag->data[tag->pos]);
350 if(flags & (ET_X1 | ET_X2 | ET_X3 | ET_X0))
352 printf(" undefined flags: %d%d%d%d",
359 while(tag->data[tag->pos++]);
360 if(flags & ET_HASTEXT)
361 // printf(" text \"%s\"\n", &tag->data[tag->pos])
364 void printhandlerflags(U16 handlerflags)
366 if(handlerflags&1) printf("[on load]");
367 if(handlerflags&2) printf("[enter frame]");
368 if(handlerflags&4) printf("[unload]");
369 if(handlerflags&8) printf("[mouse move]");
370 if(handlerflags&16) printf("[mouse down]");
371 if(handlerflags&32) printf("[mouse up]");
372 if(handlerflags&64) printf("[key down]");
373 if(handlerflags&128) printf("[key up]");
374 if(handlerflags&256) printf("[data]");
375 if(handlerflags&0xfe00) printf("[???]");
377 void handlePlaceObject2(TAG*tag, char*prefix)
379 U8 flags = swf_GetU8(tag);
383 int ppos[3] = {0,0,0};
384 swf_GetU16(tag); //depth
386 if(flags&2) swf_GetU16(tag); //id
388 swf_GetMatrix(tag,&m);
390 ppos[0] += sprintf(pstr[0], "| Matrix ");
391 ppos[1] += sprintf(pstr[1], "| %5.3f %5.3f %6.2f ", m.sx/65536.0, m.r1/65536.0, m.tx/20.0);
392 ppos[2] += sprintf(pstr[2], "| %5.3f %5.3f %6.2f ", m.r0/65536.0, m.sy/65536.0, m.ty/20.0);
396 swf_GetCXForm(tag, &cx, 1);
398 ppos[0] += sprintf(pstr[0]+ppos[0], "| CXForm r g b a ");
399 ppos[1] += sprintf(pstr[1]+ppos[1], "| mul %4.1f %4.1f %4.1f %4.1f ", cx.r0/256.0, cx.g0/256.0, cx.b0/256.0, cx.a0/256.0);
400 ppos[2] += sprintf(pstr[2]+ppos[2], "| add %4d %4d %4d %4d ", cx.r1, cx.g1, cx.b1, cx.a1);
404 U16 ratio = swf_GetU16(tag); //ratio
406 ppos[0] += sprintf(pstr[0]+ppos[0], "| Ratio ");
407 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", ratio);
408 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
412 U16 clip = swf_GetU16(tag); //clip
414 ppos[0] += sprintf(pstr[0]+ppos[0], "| Clip ");
415 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", clip);
416 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
419 if(flags&32) { while(swf_GetU8(tag)); }
420 if(placements && ppos[0]) {
422 printf("%s %s\n", prefix, pstr[0]);
423 printf("%s %s\n", prefix, pstr[1]);
424 printf("%s %s", prefix, pstr[2]);
433 unknown = swf_GetU16(tag);
434 globalflags = swf_GetU16(tag);
436 printf("Unknown parameter field not zero: %04x\n", unknown);
439 printf("global flags: %04x\n", globalflags);
440 handlerflags = swf_GetU16(tag);
442 handlerflags = swf_GetU32(tag);
445 while(handlerflags) {
450 globalflags &= ~handlerflags;
451 printf("%s flags %08x ",prefix, handlerflags);
452 printhandlerflags(handlerflags);
453 length = swf_GetU32(tag);
454 printf(", %d bytes actioncode\n",length);
455 a = swf_ActionGet(tag);
456 swf_DumpActions(a,prefix);
459 handlerflags = is32?swf_GetU32(tag):swf_GetU16(tag);
461 if(globalflags) // should go to sterr.
462 printf("ERROR: unsatisfied handlerflags: %02x\n", globalflags);
464 printf(" has action code\n");
469 void handlePlaceObject(TAG*tag, char*prefix)
474 void fontcallback1(U16 id,U8 * name)
478 void fontcallback2(U16 id,U8 * name)
480 swf_FontExtract(&swf,id,&fonts[fontnum]);
484 void hexdumpTag(TAG*tag, char* prefix)
487 printf(" %s-=> ",prefix);
488 for(t=0;t<tag->len;t++) {
489 printf("%02x ", tag->data[t]);
490 if((t && ((t&15)==15)) || (t==tag->len-1))
495 printf("\n %s-=> ",prefix);
500 void handleExportAssets(TAG*tag, char* prefix)
506 num = swf_GetU16(tag);
509 id = swf_GetU16(tag);
510 name = swf_GetString(tag);
511 printf("%sexports %04d as \"%s\"\n", prefix, id, name);
515 void dumperror(const char* format, ...)
520 va_start(arglist, format);
521 vsprintf(buf, format, arglist);
525 printf("==== Error: %s ====\n", buf);
528 static char strbuf[800];
531 char* timestring(double f)
533 int hours = (int)(f/3600);
534 int minutes = (int)((f-hours*3600)/60);
535 int seconds = (int)((f-hours*3600-minutes*60));
536 int useconds = (int)((f-(int)f)*1000+0.5);
539 sprintf(&strbuf[bufpos], "%02d:%02d:%02d,%03d",hours,minutes,seconds,useconds);
540 return &strbuf[bufpos];
543 int main (int argc,char ** argv)
551 char issprite = 0; // are we inside a sprite definition?
554 char* spriteframelabel = 0;
555 char* framelabel = 0;
559 memset(idtab,0,65536);
561 processargs(argc, argv);
565 fprintf(stderr, "You must supply a filename.\n");
569 f = open(filename,O_RDONLY);
573 perror("Couldn't open file: ");
576 if FAILED(swf_ReadSWF(f,&swf))
578 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
585 if(statbuf.st_size != swf.fileSize && !swf.compressed)
586 dumperror("Real Filesize (%d) doesn't match header Filesize (%d)",
587 statbuf.st_size, swf.fileSize);
588 filesize = statbuf.st_size;
593 xsize = (swf.movieSize.xmax-swf.movieSize.xmin)/20;
594 ysize = (swf.movieSize.ymax-swf.movieSize.ymin)/20;
598 printf("-X %d", xsize);
604 printf("-Y %d", ysize);
610 printf("-r %d", swf.frameRate*100/256);
616 printf("-f %d", swf.frameCount);
623 char*fileversions[] = {"","1,0,0,0", "2,0,0,0","3,0,0,0","4,0,0,0",
624 "5,0,0,0","6,0,23,0","7,0,0,0","8,0,0,0"};
625 if(swf.fileVersion>8) {
626 fprintf(stderr, "Fileversion>8\n");
629 printf("<OBJECT CLASSID=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n"
631 //" BGCOLOR=#ffffffff\n"?
633 //http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,23,0?
634 " CODEBASE=\"http://active.macromedia.com/flash5/cabs/swflash.cab#version=%s\">\n"
635 " <PARAM NAME=\"MOVIE\" VALUE=\"%s\">\n"
636 " <PARAM NAME=\"PLAY\" VALUE=\"true\">\n"
637 " <PARAM NAME=\"LOOP\" VALUE=\"true\">\n"
638 " <PARAM NAME=\"QUALITY\" VALUE=\"high\">\n"
639 " <EMBED SRC=\"%s\" WIDTH=\"%d\" HEIGHT=\"%d\"\n" //bgcolor=#ffffff?
640 " PLAY=\"true\" ALIGN=\"\" LOOP=\"true\" QUALITY=\"high\"\n"
641 " TYPE=\"application/x-shockwave-flash\"\n"
642 " PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\">\n"
644 "</OBJECT>\n", xsize, ysize, fileversions[swf.fileVersion],
645 filename, filename, xsize, ysize);
648 printf("[HEADER] File version: %d\n", swf.fileVersion);
650 printf("[HEADER] File is zlib compressed.");
651 if(filesize && swf.fileSize)
652 printf(" Ratio: %02d%%\n", filesize*100/(swf.fileSize));
656 printf("[HEADER] File size: %ld%s\n", swf.fileSize, swf.compressed?" (Depacked)":"");
657 printf("[HEADER] Frame rate: %f\n",swf.frameRate/256.0);
658 printf("[HEADER] Frame count: %d\n",swf.frameCount);
659 printf("[HEADER] Movie width: %.2f",(swf.movieSize.xmax-swf.movieSize.xmin)/20.0);
660 if(swf.movieSize.xmin)
661 printf(" (left offset: %.2f)\n", swf.movieSize.xmin/20.0);
664 printf("[HEADER] Movie height: %.2f",(swf.movieSize.ymax-swf.movieSize.ymin)/20.0);
665 if(swf.movieSize.ymin)
666 printf(" (top offset: %.2f)\n", swf.movieSize.ymin/20.0);
674 swf_FontEnumerate(&swf,&fontcallback1);
675 fonts = (SWFFONT**)malloc(fontnum*sizeof(SWFFONT*));
677 swf_FontEnumerate(&swf,&fontcallback2);
681 char*name = swf_TagGetName(tag);
684 dumperror("Unknown tag:0x%03x", tag->id);
688 printf("[%03x] %9ld %s%s", tag->id, tag->len, prefix, swf_TagGetName(tag));
690 if(tag->id == ST_FREECHARACTER) {
691 U16 id = swf_GetU16(tag);
695 if(swf_isDefiningTag(tag)) {
696 U16 id = swf_GetDefineID(tag);
697 printf(" defines id %04d", id);
699 dumperror("Id %04d is defined more than once.", id);
702 else if(swf_isPseudoDefiningTag(tag)) {
703 U16 id = swf_GetDefineID(tag);
704 printf(" adds information to id %04d", id);
706 dumperror("Id %04d is not yet defined.\n", id);
708 else if(tag->id == ST_PLACEOBJECT) {
709 printf(" places id %04d at depth %04x", swf_GetPlaceID(tag), swf_GetDepth(tag));
711 printf(" name \"%s\"",swf_GetName(tag));
712 handlePlaceObject(tag, myprefix);
714 else if(tag->id == ST_PLACEOBJECT2) {
721 printf(" id %04d",swf_GetPlaceID(tag));
725 printf(" at depth %04d", swf_GetDepth(tag));
727 printf(" name \"%s\"",swf_GetName(tag));
729 else if(tag->id == ST_REMOVEOBJECT) {
730 printf(" removes id %04d from depth %04d", swf_GetPlaceID(tag), swf_GetDepth(tag));
732 else if(tag->id == ST_REMOVEOBJECT2) {
733 printf(" removes object from depth %04d", swf_GetDepth(tag));
735 else if(tag->id == ST_FREECHARACTER) {
736 printf(" frees object %04d", swf_GetPlaceID(tag));
738 else if(tag->id == ST_STARTSOUND) {
741 id = swf_GetU16(tag);
742 flags = swf_GetU8(tag);
744 printf(" stops sound with id %04d", id);
746 printf(" starts sound with id %04d", id);
748 printf(" (if not already playing)");
754 printf(" looping %d times", swf_GetU16(tag));
757 else if(tag->id == ST_FRAMELABEL) {
758 int l = strlen(tag->data);
759 printf(" \"%s\"", tag->data);
761 printf(" has %d extra bytes", tag->len-1-l);
762 if(tag ->len-1-l == 1 && tag->data[tag->len-1] == 1)
765 if((framelabel && !issprite) ||
766 (spriteframelabel && issprite)) {
767 dumperror("Frame %d has more than one label",
768 issprite?spriteframe:mainframe);
770 if(issprite) spriteframelabel = tag->data;
771 else framelabel = tag->data;
773 else if(tag->id == ST_SHOWFRAME) {
774 char*label = issprite?spriteframelabel:framelabel;
775 int frame = issprite?spriteframe:mainframe;
778 while(tag->next && tag->next->id == ST_SHOWFRAME && tag->next->len == 0) {
780 if(issprite) spriteframe++;
786 printf(" %d (%s)", frame, timestring(frame*(256.0/(swf.frameRate+0.1))));
788 printf(" %d-%d (%s-%s)", frame, nframe,
789 timestring(frame*(256.0/(swf.frameRate+0.1))),
790 timestring(nframe*(256.0/(swf.frameRate+0.1)))
793 printf(" (label \"%s\")", label);
794 if(issprite) {spriteframe++; spriteframelabel = 0;}
795 if(!issprite) {mainframe++; framelabel = 0;}
798 if(tag->id == ST_SETBACKGROUNDCOLOR) {
799 U8 r = swf_GetU8(tag);
800 U8 g = swf_GetU8(tag);
801 U8 b = swf_GetU8(tag);
802 printf(" (%02x/%02x/%02x)\n",r,g,b);
804 else if(tag->id == ST_DEFINEBITSLOSSLESS ||
805 tag->id == ST_DEFINEBITSLOSSLESS2) {
806 handleDefineBits(tag);
809 else if(tag->id == ST_DEFINESOUND) {
810 handleDefineSound(tag);
813 else if(tag->id == ST_DEFINEEDITTEXT) {
817 else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) {
823 else if(tag->id == ST_PLACEOBJECT2) {
825 else if(tag->id == ST_NAMECHARACTER) {
827 printf(" \"%s\"\n", swf_GetString(tag));
833 sprintf(myprefix, " %s", prefix);
835 if(tag->id == ST_DEFINESPRITE) {
836 sprintf(prefix, " ");
838 dumperror("Sprite definition inside a sprite definition");
842 spriteframelabel = 0;
844 else if(tag->id == ST_END) {
847 spriteframelabel = 0;
849 dumperror("End Tag not empty");
851 else if(tag->id == ST_EXPORTASSETS) {
852 handleExportAssets(tag, myprefix);
854 else if(tag->id == ST_DOACTION && action) {
856 actions = swf_ActionGet(tag);
857 swf_DumpActions(actions, myprefix);
859 else if(tag->id == ST_DEFINEBUTTON && action) {
860 dumpButtonActions(tag, myprefix);
862 else if(tag->id == ST_DEFINEBUTTON2 && action) {
863 dumpButton2Actions(tag, myprefix);
865 else if(tag->id == ST_PLACEOBJECT2) {
866 handlePlaceObject2(tag, myprefix);
869 if(tag->len && used) {
870 int num = swf_GetNumUsedIDs(tag);
874 used = (int*)malloc(sizeof(int)*num);
875 swf_GetUsedIDs(tag, used);
876 printf("%s%suses IDs: ", indent, prefix);
878 swf_SetTagPos(tag, used[t]);
879 printf("%d%s", swf_GetU16(tag), t<num-1?", ":"");
885 if(tag->len && hex) {
886 hexdumpTag(tag, prefix);