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