added swf_Concatenate() function
[swftools.git] / lib / modules / swftools.c
1 /* swftools.c
2
3    Math and matrix functions, misc tools
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 // Matrix & Math tools for SWF files
25
26 #define S64 long long
27 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
28 { S64 a;
29   a = (S64)a1*(S64)b1+(S64)a2*(S64)b2;
30   return (SFIXED)(a>>16);
31 }
32 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
33 { S64 z = zaehler<<16;
34   S64 a = z/(S64)nenner;
35   return (SFIXED)a;
36 }
37 #undef S64
38
39 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
40 {        
41   if (!d) return NULL;
42   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
43   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
44   
45   d->tx = s1->tx + s2->tx;
46   d->ty = s1->ty + s2->ty;
47   
48   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
49   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
50   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
51   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
52
53   //DumpMatrix(NULL,d);
54   
55   return d;
56 }
57
58 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
59                                int x1,int y1,int x2,int y2)
60 { int dx1 = x1 - x0;
61   int dy1 = y1 - y0;
62   int dx2 = x2 - x0;
63   int dy2 = y2 - y0;
64   
65   if (!m) return NULL;
66   if ((!dx)||(!dy)) return NULL; // check DIV by zero
67
68   m->tx = x0;
69   m->ty = y0;
70   m->sx = RFXSWF_QFIX(dx1,dx);
71   m->sy = RFXSWF_QFIX(dy2,dy);
72   m->r0 = RFXSWF_QFIX(dy1,dx);
73   m->r1 = RFXSWF_QFIX(dx2,dy);
74   
75   return m;
76 }
77
78 void swf_SetDefineID(TAG * tag, U16 newid)
79 {
80   int oldlen = tag->len;
81   tag->len = 0;
82   swf_SetU16(tag, newid); /* set defining ID */
83   tag->len = oldlen;
84 }
85
86 U16 swf_GetDefineID(TAG * t)
87 // up to SWF 4.0
88 { U32 oldTagPos;
89   U16 id = 0;
90
91   oldTagPos = swf_GetTagPos(t);
92   swf_SetTagPos(t,0);
93
94   switch (swf_GetTagID(t))
95   { case ST_DEFINESHAPE:
96     case ST_DEFINESHAPE2:
97     case ST_DEFINESHAPE3:
98     case ST_DEFINEMORPHSHAPE:
99     case ST_DEFINEEDITTEXT:
100     case ST_DEFINEBITS:
101     case ST_DEFINEBITSJPEG2:
102     case ST_DEFINEBITSJPEG3:
103     case ST_DEFINEBITSLOSSLESS:
104     case ST_DEFINEBITSLOSSLESS2:
105     case ST_DEFINEBUTTON:
106     case ST_DEFINEBUTTON2:
107     case ST_DEFINEBUTTONCXFORM: //pseudodefine
108     case ST_DEFINEBUTTONSOUND: //pseudodefine
109     case ST_DEFINEFONT:
110     case ST_DEFINEFONT2:
111     case ST_DEFINEFONTINFO: //pseudodefine
112     case ST_DEFINEFONTINFO2: //pseudodefine
113     case ST_DEFINETEXT:
114     case ST_DEFINETEXT2:
115     case ST_DEFINESOUND:
116     case ST_DEFINESPRITE:
117     case ST_DEFINEMOVIE:
118     case ST_DEFINEVIDEOSTREAM:
119     case ST_VIDEOFRAME: //pseudodefine
120     case ST_NAMECHARACTER: //pseudodefine
121       id = swf_GetU16(t);
122       break;
123   }
124
125   swf_SetTagPos(t,oldTagPos);
126
127   return id;
128 }
129
130 SRECT swf_GetDefineBBox(TAG * t)
131 {
132   U32 oldTagPos;
133   U16 id = 0;
134   SRECT b1,b2;
135
136   oldTagPos = swf_GetTagPos(t);
137   swf_SetTagPos(t,0);
138
139   swf_GetRect(0, &b1);
140
141   switch (swf_GetTagID(t))
142   { case ST_DEFINESHAPE:
143     case ST_DEFINESHAPE2:
144     case ST_DEFINESHAPE3:
145     case ST_DEFINEEDITTEXT:
146     case ST_DEFINEBUTTON:
147     case ST_DEFINEBUTTON2:
148     case ST_DEFINETEXT:
149     case ST_DEFINETEXT2:
150     case ST_DEFINEVIDEOSTREAM:
151       id = swf_GetU16(t);
152       swf_GetRect(t, &b1);
153       break;
154     case ST_DEFINEMORPHSHAPE:
155       id = swf_GetU16(t);
156       swf_GetRect(t, &b1);
157       swf_GetRect(t, &b2);
158       swf_ExpandRect2(&b1, &b2);
159       break;
160     case ST_DEFINEBITSLOSSLESS:
161     case ST_DEFINEBITSLOSSLESS2:
162     case ST_DEFINEBITS:
163     case ST_DEFINEBITSJPEG2:
164     case ST_DEFINEBITSJPEG3:
165       // FIXME
166       break;
167   }
168
169   swf_SetTagPos(t,oldTagPos);
170
171   return b1;
172 }
173
174 U16 swf_GetPlaceID(TAG * t)
175 // up to SWF 4.0
176 { U32 oldTagPos;
177   U16 id = 0;
178
179   oldTagPos = swf_GetTagPos(t);
180   swf_SetTagPos(t,0);
181
182   switch (swf_GetTagID(t))
183   { case ST_PLACEOBJECT:
184     case ST_REMOVEOBJECT:
185     case ST_FREECHARACTER:
186     case ST_STARTSOUND:
187       id = swf_GetU16(t);
188       break;
189
190     case ST_PLACEOBJECT2:
191     { U8 flags = swf_GetU8(t);
192       U16 d = swf_GetU16(t);
193       id = (flags&PF_CHAR)?swf_GetU16(t):id;
194     } break;
195
196   }
197
198   swf_SetTagPos(t,oldTagPos);
199
200   return id;
201 }
202
203 static int swf_definingtagids[] =
204 {ST_DEFINESHAPE,
205  ST_DEFINESHAPE2,
206  ST_DEFINESHAPE3,
207  ST_DEFINEMORPHSHAPE,
208  ST_DEFINEFONT,
209  ST_DEFINEFONT2,
210  ST_DEFINETEXT,
211  ST_DEFINETEXT2,
212  ST_DEFINEEDITTEXT,
213  ST_DEFINEBITS,
214  ST_DEFINEBITSJPEG2,
215  ST_DEFINEBITSJPEG3,
216  ST_DEFINEBITSLOSSLESS,
217  ST_DEFINEBITSLOSSLESS2,
218  ST_DEFINEMOVIE,
219  ST_DEFINESPRITE,
220  ST_DEFINEBUTTON,
221  ST_DEFINEBUTTON2,
222  ST_DEFINESOUND,
223  ST_DEFINEVIDEOSTREAM,
224  -1
225 };
226
227 // tags which may be used inside a sprite definition
228 static int swf_spritetagids[] =
229 {ST_SHOWFRAME,
230  ST_PLACEOBJECT,
231  ST_PLACEOBJECT2,
232  ST_REMOVEOBJECT,
233  ST_REMOVEOBJECT2, //?
234  ST_DOACTION,
235  ST_STARTSOUND,
236  ST_FRAMELABEL,
237  ST_SOUNDSTREAMHEAD,
238  ST_SOUNDSTREAMHEAD2,
239  ST_SOUNDSTREAMBLOCK,
240  ST_END,
241  -1
242 };
243
244 static int swf_pseudodefiningtagids[] = 
245 {
246  ST_DEFINEFONTINFO,
247  ST_DEFINEFONTINFO2,
248  ST_DEFINEBUTTONCXFORM,
249  ST_DEFINEBUTTONSOUND,
250  ST_NAMECHARACTER,
251  ST_DOINITACTION,
252  ST_VIDEOFRAME,
253  -1
254 };
255
256 U8 swf_isAllowedSpriteTag(TAG * tag)
257 {
258     int id = tag->id;
259     int t=0;
260     while(swf_spritetagids[t]>=0)
261     {
262         if(swf_spritetagids[t] == id) 
263             return 1;
264         t++;
265     }
266     return 0; 
267 }
268
269 U8 swf_isDefiningTag(TAG * tag)
270 {
271     int id = tag->id;
272     int t=0;
273     while(swf_definingtagids[t]>=0)
274     {
275         if(swf_definingtagids[t] == id) 
276             return 1;
277         t++;
278     }
279     return 0; 
280 }
281
282 U8 swf_isPseudoDefiningTag(TAG * tag)
283 {
284     int id = tag->id;
285     int t=0;
286     while(swf_pseudodefiningtagids[t]>=0)
287     {
288         if(swf_pseudodefiningtagids[t] == id) 
289             return 1;
290         t++;
291     }
292     return 0; 
293 }
294
295 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_DEFINEFONTINFO:
662         case ST_DEFINEFONTINFO2:
663         case ST_VIDEOFRAME:
664             callback(tag, tag->pos + base, callback_data);
665         break;
666         case ST_DEFINEVIDEOSTREAM:
667         break;
668
669         case ST_DOINITACTION:
670             callback(tag, tag->pos + base, callback_data);
671         break;
672
673         case ST_DEFINEMORPHSHAPE:
674         case ST_DEFINESHAPE3:
675         num++; //fallthrough
676         case ST_DEFINESHAPE2:
677         num++; //fallthrough
678         case ST_DEFINESHAPE: {
679             int fillbits;
680             int linebits;
681             int id; 
682             int numshapes = 1;
683             int morph = 0;
684             if(tag->id == ST_DEFINEMORPHSHAPE) {
685                 numshapes = 2;
686                 morph = 1;
687             }
688
689             id = swf_GetU16(tag); // id;
690             swf_GetRect(tag, NULL); // bounds
691             if(morph) {
692                 swf_ResetReadBits(tag);
693                 swf_GetRect(tag, NULL); // bounds2
694                 swf_GetU32(tag); //offset to endedges
695             }
696    
697             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
698
699             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
700             DEBUG_ENUMERATE printf("-------\n");
701             while(--numshapes>=0) /* morph shapes define two shapes */
702             {
703                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
704                 fillbits = swf_GetBits(tag, 4);
705                 linebits = swf_GetBits(tag, 4);
706                 DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
707                 swf_ResetReadBits(tag);
708                 while(1) {
709                     int flags;
710                     flags = swf_GetBits(tag, 1);
711                     if(!flags) { //style change
712                         flags = swf_GetBits(tag, 5);
713                         if(!flags)
714                             break;
715                         if(flags&1) { //move
716                             int n = swf_GetBits(tag, 5); 
717                             int x,y;
718                             x = swf_GetBits(tag, n); //x
719                             y = swf_GetBits(tag, n); //y
720                             DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
721                         }
722                         if(flags&2) { //fill0
723                             int fill0;
724                             fill0 = swf_GetBits(tag, fillbits); 
725                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
726                         }
727                         if(flags&4) { //fill1
728                             int fill1;
729                             fill1 = swf_GetBits(tag, fillbits); 
730                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
731                         }
732                         if(flags&8) { //linestyle
733                             int line;
734                             line = swf_GetBits(tag, linebits); 
735                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
736                         }
737                         if(flags&16) {
738                             DEBUG_ENUMERATE printf("more fillstyles\n");
739                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
740                             fillbits = swf_GetBits(tag, 4);
741                             linebits = swf_GetBits(tag, 4);
742                         }
743                     } else {
744                         flags = swf_GetBits(tag, 1);
745                         if(flags) { //straight edge
746                             int n = swf_GetBits(tag, 4) + 2;
747                             if(swf_GetBits(tag, 1)) { //line flag
748                                 int x,y;
749                                 x = swf_GetSBits(tag, n); //delta x
750                                 y = swf_GetSBits(tag, n); //delta y
751                                 DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
752                             } else {
753                                 int v=swf_GetBits(tag, 1);
754                                 int d;
755                                 d = swf_GetSBits(tag, n); //vert/horz
756                                 DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
757                             }
758                         } else { //curved edge
759                             int n = swf_GetBits(tag, 4) + 2;
760                             int x1,y1,x2,y2;
761                             x1 = swf_GetSBits(tag, n);
762                             y1 = swf_GetSBits(tag, n);
763                             x2 = swf_GetSBits(tag, n);
764                             y2 = swf_GetSBits(tag, n);
765                             DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
766                         }
767                     }
768                 }
769             }
770         }
771         break;
772         default:
773         break;
774     }
775 }
776
777 void callbackCount(TAG * t,int pos, void*ptr)
778 {
779     (*(int*)ptr)++;
780     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
781 }
782
783 void callbackFillin(TAG * t,int pos, void*ptr)
784 {
785     **(int**)ptr = pos;
786     (*(int**)ptr)++;
787     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
788 }
789
790 int swf_GetNumUsedIDs(TAG * t)
791 {
792     int num = 0;
793     enumerateUsedIDs(t, 0, callbackCount, &num);
794     return num;
795 }
796
797 void swf_GetUsedIDs(TAG * t, int * positions)
798 {
799     int * ptr = positions;
800     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
801 }
802
803 void swf_Relocate (SWF*swf, char*bitmap)
804 {
805     TAG*tag;
806     int slaveids[65536];
807     memset(slaveids, -1, sizeof(slaveids));
808     tag = swf->firstTag;
809     while(tag)
810     {
811         int num; 
812         int *ptr;
813         int t;
814
815         if(swf_isDefiningTag(tag))
816         {
817             int newid;
818             int id;
819             
820             id = swf_GetDefineID(tag); //own id
821
822             if(!bitmap[id]) { //free
823                 newid = id;
824             }
825             else {
826                 newid = 0;
827                 for (t=1;t<65536;t++)
828                 {
829                     if(!bitmap[t])
830                     {
831                         newid = t;
832                         break;
833                     }
834                 }
835             }
836             bitmap[newid] = 1;
837             slaveids[id] = newid;
838
839             swf_SetDefineID(tag, newid);
840         } 
841         
842         num = swf_GetNumUsedIDs(tag);
843         ptr = malloc(sizeof(int)*num);
844         swf_GetUsedIDs(tag, ptr);
845
846         for(t=0;t<num;t++) {
847             int id = GET16(&tag->data[ptr[t]]);
848             if(slaveids[id]<0) {
849                 fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
850                 return ;
851             }
852             id = slaveids[id];
853             PUT16(&tag->data[ptr[t]], id);
854         }
855         tag=tag->next;
856     }
857 }
858
859 void swf_RelocateDepth(SWF*swf, char*bitmap)
860 {
861     TAG*tag;
862     int nr;
863     tag = swf->firstTag;
864     for(nr=65535;nr>=0;nr--) {
865         if(bitmap[nr] != 0) 
866             break;
867     }
868     // now nr is the highest used depth. So we start
869     // assigning depths at nr+1
870     nr++;
871
872     while(tag)
873     {
874         int depth = swf_GetDepth(tag);
875         if(depth>=0) {
876             int newdepth = depth+nr;
877             if(newdepth>65535) {
878                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
879                 newdepth = 65535;
880             }
881             swf_SetDepth(tag, newdepth);
882         }
883         tag=tag->next;
884     }
885 }
886
887 TAG* swf_Concatenate (TAG*list1,TAG*list2)
888 {
889     TAG*tag=0,*lasttag=0;
890     char bitmap[65536];
891     char depthmap[65536];
892     memset(bitmap, 0, sizeof(bitmap));
893     memset(depthmap, 0, sizeof(depthmap));
894     SWF swf1,swf2;
895     memset(&swf1, 0, sizeof(swf1));
896     memset(&swf2, 0, sizeof(swf2));
897
898     swf1.firstTag = list1;
899     swf_FoldAll(&swf1);
900     swf2.firstTag = list2;
901     swf_FoldAll(&swf2);
902
903     tag = list1;
904     while(tag) {
905         if(!swf_isDefiningTag(tag)) {
906             int id = swf_GetDefineID(tag);
907             bitmap[id] = 1;
908         }
909         if(tag->id == ST_PLACEOBJECT ||
910            tag->id == ST_PLACEOBJECT2) {
911             int depth = swf_GetDepth(tag);
912             depthmap[depth] = 1;
913         }
914         if(tag->id == ST_REMOVEOBJECT ||
915            tag->id == ST_REMOVEOBJECT2) {
916             int depth = swf_GetDepth(tag);
917             depthmap[depth] = 0;
918         }
919         tag = tag->next;
920         lasttag = tag;
921     }
922     swf_Relocate(&swf2, bitmap);
923     swf_RelocateDepth(&swf2, depthmap);
924     lasttag->next = swf2.firstTag;
925     swf2.firstTag->prev = lasttag;
926
927     return swf1.firstTag;
928 }