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 -atMp\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 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 handleVideoStream(TAG*tag, char*prefix)
379 U16 id = swf_GetU16(tag);
380 U16 frames = swf_GetU16(tag);
381 U16 width = swf_GetU16(tag);
382 U16 height = swf_GetU16(tag);
383 U8 flags = swf_GetU8(tag); //5-2(videopacket 01=off 10=on)-1(smoothing 1=on)
384 U8 codec = swf_GetU8(tag);
385 printf(" (%d frames, %dx%d", frames, width, height);
389 printf(" sorenson h.263)");
391 printf(" codec 0x%02x)", codec);
393 void handleVideoFrame(TAG*tag, char*prefix)
395 U32 code, version, reference, sizeflags;
398 U16 id = swf_GetU16(tag);
399 U16 frame = swf_GetU16(tag);
400 U8 deblock,flags, tmp, bit;
401 U32 quantizer, extrainfo;
402 char*types[] = {"I-frame", "P-frame", "disposable P-frame", "<reserved>"};
403 printf(" (frame %d) ", frame);
405 /* video packet follows */
406 code = swf_GetBits(tag, 17);
407 version = swf_GetBits(tag, 5);
408 reference = swf_GetBits(tag, 8);
410 sizeflags = swf_GetBits(tag, 3);
413 case 0: width = swf_GetU8(tag); height = swf_GetU8(tag); break;
414 case 1: width = swf_GetBits(tag, 16); height = swf_GetBits(tag, 16); break;
415 case 2: width = 352; height = 288; break;
416 case 3: width = 176; height = 144; break;
417 case 4: width = 128; height = 96; break;
418 case 5: width = 320; height = 240; break;
419 case 6: width = 160; height = 120; break;
420 case 7: width = -1; height = -1;/*reserved*/ break;
422 printf("%dx%d ", width, height);
423 type = swf_GetBits(tag, 2);
424 printf("%s", types[type]);
428 void handlePlaceObject2(TAG*tag, char*prefix)
430 U8 flags = swf_GetU8(tag);
434 int ppos[3] = {0,0,0};
435 swf_GetU16(tag); //depth
437 if(flags&2) swf_GetU16(tag); //id
439 swf_GetMatrix(tag,&m);
441 ppos[0] += sprintf(pstr[0], "| Matrix ");
442 ppos[1] += sprintf(pstr[1], "| %5.3f %5.3f %6.2f ", m.sx/65536.0, m.r1/65536.0, m.tx/20.0);
443 ppos[2] += sprintf(pstr[2], "| %5.3f %5.3f %6.2f ", m.r0/65536.0, m.sy/65536.0, m.ty/20.0);
447 swf_GetCXForm(tag, &cx, 1);
449 ppos[0] += sprintf(pstr[0]+ppos[0], "| CXForm r g b a ");
450 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);
451 ppos[2] += sprintf(pstr[2]+ppos[2], "| add %4d %4d %4d %4d ", cx.r1, cx.g1, cx.b1, cx.a1);
455 U16 ratio = swf_GetU16(tag); //ratio
457 ppos[0] += sprintf(pstr[0]+ppos[0], "| Ratio ");
458 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", ratio);
459 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
463 U16 clip = swf_GetU16(tag); //clip
465 ppos[0] += sprintf(pstr[0]+ppos[0], "| Clip ");
466 ppos[1] += sprintf(pstr[1]+ppos[1], "| %-5d ", clip);
467 ppos[2] += sprintf(pstr[2]+ppos[2], "| ");
470 if(flags&32) { while(swf_GetU8(tag)); }
471 if(placements && ppos[0]) {
473 printf("%s %s\n", prefix, pstr[0]);
474 printf("%s %s\n", prefix, pstr[1]);
475 printf("%s %s", prefix, pstr[2]);
484 unknown = swf_GetU16(tag);
485 globalflags = swf_GetU16(tag);
487 printf("Unknown parameter field not zero: %04x\n", unknown);
490 printf("global flags: %04x\n", globalflags);
491 handlerflags = swf_GetU16(tag);
493 handlerflags = swf_GetU32(tag);
496 while(handlerflags) {
501 globalflags &= ~handlerflags;
502 printf("%s flags %08x ",prefix, handlerflags);
503 printhandlerflags(handlerflags);
504 length = swf_GetU32(tag);
505 printf(", %d bytes actioncode\n",length);
506 a = swf_ActionGet(tag);
507 swf_DumpActions(a,prefix);
510 handlerflags = is32?swf_GetU32(tag):swf_GetU16(tag);
512 if(globalflags) // should go to sterr.
513 printf("ERROR: unsatisfied handlerflags: %02x\n", globalflags);
515 printf(" has action code\n");
520 void handlePlaceObject(TAG*tag, char*prefix)
525 void fontcallback1(U16 id,U8 * name)
529 void fontcallback2(U16 id,U8 * name)
531 swf_FontExtract(&swf,id,&fonts[fontnum]);
535 void hexdumpTag(TAG*tag, char* prefix)
538 printf(" %s-=> ",prefix);
539 for(t=0;t<tag->len;t++) {
540 printf("%02x ", tag->data[t]);
541 if((t && ((t&15)==15)) || (t==tag->len-1))
546 printf("\n %s-=> ",prefix);
551 void handleExportAssets(TAG*tag, char* prefix)
557 num = swf_GetU16(tag);
560 id = swf_GetU16(tag);
561 name = swf_GetString(tag);
562 printf("%sexports %04d as \"%s\"\n", prefix, id, name);
566 void dumperror(const char* format, ...)
571 va_start(arglist, format);
572 vsprintf(buf, format, arglist);
576 printf("==== Error: %s ====\n", buf);
579 static char strbuf[800];
582 char* timestring(double f)
584 int hours = (int)(f/3600);
585 int minutes = (int)((f-hours*3600)/60);
586 int seconds = (int)((f-hours*3600-minutes*60));
587 int useconds = (int)((f-(int)f)*1000+0.5);
590 sprintf(&strbuf[bufpos], "%02d:%02d:%02d,%03d",hours,minutes,seconds,useconds);
591 return &strbuf[bufpos];
594 int main (int argc,char ** argv)
602 char issprite = 0; // are we inside a sprite definition?
605 char* spriteframelabel = 0;
606 char* framelabel = 0;
610 memset(idtab,0,65536);
612 processargs(argc, argv);
616 fprintf(stderr, "You must supply a filename.\n");
620 f = open(filename,O_RDONLY);
624 perror("Couldn't open file: ");
627 if FAILED(swf_ReadSWF(f,&swf))
629 fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
636 if(statbuf.st_size != swf.fileSize && !swf.compressed)
637 dumperror("Real Filesize (%d) doesn't match header Filesize (%d)",
638 statbuf.st_size, swf.fileSize);
639 filesize = statbuf.st_size;
644 xsize = (swf.movieSize.xmax-swf.movieSize.xmin)/20;
645 ysize = (swf.movieSize.ymax-swf.movieSize.ymin)/20;
649 printf("-X %d", xsize);
655 printf("-Y %d", ysize);
661 printf("-r %d", swf.frameRate*100/256);
667 printf("-f %d", swf.frameCount);
674 char*fileversions[] = {"","1,0,0,0", "2,0,0,0","3,0,0,0","4,0,0,0",
675 "5,0,0,0","6,0,23,0","7,0,0,0","8,0,0,0"};
676 if(swf.fileVersion>8) {
677 fprintf(stderr, "Fileversion>8\n");
680 printf("<OBJECT CLASSID=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\"\n"
682 //" BGCOLOR=#ffffffff\n"?
684 //http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,23,0?
685 " CODEBASE=\"http://active.macromedia.com/flash5/cabs/swflash.cab#version=%s\">\n"
686 " <PARAM NAME=\"MOVIE\" VALUE=\"%s\">\n"
687 " <PARAM NAME=\"PLAY\" VALUE=\"true\">\n"
688 " <PARAM NAME=\"LOOP\" VALUE=\"true\">\n"
689 " <PARAM NAME=\"QUALITY\" VALUE=\"high\">\n"
690 " <EMBED SRC=\"%s\" WIDTH=\"%d\" HEIGHT=\"%d\"\n" //bgcolor=#ffffff?
691 " PLAY=\"true\" ALIGN=\"\" LOOP=\"true\" QUALITY=\"high\"\n"
692 " TYPE=\"application/x-shockwave-flash\"\n"
693 " PLUGINSPAGE=\"http://www.macromedia.com/go/getflashplayer\">\n"
695 "</OBJECT>\n", xsize, ysize, fileversions[swf.fileVersion],
696 filename, filename, xsize, ysize);
699 printf("[HEADER] File version: %d\n", swf.fileVersion);
701 printf("[HEADER] File is zlib compressed.");
702 if(filesize && swf.fileSize)
703 printf(" Ratio: %02d%%\n", filesize*100/(swf.fileSize));
707 printf("[HEADER] File size: %ld%s\n", swf.fileSize, swf.compressed?" (Depacked)":"");
708 printf("[HEADER] Frame rate: %f\n",swf.frameRate/256.0);
709 printf("[HEADER] Frame count: %d\n",swf.frameCount);
710 printf("[HEADER] Movie width: %.2f",(swf.movieSize.xmax-swf.movieSize.xmin)/20.0);
711 if(swf.movieSize.xmin)
712 printf(" (left offset: %.2f)\n", swf.movieSize.xmin/20.0);
715 printf("[HEADER] Movie height: %.2f",(swf.movieSize.ymax-swf.movieSize.ymin)/20.0);
716 if(swf.movieSize.ymin)
717 printf(" (top offset: %.2f)\n", swf.movieSize.ymin/20.0);
725 swf_FontEnumerate(&swf,&fontcallback1);
726 fonts = (SWFFONT**)malloc(fontnum*sizeof(SWFFONT*));
728 swf_FontEnumerate(&swf,&fontcallback2);
732 char*name = swf_TagGetName(tag);
735 dumperror("Unknown tag:0x%03x", tag->id);
739 printf("[%03x] %9ld %s%s", tag->id, tag->len, prefix, swf_TagGetName(tag));
741 if(tag->id == ST_FREECHARACTER) {
742 U16 id = swf_GetU16(tag);
746 if(swf_isDefiningTag(tag)) {
747 U16 id = swf_GetDefineID(tag);
748 printf(" defines id %04d", id);
750 dumperror("Id %04d is defined more than once.", id);
753 else if(swf_isPseudoDefiningTag(tag)) {
754 U16 id = swf_GetDefineID(tag);
755 printf(" adds information to id %04d", id);
757 dumperror("Id %04d is not yet defined.\n", id);
759 else if(tag->id == ST_PLACEOBJECT) {
760 printf(" places id %04d at depth %04x", swf_GetPlaceID(tag), swf_GetDepth(tag));
762 printf(" name \"%s\"",swf_GetName(tag));
763 handlePlaceObject(tag, myprefix);
765 else if(tag->id == ST_PLACEOBJECT2) {
772 printf(" id %04d",swf_GetPlaceID(tag));
776 printf(" at depth %04d", swf_GetDepth(tag));
778 printf(" name \"%s\"",swf_GetName(tag));
780 else if(tag->id == ST_REMOVEOBJECT) {
781 printf(" removes id %04d from depth %04d", swf_GetPlaceID(tag), swf_GetDepth(tag));
783 else if(tag->id == ST_REMOVEOBJECT2) {
784 printf(" removes object from depth %04d", swf_GetDepth(tag));
786 else if(tag->id == ST_FREECHARACTER) {
787 printf(" frees object %04d", swf_GetPlaceID(tag));
789 else if(tag->id == ST_STARTSOUND) {
792 id = swf_GetU16(tag);
793 flags = swf_GetU8(tag);
795 printf(" stops sound with id %04d", id);
797 printf(" starts sound with id %04d", id);
799 printf(" (if not already playing)");
805 printf(" looping %d times", swf_GetU16(tag));
808 else if(tag->id == ST_FRAMELABEL) {
809 int l = strlen(tag->data);
810 printf(" \"%s\"", tag->data);
812 printf(" has %d extra bytes", tag->len-1-l);
813 if(tag ->len-1-l == 1 && tag->data[tag->len-1] == 1)
816 if((framelabel && !issprite) ||
817 (spriteframelabel && issprite)) {
818 dumperror("Frame %d has more than one label",
819 issprite?spriteframe:mainframe);
821 if(issprite) spriteframelabel = tag->data;
822 else framelabel = tag->data;
824 else if(tag->id == ST_SHOWFRAME) {
825 char*label = issprite?spriteframelabel:framelabel;
826 int frame = issprite?spriteframe:mainframe;
829 while(tag->next && tag->next->id == ST_SHOWFRAME && tag->next->len == 0) {
831 if(issprite) spriteframe++;
837 printf(" %d (%s)", frame, timestring(frame*(256.0/(swf.frameRate+0.1))));
839 printf(" %d-%d (%s-%s)", frame, nframe,
840 timestring(frame*(256.0/(swf.frameRate+0.1))),
841 timestring(nframe*(256.0/(swf.frameRate+0.1)))
844 printf(" (label \"%s\")", label);
845 if(issprite) {spriteframe++; spriteframelabel = 0;}
846 if(!issprite) {mainframe++; framelabel = 0;}
849 if(tag->id == ST_SETBACKGROUNDCOLOR) {
850 U8 r = swf_GetU8(tag);
851 U8 g = swf_GetU8(tag);
852 U8 b = swf_GetU8(tag);
853 printf(" (%02x/%02x/%02x)\n",r,g,b);
855 else if(tag->id == ST_DEFINEBITSLOSSLESS ||
856 tag->id == ST_DEFINEBITSLOSSLESS2) {
857 handleDefineBits(tag);
860 else if(tag->id == ST_DEFINESOUND) {
861 handleDefineSound(tag);
864 else if(tag->id == ST_VIDEOFRAME) {
865 handleVideoFrame(tag, myprefix);
868 else if(tag->id == ST_DEFINEVIDEOSTREAM) {
869 handleVideoStream(tag, myprefix);
872 else if(tag->id == ST_DEFINEEDITTEXT) {
876 else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) {
882 else if(tag->id == ST_PLACEOBJECT2) {
884 else if(tag->id == ST_NAMECHARACTER) {
886 printf(" \"%s\"\n", swf_GetString(tag));
892 sprintf(myprefix, " %s", prefix);
894 if(tag->id == ST_DEFINESPRITE) {
895 sprintf(prefix, " ");
897 dumperror("Sprite definition inside a sprite definition");
901 spriteframelabel = 0;
903 else if(tag->id == ST_END) {
906 spriteframelabel = 0;
908 dumperror("End Tag not empty");
910 else if(tag->id == ST_EXPORTASSETS) {
911 handleExportAssets(tag, myprefix);
913 else if(tag->id == ST_DOACTION && action) {
915 actions = swf_ActionGet(tag);
916 swf_DumpActions(actions, myprefix);
918 else if(tag->id == ST_DEFINEBUTTON && action) {
919 dumpButtonActions(tag, myprefix);
921 else if(tag->id == ST_DEFINEBUTTON2 && action) {
922 dumpButton2Actions(tag, myprefix);
924 else if(tag->id == ST_PLACEOBJECT2) {
925 handlePlaceObject2(tag, myprefix);
928 if(tag->len && used) {
929 int num = swf_GetNumUsedIDs(tag);
933 used = (int*)malloc(sizeof(int)*num);
934 swf_GetUsedIDs(tag, used);
935 printf("%s%suses IDs: ", indent, prefix);
937 swf_SetTagPos(tag, used[t]);
938 printf("%d%s", swf_GetU16(tag), t<num-1?", ":"");
944 if(tag->len && hex) {
945 hexdumpTag(tag, prefix);