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