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