fixed swfcombine handling of symbolclass id 0000
[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_DEFINEBINARY:
647         case ST_DEFINEFONTNAME:
648         case ST_GENERATORTEXT:
649             callback(tag, tag->pos + base, callback_data);
650         break;
651         case ST_PLACEOBJECT:
652             callback(tag, tag->pos + base, callback_data);
653         break;
654         case ST_PLACEOBJECT2:
655             // only if placeflaghascharacter
656             if(!(tag->data[0]&2))
657                 break;
658             callback(tag, 3 + base, callback_data);
659         break;
660         case ST_PLACEOBJECT3:
661             // only if placeflaghascharacter
662             if(!(tag->data[0]&2))
663                 break;
664             callback(tag, 4 + base, callback_data);
665         break;
666         case ST_REMOVEOBJECT:
667             callback(tag, tag->pos + base, callback_data);
668         break;
669         case ST_STARTSOUND:
670             callback(tag, tag->pos + base, callback_data);
671         break;
672         case ST_DEFINESPRITE: {
673             if(tag->len <= 4)
674                 break; // sprite is expanded
675
676             swf_GetU16(tag); // id
677             swf_GetU16(tag); // framenum
678
679             while(1) {
680                 U16 flags = swf_GetU16(tag);
681                 U32 len;
682                 U16 id = flags>>6;
683                 TAG *tag2 = swf_InsertTag(NULL, id);
684                 len = flags&0x3f;
685                 if(len == 63)
686                     len = swf_GetU32(tag);
687                 if(id == ST_END)
688                     break;
689                 tag2->len = tag2->memsize = len;
690                 tag2->data = (U8*)rfx_alloc(len);
691                 memcpy(tag2->data, &tag->data[tag->pos], len);
692                 /* I never saw recursive sprites, but they are (theoretically) 
693                    possible, so better add base here again */
694                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
695                 swf_DeleteTag(0, tag2);
696                 swf_GetBlock(tag, NULL, len);
697             }
698         } 
699         break;
700         case ST_DEFINEBUTTON2: // has some font ids in the button records
701             num++; 
702         //fallthrough
703         case ST_DEFINEBUTTON: {
704             swf_GetU16(tag); //button id
705             if(num>1)
706             { 
707                 int offset;
708                 swf_GetU8(tag); //flag
709                 offset = swf_GetU16(tag); //offset
710             }
711             while(1)
712             {
713                 U8 flags = swf_GetU8(tag);
714                 if(!flags) //flags
715                     break; 
716                 callback(tag, tag->pos + base, callback_data);
717                 swf_GetU16(tag); //char
718                 swf_GetU16(tag); //layer
719                 swf_ResetReadBits(tag);
720                 swf_GetMatrix(tag, NULL);
721                 if(num>1) {
722                   swf_ResetReadBits(tag);
723                   swf_GetCXForm(tag, NULL, 1);
724                 }
725                 if(flags&0x10) {
726                     U8 num = swf_GetU8(tag);
727                     int t;
728                     for(t=0;t<num;t++) {
729                         swf_DeleteFilter(swf_GetFilter(tag));
730                     }
731                 }
732                 if(flags&0x20) {
733                     U8 blendmode = swf_GetU8(tag);
734                 }
735             }
736             // ...
737         }
738         break;
739         case ST_DEFINEEDITTEXT:  {
740             U8 flags1,flags2;
741             swf_GetU16(tag); //id
742             swf_GetRect(tag, NULL); //bounding box
743             swf_ResetReadBits(tag);
744             flags1 = swf_GetU8(tag);
745             flags2 = swf_GetU8(tag);
746             if(flags1 & 1)
747                 callback(tag, tag->pos + base, callback_data);
748         }
749         break;
750         case ST_DEFINETEXT2:
751             num ++;
752         case ST_DEFINETEXT: { 
753             int glyphbits, advancebits;
754             int id;
755             id = swf_GetU16(tag); //id
756             swf_GetRect(tag, NULL); //bounding box
757             swf_ResetReadBits(tag);
758             swf_GetMatrix(tag, NULL); //matrix
759             swf_ResetReadBits(tag);
760             glyphbits = swf_GetU8(tag); //glyphbits
761             advancebits = swf_GetU8(tag); //advancebits
762             while(1) {
763                 U16 flags;
764                 int t;
765                 swf_ResetReadBits(tag);
766                 flags = swf_GetBits(tag, 8);
767                 if(!flags) break;
768                 
769                 swf_ResetReadBits(tag);
770                 if(flags & 8) { // hasfont
771                     callback(tag, tag->pos + base, callback_data);
772                     id = swf_GetU16(tag);
773                 }
774                 if(flags & 4) { // hascolor
775                     if(num==1) swf_GetRGB(tag, NULL);
776                     else       swf_GetRGBA(tag, NULL);
777                 }
778                 if(flags & 2) { //has x offset
779                     swf_ResetReadBits(tag);
780                     swf_GetU16(tag);
781                 }
782                 if(flags & 1) { //has y offset
783                     swf_ResetReadBits(tag);
784                     swf_GetU16(tag);
785                 }
786                 if(flags & 8) { //has height
787                     swf_ResetReadBits(tag);
788                     swf_GetU16(tag);
789                 }
790                 
791                 flags = swf_GetBits(tag, 8);
792                 if(!flags) break;
793                 swf_ResetReadBits(tag);
794                 for(t=0;t<flags;t++) {
795                     swf_GetBits(tag, glyphbits);
796                     swf_GetBits(tag, advancebits);
797                 }
798             }
799             break;
800         }
801         case ST_DEFINEFONTALIGNZONES:
802         case ST_DEFINESCALINGGRID:
803         case ST_GLYPHNAMES:
804         case ST_CSMTEXTSETTINGS:
805         case ST_DEFINEFONTINFO:
806         case ST_DEFINEFONTINFO2:
807         case ST_VIDEOFRAME:
808             callback(tag, tag->pos + base, callback_data);
809         break;
810         case ST_DEFINEVIDEOSTREAM:
811         break;
812
813         case ST_DOINITACTION:
814             callback(tag, tag->pos + base, callback_data);
815         break;
816
817         case ST_DEFINEMORPHSHAPE2:
818         case ST_DEFINESHAPE4:
819         num++;
820         case ST_DEFINEMORPHSHAPE:
821         case ST_DEFINESHAPE3:
822         num++; //fallthrough
823         case ST_DEFINESHAPE2:
824         num++; //fallthrough
825         case ST_DEFINESHAPE: {
826             int fillbits;
827             int linebits;
828             int id; 
829             int numshapes = 1;
830             int morph = 0;
831             if(tag->id == ST_DEFINEMORPHSHAPE || tag->id==ST_DEFINEMORPHSHAPE2) {
832                 numshapes = 2;
833                 morph = 1;
834             }
835
836             id = swf_GetU16(tag); // id;
837             SRECT r={0,0,0,0},r2={0,0,0,0};
838             swf_GetRect(tag, &r); // shape bounds
839             if(morph) {
840                 swf_ResetReadBits(tag);
841                 swf_GetRect(tag, NULL); // shape bounds2
842                 if(num>=4) {
843                     swf_ResetReadBits(tag);
844                     swf_GetRect(tag, NULL); // edge bounds1
845                 }
846             }
847             if(num>=4) {
848                 swf_ResetReadBits(tag);
849                 swf_GetRect(tag, &r2); // edge bounds
850                 U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
851                 DEBUG_ENUMERATE printf("flags: %02x (1=scaling strokes, 2=non-scaling strokes)\n", flags);
852             }
853             if(morph) {
854                 swf_GetU32(tag); //offset to endedges
855             }
856    
857             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
858             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);
859             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);
860
861             DEBUG_ENUMERATE printf("style tag pos: %d\n", tag->pos);
862             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
863             DEBUG_ENUMERATE printf("-------\n");
864             swf_ResetReadBits(tag);
865             while(--numshapes>=0) /* morph shapes define two shapes */
866             {
867                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
868                 fillbits = swf_GetBits(tag, 4);
869                 linebits = swf_GetBits(tag, 4);
870                 DEBUG_ENUMERATE printf("fillbits=%d linebits=%d\n", fillbits, linebits);
871                 swf_ResetReadBits(tag);
872                 int x=0,y=0;
873                 while(1) {
874                     int flags;
875                     flags = swf_GetBits(tag, 1);
876                     if(!flags) { //style change
877                         flags = swf_GetBits(tag, 5);
878                         if(!flags)
879                             break;
880                         if(flags&1) { //move
881                             int n = swf_GetBits(tag, 5); 
882                             x = swf_GetBits(tag, n); //x
883                             y = swf_GetBits(tag, n); //y
884                             DEBUG_ENUMERATE printf("moveTo %.2f %.2f\n",x/20.0,y/20.0);
885                         }
886                         if(flags&2) { //fill0
887                             int fill0;
888                             fill0 = swf_GetBits(tag, fillbits); 
889                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
890                         }
891                         if(flags&4) { //fill1
892                             int fill1;
893                             fill1 = swf_GetBits(tag, fillbits); 
894                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
895                         }
896                         if(flags&8) { //linestyle
897                             int line;
898                             line = swf_GetBits(tag, linebits); 
899                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
900                         }
901                         if(flags&16) {
902                             DEBUG_ENUMERATE printf("more fillstyles\n");
903                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
904                             fillbits = swf_GetBits(tag, 4);
905                             linebits = swf_GetBits(tag, 4);
906                         }
907                     } else {
908                         flags = swf_GetBits(tag, 1);
909                         if(flags) { //straight edge
910                             int n = swf_GetBits(tag, 4) + 2;
911                             if(swf_GetBits(tag, 1)) { //line flag
912                                 x += swf_GetSBits(tag, n); //delta x
913                                 y += swf_GetSBits(tag, n); //delta y
914                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f\n",x/20.0,y/20.0);
915                             } else {
916                                 int v=swf_GetBits(tag, 1);
917                                 int d;
918                                 d = swf_GetSBits(tag, n); //vert/horz
919                                 if(!v)
920                                     x += d;
921                                 else
922                                     y += d;
923                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f (%s)\n",x/20.0,y/20.0, v?"vertical":"horizontal");
924                             }
925                         } else { //curved edge
926                             int n = swf_GetBits(tag, 4) + 2;
927                             int x1,y1,x2,y2;
928                             x1 = swf_GetSBits(tag, n);
929                             y1 = swf_GetSBits(tag, n);
930                             x2 = swf_GetSBits(tag, n);
931                             y2 = swf_GetSBits(tag, n);
932                             DEBUG_ENUMERATE printf("splineTo %.2f %.2f %.2f %.2f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
933                         }
934                     }
935                 }
936             }
937         }
938         break;
939         default:
940         break;
941     }
942 }
943
944 void callbackCount(TAG * t,int pos, void*ptr)
945 {
946     (*(int*)ptr)++;
947     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
948 }
949
950 void callbackFillin(TAG * t,int pos, void*ptr)
951 {
952     **(int**)ptr = pos;
953     (*(int**)ptr)++;
954     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
955 }
956
957 int swf_GetNumUsedIDs(TAG * t)
958 {
959     int num = 0;
960     enumerateUsedIDs(t, 0, callbackCount, &num);
961     return num;
962 }
963
964 void swf_GetUsedIDs(TAG * t, int * positions)
965 {
966     int * ptr = positions;
967     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
968 }
969
970 char swf_Relocate (SWF*swf, char*bitmap)
971 {
972     TAG*tag;
973     int slaveids[65536];
974     memset(slaveids, -1, sizeof(slaveids));
975     tag = swf->firstTag;
976     char ok = 1;
977     int current_id=0;
978     while(tag)
979     {
980         int num; 
981         int *ptr;
982
983         if(swf_isDefiningTag(tag))
984         {
985             int newid;
986             int id;
987             
988             id = swf_GetDefineID(tag); //own id
989
990             if(!bitmap[id]) { //free
991                 newid = id;
992             } else {
993                 newid = 0;
994                 for(current_id++;current_id<65536;current_id++)
995                 {
996                     if(!bitmap[current_id])
997                     {
998                         newid = current_id;
999                         break;
1000                     }
1001                 }
1002                 if(current_id==65536) {
1003                     fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs\n");
1004                     return 0;
1005                 }
1006             }
1007             bitmap[newid] = 1;
1008             slaveids[id] = newid;
1009
1010             swf_SetDefineID(tag, newid);
1011         } 
1012         
1013         num = swf_GetNumUsedIDs(tag);
1014         if(num) {
1015             ptr = (int*)rfx_alloc(sizeof(int)*num);
1016             swf_GetUsedIDs(tag, ptr);
1017             int t;
1018             for(t=0;t<num;t++) {
1019                 int id = GET16(&tag->data[ptr[t]]);
1020                 if(slaveids[id]<0) {
1021                     if(!id && bitmap[id]) {
1022                         /* id 0 is only used in SWF versions >=9. It's the ID of
1023                            the main timeline. It's used in e.g. SYMBOLTAG tags, but
1024                            never defined, so if we're asked to reallocate it, we have 
1025                            to allocate an ID for it on the fly. */
1026                         int newid = 0;
1027                         for(current_id++;current_id<65536;current_id++) {
1028                             if(!bitmap[current_id]) {
1029                                 newid = current_id;
1030                                 break;
1031                             }
1032                         }
1033                         if(current_id==65536) {
1034                             fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs\n");
1035                             return 0;
1036                         }
1037                         bitmap[newid] = 1;
1038                         slaveids[id] = newid;
1039                         id = newid;
1040                     } else if(!bitmap[id]) {
1041                         /* well- we don't know this id, but it's not reserved anyway, so just
1042                            leave it alone */
1043                     } else {
1044                         fprintf(stderr, "swf_Relocate: Mapping id (%d) never encountered before in %s\n", id,
1045                                 swf_TagGetName(tag));
1046                         ok = 0;
1047                     }
1048                 } else {
1049                     id = slaveids[id];
1050                 }
1051                 PUT16(&tag->data[ptr[t]], id);
1052             }
1053             free(ptr);
1054         }
1055         tag=tag->next;
1056     }
1057     return ok;
1058 }
1059
1060 /* untested */
1061 void swf_Relocate2(SWF*swf, int*id2id)
1062 {
1063     TAG*tag;
1064     tag = swf->firstTag;
1065     while(tag) {
1066         if(swf_isDefiningTag(tag)) {
1067             int id = swf_GetDefineID(tag);
1068             id = id2id[id];
1069             if(id>=0) {
1070                 swf_SetDefineID(tag, id);
1071             }
1072         }
1073         int num = swf_GetNumUsedIDs(tag);
1074         if(num) {
1075             int *ptr;
1076             int t;
1077             ptr = (int*)rfx_alloc(sizeof(int)*num);
1078             swf_GetUsedIDs(tag, ptr);
1079             for(t=0;t<num;t++) {
1080                 int id = GET16(&tag->data[ptr[t]]);
1081                 id = id2id[id];
1082                 if(id>=0) {
1083                     PUT16(&tag->data[ptr[t]], id);
1084                 }
1085             }
1086             free(ptr);
1087         }
1088     }
1089 }
1090
1091 void swf_RelocateDepth(SWF*swf, char*bitmap)
1092 {
1093     TAG*tag;
1094     int nr;
1095     tag = swf->firstTag;
1096     for(nr=65535;nr>=0;nr--) {
1097         if(bitmap[nr] != 0) 
1098             break;
1099     }
1100     // now nr is the highest used depth. So we start
1101     // assigning depths at nr+1
1102     nr++;
1103
1104     while(tag)
1105     {
1106         int depth;
1107         /* TODO * clip depths 
1108                 * sprites
1109          */
1110         if(tag->id == ST_PLACEOBJECT2) {
1111             SWFPLACEOBJECT obj;
1112             swf_GetPlaceObject(tag, &obj);
1113             if(obj.clipdepth) {
1114                 int newdepth = obj.clipdepth+nr;
1115                 if(newdepth>65535) {
1116                     fprintf(stderr, "Couldn't relocate depths: too large values\n");
1117                     newdepth = 65535;
1118                 }
1119                 obj.clipdepth = newdepth;
1120                 swf_ResetTag(tag, ST_PLACEOBJECT2);
1121                 swf_SetPlaceObject(tag, &obj);
1122             }
1123             swf_PlaceObjectFree(&obj);
1124         }
1125
1126         depth = swf_GetDepth(tag);
1127         if(depth>=0) {
1128             int newdepth = depth+nr;
1129             if(newdepth>65535) {
1130                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
1131                 newdepth = 65535;
1132             }
1133             swf_SetDepth(tag, newdepth);
1134         }
1135         tag=tag->next;
1136     }
1137 }
1138
1139 U8 swf_isShapeTag(TAG*tag)
1140 {
1141     if(tag->id == ST_DEFINESHAPE ||
1142        tag->id == ST_DEFINESHAPE2 ||
1143        tag->id == ST_DEFINESHAPE3 ||
1144        tag->id == ST_DEFINESHAPE4) 
1145         return 1;
1146     return 0;
1147 }
1148
1149 U8 swf_isPlaceTag(TAG*tag)
1150 {
1151     if(tag->id == ST_PLACEOBJECT ||
1152        tag->id == ST_PLACEOBJECT2 ||
1153        tag->id == ST_PLACEOBJECT3)
1154         return 1;
1155     return 0;
1156 }
1157 U8 swf_isTextTag(TAG*tag)
1158 {
1159     if(tag->id == ST_DEFINETEXT ||
1160        tag->id == ST_DEFINETEXT2)
1161         return 1;
1162     return 0;
1163 }
1164
1165 U8 swf_isFontTag(TAG*tag)
1166 {
1167     if(tag->id == ST_DEFINEFONT ||
1168        tag->id == ST_DEFINEFONT2 ||
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