f3a4b94d481298bb114811eb1f8a28cccefe7c26
[swftools.git] / lib / modules / swftools.c
1 /* swftools.c
2
3    Math and matrix functions, misc tools
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 // Matrix & Math tools for SWF files
25
26 #define S64 long long
27 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
28 { S64 a;
29   a = (S64)a1*(S64)b1+(S64)a2*(S64)b2;
30   return (SFIXED)(a>>16);
31 }
32 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
33 { S64 z = zaehler<<16;
34   S64 a = z/(S64)nenner;
35   return (SFIXED)a;
36 }
37 #undef S64
38
39 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
40 {        
41   if (!d) return NULL;
42   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
43   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
44   
45   d->tx = s1->tx + s2->tx;
46   d->ty = s1->ty + s2->ty;
47   
48   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
49   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
50   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
51   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
52
53   //DumpMatrix(NULL,d);
54   
55   return d;
56 }
57
58 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
59                                int x1,int y1,int x2,int y2)
60 { int dx1 = x1 - x0;
61   int dy1 = y1 - y0;
62   int dx2 = x2 - x0;
63   int dy2 = y2 - y0;
64   
65   if (!m) return NULL;
66   if ((!dx)||(!dy)) return NULL; // check DIV by zero
67
68   m->tx = x0;
69   m->ty = y0;
70   m->sx = RFXSWF_QFIX(dx1,dx);
71   m->sy = RFXSWF_QFIX(dy2,dy);
72   m->r0 = RFXSWF_QFIX(dy1,dx);
73   m->r1 = RFXSWF_QFIX(dx2,dy);
74   
75   return m;
76 }
77
78 void swf_SetDefineID(TAG * tag, U16 newid)
79 {
80   int oldlen = tag->len;
81   tag->len = 0;
82   swf_SetU16(tag, newid); /* set defining ID */
83   tag->len = oldlen;
84 }
85
86 U16 swf_GetDefineID(TAG * t)
87 // up to SWF 4.0
88 { U32 oldTagPos;
89   U16 id = 0;
90
91   oldTagPos = swf_GetTagPos(t);
92   swf_SetTagPos(t,0);
93
94   switch (swf_GetTagID(t))
95   { case ST_DEFINESHAPE:
96     case ST_DEFINESHAPE2:
97     case ST_DEFINESHAPE3:
98     case ST_DEFINEMORPHSHAPE:
99     case ST_DEFINEEDITTEXT:
100     case ST_DEFINEBITS:
101     case ST_DEFINEBITSJPEG2:
102     case ST_DEFINEBITSJPEG3:
103     case ST_DEFINEBITSLOSSLESS:
104     case ST_DEFINEBITSLOSSLESS2:
105     case ST_DEFINEBUTTON:
106     case ST_DEFINEBUTTON2:
107     case ST_DEFINEBUTTONCXFORM: //pseudodefine
108     case ST_DEFINEBUTTONSOUND: //pseudodefine
109     case ST_DEFINEFONT:
110     case ST_DEFINEFONT2:
111     case ST_DEFINEFONTINFO: //pseudodefine
112     case ST_DEFINEFONTINFO2: //pseudodefine
113     case ST_DEFINETEXT:
114     case ST_DEFINETEXT2:
115     case ST_DEFINESOUND:
116     case ST_DEFINESPRITE:
117     case ST_DEFINEMOVIE:
118     case ST_DEFINEVIDEOSTREAM:
119     case ST_GLYPHNAMES: //pseudodefine
120     case ST_VIDEOFRAME: //pseudodefine
121     case ST_NAMECHARACTER: //pseudodefine
122     case ST_DOINITACTION: //pseudodefine
123       id = swf_GetU16(t);
124       break;
125   }
126
127   swf_SetTagPos(t,oldTagPos);
128
129   return id;
130 }
131
132 SRECT swf_GetDefineBBox(TAG * t)
133 {
134   U32 oldTagPos;
135   U16 id = 0;
136   SRECT b1,b2;
137
138   oldTagPos = swf_GetTagPos(t);
139   swf_SetTagPos(t,0);
140
141   swf_GetRect(0, &b1);
142
143   switch (swf_GetTagID(t))
144   { case ST_DEFINESHAPE:
145     case ST_DEFINESHAPE2:
146     case ST_DEFINESHAPE3:
147     case ST_DEFINEEDITTEXT:
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  ST_GLYPHNAMES,
254  -1
255 };
256
257 U8 swf_isAllowedSpriteTag(TAG * tag)
258 {
259     int id = tag->id;
260     int t=0;
261     while(swf_spritetagids[t]>=0)
262     {
263         if(swf_spritetagids[t] == id) 
264             return 1;
265         t++;
266     }
267     return 0; 
268 }
269
270 U8 swf_isDefiningTag(TAG * tag)
271 {
272     int id = tag->id;
273     int t=0;
274     while(swf_definingtagids[t]>=0)
275     {
276         if(swf_definingtagids[t] == id) 
277             return 1;
278         t++;
279     }
280     return 0; 
281 }
282
283 U8 swf_isPseudoDefiningTag(TAG * tag)
284 {
285     int id = tag->id;
286     int t=0;
287     while(swf_pseudodefiningtagids[t]>=0)
288     {
289         if(swf_pseudodefiningtagids[t] == id) 
290             return 1;
291         t++;
292     }
293     return 0; 
294 }
295
296 int swf_GetDepth(TAG * t)
297
298   int depth = -1;
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     case ST_SETTABINDEX:
317     {
318       depth = swf_GetU16(t);
319     }
320   }
321   swf_SetTagPos(t,oldTagPos);
322   return depth;
323 }
324
325 void swf_SetDepth(TAG * t, U16 depth)
326
327   switch (swf_GetTagID(t))
328   { case ST_PLACEOBJECT:
329     case ST_REMOVEOBJECT:
330       PUT16(t->data, depth);
331       break;
332     case ST_REMOVEOBJECT2:
333       PUT16(t->data, depth);
334       break;
335     case ST_PLACEOBJECT2:
336       PUT16(&t->data[1], depth);
337       break;
338     case ST_SETTABINDEX:
339       PUT16(t->data, depth);
340       break;
341     default:
342       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
343   }
344 }
345
346 char* swf_GetName(TAG * t)
347 {
348     char* name = 0;
349     U32 oldTagPos;
350     MATRIX m;
351     CXFORM c;
352     oldTagPos = swf_GetTagPos(t);
353     swf_SetTagPos(t,0);
354     switch(swf_GetTagID(t))
355     {
356         case ST_FRAMELABEL:
357             name = &t->data[swf_GetTagPos(t)];
358         break;
359         case ST_PLACEOBJECT2: {   
360             U8 flags = swf_GetU8(t);
361             swf_GetU16(t); //depth;
362             if(flags&PF_CHAR) 
363               swf_GetU16(t); //id
364             if(flags&PF_MATRIX)
365               swf_GetMatrix(t, &m);
366             if(flags&PF_CXFORM)
367               swf_GetCXForm(t, &c, 1);
368             if(flags&PF_RATIO)
369               swf_GetU16(t);
370             if(flags&PF_CLIPACTION)
371               swf_GetU16(t);
372             if(flags&PF_NAME) {
373               swf_ResetReadBits(t);
374               name = &t->data[swf_GetTagPos(t)];
375             }
376         }
377         break;
378     }
379     swf_SetTagPos(t,oldTagPos);
380     return name;
381 }
382
383 /* used in enumerateUsedIDs */
384 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
385 {
386     GRADIENT dummy1;
387     GRADIENT dummy2;
388     int t;
389     if(!gradient1)
390         gradient1 = &dummy1;
391     if(!gradient2)
392         gradient2 = &dummy2;
393     gradient1->num = 
394     gradient2->num = swf_GetU8(tag);
395     for(t=0;t<gradient1->num;t++)
396     {
397         int s=t;
398         if(s>=8) //FIXME
399             s=7;
400         gradient1->ratios[t] = swf_GetU8(tag);
401         swf_GetRGBA(tag, &gradient1->rgba[t]);
402         gradient2->ratios[t] = swf_GetU8(tag);
403         swf_GetRGBA(tag, &gradient2->rgba[t]);
404     }
405 }
406
407 #define DEBUG_ENUMERATE if(0)
408
409 static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
410 {
411     U16 count;
412     int t;
413     count = swf_GetU8(tag);
414     if(count == 0xff && num>1) // defineshape2,3 only
415         count = swf_GetU16(tag);
416
417     for(t=0;t<count;t++)
418     {
419         int type;
420         U8*pos;
421         swf_ResetReadBits(tag);
422         type = swf_GetU8(tag); //type
423         if(type == 0) {
424             if(num == 3)
425                 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
426             else 
427                 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
428         }
429         else if(type == 0x10 || type == 0x12)
430         {
431             swf_ResetReadBits(tag);
432             swf_GetMatrix(tag, NULL);
433             if(morph)
434                 swf_GetMatrix(tag, NULL);
435             swf_ResetReadBits(tag);
436             if(morph)
437                 swf_GetMorphGradient(tag, NULL, NULL);
438             else
439                 swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
440         }
441         else if(type == 0x40 || type == 0x41)
442         {
443             swf_ResetReadBits(tag);
444             // we made it.
445             if(tag->data[tag->pos] != 0xff ||
446                tag->data[tag->pos+1] != 0xff)
447             (callback)(tag, tag->pos, callback_data);
448
449             swf_GetU16(tag);
450             swf_ResetReadBits(tag);
451             swf_GetMatrix(tag, NULL);
452             if(morph)
453                 swf_GetMatrix(tag, NULL);
454         }
455         else {
456             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
457         }
458     }
459     swf_ResetReadBits(tag);
460     count = swf_GetU8(tag); // line style array
461     if(count == 0xff)
462         count = swf_GetU16(tag);
463     for(t=0;t<count;t++) 
464     {
465         swf_GetU16(tag);
466         if(morph)
467             swf_GetU16(tag);
468         if(num == 3)
469             {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
470         else
471             {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
472     }
473 }
474
475 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
476 {
477     int num = 1;
478     swf_ResetReadBits(tag);
479     tag->pos = 0;
480     switch(tag->id)
481     {
482         case ST_DEFINEBUTTONCXFORM: {
483             int t;
484             callback(tag, tag->pos + base, callback_data);
485             for(t=0;t<4;t++) {
486                 int flags;
487                 callback(tag, tag->pos + base, callback_data);
488                 swf_GetU16(tag); //sound id
489                 flags = swf_GetU8(tag);
490                 if(flags&1)
491                     swf_GetU32(tag); // in point
492                 if(flags&2)
493                     swf_GetU32(tag); // out points
494                 if(flags&4)
495                     swf_GetU16(tag); // loop count
496                 if(flags&8)
497                 {
498                     int npoints = swf_GetU8(tag);
499                     int s;
500                     for(s=0;s<npoints;s++)
501                     {
502                         swf_GetU32(tag);
503                         swf_GetU16(tag);
504                         swf_GetU16(tag);
505                     }
506                 }
507             }
508         } break;
509         case ST_DEFINEBUTTONSOUND:
510             callback(tag, tag->pos + base, callback_data); //button id
511         break;
512
513         case ST_EXPORTASSETS: {
514             int num =  swf_GetU16(tag);
515             int t;
516             for(t=0;t<num;t++) {
517                 callback(tag, tag->pos + base, callback_data); //button id
518                 swf_GetU16(tag); //id
519                 while(swf_GetU8(tag)); //name
520             }
521         } break;
522
523         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
524         case ST_NAMECHARACTER:
525         case ST_GENERATORTEXT:
526             callback(tag, tag->pos + base, callback_data);
527         break;
528         case ST_PLACEOBJECT:
529             callback(tag, tag->pos + base, callback_data);
530         break;
531         case ST_PLACEOBJECT2:
532             // only if placeflaghascharacter
533             if(!(tag->data[0]&2))
534                 break;
535             callback(tag, 3 + base, callback_data);
536         break;
537         case ST_REMOVEOBJECT:
538             callback(tag, tag->pos + base, callback_data);
539         break;
540         case ST_STARTSOUND:
541             callback(tag, tag->pos + base, callback_data);
542         break;
543         case ST_DEFINESPRITE: {
544             if(tag->len <= 4)
545                 break; // sprite is expanded
546
547             swf_GetU16(tag); // id
548             swf_GetU16(tag); // framenum
549
550             while(1) {
551                 U16 flags = swf_GetU16(tag);
552                 U32 len;
553                 U16 id = flags>>6;
554                 TAG *tag2 = swf_InsertTag(NULL, id);
555                 len = flags&0x3f;
556                 if(len == 63)
557                     len = swf_GetU32(tag);
558                 if(id == ST_END)
559                     break;
560                 tag2->len = tag2->memsize = len;
561                 tag2->data = malloc(len);
562                 memcpy(tag2->data, &tag->data[tag->pos], len);
563                 /* I never saw recursive sprites, but they are (theoretically) 
564                    possible, so better add base here again */
565                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
566                 swf_DeleteTag(tag2);
567                 swf_GetBlock(tag, NULL, len);
568             }
569         } 
570         break;
571         case ST_DEFINEBUTTON2: // has some font ids in the button records
572             num++; 
573         //fallthrough
574         case ST_DEFINEBUTTON: {
575             swf_GetU16(tag); //button id
576             if(num>1)
577             { 
578                 int offset;
579                 swf_GetU8(tag); //flag
580                 offset = swf_GetU16(tag); //offset
581             }
582             while(1)
583             {
584                 U16 charid;
585                 if(!swf_GetU8(tag)) //flags
586                     break; 
587                 callback(tag, tag->pos + base, callback_data);
588                 swf_GetU16(tag); //char
589                 swf_GetU16(tag); //layer
590                 swf_ResetReadBits(tag);
591                 swf_GetMatrix(tag, NULL);
592                 if(num>1) {
593                   swf_ResetReadBits(tag);
594                   swf_GetCXForm(tag, NULL, 1);
595                 }
596             }
597             // ...
598         }
599         break;
600         case ST_DEFINEEDITTEXT:  {
601             U8 flags1,flags2;
602             swf_GetU16(tag); //id
603             swf_GetRect(tag, NULL); //bounding box
604             swf_ResetReadBits(tag);
605             flags1 = swf_GetU8(tag);
606             flags2 = swf_GetU8(tag);
607             if(flags1 & 1)
608                 callback(tag, tag->pos + base, callback_data);
609         }
610         break;
611         case ST_DEFINETEXT2:
612             num ++;
613         case ST_DEFINETEXT: { 
614             int glyphbits, advancebits;
615             int id;
616             id = swf_GetU16(tag); //id
617             swf_GetRect(tag, NULL); //bounding box
618             swf_ResetReadBits(tag);
619             swf_GetMatrix(tag, NULL); //matrix
620             swf_ResetReadBits(tag);
621             glyphbits = swf_GetU8(tag); //glyphbits
622             advancebits = swf_GetU8(tag); //advancebits
623             while(1) {
624                 U16 flags;
625                 swf_ResetReadBits(tag);
626                 flags = swf_GetBits(tag, 8);
627                 if(!flags) break;
628                 if(flags & 128) // text style record
629                 {
630                     swf_ResetReadBits(tag);
631                     if(flags & 8) { // hasfont
632                         callback(tag, tag->pos + base, callback_data);
633                         id = swf_GetU16(tag);
634                     }
635                     if(flags & 4) { // hascolor
636                         if(num==1) swf_GetRGB(tag, NULL);
637                         else       swf_GetRGBA(tag, NULL);
638                     }
639                     if(flags & 2) { //has x offset
640                         swf_ResetReadBits(tag);
641                         swf_GetU16(tag);
642                     }
643                     if(flags & 1) { //has y offset
644                         swf_ResetReadBits(tag);
645                         swf_GetU16(tag);
646                     }
647                     if(flags & 8) { //has height
648                         swf_ResetReadBits(tag);
649                         swf_GetU16(tag);
650                     }
651                 } else { // glyph record
652                     int t;
653                     swf_ResetReadBits(tag);
654                     for(t=0;t<flags;t++) {
655                         swf_GetBits(tag, glyphbits);
656                         swf_GetBits(tag, advancebits);
657                     }
658                 }
659             }
660             break;
661         }
662         case ST_GLYPHNAMES:
663         case ST_DEFINEFONTINFO:
664         case ST_DEFINEFONTINFO2:
665         case ST_VIDEOFRAME:
666             callback(tag, tag->pos + base, callback_data);
667         break;
668         case ST_DEFINEVIDEOSTREAM:
669         break;
670
671         case ST_DOINITACTION:
672             callback(tag, tag->pos + base, callback_data);
673         break;
674
675         case ST_DEFINEMORPHSHAPE:
676         case ST_DEFINESHAPE3:
677         num++; //fallthrough
678         case ST_DEFINESHAPE2:
679         num++; //fallthrough
680         case ST_DEFINESHAPE: {
681             int fillbits;
682             int linebits;
683             int id; 
684             int numshapes = 1;
685             int morph = 0;
686             if(tag->id == ST_DEFINEMORPHSHAPE) {
687                 numshapes = 2;
688                 morph = 1;
689             }
690
691             id = swf_GetU16(tag); // id;
692             swf_GetRect(tag, NULL); // bounds
693             if(morph) {
694                 swf_ResetReadBits(tag);
695                 swf_GetRect(tag, NULL); // bounds2
696                 swf_GetU32(tag); //offset to endedges
697             }
698    
699             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
700
701             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
702             DEBUG_ENUMERATE printf("-------\n");
703             while(--numshapes>=0) /* morph shapes define two shapes */
704             {
705                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
706                 fillbits = swf_GetBits(tag, 4);
707                 linebits = swf_GetBits(tag, 4);
708                 DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
709                 swf_ResetReadBits(tag);
710                 while(1) {
711                     int flags;
712                     flags = swf_GetBits(tag, 1);
713                     if(!flags) { //style change
714                         flags = swf_GetBits(tag, 5);
715                         if(!flags)
716                             break;
717                         if(flags&1) { //move
718                             int n = swf_GetBits(tag, 5); 
719                             int x,y;
720                             x = swf_GetBits(tag, n); //x
721                             y = swf_GetBits(tag, n); //y
722                             DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
723                         }
724                         if(flags&2) { //fill0
725                             int fill0;
726                             fill0 = swf_GetBits(tag, fillbits); 
727                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
728                         }
729                         if(flags&4) { //fill1
730                             int fill1;
731                             fill1 = swf_GetBits(tag, fillbits); 
732                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
733                         }
734                         if(flags&8) { //linestyle
735                             int line;
736                             line = swf_GetBits(tag, linebits); 
737                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
738                         }
739                         if(flags&16) {
740                             DEBUG_ENUMERATE printf("more fillstyles\n");
741                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
742                             fillbits = swf_GetBits(tag, 4);
743                             linebits = swf_GetBits(tag, 4);
744                         }
745                     } else {
746                         flags = swf_GetBits(tag, 1);
747                         if(flags) { //straight edge
748                             int n = swf_GetBits(tag, 4) + 2;
749                             if(swf_GetBits(tag, 1)) { //line flag
750                                 int x,y;
751                                 x = swf_GetSBits(tag, n); //delta x
752                                 y = swf_GetSBits(tag, n); //delta y
753                                 DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
754                             } else {
755                                 int v=swf_GetBits(tag, 1);
756                                 int d;
757                                 d = swf_GetSBits(tag, n); //vert/horz
758                                 DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
759                             }
760                         } else { //curved edge
761                             int n = swf_GetBits(tag, 4) + 2;
762                             int x1,y1,x2,y2;
763                             x1 = swf_GetSBits(tag, n);
764                             y1 = swf_GetSBits(tag, n);
765                             x2 = swf_GetSBits(tag, n);
766                             y2 = swf_GetSBits(tag, n);
767                             DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
768                         }
769                     }
770                 }
771             }
772         }
773         break;
774         default:
775         break;
776     }
777 }
778
779 void callbackCount(TAG * t,int pos, void*ptr)
780 {
781     (*(int*)ptr)++;
782     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
783 }
784
785 void callbackFillin(TAG * t,int pos, void*ptr)
786 {
787     **(int**)ptr = pos;
788     (*(int**)ptr)++;
789     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
790 }
791
792 int swf_GetNumUsedIDs(TAG * t)
793 {
794     int num = 0;
795     enumerateUsedIDs(t, 0, callbackCount, &num);
796     return num;
797 }
798
799 void swf_GetUsedIDs(TAG * t, int * positions)
800 {
801     int * ptr = positions;
802     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
803 }
804
805 void swf_Relocate (SWF*swf, char*bitmap)
806 {
807     TAG*tag;
808     int slaveids[65536];
809     memset(slaveids, -1, sizeof(slaveids));
810     tag = swf->firstTag;
811     while(tag)
812     {
813         int num; 
814         int *ptr;
815         int t;
816
817         if(swf_isDefiningTag(tag))
818         {
819             int newid;
820             int id;
821             
822             id = swf_GetDefineID(tag); //own id
823
824             if(!bitmap[id]) { //free
825                 newid = id;
826             }
827             else {
828                 newid = 0;
829                 for (t=1;t<65536;t++)
830                 {
831                     if(!bitmap[t])
832                     {
833                         newid = t;
834                         break;
835                     }
836                 }
837             }
838             bitmap[newid] = 1;
839             slaveids[id] = newid;
840
841             swf_SetDefineID(tag, newid);
842         } 
843         
844         num = swf_GetNumUsedIDs(tag);
845         ptr = malloc(sizeof(int)*num);
846         swf_GetUsedIDs(tag, ptr);
847
848         for(t=0;t<num;t++) {
849             int id = GET16(&tag->data[ptr[t]]);
850             if(slaveids[id]<0) {
851                 fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
852                 return ;
853             }
854             id = slaveids[id];
855             PUT16(&tag->data[ptr[t]], id);
856         }
857         tag=tag->next;
858     }
859 }
860
861 void swf_RelocateDepth(SWF*swf, char*bitmap)
862 {
863     TAG*tag;
864     int nr;
865     tag = swf->firstTag;
866     for(nr=65535;nr>=0;nr--) {
867         if(bitmap[nr] != 0) 
868             break;
869     }
870     // now nr is the highest used depth. So we start
871     // assigning depths at nr+1
872     nr++;
873
874     while(tag)
875     {
876         /* TODO * clip depths 
877                 * sprites
878          */
879         int depth = swf_GetDepth(tag);
880         if(depth>=0) {
881             int newdepth = depth+nr;
882             if(newdepth>65535) {
883                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
884                 newdepth = 65535;
885             }
886             swf_SetDepth(tag, newdepth);
887         }
888         tag=tag->next;
889     }
890 }
891
892 TAG* swf_Concatenate (TAG*list1,TAG*list2)
893 {
894     TAG*tag=0,*lasttag=0;
895     char bitmap[65536];
896     char depthmap[65536];
897     SWF swf1,swf2;
898     memset(bitmap, 0, sizeof(bitmap));
899     memset(depthmap, 0, sizeof(depthmap));
900     memset(&swf1, 0, sizeof(swf1));
901     memset(&swf2, 0, sizeof(swf2));
902
903     swf1.firstTag = list1;
904     swf_FoldAll(&swf1);
905     swf2.firstTag = list2;
906     swf_FoldAll(&swf2);
907
908     tag = list1;
909     while(tag) {
910         if(!swf_isDefiningTag(tag)) {
911             int id = swf_GetDefineID(tag);
912             bitmap[id] = 1;
913         }
914         if(tag->id == ST_PLACEOBJECT ||
915            tag->id == ST_PLACEOBJECT2) {
916             int depth = swf_GetDepth(tag);
917             depthmap[depth] = 1;
918         }
919         if(tag->id == ST_REMOVEOBJECT ||
920            tag->id == ST_REMOVEOBJECT2) {
921             int depth = swf_GetDepth(tag);
922             depthmap[depth] = 0;
923         }
924         tag = tag->next;
925         lasttag = tag;
926     }
927     swf_Relocate(&swf2, bitmap);
928     swf_RelocateDepth(&swf2, depthmap);
929     lasttag->next = swf2.firstTag;
930     swf2.firstTag->prev = lasttag;
931
932     return swf1.firstTag;
933 }