removed buttons from swf_GetDefineBBox(), as they don't have one.
[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_DEFINETEXT:
147     case ST_DEFINETEXT2:
148     case ST_DEFINEVIDEOSTREAM:
149       id = swf_GetU16(t);
150       swf_GetRect(t, &b1);
151       break;
152     case ST_DEFINEMORPHSHAPE:
153       id = swf_GetU16(t);
154       swf_GetRect(t, &b1);
155       swf_GetRect(t, &b2);
156       swf_ExpandRect2(&b1, &b2);
157       break;
158     case ST_DEFINEBITSLOSSLESS:
159     case ST_DEFINEBITSLOSSLESS2:
160     case ST_DEFINEBITS:
161     case ST_DEFINEBITSJPEG2:
162     case ST_DEFINEBITSJPEG3:
163       // FIXME
164       break;
165   }
166
167   swf_SetTagPos(t,oldTagPos);
168
169   return b1;
170 }
171
172 U16 swf_GetPlaceID(TAG * t)
173 // up to SWF 4.0
174 { U32 oldTagPos;
175   U16 id = 0;
176
177   oldTagPos = swf_GetTagPos(t);
178   swf_SetTagPos(t,0);
179
180   switch (swf_GetTagID(t))
181   { case ST_PLACEOBJECT:
182     case ST_REMOVEOBJECT:
183     case ST_FREECHARACTER:
184     case ST_STARTSOUND:
185       id = swf_GetU16(t);
186       break;
187
188     case ST_PLACEOBJECT2:
189     { U8 flags = swf_GetU8(t);
190       U16 d = swf_GetU16(t);
191       id = (flags&PF_CHAR)?swf_GetU16(t):id;
192     } break;
193
194   }
195
196   swf_SetTagPos(t,oldTagPos);
197
198   return id;
199 }
200
201 static int swf_definingtagids[] =
202 {ST_DEFINESHAPE,
203  ST_DEFINESHAPE2,
204  ST_DEFINESHAPE3,
205  ST_DEFINEMORPHSHAPE,
206  ST_DEFINEFONT,
207  ST_DEFINEFONT2,
208  ST_DEFINETEXT,
209  ST_DEFINETEXT2,
210  ST_DEFINEEDITTEXT,
211  ST_DEFINEBITS,
212  ST_DEFINEBITSJPEG2,
213  ST_DEFINEBITSJPEG3,
214  ST_DEFINEBITSLOSSLESS,
215  ST_DEFINEBITSLOSSLESS2,
216  ST_DEFINEMOVIE,
217  ST_DEFINESPRITE,
218  ST_DEFINEBUTTON,
219  ST_DEFINEBUTTON2,
220  ST_DEFINESOUND,
221  ST_DEFINEVIDEOSTREAM,
222  -1
223 };
224
225 // tags which may be used inside a sprite definition
226 static int swf_spritetagids[] =
227 {ST_SHOWFRAME,
228  ST_PLACEOBJECT,
229  ST_PLACEOBJECT2,
230  ST_REMOVEOBJECT,
231  ST_REMOVEOBJECT2, //?
232  ST_DOACTION,
233  ST_STARTSOUND,
234  ST_FRAMELABEL,
235  ST_SOUNDSTREAMHEAD,
236  ST_SOUNDSTREAMHEAD2,
237  ST_SOUNDSTREAMBLOCK,
238  ST_END,
239  -1
240 };
241
242 static int swf_pseudodefiningtagids[] = 
243 {
244  ST_DEFINEFONTINFO,
245  ST_DEFINEFONTINFO2,
246  ST_DEFINEBUTTONCXFORM,
247  ST_DEFINEBUTTONSOUND,
248  ST_NAMECHARACTER,
249  ST_DOINITACTION,
250  ST_VIDEOFRAME,
251  ST_GLYPHNAMES,
252  -1
253 };
254
255 U8 swf_isAllowedSpriteTag(TAG * tag)
256 {
257     int id = tag->id;
258     int t=0;
259     while(swf_spritetagids[t]>=0)
260     {
261         if(swf_spritetagids[t] == id) 
262             return 1;
263         t++;
264     }
265     return 0; 
266 }
267
268 U8 swf_isDefiningTag(TAG * tag)
269 {
270     int id = tag->id;
271     int t=0;
272     while(swf_definingtagids[t]>=0)
273     {
274         if(swf_definingtagids[t] == id) 
275             return 1;
276         t++;
277     }
278     return 0; 
279 }
280
281 U8 swf_isPseudoDefiningTag(TAG * tag)
282 {
283     int id = tag->id;
284     int t=0;
285     while(swf_pseudodefiningtagids[t]>=0)
286     {
287         if(swf_pseudodefiningtagids[t] == id) 
288             return 1;
289         t++;
290     }
291     return 0; 
292 }
293
294 int swf_GetDepth(TAG * t)
295
296   int depth = -1;
297   U32 oldTagPos;
298   oldTagPos = swf_GetTagPos(t);
299   swf_SetTagPos(t,0);
300
301   switch (swf_GetTagID(t))
302   { case ST_PLACEOBJECT:
303     case ST_REMOVEOBJECT:
304       swf_GetU16(t); //id
305       depth = swf_GetU16(t);
306       break;
307     case ST_REMOVEOBJECT2:
308       depth = swf_GetU16(t);
309       break;
310     case ST_PLACEOBJECT2:
311     { U8 flags = swf_GetU8(t);
312       depth = swf_GetU16(t);
313     } break;
314     case ST_SETTABINDEX:
315     {
316       depth = swf_GetU16(t);
317     }
318   }
319   swf_SetTagPos(t,oldTagPos);
320   return depth;
321 }
322
323 void swf_SetDepth(TAG * t, U16 depth)
324
325   switch (swf_GetTagID(t))
326   { case ST_PLACEOBJECT:
327     case ST_REMOVEOBJECT:
328       PUT16(t->data, depth);
329       break;
330     case ST_REMOVEOBJECT2:
331       PUT16(t->data, depth);
332       break;
333     case ST_PLACEOBJECT2:
334       PUT16(&t->data[1], depth);
335       break;
336     case ST_SETTABINDEX:
337       PUT16(t->data, depth);
338       break;
339     default:
340       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
341   }
342 }
343
344 char* swf_GetName(TAG * t)
345 {
346     char* name = 0;
347     U32 oldTagPos;
348     MATRIX m;
349     CXFORM c;
350     oldTagPos = swf_GetTagPos(t);
351     swf_SetTagPos(t,0);
352     switch(swf_GetTagID(t))
353     {
354         case ST_FRAMELABEL:
355             name = &t->data[swf_GetTagPos(t)];
356         break;
357         case ST_PLACEOBJECT2: {   
358             U8 flags = swf_GetU8(t);
359             swf_GetU16(t); //depth;
360             if(flags&PF_CHAR) 
361               swf_GetU16(t); //id
362             if(flags&PF_MATRIX)
363               swf_GetMatrix(t, &m);
364             if(flags&PF_CXFORM)
365               swf_GetCXForm(t, &c, 1);
366             if(flags&PF_RATIO)
367               swf_GetU16(t);
368             if(flags&PF_CLIPACTION)
369               swf_GetU16(t);
370             if(flags&PF_NAME) {
371               swf_ResetReadBits(t);
372               name = &t->data[swf_GetTagPos(t)];
373             }
374         }
375         break;
376     }
377     swf_SetTagPos(t,oldTagPos);
378     return name;
379 }
380
381 /* used in enumerateUsedIDs */
382 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
383 {
384     GRADIENT dummy1;
385     GRADIENT dummy2;
386     int t;
387     if(!gradient1)
388         gradient1 = &dummy1;
389     if(!gradient2)
390         gradient2 = &dummy2;
391     gradient1->num = 
392     gradient2->num = swf_GetU8(tag);
393     for(t=0;t<gradient1->num;t++)
394     {
395         int s=t;
396         if(s>=8) //FIXME
397             s=7;
398         gradient1->ratios[t] = swf_GetU8(tag);
399         swf_GetRGBA(tag, &gradient1->rgba[t]);
400         gradient2->ratios[t] = swf_GetU8(tag);
401         swf_GetRGBA(tag, &gradient2->rgba[t]);
402     }
403 }
404
405 #define DEBUG_ENUMERATE if(0)
406
407 static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
408 {
409     U16 count;
410     int t;
411     count = swf_GetU8(tag);
412     if(count == 0xff && num>1) // defineshape2,3 only
413         count = swf_GetU16(tag);
414
415     for(t=0;t<count;t++)
416     {
417         int type;
418         U8*pos;
419         swf_ResetReadBits(tag);
420         type = swf_GetU8(tag); //type
421         if(type == 0) {
422             if(num == 3)
423                 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
424             else 
425                 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
426         }
427         else if(type == 0x10 || type == 0x12)
428         {
429             swf_ResetReadBits(tag);
430             swf_GetMatrix(tag, NULL);
431             if(morph)
432                 swf_GetMatrix(tag, NULL);
433             swf_ResetReadBits(tag);
434             if(morph)
435                 swf_GetMorphGradient(tag, NULL, NULL);
436             else
437                 swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
438         }
439         else if(type == 0x40 || type == 0x41)
440         {
441             swf_ResetReadBits(tag);
442             // we made it.
443             if(tag->data[tag->pos] != 0xff ||
444                tag->data[tag->pos+1] != 0xff)
445             (callback)(tag, tag->pos, callback_data);
446
447             swf_GetU16(tag);
448             swf_ResetReadBits(tag);
449             swf_GetMatrix(tag, NULL);
450             if(morph)
451                 swf_GetMatrix(tag, NULL);
452         }
453         else {
454             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
455         }
456     }
457     swf_ResetReadBits(tag);
458     count = swf_GetU8(tag); // line style array
459     if(count == 0xff)
460         count = swf_GetU16(tag);
461     for(t=0;t<count;t++) 
462     {
463         swf_GetU16(tag);
464         if(morph)
465             swf_GetU16(tag);
466         if(num == 3)
467             {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
468         else
469             {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
470     }
471 }
472
473 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
474 {
475     int num = 1;
476     swf_ResetReadBits(tag);
477     tag->pos = 0;
478     switch(tag->id)
479     {
480         case ST_DEFINEBUTTONCXFORM: {
481             int t;
482             callback(tag, tag->pos + base, callback_data);
483             for(t=0;t<4;t++) {
484                 int flags;
485                 callback(tag, tag->pos + base, callback_data);
486                 swf_GetU16(tag); //sound id
487                 flags = swf_GetU8(tag);
488                 if(flags&1)
489                     swf_GetU32(tag); // in point
490                 if(flags&2)
491                     swf_GetU32(tag); // out points
492                 if(flags&4)
493                     swf_GetU16(tag); // loop count
494                 if(flags&8)
495                 {
496                     int npoints = swf_GetU8(tag);
497                     int s;
498                     for(s=0;s<npoints;s++)
499                     {
500                         swf_GetU32(tag);
501                         swf_GetU16(tag);
502                         swf_GetU16(tag);
503                     }
504                 }
505             }
506         } break;
507         case ST_DEFINEBUTTONSOUND:
508             callback(tag, tag->pos + base, callback_data); //button id
509         break;
510
511         case ST_EXPORTASSETS: {
512             int num =  swf_GetU16(tag);
513             int t;
514             for(t=0;t<num;t++) {
515                 callback(tag, tag->pos + base, callback_data); //button id
516                 swf_GetU16(tag); //id
517                 while(swf_GetU8(tag)); //name
518             }
519         } break;
520
521         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
522         case ST_NAMECHARACTER:
523         case ST_GENERATORTEXT:
524             callback(tag, tag->pos + base, callback_data);
525         break;
526         case ST_PLACEOBJECT:
527             callback(tag, tag->pos + base, callback_data);
528         break;
529         case ST_PLACEOBJECT2:
530             // only if placeflaghascharacter
531             if(!(tag->data[0]&2))
532                 break;
533             callback(tag, 3 + base, callback_data);
534         break;
535         case ST_REMOVEOBJECT:
536             callback(tag, tag->pos + base, callback_data);
537         break;
538         case ST_STARTSOUND:
539             callback(tag, tag->pos + base, callback_data);
540         break;
541         case ST_DEFINESPRITE: {
542             if(tag->len <= 4)
543                 break; // sprite is expanded
544
545             swf_GetU16(tag); // id
546             swf_GetU16(tag); // framenum
547
548             while(1) {
549                 U16 flags = swf_GetU16(tag);
550                 U32 len;
551                 U16 id = flags>>6;
552                 TAG *tag2 = swf_InsertTag(NULL, id);
553                 len = flags&0x3f;
554                 if(len == 63)
555                     len = swf_GetU32(tag);
556                 if(id == ST_END)
557                     break;
558                 tag2->len = tag2->memsize = len;
559                 tag2->data = malloc(len);
560                 memcpy(tag2->data, &tag->data[tag->pos], len);
561                 /* I never saw recursive sprites, but they are (theoretically) 
562                    possible, so better add base here again */
563                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
564                 swf_DeleteTag(tag2);
565                 swf_GetBlock(tag, NULL, len);
566             }
567         } 
568         break;
569         case ST_DEFINEBUTTON2: // has some font ids in the button records
570             num++; 
571         //fallthrough
572         case ST_DEFINEBUTTON: {
573             swf_GetU16(tag); //button id
574             if(num>1)
575             { 
576                 int offset;
577                 swf_GetU8(tag); //flag
578                 offset = swf_GetU16(tag); //offset
579             }
580             while(1)
581             {
582                 U16 charid;
583                 if(!swf_GetU8(tag)) //flags
584                     break; 
585                 callback(tag, tag->pos + base, callback_data);
586                 swf_GetU16(tag); //char
587                 swf_GetU16(tag); //layer
588                 swf_ResetReadBits(tag);
589                 swf_GetMatrix(tag, NULL);
590                 if(num>1) {
591                   swf_ResetReadBits(tag);
592                   swf_GetCXForm(tag, NULL, 1);
593                 }
594             }
595             // ...
596         }
597         break;
598         case ST_DEFINEEDITTEXT:  {
599             U8 flags1,flags2;
600             swf_GetU16(tag); //id
601             swf_GetRect(tag, NULL); //bounding box
602             swf_ResetReadBits(tag);
603             flags1 = swf_GetU8(tag);
604             flags2 = swf_GetU8(tag);
605             if(flags1 & 1)
606                 callback(tag, tag->pos + base, callback_data);
607         }
608         break;
609         case ST_DEFINETEXT2:
610             num ++;
611         case ST_DEFINETEXT: { 
612             int glyphbits, advancebits;
613             int id;
614             id = swf_GetU16(tag); //id
615             swf_GetRect(tag, NULL); //bounding box
616             swf_ResetReadBits(tag);
617             swf_GetMatrix(tag, NULL); //matrix
618             swf_ResetReadBits(tag);
619             glyphbits = swf_GetU8(tag); //glyphbits
620             advancebits = swf_GetU8(tag); //advancebits
621             while(1) {
622                 U16 flags;
623                 swf_ResetReadBits(tag);
624                 flags = swf_GetBits(tag, 8);
625                 if(!flags) break;
626                 if(flags & 128) // text style record
627                 {
628                     swf_ResetReadBits(tag);
629                     if(flags & 8) { // hasfont
630                         callback(tag, tag->pos + base, callback_data);
631                         id = swf_GetU16(tag);
632                     }
633                     if(flags & 4) { // hascolor
634                         if(num==1) swf_GetRGB(tag, NULL);
635                         else       swf_GetRGBA(tag, NULL);
636                     }
637                     if(flags & 2) { //has x offset
638                         swf_ResetReadBits(tag);
639                         swf_GetU16(tag);
640                     }
641                     if(flags & 1) { //has y offset
642                         swf_ResetReadBits(tag);
643                         swf_GetU16(tag);
644                     }
645                     if(flags & 8) { //has height
646                         swf_ResetReadBits(tag);
647                         swf_GetU16(tag);
648                     }
649                 } else { // glyph record
650                     int t;
651                     swf_ResetReadBits(tag);
652                     for(t=0;t<flags;t++) {
653                         swf_GetBits(tag, glyphbits);
654                         swf_GetBits(tag, advancebits);
655                     }
656                 }
657             }
658             break;
659         }
660         case ST_GLYPHNAMES:
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     SWF swf1,swf2;
893     memset(bitmap, 0, sizeof(bitmap));
894     memset(depthmap, 0, sizeof(depthmap));
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 }