1dc66d89fd6e7dca20a76ca669c1f3414be0a76d
[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             for(t=0;t<4;t++) {
588                 int flags;
589                 callback(tag, tag->pos + base, callback_data);
590                 swf_GetU16(tag); //sound id
591                 flags = swf_GetU8(tag);
592                 if(flags&1)
593                     swf_GetU32(tag); // in point
594                 if(flags&2)
595                     swf_GetU32(tag); // out points
596                 if(flags&4)
597                     swf_GetU16(tag); // loop count
598                 if(flags&8)
599                 {
600                     int npoints = swf_GetU8(tag);
601                     int s;
602                     for(s=0;s<npoints;s++)
603                     {
604                         swf_GetU32(tag);
605                         swf_GetU16(tag);
606                         swf_GetU16(tag);
607                     }
608                 }
609             }
610         } break;
611         case ST_DEFINEBUTTONCXFORM:
612             callback(tag, tag->pos + base, callback_data); //button id
613         break;
614
615         case ST_SYMBOLCLASS:
616         case ST_EXPORTASSETS: {
617             int num =  swf_GetU16(tag);
618             int t;
619             for(t=0;t<num;t++) {
620                 callback(tag, tag->pos + base, callback_data); //button id
621                 swf_GetU16(tag); //id
622                 while(swf_GetU8(tag)); //name
623             }
624         } break;
625
626         case ST_IMPORTASSETS: 
627         case ST_IMPORTASSETS2: {
628             swf_GetString(tag); //count
629             swf_GetU8(tag); //reserved
630             swf_GetU8(tag); //reserved
631             int num =  swf_GetU16(tag); //url
632             int t;
633             for(t=0;t<num;t++) {
634                 callback(tag, tag->pos + base, callback_data); //button id
635                 swf_GetU16(tag); //id
636                 while(swf_GetU8(tag)); //name
637             }
638         } break;
639
640         case ST_DOABC:
641         case ST_RAWABC:
642         break;
643
644         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
645         case ST_NAMECHARACTER:
646         case ST_DEFINEFONTNAME:
647         case ST_GENERATORTEXT:
648             callback(tag, tag->pos + base, callback_data);
649         break;
650         case ST_PLACEOBJECT:
651             callback(tag, tag->pos + base, callback_data);
652         break;
653         case ST_PLACEOBJECT2:
654             // only if placeflaghascharacter
655             if(!(tag->data[0]&2))
656                 break;
657             callback(tag, 3 + base, callback_data);
658         break;
659         case ST_PLACEOBJECT3:
660             // only if placeflaghascharacter
661             if(!(tag->data[0]&2))
662                 break;
663             callback(tag, 4 + base, callback_data);
664         break;
665         case ST_REMOVEOBJECT:
666             callback(tag, tag->pos + base, callback_data);
667         break;
668         case ST_STARTSOUND:
669             callback(tag, tag->pos + base, callback_data);
670         break;
671         case ST_DEFINESPRITE: {
672             if(tag->len <= 4)
673                 break; // sprite is expanded
674
675             swf_GetU16(tag); // id
676             swf_GetU16(tag); // framenum
677
678             while(1) {
679                 U16 flags = swf_GetU16(tag);
680                 U32 len;
681                 U16 id = flags>>6;
682                 TAG *tag2 = swf_InsertTag(NULL, id);
683                 len = flags&0x3f;
684                 if(len == 63)
685                     len = swf_GetU32(tag);
686                 if(id == ST_END)
687                     break;
688                 tag2->len = tag2->memsize = len;
689                 tag2->data = (U8*)rfx_alloc(len);
690                 memcpy(tag2->data, &tag->data[tag->pos], len);
691                 /* I never saw recursive sprites, but they are (theoretically) 
692                    possible, so better add base here again */
693                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
694                 swf_DeleteTag(0, tag2);
695                 swf_GetBlock(tag, NULL, len);
696             }
697         } 
698         break;
699         case ST_DEFINEBUTTON2: // has some font ids in the button records
700             num++; 
701         //fallthrough
702         case ST_DEFINEBUTTON: {
703             swf_GetU16(tag); //button id
704             if(num>1)
705             { 
706                 int offset;
707                 swf_GetU8(tag); //flag
708                 offset = swf_GetU16(tag); //offset
709             }
710             while(1)
711             {
712                 U8 flags = swf_GetU8(tag);
713                 if(!flags) //flags
714                     break; 
715                 callback(tag, tag->pos + base, callback_data);
716                 swf_GetU16(tag); //char
717                 swf_GetU16(tag); //layer
718                 swf_ResetReadBits(tag);
719                 swf_GetMatrix(tag, NULL);
720                 if(num>1) {
721                   swf_ResetReadBits(tag);
722                   swf_GetCXForm(tag, NULL, 1);
723                 }
724                 if(flags&0x10) {
725                     U8 num = swf_GetU8(tag);
726                     int t;
727                     for(t=0;t<num;t++) {
728                         swf_DeleteFilter(swf_GetFilter(tag));
729                     }
730                 }
731                 if(flags&0x20) {
732                     U8 blendmode = swf_GetU8(tag);
733                 }
734             }
735             // ...
736         }
737         break;
738         case ST_DEFINEEDITTEXT:  {
739             U8 flags1,flags2;
740             swf_GetU16(tag); //id
741             swf_GetRect(tag, NULL); //bounding box
742             swf_ResetReadBits(tag);
743             flags1 = swf_GetU8(tag);
744             flags2 = swf_GetU8(tag);
745             if(flags1 & 1)
746                 callback(tag, tag->pos + base, callback_data);
747         }
748         break;
749         case ST_DEFINETEXT2:
750             num ++;
751         case ST_DEFINETEXT: { 
752             int glyphbits, advancebits;
753             int id;
754             id = swf_GetU16(tag); //id
755             swf_GetRect(tag, NULL); //bounding box
756             swf_ResetReadBits(tag);
757             swf_GetMatrix(tag, NULL); //matrix
758             swf_ResetReadBits(tag);
759             glyphbits = swf_GetU8(tag); //glyphbits
760             advancebits = swf_GetU8(tag); //advancebits
761             while(1) {
762                 U16 flags;
763                 int t;
764                 swf_ResetReadBits(tag);
765                 flags = swf_GetBits(tag, 8);
766                 if(!flags) break;
767                 
768                 swf_ResetReadBits(tag);
769                 if(flags & 8) { // hasfont
770                     callback(tag, tag->pos + base, callback_data);
771                     id = swf_GetU16(tag);
772                 }
773                 if(flags & 4) { // hascolor
774                     if(num==1) swf_GetRGB(tag, NULL);
775                     else       swf_GetRGBA(tag, NULL);
776                 }
777                 if(flags & 2) { //has x offset
778                     swf_ResetReadBits(tag);
779                     swf_GetU16(tag);
780                 }
781                 if(flags & 1) { //has y offset
782                     swf_ResetReadBits(tag);
783                     swf_GetU16(tag);
784                 }
785                 if(flags & 8) { //has height
786                     swf_ResetReadBits(tag);
787                     swf_GetU16(tag);
788                 }
789                 
790                 flags = swf_GetBits(tag, 8);
791                 if(!flags) break;
792                 swf_ResetReadBits(tag);
793                 for(t=0;t<flags;t++) {
794                     swf_GetBits(tag, glyphbits);
795                     swf_GetBits(tag, advancebits);
796                 }
797             }
798             break;
799         }
800         case ST_DEFINEFONTALIGNZONES:
801         case ST_DEFINESCALINGGRID:
802         case ST_GLYPHNAMES:
803         case ST_CSMTEXTSETTINGS:
804         case ST_DEFINEFONTINFO:
805         case ST_DEFINEFONTINFO2:
806         case ST_VIDEOFRAME:
807             callback(tag, tag->pos + base, callback_data);
808         break;
809         case ST_DEFINEVIDEOSTREAM:
810         break;
811
812         case ST_DOINITACTION:
813             callback(tag, tag->pos + base, callback_data);
814         break;
815
816         case ST_DEFINEMORPHSHAPE2:
817         case ST_DEFINESHAPE4:
818         num++;
819         case ST_DEFINEMORPHSHAPE:
820         case ST_DEFINESHAPE3:
821         num++; //fallthrough
822         case ST_DEFINESHAPE2:
823         num++; //fallthrough
824         case ST_DEFINESHAPE: {
825             int fillbits;
826             int linebits;
827             int id; 
828             int numshapes = 1;
829             int morph = 0;
830             if(tag->id == ST_DEFINEMORPHSHAPE || tag->id==ST_DEFINEMORPHSHAPE2) {
831                 numshapes = 2;
832                 morph = 1;
833             }
834
835             id = swf_GetU16(tag); // id;
836             SRECT r={0,0,0,0},r2={0,0,0,0};
837             swf_GetRect(tag, &r); // shape bounds
838             if(morph) {
839                 swf_ResetReadBits(tag);
840                 swf_GetRect(tag, NULL); // shape bounds2
841                 if(num>=4) {
842                     swf_ResetReadBits(tag);
843                     swf_GetRect(tag, NULL); // edge bounds1
844                 }
845             }
846             if(num>=4) {
847                 swf_ResetReadBits(tag);
848                 swf_GetRect(tag, &r2); // edge bounds
849                 U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
850                 DEBUG_ENUMERATE printf("flags: %02x (1=scaling strokes, 2=non-scaling strokes)\n", flags);
851             }
852             if(morph) {
853                 swf_GetU32(tag); //offset to endedges
854             }
855    
856             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
857             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);
858             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);
859
860             DEBUG_ENUMERATE printf("style tag pos: %d\n", tag->pos);
861             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
862             DEBUG_ENUMERATE printf("-------\n");
863             swf_ResetReadBits(tag);
864             while(--numshapes>=0) /* morph shapes define two shapes */
865             {
866                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
867                 fillbits = swf_GetBits(tag, 4);
868                 linebits = swf_GetBits(tag, 4);
869                 DEBUG_ENUMERATE printf("fillbits=%d linebits=%d\n", fillbits, linebits);
870                 swf_ResetReadBits(tag);
871                 int x=0,y=0;
872                 while(1) {
873                     int flags;
874                     flags = swf_GetBits(tag, 1);
875                     if(!flags) { //style change
876                         flags = swf_GetBits(tag, 5);
877                         if(!flags)
878                             break;
879                         if(flags&1) { //move
880                             int n = swf_GetBits(tag, 5); 
881                             x = swf_GetBits(tag, n); //x
882                             y = swf_GetBits(tag, n); //y
883                             DEBUG_ENUMERATE printf("moveTo %.2f %.2f\n",x/20.0,y/20.0);
884                         }
885                         if(flags&2) { //fill0
886                             int fill0;
887                             fill0 = swf_GetBits(tag, fillbits); 
888                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
889                         }
890                         if(flags&4) { //fill1
891                             int fill1;
892                             fill1 = swf_GetBits(tag, fillbits); 
893                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
894                         }
895                         if(flags&8) { //linestyle
896                             int line;
897                             line = swf_GetBits(tag, linebits); 
898                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
899                         }
900                         if(flags&16) {
901                             DEBUG_ENUMERATE printf("more fillstyles\n");
902                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
903                             fillbits = swf_GetBits(tag, 4);
904                             linebits = swf_GetBits(tag, 4);
905                         }
906                     } else {
907                         flags = swf_GetBits(tag, 1);
908                         if(flags) { //straight edge
909                             int n = swf_GetBits(tag, 4) + 2;
910                             if(swf_GetBits(tag, 1)) { //line flag
911                                 x += swf_GetSBits(tag, n); //delta x
912                                 y += swf_GetSBits(tag, n); //delta y
913                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f\n",x/20.0,y/20.0);
914                             } else {
915                                 int v=swf_GetBits(tag, 1);
916                                 int d;
917                                 d = swf_GetSBits(tag, n); //vert/horz
918                                 if(!v)
919                                     x += d;
920                                 else
921                                     y += d;
922                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f (%s)\n",x/20.0,y/20.0, v?"vertical":"horizontal");
923                             }
924                         } else { //curved edge
925                             int n = swf_GetBits(tag, 4) + 2;
926                             int x1,y1,x2,y2;
927                             x1 = swf_GetSBits(tag, n);
928                             y1 = swf_GetSBits(tag, n);
929                             x2 = swf_GetSBits(tag, n);
930                             y2 = swf_GetSBits(tag, n);
931                             DEBUG_ENUMERATE printf("splineTo %.2f %.2f %.2f %.2f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
932                         }
933                     }
934                 }
935             }
936         }
937         break;
938         default:
939         break;
940     }
941 }
942
943 void callbackCount(TAG * t,int pos, void*ptr)
944 {
945     (*(int*)ptr)++;
946     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
947 }
948
949 void callbackFillin(TAG * t,int pos, void*ptr)
950 {
951     **(int**)ptr = pos;
952     (*(int**)ptr)++;
953     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
954 }
955
956 int swf_GetNumUsedIDs(TAG * t)
957 {
958     int num = 0;
959     enumerateUsedIDs(t, 0, callbackCount, &num);
960     return num;
961 }
962
963 void swf_GetUsedIDs(TAG * t, int * positions)
964 {
965     int * ptr = positions;
966     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
967 }
968
969 char swf_Relocate (SWF*swf, char*bitmap)
970 {
971     TAG*tag;
972     int slaveids[65536];
973     memset(slaveids, -1, sizeof(slaveids));
974     tag = swf->firstTag;
975     char ok = 1;
976
977     int current_id=0;
978 #define NEW_ID(n) \
979                 for(current_id++;current_id<65536;current_id++) { \
980                     if(!bitmap[current_id]) { \
981                         n = current_id; \
982                         break; \
983                     } \
984                 } \
985                 if(current_id==65536) { \
986                     fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs\n"); \
987                     return 0; \
988                 }
989
990     while(tag)
991     {
992         int num; 
993         int *ptr;
994
995         if(swf_isDefiningTag(tag))
996         {
997             int newid;
998             int id;
999             
1000             id = swf_GetDefineID(tag); //own id
1001
1002             if(!bitmap[id]) { //free
1003                 newid = id;
1004             } else if(slaveids[id]>0) {
1005                 newid = slaveids[id];
1006             } else {
1007                 NEW_ID(newid);
1008             }
1009
1010             bitmap[newid] = 1;
1011             slaveids[id] = newid;
1012
1013             swf_SetDefineID(tag, newid);
1014         } 
1015         
1016         num = swf_GetNumUsedIDs(tag);
1017         if(num) {
1018             ptr = (int*)rfx_alloc(sizeof(int)*num);
1019             swf_GetUsedIDs(tag, ptr);
1020             int t;
1021             for(t=0;t<num;t++) {
1022                 int id = GET16(&tag->data[ptr[t]]);
1023                 if(slaveids[id]<0) {
1024                     if(!id && bitmap[id]) {
1025                         /* id 0 is only used in SWF versions >=9. It's the ID of
1026                            the main timeline. It's used in e.g. SYMBOLTAG tags, but
1027                            never defined, so if we're asked to reallocate it, we have 
1028                            to allocate an ID for it on the fly. */
1029                         int newid;
1030                         NEW_ID(newid);
1031                         bitmap[newid] = 1;
1032                         slaveids[id] = newid;
1033                         id = newid;
1034                     } else if(!bitmap[id]) {
1035                         /* well- we don't know this id, but it's not reserved anyway, so just
1036                            leave it alone */
1037                     } else {
1038                         /* this actually happens with files created with Flash CS4 and never.
1039                            Apparently e.g. DefineButton tags are able to use forward declarations of objects. */
1040                         fprintf(stderr, "warning: Mapping id (%d) never encountered before in %s\n", id,
1041                                 swf_TagGetName(tag));
1042                         int newid;
1043                         NEW_ID(newid);
1044                         id = slaveids[id] = newid;
1045                         ok = 0;
1046                     }
1047                 } else {
1048                     id = slaveids[id];
1049                 }
1050                 PUT16(&tag->data[ptr[t]], id);
1051             }
1052             free(ptr);
1053         }
1054         tag=tag->next;
1055     }
1056     return ok;
1057 }
1058
1059 /* untested */
1060 void swf_Relocate2(SWF*swf, int*id2id)
1061 {
1062     TAG*tag;
1063     tag = swf->firstTag;
1064     while(tag) {
1065         if(swf_isDefiningTag(tag)) {
1066             int id = swf_GetDefineID(tag);
1067             id = id2id[id];
1068             if(id>=0) {
1069                 swf_SetDefineID(tag, id);
1070             }
1071         }
1072         int num = swf_GetNumUsedIDs(tag);
1073         if(num) {
1074             int *ptr;
1075             int t;
1076             ptr = (int*)rfx_alloc(sizeof(int)*num);
1077             swf_GetUsedIDs(tag, ptr);
1078             for(t=0;t<num;t++) {
1079                 int id = GET16(&tag->data[ptr[t]]);
1080                 id = id2id[id];
1081                 if(id>=0) {
1082                     PUT16(&tag->data[ptr[t]], id);
1083                 }
1084             }
1085             free(ptr);
1086         }
1087     }
1088 }
1089
1090 void swf_RelocateDepth(SWF*swf, char*bitmap)
1091 {
1092     TAG*tag;
1093     int nr;
1094     tag = swf->firstTag;
1095     for(nr=65535;nr>=0;nr--) {
1096         if(bitmap[nr] != 0) 
1097             break;
1098     }
1099     // now nr is the highest used depth. So we start
1100     // assigning depths at nr+1
1101     nr++;
1102
1103     while(tag)
1104     {
1105         int depth;
1106         /* TODO * clip depths 
1107                 * sprites
1108          */
1109         if(tag->id == ST_PLACEOBJECT2) {
1110             SWFPLACEOBJECT obj;
1111             swf_GetPlaceObject(tag, &obj);
1112             if(obj.clipdepth) {
1113                 int newdepth = obj.clipdepth+nr;
1114                 if(newdepth>65535) {
1115                     fprintf(stderr, "Couldn't relocate depths: too large values\n");
1116                     newdepth = 65535;
1117                 }
1118                 obj.clipdepth = newdepth;
1119                 swf_ResetTag(tag, ST_PLACEOBJECT2);
1120                 swf_SetPlaceObject(tag, &obj);
1121             }
1122             swf_PlaceObjectFree(&obj);
1123         }
1124
1125         depth = swf_GetDepth(tag);
1126         if(depth>=0) {
1127             int newdepth = depth+nr;
1128             if(newdepth>65535) {
1129                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
1130                 newdepth = 65535;
1131             }
1132             swf_SetDepth(tag, newdepth);
1133         }
1134         tag=tag->next;
1135     }
1136 }
1137
1138 U8 swf_isShapeTag(TAG*tag)
1139 {
1140     if(tag->id == ST_DEFINESHAPE ||
1141        tag->id == ST_DEFINESHAPE2 ||
1142        tag->id == ST_DEFINESHAPE3 ||
1143        tag->id == ST_DEFINESHAPE4) 
1144         return 1;
1145     return 0;
1146 }
1147
1148 U8 swf_isPlaceTag(TAG*tag)
1149 {
1150     if(tag->id == ST_PLACEOBJECT ||
1151        tag->id == ST_PLACEOBJECT2 ||
1152        tag->id == ST_PLACEOBJECT3)
1153         return 1;
1154     return 0;
1155 }
1156 U8 swf_isTextTag(TAG*tag)
1157 {
1158     if(tag->id == ST_DEFINETEXT ||
1159        tag->id == ST_DEFINETEXT2)
1160         return 1;
1161     return 0;
1162 }
1163
1164 U8 swf_isFontTag(TAG*tag)
1165 {
1166     if(tag->id == ST_DEFINEFONT ||
1167        tag->id == ST_DEFINEFONT2 ||
1168        tag->id == ST_DEFINEFONT3 ||
1169        tag->id == ST_DEFINEFONTINFO)
1170         return 1;
1171     return 0;
1172 }
1173
1174 U8  swf_isImageTag(TAG*tag)
1175 {
1176     if(tag->id == ST_DEFINEBITSJPEG || 
1177        tag->id == ST_DEFINEBITSJPEG2 || 
1178        tag->id == ST_DEFINEBITSJPEG3 ||
1179        tag->id == ST_DEFINEBITSLOSSLESS || 
1180        tag->id == ST_DEFINEBITSLOSSLESS2)
1181         return 1;
1182     return 0;
1183 }
1184
1185 TAG* swf_Concatenate (TAG*list1,TAG*list2)
1186 {
1187     TAG*tag=0,*lasttag=0;
1188     char bitmap[65536];
1189     char depthmap[65536];
1190     SWF swf1,swf2;
1191     memset(bitmap, 0, sizeof(bitmap));
1192     memset(depthmap, 0, sizeof(depthmap));
1193     memset(&swf1, 0, sizeof(swf1));
1194     memset(&swf2, 0, sizeof(swf2));
1195
1196     swf1.firstTag = list1;
1197     swf_FoldAll(&swf1);
1198     swf2.firstTag = list2;
1199     swf_FoldAll(&swf2);
1200
1201     tag = list1;
1202     while(tag) {
1203         if(!swf_isDefiningTag(tag)) {
1204             int id = swf_GetDefineID(tag);
1205             bitmap[id] = 1;
1206         }
1207         if(tag->id == ST_PLACEOBJECT ||
1208            tag->id == ST_PLACEOBJECT2) {
1209             int depth = swf_GetDepth(tag);
1210             depthmap[depth] = 1;
1211         }
1212         if(tag->id == ST_REMOVEOBJECT ||
1213            tag->id == ST_REMOVEOBJECT2) {
1214             int depth = swf_GetDepth(tag);
1215             depthmap[depth] = 0;
1216         }
1217         tag = tag->next;
1218         lasttag = tag;
1219     }
1220     swf_Relocate(&swf2, bitmap);
1221     swf_RelocateDepth(&swf2, depthmap);
1222     lasttag->next = swf2.firstTag;
1223     swf2.firstTag->prev = lasttag;
1224
1225     return swf1.firstTag;
1226 }
1227
1228 static int tagHash(TAG*tag)
1229 {
1230     int t, h=0;
1231     unsigned int a = 0x6b973e5a;
1232     /* start at pos 2, as 0 and 1 are the id */
1233     for(t=2;t<tag->len;t++) {
1234         unsigned int b = a;
1235         a >>= 8;
1236         a += tag->data[t]*0xefbc35a5*b*(t+1);
1237     }
1238     return a&0x7fffffff; //always return positive number
1239 }
1240
1241 void swf_Optimize(SWF*swf)
1242 {
1243     const int hash_size = 131072;
1244     char* dontremap = (char*)rfx_calloc(sizeof(char)*65536);
1245     U16* remap = (U16*)rfx_alloc(sizeof(U16)*65536);
1246     TAG* id2tag = (TAG*)rfx_calloc(sizeof(TAG*)*65536);
1247     TAG** hashmap = (TAG**)rfx_calloc(sizeof(TAG*)*hash_size);
1248     TAG* tag;
1249     int t;
1250     for(t=0;t<65536;t++) {
1251         remap[t] = t;
1252     }
1253
1254     swf_FoldAll(swf);
1255
1256     tag = swf->firstTag;
1257     while(tag) {
1258         /* make sure we don't remap to this tag,
1259            as it might have different "helper tags" 
1260            FIXME: a better way would be to compare
1261                   the helper tags, too.
1262          */
1263         if(swf_isPseudoDefiningTag(tag) &&
1264            tag->id != ST_NAMECHARACTER) {
1265             dontremap[swf_GetDefineID(tag)] = 1;
1266         }
1267         tag=tag->next;
1268     }
1269     tag = swf->firstTag;
1270     while(tag) {
1271         TAG*next = tag->next;
1272
1273         /* remap the tag */
1274         int num = swf_GetNumUsedIDs(tag);
1275         int*positions = (int*)rfx_alloc(sizeof(int)*num);
1276         int t;
1277         swf_GetUsedIDs(tag, positions);
1278         for(t=0;t<num;t++) {
1279             int id = GET16(&tag->data[positions[t]]);
1280             id = remap[id];
1281             PUT16(&tag->data[positions[t]], id);
1282         }
1283         rfx_free(positions);
1284
1285         /* now look for previous tags with the same
1286            content */
1287         if(swf_isDefiningTag(tag)) {
1288             TAG*tag2;
1289             int id = swf_GetDefineID(tag);
1290             int hash = tagHash(tag);
1291             int match=0;
1292             if(!dontremap[id]) 
1293             while((tag2 = hashmap[hash%hash_size])) {
1294                 if(tag2 != (TAG*)0 && tag->len == tag2->len) {
1295                     if(memcmp(&tag->data[2],&tag2->data[2],tag->len-2) == 0) {
1296                         match = 1;
1297                         break;
1298                     }
1299                 }
1300                 hash++;
1301             }
1302             if(!match) {
1303                 while(hashmap[hash%hash_size]) hash++;
1304                 hashmap[hash%hash_size] = tag;
1305             } else {
1306                 /* we found two identical tags- remap one
1307                    of them */
1308                 remap[id] = swf_GetDefineID(tag2);
1309                 swf_DeleteTag(swf, tag);
1310             }
1311         } else if(swf_isPseudoDefiningTag(tag)) {
1312             int id = swf_GetDefineID(tag);
1313             if(remap[id]!=id) {
1314                 /* if this tag was remapped, we don't
1315                    need the helper tag anymore. Discard
1316                    it. */
1317                 swf_DeleteTag(swf, tag);
1318             }
1319         }
1320
1321         tag = next;
1322     }
1323     
1324     rfx_free(dontremap);
1325     rfx_free(remap);
1326     rfx_free(id2tag);
1327     rfx_free(hashmap);
1328 }
1329
1330 void swf_SetDefineBBox(TAG * tag, SRECT newbbox)
1331 {
1332     U16 id = 0;
1333     SRECT b1;
1334     swf_SetTagPos(tag,0);
1335
1336     switch (swf_GetTagID(tag))
1337     { 
1338         case ST_DEFINESHAPE:
1339         case ST_DEFINESHAPE2:
1340         case ST_DEFINESHAPE3:
1341         case ST_DEFINEEDITTEXT:
1342         case ST_DEFINETEXT:
1343         case ST_DEFINETEXT2:
1344         case ST_DEFINEVIDEOSTREAM: {
1345               U32 after_bbox_offset = 0, len;
1346               U8*data;
1347               id = swf_GetU16(tag);
1348               swf_GetRect(tag, &b1);
1349               swf_ResetReadBits(tag);
1350               after_bbox_offset = tag->pos;
1351               len = tag->len - after_bbox_offset;
1352               data = (U8*)malloc(len);
1353               memcpy(data, &tag->data[after_bbox_offset], len);
1354               tag->writeBit = 0;
1355               tag->len = 2;
1356               swf_SetRect(tag, &newbbox);
1357               swf_SetBlock(tag, data, len);
1358               free(data);
1359               tag->pos = tag->readBit = 0;
1360
1361         } break;
1362         default:
1363             fprintf(stderr, "rfxswf: Tag %d (%s) has no bbox\n", tag->id, swf_TagGetName(tag));
1364     }
1365 }
1366
1367 RGBA swf_GetSWFBackgroundColor(SWF*swf)
1368 {
1369     TAG*t=swf->firstTag;
1370     RGBA color;
1371     color.r = color.b = color.g = 0;
1372     color.a = 255;
1373     while(t) {
1374         if(t->id == ST_SETBACKGROUNDCOLOR) {
1375             swf_SetTagPos(t, 0);
1376             color.r = swf_GetU8(t);
1377             color.g = swf_GetU8(t);
1378             color.b = swf_GetU8(t);
1379             break;
1380         }
1381         t=t->next;
1382     }
1383     return color;
1384 }
1385