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