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