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