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