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