new parameter -s textonly
[swftools.git] / lib / modules / swftools.c
1 /* swftools.c
2
3    Math and matrix functions, misc tools
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 // Matrix & Math tools for SWF files
25
26 #include "../rfxswf.h"
27
28 #define S64 long long
29 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
30 { S64 a = ((S64)a1*(S64)b1+(S64)a2*(S64)b2)>>16;
31   SFIXED result = (SFIXED)(a);
32   if(a!=result) 
33       fprintf(stderr, "Warning: overflow in matrix multiplication\n");
34   return result;
35 }
36 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
37 { S64 z = zaehler<<16;
38   S64 a = z/(S64)nenner;
39   return (SFIXED)a;
40 }
41 #undef S64
42
43 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
44 {        
45   if (!d) return NULL;
46   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
47   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
48   
49   d->tx = s1->tx + RFXSWF_SP(s1->sx,s1->r1,s2->tx,s2->ty);
50   d->ty = s1->ty + RFXSWF_SP(s1->r0,s1->sy,s2->tx,s2->ty);
51   
52   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
53   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
54
55   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
56   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
57
58   //DumpMatrix(NULL,d);
59   
60   return d;
61 }
62
63 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
64                                int x1,int y1,int x2,int y2)
65 { int dx1 = x1 - x0;
66   int dy1 = y1 - y0;
67   int dx2 = x2 - x0;
68   int dy2 = y2 - y0;
69   
70   if (!m) return NULL;
71   if ((!dx)||(!dy)) return NULL; // check DIV by zero
72
73   m->tx = x0;
74   m->ty = y0;
75   m->sx = RFXSWF_QFIX(dx1,dx);
76   m->sy = RFXSWF_QFIX(dy2,dy);
77   m->r0 = RFXSWF_QFIX(dy1,dx);
78   m->r1 = RFXSWF_QFIX(dx2,dy);
79   
80   return m;
81 }
82
83 void swf_SetDefineID(TAG * tag, U16 newid)
84 {
85   int oldlen = tag->len;
86   tag->len = 0;
87   swf_SetU16(tag, newid); /* set defining ID */
88   tag->len = oldlen;
89 }
90
91 U16 swf_GetDefineID(TAG * t)
92 // up to SWF 4.0
93 { U32 oldTagPos;
94   U16 id = 0;
95
96   oldTagPos = swf_GetTagPos(t);
97   swf_SetTagPos(t,0);
98
99   switch (swf_GetTagID(t))
100   { case ST_DEFINESHAPE:
101     case ST_DEFINESHAPE2:
102     case ST_DEFINESHAPE3:
103     case ST_DEFINESHAPE4:
104     case ST_DEFINEMORPHSHAPE:
105     case ST_DEFINEMORPHSHAPE2:
106     case ST_DEFINEEDITTEXT:
107     case ST_DEFINEBITS:
108     case ST_DEFINEBITSJPEG2:
109     case ST_DEFINEBITSJPEG3:
110     case ST_DEFINEBITSLOSSLESS:
111     case ST_DEFINEBITSLOSSLESS2:
112     case ST_DEFINESCALINGGRID: //pseudodefine
113     case ST_DEFINEBUTTON:
114     case ST_DEFINEBUTTON2:
115     case ST_DEFINEBUTTONCXFORM: //pseudodefine
116     case ST_DEFINEBUTTONSOUND: //pseudodefine
117     case ST_CSMTEXTSETTINGS: //pseudodefine
118     case ST_DEFINEFONT:
119     case ST_DEFINEFONT2:
120     case ST_DEFINEFONT3:
121     case ST_DEFINEFONTINFO: //pseudodefine
122     case ST_DEFINEFONTINFO2: //pseudodefine
123     case ST_DEFINEFONTALIGNZONES: //pseudodefine
124     case ST_DEFINEFONTNAME: //pseudodefine
125     case ST_DEFINETEXT:
126     case ST_DEFINEBINARY:
127     case ST_DEFINETEXT2:
128     case ST_DEFINESOUND:
129     case ST_DEFINESPRITE:
130     case ST_DEFINEMOVIE:
131     case ST_DEFINEVIDEOSTREAM:
132     case ST_GLYPHNAMES: //pseudodefine
133     case ST_VIDEOFRAME: //pseudodefine
134     case ST_NAMECHARACTER: //pseudodefine
135     case ST_DOINITACTION: //pseudodefine
136       id = swf_GetU16(t);
137       break;
138     default:
139       fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
140   }
141
142   swf_SetTagPos(t,oldTagPos);
143
144   return id;
145 }
146
147 SRECT swf_GetDefineBBox(TAG * t)
148 {
149   U32 oldTagPos;
150   U16 id = 0;
151   SRECT b1,b2;
152   memset(&b1, 0, sizeof(b1));
153
154   oldTagPos = swf_GetTagPos(t);
155   swf_SetTagPos(t,0);
156
157   swf_GetRect(0, &b1);
158
159   switch (swf_GetTagID(t))
160   { case ST_DEFINESHAPE:
161     case ST_DEFINESHAPE2:
162     case ST_DEFINESHAPE3:
163     case ST_DEFINESHAPE4:
164     case ST_DEFINEEDITTEXT:
165     case ST_DEFINETEXT:
166     case ST_DEFINETEXT2:
167     case ST_DEFINEVIDEOSTREAM:
168       id = swf_GetU16(t);
169       swf_GetRect(t, &b1);
170       break;
171     case ST_DEFINEMORPHSHAPE:
172       id = swf_GetU16(t);
173       swf_GetRect(t, &b1);
174       swf_GetRect(t, &b2);
175       swf_ExpandRect2(&b1, &b2);
176       break;
177     case ST_DEFINEBITSLOSSLESS:
178     case ST_DEFINEBITSLOSSLESS2:
179     case ST_DEFINEBITS:
180     case ST_DEFINEBITSJPEG2:
181     case ST_DEFINEBITSJPEG3:
182       // FIXME
183       break;
184   }
185
186   swf_SetTagPos(t,oldTagPos);
187
188   return b1;
189 }
190
191 U16 swf_GetPlaceID(TAG * t)
192 // up to SWF 4.0
193 { U32 oldTagPos;
194   U16 id = 0;
195
196   oldTagPos = swf_GetTagPos(t);
197   swf_SetTagPos(t,0);
198
199   switch (swf_GetTagID(t))
200   { case ST_PLACEOBJECT:
201     case ST_REMOVEOBJECT:
202     case ST_FREECHARACTER:
203     case ST_STARTSOUND:
204       id = swf_GetU16(t);
205       break;
206
207     case ST_PLACEOBJECT2:
208     { U8 flags = swf_GetU8(t);
209       U16 d = swf_GetU16(t);
210       id = (flags&PF_CHAR)?swf_GetU16(t):id;
211     } break;
212     case ST_PLACEOBJECT3:
213     { U8 flags = swf_GetU8(t);
214       U8 flags2 = swf_GetU8(t);
215       U16 d = swf_GetU16(t);
216       id = (flags&PF_CHAR)?swf_GetU16(t):id;
217     } break;
218
219   }
220
221   swf_SetTagPos(t,oldTagPos);
222
223   return id;
224 }
225
226 static int swf_definingtagids[] =
227 {ST_DEFINESHAPE,
228  ST_DEFINESHAPE2,
229  ST_DEFINESHAPE3,
230  ST_DEFINESHAPE4,
231  ST_DEFINEMORPHSHAPE,
232  ST_DEFINEMORPHSHAPE2,
233  ST_DEFINEFONT,
234  ST_DEFINEFONT2,
235  ST_DEFINEFONT3,
236  ST_DEFINETEXT,
237  ST_DEFINETEXT2,
238  ST_DEFINEEDITTEXT,
239  ST_DEFINEBITS,
240  ST_DEFINEBITSJPEG2,
241  ST_DEFINEBITSJPEG3,
242  ST_DEFINEBITSLOSSLESS,
243  ST_DEFINEBITSLOSSLESS2,
244  ST_DEFINEMOVIE,
245  ST_DEFINESPRITE,
246  ST_DEFINEBUTTON,
247  ST_DEFINEBUTTON2,
248  ST_DEFINESOUND,
249  ST_DEFINEVIDEOSTREAM,
250  ST_DEFINEBINARY,
251  -1
252 };
253
254 // tags which may be used inside a sprite definition
255 static int swf_spritetagids[] =
256 {ST_SHOWFRAME,
257  ST_PLACEOBJECT,
258  ST_PLACEOBJECT2,
259  ST_PLACEOBJECT3,
260  ST_REMOVEOBJECT,
261  ST_REMOVEOBJECT2,
262  ST_DOACTION,
263  ST_DOABC,
264  ST_STARTSOUND,
265  ST_FRAMELABEL,
266  ST_SOUNDSTREAMHEAD,
267  ST_SOUNDSTREAMHEAD2,
268  ST_SOUNDSTREAMBLOCK,
269  ST_END,
270  -1
271 };
272
273 /* tags which add content or information to a character with a given ID */
274 static int swf_pseudodefiningtagids[] = 
275 {
276  ST_DEFINEFONTINFO,
277  ST_DEFINEFONTINFO2,
278  ST_DEFINEFONTALIGNZONES,
279  ST_DEFINEFONTNAME,
280  ST_DEFINEBUTTONCXFORM,
281  ST_DEFINEBUTTONSOUND,
282  ST_DEFINESCALINGGRID,
283  ST_CSMTEXTSETTINGS,
284  ST_NAMECHARACTER,
285  ST_DOINITACTION,
286  ST_VIDEOFRAME,
287  ST_GLYPHNAMES,
288  -1
289 };
290
291 U8 swf_isAllowedSpriteTag(TAG * tag)
292 {
293     int id = tag->id;
294     int t=0;
295     while(swf_spritetagids[t]>=0)
296     {
297         if(swf_spritetagids[t] == id) 
298             return 1;
299         t++;
300     }
301     return 0; 
302 }
303
304 U8 swf_isDefiningTag(TAG * tag)
305 {
306     int id = tag->id;
307     int t=0;
308     while(swf_definingtagids[t]>=0)
309     {
310         if(swf_definingtagids[t] == id) 
311             return 1;
312         t++;
313     }
314     return 0; 
315 }
316
317 U8 swf_isPseudoDefiningTag(TAG * tag)
318 {
319     int id = tag->id;
320     int t=0;
321     while(swf_pseudodefiningtagids[t]>=0)
322     {
323         if(swf_pseudodefiningtagids[t] == id) 
324             return 1;
325         t++;
326     }
327     return 0; 
328 }
329
330 int swf_GetDepth(TAG * t)
331
332   int depth = -1;
333   U32 oldTagPos;
334   oldTagPos = swf_GetTagPos(t);
335   swf_SetTagPos(t,0);
336
337   switch (swf_GetTagID(t))
338   { case ST_PLACEOBJECT:
339     case ST_REMOVEOBJECT:
340       swf_GetU16(t); //id
341       depth = swf_GetU16(t);
342       break;
343     case ST_REMOVEOBJECT2:
344       depth = swf_GetU16(t);
345       break;
346     case ST_PLACEOBJECT2:
347     { U8 flags = swf_GetU8(t);
348       depth = swf_GetU16(t);
349     } break;
350     case ST_PLACEOBJECT3:
351     { U8 flags = swf_GetU8(t);
352       U8 flags2 = swf_GetU8(t);
353       depth = swf_GetU16(t);
354     } break;
355     case ST_SETTABINDEX:
356     {
357       depth = swf_GetU16(t);
358     }
359   }
360   swf_SetTagPos(t,oldTagPos);
361   return depth;
362 }
363
364 void swf_SetDepth(TAG * t, U16 depth)
365
366   switch (swf_GetTagID(t))
367   { case ST_PLACEOBJECT:
368     case ST_REMOVEOBJECT:
369       PUT16(t->data, depth);
370       break;
371     case ST_REMOVEOBJECT2:
372       PUT16(t->data, depth);
373       break;
374     case ST_PLACEOBJECT2:
375       PUT16(&t->data[1], depth);
376       break;
377     case ST_SETTABINDEX:
378       PUT16(t->data, depth);
379       break;
380     default:
381       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
382   }
383 }
384
385 char* swf_GetName(TAG * t)
386 {
387     char* name = 0;
388     U32 oldTagPos;
389     MATRIX m;
390     CXFORM c;
391     oldTagPos = swf_GetTagPos(t);
392     swf_SetTagPos(t,0);
393     switch(swf_GetTagID(t))
394     {
395         case ST_FRAMELABEL:
396             name = (char*)&t->data[swf_GetTagPos(t)];
397         break;
398         case ST_PLACEOBJECT3:
399         case ST_PLACEOBJECT2: {   
400             U8 flags = swf_GetU8(t);
401             if(t->id == ST_PLACEOBJECT3)
402                 swf_GetU8(t);
403             swf_GetU16(t); //depth;
404             if(flags&PF_CHAR) 
405               swf_GetU16(t); //id
406             if(flags&PF_MATRIX)
407               swf_GetMatrix(t, &m);
408             if(flags&PF_CXFORM)
409               swf_GetCXForm(t, &c, 1);
410             if(flags&PF_RATIO)
411               swf_GetU16(t);
412             if(flags&PF_CLIPDEPTH)
413               swf_GetU16(t);
414             if(flags&PF_NAME) {
415               swf_ResetReadBits(t);
416               name = (char*)&t->data[swf_GetTagPos(t)];
417             }
418         }
419         break;
420     }
421     swf_SetTagPos(t,oldTagPos);
422     return name;
423 }
424
425 /* used in enumerateUsedIDs */
426 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
427 {
428     int t;
429     int num = swf_GetU8(tag) & 15;
430     if(gradient1) gradient1->num = num;
431     if(gradient2) gradient2->num = num;
432     
433     if(gradient1) {
434         gradient1->num = num;
435         gradient1->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient1->num);
436         gradient1->ratios = (U8*)rfx_calloc(sizeof(gradient1->ratios[0])*gradient1->num);
437     }
438     if(gradient2) {
439         gradient2->num = num;
440         gradient2->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient2->num);
441         gradient2->ratios = (U8*)rfx_calloc(sizeof(gradient2->ratios[0])*gradient2->num);
442     }
443     for(t=0;t<num;t++)
444     {
445         U8 ratio;
446         RGBA color;
447         
448         ratio = swf_GetU8(tag);
449         swf_GetRGBA(tag, &color);
450         if(gradient1) {
451             gradient1->ratios[t] = ratio;
452             gradient1->rgba[t] = color;
453         }
454
455         ratio = swf_GetU8(tag);
456         swf_GetRGBA(tag, &color);
457         if(gradient2) {
458             gradient2->ratios[t] = ratio;
459             gradient2->rgba[t] = color;
460         }
461     }
462 }
463
464 #define DEBUG_ENUMERATE if(0)
465 //#define DEBUG_ENUMERATE
466
467 void enumerateUsedIDs_fillstyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
468 {
469     int type;
470     type = swf_GetU8(tag); //type
471     DEBUG_ENUMERATE printf("fill style %d) type=%02x (tagpos=%d)\n", t, type, tag->pos);
472     if(type == 0) {
473         RGBA color;
474         if(num >= 3)
475             {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
476         else 
477             {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
478         DEBUG_ENUMERATE printf("               %02x%02x%02x%02x\n", color.r,color.g,color.b,color.a);
479     }
480     else if(type == 0x10 || type == 0x12 || type == 0x13)
481     {
482         swf_ResetReadBits(tag);
483         MATRIX m;
484         swf_GetMatrix(tag, &m);
485         DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
486         if(morph) {
487             swf_GetMatrix(tag, &m);
488             DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
489         }
490         swf_ResetReadBits(tag);
491         if(morph) {
492             swf_GetMorphGradient(tag, NULL, NULL);
493             if(type == 0x13) {
494                 swf_GetU16(tag);
495                 swf_GetU16(tag);
496             }
497         } else {
498             GRADIENT g;
499             swf_GetGradient(tag, &g, /*alpha*/ num>=3?1:0);
500             DEBUG_ENUMERATE swf_DumpGradient(stdout, &g);
501             if(type == 0x13)
502                 swf_GetU16(tag);
503         }
504     }
505     else if(type == 0x40 || type == 0x41 || type == 0x42 || type == 0x43)
506     {
507         swf_ResetReadBits(tag);
508         if(tag->data[tag->pos] != 0xff ||
509            tag->data[tag->pos+1] != 0xff)
510         (callback)(tag, tag->pos, callback_data);
511
512         swf_GetU16(tag);
513         swf_ResetReadBits(tag);
514         swf_GetMatrix(tag, NULL);
515         if(morph)
516             swf_GetMatrix(tag, NULL);
517     }
518     else {
519         fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x in tag %02d\n",type, tag->id);
520     }
521 }
522
523 void enumerateUsedIDs_linestyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
524 {
525     U16  width;
526     RGBA color;
527     width = swf_GetU16(tag);
528     char fill=0;
529     if(morph)
530         swf_GetU16(tag);
531     if(num >= 4) {
532         U16 flags = swf_GetU16(tag);
533         DEBUG_ENUMERATE printf("line style %d) flags: %08x\n", t, flags);
534         if((flags & 0x30) == 0x20) {
535             U16 miter = swf_GetU16(tag); // miter limit
536             DEBUG_ENUMERATE printf("line style %d) miter join: %08x\n", t, miter);
537         }
538         if(flags & 0x08) {
539             fill = 1;
540         }
541     }
542     if(!fill) {
543         if(num >= 3)
544             {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
545         else
546             {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
547     } else {
548         enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
549     }
550     DEBUG_ENUMERATE printf("line style %d) width=%.2f color=%02x%02x%02x%02x \n", t, width/20.0, color.r,color.g,color.b,color.a);
551 }
552
553 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
554 {
555     U16 count;
556     int t;
557     count = swf_GetU8(tag);
558     if(count == 0xff && num>1) // defineshape2,3,4 only
559         count = swf_GetU16(tag);
560
561     DEBUG_ENUMERATE printf("%d fill styles\n", count);
562     for(t=0;t<count;t++)
563     {
564         enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
565     }
566     swf_ResetReadBits(tag);
567     count = swf_GetU8(tag); // line style array
568     if(count == 0xff)
569         count = swf_GetU16(tag);
570     DEBUG_ENUMERATE printf("%d line styles\n", count);
571     for(t=0;t<count;t++) 
572     {
573         enumerateUsedIDs_linestyle(tag, t, callback, callback_data, num, morph);
574     }
575 }
576
577 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
578 {
579     int num = 1;
580     swf_ResetReadBits(tag);
581     tag->pos = 0;
582     switch(tag->id)
583     {
584         case ST_DEFINEBUTTONSOUND: {
585             int t;
586             callback(tag, tag->pos + base, callback_data);
587             swf_GetU16(tag); //button id
588             for(t=0;t<4;t++) {
589                 int flags;
590                 callback(tag, tag->pos + base, callback_data);
591                 U16 sound_id = swf_GetU16(tag); //sound id
592                 if(!sound_id)
593                     continue;
594                 flags = swf_GetU8(tag);
595                 if(flags&1)
596                     swf_GetU32(tag); // in point
597                 if(flags&2)
598                     swf_GetU32(tag); // out points
599                 if(flags&4)
600                     swf_GetU16(tag); // loop count
601                 if(flags&8)
602                 {
603                     int npoints = swf_GetU8(tag);
604                     int s;
605                     for(s=0;s<npoints;s++)
606                     {
607                         swf_GetU32(tag);
608                         swf_GetU16(tag);
609                         swf_GetU16(tag);
610                     }
611                 }
612             }
613         } break;
614         case ST_DEFINEBUTTONCXFORM:
615             callback(tag, tag->pos + base, callback_data); //button id
616         break;
617
618         case ST_SYMBOLCLASS:
619         case ST_EXPORTASSETS: {
620             int num =  swf_GetU16(tag);
621             int t;
622             for(t=0;t<num;t++) {
623                 callback(tag, tag->pos + base, callback_data); //button id
624                 swf_GetU16(tag); //id
625                 while(swf_GetU8(tag)); //name
626             }
627         } break;
628
629         case ST_IMPORTASSETS: 
630         case ST_IMPORTASSETS2: {
631             swf_GetString(tag); //count
632             swf_GetU8(tag); //reserved
633             swf_GetU8(tag); //reserved
634             int num =  swf_GetU16(tag); //url
635             int t;
636             for(t=0;t<num;t++) {
637                 callback(tag, tag->pos + base, callback_data); //button id
638                 swf_GetU16(tag); //id
639                 while(swf_GetU8(tag)); //name
640             }
641         } break;
642
643         case ST_DOABC:
644         case ST_RAWABC:
645         break;
646
647         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
648         case ST_NAMECHARACTER:
649         case ST_DEFINEFONTNAME:
650         case ST_GENERATORTEXT:
651             callback(tag, tag->pos + base, callback_data);
652         break;
653         case ST_PLACEOBJECT:
654             callback(tag, tag->pos + base, callback_data);
655         break;
656         case ST_PLACEOBJECT2:
657             // only if placeflaghascharacter
658             if(!(tag->data[0]&2))
659                 break;
660             callback(tag, 3 + base, callback_data);
661         break;
662         case ST_PLACEOBJECT3:
663             // only if placeflaghascharacter
664             if(!(tag->data[0]&2))
665                 break;
666             callback(tag, 4 + base, callback_data);
667         break;
668         case ST_REMOVEOBJECT:
669             callback(tag, tag->pos + base, callback_data);
670         break;
671         case ST_STARTSOUND:
672             callback(tag, tag->pos + base, callback_data);
673         break;
674         case ST_DEFINESPRITE: {
675             if(tag->len <= 4)
676                 break; // sprite is expanded
677
678             swf_GetU16(tag); // id
679             swf_GetU16(tag); // framenum
680
681             while(1) {
682                 U16 flags = swf_GetU16(tag);
683                 U32 len;
684                 U16 id = flags>>6;
685                 TAG *tag2 = swf_InsertTag(NULL, id);
686                 len = flags&0x3f;
687                 if(len == 63)
688                     len = swf_GetU32(tag);
689                 if(id == ST_END)
690                     break;
691                 tag2->len = tag2->memsize = len;
692                 tag2->data = (U8*)rfx_alloc(len);
693                 memcpy(tag2->data, &tag->data[tag->pos], len);
694                 /* I never saw recursive sprites, but they are (theoretically) 
695                    possible, so better add base here again */
696                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
697                 swf_DeleteTag(0, tag2);
698                 swf_GetBlock(tag, NULL, len);
699             }
700         } 
701         break;
702         case ST_DEFINEBUTTON2: // has some font ids in the button records
703             num++; 
704         //fallthrough
705         case ST_DEFINEBUTTON: {
706             swf_GetU16(tag); //button id
707             if(num>1)
708             { 
709                 int offset;
710                 swf_GetU8(tag); //flag
711                 offset = swf_GetU16(tag); //offset
712             }
713             while(1)
714             {
715                 U8 flags = swf_GetU8(tag);
716                 if(!flags) //flags
717                     break; 
718                 callback(tag, tag->pos + base, callback_data);
719                 swf_GetU16(tag); //char
720                 swf_GetU16(tag); //layer
721                 swf_ResetReadBits(tag);
722                 swf_GetMatrix(tag, NULL);
723                 if(num>1) {
724                   swf_ResetReadBits(tag);
725                   swf_GetCXForm(tag, NULL, 1);
726                 }
727                 if(flags&0x10) {
728                     U8 num = swf_GetU8(tag);
729                     int t;
730                     for(t=0;t<num;t++) {
731                         swf_DeleteFilter(swf_GetFilter(tag));
732                     }
733                 }
734                 if(flags&0x20) {
735                     U8 blendmode = swf_GetU8(tag);
736                 }
737             }
738             // ...
739         }
740         break;
741         case ST_DEFINEEDITTEXT:  {
742             U8 flags1,flags2;
743             swf_GetU16(tag); //id
744             swf_GetRect(tag, NULL); //bounding box
745             swf_ResetReadBits(tag);
746             flags1 = swf_GetU8(tag);
747             flags2 = swf_GetU8(tag);
748             if(flags1 & 1)
749                 callback(tag, tag->pos + base, callback_data);
750         }
751         break;
752         case ST_DEFINETEXT2:
753             num ++;
754         case ST_DEFINETEXT: { 
755             int glyphbits, advancebits;
756             int id;
757             id = swf_GetU16(tag); //id
758             swf_GetRect(tag, NULL); //bounding box
759             swf_ResetReadBits(tag);
760             swf_GetMatrix(tag, NULL); //matrix
761             swf_ResetReadBits(tag);
762             glyphbits = swf_GetU8(tag); //glyphbits
763             advancebits = swf_GetU8(tag); //advancebits
764             while(1) {
765                 U16 flags;
766                 int t;
767                 swf_ResetReadBits(tag);
768                 flags = swf_GetBits(tag, 8);
769                 if(!flags) break;
770                 
771                 swf_ResetReadBits(tag);
772                 if(flags & 8) { // hasfont
773                     callback(tag, tag->pos + base, callback_data);
774                     id = swf_GetU16(tag);
775                 }
776                 if(flags & 4) { // hascolor
777                     if(num==1) swf_GetRGB(tag, NULL);
778                     else       swf_GetRGBA(tag, NULL);
779                 }
780                 if(flags & 2) { //has x offset
781                     swf_ResetReadBits(tag);
782                     swf_GetU16(tag);
783                 }
784                 if(flags & 1) { //has y offset
785                     swf_ResetReadBits(tag);
786                     swf_GetU16(tag);
787                 }
788                 if(flags & 8) { //has height
789                     swf_ResetReadBits(tag);
790                     swf_GetU16(tag);
791                 }
792                 
793                 flags = swf_GetBits(tag, 8);
794                 if(!flags) break;
795                 swf_ResetReadBits(tag);
796                 for(t=0;t<flags;t++) {
797                     swf_GetBits(tag, glyphbits);
798                     swf_GetBits(tag, advancebits);
799                 }
800             }
801             break;
802         }
803         case ST_DEFINEFONTALIGNZONES:
804         case ST_DEFINESCALINGGRID:
805         case ST_GLYPHNAMES:
806         case ST_CSMTEXTSETTINGS:
807         case ST_DEFINEFONTINFO:
808         case ST_DEFINEFONTINFO2:
809         case ST_VIDEOFRAME:
810             callback(tag, tag->pos + base, callback_data);
811         break;
812         case ST_DEFINEVIDEOSTREAM:
813         break;
814
815         case ST_DOINITACTION:
816             callback(tag, tag->pos + base, callback_data);
817         break;
818
819         case ST_DEFINEMORPHSHAPE2:
820         case ST_DEFINESHAPE4:
821         num++;
822         case ST_DEFINEMORPHSHAPE:
823         case ST_DEFINESHAPE3:
824         num++; //fallthrough
825         case ST_DEFINESHAPE2:
826         num++; //fallthrough
827         case ST_DEFINESHAPE: {
828             int fillbits;
829             int linebits;
830             int id; 
831             int numshapes = 1;
832             int morph = 0;
833             if(tag->id == ST_DEFINEMORPHSHAPE || tag->id==ST_DEFINEMORPHSHAPE2) {
834                 numshapes = 2;
835                 morph = 1;
836             }
837
838             id = swf_GetU16(tag); // id;
839             SRECT r={0,0,0,0},r2={0,0,0,0};
840             swf_GetRect(tag, &r); // shape bounds
841             if(morph) {
842                 swf_ResetReadBits(tag);
843                 swf_GetRect(tag, NULL); // shape bounds2
844                 if(num>=4) {
845                     swf_ResetReadBits(tag);
846                     swf_GetRect(tag, NULL); // edge bounds1
847                 }
848             }
849             if(num>=4) {
850                 swf_ResetReadBits(tag);
851                 swf_GetRect(tag, &r2); // edge bounds
852                 U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
853                 DEBUG_ENUMERATE printf("flags: %02x (1=scaling strokes, 2=non-scaling strokes)\n", flags);
854             }
855             if(morph) {
856                 swf_GetU32(tag); //offset to endedges
857             }
858    
859             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
860             DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r.xmin/20.0,r.ymin/20.0,r.xmax/20.0,r.ymax/20.0);
861             DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r2.xmin/20.0,r2.ymin/20.0,r2.xmax/20.0,r2.ymax/20.0);
862
863             DEBUG_ENUMERATE printf("style tag pos: %d\n", tag->pos);
864             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
865             DEBUG_ENUMERATE printf("-------\n");
866             swf_ResetReadBits(tag);
867             while(--numshapes>=0) /* morph shapes define two shapes */
868             {
869                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
870                 fillbits = swf_GetBits(tag, 4);
871                 linebits = swf_GetBits(tag, 4);
872                 DEBUG_ENUMERATE printf("fillbits=%d linebits=%d\n", fillbits, linebits);
873                 swf_ResetReadBits(tag);
874                 int x=0,y=0;
875                 while(1) {
876                     int flags;
877                     flags = swf_GetBits(tag, 1);
878                     if(!flags) { //style change
879                         flags = swf_GetBits(tag, 5);
880                         if(!flags)
881                             break;
882                         if(flags&1) { //move
883                             int n = swf_GetBits(tag, 5); 
884                             x = swf_GetBits(tag, n); //x
885                             y = swf_GetBits(tag, n); //y
886                             DEBUG_ENUMERATE printf("moveTo %.2f %.2f\n",x/20.0,y/20.0);
887                         }
888                         if(flags&2) { //fill0
889                             int fill0;
890                             fill0 = swf_GetBits(tag, fillbits); 
891                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
892                         }
893                         if(flags&4) { //fill1
894                             int fill1;
895                             fill1 = swf_GetBits(tag, fillbits); 
896                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
897                         }
898                         if(flags&8) { //linestyle
899                             int line;
900                             line = swf_GetBits(tag, linebits); 
901                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
902                         }
903                         if(flags&16) {
904                             DEBUG_ENUMERATE printf("more fillstyles\n");
905                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
906                             fillbits = swf_GetBits(tag, 4);
907                             linebits = swf_GetBits(tag, 4);
908                         }
909                     } else {
910                         flags = swf_GetBits(tag, 1);
911                         if(flags) { //straight edge
912                             int n = swf_GetBits(tag, 4) + 2;
913                             if(swf_GetBits(tag, 1)) { //line flag
914                                 x += swf_GetSBits(tag, n); //delta x
915                                 y += swf_GetSBits(tag, n); //delta y
916                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f\n",x/20.0,y/20.0);
917                             } else {
918                                 int v=swf_GetBits(tag, 1);
919                                 int d;
920                                 d = swf_GetSBits(tag, n); //vert/horz
921                                 if(!v)
922                                     x += d;
923                                 else
924                                     y += d;
925                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f (%s)\n",x/20.0,y/20.0, v?"vertical":"horizontal");
926                             }
927                         } else { //curved edge
928                             int n = swf_GetBits(tag, 4) + 2;
929                             int x1,y1,x2,y2;
930                             x1 = swf_GetSBits(tag, n);
931                             y1 = swf_GetSBits(tag, n);
932                             x2 = swf_GetSBits(tag, n);
933                             y2 = swf_GetSBits(tag, n);
934                             DEBUG_ENUMERATE printf("splineTo %.2f %.2f %.2f %.2f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
935                         }
936                     }
937                 }
938             }
939         }
940         break;
941         default:
942         break;
943     }
944 }
945
946 void callbackCount(TAG * t,int pos, void*ptr)
947 {
948     (*(int*)ptr)++;
949     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
950 }
951
952 void callbackFillin(TAG * t,int pos, void*ptr)
953 {
954     **(int**)ptr = pos;
955     (*(int**)ptr)++;
956     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
957 }
958
959 int swf_GetNumUsedIDs(TAG * t)
960 {
961     int num = 0;
962     enumerateUsedIDs(t, 0, callbackCount, &num);
963     return num;
964 }
965
966 void swf_GetUsedIDs(TAG * t, int * positions)
967 {
968     int * ptr = positions;
969     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
970 }
971
972 char swf_Relocate (SWF*swf, char*bitmap)
973 {
974     TAG*tag;
975     int slaveids[65536];
976     memset(slaveids, -1, sizeof(slaveids));
977     tag = swf->firstTag;
978     char ok = 1;
979
980     int current_id=0;
981 #define NEW_ID(n) \
982                 for(current_id++;current_id<65536;current_id++) { \
983                     if(!bitmap[current_id]) { \
984                         n = current_id; \
985                         break; \
986                     } \
987                 } \
988                 if(current_id==65536) { \
989                     fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs\n"); \
990                     return 0; \
991                 }
992
993     while(tag)
994     {
995         int num; 
996         int *ptr;
997
998         if(swf_isDefiningTag(tag))
999         {
1000             int newid;
1001             int id;
1002             
1003             id = swf_GetDefineID(tag); //own id
1004
1005             if(!bitmap[id]) { //free
1006                 newid = id;
1007             } else if(slaveids[id]>0) {
1008                 newid = slaveids[id];
1009             } else {
1010                 NEW_ID(newid);
1011             }
1012
1013             bitmap[newid] = 1;
1014             slaveids[id] = newid;
1015
1016             swf_SetDefineID(tag, newid);
1017         } 
1018         
1019         num = swf_GetNumUsedIDs(tag);
1020         if(num) {
1021             ptr = (int*)rfx_alloc(sizeof(int)*num);
1022             swf_GetUsedIDs(tag, ptr);
1023             int t;
1024             for(t=0;t<num;t++) {
1025                 int id = GET16(&tag->data[ptr[t]]);
1026                 if(slaveids[id]<0) {
1027                     if(!id && bitmap[id]) {
1028                         /* id 0 is only used in SWF versions >=9. It's the ID of
1029                            the main timeline. It's used in e.g. SYMBOLTAG tags, but
1030                            never defined, so if we're asked to reallocate it, we have 
1031                            to allocate an ID for it on the fly. */
1032                         int newid;
1033                         NEW_ID(newid);
1034                         bitmap[newid] = 1;
1035                         slaveids[id] = newid;
1036                         id = newid;
1037                     } else if(!bitmap[id]) {
1038                         /* well- we don't know this id, but it's not reserved anyway, so just
1039                            leave it alone */
1040                     } else {
1041                         /* this actually happens with files created with Flash CS4 and never.
1042                            Apparently e.g. DefineButton tags are able to use forward declarations of objects. */
1043                         fprintf(stderr, "warning: Mapping id (%d) never encountered before in %s\n", id,
1044                                 swf_TagGetName(tag));
1045                         int newid;
1046                         NEW_ID(newid);
1047                         id = slaveids[id] = newid;
1048                         ok = 0;
1049                     }
1050                 } else {
1051                     id = slaveids[id];
1052                 }
1053                 PUT16(&tag->data[ptr[t]], id);
1054             }
1055             free(ptr);
1056         }
1057         tag=tag->next;
1058     }
1059     return ok;
1060 }
1061
1062 /* untested */
1063 void swf_Relocate2(SWF*swf, int*id2id)
1064 {
1065     TAG*tag;
1066     tag = swf->firstTag;
1067     while(tag) {
1068         if(swf_isDefiningTag(tag)) {
1069             int id = swf_GetDefineID(tag);
1070             id = id2id[id];
1071             if(id>=0) {
1072                 swf_SetDefineID(tag, id);
1073             }
1074         }
1075         int num = swf_GetNumUsedIDs(tag);
1076         if(num) {
1077             int *ptr;
1078             int t;
1079             ptr = (int*)rfx_alloc(sizeof(int)*num);
1080             swf_GetUsedIDs(tag, ptr);
1081             for(t=0;t<num;t++) {
1082                 int id = GET16(&tag->data[ptr[t]]);
1083                 id = id2id[id];
1084                 if(id>=0) {
1085                     PUT16(&tag->data[ptr[t]], id);
1086                 }
1087             }
1088             free(ptr);
1089         }
1090     }
1091 }
1092
1093 void swf_RelocateDepth(SWF*swf, char*bitmap)
1094 {
1095     TAG*tag;
1096     int nr;
1097     tag = swf->firstTag;
1098     for(nr=65535;nr>=0;nr--) {
1099         if(bitmap[nr] != 0) 
1100             break;
1101     }
1102     // now nr is the highest used depth. So we start
1103     // assigning depths at nr+1
1104     nr++;
1105
1106     while(tag)
1107     {
1108         int depth;
1109         /* TODO * clip depths 
1110                 * sprites
1111          */
1112         if(tag->id == ST_PLACEOBJECT2) {
1113             SWFPLACEOBJECT obj;
1114             swf_GetPlaceObject(tag, &obj);
1115             if(obj.clipdepth) {
1116                 int newdepth = obj.clipdepth+nr;
1117                 if(newdepth>65535) {
1118                     fprintf(stderr, "Couldn't relocate depths: too large values\n");
1119                     newdepth = 65535;
1120                 }
1121                 obj.clipdepth = newdepth;
1122                 swf_ResetTag(tag, ST_PLACEOBJECT2);
1123                 swf_SetPlaceObject(tag, &obj);
1124             }
1125             swf_PlaceObjectFree(&obj);
1126         }
1127
1128         depth = swf_GetDepth(tag);
1129         if(depth>=0) {
1130             int newdepth = depth+nr;
1131             if(newdepth>65535) {
1132                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
1133                 newdepth = 65535;
1134             }
1135             swf_SetDepth(tag, newdepth);
1136         }
1137         tag=tag->next;
1138     }
1139 }
1140
1141 U8 swf_isShapeTag(TAG*tag)
1142 {
1143     if(tag->id == ST_DEFINESHAPE ||
1144        tag->id == ST_DEFINESHAPE2 ||
1145        tag->id == ST_DEFINESHAPE3 ||
1146        tag->id == ST_DEFINESHAPE4) 
1147         return 1;
1148     return 0;
1149 }
1150
1151 U8 swf_isPlaceTag(TAG*tag)
1152 {
1153     if(tag->id == ST_PLACEOBJECT ||
1154        tag->id == ST_PLACEOBJECT2 ||
1155        tag->id == ST_PLACEOBJECT3)
1156         return 1;
1157     return 0;
1158 }
1159 U8 swf_isTextTag(TAG*tag)
1160 {
1161     if(tag->id == ST_DEFINETEXT ||
1162        tag->id == ST_DEFINETEXT2)
1163         return 1;
1164     return 0;
1165 }
1166
1167 U8 swf_isFontTag(TAG*tag)
1168 {
1169     if(tag->id == ST_DEFINEFONT ||
1170        tag->id == ST_DEFINEFONT2 ||
1171        tag->id == ST_DEFINEFONT3 ||
1172        tag->id == ST_DEFINEFONTINFO)
1173         return 1;
1174     return 0;
1175 }
1176
1177 U8  swf_isImageTag(TAG*tag)
1178 {
1179     if(tag->id == ST_DEFINEBITSJPEG || 
1180        tag->id == ST_DEFINEBITSJPEG2 || 
1181        tag->id == ST_DEFINEBITSJPEG3 ||
1182        tag->id == ST_DEFINEBITSLOSSLESS || 
1183        tag->id == ST_DEFINEBITSLOSSLESS2)
1184         return 1;
1185     return 0;
1186 }
1187
1188 TAG* swf_Concatenate (TAG*list1,TAG*list2)
1189 {
1190     TAG*tag=0,*lasttag=0;
1191     char bitmap[65536];
1192     char depthmap[65536];
1193     SWF swf1,swf2;
1194     memset(bitmap, 0, sizeof(bitmap));
1195     memset(depthmap, 0, sizeof(depthmap));
1196     memset(&swf1, 0, sizeof(swf1));
1197     memset(&swf2, 0, sizeof(swf2));
1198
1199     swf1.firstTag = list1;
1200     swf_FoldAll(&swf1);
1201     swf2.firstTag = list2;
1202     swf_FoldAll(&swf2);
1203
1204     tag = list1;
1205     while(tag) {
1206         if(!swf_isDefiningTag(tag)) {
1207             int id = swf_GetDefineID(tag);
1208             bitmap[id] = 1;
1209         }
1210         if(tag->id == ST_PLACEOBJECT ||
1211            tag->id == ST_PLACEOBJECT2) {
1212             int depth = swf_GetDepth(tag);
1213             depthmap[depth] = 1;
1214         }
1215         if(tag->id == ST_REMOVEOBJECT ||
1216            tag->id == ST_REMOVEOBJECT2) {
1217             int depth = swf_GetDepth(tag);
1218             depthmap[depth] = 0;
1219         }
1220         tag = tag->next;
1221         lasttag = tag;
1222     }
1223     swf_Relocate(&swf2, bitmap);
1224     swf_RelocateDepth(&swf2, depthmap);
1225     lasttag->next = swf2.firstTag;
1226     swf2.firstTag->prev = lasttag;
1227
1228     return swf1.firstTag;
1229 }
1230
1231 static int tagHash(TAG*tag)
1232 {
1233     int t, h=0;
1234     unsigned int a = 0x6b973e5a;
1235     /* start at pos 2, as 0 and 1 are the id */
1236     for(t=2;t<tag->len;t++) {
1237         unsigned int b = a;
1238         a >>= 8;
1239         a += tag->data[t]*0xefbc35a5*b*(t+1);
1240     }
1241     return a&0x7fffffff; //always return positive number
1242 }
1243
1244 void swf_Optimize(SWF*swf)
1245 {
1246     const int hash_size = 131072;
1247     char* dontremap = (char*)rfx_calloc(sizeof(char)*65536);
1248     U16* remap = (U16*)rfx_alloc(sizeof(U16)*65536);
1249     TAG* id2tag = (TAG*)rfx_calloc(sizeof(TAG*)*65536);
1250     TAG** hashmap = (TAG**)rfx_calloc(sizeof(TAG*)*hash_size);
1251     TAG* tag;
1252     int t;
1253     for(t=0;t<65536;t++) {
1254         remap[t] = t;
1255     }
1256
1257     swf_FoldAll(swf);
1258
1259     tag = swf->firstTag;
1260     while(tag) {
1261         /* make sure we don't remap to this tag,
1262            as it might have different "helper tags" 
1263            FIXME: a better way would be to compare
1264                   the helper tags, too.
1265          */
1266         if(swf_isPseudoDefiningTag(tag) &&
1267            tag->id != ST_NAMECHARACTER) {
1268             dontremap[swf_GetDefineID(tag)] = 1;
1269         }
1270         tag=tag->next;
1271     }
1272     tag = swf->firstTag;
1273     while(tag) {
1274         TAG*next = tag->next;
1275
1276         /* remap the tag */
1277         int num = swf_GetNumUsedIDs(tag);
1278         int*positions = (int*)rfx_alloc(sizeof(int)*num);
1279         int t;
1280         swf_GetUsedIDs(tag, positions);
1281         for(t=0;t<num;t++) {
1282             int id = GET16(&tag->data[positions[t]]);
1283             id = remap[id];
1284             PUT16(&tag->data[positions[t]], id);
1285         }
1286         rfx_free(positions);
1287
1288         /* now look for previous tags with the same
1289            content */
1290         if(swf_isDefiningTag(tag)) {
1291             TAG*tag2;
1292             int id = swf_GetDefineID(tag);
1293             int hash = tagHash(tag);
1294             int match=0;
1295             if(!dontremap[id]) 
1296             while((tag2 = hashmap[hash%hash_size])) {
1297                 if(tag2 != (TAG*)0 && tag->len == tag2->len) {
1298                     if(memcmp(&tag->data[2],&tag2->data[2],tag->len-2) == 0) {
1299                         match = 1;
1300                         break;
1301                     }
1302                 }
1303                 hash++;
1304             }
1305             if(!match) {
1306                 while(hashmap[hash%hash_size]) hash++;
1307                 hashmap[hash%hash_size] = tag;
1308             } else {
1309                 /* we found two identical tags- remap one
1310                    of them */
1311                 remap[id] = swf_GetDefineID(tag2);
1312                 swf_DeleteTag(swf, tag);
1313             }
1314         } else if(swf_isPseudoDefiningTag(tag)) {
1315             int id = swf_GetDefineID(tag);
1316             if(remap[id]!=id) {
1317                 /* if this tag was remapped, we don't
1318                    need the helper tag anymore. Discard
1319                    it. */
1320                 swf_DeleteTag(swf, tag);
1321             }
1322         }
1323
1324         tag = next;
1325     }
1326     
1327     rfx_free(dontremap);
1328     rfx_free(remap);
1329     rfx_free(id2tag);
1330     rfx_free(hashmap);
1331 }
1332
1333 void swf_SetDefineBBox(TAG * tag, SRECT newbbox)
1334 {
1335     U16 id = 0;
1336     SRECT b1;
1337     swf_SetTagPos(tag,0);
1338
1339     switch (swf_GetTagID(tag))
1340     { 
1341         case ST_DEFINESHAPE:
1342         case ST_DEFINESHAPE2:
1343         case ST_DEFINESHAPE3:
1344         case ST_DEFINEEDITTEXT:
1345         case ST_DEFINETEXT:
1346         case ST_DEFINETEXT2:
1347         case ST_DEFINEVIDEOSTREAM: {
1348               U32 after_bbox_offset = 0, len;
1349               U8*data;
1350               id = swf_GetU16(tag);
1351               swf_GetRect(tag, &b1);
1352               swf_ResetReadBits(tag);
1353               after_bbox_offset = tag->pos;
1354               len = tag->len - after_bbox_offset;
1355               data = (U8*)malloc(len);
1356               memcpy(data, &tag->data[after_bbox_offset], len);
1357               tag->writeBit = 0;
1358               tag->len = 2;
1359               swf_SetRect(tag, &newbbox);
1360               swf_SetBlock(tag, data, len);
1361               free(data);
1362               tag->pos = tag->readBit = 0;
1363
1364         } break;
1365         default:
1366             fprintf(stderr, "rfxswf: Tag %d (%s) has no bbox\n", tag->id, swf_TagGetName(tag));
1367     }
1368 }
1369
1370 RGBA swf_GetSWFBackgroundColor(SWF*swf)
1371 {
1372     TAG*t=swf->firstTag;
1373     RGBA color;
1374     color.r = color.b = color.g = 0;
1375     color.a = 255;
1376     while(t) {
1377         if(t->id == ST_SETBACKGROUNDCOLOR) {
1378             swf_SetTagPos(t, 0);
1379             color.r = swf_GetU8(t);
1380             color.g = swf_GetU8(t);
1381             color.b = swf_GetU8(t);
1382             break;
1383         }
1384         t=t->next;
1385     }
1386     return color;
1387 }
1388