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