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