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