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