added (long) GPL headers.
[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_VIDEOFRAME: //pseudodefine
120     case ST_NAMECHARACTER: //pseudodefine
121       id = swf_GetU16(t);
122       break;
123   }
124
125   swf_SetTagPos(t,oldTagPos);
126
127   return id;
128 }
129
130 SRECT swf_GetDefineBBox(TAG * t)
131 {
132   U32 oldTagPos;
133   U16 id = 0;
134   SRECT b1,b2;
135
136   oldTagPos = swf_GetTagPos(t);
137   swf_SetTagPos(t,0);
138
139   swf_GetRect(0, &b1);
140
141   switch (swf_GetTagID(t))
142   { case ST_DEFINESHAPE:
143     case ST_DEFINESHAPE2:
144     case ST_DEFINESHAPE3:
145     case ST_DEFINEEDITTEXT:
146     case ST_DEFINEBUTTON:
147     case ST_DEFINEBUTTON2:
148     case ST_DEFINETEXT:
149     case ST_DEFINETEXT2:
150     case ST_DEFINEVIDEOSTREAM:
151       id = swf_GetU16(t);
152       swf_GetRect(t, &b1);
153       break;
154     case ST_DEFINEMORPHSHAPE:
155       id = swf_GetU16(t);
156       swf_GetRect(t, &b1);
157       swf_GetRect(t, &b2);
158       swf_ExpandRect2(&b1, &b2);
159       break;
160     case ST_DEFINEBITSLOSSLESS:
161     case ST_DEFINEBITSLOSSLESS2:
162     case ST_DEFINEBITS:
163     case ST_DEFINEBITSJPEG2:
164     case ST_DEFINEBITSJPEG3:
165       // FIXME
166       break;
167   }
168
169   swf_SetTagPos(t,oldTagPos);
170
171   return b1;
172 }
173
174 U16 swf_GetPlaceID(TAG * t)
175 // up to SWF 4.0
176 { U32 oldTagPos;
177   U16 id = 0;
178
179   oldTagPos = swf_GetTagPos(t);
180   swf_SetTagPos(t,0);
181
182   switch (swf_GetTagID(t))
183   { case ST_PLACEOBJECT:
184     case ST_REMOVEOBJECT:
185     case ST_FREECHARACTER:
186     case ST_STARTSOUND:
187       id = swf_GetU16(t);
188       break;
189
190     case ST_PLACEOBJECT2:
191     { U8 flags = swf_GetU8(t);
192       U16 d = swf_GetU16(t);
193       id = (flags&PF_CHAR)?swf_GetU16(t):id;
194     } break;
195
196   }
197
198   swf_SetTagPos(t,oldTagPos);
199
200   return id;
201 }
202
203 static int swf_definingtagids[] =
204 {ST_DEFINESHAPE,
205  ST_DEFINESHAPE2,
206  ST_DEFINESHAPE3,
207  ST_DEFINEMORPHSHAPE,
208  ST_DEFINEFONT,
209  ST_DEFINEFONT2,
210  ST_DEFINETEXT,
211  ST_DEFINETEXT2,
212  ST_DEFINEEDITTEXT,
213  ST_DEFINEBITS,
214  ST_DEFINEBITSJPEG2,
215  ST_DEFINEBITSJPEG3,
216  ST_DEFINEBITSLOSSLESS,
217  ST_DEFINEBITSLOSSLESS2,
218  ST_DEFINEMOVIE,
219  ST_DEFINESPRITE,
220  ST_DEFINEBUTTON,
221  ST_DEFINEBUTTON2,
222  ST_DEFINESOUND,
223  ST_DEFINEVIDEOSTREAM,
224  -1
225 };
226
227 // tags which may be used inside a sprite definition
228 static int swf_spritetagids[] =
229 {ST_SHOWFRAME,
230  ST_PLACEOBJECT,
231  ST_PLACEOBJECT2,
232  ST_REMOVEOBJECT,
233  ST_REMOVEOBJECT2, //?
234  ST_DOACTION,
235  ST_STARTSOUND,
236  ST_FRAMELABEL,
237  ST_SOUNDSTREAMHEAD,
238  ST_SOUNDSTREAMHEAD2,
239  ST_SOUNDSTREAMBLOCK,
240  ST_END,
241  -1
242 };
243
244 static int swf_pseudodefiningtagids[] = 
245 {
246  ST_DEFINEFONTINFO,
247  ST_DEFINEFONTINFO2,
248  ST_DEFINEBUTTONCXFORM,
249  ST_DEFINEBUTTONSOUND,
250  ST_NAMECHARACTER,
251  ST_DOINITACTION,
252  ST_VIDEOFRAME,
253  -1
254 };
255
256 U8 swf_isAllowedSpriteTag(TAG * tag)
257 {
258     int id = tag->id;
259     int t=0;
260     while(swf_spritetagids[t]>=0)
261     {
262         if(swf_spritetagids[t] == id) 
263             return 1;
264         t++;
265     }
266     return 0; 
267 }
268
269 U8 swf_isDefiningTag(TAG * tag)
270 {
271     int id = tag->id;
272     int t=0;
273     while(swf_definingtagids[t]>=0)
274     {
275         if(swf_definingtagids[t] == id) 
276             return 1;
277         t++;
278     }
279     return 0; 
280 }
281
282 U8 swf_isPseudoDefiningTag(TAG * tag)
283 {
284     int id = tag->id;
285     int t=0;
286     while(swf_pseudodefiningtagids[t]>=0)
287     {
288         if(swf_pseudodefiningtagids[t] == id) 
289             return 1;
290         t++;
291     }
292     return 0; 
293 }
294
295 U16 swf_GetDepth(TAG * t)
296 // up to SWF 4.0
297
298   U16 depth = 0;
299   U32 oldTagPos;
300   oldTagPos = swf_GetTagPos(t);
301   swf_SetTagPos(t,0);
302
303   switch (swf_GetTagID(t))
304   { case ST_PLACEOBJECT:
305     case ST_REMOVEOBJECT:
306       swf_GetU16(t); //id
307       depth = swf_GetU16(t);
308       break;
309     case ST_REMOVEOBJECT2:
310       depth = swf_GetU16(t);
311       break;
312     case ST_PLACEOBJECT2:
313     { U8 flags = swf_GetU8(t);
314       depth = swf_GetU16(t);
315     } break;
316   }
317   swf_SetTagPos(t,oldTagPos);
318   return depth;
319 }
320
321 char* swf_GetName(TAG * t)
322 {
323     char* name = 0;
324     U32 oldTagPos;
325     MATRIX m;
326     CXFORM c;
327     oldTagPos = swf_GetTagPos(t);
328     swf_SetTagPos(t,0);
329     switch(swf_GetTagID(t))
330     {
331         case ST_FRAMELABEL:
332             name = &t->data[swf_GetTagPos(t)];
333         break;
334         case ST_PLACEOBJECT2: {   
335             U8 flags = swf_GetU8(t);
336             swf_GetU16(t); //depth;
337             if(flags&PF_CHAR) 
338               swf_GetU16(t); //id
339             if(flags&PF_MATRIX)
340               swf_GetMatrix(t, &m);
341             if(flags&PF_CXFORM)
342               swf_GetCXForm(t, &c, 1);
343             if(flags&PF_RATIO)
344               swf_GetU16(t);
345             if(flags&PF_CLIPACTION)
346               swf_GetU16(t);
347             if(flags&PF_NAME) {
348               swf_ResetReadBits(t);
349               name = &t->data[swf_GetTagPos(t)];
350             }
351         }
352         break;
353     }
354     swf_SetTagPos(t,oldTagPos);
355     return name;
356 }
357
358 /* used in enumerateUsedIDs */
359 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
360 {
361     GRADIENT dummy1;
362     GRADIENT dummy2;
363     int t;
364     if(!gradient1)
365         gradient1 = &dummy1;
366     if(!gradient2)
367         gradient2 = &dummy2;
368     gradient1->num = 
369     gradient2->num = swf_GetU8(tag);
370     for(t=0;t<gradient1->num;t++)
371     {
372         int s=t;
373         if(s>=8) //FIXME
374             s=7;
375         gradient1->ratios[t] = swf_GetU8(tag);
376         swf_GetRGBA(tag, &gradient1->rgba[t]);
377         gradient2->ratios[t] = swf_GetU8(tag);
378         swf_GetRGBA(tag, &gradient2->rgba[t]);
379     }
380 }
381
382 #define DEBUG_ENUMERATE if(0)
383
384 static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
385 {
386     U16 count;
387     int t;
388     count = swf_GetU8(tag);
389     if(count == 0xff && num>1) // defineshape2,3 only
390         count = swf_GetU16(tag);
391
392     for(t=0;t<count;t++)
393     {
394         int type;
395         U8*pos;
396         swf_ResetReadBits(tag);
397         type = swf_GetU8(tag); //type
398         if(type == 0) {
399             if(num == 3)
400                 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
401             else 
402                 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
403         }
404         else if(type == 0x10 || type == 0x12)
405         {
406             swf_ResetReadBits(tag);
407             swf_GetMatrix(tag, NULL);
408             if(morph)
409                 swf_GetMatrix(tag, NULL);
410             swf_ResetReadBits(tag);
411             if(morph)
412                 swf_GetMorphGradient(tag, NULL, NULL);
413             else
414                 swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
415         }
416         else if(type == 0x40 || type == 0x41)
417         {
418             swf_ResetReadBits(tag);
419             // we made it.
420             if(tag->data[tag->pos] != 0xff ||
421                tag->data[tag->pos+1] != 0xff)
422             (callback)(tag, tag->pos, callback_data);
423
424             swf_GetU16(tag);
425             swf_ResetReadBits(tag);
426             swf_GetMatrix(tag, NULL);
427             if(morph)
428                 swf_GetMatrix(tag, NULL);
429         }
430         else {
431             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
432         }
433     }
434     swf_ResetReadBits(tag);
435     count = swf_GetU8(tag); // line style array
436     if(count == 0xff)
437         count = swf_GetU16(tag);
438     for(t=0;t<count;t++) 
439     {
440         swf_GetU16(tag);
441         if(morph)
442             swf_GetU16(tag);
443         if(num == 3)
444             {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
445         else
446             {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
447     }
448 }
449
450 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
451 {
452     int num = 1;
453     swf_ResetReadBits(tag);
454     tag->pos = 0;
455     switch(tag->id)
456     {
457         case ST_DEFINEBUTTONCXFORM: {
458             int t;
459             callback(tag, tag->pos + base, callback_data);
460             for(t=0;t<4;t++) {
461                 int flags;
462                 callback(tag, tag->pos + base, callback_data);
463                 swf_GetU16(tag); //sound id
464                 flags = swf_GetU8(tag);
465                 if(flags&1)
466                     swf_GetU32(tag); // in point
467                 if(flags&2)
468                     swf_GetU32(tag); // out points
469                 if(flags&4)
470                     swf_GetU16(tag); // loop count
471                 if(flags&8)
472                 {
473                     int npoints = swf_GetU8(tag);
474                     int s;
475                     for(s=0;s<npoints;s++)
476                     {
477                         swf_GetU32(tag);
478                         swf_GetU16(tag);
479                         swf_GetU16(tag);
480                     }
481                 }
482             }
483         } break;
484         case ST_DEFINEBUTTONSOUND:
485             callback(tag, tag->pos + base, callback_data); //button id
486         break;
487
488         case ST_EXPORTASSETS: {
489             int num =  swf_GetU16(tag);
490             int t;
491             for(t=0;t<num;t++) {
492                 callback(tag, tag->pos + base, callback_data); //button id
493                 swf_GetU16(tag); //id
494                 while(swf_GetU8(tag)); //name
495             }
496         } break;
497
498         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
499         case ST_NAMECHARACTER:
500         case ST_GENERATORTEXT:
501             callback(tag, tag->pos + base, callback_data);
502         break;
503         case ST_PLACEOBJECT:
504             callback(tag, tag->pos + base, callback_data);
505         break;
506         case ST_PLACEOBJECT2:
507             // only if placeflaghascharacter
508             if(!(tag->data[0]&2))
509                 break;
510             callback(tag, 3 + base, callback_data);
511         break;
512         case ST_REMOVEOBJECT:
513             callback(tag, tag->pos + base, callback_data);
514         break;
515         case ST_STARTSOUND:
516             callback(tag, tag->pos + base, callback_data);
517         break;
518         case ST_DEFINESPRITE: {
519             if(tag->len <= 4)
520                 break; // sprite is expanded
521
522             swf_GetU16(tag); // id
523             swf_GetU16(tag); // framenum
524
525             while(1) {
526                 U16 flags = swf_GetU16(tag);
527                 U32 len;
528                 U16 id = flags>>6;
529                 TAG *tag2 = swf_InsertTag(NULL, id);
530                 len = flags&0x3f;
531                 if(len == 63)
532                     len = swf_GetU32(tag);
533                 if(id == ST_END)
534                     break;
535                 tag2->len = tag2->memsize = len;
536                 tag2->data = malloc(len);
537                 memcpy(tag2->data, &tag->data[tag->pos], len);
538                 /* I never saw recursive sprites, but they are (theoretically) 
539                    possible, so better add base here again */
540                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
541                 swf_DeleteTag(tag2);
542                 swf_GetBlock(tag, NULL, len);
543             }
544         } 
545         break;
546         case ST_DEFINEBUTTON2: // has some font ids in the button records
547             num++; 
548         //fallthrough
549         case ST_DEFINEBUTTON: {
550             swf_GetU16(tag); //button id
551             if(num>1)
552             { 
553                 int offset;
554                 swf_GetU8(tag); //flag
555                 offset = swf_GetU16(tag); //offset
556             }
557             while(1)
558             {
559                 U16 charid;
560                 if(!swf_GetU8(tag)) //flags
561                     break; 
562                 callback(tag, tag->pos + base, callback_data);
563                 swf_GetU16(tag); //char
564                 swf_GetU16(tag); //layer
565                 swf_ResetReadBits(tag);
566                 swf_GetMatrix(tag, NULL);
567                 if(num>1) {
568                   swf_ResetReadBits(tag);
569                   swf_GetCXForm(tag, NULL, 1);
570                 }
571             }
572             // ...
573         }
574         break;
575         case ST_DEFINEEDITTEXT:  {
576             U8 flags1,flags2;
577             swf_GetU16(tag); //id
578             swf_GetRect(tag, NULL); //bounding box
579             swf_ResetReadBits(tag);
580             flags1 = swf_GetU8(tag);
581             flags2 = swf_GetU8(tag);
582             if(flags1 & 1)
583                 callback(tag, tag->pos + base, callback_data);
584         }
585         break;
586         case ST_DEFINETEXT2:
587             num ++;
588         case ST_DEFINETEXT: { 
589             int glyphbits, advancebits;
590             int id;
591             id = swf_GetU16(tag); //id
592             swf_GetRect(tag, NULL); //bounding box
593             swf_ResetReadBits(tag);
594             swf_GetMatrix(tag, NULL); //matrix
595             swf_ResetReadBits(tag);
596             glyphbits = swf_GetU8(tag); //glyphbits
597             advancebits = swf_GetU8(tag); //advancebits
598             while(1) {
599                 U16 flags;
600                 swf_ResetReadBits(tag);
601                 flags = swf_GetBits(tag, 8);
602                 if(!flags) break;
603                 if(flags & 128) // text style record
604                 {
605                     swf_ResetReadBits(tag);
606                     if(flags & 8) { // hasfont
607                         callback(tag, tag->pos + base, callback_data);
608                         id = swf_GetU16(tag);
609                     }
610                     if(flags & 4) { // hascolor
611                         if(num==1) swf_GetRGB(tag, NULL);
612                         else       swf_GetRGBA(tag, NULL);
613                     }
614                     if(flags & 2) { //has x offset
615                         swf_ResetReadBits(tag);
616                         swf_GetU16(tag);
617                     }
618                     if(flags & 1) { //has y offset
619                         swf_ResetReadBits(tag);
620                         swf_GetU16(tag);
621                     }
622                     if(flags & 8) { //has height
623                         swf_ResetReadBits(tag);
624                         swf_GetU16(tag);
625                     }
626                 } else { // glyph record
627                     int t;
628                     swf_ResetReadBits(tag);
629                     for(t=0;t<flags;t++) {
630                         swf_GetBits(tag, glyphbits);
631                         swf_GetBits(tag, advancebits);
632                     }
633                 }
634             }
635             break;
636         }
637         case ST_DEFINEFONTINFO:
638         case ST_DEFINEFONTINFO2:
639         case ST_VIDEOFRAME:
640             callback(tag, tag->pos + base, callback_data);
641         break;
642         case ST_DEFINEVIDEOSTREAM:
643         break;
644
645         case ST_DOINITACTION:
646             callback(tag, tag->pos + base, callback_data);
647         break;
648
649         case ST_DEFINEMORPHSHAPE:
650         case ST_DEFINESHAPE3:
651         num++; //fallthrough
652         case ST_DEFINESHAPE2:
653         num++; //fallthrough
654         case ST_DEFINESHAPE: {
655             int fillbits;
656             int linebits;
657             int id; 
658             int numshapes = 1;
659             int morph = 0;
660             if(tag->id == ST_DEFINEMORPHSHAPE) {
661                 numshapes = 2;
662                 morph = 1;
663             }
664
665             id = swf_GetU16(tag); // id;
666             swf_GetRect(tag, NULL); // bounds
667             if(morph) {
668                 swf_ResetReadBits(tag);
669                 swf_GetRect(tag, NULL); // bounds2
670                 swf_GetU32(tag); //offset to endedges
671             }
672    
673             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
674
675             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
676             DEBUG_ENUMERATE printf("-------\n");
677             while(--numshapes>=0) /* morph shapes define two shapes */
678             {
679                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
680                 fillbits = swf_GetBits(tag, 4);
681                 linebits = swf_GetBits(tag, 4);
682                 DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
683                 swf_ResetReadBits(tag);
684                 while(1) {
685                     int flags;
686                     flags = swf_GetBits(tag, 1);
687                     if(!flags) { //style change
688                         flags = swf_GetBits(tag, 5);
689                         if(!flags)
690                             break;
691                         if(flags&1) { //move
692                             int n = swf_GetBits(tag, 5); 
693                             int x,y;
694                             x = swf_GetBits(tag, n); //x
695                             y = swf_GetBits(tag, n); //y
696                             DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
697                         }
698                         if(flags&2) { //fill0
699                             int fill0;
700                             fill0 = swf_GetBits(tag, fillbits); 
701                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
702                         }
703                         if(flags&4) { //fill1
704                             int fill1;
705                             fill1 = swf_GetBits(tag, fillbits); 
706                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
707                         }
708                         if(flags&8) { //linestyle
709                             int line;
710                             line = swf_GetBits(tag, linebits); 
711                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
712                         }
713                         if(flags&16) {
714                             DEBUG_ENUMERATE printf("more fillstyles\n");
715                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
716                             fillbits = swf_GetBits(tag, 4);
717                             linebits = swf_GetBits(tag, 4);
718                         }
719                     } else {
720                         flags = swf_GetBits(tag, 1);
721                         if(flags) { //straight edge
722                             int n = swf_GetBits(tag, 4) + 2;
723                             if(swf_GetBits(tag, 1)) { //line flag
724                                 int x,y;
725                                 x = swf_GetSBits(tag, n); //delta x
726                                 y = swf_GetSBits(tag, n); //delta y
727                                 DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
728                             } else {
729                                 int v=swf_GetBits(tag, 1);
730                                 int d;
731                                 d = swf_GetSBits(tag, n); //vert/horz
732                                 DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
733                             }
734                         } else { //curved edge
735                             int n = swf_GetBits(tag, 4) + 2;
736                             int x1,y1,x2,y2;
737                             x1 = swf_GetSBits(tag, n);
738                             y1 = swf_GetSBits(tag, n);
739                             x2 = swf_GetSBits(tag, n);
740                             y2 = swf_GetSBits(tag, n);
741                             DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
742                         }
743                     }
744                 }
745             }
746         }
747         break;
748         default:
749         break;
750     }
751 }
752
753 void callbackCount(TAG * t,int pos, void*ptr)
754 {
755     (*(int*)ptr)++;
756     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
757 }
758
759 void callbackFillin(TAG * t,int pos, void*ptr)
760 {
761     **(int**)ptr = pos;
762     (*(int**)ptr)++;
763     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
764 }
765
766 int swf_GetNumUsedIDs(TAG * t)
767 {
768     int num = 0;
769     enumerateUsedIDs(t, 0, callbackCount, &num);
770     return num;
771 }
772
773 void swf_GetUsedIDs(TAG * t, int * positions)
774 {
775     int * ptr = positions;
776     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
777 }
778
779 void swf_Relocate (SWF*swf, char*bitmap)
780 {
781     TAG*tag;
782     int slaveids[65536];
783     memset(slaveids, -1, sizeof(slaveids));
784     tag = swf->firstTag;
785     while(tag)
786     {
787         int num; 
788         int *ptr;
789         int t;
790
791         if(swf_isDefiningTag(tag))
792         {
793             int newid;
794             int id;
795             
796             id = swf_GetDefineID(tag); //own id
797
798             if(!bitmap[id]) { //free
799                 newid = id;
800             }
801             else {
802                 newid = 0;
803                 for (t=1;t<65536;t++)
804                 {
805                     if(!bitmap[t])
806                     {
807                         newid = t;
808                         break;
809                     }
810                 }
811             }
812             bitmap[newid] = 1;
813             slaveids[id] = newid;
814
815             swf_SetDefineID(tag, newid);
816         } 
817         
818         num = swf_GetNumUsedIDs(tag);
819         ptr = malloc(sizeof(int)*num);
820         swf_GetUsedIDs(tag, ptr);
821
822         for(t=0;t<num;t++) {
823             int id = GET16(&tag->data[ptr[t]]);
824             if(slaveids[id]<0) {
825                 fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
826                 return ;
827             }
828             id = slaveids[id];
829             PUT16(&tag->data[ptr[t]], id);
830         }
831         tag=tag->next;
832     }
833 }
834