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