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