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