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